Merge branch 'master' of https://gitlab.zcorp.cc/easycorp/zentaopms into importExport

This commit is contained in:
tanghucheng
2022-07-13 13:19:44 +08:00
104 changed files with 1924 additions and 734 deletions
+1
View File
@@ -271,6 +271,7 @@ $filter->project->task->cookie['projectTaskOrder'] = 'reg::orderBy';
$filter->project->task->cookie['windowWidth'] = 'int';
$filter->project->export->cookie['checkedItem'] = 'reg::checked';
$filter->project->execution->cookie['pagerExecutionAll'] = 'int';
$filter->project->execution->cookie['showTask'] = 'code';
$filter->projectstory->story->cookie['storyModuleParam'] = 'int';
$filter->projectstory->story->cookie['pagerProductBrowse'] = 'int';
+2
View File
@@ -14,3 +14,5 @@ ALTER TABLE `zt_doc` CHANGE `assignedDate` `assignedDate` datetime NOT NULL AFTE
UPDATE `zt_approval` SET `createdDate` = '';
ALTER TABLE `zt_approval` CHANGE `createdDate` `createdDate` datetime NOT NULL AFTER `createdBy`;
ALTER TABLE `zt_reviewissue` ADD `approval` MEDIUMINT NOT NULL AFTER `review`;
+1
View File
@@ -9526,6 +9526,7 @@ CREATE TABLE IF NOT EXISTS `zt_reviewissue` (
`id` mediumint(8) NOT NULL AUTO_INCREMENT,
`project` mediumint(8) unsigned NOT NULL,
`review` mediumint(8) NOT NULL,
`approval` mediumint(8) NOT NULL,
`injection` mediumint(8) NOT NULL,
`identify` mediumint(8) NOT NULL,
`type` char(30) NOT NULL DEFAULT 'review',
+2 -1
View File
@@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, imported to <strong>$extra</
$lang->action->desc->fromsonarqube = '$date, created by <strong>$actor</strong> from <strong>SonarQube Issue</strong>.' . "\n";
$lang->action->desc->tolib = '$date, imported by <strong>$actor</strong> .' . "\n";
$lang->action->desc->updatetolib = '$date, updated to ' . $lang->testcase->common . ' by <strong>$actor</strong>.' . "\n";
$lang->action->desc->adjusttasktowait = '$date, System Reminder: The task status will be set to Not Started as the consumed work hour is adjusted to 0. ' . "\n";
/* Used to describe the history of operations related to parent-child tasks. */
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> created a child task <strong>$extra</strong>。' . "\n";
@@ -305,7 +306,7 @@ $lang->action->label->suspended = 'suspended ';
$lang->action->label->login = 'Login';
$lang->action->label->logout = "Logout";
$lang->action->label->notified = "Notified";
$lang->action->label->deleteestimate = "deleted ";
$lang->action->label->deleteestimate = "deleted hours";
$lang->action->label->linked2build = "linked ";
$lang->action->label->linked2bug = "linked ";
$lang->action->label->linked2testtask = "linked";
+2 -1
View File
@@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, imported to <strong>$extra</
$lang->action->desc->fromsonarqube = '$date, created by <strong>$actor</strong> from <strong>SonarQube Issue</strong>.' . "\n";
$lang->action->desc->tolib = '$date, imported by <strong>$actor</strong> .' . "\n";
$lang->action->desc->updatetolib = '$date, updated to ' . $lang->testcase->common . ' by <strong>$actor</strong>.' . "\n";
$lang->action->desc->adjusttasktowait = '$date, System Reminder: The task status will be set to Not Started as the consumed work hour is adjusted to 0. ' . "\n";
/* Used to describe the history of operations related to parent-child tasks. */
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> created a child task <strong>$extra</strong>。' . "\n";
@@ -305,7 +306,7 @@ $lang->action->label->suspended = 'suspended ';
$lang->action->label->login = 'login';
$lang->action->label->logout = "logout";
$lang->action->label->notified = "Notified";
$lang->action->label->deleteestimate = "deleted ";
$lang->action->label->deleteestimate = "deleted hours";
$lang->action->label->linked2build = "linked ";
$lang->action->label->linked2bug = "linked ";
$lang->action->label->linked2testtask = "linked";
+1
View File
@@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, imported to <strong>$extra</
$lang->action->desc->fromsonarqube = '$date, created by <strong>$actor</strong> from <strong>SonarQube Issue</strong>.' . "\n";
$lang->action->desc->tolib = '$date, Importé par <strong>$actor</strong> .' . "\n";
$lang->action->desc->updatetolib = '$date, MàJ de ' . $lang->testcase->common . ' par <strong>$actor</strong>.' . "\n";
$lang->action->desc->adjusttasktowait = '$date, System Reminder: The task status will be set to Not Started as the consumed work hour is adjusted to 0. ' . "\n";
/* Used to describe the history of operations related to parent-child tasks. */
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> a créé un sous-tâche <strong>$extra</strong>。' . "\n";
+1
View File
@@ -209,6 +209,7 @@ $lang->action->desc->importedbuild = '$date, 由 <strong>$actor</strong>
$lang->action->desc->fromsonarqube = '$date, 由 <strong>$actor</strong> 从<strong>SonarQube问题</strong>转化而来。' . "\n";
$lang->action->desc->tolib = '$date, 由 <strong>$actor</strong> 导入。' . "\n";
$lang->action->desc->updatetolib = '$date, 由 <strong>$actor</strong> 从' . $lang->testcase->common . '更新。' . "\n";
$lang->action->desc->adjusttasktowait = '$date, 系统判断由于消耗工时调整为0,将任务状态置为未开始。' . "\n";
/* 用来描述和父子任务相关的操作历史记录。*/
$lang->action->desc->createchildren = '$date, 由 <strong>$actor</strong> 创建子任务 <strong>$extra</strong>。' . "\n";
+1 -1
View File
@@ -38,7 +38,7 @@ $lang->block->lblBlock = 'Block';
$lang->block->lblNum = 'Number';
$lang->block->lblHtml = 'HTML';
$lang->block->dynamic = 'Dynamics';
$lang->block->assignToMe = 'Todo';
$lang->block->assignToMe = 'Work';
$lang->block->wait = 'Wait';
$lang->block->doing = 'Doing';
$lang->block->done = 'Done';
+1
View File
@@ -18,6 +18,7 @@
$i = 0;
foreach($actions as $action)
{
if($action->action == 'adjusttasktowait') continue;
$user = zget($users, $action->actor);
if($action->action == 'login' or $action->action == 'logout') $action->objectName = $action->objectLabel = '';
if($action->objectType == 'sonarqubeproject') $action->objectName = $action->extra;
+1 -1
View File
@@ -274,7 +274,7 @@ class branch extends control
{
$branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
}
if(!isset($branchTagOption[$oldBranch]))
if(is_numeric($oldBranch) and !isset($branchTagOption[$oldBranch]))
{
$branch = $this->branch->getById($oldBranch, $productID, '');
$branchTagOption[$oldBranch] = $oldBranch == BRANCH_MAIN ? $branch : ($branch->name . ($branch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : ''));
+102 -110
View File
@@ -1390,11 +1390,12 @@ class bug extends control
* Update assign of bug.
*
* @param int $bugID
* @parm string $kanbanGroup
* @param string $kanbanGroup
* @param string $from taskkanban
* @access public
* @return void
*/
public function assignTo($bugID, $kanbanGroup = 'default')
public function assignTo($bugID, $kanbanGroup = 'default', $from = '')
{
$bug = $this->bug->getById($bugID);
$this->bug->checkBugExecutionPriv($bug);
@@ -1414,28 +1415,24 @@ class bug extends control
if(isonlybody())
{
$bug = $this->bug->getById($bugID);
$execution = $this->loadModel('execution')->getByID($bug->execution);
if($this->app->tab == 'execution')
$bug = $this->bug->getById($bugID);
$execution = $this->loadModel('execution')->getByID($bug->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if(isset($execution->type) and $execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', 0, $kanbanGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', 0, $kanbanGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
else
{
@@ -1627,10 +1624,11 @@ class bug extends control
*
* @param int $bugID
* @param string $extra
* @param string $from taskkanban
* @access public
* @return void
*/
public function confirmBug($bugID, $extra = '')
public function confirmBug($bugID, $extra = '', $from = '')
{
$bug = $this->bug->getById($bugID);
if(!empty($_POST))
@@ -1646,28 +1644,24 @@ class bug extends control
parse_str($extra, $output);
if(isonlybody())
{
$execution = $this->loadModel('execution')->getByID($bug->execution);
if($this->app->tab == 'execution')
$execution = $this->loadModel('execution')->getByID($bug->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if(isset($execution->type) and $execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
else
{
@@ -1714,10 +1708,11 @@ class bug extends control
*
* @param int $bugID
* @param string $extra
* @param string $from taskkanban
* @access public
* @return void
*/
public function resolve($bugID, $extra = '')
public function resolve($bugID, $extra = '', $from = '')
{
$bug = $this->bug->getById($bugID);
if($bug->execution) $execution = $this->loadModel('execution')->getByID($bug->execution);
@@ -1753,27 +1748,23 @@ class bug extends control
parse_str($extra, $output);
if(isonlybody())
{
if($this->app->tab == 'execution')
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if(isset($execution->type) and $execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
else
{
@@ -1842,10 +1833,11 @@ class bug extends control
*
* @param int $bugID
* @param string $extra
* @param string $from taskkanban
* @access public
* @return void
*/
public function activate($bugID, $extra = '')
public function activate($bugID, $extra = '', $from = '')
{
$bug = $this->bug->getById($bugID);
if(!empty($_POST))
@@ -1866,27 +1858,23 @@ class bug extends control
if(isonlybody())
{
$execution = $this->loadModel('execution')->getByID($bug->execution);
if($this->app->tab == 'execution')
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if(isset($execution->type) and $execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
else
{
@@ -1917,10 +1905,11 @@ class bug extends control
*
* @param int $bugID
* @param string $extra
* @param string $from taskkanban
* @access public
* @return void
*/
public function close($bugID, $extra = '')
public function close($bugID, $extra = '', $from = '')
{
$bug = $this->bug->getById($bugID);
if(!empty($_POST))
@@ -1937,28 +1926,24 @@ class bug extends control
parse_str($extra, $output);
if(isonlybody())
{
$execution = $this->loadModel('execution')->getByID($bug->execution);
if($this->app->tab == 'execution')
$execution = $this->loadModel('execution')->getByID($bug->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and isset($execution->type) and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if(isset($execution->type) and $execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($bug->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($bug->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'bug' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"bug\", $kanbanData)"));
}
else
{
@@ -2037,11 +2022,14 @@ class bug extends control
/**
* Batch close bugs.
*
* @param int $releaseID
* @param string $viewType
* @access public
* @return void
*/
public function batchClose()
public function batchClose($releaseID = '', $viewType = '')
{
if($releaseID) $this->post->bugIDList = $this->post->unlinkBugs;
if($this->post->bugIDList)
{
$bugIDList = $this->post->bugIDList;
@@ -2066,6 +2054,10 @@ class bug extends control
}
$this->loadModel('score')->create('ajax', 'batchOther');
if(isset($skipBugs)) echo js::alert(sprintf($this->lang->bug->skipClose, join(',', $skipBugs)));
if($viewType)
{
return print(js::locate($this->createLink($viewType, 'view', "releaseID=$releaseID&type=bug"), 'parent'));
}
}
return print(js::reload('parent'));
}
+2
View File
@@ -28,6 +28,8 @@ class caselibModel extends model
if(!empty($products) and $this->session->product) $this->loadModel('qa')->setMenu($products, $this->session->product);
if(empty($products)) $this->loadModel('qa')->setMenu(array(0 => ''), 0);
$this->lang->qa->menu->caselib['subModule'] .= ',testcase';
if($libraries)
{
$libName = '';
+1 -1
View File
@@ -245,7 +245,7 @@ $lang->scrum->menu->settings['subMenu']->group = array('link' => "{$lang->
/* Waterfall menu. */
$lang->waterfall->menu = new stdclass();
$lang->waterfall->menu->index = array('link' => "$lang->dashboard|project|index|project=%s");
$lang->waterfall->menu->execution = array('link' => "{$lang->stage->common}|project|execution|status=all&projectID=%s", 'subModule' => 'programplan');
$lang->waterfall->menu->execution = array('link' => "{$lang->stage->common}|project|execution|status=all&projectID=%s", 'subModule' => 'programplan,task');
$lang->waterfall->menu->story = array('link' => "$lang->SRCommon|projectstory|story|project=%s", 'subModule' => 'projectstory,tree', 'exclude' => 'projectstory-track');
$lang->waterfall->menu->design = array('link' => "{$lang->design->common}|design|browse|project=%s");
$lang->waterfall->menu->qa = array('link' => "{$lang->qa->common}|project|bug|projectID=%s", 'subModule' => 'testcase,testtask,bug,testreport', 'alias' => 'bug,testtask,testcase,testreport');
+1
View File
@@ -64,6 +64,7 @@
</div>
<ul class="timeline timeline-tag-left <?php if($browseType == 'all') echo 'margin-l-50px';?>">
<?php foreach($actions as $i => $action):?>
<?php if($action->action == 'adjusttasktowait') continue;?>
<?php if(empty($firstAction)) $firstAction = $action;?>
<li <?php if($action->major) echo "class='active'";?>>
<div>
+2
View File
@@ -0,0 +1,2 @@
#readOnlyOfExecution {font-size: 13px; color: #5e626d;}
#readOnlyOfExecution i {font-size: 14px; color: #0075ff; margin-right: 3px;}
+2
View File
@@ -0,0 +1,2 @@
#readOnlyOfKanban {font-size: 13px; color: #5e626d;}
#readOnlyOfKanban i {font-size: 14px; color: #0075ff; margin-right: 3px;}
+2
View File
@@ -0,0 +1,2 @@
#readOnlyOfProduct {font-size: 13px; color: #5e626d;}
#readOnlyOfProduct i {font-size: 14px; color: #0075ff; margin-right: 3px;}
@@ -44,9 +44,10 @@
<?php endif;?>
<td class='text-left SRBox'><?php echo $URSR['SRName'];?></td>
<td class='c-actions'>
<?php $disabled = $key == $config->custom->URSR ? "disabled=disabled" : '';?>
<?php $disabled = $key == $config->custom->URSR ? "disabled=disabled" : '';?>
<?php $deleteLink = empty($disabled) ? $this->createLink('custom', 'deleteStoryConcept', "id=$key") : '#';?>
<?php if(common::hasPriv('custom', 'editStoryConcept')) echo html::a($this->createLink('custom', 'editStoryConcept', "id=$key", '', true), "<i class='icon icon-edit'></i>", '', "class='btn iframe' data-width=" . ($this->config->URAndSR ? "480px" : "330px") . " title={$lang->edit}");?>
<?php if(common::hasPriv('custom', 'deleteStoryConcept')) echo html::a($this->createLink('custom', 'deleteStoryConcept', "id=$key"), "<i class='icon icon-trash'></i>", 'hiddenwin', "class='btn' $disabled title={$lang->delete} data-group='admin'");?>
<?php if(common::hasPriv('custom', 'deleteStoryConcept')) echo html::a($deleteLink, "<i class='icon icon-trash'></i>", 'hiddenwin', "class='btn' $disabled title={$lang->delete} data-group='admin'");?>
</td>
</tr>
<?php endforeach;?>
+7 -1
View File
@@ -21,7 +21,13 @@
<label class="radio-inline"><input type="radio" name="execution" value="<?php echo $key?>"<?php echo $key == $checkedKey ? " checked='checked'" : ''?> id="execution<?php echo $key;?>"><?php echo $value;?></label>
<?php endforeach;?>
</td>
<td><span class='alert alert-info no-margin'><?php echo $lang->custom->notice->readOnlyOfExecution;?></span></td>
<td></td>
</tr>
<tr>
<th></th>
<td colspan="2" id="readOnlyOfExecution">
<i class="icon-exclamation-sign"></i>&nbsp;<?php echo $lang->custom->notice->readOnlyOfExecution;?>
</td>
</tr>
<tr>
<th></th>
+7 -1
View File
@@ -21,7 +21,13 @@
<label class="radio-inline"><input type="radio" name="kanban" value="<?php echo $key?>"<?php echo $key == $checkedKey ? " checked='checked'" : ''?> id="kanban<?php echo $key;?>"><?php echo $value;?></label>
<?php endforeach;?>
</td>
<td><span class='alert alert-info no-margin'><?php echo $lang->custom->notice->readOnlyOfKanban;?></span></td>
<td></td>
</tr>
<tr>
<th></th>
<td colspan="2" id="readOnlyOfKanban">
<i class="icon-exclamation-sign"></i>&nbsp;<?php echo $lang->custom->notice->readOnlyOfKanban;?>
</td>
</tr>
<tr>
<th></th>
+7 -1
View File
@@ -21,7 +21,13 @@
<label class="radio-inline"><input type="radio" name="product" value="<?php echo $key?>"<?php echo $key == $checkedKey ? " checked='checked'" : ''?> id="product<?php echo $key;?>"><?php echo $value;?></label>
<?php endforeach;?>
</td>
<td><div class='alert alert-info no-margin'><?php echo $lang->custom->notice->readOnlyOfProduct;?></div></td>
<td></td>
</tr>
<tr>
<th></th>
<td colspan="2" id="readOnlyOfProduct">
<i class="icon-exclamation-sign"></i>&nbsp;<?php echo $lang->custom->notice->readOnlyOfProduct;?>
</td>
</tr>
<tr>
<th></th>
+6 -6
View File
@@ -54,9 +54,9 @@
<?php echo $api['desc'];?>
<table class='table table-bordered'>
<tr>
<td><?php echo $lang->dev->params?></td>
<td><?php echo $lang->dev->type?></td>
<td><?php echo $lang->dev->desc?></td>
<th><?php echo $lang->dev->params?></th>
<th><?php echo $lang->dev->type?></th>
<th><?php echo $lang->dev->desc?></th>
</tr>
<?php if(isset($api['param'])):?>
<?php foreach($api['param'] as $param):?>
@@ -78,9 +78,9 @@
<table class='table table-bordered'>
<caption><?php echo $lang->dev->post;?></caption>
<tr>
<td><?php echo $lang->dev->params?></td>
<td><?php echo $lang->dev->type?></td>
<td><?php echo $lang->dev->desc?></td>
<th><?php echo $lang->dev->params?></th>
<th><?php echo $lang->dev->type?></th>
<th><?php echo $lang->dev->desc?></th>
</tr>
<?php foreach($config->dev->postParams[$selectedModule][$methodName] as $paramName => $paramType):?>
<tr>
+69 -41
View File
@@ -107,7 +107,7 @@ class execution extends control
* @access public
* @return void
*/
public function task($executionID = 0, $status = 'unclosed', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 100, $pageID = 1)
public function task($executionID = 0, $status = 'all', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
$this->loadModel('tree');
$this->loadModel('search');
@@ -219,8 +219,14 @@ class execution extends control
$showModule = !empty($this->config->datatable->executionTask->showModule) ? $this->config->datatable->executionTask->showModule : '';
$this->view->modulePairs = $showModule ? $this->tree->getModulePairs($executionID, 'task', $showModule) : array();
$allTasksNum = $this->dao->select('COUNT(id) AS count')->from(TABLE_TASK)
->where('execution')->eq($executionID)
->andWhere('deleted')->eq(0)
->fetch();
/* Assign. */
$this->view->tasks = $tasks;
$this->view->allTasksNum = $allTasksNum;
$this->view->summary = $this->execution->summary($tasks);
$this->view->tabID = 'task';
$this->view->pager = $pager;
@@ -1364,10 +1370,13 @@ class execution extends control
*
* @param int $executionID
* @param string $type
* @param string $withWeekend
* @param string $begin
* @param string $end
* @access public
* @return void
*/
public function cfd($executionID = 0, $type = 'story', $withWeekend = 'false')
public function cfd($executionID = 0, $type = 'story', $withWeekend = 'false', $begin = '', $end = '')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
@@ -1375,7 +1384,30 @@ class execution extends control
$this->loadModel('kanban');
$this->app->loadClass('date');
list($begin, $end) = $this->execution->getBeginEnd4CFD($execution);
if(!empty($_POST))
{
$begin = htmlspecialchars($this->post->begin, ENT_QUOTES);
$end = htmlspecialchars($this->post->end, ENT_QUOTES);
if(empty($begin)) return $this->sendError(sprintf($this->lang->error->notempty, $this->lang->execution->charts->cfd->begin));
if(empty($end)) return $this->sendError(sprintf($this->lang->error->notempty, $this->lang->execution->charts->cfd->end));
if($begin >= $end) return $this->sendError($this->lang->execution->charts->cfd->errorBegin);
if(date("Y-m-d", strtotime("-3 months", strtotime($end))) > $begin) return $this->sendError($this->lang->execution->charts->cfd->errorDateRange);
$this->execution->computeCFD($executionID);
$this->execution->checkCFDData($executionID, $begin);
return print(js::locate($this->createLink('execution', 'cfd', "executionID=$executionID&type=$type&withWeekend=$withWeekend&begin=" . helper::safe64Encode(urlencode($begin)) . "&end=" . helper::safe64Encode(urlencode($end))), 'parent'));
}
if($begin and $end)
{
$begin = urldecode(helper::safe64Decode($begin));
$end = urldecode(helper::safe64Decode($end));
}
else
{
list($begin, $end) = $this->execution->getBeginEnd4CFD($execution);
}
$dateList = date::getDateList($begin, $end, 'Y-m-d', $withWeekend == 'false'? 'noweekend' : '');
//list($cycleTimeAvg, $throughput) = $this->execution->getCFDStatistics($executionID, $dateList, $type);
@@ -1390,6 +1422,8 @@ class execution extends control
$this->view->executionName = $execution->name;
$this->view->executionID = $executionID;
$this->view->chartData = $chartData;
$this->view->begin = $begin;
$this->view->end = $end;
$this->display();
}
@@ -2442,11 +2476,12 @@ class execution extends control
unset($this->lang->kanban->type['all']);
}
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy);
if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'pri_asc';
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
if(empty($kanbanGroup))
{
$this->kanban->createExecutionLane($executionID, $browseType, $groupBy);
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy);
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
}
/* Determines whether an object is editable. */
@@ -3194,15 +3229,16 @@ class execution extends control
* @param int $executionID
* @param int $storyID
* @param string $confirm yes|no
* @param string $from taskkanban
* @access public
* @return void
*/
public function unlinkStory($executionID, $storyID, $confirm = 'no')
public function unlinkStory($executionID, $storyID, $confirm = 'no', $from = '')
{
if($confirm == 'no')
{
$tip = $this->app->rawModule == 'projectstory' ? $this->lang->execution->confirmUnlinkExecutionStory : $this->lang->execution->confirmUnlinkStory;
return print(js::confirm($tip, $this->createLink('execution', 'unlinkstory', "executionID=$executionID&storyID=$storyID&confirm=yes")));
return print(js::confirm($tip, $this->createLink('execution', 'unlinkstory', "executionID=$executionID&storyID=$storyID&confirm=yes&from=$from")));
}
else
{
@@ -3224,26 +3260,23 @@ class execution extends control
return $this->send($response);
}
$execution = $this->execution->getByID($executionID);
if($this->app->tab == 'execution')
$execution = $this->execution->getByID($executionID);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent', '', "parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($executionID, $execLaneType, $execGroupBy);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent', '', "parent.updateKanban(\"story\", $kanbanData)"));
}
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent', '', "parent.updateKanban($kanbanData)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($executionID, $execLaneType, $execGroupBy);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent', '', "parent.updateKanban(\"story\", $kanbanData)"));
}
return print(js::reload('parent'));
@@ -3635,18 +3668,6 @@ class execution extends control
$from = $this->app->tab;
if($from == 'execution') $this->session->set('executionList', $this->app->getURI(true), 'execution');
if($from == 'project')
{
$projects = $this->project->getPairsByProgram();
$projectID = $this->project->saveState($projectID, $projects);
$project = $this->loadModel('project')->getByID($projectID);
$this->view->project = $project;
$this->project->setMenu($projectID);
if(!$projectID) return print(js::locate($this->createLink('project', 'browse')));
if(!empty($project->model) and $project->model == 'kanban' and !(defined('RUN_MODE') and RUN_MODE == 'api')) return print(js::locate($this->createLink('project', 'index', "projectID=$projectID")));
}
if($this->app->viewType == 'mhtml')
{
if($this->app->rawModule == 'project' and $this->app->rawMethod == 'execution')
@@ -3680,6 +3701,9 @@ class execution extends control
$parents = array();
if($parentIdList) $parents = $this->execution->getByIdList($parentIdList);
$allExecutionsNum = $this->project->getStats($projectID, 'all');
$this->view->allExecutionsNum = count($allExecutionsNum);
$this->view->executionStats = $executionStats;
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID);
$this->view->productID = $productID;
@@ -3939,8 +3963,8 @@ class execution extends control
$projectID = $this->dao->findByID($executionID)->from(TABLE_EXECUTION)->fetch('project');
$planStories = array_keys($planStory);
$this->execution->linkStory($executionID, $planStories, $planProducts, $extra);
if($this->config->systemMode == 'new' and $executionID != $projectID) $this->execution->linkStory($projectID, $planStories, $planProducts);
$this->execution->linkStory($executionID, $planStories, $planProducts, $extra);
}
$moduleName = 'execution';
@@ -4072,11 +4096,15 @@ class execution extends control
* @param string $groupBy
* @param string $from execution|RD
* @param string $searchValue
* @param string $orderBy
* @access public
* @return array
*/
public function ajaxUpdateKanban($executionID = 0, $enterTime = '', $browseType = '', $groupBy = '', $from = 'execution', $searchValue = '')
public function ajaxUpdateKanban($executionID = 0, $enterTime = '', $browseType = '', $groupBy = '', $from = 'execution', $searchValue = '', $orderBy = 'id_asc')
{
$this->loadModel('kanban');
if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'pri_asc';
$enterTime = date('Y-m-d H:i:s', $enterTime);
$lastEditedTime = $this->dao->select("max(lastEditedTime) as lastEditedTime")->from(TABLE_KANBANLANE)->where('execution')->eq($executionID)->fetch('lastEditedTime');
@@ -4084,7 +4112,7 @@ class execution extends control
if($from == 'RD') $this->session->set('rdSearchValue', $searchValue);
if(strtotime($lastEditedTime) < 0 or $lastEditedTime > $enterTime or $groupBy != 'default' or !empty($searchValue))
{
$kanbanGroup = $from == 'execution' ? $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue) : $this->loadModel('kanban')->getRDKanban($executionID, $browseType, 'id_asc', 0, $groupBy, $searchValue);
$kanbanGroup = $from == 'execution' ? $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue, $orderBy) : $this->kanban->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy, $searchValue);
return print(json_encode($kanbanGroup));
}
}
+6
View File
@@ -1,4 +1,6 @@
.main-content > h2 {font-size: 16px;}
#mainContent h2 .icon-help {font-size: 16px; vertical-align: text-bottom;}
.tooltip-inner {font-size: 13px; letter-spacing: 1px; line-height: 22px; max-width: 400px !important; text-align: left;}
.container {max-width: 1700px !important}
.pull-left .input-control {margin-right: 10px;}
@@ -29,3 +31,7 @@
#burnStatistics .bg-primary {margin-right: 5px;}
#burnStatistics .icon-help {color: black;}
#burnStatistics h3 {margin-top: 10px; text-indent: 10px; margin-bottom: 30px;}
#cfdDateSelect {float: left;}
#cfdDateSelect .input-group {width: 300px; float: left;}
#datePreview {float: left; margin-left: 10px;}
+1
View File
@@ -36,6 +36,7 @@
.region .kanban-header-col > .title {max-width: 80% !important;}
.region .kanban-header-col > .title {margin: 0}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 85px !important;}
.region .kanban-header-col > .title > span.storyColumn {max-width: 100% !important;}
.region .kanban-header-col > .title > .text-grey {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .count {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .error {color: #333333; padding-left: 2px; font-size: 10px; padding-top: 1px; padding-right: 2px;}
+1
View File
@@ -29,6 +29,7 @@
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
.kanban-header-col > .title > .text {max-width: 110px !important;}
.kanban-header-col > .title > .storyColumn.text {max-width: 100% !important;}
#kanbanContainer {margin: 0;}
#kanbanContainer > .panel-body {overflow: auto;}
+6 -1
View File
@@ -132,6 +132,11 @@ $(function()
{
var type = $('#type').val();
withWeekend = withWeekend == 'true' ? 'false' : 'true';
location.href = createLink('execution', 'cfd', 'executionID=' + executionID + '&type=' + type + '&withWeekend=' + withWeekend);
var begin = Base64.encode(encodeURIComponent($('#begin').val()));
var end = Base64.encode(encodeURIComponent($('#end').val()));
location.href = createLink('execution', 'cfd', 'executionID=' + executionID + '&type=' + type + '&withWeekend=' + withWeekend + '&begin=' + begin + '&end=' + end);
});
$("#end, #begin").datetimepicker('setEndDate', today)
$('.datetimepicker-days table tfoot').append('<tr><th colspan="7">' + dateRangeTip + '</th></tr>');
});
+36 -5
View File
@@ -322,7 +322,7 @@ function updateRegionName(regionID, name)
*/
function updateLaneName(laneID, name)
{
$('.kanban-lane[data-id="' + laneID + '"] > .kanban-lane-name > span').text(name);
$('.kanban-lane[data-id="' + laneID + '"] > .kanban-lane-name > span').text(name).attr('title', name);
}
/**
@@ -349,7 +349,7 @@ function updateLaneColor(laneID, color)
*/
function updateColumnName(columnID, name, color)
{
$('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).css('color', color);
$('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).attr('title', name).css('color', color);
}
/**
@@ -547,6 +547,15 @@ function renderEstStarted(estStarted, status)
*/
function renderStoryItem(item, $item, col)
{
if(groupBy == 'story' && item.id == '0')
{
$('.storyCell').css('width', '100%');
$parentItem = $item[0] == undefined ? $('.storyCell') : $item.parent();
$parentItem.addClass('text-center storyCell');
$parentItem.css('line-height', ($parentItem.parent().height() - 20) + 'px');
$item.replaceWith('<span class="text-muted">' + item.title + '</span>');
return;
}
var scaleSize = window.kanbanScaleSize;
if(+$item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
@@ -749,6 +758,14 @@ addColumnRenderer('task', renderTaskItem);
*/
function renderCount($count, count, column)
{
if(groupBy == 'story' && column.type == 'story')
{
$count.prev().addClass('storyColumn');
$count.prev().parent().append('<span class="caret changeOrderBy"></span>');
$count.remove();
return;
}
/* Render WIP. */
var limit = !column.limit || column.limit == '-1' ? '<i class="icon icon-md icon-infinite"></i>' : column.limit;
if($count.parent().find('.limit').length)
@@ -793,6 +810,9 @@ function tips()
*/
function renderHeaderCol($column, column, $header, kanbanData)
{
if(groupBy == 'story' && column.type == 'story') return;
console.log(groupBy);
console.log(column.type);
/* Render group header. */
var privs = kanbanData.actions;
var columnPrivs = kanbanData.columns[0].actions;
@@ -850,6 +870,11 @@ function renderHeaderCol($column, column, $header, kanbanData)
*/
function renderLaneName($lane, lane, $kanban, columns, kanban)
{
if(groupBy == 'story')
{
$lane.hide();
return;
}
if(groupBy != 'default') return;
var canEditLaneColor = lane.actions.includes('editLaneColor');
var canEditLaneName = lane.actions.includes('editLaneName');
@@ -1375,7 +1400,7 @@ function handleSortCards(event)
}
else
{
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD'), function(data)
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD' + '&serachValue=' + rdSearchValue + '&orderBy=' + orderBy), function(data)
{
if(data && lastUpdateData !== data)
{
@@ -1567,6 +1592,12 @@ $(function()
$('.color0 .cardcolor').css('border', '1px solid #fff');
});
$(document).on('click', '#kanban span.caret.changeOrderBy', function(event)
{
orderBy = orderBy == 'pri_desc' ? 'pri_asc' : 'pri_desc';
searchCards(rdSearchValue);
})
/* Init sortable */
var sortType = '';
var $cards = null;
@@ -1694,7 +1725,7 @@ $(function()
{
if(rdSearchValue == '')
{
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue), function(data)
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue + '&orderBy=' + orderBy), function(data)
{
if(lastUpdateData == '') lastUpdateData = data;
if(data && lastUpdateData !== data)
@@ -1800,7 +1831,7 @@ function toggleRDSearchBox()
function searchCards(value)
{
rdSearchValue = value;
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue), function(data)
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=RD&searchValue=' + rdSearchValue + '&orderBy=' + orderBy), function(data)
{
lastUpdateData = data;
kanbanData = $.parseJSON(data);
+49 -21
View File
@@ -9,7 +9,7 @@
*/
function updateColumnName(columnID, name, color)
{
$('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).css('color', color);
$('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).attr('title', name).css('color', color);
}
/**
@@ -43,17 +43,17 @@ function renderUserAvatar(user, objectType, objectID, size, objectStatus)
if(objectType == 'task')
{
if(!priv.canAssignTask && !user) return $noPrivAndNoAssigned;
var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID, '', true);
var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID + '&kanbanGroup=default&from=taskkanban', '', true);
}
if(objectType == 'story')
{
if(!priv.canAssignStory && !user) return $noPrivAndNoAssigned;
var link = createLink('story', 'assignto', 'id=' + objectID, '', true);
var link = createLink('story', 'assignto', 'id=' + objectID + '&kanbanGroup=default&from=taskkanban', '', true);
}
if(objectType == 'bug')
{
if(!priv.canAssignBug && !user) return $noPrivAndNoAssigned;
var link = createLink('bug', 'assignto', 'id=' + objectID, '', true);
var link = createLink('bug', 'assignto', 'id=' + objectID + '&kanbanGroup=default&from=taskkanban', '', true);
}
if(!user) return objectStatus == 'closed' ? '' : $('<a class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" title="' + noAssigned + '" style="background: #ccc" href="' + link + '"><i class="icon icon-person"></i></a>');
@@ -128,6 +128,15 @@ function renderEstStarted(estStarted, status)
*/
function renderStoryItem(item, $item, col)
{
if(groupBy == 'story' && item.id == '0')
{
$('.storyCell').css('width', '100%');
$parentItem = $item[0] == undefined ? $('.storyCell') : $item.parent();
$parentItem.addClass('text-center storyCell');
$parentItem.css('line-height', ($parentItem.parent().height() - 20) + 'px');
$item.replaceWith('<span class="text-muted">' + item.title + '</span>');
return;
}
var scaleSize = window.kanbanScaleSize;
if($item.attr('data-scale-size') !== scaleSize) $item.empty().attr('data-scale-size', scaleSize);
@@ -330,6 +339,14 @@ addColumnRenderer('task', renderTaskItem);
*/
function renderColumnCount($count, count, col)
{
if(groupBy == 'story' && col.type == 'story')
{
$count.prev().addClass('storyColumn');
$count.prev().parent().append('<span class="caret changeOrderBy"></span>');
$count.remove();
return;
}
var text = count + '/' + (col.limit < 0 ? '<i class="icon icon-infinite"></i>' : col.limit);
$count.html(text + '<i class="icon icon-arrow-up"></i>');
@@ -370,7 +387,7 @@ function tips()
function renderHeaderCol($col, col, $header, kanban)
{
if(col.asParent) $col = $col.children('.kanban-header-col');
if($col.children('.actions').context != undefined) return;
if($col.children('.actions').context != undefined || (groupBy == 'story' && col.type == 'story')) return;
var $actions = $('<div class="actions createButton" />');
var printStoryButton = printTaskButton = printBugButton = false;
@@ -410,6 +427,11 @@ function renderHeaderCol($col, col, $header, kanban)
*/
function renderLaneName($name, lane, $kanban, columns, kanban)
{
if(groupBy == 'story')
{
$name.hide();
return;
}
if(lane.id != 'story' && lane.id != 'task' && lane.id != 'bug') return false;
if(!$name.children('.actions').length && (priv.canSetLane || priv.canMoveLane))
{
@@ -650,7 +672,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if((fromColType == 'developing' || fromColType == 'wait') && priv.canFinishTask)
{
var link = createLink('task', 'finish', 'taskID=' + objectID, '', true);
var link = createLink('task', 'finish', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true);
showIframe = true;
}
}
@@ -658,7 +680,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if(fromColType == 'developing' && priv.canPauseTask)
{
var link = createLink('task', 'pause', 'taskID=' + objectID, '', true);
var link = createLink('task', 'pause', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true);
showIframe = true;
}
}
@@ -666,17 +688,17 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if((fromColType == 'canceled' || fromColType == 'closed' || fromColType == 'developed') && priv.canActivateTask)
{
var link = createLink('task', 'activate', 'taskID=' + objectID, '', true);
var link = createLink('task', 'activate', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true);
showIframe = true;
}
if(fromColType == 'pause' && priv.canActivateTask)
{
var link = createLink('task', 'restart', 'taskID=' + objectID, '', true);
var link = createLink('task', 'restart', 'taskID=' + objectID + '&from=' + 'taskkanban', '', true);
showIframe = true;
}
if(fromColType == 'wait' && priv.canStartTask)
{
var link = createLink('task', 'start', 'taskID=' + objectID, '', true);
var link = createLink('task', 'start', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true);
showIframe = true;
}
}
@@ -684,7 +706,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if((fromColType == 'developing' || fromColType == 'wait' || fromColType == 'pause') && priv.canCancelTask)
{
var link = createLink('task', 'cancel', 'taskID=' + objectID, '', true);
var link = createLink('task', 'cancel', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true);
showIframe = true;
}
}
@@ -692,7 +714,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if((fromColType == 'developed' || fromColType == 'canceled') && priv.canCloseTask)
{
var link = createLink('task', 'close', 'taskID=' + objectID, '', true);
var link = createLink('task', 'close', 'taskID=' + objectID + '&extra=from=' + 'taskkanban', '', true);
showIframe = true;
}
}
@@ -705,7 +727,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if(fromColType == 'unconfirmed' && priv.canConfirmBug)
{
var link = createLink('bug', 'confirmBug', 'bugID=' + objectID, '', true);
var link = createLink('bug', 'confirmBug', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true);
showIframe = true;
}
}
@@ -714,7 +736,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
if(fromColType == 'confirmed' || fromColType == 'unconfirmed') moveCard = true;
if((fromColType == 'closed' || fromColType == 'fixed' || fromColType == 'testing' || fromColType == 'tested') && priv.canActivateBug)
{
var link = createLink('bug', 'activate', 'bugID=' + objectID, '', true);
var link = createLink('bug', 'activate', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true);
showIframe = true;
}
}
@@ -722,7 +744,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if(fromColType == 'fixing' || fromColType == 'confirmed' || fromColType == 'unconfirmed')
{
var link = createLink('bug', 'resolve', 'bugID=' + objectID, '', true);
var link = createLink('bug', 'resolve', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true);
showIframe = true;
}
}
@@ -738,7 +760,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if(fromColType == 'testing' || fromColType == 'tested')
{
var link = createLink('bug', 'close', 'bugID=' + objectID, '', true);
var link = createLink('bug', 'close', 'bugID=' + objectID + '&extra=&from=taskkanban', '', true);
showIframe = true;
}
}
@@ -769,7 +791,7 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
{
if(toColType == 'closed' && priv.canCloseStory)
{
var link = createLink('story', 'close', 'storyID=' + objectID, '', true);
var link = createLink('story', 'close', 'storyID=' + objectID + '&from=taskkanban', '', true);
showIframe = true;
}
else
@@ -930,7 +952,7 @@ function createColumnCreateMenu(options)
if(col.laneType == 'story')
{
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID, '', true), className: 'iframe'});
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&objectID=' + executionID, '', true), className: 'iframe'});
if(priv.canBatchCreateStory) items.push({label: executionLang.batchCreateStory, url: $.createLink('story', 'batchcreate', 'productID=' + productID + '&branch=0&moduleID=0&storyID=0&executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}});
if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID, '', true), className: 'iframe', attrs: {'data-width': '90%'}});
if(priv.canLinkStoryByPlan) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}});
@@ -1167,7 +1189,7 @@ function handleSortCards(event)
}
else
{
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution'), function(data)
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution' + '&searchValue=' + searchValue + '&orderBy=' + orderBy), function(data)
{
if(data && lastUpdateData !== data)
{
@@ -1252,6 +1274,12 @@ $(function()
event.preventDefault();
});
$(document).on('click', '#kanbans span.caret.changeOrderBy', function(event)
{
orderBy = orderBy == 'pri_desc' ? 'pri_asc' : 'pri_desc';
searchCards(searchValue);
})
/* Init contextmenu */
$('#kanbans').on('click', '[data-contextmenu]', function(event)
{
@@ -1311,7 +1339,7 @@ $(function()
lastUpdateData = '';
setInterval(function()
{
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + searchValue), function(data)
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=" + entertime + "&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + searchValue + '&orderBy' + orderBy), function(data)
{
if(lastUpdateData == '') lastUpdateData = data;
if(data && lastUpdateData !== data)
@@ -1447,7 +1475,7 @@ function toggleSearchBox()
function searchCards(value)
{
searchValue = value;
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + value), function(data)
$.get(createLink('execution', 'ajaxUpdateKanban', "executionID=" + executionID + "&entertime=0&browseType=" + browseType + "&groupBy=" + groupBy + '&from=execution&searchValue=' + value + '&orderBy=' + orderBy), function(data)
{
lastUpdateData = data;
var kanbanData = $.parseJSON(data);
+11
View File
@@ -431,11 +431,22 @@ $lang->execution->charts->burn->graph->actuality = 'Aktualität';
$lang->execution->charts->burn->graph->delay = 'Delay';
$lang->execution->charts->cfd = new stdclass();
$lang->execution->charts->cfd->cfdTip = "<p>
1. The CFD(Cumulative Flow Diagram)reflects the trend of accumulated workload at each stage over time.<br>
2. The horizontal axis represents the date, and the vertical axis represents the number of work items.<br>
3. To learn about the team's delivery, you can calculate the WIP quantity, delivery rate and average lead time through the CFD.
<p>";
$lang->execution->charts->cfd->cycleTime = 'Average cycle time';
$lang->execution->charts->cfd->cycleTimeTip = 'Average cycle time of each card from development start to completion';
$lang->execution->charts->cfd->throughput = 'Throughput Rate';
$lang->execution->charts->cfd->throughputTip = 'Throughput Rate = WIP / Average cycle time';
$lang->execution->charts->cfd->begin = 'Begin';
$lang->execution->charts->cfd->end = 'End';
$lang->execution->charts->cfd->errorBegin = 'The start time cannot be greater than the end time.';
$lang->execution->charts->cfd->errorDateRange = 'The Cumulative Flow Diagram(CFD) only provides data display within 3 months.';
$lang->execution->charts->cfd->dateRangeTip = 'CFD only shows the data within 3 months';
$lang->execution->placeholder = new stdclass();
$lang->execution->placeholder->code = 'Abkurzung des Projektnamens';
$lang->execution->placeholder->totalLeft = 'Schätzungen zu Beginn des Projekts.';
+11
View File
@@ -431,11 +431,22 @@ $lang->execution->charts->burn->graph->actuality = 'Actual';
$lang->execution->charts->burn->graph->delay = 'Delay';
$lang->execution->charts->cfd = new stdclass();
$lang->execution->charts->cfd->cfdTip = "<p>
1. The CFD(Cumulative Flow Diagram)reflects the trend of accumulated workload at each stage over time.<br>
2. The horizontal axis represents the date, and the vertical axis represents the number of work items.<br>
3. To learn about the team's delivery, you can calculate the WIP quantity, delivery rate and average lead time through the CFD.
<p>";
$lang->execution->charts->cfd->cycleTime = 'Average cycle time';
$lang->execution->charts->cfd->cycleTimeTip = 'Average cycle time of each card from development start to completion';
$lang->execution->charts->cfd->throughput = 'Throughput Rate';
$lang->execution->charts->cfd->throughputTip = 'Throughput Rate = WIP / Average cycle time';
$lang->execution->charts->cfd->begin = 'Begin';
$lang->execution->charts->cfd->end = 'End';
$lang->execution->charts->cfd->errorBegin = 'The start time cannot be greater than the end time.';
$lang->execution->charts->cfd->errorDateRange = 'The Cumulative Flow Diagram(CFD) only provides data display within 3 months.';
$lang->execution->charts->cfd->dateRangeTip = 'CFD only shows the data within 3 months';
$lang->execution->placeholder = new stdclass();
$lang->execution->placeholder->code = "Abbreviation of {$lang->executionCommon} name";
$lang->execution->placeholder->totalLeft = "Hours estimated on the first day of the {$lang->executionCommon}.";
+10
View File
@@ -434,11 +434,21 @@ $lang->execution->charts->burn->graph->actuality = 'Actuel';
$lang->execution->charts->burn->graph->delay = 'Delay';
$lang->execution->charts->cfd = new stdclass();
$lang->execution->charts->cfd->cfdTip = "<p>
1. Le CFD(Cumulative Flow Diagram)indique la tendance de la charge de travail cumulée de chaque étape au fil du temps.<br>
2. L'axe horizontal représente la date et l'axe vertical représente le nombre de travaux.<br>
3. Ce CFD vous permet de calculer les quantités de travail en cours (WIP), les taux de livraison et les délais moyens pour comprendre comment votre équipe travaille.<p>";
$lang->execution->charts->cfd->cycleTime = 'Average cycle time';
$lang->execution->charts->cfd->cycleTimeTip = 'Average cycle time of each card from development start to completion';
$lang->execution->charts->cfd->throughput = 'Throughput Rate';
$lang->execution->charts->cfd->throughputTip = 'Throughput Rate = WIP / Average cycle time';
$lang->execution->charts->cfd->begin = 'Begin';
$lang->execution->charts->cfd->end = 'End';
$lang->execution->charts->cfd->errorBegin = 'The start time cannot be greater than the end time.';
$lang->execution->charts->cfd->errorDateRange = 'The Cumulative Flow Diagram(CFD) only provides data display within 3 months.';
$lang->execution->charts->cfd->dateRangeTip = 'CFD only shows the data within 3 months';
$lang->execution->placeholder = new stdclass();
$lang->execution->placeholder->code = "Abréviation du nom du {$lang->executionCommon}";
$lang->execution->placeholder->totalLeft = "Heures estimées le premier jour du {$lang->executionCommon}.";
+10
View File
@@ -431,11 +431,21 @@ $lang->execution->charts->burn->graph->actuality = '实际';
$lang->execution->charts->burn->graph->delay = '延期';
$lang->execution->charts->cfd = new stdclass();
$lang->execution->charts->cfd->cfdTip = "<p>
1.累积流图反应各个阶段累积处理的工作项数量随时间的变化趋势。</br>
2.横轴代表日期,纵轴代表工作项数量。</br>
3.通过此图可计算出在制品数量,交付速率以及平均前置时间,从而了解团队的交付情况。</p>";
$lang->execution->charts->cfd->cycleTime = '平均周期时间';
$lang->execution->charts->cfd->cycleTimeTip = '平均每个卡片从开发启动到完成的周期时间';
$lang->execution->charts->cfd->throughput = '吞吐率';
$lang->execution->charts->cfd->throughputTip = '吞吐率 = 在制品 / 平均周期时间';
$lang->execution->charts->cfd->begin = '开始日期';
$lang->execution->charts->cfd->end = '结束日期';
$lang->execution->charts->cfd->errorBegin = '开始日期应小于结束日期';
$lang->execution->charts->cfd->errorDateRange = '累积流图只提供3个月内的数据展示';
$lang->execution->charts->cfd->dateRangeTip = '累积流图只展示3个月内的数据';
$lang->execution->placeholder = new stdclass();
$lang->execution->placeholder->code = '团队内部的简称';
$lang->execution->placeholder->totalLeft = "{$lang->executionCommon}开始时的总预计工时";
+134 -1
View File
@@ -79,7 +79,7 @@ class executionModel extends model
$executions = $this->getPairs(0, 'all', 'nocode');
if(!$executionID and $this->session->execution) $executionID = $this->session->execution;
if(!$executionID or !in_array($executionID, array_keys($executions))) $executionID = key($executions);
$this->session->set('execution', $executionID);
$this->session->set('execution', $executionID, $this->app->tab);
/* Unset story, bug, build and testtask if type is ops. */
if($execution and $execution->type == 'stage' and $this->config->systemMode == 'new')
@@ -3072,6 +3072,42 @@ class executionModel extends model
}
}
/**
* Check whether there is data on the specified date of execution, and there is no data with the latest date added.
*
* @param int $executionID
* @param string $date
* @access public
* @return void
*/
public function checkCFDData($executionID, $date)
{
$today = helper::today();
if($date >= $today) return;
$checkData = $this->dao->select("date, `count` AS value, `name`")->from(TABLE_CFD)
->where('execution')->eq((int)$executionID)
->andWhere('date')->eq($date)
->orderBy('date DESC, id asc')->fetchGroup('name', 'date');
if(!$checkData)
{
$closetoDate = $this->dao->select("max(date) as date")->from(TABLE_CFD)->where('execution')->eq((int)$executionID)->andWhere('date')->lt($date)->fetch('date');
if($closetoDate)
{
$copyData = $this->dao->select("*")->from(TABLE_CFD)
->where('execution')->eq((int)$executionID)
->andWhere('date')->eq($closetoDate)
->fetchAll();
foreach($copyData as $data)
{
unset($data->id);
$data->date = $date;
$this->dao->replace(TABLE_CFD)->data($data)->exec();
}
}
}
}
/**
* Fix burn for first day.
*
@@ -4269,6 +4305,103 @@ class executionModel extends model
return $html;
}
/**
* Print execution nested list.
*
* @param object $execution
* @param int $isChild
* @param array $users
* @access public
* @return void
*/
public function printNestedList($execution, $isChild = false, $users, $productID)
{
$this->loadModel('task');
$this->loadModel('execution');
$this->loadModel('programplan');
if(!$isChild)
{
$trClass = 'is-top-level table-nest-child-hide';
$trAttrs = "data-id='$execution->id' data-order='$execution->order' data-nested='true'";
}
else
{
$trClass = 'is-nest-child';
$trAttrs = "data-id={$execution->id} data-parent={$execution->parent}";
$trAttrs .= " data-nest-parent='$execution->parent' data-order='$execution->order' data-nest-path=',$execution->parent,$execution->id,'";
}
$burns = join(',', $execution->burns);
echo "<tr $trAttrs class=$trClass>";
echo "<td><span id=$execution->id class='table-nest-icon icon table-nest-toggle'></span>";
if($this->config->systemMode == 'new')
{
$spanClass = $execution->type == 'stage' ? 'label-warning' : 'label-info';
echo "<span class='project-type-label label label-outline $spanClass'>{$this->lang->execution->typeList[$execution->type]}</span> ";
}
echo html::a(helper::createLink('execution', 'view', "executionID=$execution->id"), $execution->name);
echo '<td>' . zget($users, $execution->PM) . '</td>';
echo "<td class='status-{$execution->status}'>" . zget($this->lang->project->statusList, $execution->status) . '</td>';
echo '<td>' . html::ring($execution->hours->progress) . '</td>';
echo '<td>' . $execution->begin . '</td>';
echo '<td>' . $execution->end . '</td>';
echo "<td class='hours' title='{$execution->hours->totalEstimate}{$this->lang->execution->workHour}'>" . $execution->hours->totalEstimate . $this->lang->execution->workHourUnit . '</td>';
echo "<td class='hours' title='{$execution->hours->totalConsumed}{$this->lang->execution->workHour}'>" . $execution->hours->totalConsumed . $this->lang->execution->workHourUnit . '</td>';
echo "<td class='hours' title='{$execution->hours->totalLeft}{$this->lang->execution->workHour}'>" . $execution->hours->totalLeft . $this->lang->execution->workHourUnit . '</td>';
echo "<td id='spark-{$execution->id}' class='sparkline text-left no-padding' values='$burns'></td>";
echo '<td class="c-actions">';
common::printIcon('execution', 'start', "executionID={$execution->id}", $execution, 'list', '', '', 'iframe', true);
$class = !empty($execution->children) ? 'disabled' : '';
common::printIcon('task', 'create', "executionID={$execution->id}", $execution, 'list', '', '', $class, false, "data-app='execution'");
if($execution->type == 'stage')
{
if($execution->grade == 1 && $this->loadModel('programplan')->isCreateTask($execution->id))
{
common::printIcon('programplan', 'create', "program={$execution->parent}&productID=$productID&planID=$execution->id", $execution, 'list', 'split', '', '', '', '', $this->lang->programplan->createSubPlan);
}
else
{
$disabled = ($execution->grade == 2) ? ' disabled' : '';
echo common::hasPriv('programplan', 'create') ? html::a('javascript:alert("' . $this->lang->programplan->error->createdTask . '");', '<i class="icon-programplan-create icon-split"></i>', '', 'class="btn ' . $disabled . '"') : '';
}
}
common::printIcon('programplan', 'edit', "stageID=$execution->id&projectID=$execution->project", $execution, 'list', '', '', 'iframe', true);
$disabled = !empty($execution->children) ? ' disabled' : '';
if($execution->status != 'closed' and common::hasPriv('execution', 'close', $execution))
{
common::printIcon('execution', 'close', "stageID=$execution->id", $execution, 'list', 'off', 'hiddenwin' , $disabled . ' iframe', true, '', $this->lang->execution->close);
}
elseif($execution->status == 'closed' and common::hasPriv('execution', 'activate', $execution))
{
common::printIcon('execution', 'activate', "stageID=$execution->id", $execution, 'list', 'magic', 'hiddenwin' , $disabled . ' iframe', true, '', $this->lang->execution->activate);
}
if(common::hasPriv('execution', 'delete', $execution))
{
common::printIcon('execution', 'delete', "stageID=$execution->id&confirm=no", $execution, 'list', 'trash', 'hiddenwin' , $disabled, '', '', $this->lang->delete);
}
echo '</td>';
echo '</tr>';
if(!empty($execution->children))
{
foreach($execution->children as $child) $this->printNestedList($child, true, $users, $productID);
}
if(!empty($execution->tasks))
{
foreach($execution->tasks as $task)
{
$showmore = (count($execution->tasks) == 50) && ($task == end($execution->tasks));
echo $this->task->buildNestedList($execution, $task, false, $showmore, $users);
}
}
}
/**
* Update user view of execution and it's product.
*
+1 -1
View File
@@ -73,7 +73,7 @@ js::set('isCNLang', !$this->loadModel('common')->checkNotCN())
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $from == 'execution' ? $lang->execution->noExecutions : $lang->execution->noExecution;?></span>
<?php if($status == "all"):?>
<?php if(empty($allExecutionsNum)):?>
<?php if(common::hasPriv('programplan', 'create') and $isStage):?>
<?php echo html::a($this->createLink('programplan', 'create', "projectID=$projectID&productID=$productID"), "<i class='icon icon-plus'></i> " . $lang->programplan->create, '', "class='btn btn-info'");?>
<?php else: ?>
+14 -1
View File
@@ -13,11 +13,14 @@
<?php include '../../common/view/header.html.php';?>
<?php js::import($jsRoot . 'echarts/echarts.common.min.js'); ?>
<?php js::import($jsRoot . 'html2canvas/min.js'); ?>
<?php js::import($jsRoot . 'misc/base64.js');?>
<?php js::set('executionID', $executionID); ?>
<?php js::set('withWeekend', $withWeekend); ?>
<?php js::set('chartData', $chartData); ?>
<?php js::set('YUnit', $lang->execution->count); ?>
<?php js::set('XUnit', $lang->execution->burnXUnit); ?>
<?php js::set('dateRangeTip', $lang->execution->charts->cfd->dateRangeTip); ?>
<?php js::set('today', date(DT_DATE1)); ?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php
@@ -32,10 +35,20 @@
<div class='input-control w-100px'>
<?php echo html::select('type', $lang->execution->cfdTypeList, $type, "class='form-control chosen'");?>
</div>
<div id="cfdDateSelect">
<form method='post' target='hiddenwin' class='not-watch'>
<div class='input-group'>
<?php echo html::input('begin', $begin, "class='form-control form-date' onchange='$(\"#datePreview\").removeClass(\"hidden\")' placeholder='" . $lang->execution->charts->cfd->begin . "'");?>
<span class='input-group-addon'><?php echo $lang->project->to;?></span>
<?php echo html::input('end', $end, "class='form-control form-date' onchange='$(\"#datePreview\").removeClass(\"hidden\")' placeholder='" . $lang->execution->charts->cfd->end . "'");?>
</div>
<?php echo html::commonButton($lang->preview, "onclick='$(this).closest(\"form\").submit()' id='datePreview'", 'btn btn-primary hidden');?>
</form>
</div>
</div>
</div>
<div id='mainContent' class='main-content'>
<h2 class='text-center'><?php echo $executionName . ' - ' . zget($lang->execution->cfdTypeList, $type) . $lang->execution->CFD;?></h2>
<h2 class='text-center'><?php echo $executionName . ' - ' . zget($lang->execution->cfdTypeList, $type) . $lang->execution->CFD;?> <i class="icon icon-help" data-toggle="tooltip" data-tip-class="tooltip-help" data-html='true' data-placement="bottom" title="<?php echo $lang->execution->charts->cfd->cfdTip;?>"></i></h2>
<?php if(isset($chartData['labels']) and count($chartData['labels']) != 1): ?>
<div id="cfdWrapper">
<div id="cfdChart" style="width: 1200px; height: 600px"></div>
+6 -1
View File
@@ -118,6 +118,11 @@ js::set('priv',
)
);
?>
<?php if($groupBy == 'story' and $browseType == 'task'):?>
<style>
.kanban-cols {left: 0px !important;}
</style>
<?php endif;?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class="input-control space c-type">
@@ -143,7 +148,7 @@ js::set('priv',
<div class='btn-toolbar pull-right'>
<div class="input-group" id="rdKanbanSearch">
<div class="input-control search-box" id="rdSearchBox">
<input type="text" name="rdKanbanSearchInput" id="rdKanbanSearchInput" value="" class="form-control" oninput="searchCards(this.value)" placeholder="<?php echo $lang->execution->pleaseInput?>">
<input type="text" name="rdKanbanSearchInput" id="rdKanbanSearchInput" value="" class="form-control" oninput="searchCards(this.value)" placeholder="<?php echo $lang->execution->pleaseInput?>" autocomplete="off">
</div>
</div>
<?php
+1 -1
View File
@@ -198,7 +198,7 @@ body {margin-bottom: 25px;}
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->task->noTask;?></span>
<?php if($canBeChanged and common::hasPriv('task', 'create') and $status == "all"):?>
<?php if($canBeChanged and common::hasPriv('task', 'create') and empty($allTasksNum->count)):?>
<?php echo html::a($taskCreateLink, "<i class='icon icon-plus'></i> " . $lang->task->create, '', "class='btn btn-info'");?>
<?php endif;?>
</p>
+7 -1
View File
@@ -10,6 +10,11 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kanban.html.php';?>
<?php if($groupBy == 'story' and $browseType == 'task'):?>
<style>
.kanban-cols {left: 0px !important;}
</style>
<?php endif;?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class="input-control space c-type">
@@ -35,7 +40,7 @@
<div class='btn-toolbar pull-right'>
<div class="input-group" id="taskKanbanSearch">
<div class="input-control search-box" id="searchBox">
<input type="text" name="taskKanbanSearchInput" id="taskKanbanSearchInput" value="" class="form-control" oninput="searchCards(this.value)" placeholder="<?php echo $lang->execution->pleaseInput;?>">
<input type="text" name="taskKanbanSearchInput" id="taskKanbanSearchInput" value="" class="form-control" oninput="searchCards(this.value)" placeholder="<?php echo $lang->execution->pleaseInput;?>" autocomplete="off">
</div>
</div>
<?php
@@ -220,4 +225,5 @@ js::set('priv',
<?php js::set('displayCards', $execution->displayCards);?>
<?php js::set('needLinkProducts', $lang->execution->needLinkProducts);?>
<?php js::set('hourUnit', $config->hourUnit);?>
<?php js::set('orderBy', $storyOrder);?>
<?php include '../../common/view/footer.html.php';?>
+9 -1
View File
@@ -280,9 +280,14 @@ class file extends control
$file = $this->file->getById($fileID);
$this->dao->delete()->from(TABLE_FILE)->where('id')->eq($fileID)->exec();
$this->loadModel('action')->create($file->objectType, $file->objectID, 'deletedFile', '', $extra=$file->title);
/* Fix Bug #1518. */
$fileRecord = $this->dao->select('id')->from(TABLE_FILE)->where('pathname')->eq($file->pathname)->fetch();
if(empty($fileRecord)) @unlink($file->realPath);
/* Update test case version for test case synchronization. */
if($file->objectType == 'testcase') $this->file->updateTestcaseVersion($file);
return print(js::reload('parent'));
}
}
@@ -331,9 +336,12 @@ class file extends control
$extension = "." . $file->extension;
$actionID = $this->loadModel('action')->create($file->objectType, $file->objectID, 'editfile', '', $fileName);
$changes[] = array('field' => 'fileName', 'old' => $file->title . $extension, 'new' => $fileName);
$changes[] = array('field' => 'fileName', 'old' => $file->title, 'new' => $fileName);
$this->action->logHistory($actionID, $changes);
/* Update test case version for test case synchronization. */
if($file->objectType == 'testcase' and $file->title != $fileName) $this->file->updateTestcaseVersion($file);
return print(js::reload('parent.parent'));
}
+1 -1
View File
@@ -18,7 +18,7 @@ $lang->file->deleted = 'Deleted';
$lang->file->uploadImages = 'Batch Upload Images';
$lang->file->download = 'Download Files';
$lang->file->uploadDate = 'Uploaded';
$lang->file->edit = 'Rename File';
$lang->file->edit = 'Rename';
$lang->file->inputFileName = 'Enter File Name';
$lang->file->delete = 'Delete File';
$lang->file->label = 'Label:';
+19
View File
@@ -1046,4 +1046,23 @@ class fileModel extends model
->andWhere('deleted')->eq('0')
->fetchPairs();
}
/**
* Update test case version.
*
* @param object $file
* @access public
* @return void
*/
public function updateTestcaseVersion($file)
{
$oldCase = $this->loadModel('testcase')->getByID($file->objectID);
$isLibCase = ($oldCase->lib and empty($oldCase->product));
if($isLibCase)
{
$fromcaseVersion = $this->dao->select('fromCaseVersion')->from(TABLE_CASE)->where('fromCaseID')->eq($file->objectID)->fetch('fromCaseVersion');
$fromcaseVersion += 1;
$this->dao->update(TABLE_CASE)->set('`fromCaseVersion`')->eq($fromcaseVersion)->where('`fromCaseID`')->eq($file->objectID)->exec();
}
}
}
+3 -2
View File
@@ -2,6 +2,7 @@
<style>
.file-input .file-editbox{max-width:40%;}
.file-input-list .input-group-btn{float:left}
.fileAction {color: #0c64eb !important;}
</style>
<?php js::set('dangerExtensions', ',' . $this->config->file->dangers . ',');?>
<?php js::set('maxUploadSize', $maxUploadSize);?>
@@ -25,8 +26,8 @@
<small class="file-size muted"></small>
</div>
<div class="input-group-btn">
<button type="button" class="btn btn-link file-input-rename"><?php echo $lang->file->edit;?></button>
<button type="button" class="btn btn-link file-input-delete"><?php echo $lang->delete;?></button>
<button type="button" class="fileAction btn btn-link file-input-rename"><?php echo $lang->file->edit;?></button>
<button type="button" class="fileAction btn btn-link file-input-delete"><?php echo $lang->delete;?></button>
</div>
</div>
<input type="file" name="<?php echo $filesName;?>[]" onchange="checkDangerExtension(this)" />
+3 -4
View File
@@ -7,8 +7,7 @@
<style>
.files-list>li>a {display: inline; word-wrap: break-word;}
.files-list>li>.right-icon {opacity: 1;}
.files-list>li>.right-icon>a {padding-left: 7px;}
html[lang|="zh"] .files-list>li>.right-icon>a {padding-left: 3px;}
.fileAction {color: #0c64eb !important;}
</style>
<script>
/* Delete a file. */
@@ -93,8 +92,8 @@
if(common::hasPriv($objectType, 'edit', $object))
{
echo "<span class='right-icon'>&nbsp; ";
common::printLink('file', 'edit', "fileID=$file->id", $lang->file->edit, '', "data-width='400' class='edit iframe text-primary' title='{$lang->file->edit}'");
if(common::hasPriv('file', 'delete')) echo html::a('###', $lang->delete, '', "class='text-primary' onclick='deleteFile($file->id)' title='$lang->delete'");
common::printLink('file', 'edit', "fileID=$file->id", $lang->file->edit, '', "data-width='400' class='fileAction btn btn-link edit iframe text-primary' title='{$lang->file->edit}'");
if(common::hasPriv('file', 'delete')) echo html::a('###', $lang->delete, '', "class='fileAction btn btn-link text-primary' onclick='deleteFile($file->id)' title='$lang->delete'");
echo '</span>';
}
echo '</li>';
+6 -7
View File
@@ -8,8 +8,7 @@
<style>
.files-list>li>a {display: inline; word-wrap: break-word;}
.files-list>li>.right-icon {opacity: 1;}
.files-list>li>.right-icon>a {padding-left: 7px;}
html[lang|="zh"] .files-list>li>.right-icon>a {padding-left: 3px;}
.fileAction {color: #0c64eb !important;}
</style>
<script>
/* Delete a file. */
@@ -107,7 +106,7 @@
/* For the open source version of the file judgment. */
if(stripos('txt|jpg|jpeg|gif|png|bmp', $file->extension) !== false)
{
echo html::a($downloadLink, $lang->file->preview, '_blank', "class='text-primary' onclick=\"return downloadFile($file->id, '$file->extension', $imageWidth, '$file->title')\"");
echo html::a($downloadLink, $lang->file->preview, '_blank', "class='fileAction btn btn-link text-primary' onclick=\"return downloadFile($file->id, '$file->extension', $imageWidth, '$file->title')\"");
}
/* For the max version of the file judgment. */
@@ -116,13 +115,13 @@
$officeTypes = 'doc|docx|xls|xlsx|ppt|pptx|pdf';
if(stripos($officeTypes, $file->extension) !== false)
{
echo html::a($downloadLink, $lang->file->preview, '_blank', "class='text-primary' onclick=\"return downloadFile($file->id, '$file->extension', $imageWidth, '$file->title')\"");
echo html::a($downloadLink, $lang->file->preview, '_blank', "class='fileAction btn btn-link text-primary' onclick=\"return downloadFile($file->id, '$file->extension', $imageWidth, '$file->title')\"");
}
}
common::printLink('file', 'download', "fileID=$file->id", $lang->file->downloadFile, '_blank', "class='text-primary' title='{$lang->file->downloadFile}'");
common::printLink('file', 'edit', "fileID=$file->id", $lang->file->edit, '', "data-width='400' class='edit iframe text-primary' title='{$lang->file->edit}'");
if(common::hasPriv('file', 'delete')) echo html::a('###', $lang->delete, '', "class='text-primary' onclick='deleteFile($file->id)' title='$lang->delete'");
common::printLink('file', 'download', "fileID=$file->id", $lang->file->downloadFile, '_blank', "class='fileAction btn btn-link text-primary' title='{$lang->file->downloadFile}'");
common::printLink('file', 'edit', "fileID=$file->id", $lang->file->edit, '', "data-width='400' class='fileAction btn btn-link edit iframe text-primary' title='{$lang->file->edit}'");
if(common::hasPriv('file', 'delete')) echo html::a('###', $lang->delete, '', "class='fileAction btn btn-link text-primary' onclick='deleteFile($file->id)' title='$lang->delete'");
echo '</span>';
}
echo '</li>';
+58 -11
View File
@@ -14,7 +14,7 @@ class kanban extends control
/**
* Kanban space.
*
* @param string $browseType all|my|other|closed
* @param string $browseType involved|cooperation|public|private
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
@@ -201,11 +201,16 @@ class kanban extends control
*
* @param int $spaceID
* @param string $type
* @param int $copyKanbanID
* @param string $extra
* @access public
* @return void
*/
public function create($spaceID = 0, $type = 'private')
public function create($spaceID = 0, $type = 'private', $copyKanbanID = 0, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
if(!empty($_POST))
{
$kanbanID = $this->kanban->create();
@@ -216,6 +221,15 @@ class kanban extends control
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
$enableImport = 'off';
$importObjects = array();
if($copyKanbanID)
{
$copyKanban = $this->kanban->getByID($copyKanbanID);
$enableImport = empty($copyKanban->object) ? 'off' : 'on';
$importObjects = empty($copyKanban->object) ? array() : explode(',', $copyKanban->object);
}
unset($this->lang->kanbanspace->featureBar['involved']);
$space = $this->kanban->getSpaceById($spaceID);
@@ -223,12 +237,18 @@ class kanban extends control
$users = $this->loadModel('user')->getPairs('noclosed|nodeleted', '', 0, $spaceUsers);
$whitelist = (isset($space->whitelist) and !empty($space->whitelist)) ? $space->whitelist : ',';
$this->view->users = $users;
$this->view->whitelist = $this->user->getPairs('noclosed|nodeleted', '', 0, $whitelist);
$this->view->spaceID = $spaceID;
$this->view->spacePairs = array(0 => '') + $this->kanban->getSpacePairs($type);
$this->view->type = $type;
$this->view->typeList = $this->lang->kanbanspace->featureBar;
$this->view->users = $users;
$this->view->whitelist = $this->user->getPairs('noclosed|nodeleted', '', 0, $whitelist);
$this->view->spaceID = $spaceID;
$this->view->spacePairs = array(0 => '') + $this->kanban->getSpacePairs($type);
$this->view->type = $type;
$this->view->typeList = $this->lang->kanbanspace->featureBar;
$this->view->kanbans = array('' => '') + $this->kanban->getPairs();
$this->view->copyKanbanID = $copyKanbanID;
$this->view->copyKanban = $copyKanbanID ? $copyKanban : '';
$this->view->enableImport = $enableImport;
$this->view->importObjects = $importObjects;
$this->view->copyRegion = isset($output['copyRegion']) ? true : false;
$this->display();
}
@@ -386,19 +406,20 @@ class kanban extends control
*
* @param int $kanbanID
* @param string $confirm
* @param string $browseType involved|cooperation|public|private
* @access public
* @return void
*/
public function delete($kanbanID, $confirm = 'no')
public function delete($kanbanID, $confirm = 'no', $browseType = 'involved')
{
if($confirm == 'no')
{
return print(js::confirm($this->lang->kanban->confirmDeleteKanban, $this->createLink('kanban', 'delete', "kanbanID=$kanbanID&confirm=yes")));
return print(js::confirm($this->lang->kanban->confirmDeleteKanban, $this->createLink('kanban', 'delete', "kanbanID=$kanbanID&confirm=yes&browseType=$browseType")));
}
else
{
$this->kanban->delete(TABLE_KANBAN, $kanbanID);
return print(js::locate($this->createLink('kanban', 'space'), 'parent'));
return print(js::locate($this->createLink('kanban', 'space', "browseType=$browseType"), 'parent'));
}
}
@@ -1883,4 +1904,30 @@ class kanban extends control
return print(html::select($field, $lanes, '', "class='form-control'"));
}
/**
* Ajax load space users.
*
* @param int $spaceID
* @param string $field team|whitelist|owner
* @param string $selectedUser
* @access public
* @return string
*/
public function ajaxLoadSpaceUsers($spaceID, $field = '', $selectedUser = '')
{
$space = $this->kanban->getSpaceById($spaceID);
$accounts = '';
if(!empty($space))
{
if($space->type == 'private' and !empty($space->whitelist)) $accounts = $space->whitelist;
if($space->type != 'private') $accounts = trim($space->owner) . ',' . trim($space->team);
}
$users = $this->loadModel('user')->getPairs('noclosed|nodeleted', '', 0, $accounts);
$multiple = in_array($field, array('team', 'whitelist')) ? 'multiple' : '';
$fieldName = $multiple ? $field . '[]' : $field;
return print(html::select($fieldName, $users, $selectedUser, "class='form-control' $multiple"));
}
}
+14
View File
@@ -9,3 +9,17 @@
#mainContent .objectBox .checkbox-primary>label:after {width: 14px; height: 14px;}
[lang^='en'] .columnWidth {width: 120px;}
[lang^='zh-cn'] .columnWidth {width: 80px;}
#copyKanbanModal {padding: 0;}
#copyKanbanModal {padding: 0 2px;}
#copyKanbanModal > div {position: relative;}
#copyKanbanModal a {display: block; min-height: 30px; line-height: 30px; padding: 5px 15px; border: 1px solid #e5e5e5; color: #333; margin: 5px 0; border-radius: 3px;}
#copyKanbanModal a > i {display: inline-block; margin-right: 5px;}
#copyKanbanModal a:hover {border: 1px solid #00a9fc; background-color: #E9F2FB; text-decoration: none;}
#copyKanbanModal a.active {border-color: #00da88; color: #00da88; background-color: #E5FFE6;}
#copyKanbanModal a.active:after {position: absolute; content: '\e92f'; font-family: ZentaoIcon; font-size: 20px; right: 25px;}
#copyKanbanModal a.cancel {color: #ff5d5d;}
#copyKanbanModal .copyContentBox {display: inline-block;}
#copyKanbanModal .copyContentBox > .checkbox-primary {display: inline-block; margin-left: 10px; margin-top: -1px;}
#copyKanbanModal .copyContentBox > .checkbox-primary:first-child {margin-left: 20px;}
#copyContentbasicInfo {cursor: not-allowed;}
+68 -1
View File
@@ -1,6 +1,14 @@
$(function()
{
if(enableImport == 'off') $("input[name^='importObjectList']").attr('disabled', 'disabled');
$("input[id='copyContentbasicInfo']").click(function(){return false;})
copyRegion = $("input[id='copyContentregion']").prop('checked');
$("input[id='copyContentregion']").click(function()
{
copyRegion = $(this).prop('checked');
$('#copyRegion').val(copyRegion);
});
$("input[name='import']").change(function()
{
@@ -30,10 +38,28 @@ $(function()
return false;
}
})
$(document).on('click', '#copyKanbans a', function()
{
setCopyKanban($(this).data('id')); $('#copyKanbanModal').modal('hide');
});
})
/**
* When space type or space value change.
* Set copy kanban.
*
* @param int $kanbanID
* @access public
* @return void
*/
function setCopyKanban(kanbanID)
{
var extra = copyRegion ? '&extra=copyRegion=' + copyRegion : '';
location.href = createLink('kanban', 'create', 'spaceID=' + spaceID + '&type=' + spaceType + '&copyKanbanID=' + kanbanID + extra);
}
/**
* When space type change.
*
* @oaram int spaceID
* @param string type
@@ -45,3 +71,44 @@ function changeValue(spaceID, type)
if(typeof type === 'undefined') type = spaceType;
location.href = createLink('kanban', 'create', 'spaceID=' + spaceID + '&type=' + type);
}
/**
* The team or whitelist member that loads kanban.
*
* @oaram int spaceID
* @access public
* @return void
*/
function loadUsers(spaceID)
{
var field = spaceType == 'private' ? 'whitelist' : 'team';
var link = createLink('kanban', 'ajaxLoadSpaceUsers', 'spaceID='+ spaceID + '&field=' + field + '&selectedUser=' + $('#' + field).val());
$.get(link, function(data)
{
$('#' + field).replaceWith(data);
$('#' + field).next('.picker').remove();
$('#' + field).picker();
});
if(spaceType != 'private') loadOwners(spaceID);
}
/**
* The owners that loads kanban.
*
* @oaram int spaceID
* @access public
* @return void
*/
function loadOwners(spaceID)
{
var link = createLink('kanban', 'ajaxLoadSpaceUsers', 'spaceID='+ spaceID + '&field=owner&selectedUser=' + $('#owner').val());
$.get(link, function(data)
{
$('#owner').replaceWith(data);
$('#owner' + "_chosen").remove();
$('#owner').next('.picker').remove();
$('#owner').chosen();
});
}
+2 -2
View File
@@ -922,7 +922,7 @@ function updateRegionName(regionID, name)
*/
function updateLaneName(laneID, name)
{
$('.kanban-lane[data-id="' + laneID + '"] > .kanban-lane-name > span').text(name);
$('.kanban-lane[data-id="' + laneID + '"] > .kanban-lane-name > span').text(name).attr('title', name);
}
/**
@@ -949,7 +949,7 @@ function updateLaneColor(laneID, color)
*/
function updateColumnName(columnID, name, color)
{
$('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).css('color', color);
$('.kanban-col[data-id="' + columnID + '"] > div.title > span:first').text(name).attr('title', name).css('color', color);
}
/**
+11
View File
@@ -203,6 +203,10 @@ $lang->kanban->setting = 'Setting';
$lang->kanban->my = 'My';
$lang->kanban->other = 'Other';
$lang->kanban->showWIP = 'Show WIP';
$lang->kanban->copyTitle = 'Please select a Kanban to copy';
$lang->kanban->copyNoKanban = 'No Kanban available to copy';
$lang->kanban->cancelCopy = 'Cancel Copying';
$lang->kanban->copyContent = 'Copy Content';
$lang->kanban->error = new stdclass();
$lang->kanban->error->mustBeInt = 'The WIPs must be positive integer.';
@@ -211,6 +215,9 @@ $lang->kanban->error->childLimitNote = 'The sum of products in the child colu
$lang->kanban->error->importObjNotEmpty = 'Please select at least one import object.';
$lang->kanban->error->childLimitEmpty = 'Please set the WIP quantity of the sub column before operation.';
$lang->kanban->orderList['pri_asc'] = 'Story Priority Ascending';
$lang->kanban->orderList['pri_desc'] = 'Story Priority Descending';
$lang->kanban->importList = array();
$lang->kanban->importList['off'] = 'Import is not enabled';
$lang->kanban->importList['on'] = 'Enable the import function, you can only import content that you have permission to view.';
@@ -269,6 +276,10 @@ $lang->kanban->taskColumn['pause'] = 'Pause';
$lang->kanban->taskColumn['canceled'] = 'Canceled';
$lang->kanban->taskColumn['closed'] = 'Closed';
$lang->kanban->copyContentList = array();
$lang->kanban->copyContentList['basicInfo'] = 'Basic Information';
$lang->kanban->copyContentList['region'] = 'Columns and swimlanes';
$lang->kanbanspace = new stdclass();
$lang->kanbanspace->common = 'Kanban Space';
$lang->kanbanspace->name = 'Name';
+11
View File
@@ -203,6 +203,10 @@ $lang->kanban->setting = 'Setting';
$lang->kanban->my = 'My';
$lang->kanban->other = 'Other';
$lang->kanban->showWIP = 'Show WIP';
$lang->kanban->copyTitle = 'Please select a Kanban to copy';
$lang->kanban->copyNoKanban = 'No Kanban available to copy';
$lang->kanban->cancelCopy = 'Cancel Copying';
$lang->kanban->copyContent = 'Copy Content';
$lang->kanban->error = new stdclass();
$lang->kanban->error->mustBeInt = 'The WIPs must be positive integer.';
@@ -211,6 +215,9 @@ $lang->kanban->error->childLimitNote = 'The sum of products in the child colu
$lang->kanban->error->importObjNotEmpty = 'Please select at least one import object.';
$lang->kanban->error->childLimitEmpty = 'Please set the WIP quantity of the sub column before operation.';
$lang->kanban->orderList['pri_asc'] = 'Story Priority Ascending';
$lang->kanban->orderList['pri_desc'] = 'Story Priority Descending';
$lang->kanban->importList = array();
$lang->kanban->importList['off'] = 'Import is not enabled';
$lang->kanban->importList['on'] = 'Enable import function (You can import content that you have permission to view as cards.)';
@@ -269,6 +276,10 @@ $lang->kanban->taskColumn['pause'] = 'Pause';
$lang->kanban->taskColumn['canceled'] = 'Canceled';
$lang->kanban->taskColumn['closed'] = 'Closed';
$lang->kanban->copyContentList = array();
$lang->kanban->copyContentList['basicInfo'] = 'Basic Information';
$lang->kanban->copyContentList['region'] = 'Columns and swimlanes';
$lang->kanbanspace = new stdclass();
$lang->kanbanspace->common = 'Kanban Space';
$lang->kanbanspace->name = 'Name';
+11
View File
@@ -203,6 +203,10 @@ $lang->kanban->setting = 'Setting';
$lang->kanban->my = 'My';
$lang->kanban->other = 'Other';
$lang->kanban->showWIP = 'Show WIP';
$lang->kanban->copyTitle = 'Veuillez sélectionner un Kanban àcopier';
$lang->kanban->copyNoKanban = 'Aucun Kanban disponible àcopier';
$lang->kanban->cancelCopy = 'Annuler Copie';
$lang->kanban->copyContent = 'Copier Contenu';
$lang->kanban->error = new stdclass();
$lang->kanban->error->mustBeInt = 'The WIPs must be positive integer.';
@@ -211,6 +215,9 @@ $lang->kanban->error->childLimitNote = 'The sum of products in the child colu
$lang->kanban->error->importObjNotEmpty = 'Please select at least one import object.';
$lang->kanban->error->childLimitEmpty = 'Please set the WIP quantity of the sub column before operation.';
$lang->kanban->orderList['pri_asc'] = 'Story Priorité Ascendante';
$lang->kanban->orderList['pri_desc'] = 'Story Priorité Descendante';
$lang->kanban->importList = array();
$lang->kanban->importList['off'] = 'Import is not enabled';
$lang->kanban->importList['on'] = 'Enable the import function, you can only import content that you have permission to view.';
@@ -269,6 +276,10 @@ $lang->kanban->taskColumn['pause'] = 'Pause';
$lang->kanban->taskColumn['canceled'] = 'Canceled';
$lang->kanban->taskColumn['closed'] = 'Closed';
$lang->kanban->copyContentList = array();
$lang->kanban->copyContentList['basicInfo'] = 'Informations de base';
$lang->kanban->copyContentList['region'] = 'Colonnes et swimlanes';
$lang->kanbanspace = new stdclass();
$lang->kanbanspace->common = 'Kanban Space';
$lang->kanbanspace->name = 'Name';
+11
View File
@@ -203,6 +203,10 @@ $lang->kanban->setting = '设置';
$lang->kanban->my = '我的看板';
$lang->kanban->other = '其他';
$lang->kanban->showWIP = '显示在制品限制';
$lang->kanban->copyTitle = '请选择一个看板来复制';
$lang->kanban->copyNoKanban = '没有可用的看板来复制';
$lang->kanban->cancelCopy = '取消复制';
$lang->kanban->copyContent = '复制内容';
$lang->kanban->error = new stdclass();
$lang->kanban->error->mustBeInt = '在制品数量必须是正整数。';
@@ -211,6 +215,9 @@ $lang->kanban->error->childLimitNote = '子列在制品数量之和不能大
$lang->kanban->error->importObjNotEmpty = '请至少选择一个导入对象';
$lang->kanban->error->childLimitEmpty = '请先设置子列的在制品数量后再操作';
$lang->kanban->orderList['pri_asc'] = '需求优先级正序排序';
$lang->kanban->orderList['pri_desc'] = '需求优先级倒序排序';
$lang->kanban->importList = array();
$lang->kanban->importList['off'] = '不启用导入功能';
$lang->kanban->importList['on'] = '启用导入功能(可以导入自己有权查看的内容作为卡片)';
@@ -269,6 +276,10 @@ $lang->kanban->taskColumn['pause'] = '已暂停';
$lang->kanban->taskColumn['canceled'] = '已取消';
$lang->kanban->taskColumn['closed'] = '已关闭';
$lang->kanban->copyContentList = array();
$lang->kanban->copyContentList['basicInfo'] = '基本信息';
$lang->kanban->copyContentList['region'] = '列和泳道';
$lang->kanbanspace = new stdclass();
$lang->kanbanspace->common = '看板空间';
$lang->kanbanspace->name = '空间名称';
+132 -37
View File
@@ -50,15 +50,44 @@ class kanbanModel extends model
public function createDefaultRegion($kanban)
{
$region = new stdclass();
$region->name = $this->lang->kanbanregion->default;
$region->kanban = $kanban->id;
$region->space = $kanban->space;
$region->createdBy = $this->app->user->account;
$region->createdDate = helper::today();
$region->name = $this->lang->kanbanregion->default;
$region->kanban = $kanban->id;
$region->space = $kanban->space;
$region->createdBy = $this->app->user->account;
$region->createdDate = helper::now();
return $this->createRegion($kanban, $region);
}
/**
* Copy kanban regions.
*
* @param object $kanban
* @param int $copyKanbanID
* @access public
* @return void
*/
public function copyRegions($kanban, $copyKanbanID)
{
if(empty($kanban) or empty($copyKanbanID)) return;
$regions = $this->getRegionPairs($copyKanbanID);
$order = 1;
foreach($regions as $copyID => $copyName)
{
$region = new stdclass();
$region->name = $copyName;
$region->kanban = $kanban->id;
$region->space = $kanban->space;
$region->createdBy = $this->app->user->account;
$region->createdDate = helper::now();
$region->order = $order;
$this->createRegion($kanban, $region, $copyID, 'kanban', 'withArchived');
$order ++;
}
}
/**
* Create a new region.
*
@@ -66,10 +95,11 @@ class kanbanModel extends model
* @param object $region
* @param int $copyRegionID
* @param string $from kanban|execution
* @param string $param
* @access public
* @return int
*/
public function createRegion($kanban, $region = null, $copyRegionID = 0, $from = 'kanban')
public function createRegion($kanban, $region = null, $copyRegionID = 0, $from = 'kanban', $param = '')
{
$account = $this->app->user->account;
$order = 1;
@@ -90,7 +120,7 @@ class kanbanModel extends model
$region->space = $from == 'kanban' ? $kanban->space : 0;
}
$region->order = $order;
$region->order = isset($region->order) ? $region->order : $order;
$this->dao->insert(TABLE_KANBANREGION)->data($region)
->batchCheck($this->config->kanban->require->createregion, 'notempty')
->check('name', 'unique', "kanban = {$kanban->id} AND deleted = '0' AND space='$region->space'")
@@ -107,7 +137,7 @@ class kanbanModel extends model
/* Gets the groups, lanes and columns of the replication region. */
$copyGroups = $this->getGroupGroupByRegions($copyRegionID);
$copyLaneGroup = $this->getLaneGroupByRegions($copyRegionID);
$copyColumnGroup = $this->getColumnGroupByRegions($copyRegionID, 'id_asc');
$copyColumnGroup = $this->getColumnGroupByRegions($copyRegionID, 'id_asc', $param);
/* Create groups, lanes, and columns. */
if(empty($copyGroups)) return $regionID;
@@ -651,6 +681,21 @@ class kanbanModel extends model
return $kanban;
}
/**
* Get kanban pairs.
*
* @access public
* @return array
*/
public function getPairs()
{
$idList = $this->getCanViewObjects();
return $this->dao->select('id,name')->from(TABLE_KANBAN)
->where('id')->in($idList)
->andWhere('deleted')->eq('0')
->fetchPairs();
}
/**
* Get kanban data.
*
@@ -827,7 +872,7 @@ class kanbanModel extends model
*/
public function getRDKanban($executionID, $browseType = 'all', $orderBy = 'id_desc', $regionID = 0, $groupBy = 'default', $searchValue = '')
{
if($groupBy != 'default' and $groupBy != '') return $this->getKanban4Group($executionID, $browseType, $groupBy, $searchValue);
if($groupBy != 'default' and $groupBy != '') return $this->getKanban4Group($executionID, $browseType, $groupBy, $searchValue, $orderBy);
$kanbanData = array();
$actions = array('sortGroup');
@@ -996,15 +1041,16 @@ class kanbanModel extends model
*
* @param array $regions
* @param string $order order|id_asc
* @param string $param
* @access public
* @return array
*/
public function getColumnGroupByRegions($regions, $order = 'order')
public function getColumnGroupByRegions($regions, $order = 'order', $param = '')
{
$columnGroup = $this->dao->select("*")->from(TABLE_KANBANCOLUMN)
->where('deleted')->eq('0')
->andWhere('archived')->eq('0')
->andWhere('region')->in($regions)
->beginIF(strpos(",$param,", ',withArchived,') === false)->andWhere('archived')->eq('0')->fi()
->orderBy($order)
->fetchGroup('group');
@@ -1347,9 +1393,9 @@ class kanbanModel extends model
* @access public
* @return array
*/
public function getExecutionKanban($executionID, $browseType = 'all', $groupBy = 'default', $searchValue = '')
public function getExecutionKanban($executionID, $browseType = 'all', $groupBy = 'default', $searchValue = '', $orderBy = 'pri_asc')
{
if($groupBy != 'default') return $this->getKanban4Group($executionID, $browseType, $groupBy, $searchValue);
if($groupBy != 'default') return $this->getKanban4Group($executionID, $browseType, $groupBy, $searchValue, $orderBy);
$lanes = $this->dao->select('*')->from(TABLE_KANBANLANE)
->where('execution')->eq($executionID)
@@ -1470,11 +1516,12 @@ class kanbanModel extends model
* @param string $browseType
* @param string $groupBy
* @param string $searchValue
* @param string $orderBy
*
* @access public
* @return array
*/
public function getKanban4Group($executionID, $browseType, $groupBy, $searchValue = '')
public function getKanban4Group($executionID, $browseType, $groupBy, $searchValue = '', $orderBy = 'pri_asc')
{
/* Get card data. */
$cardList = array();
@@ -1500,7 +1547,8 @@ class kanbanModel extends model
if($browseType == 'bug') $bugCardMenu = $this->getKanbanCardMenu($executionID, $cardList, 'bug');
if($browseType == 'task') $taskCardMenu = $this->getKanbanCardMenu($executionID, $cardList, 'task');
$lanes = $this->getLanes4Group($executionID, $browseType, $groupBy, $cardList);
if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'pri_asc';
$lanes = $this->getLanes4Group($executionID, $browseType, $groupBy, $cardList, $orderBy);
if(empty($lanes)) return array();
$execution = $this->loadModel('execution')->getByID($executionID);
@@ -1547,6 +1595,33 @@ class kanbanModel extends model
$laneData['type'] = $browseType;
$laneData['defaultCardType'] = $browseType;
if($browseType == 'task' and $groupBy == 'story')
{
$columnData[0]['id'] = 0;
$columnData[0]['type'] = 'story';
$columnData[0]['name'] = zget($this->lang->kanban->orderList, $orderBy, '');
$columnData[0]['color'] = '#333';
$columnData[0]['limit'] = '-1';
$columnData[0]['laneType'] = $browseType;
$columnData[0]['asParent'] = false;
$columnData[0]['parentType'] = '';
$columnData[0]['actions'] = array();
if(empty($searchValue) or strpos($lane->name, $searchValue) !== false)
{
$cardData = array();
$cardData['id'] = $laneID;
$cardData['title'] = $lane->name;
$cardData['order'] = 1;
$cardData['pri'] = $lane->pri;
$cardData['estimate'] = '';
$cardData['assignedTo'] = $lane->assignedTo;
$cardData['deadline'] = '';
$cardData['severity'] = '';
$laneData['cards']['story'][] = $cardData;
}
}
/* Construct kanban column data. */
foreach($columns as $column)
{
@@ -1638,9 +1713,11 @@ class kanbanModel extends model
* @param string $browseType
* @param string $groupBy
* @param array $cardList
* @param string $orderBy
*
* @return array
*/
public function getLanes4Group($executionID, $browseType, $groupBy, $cardList)
public function getLanes4Group($executionID, $browseType, $groupBy, $cardList, $orderBy = 'pri_asc')
{
$lanes = array();
$groupByList = array();
@@ -1673,7 +1750,14 @@ class kanbanModel extends model
$objectPairs += $this->dao->select('id,title')->from(TABLE_STORY)
->where('deleted')->eq(0)
->andWhere('id')->in($groupByList)
->orderBy($orderBy)
->fetchPairs();
$objects = $this->dao->select('*')->from(TABLE_STORY)
->where('deleted')->eq(0)
->andWhere('id')->in($groupByList)
->orderBy($orderBy)
->fetchAll('id');
}
else
{
@@ -1701,12 +1785,14 @@ class kanbanModel extends model
if(!isset($groupByList[$objectType]) and $objectType and !in_array($objectType, array('feature', 'design'))) continue;
$lane = new stdclass();
$lane->id = $groupBy . $objectType;
$lane->type = $browseType;
$lane->execution = $executionID;
$lane->name = $objectName;
$lane->order = $order;
$lane->color = $this->config->kanban->laneColorList[$laneColor];
$lane->id = $groupBy . $objectType;
$lane->type = $browseType;
$lane->execution = $executionID;
$lane->name = $objectName;
$lane->order = $order;
$lane->color = $this->config->kanban->laneColorList[$laneColor];
$lane->pri = (isset($objects) and isset($objects[$objectType]->pri)) ? $objects[$objectType]->pri : '';
$lane->assignedTo = (isset($objects) and isset($objects[$objectType]->assignedTo)) ? $objects[$objectType]->assignedTo : '';
$order += 1;
$laneColor += 1;
@@ -1791,11 +1877,11 @@ class kanbanModel extends model
* Get can view objects.
*
* @param string $objectType kanbanspace|kanban
* @param string $param noclosed|private|cooperation|public|involved
* @param string $param all|noclosed|private|cooperation|public|involved
* @access public
* @return array
*/
public function getCanViewObjects($objectType = 'kanban', $param = '')
public function getCanViewObjects($objectType = 'kanban', $param = 'all')
{
$table = $this->config->objectTables[$objectType];
$objects = $this->dao->select('*')->from($table)
@@ -2104,7 +2190,7 @@ class kanbanModel extends model
->join('whitelist', ',')
->join('team', ',')
->trim('name')
->remove('uid,contactListMenu,type,import,importObjectList')
->remove('contactListMenu,type,import,importObjectList,copyKanbanID,copyRegion')
->get();
if($this->post->import == 'on') $kanban->object = implode(',', $this->post->importObjectList);
@@ -2134,7 +2220,15 @@ class kanbanModel extends model
$kanbanID = $this->dao->lastInsertID();
$kanban = $this->getByID($kanbanID);
$this->createDefaultRegion($kanban);
if($this->post->copyRegion)
{
$this->copyRegions($kanban, $this->post->copyKanbanID);
}
else
{
$this->createDefaultRegion($kanban);
}
$this->loadModel('file')->saveUpload('kanban', $kanbanID);
$this->file->updateObjectID($this->post->uid, $kanbanID, 'kanban');
@@ -3660,6 +3754,7 @@ class kanbanModel extends model
public function getKanbanCardMenu($executionID, $objects, $objecType)
{
$this->app->loadLang('execution');
$methodName = $this->app->rawMethod;
$menus = array();
switch ($objecType)
@@ -3679,7 +3774,7 @@ class kanbanModel extends model
if(common::hasPriv('task', 'create') and $toTaskPriv) $menu[] = array('label' => $this->lang->execution->wbs, 'icon' => 'plus', 'url' => helper::createLink('task', 'create', "executionID=$executionID&storyID=$story->id&moduleID=$story->module", '', true), 'size' => '95%');
if(common::hasPriv('task', 'batchCreate') and $toTaskPriv) $menu[] = array('label' => $this->lang->execution->batchWBS, 'icon' => 'pluses', 'url' => helper::createLink('task', 'batchCreate', "executionID=$executionID&storyID=$story->id&moduleID=0&taskID=0&iframe=true", '', true), 'size' => '95%');
if(common::hasPriv('story', 'activate') and $this->story->isClickable($story, 'activate')) $menu[] = array('label' => $this->lang->story->activate, 'icon' => 'magic', 'url' => helper::createLink('story', 'activate', "storyID=$story->id", '', true));
if(common::hasPriv('execution', 'unlinkStory')) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID=$executionID&storyID=$story->story&confirm=no", '', true));
if(common::hasPriv('execution', 'unlinkStory')) $menu[] = array('label' => $this->lang->execution->unlinkStory, 'icon' => 'unlink', 'url' => helper::createLink('execution', 'unlinkStory', "executionID=$executionID&storyID=$story->story&confirm=no&from=taskkanban", '', true));
if(common::hasPriv('story', 'delete')) $menu[] = array('label' => $this->lang->story->delete, 'icon' => 'trash', 'url' => helper::createLink('story', 'delete', "storyID=$story->id&confirm=no&from=taskkanban"));
$menus[$story->id] = $menu;
@@ -3693,11 +3788,11 @@ class kanbanModel extends model
$menu = array();
if(common::hasPriv('bug', 'edit') and $this->bug->isClickable($bug, 'edit')) $menu[] = array('label' => $this->lang->bug->edit, 'icon' => 'edit', 'url' => helper::createLink('bug', 'edit', "bugID=$bug->id", '', true), 'size' => '95%');
if(common::hasPriv('bug', 'confirmBug') and $this->bug->isClickable($bug, 'confirmBug')) $menu[] = array('label' => $this->lang->bug->confirmBug, 'icon' => 'ok', 'url' => helper::createLink('bug', 'confirmBug', "bugID=$bug->id", '', true));
if(common::hasPriv('bug', 'resolve') and $this->bug->isClickable($bug, 'resolve')) $menu[] = array('label' => $this->lang->bug->resolve, 'icon' => 'checked', 'url' => helper::createLink('bug', 'resolve', "bugID=$bug->id", '', true));
if(common::hasPriv('bug', 'close') and $this->bug->isClickable($bug, 'close')) $menu[] = array('label' => $this->lang->bug->close, 'icon' => 'off', 'url' => helper::createLink('bug', 'close', "bugID=$bug->id", '', true));
if(common::hasPriv('bug', 'confirmBug') and $this->bug->isClickable($bug, 'confirmBug')) $menu[] = array('label' => $this->lang->bug->confirmBug, 'icon' => 'ok', 'url' => helper::createLink('bug', 'confirmBug', "bugID=$bug->id&extra=&from=taskkanban", '', true));
if(common::hasPriv('bug', 'resolve') and $this->bug->isClickable($bug, 'resolve')) $menu[] = array('label' => $this->lang->bug->resolve, 'icon' => 'checked', 'url' => helper::createLink('bug', 'resolve', "bugID=$bug->id&extra=&from=taskkanban", '', true));
if(common::hasPriv('bug', 'close') and $this->bug->isClickable($bug, 'close')) $menu[] = array('label' => $this->lang->bug->close, 'icon' => 'off', 'url' => helper::createLink('bug', 'close', "bugID=$bug->id&extra=&from=taskkanban", '', true));
if(common::hasPriv('bug', 'create') and $this->bug->isClickable($bug, 'create')) $menu[] = array('label' => $this->lang->bug->copy, 'icon' => 'copy', 'url' => helper::createLink('bug', 'create', "productID=$bug->product&branch=$bug->branch&extras=bugID=$bug->id", '', true), 'size' => '95%');
if(common::hasPriv('bug', 'activate') and $this->bug->isClickable($bug, 'activate')) $menu[] = array('label' => $this->lang->bug->activate, 'icon' => 'magic', 'url' => helper::createLink('bug', 'activate', "bugID=$bug->id", '', true));
if(common::hasPriv('bug', 'activate') and $this->bug->isClickable($bug, 'activate')) $menu[] = array('label' => $this->lang->bug->activate, 'icon' => 'magic', 'url' => helper::createLink('bug', 'activate', "bugID=$bug->id&extra=&from=taskkanban", '', true));
if(common::hasPriv('story', 'create') and $bug->status != 'closed') $menu[] = array('label' => $this->lang->bug->toStory, 'icon' => 'lightbulb', 'url' => helper::createLink('story', 'create', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", '', true), 'size' => '95%');
if(common::hasPriv('bug', 'delete')) $menu[] = array('label' => $this->lang->bug->delete, 'icon' => 'trash', 'url' => helper::createLink('bug', 'delete', "bugID=$bug->id&confirm=no&from=taskkanban"));
@@ -3712,14 +3807,14 @@ class kanbanModel extends model
$menu = array();
if(common::hasPriv('task', 'edit') and $this->task->isClickable($task, 'edit')) $menu[] = array('label' => $this->lang->task->edit, 'icon' => 'edit', 'url' => helper::createLink('task', 'edit', "taskID=$task->id", '', true), 'size' => '95%');
if(common::hasPriv('task', 'pause') and $this->task->isClickable($task, 'pause')) $menu[] = array('label' => $this->lang->task->pause, 'icon' => 'pause', 'url' => helper::createLink('task', 'pause', "taskID=$task->id", '', true));
if(common::hasPriv('task', 'restart') and $this->task->isClickable($task, 'restart')) $menu[] = array('label' => $this->lang->task->restart, 'icon' => 'play', 'url' => helper::createLink('task', 'restart', "taskID=$task->id", '', true));
if(common::hasPriv('task', 'recordEstimate') and $this->task->isClickable($task, 'recordEstimate')) $menu[] = array('label' => $this->lang->task->recordEstimate, 'icon' => 'time', 'url' => helper::createLink('task', 'recordEstimate', "taskID=$task->id", '', true));
if(common::hasPriv('task', 'activate') and $this->task->isClickable($task, 'activate')) $menu[] = array('label' => $this->lang->task->activate, 'icon' => 'magic', 'url' => helper::createLink('task', 'activate', "taskID=$task->id", '', true));
if(common::hasPriv('task', 'pause') and $this->task->isClickable($task, 'pause')) $menu[] = array('label' => $this->lang->task->pause, 'icon' => 'pause', 'url' => helper::createLink('task', 'pause', "taskID=$task->id&extra=from=taskkanban", '', true));
if(common::hasPriv('task', 'restart') and $this->task->isClickable($task, 'restart')) $menu[] = array('label' => $this->lang->task->restart, 'icon' => 'play', 'url' => helper::createLink('task', 'restart', "taskID=$task->id&from=taskkanban", '', true));
if(common::hasPriv('task', 'recordEstimate') and $this->task->isClickable($task, 'recordEstimate')) $menu[] = array('label' => $this->lang->task->recordEstimate, 'icon' => 'time', 'url' => helper::createLink('task', 'recordEstimate', "taskID=$task->id&from=taskkanban", '', true));
if(common::hasPriv('task', 'activate') and $this->task->isClickable($task, 'activate')) $menu[] = array('label' => $this->lang->task->activate, 'icon' => 'magic', 'url' => helper::createLink('task', 'activate', "taskID=$task->id&extra=from=taskkanban", '', true));
if(common::hasPriv('task', 'batchCreate') and $this->task->isClickable($task, 'batchCreate')) $menu[] = array('label' => $this->lang->task->children, 'icon' => 'split', 'url' => helper::createLink('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", '', true), 'size' => '95%');
if(common::hasPriv('task', 'create') and $this->task->isClickable($task, 'create')) $menu[] = array('label' => $this->lang->task->copy, 'icon' => 'copy', 'url' => helper::createLink('task', 'create', "projctID=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", '', true), 'size' => '95%');
if(common::hasPriv('task', 'cancel') and $this->task->isClickable($task, 'cancel')) $menu[] = array('label' => $this->lang->task->cancel, 'icon' => 'ban-circle', 'url' => helper::createLink('task', 'cancel', "taskID=$task->id", '', true));
if(common::hasPriv('task', 'delete')) $menu[] = array('label' => $this->lang->task->delete, 'icon' => 'trash', 'url' => helper::createLink('task', 'delete', "executionID=$task->execution&taskID=$task->id&confirm=no&from=taskkanban"));
if(common::hasPriv('task', 'cancel') and $this->task->isClickable($task, 'cancel')) $menu[] = array('label' => $this->lang->task->cancel, 'icon' => 'ban-circle', 'url' => helper::createLink('task', 'cancel', "taskID=$task->id&extra=from=taskkanban", '', true));
if(common::hasPriv('task', 'delete')) $menu[] = array('label' => $this->lang->task->delete, 'icon' => 'trash', 'url' => helper::createLink('task', 'delete', "executionID=$task->execution&taskID=$task->id&confirm=no&from=taskkanban"));
$menus[$task->id] = $menu;
}
+51 -18
View File
@@ -12,12 +12,16 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('spaceID', $spaceID);?>
<?php js::set('spaceType', $type);?>
<?php js::set('enableImport', 'off');?>
<?php js::set('enableImport', $enableImport);?>
<?php js::set('vision', $this->config->vision);?>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2><?php echo $lang->kanban->create;?></h2>
<div class="pull-right btn-toolbar">
<button type='button' class='btn btn-link' data-toggle='modal' data-target='#copyKanbanModal'><?php echo html::icon($lang->icons['copy'], 'muted') . ' ' . $lang->kanban->copy . $lang->kanban->common;?></button>
</div>
</div>
<form class='form-indicator main-form form-ajax' method='post' enctype='multipart/form-data' id='dataform'>
<table class='table table-form'>
@@ -28,78 +32,107 @@
</tr>
<tr>
<th><?php echo $lang->kanban->WIPCount;?></th>
<td><?php echo html::radio('showWIP', $lang->kanban->showWIPList, 1);?></td>
<td><?php echo html::radio('showWIP', $lang->kanban->showWIPList, isset($copyKanban->showWIP) ? $copyKanban->showWIP : 1);?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->kanban->space;?></th>
<td><?php echo html::select('space', $spacePairs, $spaceID, "onchange='changeValue(this.value)' class='form-control chosen'");?></td>
<td><?php echo html::select('space', $spacePairs, isset($copyKanban->space) ? $copyKanban->space : $spaceID, "onchange='loadUsers(this.value)' class='form-control chosen'");?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->kanban->name;?></th>
<td><?php echo html::input('name', '', "class='form-control'");?></td>
<td><?php echo html::input('name', isset($copyKanban->name) ? $copyKanban->name : '', "class='form-control'");?></td>
</tr>
<?php if($type != 'private'):?>
<tr>
<th><?php echo $lang->kanban->owner;?></th>
<td><?php echo html::select('owner', $users, '', "class='form-control chosen' data-drop_direction='down'");?></td>
<td><?php echo html::select('owner', $users, isset($copyKanban->owner) ? $copyKanban->owner : '', "class='form-control chosen' data-drop_direction='down'");?></td>
</tr>
<tr>
<th><?php echo $lang->kanban->team;?></th>
<td colspan='2'>
<div class="input-group">
<?php echo html::select('team[]', $users, '', "class='form-control picker-select' multiple data-dropDirection='bottom'");?>
<?php echo html::select('team[]', $users, isset($copyKanban->team) ? $copyKanban->team : '', "class='form-control picker-select' multiple data-dropDirection='bottom'");?>
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th class='columnWidth'><?php echo $lang->kanban->columnWidth;?></th>
<td><?php echo nl2br(html::radio('fluidBoard', $lang->kanbancolumn->fluidBoardList, 0));?></td>
</tr>
<tr class="hidden" id='cardBox'>
<th class='c-count'><?php echo $lang->kanban->cardCount;?></th>
<td><?php echo html::input('displayCards', '', "class='form-control' required placeholder='{$lang->kanban->cardCountTip}' autocomplete='off'");?></td>
<td><?php echo nl2br(html::radio('fluidBoard', $lang->kanbancolumn->fluidBoardList, isset($copyKanban->fluidBoard) ? $copyKanban->fluidBoard : 0));?></td>
</tr>
<tr>
<th rowspan='2'><?php echo $lang->kanban->import?></th>
<td colspan='2' class='importBox'><?php echo nl2br(html::radio('import', $lang->kanban->importList, 'off'));?></td>
<td colspan='2' class='importBox'><?php echo nl2br(html::radio('import', $lang->kanban->importList, $enableImport));?></td>
</tr>
<tr>
<td colspan='2' class='objectBox'><?php echo html::checkbox('importObjectList', $lang->kanban->importObjectList, array());?></td>
<td colspan='2' class='objectBox'><?php echo html::checkbox('importObjectList', $lang->kanban->importObjectList, $importObjects);?></td>
</tr>
<tr id='emptyTip' class='hidden'><th></th><td colspan='2' style='color: red;'><?php echo $lang->kanban->error->importObjNotEmpty;?></td></tr>
<tr>
<th class='w-90px'><?php echo $lang->kanban->archive;?></th>
<td><?php echo nl2br(html::radio('archived', $lang->kanban->archiveList, '0'));?></td>
<td><?php echo nl2br(html::radio('archived', $lang->kanban->archiveList, isset($copyKanban->archived) ? $copyKanban->archived : '0'));?></td>
</tr>
<tr>
<th id='c-title'><?php echo $lang->kanban->manageProgress;?></th>
<td><?php echo nl2br(html::radio('performable', $lang->kanban->enableList, '0'));?></td>
<td><?php echo nl2br(html::radio('performable', $lang->kanban->enableList, isset($copyKanban->performable) ? $copyKanban->performable : '0'));?></td>
</tr>
<tr>
<th id='c-title'><?php echo $lang->kanban->alignment;?></th>
<td><?php echo nl2br(html::radio('alignment', $lang->kanban->alignmentList, 'center'));?></td>
<td><?php echo nl2br(html::radio('alignment', $lang->kanban->alignmentList, isset($copyKanban->alignment) ? $copyKanban->alignment : 'center'));?></td>
</tr>
<tr>
<th><?php echo $lang->kanban->desc;?></th>
<td colspan='2'>
<?php echo html::textarea('desc', '', "rows='10' class='form-control'");?>
<?php echo html::textarea('desc', isset($copyKanban->desc) ? $copyKanban->desc : '', "rows='10' class='form-control'");?>
</td>
</tr>
<?php if($type == 'private'):?>
<tr id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $whitelist, '', 'class="form-control picker-select" multiple');?></td>
<td><?php echo html::select('whitelist[]', $whitelist, isset($copyKanban->whitelist) ? $copyKanban->whitelist : '', 'class="form-control picker-select" multiple');?></td>
</tr>
<?php endif;?>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::hidden('copyKanbanID', $copyKanbanID)?>
<?php echo html::hidden('copyRegion', $copyRegion)?>
<?php echo html::submitButton();?>
</td>
</tr>
</table>
</form>
</div>
<div class='modal fade modal-scroll-inside' id='copyKanbanModal'>
<div class='modal-dialog mw-900px'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'><i class="icon icon-close"></i></button>
<div class='titleBox'><h4 class='modal-title' id='myModalLabel'><?php echo $lang->kanban->copyTitle;?></h4></div>
</div>
<div class='modal-body'>
<?php if(count($kanbans) == 1):?>
<div class='alert with-icon'>
<i class='icon-exclamation-sign'></i>
<div class='content'><?php echo $lang->kanban->copyNoKanban;?></div>
</div>
<?php else:?>
<?php $copyContent = array('basicInfo');?>
<?php if($copyRegion) $copyContent[] = 'region';?>
<div class='copyContentBox'><?php echo $lang->kanban->copyContent . html::checkbox('copyContent', $lang->kanban->copyContentList, $copyContent);?></div>
<div id='copyKanbans' class='row'>
<?php foreach ($kanbans as $id => $name):?>
<?php if(empty($id)):?>
<?php if($copyKanbanID != 0):?>
<div class='col-md-4 col-sm-4'><a href='javascript:;' data-id='' class='cancel'><?php echo html::icon($lang->icons['cancel']) . ' ' . $lang->kanban->cancelCopy;?></a></div>
<?php endif;?>
<?php else: ?>
<div class='col-md-4 col-sm-4'><a href='javascript:;' data-id='<?php echo $id;?>' class='nobr <?php echo ($copyKanbanID == $id) ? ' active' : '';?>'><?php echo html::icon($lang->icons['kanban'], 'text-muted') . ' ' . $name;?></a></div>
<?php endif; ?>
<?php endforeach;?>
</div>
<?php endif;?>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -115,7 +115,7 @@
if($canDelete)
{
echo '<li>';
common::printLink('kanban', 'delete', "kanbanID={$kanban->id}", '<i class="icon icon-trash"></i> ' . $lang->kanban->delete, 'hiddenwin');
common::printLink('kanban', 'delete', "kanbanID={$kanban->id}&confirm=no&browseType=$browseType", '<i class="icon icon-trash"></i> ' . $lang->kanban->delete, 'hiddenwin');
echo '</li>';
}
?>
+1 -1
View File
@@ -27,7 +27,7 @@ $lang->message->label->assigned = '指派';
$lang->message->label->closed = '关闭';
$lang->message->label->deleted = '删除';
$lang->message->label->undeleted = '还原';
$lang->message->label->commented = '评论';
$lang->message->label->commented = '备注';
$lang->message->label->activated = '激活';
$lang->message->label->resolved = '解决';
$lang->message->label->reviewed = '评审';
+1 -1
View File
@@ -27,7 +27,7 @@ $lang->message->label->assigned = '指派';
$lang->message->label->closed = '關閉';
$lang->message->label->deleted = '刪除';
$lang->message->label->undeleted = '還原';
$lang->message->label->commented = '評論';
$lang->message->label->commented = '備註';
$lang->message->label->activated = '激活';
$lang->message->label->resolved = '解決';
$lang->message->label->reviewed = '評審';
+4 -1
View File
@@ -1,2 +1,5 @@
.c-gender {width: 80px;}
.c-phone, .c-skype, .c-visits {width: 100px;}
.c-skype, .c-visits {width: 100px;}
.c-phone {width: 128px;}
.c-email {width: 128px;}
.c-role {width: 96px;}
+7 -1
View File
@@ -292,7 +292,7 @@ class productplan extends control
* @access public
* @return void
*/
public function browse($productID = 0, $branch = '', $browseType = 'undone', $queryID = 0, $orderBy = 'begin_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1 )
public function browse($productID = 0, $branch = '', $browseType = 'all', $queryID = 0, $orderBy = 'begin_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1 )
{
$branchID = $branch === '' ? 'all' : $branch;
if(!$branch) $branch = 0;
@@ -351,6 +351,12 @@ class productplan extends control
$this->view->kanbanData = $this->loadModel('kanban')->getPlanKanban($product, $branchID, $planGroup);
}
$productPlansNum = $this->dao->select('COUNT(id) AS count')->from(TABLE_PRODUCTPLAN)
->where('product')->eq($productID)
->andWhere('deleted')->eq(0)
->fetch();
$this->view->productPlansNum = $productPlansNum;
$this->view->title = $productName . $this->lang->colon . $this->lang->productplan->browse;
$this->view->position[] = $this->lang->productplan->browse;
$this->view->productID = $productID;
@@ -41,7 +41,7 @@
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->productplan->noPlan;?></span>
<?php if(common::canModify('product', $product) and common::hasPriv('productplan', 'create') and $browseType == "all"):?>
<?php if(common::canModify('product', $product) and common::hasPriv('productplan', 'create') and empty($productPlansNum->count)):?>
<?php echo html::a($this->createLink('productplan', 'create', "productID=$product->id&branch=$branch"), "<i class='icon icon-plus'></i> " . $lang->productplan->create, '', "class='btn btn-info'");?>
<?php endif;?>
</p>
+1 -1
View File
@@ -88,7 +88,7 @@
$createMisc = common::hasPriv('story', 'create') ? 'btn btn-secondary' : " btn btn-secondary disabled";
echo html::a($createLink, "<i class='icon icon-plus'></i><span class='text'>" . $lang->story->create . "</span><span class='caret'>", '', "class='$createMisc'");
?>
<ul class='dropdown-menu'>
<ul class='dropdown-menu pull-right'>
<?php $disabled = common::hasPriv('story', 'batchCreate') ? '' : "class='disabled'";?>
<li <?php echo $disabled?>>
<?php
+8 -1
View File
@@ -450,7 +450,7 @@ class program extends control
* @access public
* @return void
*/
public function project($programID = 0, $browseType = 'doing', $orderBy = 'order_desc', $recTotal = 0, $recPerPage = 15, $pageID = 1)
public function project($programID = 0, $browseType = 'all', $orderBy = 'order_desc', $recTotal = 0, $recPerPage = 15, $pageID = 1)
{
$programID = $this->program->saveState($programID, $this->program->getPairs());
setCookie("lastProgram", $programID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
@@ -473,6 +473,13 @@ class program extends control
$sortField = zget($this->config->program->sortFields, $order[0], 'id') . '_' . $order[1];
$projectStats = $this->program->getProjectStats($programID, $browseType, 0, $sortField, $pager, $programTitle);
$allProjectsNum = $this->dao->select('COUNT(id) AS count')->from(TABLE_PROJECT)
->where('parent')->eq($programID)
->andWhere('type')->eq('project')
->andWhere('deleted')->eq(0)
->fetch();
$this->view->allProjectsNum = $allProjectsNum;
$this->view->title = $this->lang->program->project;
$this->view->position[] = $this->lang->program->project;
+1
View File
@@ -15,3 +15,4 @@ tbody.sortable > tr > td.sort-handler {color: #3c4353 !important;}
.has-suffix > a {max-width: calc(100% - 100px); padding-right: 5px; color: #0c60e1; display: inline-block; max-width: calc(100% - 50px);}
div.checkbox-primary + span.table-nest-icon {margin-left:0px !important;}
#checkAll + label.hover:after {border-width: 0px;}
+12
View File
@@ -59,6 +59,7 @@ $(function()
$('#programSummary').addClass('hidden');
$('#projectsSummary').remove();
$('.editCheckbox').after(statistic);
$(this).next('label').addClass('hover');
}
else
{
@@ -66,8 +67,19 @@ $(function()
$('#programForm').removeClass('has-row-checked');
$('#programSummary').removeClass('hidden');
$('#projectsSummary').addClass('hidden');
$(this).next('label').removeClass('hover');
}
});
/* Solve the problem that clicking the browser back button causes the checkbox to be selected by default. */
setTimeout(function()
{
$(":checkbox[name^='projectIdList']").each(function()
{
$(this).prop('checked', false);
});
$('.table-footer #checkAll').prop('checked', false);
}, 10);
});
function showEditCheckbox(show)
+1 -1
View File
@@ -35,7 +35,7 @@ js::set('browseType', $browseType);
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->project->empty;?></span>
<?php if($browseType == 'all') common::printLink('project', 'createGuide', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info btn-wide " data-toggle="modal"');?>
<?php if(empty($allProjectsNum->count)) common::printLink('project', 'createGuide', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info btn-wide " data-toggle="modal"');?>
</p>
</div>
<?php else:?>
+1
View File
@@ -23,6 +23,7 @@ $lang->programplan->createSubPlan = '创建二级阶段';
$lang->programplan->parent = '父阶段';
$lang->programplan->emptyParent = '无';
$lang->programplan->name = '阶段名称';
$lang->programplan->status = '阶段进度';
$lang->programplan->PM = '阶段负责人';
$lang->programplan->acl = '访问控制';
$lang->programplan->subStageName = '子阶段名称';
+40 -4
View File
@@ -309,6 +309,12 @@ class project extends control
$programTitle = $this->loadModel('setting')->getItem('owner=' . $this->app->user->account . '&module=project&key=programTitle');
$projectStats = $this->loadModel('program')->getProjectStats($programID, $browseType, $queryID, $orderBy, $pager, $programTitle);
$allProjectsNum = $this->dao->select('COUNT(id) AS count')->from(TABLE_PROJECT)
->where('type')->eq('project')
->andWhere('deleted')->eq(0)
->fetch();
$this->view->allProjectsNum = $allProjectsNum;
$this->view->title = $this->lang->project->browse;
$this->view->position[] = $this->lang->project->browse;
@@ -487,7 +493,10 @@ class project extends control
$products = $this->product->getProducts($copyProjectID);
foreach($products as $product)
{
$productPlans[$product->id] = $this->loadModel('productplan')->getPairs($product->id, '', 'noclosed');
foreach($product->branches as $branch)
{
$productPlans[$product->id][$branch] = $this->loadModel('productplan')->getPairs($product->id, $branch, 'noclosed', true);
}
}
}
@@ -956,10 +965,37 @@ class project extends control
*/
public function execution($status = 'all', $projectID = 0, $orderBy = 'order_asc', $productID = 0, $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
$uri = $this->app->getURI(true);
$this->app->session->set('executionList', $uri, 'project');
$this->loadModel('execution');
$this->loadModel('task');
$this->loadModel('programplan');
echo $this->fetch('execution', 'all', "status=$status&projectID=$projectID&orderBy=$orderBy&productID=$productID&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
$projects = $this->project->getPairsByProgram();
$projectID = $this->project->saveState($projectID, $projects);
$project = $this->project->getByID($projectID);
$this->project->setMenu($projectID);
if(!$projectID) return print(js::locate($this->createLink('project', 'browse')));
if(!empty($project->model) and $project->model == 'kanban' and !(defined('RUN_MODE') and RUN_MODE == 'api')) return print(js::locate($this->createLink('project', 'index', "projectID=$projectID")));
/* Load pager and get tasks. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->view->title = $this->lang->execution->allExecutions;
$this->view->position[] = $this->lang->execution->allExecutions;
$this->view->executionStats = $this->project->getStats($projectID, $status, $productID, 0, 30, $orderBy, $pager, $this->cookie->showTask);
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID);
$this->view->productID = $productID;
$this->view->projectID = $projectID;
$this->view->project = $project;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->status = $status;
$this->view->isStage = (isset($project->model) and $project->model == 'waterfall') ? true : false;
$this->display();
}
/**
+4
View File
@@ -0,0 +1,4 @@
#executionList > thead > tr > th .table-nest-toggle-global {top: 6px}
#executionList > thead > tr > th .table-nest-toggle-global:before {color: #a6aab8;}
#mainMenu .pull-left .checkbox-primary {margin-top: 5px;}
.main-table tbody>tr>td:first-child, .main-table thead>tr>th:first-child { padding-left: 8px; }
+82
View File
@@ -0,0 +1,82 @@
$(function()
{
$('input[name^="showTask"]').click(function()
{
var show = $(this).is(':checked') ? 1 : 0;
$.cookie('showTask', show, {expires:config.cookieLife, path:config.webRoot});
window.location.reload();
});
})
window.addEventListener('scroll', this.handleScroll)
function handleScroll(e)
{
var relative = 200; // 相对距离
$('tr.showmore').each(function()
{
var $showmore = $(this);
var offsetTop = $showmore[0].offsetTop;
if(offsetTop == 0) return true;
if(getScrollTop() + getWindowHeight() >= offsetTop - relative)
{
throttle(loadData($showmore), 150)
}
})
}
function loadData($showmore)
{
$showmore.removeClass('showmore');
var executionID = $showmore.attr('data-parent');
var maxTaskID = $showmore.attr('data-id');
var link = createLink('task', 'ajaxGetTasks', 'executionID=' + executionID + '&maxTaskID=' + maxTaskID);
$.get(link, function(data)
{
$showmore.before(data);
$(".iframe").modalTrigger({type:'iframe'});
$('#executionForm').table('initNestedList');
})
}
function throttle(fn, threshhold)
{
var last;
var timer;
threshhold || (threshhold = 250);
return function()
{
var context = this;
var args = arguments;
var now = +new Date()
if (last && now < last + threshhold)
{
clearTimeout(timer);
timer = setTimeout(function ()
{
last = now
fn.apply(context, args)
}, threshhold)
}
else
{
last = now
fn.apply(context, args)
}
}
}
function getScrollTop()
{
return scrollTop = document.body.scrollTop + document.documentElement.scrollTop
}
function getWindowHeight()
{
return document.compatMode == "CSS1Compat" ? windowHeight = document.documentElement.clientHeight : windowHeight = document.body.clientHeight
}
+45 -6
View File
@@ -1527,6 +1527,7 @@ class projectModel extends model
$projects[$projectID]->id = $projectID;
$projects[$projectID]->name = $projectName;
$projects[$projectID]->code = $projectCode;
$projects[$projectID]->model = $oldProjects[$projectID]->model;
$projects[$projectID]->PM = $data->PMs[$projectID];
$projects[$projectID]->begin = $data->begins[$projectID];
$projects[$projectID]->end = isset($data->ends[$projectID]) ? $data->ends[$projectID] : LONG_TIME;
@@ -1544,15 +1545,13 @@ class projectModel extends model
/* Child project begin cannot less than parent. */
if(!empty($projects[$projectID]->name) and $projects[$projectID]->begin < $parentProject->begin)
{
dao::$errors['begin'] = sprintf($this->lang->project->beginGreateChild, $parentProject->begin);
return false;
dao::$errors[] = "ID {$projects[$projectID]->id}" . sprintf($this->lang->project->beginGreateChild, $parentProject->begin) . "\n";
}
/* When parent set end then child project end cannot greater than parent. */
if(!empty($projects[$projectID]->name) and $parentProject->end != '0000-00-00' and $projects[$projectID]->end > $parentProject->end)
{
dao::$errors['end'] = sprintf($this->lang->project->endLetterChild, $parentProject->end);
return false;
dao::$errors[] = "ID {$projects[$projectID]->id}" . sprintf($this->lang->project->endLetterChild, $parentProject->end) . "\n";
}
}
}
@@ -1584,7 +1583,17 @@ class projectModel extends model
->where('id')->eq($projectID)
->exec();
if(dao::isError()) helper::end(js::error('project#' . $projectID . dao::getError(true)));
if(dao::isError())
{
$errors = dao::getError();
foreach($errors as $key => $error)
{
dao::$errors[$key][0] = 'project#' . $projectID . $error[0];
}
return false;
}
if(!dao::isError())
{
$linkedProducts = $this->dao->select('product')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchPairs();
@@ -2308,7 +2317,7 @@ class projectModel extends model
* @access public
* @return array
*/
public function getStats($projectID = 0, $status = 'undone', $productID = 0, $branch = 0, $itemCounts = 30, $orderBy = 'id_asc', $pager = null)
public function getStats($projectID = 0, $status = 'undone', $productID = 0, $branch = 0, $itemCounts = 30, $orderBy = 'id_asc', $pager = null, $withTasks = false)
{
if(empty($productID))
{
@@ -2384,6 +2393,31 @@ class projectModel extends model
$burns[$executionID] = $executionBurns;
}
if($withTasks)
{
$executionTasks = $this->dao->select('*')->from(TABLE_TASK)
->where('deleted')->eq(0)
->andWhere('status')->notin('closed,cancel')
->andWhere('execution')->in(array_keys($executions))
->orderBy('id_asc')
->fetchGroup('execution', 'id');
foreach($executionTasks as $executionID => $tasks)
{
foreach($tasks as $task)
{
if($task->parent > 0)
{
if(isset($executionTasks[$executionID][$task->parent]))
{
$executionTasks[$executionID][$task->parent]->children[$task->id] = $task;
unset($executionTasks[$executionID][$task->id]);
}
}
}
}
}
/* Process executions. */
$parents = array();
$children = array();
@@ -2409,6 +2443,11 @@ class projectModel extends model
$execution->children = array();
$execution->grade == 1 ? $parents[$execution->id] = $execution : $children[$execution->parent][] = $execution;
if(isset($executionTasks) and isset($executionTasks[$execution->id]))
{
$tasks = array_chunk($executionTasks[$execution->id], 50, true);
$execution->tasks = $tasks[0];
}
}
/* In the case of the waterfall model, calculate the sub-stage. */
+1 -1
View File
@@ -73,7 +73,7 @@
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->project->empty;?></span>
<?php if($browseType == "all"):?>
<?php if(empty($allProjectsNum->count)):?>
<?php if(!defined('TUTORIAL')):?>
<?php if(common::hasPriv('project', 'create') and $browseType != 'bysearch') common::printLink('project', 'createGuide', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info" data-toggle="modal"');?>
<?php else:?>
+9 -4
View File
@@ -109,16 +109,18 @@
<div class='row'>
<?php $i = 0;?>
<?php foreach($products as $product):?>
<?php foreach($product->branches as $branch):?>
<div class="col-sm-4" style="margin-bottom: 10px; padding-right: 6px;">
<?php $hasBranch = $product->type != 'normal' and isset($branchGroups[$product->id]);?>
<div class="input-group<?php if($hasBranch) echo ' has-branch';?>">
<?php echo html::select("products[$i]", $allProducts, $product->id, "class='form-control chosen' onchange='loadBranches(this)' data-last='" . $product->id . "'");?>
<span class='input-group-addon fix-border'></span>
<?php if($hasBranch) echo html::select("branch[$i]", $branchGroups[$product->id], key($product->branches), "class='form-control chosen' onchange=\"loadPlans('#products{$i}', this.value)\"");?>
<?php if($hasBranch) echo html::select("branch[$i]", $branchGroups[$product->id], $branch, "class='form-control chosen' onchange=\"loadPlans('#products{$i}', this.value)\"");?>
</div>
</div>
<?php $i++;?>
<?php endforeach;?>
<?php endforeach;?>
<div class='col-sm-4 <?php if($programID) echo 'required';?>' style="padding-right: 6px;">
<div class='input-group'>
<?php echo html::select("products[$i]", $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");?>
@@ -146,12 +148,15 @@
<?php if($copyProjectID):?>
<?php $i = 0;?>
<?php foreach($products as $product):?>
<?php $plans = zget($productPlans, $product->id, array(0 => ''));?>
<div class="col-sm-4" id="plan<?php echo $i;?>"><?php echo html::select("plans[" . $product->id . "]", $plans, '', "class='form-control chosen' multiple");?></div>
<?php $productPlan = zget($productPlans, $product->id, array(0 => ''));?>
<?php foreach($product->branches as $branch):?>
<?php $plans = zget($productPlan, $branch, array(0 => ''));?>
<div class="col-sm-4" id="plan<?php echo $i;?>"><?php echo html::select('plans[' . $product->id . '][' . $branch . '][]', $plans, '', "class='form-control chosen' multiple");?></div>
<?php $i++;?>
<?php endforeach;?>
<?php endforeach;?>
<?php else:?>
<div class="col-sm-4" id="plan0" style="padding-right: 6px;"><?php echo html::select("plans[][]", '', '', "class='form-control chosen' multiple");?></div>
<div class="col-sm-4" id="plan0" style="padding-right: 6px;"><?php echo html::select("plans[][][]", '', '', "class='form-control chosen' multiple");?></div>
<?php endif;?>
</div>
</td>
+93
View File
@@ -0,0 +1,93 @@
<?php include '../../common/view/header.html.php';?>
<?php js::set('projectID', $projectID);?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class='btn-group'>
<?php $viewName = $productID != 0 ? zget($productList,$productID) : $lang->product->allProduct;?>
<a href='javascript:;' class='btn btn-link btn-limit' data-toggle='dropdown'><span class='text' title='<?php echo $viewName;?>'><?php echo $viewName;?></span> <span class='caret'></span></a>
<ul class='dropdown-menu' style='max-height:240px; max-width: 300px; overflow-y:auto'>
<?php
$class = '';
if($productID == 0) $class = 'class="active"';
echo "<li $class>" . html::a($this->createLink('project', 'execution', "status=$status&projectID=$projectID&orderby=$orderBy"), $lang->product->allProduct) . "</li>";
foreach($productList as $key => $product)
{
$class = $productID == $key ? 'class="active"' : '';
echo "<li $class>" . html::a($this->createLink('project', 'execution', "status=$status&projectID=$projectID&orderby=$orderBy&productID=$key"), $product) . "</li>";
}
?>
</ul>
</div>
<?php foreach($lang->execution->featureBar['all'] as $key => $label):?>
<?php $label = "<span class='text'>$label</span>";?>
<?php if($status == $key) $label .= " <span class='label label-light label-badge'>{$pager->recTotal}</span>";?>
<?php echo html::a($this->createLink('project', 'execution', "status=$key&projectID=$projectID&orderBy=$orderBy&productID=$productID"), $label, '', "class='btn btn-link' id='{$key}Tab'");?>
<?php endforeach;?>
<?php echo html::checkbox('showTask', array('1' => $lang->programplan->stageCustom->task), '', $this->cookie->showTask ? 'checked=checked' : '');?>
</div>
<div class='btn-toolbar pull-right'>
<?php common::printLink('execution', 'export', "status=$status&productID=$productID&orderBy=$orderBy&from=project", "<i class='icon-export muted'> </i> " . $lang->export, '', "class='btn btn-link export'")?>
<?php if(common::hasPriv('programplan', 'create') and $isStage):?>
<?php echo html::a($this->createLink('programplan', 'create', "projectID=$projectID&productID=$productID"), "<i class='icon icon-plus'></i> " . $lang->programplan->create, '', "class='btn btn-primary'");?>
<?php else: ?>
<?php if(common::hasPriv('execution', 'create')) echo html::a($this->createLink('execution', 'create', "projectID=$projectID"), "<i class='icon icon-sm icon-plus'></i> " . $lang->execution->create, '', "class='btn btn-primary create-execution-btn' data-app='execution' onclick='$(this).removeAttr(\"data-toggle\")'");?>
<?php endif;?>
</div>
</div>
<div id='mainContent' class="main-row fade">
<?php if(empty($executionStats)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->execution->noExecution;?></span>
<?php if($status == "all"):?>
<?php if(common::hasPriv('programplan', 'create') and $isStage):?>
<?php echo html::a($this->createLink('programplan', 'create', "projectID=$projectID&productID=$productID"), "<i class='icon icon-plus'></i> " . $lang->programplan->create, '', "class='btn btn-info'");?>
<?php else: ?>
<?php if(common::hasPriv('execution', 'create')):?>
<?php echo html::a($this->createLink('execution', 'create', "projectID=$projectID"), "<i class='icon icon-plus'></i> " . $lang->execution->create, '', "class='btn btn-info' data-app='execution'");?>
<?php endif;?>
<?php endif;?>
<?php endif;?>
</p>
</div>
<?php else:?>
<?php $canBatchEdit = common::hasPriv('execution', 'batchEdit'); ?>
<form class='main-table' id='executionForm' method='post' data-ride='table' data-nested='true' data-expand-nest-child='false' data-checkable='false' data-enable-empty-nested-row='true' data-replace-id='executionTableList' data-preserve-nested='true'>
<table class="table table-from table-fixed table-nested" id="executionList">
<?php $vars = "status=$status&orderBy=%s";?>
<thead>
<tr>
<th class='table-nest-title'>
<a class='table-nest-toggle icon table-nest-toggle-global' data-expand-text='<?php echo $lang->expand; ?>' data-collapse-text='<?php echo $lang->collapse;?>'></a>
<?php echo $lang->nameAB;?>
</th>
<th class='c-user'><?php echo $lang->execution->owner;?></th>
<th class='c-status'><?php echo $lang->project->status;?></th>
<th class='c-hours'><?php echo $lang->project->progress;?></th>
<th class='c-date'><?php echo $lang->programplan->begin;?></th>
<th class='c-date'><?php echo $lang->programplan->end;?></th>
<th class='c-hours'><?php echo $lang->task->estimateAB;?></th>
<th class='c-hours'><?php echo $lang->task->consumedAB;?></th>
<th class='c-hours'><?php echo $lang->task->leftAB;?> </th>
<th class='c-progress'><?php echo $lang->execution->burn;?> </th>
<th class='text-center c-actions-6'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody id="executionTableList">
<?php foreach($executionStats as $execution):?>
<?php $this->execution->printNestedList($execution, false, $users, $productID);?>
<?php endforeach;?>
</tbody>
</table>
<div class='table-footer'>
<?php $pager->show('right', 'pagerjs');?>
</div>
</form>
<?php endif;?>
</div>
<?php js::set('status', $status)?>
<?php js::set('orderBy', $orderBy)?>
<script>
$("#<?php echo $status;?>Tab").addClass('btn-active-text');
</script>
<?php include '../../common/view/footer.html.php';?>
+16 -4
View File
@@ -150,9 +150,10 @@
<div class='actions'><?php echo html::a("javascript:showLink({$release->id}, \"bug\")", '<i class="icon-bug"></i> ' . $lang->release->linkBug, '', "class='btn btn-primary'");?></div>
<div class='linkBox cell hidden'></div>
<?php endif;?>
<form class='main-table table-bug' method='post' target='hiddenwin' action="<?php echo inLink('batchUnlinkBug', "releaseID=$release->id");?>" id='linkedBugsForm' data-ride="table">
<form class='main-table table-bug' method='post' target='hiddenwin' id='linkedBugsForm' data-ride="table">
<table class='table has-sort-head' id='bugList'>
<?php $canBatchUnlink = common::hasPriv('projectrelease', 'batchUnlinkBug');?>
<?php $canBatchClose = common::hasPriv('bug', 'batchClose');?>
<?php $vars = "releaseID={$release->id}&type=bug&link=$link&param=$param&orderBy=%s";?>
<thead>
<tr class='text-center'>
@@ -178,7 +179,7 @@
<?php $bugLink = $this->createLink('bug', 'view', "bugID=$bug->id", '', true);?>
<tr>
<td class='c-id text-left'>
<?php if($canBatchUnlink and $canBeChanged):?>
<?php if(($canBatchUnlink or $canBatchClose) and $canBeChanged):?>
<div class="checkbox-primary">
<input type='checkbox' name='unlinkBugs[]' value='<?php echo $bug->id;?>'/>
<label></label>
@@ -208,10 +209,21 @@
</tbody>
</table>
<div class='table-footer'>
<?php if($countBugs and $canBatchUnlink and $canBeChanged):?>
<?php if($countBugs and ($canBatchUnlink or $canBatchClose) and $canBeChanged):?>
<div class="checkbox-primary check-all"><label><?php echo $lang->selectAll?></label></div>
<div class="table-actions btn-toolbar">
<?php echo html::submitButton($lang->release->batchUnlink, '', 'btn');?>
<?php
if($canBatchUnlink and $canBeChanged)
{
$unlinkURL = $this->createLink('projectrelease', 'batchUnlinkBug', "releaseID=$release->id");
echo html::a("###", $lang->release->batchUnlink, '', "onclick='setFormAction(\"$unlinkURL\", \"hiddenwin\", this)' class='btn'");
}
if($canBatchClose and $canBeChanged)
{
$closeURL = $this->createLink('bug', 'batchClose', "releaseID=$release->id&viewType=projectrelease");
echo html::a("###", $lang->story->batchClose, '', "onclick='setFormAction(\"$closeURL\", \"hiddenwin\", this)' class='btn'");
}
?>
</div>
<div class='table-statistic'><?php echo sprintf($lang->release->resolvedBugs, $countBugs);?></div>
<?php endif;?>
+16 -4
View File
@@ -152,9 +152,10 @@
<div class='actions'><?php echo html::a("javascript:showLink({$release->id}, \"bug\")", '<i class="icon-bug"></i> ' . $lang->release->linkBug, '', "class='btn btn-primary'");?></div>
<div class='linkBox cell hidden'></div>
<?php endif;?>
<form class='main-table table-bug' method='post' target='hiddenwin' action="<?php echo inLink('batchUnlinkBug', "releaseID=$release->id");?>" id='linkedBugsForm' data-ride="table">
<form class='main-table table-bug' method='post' target='hiddenwin' id='linkedBugsForm' data-ride="table">
<table class='table has-sort-head' id='bugList'>
<?php $canBatchUnlink = common::hasPriv('release', 'batchUnlinkBug');?>
<?php $canBatchClose = common::hasPriv('bug', 'batchClose');?>
<?php $vars = "releaseID={$release->id}&type=bug&link=$link&param=$param&orderBy=%s";?>
<thead>
<tr class='text-center'>
@@ -180,7 +181,7 @@
<?php $bugLink = $this->createLink('bug', 'view', "bugID=$bug->id", '', true);?>
<tr>
<td class='c-id text-left'>
<?php if($canBatchUnlink and $canBeChanged):?>
<?php if($canBatchUnlink or $canBatchClose and $canBeChanged):?>
<div class="checkbox-primary">
<input type='checkbox' name='unlinkBugs[]' value='<?php echo $bug->id;?>'/>
<label></label>
@@ -210,10 +211,21 @@
</tbody>
</table>
<div class='table-footer'>
<?php if($countBugs and $canBatchUnlink and $canBeChanged):?>
<?php if($countBugs and ($canBatchUnlink or $canBatchClose) and $canBeChanged):?>
<div class="checkbox-primary check-all"><label><?php echo $lang->selectAll?></label></div>
<div class="table-actions btn-toolbar">
<?php echo html::submitButton($lang->release->batchUnlink, '', 'btn');?>
<?php
if($canBatchUnlink)
{
$unlinkURL = inlink('batchUnlinkBug', "release=$release->id");
echo html::a('###', $lang->release->batchUnlink, '', "onclick='setFormAction(\"$unlinkURL\", \"hiddenwin\", this)' class='btn'");
}
if($canBatchClose)
{
$closeURL = $this->createLink('bug', 'batchClose', "release=$release->id&viewType=release");
echo html::a("###", $lang->story->batchClose, '', "onclick='setFormAction(\"$closeURL\", \"hiddenwin\", this)' class='btn'");
}
?>
</div>
<div class='table-statistic'><?php echo sprintf($lang->release->resolvedBugs, $countBugs);?></div>
<?php endif;?>
+2
View File
@@ -1,3 +1,5 @@
td.child {padding-left: 15px;}
.c-name {width: 200px;}
.c-count {width: 70px;}
#conditions {display: flex;}
#conditions .condition-options {margin-left: 16px;}
+3 -4
View File
@@ -22,10 +22,9 @@
<div class='panel'>
<div class="panel-heading">
<div class="panel-title">
<div class="table-row" id='conditions'>
<div class="col-xs"><?php echo $title;?></div>
<div class="col-xs text-right text-gray text-middle"><?php echo $lang->report->conditions?></div>
<div class='col'>
<div id='conditions'>
<div><?php echo $title;?></div>
<div class='condition-options'>
<div class="checkbox-primary inline-block">
<input type="checkbox" name="closedProduct" value="closedProduct" id="closedProduct" <?php if(strpos($conditions, 'closedProduct') !== false) echo "checked='checked'"?> />
<label for="closedProduct"><?php echo $lang->report->closedProduct?></label>
+1 -1
View File
@@ -248,7 +248,7 @@ foreach($fieldParams as $fieldName => $param)
echo html::submitButton($lang->search->common, '', 'btn btn-primary') . " &nbsp; ";
if($style != 'simple') echo html::commonButton($lang->search->reset, '', 'btn-reset-form btn');
echo html::commonButton('<i class="icon icon-chevron-double-down"></i>', '', 'btn-expand-form btn btn-info pull-right');
if($style != 'simple' and common::hasPriv('search', 'saveQuery')) echo html::a($this->createLink('search', 'saveQuery', "module=$module&onMenuBar=$onMenuBar"), '<span class="text"><i class="icon-bug-confirmBug icon-ok"></i> ' . $lang->search->saveCondition . '</span>', '', "class='btn-save-form btn btn-link btn-active-text text iframe' id='save-query'");
if($style != 'simple' and common::hasPriv('search', 'saveQuery')) echo html::a($this->createLink('search', 'saveQuery', "module=$module&onMenuBar=$onMenuBar"), '<span class="text"><i class="icon-bug-confirmBug icon-save"></i> ' . $lang->search->saveCondition . '</span>', '', "class='btn-save-form btn btn-link btn-active-text text iframe' id='save-query'");
echo html::hidden('formType', zget($formSession, 'formType', 'lite'));
?>
</td>
+42 -45
View File
@@ -162,12 +162,14 @@ class story extends control
$object = $this->dao->findById((int)$objectID)->from(TABLE_PROJECT)->fetch();
if($object->type != 'project')
{
if($this->config->systemMode == 'new') $this->action->create('story', $storyID, 'linked2project', '', $object->project);
$actionType = $object->type == 'kanban' ? 'linked2kanban' : 'linked2execution';
$this->loadModel('action')->create('story', $storyID, $actionType, '', $objectID);
$this->action->create('story', $storyID, $actionType, '', $objectID);
}
else
{
$this->loadModel('action')->create('story', $storyID, 'linked2project', '', $objectID);
$this->action->create('story', $storyID, 'linked2project', '', $objectID);
}
}
@@ -499,8 +501,8 @@ class story extends control
{
$products = array();
foreach($mails as $story) $products[$story->storyID] = $productID;
$this->execution->linkStory($executionID, $stories, $products, $extra, $lanes);
if($executionID != $this->session->project) $this->execution->linkStory($this->session->project, $stories, $products);
$this->execution->linkStory($executionID, $stories, $products, $extra, $lanes);
}
/* If storyID not equal zero, subdivide this story to child stories and close it. */
@@ -1200,8 +1202,7 @@ class story extends control
public function view($storyID, $version = 0, $param = 0)
{
$uri = $this->app->getURI(true);
$this->session->set('productList', $uri . "#app={$this->app->tab}", 'product');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('productList', $uri . "#app={$this->app->tab}", 'product');
$storyID = (int)$storyID;
$story = $this->story->getById($storyID, $version, true);
@@ -1485,10 +1486,11 @@ class story extends control
* Close a story.
*
* @param int $storyID
* @param string $from taskkanban
* @access public
* @return void
*/
public function close($storyID)
public function close($storyID, $from = '')
{
if(!empty($_POST))
{
@@ -1506,28 +1508,25 @@ class story extends control
if(isonlybody())
{
$execution = $this->execution->getByID($this->session->execution);
if($this->app->tab == 'execution')
$execution = $this->execution->getByID($this->session->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($this->app->tab == 'execution' and $execution->type == 'kanban')
{
$this->loadModel('kanban')->updateLane($this->session->execution, 'story', $storyID);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($this->session->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($this->session->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"story\", $kanbanData)"));
}
$kanbanData = $this->loadModel('kanban')->getRDKanban($this->session->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($this->session->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"story\", $kanbanData)"));
}
else
{
@@ -1853,10 +1852,11 @@ class story extends control
*
* @param int $storyID
* @param string $kanbanGroup
* @param string $from taskkanban
* @access public
* @return void
*/
public function assignTo($storyID, $kanbanGroup = 'default')
public function assignTo($storyID, $kanbanGroup = 'default', $from = '')
{
if(!empty($_POST))
{
@@ -1872,26 +1872,23 @@ class story extends control
if(isonlybody())
{
$execution = $this->execution->getByID($this->session->execution);
if($this->app->tab == 'execution')
$execution = $this->execution->getByID($this->session->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
if($this->app->tab == 'execution' and $execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($this->session->execution, $execLaneType, 'id_desc', 0, $kanbanGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($execution->id, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"story\", $kanbanData)"));
}
$kanbanData = $this->loadModel('kanban')->getRDKanban($this->session->execution, $execLaneType, 'id_desc', 0, $kanbanGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
elseif($from == 'taskkanban')
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($execution->id, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban('$execLaneType', $kanbanData)"));
}
else
{
+1 -1
View File
@@ -3931,7 +3931,7 @@ class storyModel extends model
if($story->stage == 'projected') $title = $this->lang->story->subDivideTip['projected'];
}
}
$menu .= $this->buildMenu('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params&executionID={$this->session->project}", $story, $type, 'split', '', 'showinonlybody', '', '', $title);
$menu .= $this->buildMenu('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=$story->module&$params", $story, $type, 'split', '', 'showinonlybody', '', '', $title);
}
if($this->app->rawModule == 'projectstory' and $this->config->vision != 'lite') $menu .= $this->buildMenu('projectstory', 'unlinkStory', "projectID={$this->session->project}&$params", $story, $type, 'unlink', 'hiddenwin', 'showinonlybody');
+248 -235
View File
@@ -49,6 +49,7 @@ class task extends control
$executionID = $this->execution->saveState($executionID, $executions);
$execution = $this->execution->getById($executionID);
$this->execution->setMenu($executionID);
if($this->app->tab == 'project') $this->loadModel('project')->setMenu($this->session->project);
$this->execution->getLimitedExecution();
$limitedExecutions = !empty($_SESSION['limitedExecutions']) ? $_SESSION['limitedExecutions'] : '';
@@ -347,6 +348,7 @@ class task extends control
/* Set menu. */
$this->execution->setMenu($execution->id);
if($this->app->tab == 'project') $this->loadModel('project')->setMenu($this->session->project);
/* When common task are child tasks, query whether common task are consumed. */
$taskConsumed = 0;
@@ -542,33 +544,27 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
return print(js::reload('parent.parent'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::reload('parent.parent'));
}
if(defined('RUN_MODE') && RUN_MODE == 'api')
@@ -581,6 +577,8 @@ class task extends control
}
}
if($this->app->tab == 'project') $this->loadModel('project')->setMenu($this->session->project);
$tasks = $this->task->getParentTaskPairs($this->view->execution->id, $this->view->task->parent);
if(isset($tasks[$taskID])) unset($tasks[$taskID]);
@@ -750,10 +748,11 @@ class task extends control
* @param int $requestID
* @param int $taskID
* @param string $kanbanGroup
* @param string $from
* @access public
* @return void
*/
public function assignTo($executionID, $taskID, $kanbanGroup = 'default')
public function assignTo($executionID, $taskID, $kanbanGroup = 'default', $from = '')
{
$this->commonAction($taskID);
$task = $this->task->getByID($taskID);
@@ -782,34 +781,28 @@ class task extends control
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success'));
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroup, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
if($from == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -910,6 +903,7 @@ class task extends control
$this->session->set('executionList', $this->app->getURI(true), 'execution');
$this->commonAction($taskID);
if($this->app->tab == 'project') $this->loadModel('project')->setMenu($this->session->project);
$taskID = (int)$taskID;
$task = $this->task->getById($taskID, true);
@@ -1046,34 +1040,28 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
if($output['from'] == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -1091,10 +1079,11 @@ class task extends control
* Record consumed and estimate.
*
* @param int $taskID
* @param string $from
* @access public
* @return void
*/
public function recordEstimate($taskID)
public function recordEstimate($taskID, $from = '')
{
$this->commonAction($taskID);
@@ -1123,33 +1112,27 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
if($from == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -1204,19 +1187,36 @@ class task extends control
*/
public function deleteEstimate($estimateID, $confirm = 'no')
{
if($confirm == 'no')
$estimate = $this->task->getEstimateById($estimateID);
$task = $this->task->getById($estimate->task);
if($confirm == 'no' and $task->consumed - $estimate->consumed != 0)
{
return print(js::confirm($this->lang->task->confirmDeleteEstimate, $this->createLink('task', 'deleteEstimate', "estimateID=$estimateID&confirm=yes")));
}
elseif($confirm == 'no' and $task->consumed - $estimate->consumed == 0)
{
return print(js::confirm($this->lang->task->confirmDeleteLastEstimate, $this->createLink('task', 'deleteEstimate', "estimateID=$estimateID&confirm=yes")));
}
else
{
$estimate = $this->task->getEstimateById($estimateID);
$changes = $this->task->deleteEstimate($estimateID);
if(dao::isError()) return print(js::error(dao::getError()));
$actionID = $this->loadModel('action')->create('task', $estimate->task, 'DeleteEstimate');
$this->action->logHistory($actionID, $changes);
return print(js::reload('parent'));
if($task->consumed - $estimate->consumed == 0)
{
$actionID = $this->loadModel('action')->create('task', $estimate->task, 'Adjusttasktowait');
$this->action->logHistory($actionID, $changes);
}
if($task->consumed - $estimate->consumed == 0)
{
return print(js::reload('parent.parent'));
}
else
{
return print(js::reload('parent'));
}
}
}
@@ -1273,34 +1273,28 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == "kanban")
{
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
if($output['from'] == "taskkanban")
{
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
}
if(defined('RUN_MODE') && RUN_MODE == 'api')
@@ -1375,35 +1369,29 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
if($output['from'] == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
@@ -1420,10 +1408,11 @@ class task extends control
* Restart task
*
* @param int $taskID
* @param string $from
* @access public
* @return void
*/
public function restart($taskID)
public function restart($taskID, $from = '')
{
$this->commonAction($taskID);
@@ -1446,34 +1435,28 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
}
else
if($from == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -1534,33 +1517,27 @@ class task extends control
}
}
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
if($output['from'] == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this', "function(){parent.parent.location.reload();}"));
}
if(defined('RUN_MODE') && RUN_MODE == 'api')
@@ -1710,35 +1687,29 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
if($output['from'] == 'taskkanban')
{
return print(js::closeModal('parent.parent', 'this'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -1781,35 +1752,29 @@ class task extends control
if(isonlybody())
{
if($this->app->tab == 'execution' or $this->config->vision == 'lite')
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == "kanban")
{
$task = $this->task->getById($taskID);
$execution = $this->execution->getByID($task->execution);
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
if($execution->type == 'kanban')
{
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
$regionID = !empty($output['regionID']) ? $output['regionID'] : 0;
$kanbanData = $this->loadModel('kanban')->getRDKanban($task->execution, $execLaneType, 'id_desc', $regionID, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, $regionID)"));
}
else
if($output['from'] == "taskkanban")
{
return print(js::closeModal('parent.parent', 'this'));
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($task->execution, $execLaneType, $execGroupBy, $rdSearchValue);
$kanbanType = $execLaneType == 'all' ? 'task' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)"));
}
return print(js::closeModal('parent.parent', 'this'));
}
return print(js::locate($this->createLink('task', 'view', "taskID=$taskID"), 'parent'));
}
@@ -1919,6 +1884,54 @@ class task extends control
echo html::select('task', empty($tasks) ? array('' => '') : $tasks, $taskID, "class='form-control'");
}
/**
* Ajax get tasks for execution list.
*
* @param int $executionID
* @param int $maxTaskID
* @access public
* @return void
*/
public function ajaxGetTasks($executionID, $maxTaskID = 0)
{
$this->loadModel('task');
$this->loadModel('execution');
$execution = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch();
$tasks = $this->dao->select('*')->from(TABLE_TASK)
->where('deleted')->eq(0)
->andWhere('status')->notin('closed,cancel')
->andWhere('execution')->eq($executionID)
->andWhere('id')->gt($maxTaskID)
->orderBy('id_asc')
->fetchAll('id');
$users = $this->loadModel('user')->getPairs('noletter|nodeleted');
foreach($tasks as $task)
{
if($task->parent > 0)
{
if(isset($tasks[$task->parent]))
{
$tasks[$task->parent]->children[$task->id] = $task;
unset($tasks[$task->id]);
}
}
}
$list = '';
$tasks = array_chunk($tasks, 50, true);
$tasks = $tasks[0];
$count = count($tasks);
foreach($tasks as $task)
{
$showmore = ($count == 50) && ($task == end($tasks));
$list .= $this->task->buildNestedList($execution, $task, false, $showmore, $users);
}
die($list);
}
/**
* AJAX: get the actions of a task. for web app.
*
+1
View File
@@ -1,3 +1,4 @@
.pl-10px {padding-left: 10px;}
#showAllModuleBox{width:60px;}
#showAllModuleBox .no-margin {padding-left:22px;}
#taskTeamEditor .sortable .input-group .input-group-addon.required:after {top: 10px; right: 2px; z-index: 3;}
+20 -8
View File
@@ -274,7 +274,7 @@ function setLane(regionID)
}
/* Get select of stories.*/
function setStories(moduleID, executionID)
function setStories(moduleID)
{
link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID + '&storyID=0&number=&type=full&status=unclosed');
$.get(link, function(stories)
@@ -530,8 +530,9 @@ $(document).on('click', '#testStory_chosen', function()
$('#modalTeam .btn').click(function()
{
var team = '';
var time = 0;
var team = '';
var time = 0;
var error = false;
/* Unique team. */
$('select[name^=team]').each(function(i)
@@ -548,9 +549,10 @@ $('#modalTeam .btn').click(function()
$('select[name^=team]').each(function()
{
if($(this).find('option:selected').text() != '')
var account = $(this).find('option:selected').text();
if(account != '')
{
team += ' ' + $(this).find('option:selected').text();
team += ' ' + account;
}
estimate = parseFloat($(this).parents('td').next('td').find('[name^=teamEstimate]').val());
@@ -559,9 +561,16 @@ $('#modalTeam .btn').click(function()
time += estimate;
}
$('#teamMember').val(team);
$('#estimate').val(time);
})
var requiredFieldList = ',' + requiredFields + ',';
if(account && requiredFieldList.indexOf(',estimate,') >= 0 && (estimate == 0 || isNaN(estimate)))
{
alert(estimateNotEmpty);
error = true;
return false;
}
});
if(error) return false;
var teamList = team.split(" ");
if(teamList.length <= 2)
{
@@ -570,6 +579,9 @@ $('#modalTeam .btn').click(function()
}
else
{
$('#teamMember').val(team);
$('#estimate').val(time);
if(config.onlybody == 'yes' && vision == 'lite')
{
$('.close').parent().click();
+19 -4
View File
@@ -176,6 +176,7 @@ $('#confirmButton').click(function()
var totalEstimate = 0;
var totalConsumed = oldConsumed;
var totalLeft = 0;
var error = false;
$('select[name^=team]').each(function()
{
if($(this).find('option:selected').text() == '') return;
@@ -190,22 +191,36 @@ $('#confirmButton').click(function()
left = parseFloat($(this).parents('td').next('td').find('[name^=teamLeft]').val());
if(!isNaN(left)) totalLeft += left;
var requiredFieldList = ',' + requiredFields + ',';
if(requiredFieldList.indexOf(',estimate,') >= 0 && (estimate == 0 || isNaN(estimate)))
{
$(this).val('').trigger("chosen:updated");
alert(estimateNotEmpty);
error = true;
return false;
}
})
$('#estimate').val(totalEstimate);
$('#consumedSpan').html(totalConsumed);
$('#left').val(totalLeft);
updateAssignedTo();
if(error) return false;
if(memberCount < 2)
{
alert(teamMemberError);
return false;
}
if(totalLeft == 0 && (taskStatus == 'doing' || taskStatus == 'pause'))
{
alert(totalLeftError);
return false;
}
$('#estimate').val(totalEstimate);
$('#consumedSpan').html(totalConsumed);
$('#left').val(totalLeft);
updateAssignedTo();
$('.close').click();
});
+21 -21
View File
@@ -214,27 +214,27 @@ $lang->task->legendDesc = 'Aufgabenbeschreibung';
$lang->task->legendDetail = 'Task Detail';
$lang->task->legendMisc = 'Misc.';
$lang->task->confirmDelete = "Möchten Sie diese Aufgabe löschen?";
$lang->task->confirmDeleteEstimate = "Möchten Sie das löschen?";
$lang->task->copyStoryTitle = "Story kopieren";
$lang->task->afterSubmit = "Nach der Erstellung,";
$lang->task->successSaved = "Erstellt!";
$lang->task->delayWarning = " <strong class='text-danger'> Verzögerung %s Tage </strong>";
$lang->task->remindBug = "Diese Aufgabe wurde aus einem Bus erzeugt. Möchten Sie den Bug aktualisieren:%s?";
$lang->task->remindIssue = "This task is converted from a issue. Do you want to update the Issue:%s?";
$lang->task->confirmChangeExecution = "Wenn Sie {$lang->executionCommon} ändern, das verwendete Modul, Story and Assignor will be changed. Do you want to do it?";
$lang->task->confirmFinish = '"Reststunden " sind 0. Möchten Sue den Status auf Erledigt setzen?';
$lang->task->confirmRecord = '"Reststunden " sind 0. Möchten Sie den Task auf Erledigt setzen?';
$lang->task->confirmTransfer = '"Reststunden " sind 0. Möchten Sie den Task transferieren?';
$lang->task->noticeTaskStart = '"Cost Hour" and "Left Hour" cannot be 0 at the same time.';
$lang->task->noticeLinkStory = "Es wurde keine Story verknüpft. Sie können %s für dieses Projekt, anschließend %s.";
$lang->task->noticeSaveRecord = 'Ihre Stunden wurden nicht gespeichrt. Bitte erst speichern.';
$lang->task->commentActions = '%s. %s, kommentiert von <strong>%s</strong>.';
$lang->task->deniedNotice = 'Nur %s kann die Aufgabe %s.';
$lang->task->noTask = 'Keine Aufagben. ';
$lang->task->createDenied = 'Aufgben erstellen it in diesem Projekt gesperrt';
$lang->task->cannotDeleteParent = 'Cannot delete parent task';
$lang->task->addChildTask = 'Because the task has already consumed consumption, to ensure data consistency, we will help you create a subtask with the same name to record the consumption.';
$lang->task->confirmDelete = "Möchten Sie diese Aufgabe löschen?";
$lang->task->confirmDeleteLastEstimate = "Do you want to delete the log? After deleting the last work log, the task status will be adjusted to Not Started.";
$lang->task->copyStoryTitle = "Story kopieren";
$lang->task->afterSubmit = "Nach der Erstellung,";
$lang->task->successSaved = "Erstellt!";
$lang->task->delayWarning = " <strong class='text-danger'> Verzögerung %s Tage </strong>";
$lang->task->remindBug = "Diese Aufgabe wurde aus einem Bus erzeugt. Möchten Sie den Bug aktualisieren:%s?";
$lang->task->remindIssue = "This task is converted from a issue. Do you want to update the Issue:%s?";
$lang->task->confirmChangeExecution = "Wenn Sie {$lang->executionCommon} ändern, das verwendete Modul, Story and Assignor will be changed. Do you want to do it?";
$lang->task->confirmFinish = '"Reststunden " sind 0. Möchten Sue den Status auf Erledigt setzen?';
$lang->task->confirmRecord = '"Reststunden " sind 0. Möchten Sie den Task auf Erledigt setzen?';
$lang->task->confirmTransfer = '"Reststunden " sind 0. Möchten Sie den Task transferieren?';
$lang->task->noticeTaskStart = '"Cost Hour" and "Left Hour" cannot be 0 at the same time.';
$lang->task->noticeLinkStory = "Es wurde keine Story verknüpft. Sie können %s für dieses Projekt, anschließend %s.";
$lang->task->noticeSaveRecord = 'Ihre Stunden wurden nicht gespeichrt. Bitte erst speichern.';
$lang->task->commentActions = '%s. %s, kommentiert von <strong>%s</strong>.';
$lang->task->deniedNotice = 'Nur %s kann die Aufgabe %s.';
$lang->task->noTask = 'Keine Aufagben. ';
$lang->task->createDenied = 'Aufgben erstellen it in diesem Projekt gesperrt';
$lang->task->cannotDeleteParent = 'Cannot delete parent task';
$lang->task->addChildTask = 'Because the task has already consumed consumption, to ensure data consistency, we will help you create a subtask with the same name to record the consumption.';
$lang->task->error = new stdclass();
$lang->task->error->totalNumber = '"Total Cost" must be numbers.';
+22 -21
View File
@@ -214,27 +214,28 @@ $lang->task->legendDesc = 'Task Description';
$lang->task->legendDetail = 'Task Detail';
$lang->task->legendMisc = 'Misc.';
$lang->task->confirmDelete = "Do you want to delete this task?";
$lang->task->confirmDeleteEstimate = "Do you want to delete it?";
$lang->task->copyStoryTitle = "Copy Story";
$lang->task->afterSubmit = "Next ";
$lang->task->successSaved = "Created!";
$lang->task->delayWarning = " <strong class='text-danger'> Delay %s days </strong>";
$lang->task->remindBug = "This task is converted from a bug. Do you want to update the Bug:%s?";
$lang->task->remindIssue = "This task is converted from a issue. Do you want to update the Issue:%s?";
$lang->task->confirmChangeExecution = "If you change {$lang->executionCommon}, Module, Story and AssignedTo will also be changed. Do you want to change it?";
$lang->task->confirmFinish = '"Left Hour" is 0. Do you want to change the status to "Finished"?';
$lang->task->confirmRecord = '"Left Hour" is 0. Do you want to set the task as "Finished"?';
$lang->task->confirmTransfer = '"Left Hour" is 0,Do you want to transfer task?';
$lang->task->noticeTaskStart = '"Cost Hour" and "Left Hour" cannot be 0 at the same time.';
$lang->task->noticeLinkStory = "No story has been linked. You can %s for this project, then %s.";
$lang->task->noticeSaveRecord = 'Your Hour is not saved. Please save it first.';
$lang->task->commentActions = '%s. %s, commented by <strong>%s</strong>.';
$lang->task->deniedNotice = 'Only the %s can %s the task.';
$lang->task->noTask = 'No tasks yet. ';
$lang->task->createDenied = 'Create Task is denied in this project';
$lang->task->cannotDeleteParent = 'Cannot delete parent task';
$lang->task->addChildTask = 'Because the task has cost hours, ZenTao will create a child task with the same name to record the cost housrs to ensure data consistency.';
$lang->task->confirmDelete = "Do you want to delete this task?";
$lang->task->confirmDeleteEstimate = "Do you want to delete it?";
$lang->task->confirmDeleteLastEstimate = "Do you want to delete the log? After deleting the last work log, the task status will be adjusted to Not Started.";
$lang->task->copyStoryTitle = "Copy Story";
$lang->task->afterSubmit = "Next ";
$lang->task->successSaved = "Created!";
$lang->task->delayWarning = " <strong class='text-danger'> Delay %s days </strong>";
$lang->task->remindBug = "This task is converted from a bug. Do you want to update the Bug:%s?";
$lang->task->remindIssue = "This task is converted from a issue. Do you want to update the Issue:%s?";
$lang->task->confirmChangeExecution = "If you change {$lang->executionCommon}, Module, Story and AssignedTo will also be changed. Do you want to change it?";
$lang->task->confirmFinish = '"Left Hour" is 0. Do you want to change the status to "Finished"?';
$lang->task->confirmRecord = '"Left Hour" is 0. Do you want to set the task as "Finished"?';
$lang->task->confirmTransfer = '"Left Hour" is 0,Do you want to transfer task?';
$lang->task->noticeTaskStart = '"Cost Hour" and "Left Hour" cannot be 0 at the same time.';
$lang->task->noticeLinkStory = "No story has been linked. You can %s for this project, then %s.";
$lang->task->noticeSaveRecord = 'Your Hour is not saved. Please save it first.';
$lang->task->commentActions = '%s. %s, commented by <strong>%s</strong>.';
$lang->task->deniedNotice = 'Only the %s can %s the task.';
$lang->task->noTask = 'No tasks yet. ';
$lang->task->createDenied = 'Create Task is denied in this project';
$lang->task->cannotDeleteParent = 'Cannot delete parent task';
$lang->task->addChildTask = 'Because the task has cost hours, ZenTao will create a child task with the same name to record the cost housrs to ensure data consistency.';
$lang->task->error = new stdclass();
$lang->task->error->totalNumber = '"Total Cost" must be numbers.';
+22 -21
View File
@@ -214,27 +214,28 @@ $lang->task->legendDesc = 'Description Tâche';
$lang->task->legendDetail = 'Task Detail';
$lang->task->legendMisc = 'Misc.';
$lang->task->confirmDelete = "Voulez-vous réellement supprimer cette tâche ?";
$lang->task->confirmDeleteEstimate = "Voulez-vous la supprimer ?";
$lang->task->copyStoryTitle = '<span style="color:blue;">StoryModel</span>';
$lang->task->afterSubmit = "Suiv. ";
$lang->task->successSaved = "Créée !";
$lang->task->delayWarning = " <strong class='text-danger'> Retard %s jours </strong>";
$lang->task->remindBug = "Cette tâche a été convertie depuis un bug. Voulez-vous mettre à jour le Bug : %s ?";
$lang->task->remindIssue = "This task is converted from a issue. Do you want to update the Issue:%s?";
$lang->task->confirmChangeExecution = "Si vous changez {$lang->executionCommon}, Module, Story et AssignedTo seront également changés. Voulez-vous le faire ?";
$lang->task->confirmFinish = '"Heures Restantes" à 0. Voulez-vous passer le statut à "Terminée" ?';
$lang->task->confirmRecord = '"Heures Restantes" à 0. Voulez-vous passer le statut de la tâche à "Finie" ?';
$lang->task->confirmTransfer = '"Heures Restantes" à 0. Voulez-vous transférer la tâche ?';
$lang->task->noticeTaskStart = '"Cost Hour" and "Left Hour" cannot be 0 at the same time.';
$lang->task->noticeLinkStory = "Aucune Story n'est associée. Vous pouvez %s pour ce projet, alors %s.";
$lang->task->noticeSaveRecord = "Votre temps n'a pas été sauvé. Enregistrez-le d'abord.";
$lang->task->commentActions = '%s. %s, commenté par <strong>%s</strong>.';
$lang->task->deniedNotice = 'Seulement le %s peut %s la tâche.';
$lang->task->noTask = "Pas de tâche pour l'instant. ";
$lang->task->createDenied = 'La création de tâches est interdite dans ce projet';
$lang->task->cannotDeleteParent = 'Impossible de supprimer la tâche parente';
$lang->task->addChildTask = 'Because the task has already consumed consumption, to ensure data consistency, we will help you create a subtask with the same name to record the consumption.';
$lang->task->confirmDelete = "Voulez-vous réellement supprimer cette tâche ?";
$lang->task->confirmDeleteEstimate = "Voulez-vous la supprimer ?";
$lang->task->confirmDeleteLastEstimate = "Do you want to delete the log? After deleting the last work log, the task status will be adjusted to Not Started.";
$lang->task->copyStoryTitle = '<span style="color:blue;">StoryModel</span>';
$lang->task->afterSubmit = "Suiv. ";
$lang->task->successSaved = "Créée !";
$lang->task->delayWarning = " <strong class='text-danger'> Retard %s jours </strong>";
$lang->task->remindBug = "Cette tâche a été convertie depuis un bug. Voulez-vous mettre à jour le Bug : %s ?";
$lang->task->remindIssue = "This task is converted from a issue. Do you want to update the Issue:%s?";
$lang->task->confirmChangeExecution = "Si vous changez {$lang->executionCommon}, Module, Story et AssignedTo seront également changés. Voulez-vous le faire ?";
$lang->task->confirmFinish = '"Heures Restantes" à 0. Voulez-vous passer le statut à "Terminée" ?';
$lang->task->confirmRecord = '"Heures Restantes" à 0. Voulez-vous passer le statut de la tâche à "Finie" ?';
$lang->task->confirmTransfer = '"Heures Restantes" à 0. Voulez-vous transférer la tâche ?';
$lang->task->noticeTaskStart = '"Cost Hour" and "Left Hour" cannot be 0 at the same time.';
$lang->task->noticeLinkStory = "Aucune Story n'est associée. Vous pouvez %s pour ce projet, alors %s.";
$lang->task->noticeSaveRecord = "Votre temps n'a pas été sauvé. Enregistrez-le d'abord.";
$lang->task->commentActions = '%s. %s, commenté par <strong>%s</strong>.';
$lang->task->deniedNotice = 'Seulement le %s peut %s la tâche.';
$lang->task->noTask = "Pas de tâche pour l'instant. ";
$lang->task->createDenied = 'La création de tâches est interdite dans ce projet';
$lang->task->cannotDeleteParent = 'Impossible de supprimer la tâche parente';
$lang->task->addChildTask = 'Because the task has already consumed consumption, to ensure data consistency, we will help you create a subtask with the same name to record the consumption.';
$lang->task->error = new stdclass();
$lang->task->error->totalNumber = '"Coût Total" doit être numérique.';
+22 -21
View File
@@ -214,27 +214,28 @@ $lang->task->legendDesc = '任务描述';
$lang->task->legendDetail = '任务详情';
$lang->task->legendMisc = '其他相关';
$lang->task->confirmDelete = "您确定要删除这个任务吗?";
$lang->task->confirmDeleteEstimate = "您确定要删除这个记录吗?";
$lang->task->copyStoryTitle = "同{$lang->SRCommon}";
$lang->task->afterSubmit = "添加之后";
$lang->task->successSaved = "成功添加,";
$lang->task->delayWarning = " <strong class='text-danger'> 延期%s天 </strong>";
$lang->task->remindBug = "该任务为Bug转化得到,是否更新Bug:%s ?";
$lang->task->remindIssue = "该任务为问题转化得到,是否更新问题:%s ?";
$lang->task->confirmChangeExecution = "修改{$lang->executionCommon}会导致相应的所属模块、相关{$lang->SRCommon}和指派人发生变化,确定吗?";
$lang->task->confirmFinish = '"预计剩余"为0,确认将任务状态改为"已完成"吗?';
$lang->task->confirmRecord = '"剩余"为0,任务将标记为"已完成",您确定吗?';
$lang->task->confirmTransfer = '"当前剩余"为0,任务将被转交,您确定吗?';
$lang->task->noticeTaskStart = '"总计消耗"和"预计剩余"不能同时为0';
$lang->task->noticeLinkStory = "没有可关联的相关{$lang->SRCommon},您可以为当前项目%s,然后%s";
$lang->task->noticeSaveRecord = '您有尚未保存的工时记录,请先将其保存。';
$lang->task->commentActions = '%s. %s, 由 <strong>%s</strong> 添加备注。';
$lang->task->deniedNotice = '当前任务只有%s才可以%s。';
$lang->task->noTask = '暂时没有任务。';
$lang->task->createDenied = '你不能在该项目添加任务';
$lang->task->cannotDeleteParent = '不能删除父任务。';
$lang->task->addChildTask = '因该任务已经产生消耗,为保证数据一致性,我们会帮您创建一条同名子任务记录该消耗。';
$lang->task->confirmDelete = "您确定要删除这个任务吗?";
$lang->task->confirmDeleteEstimate = "您确定要删除这个记录吗?";
$lang->task->confirmDeleteLastEstimate = "您确定要删除该条日志吗?删除最后一条工时日志后,该任务状态将调整为未开始。";
$lang->task->copyStoryTitle = "同{$lang->SRCommon}";
$lang->task->afterSubmit = "添加之后";
$lang->task->successSaved = "成功添加,";
$lang->task->delayWarning = " <strong class='text-danger'> 延期%s天 </strong>";
$lang->task->remindBug = "该任务为Bug转化得到,是否更新Bug:%s ?";
$lang->task->remindIssue = "该任务为问题转化得到,是否更新问题:%s ?";
$lang->task->confirmChangeExecution = "修改{$lang->executionCommon}会导致相应的所属模块、相关{$lang->SRCommon}和指派人发生变化,确定吗?";
$lang->task->confirmFinish = '"预计剩余"为0,确认将任务状态改为"已完成"吗?';
$lang->task->confirmRecord = '"剩余"为0,任务将标记为"已完成",您确定吗?';
$lang->task->confirmTransfer = '"当前剩余"为0,任务将被转交,您确定吗?';
$lang->task->noticeTaskStart = '"总计消耗"和"预计剩余"不能同时为0';
$lang->task->noticeLinkStory = "没有可关联的相关{$lang->SRCommon},您可以为当前项目%s,然后%s";
$lang->task->noticeSaveRecord = '您有尚未保存的工时记录,请先将其保存。';
$lang->task->commentActions = '%s. %s, 由 <strong>%s</strong> 添加备注。';
$lang->task->deniedNotice = '当前任务只有%s才可以%s。';
$lang->task->noTask = '暂时没有任务。';
$lang->task->createDenied = '你不能在该项目添加任务';
$lang->task->cannotDeleteParent = '不能删除父任务。';
$lang->task->addChildTask = '因该任务已经产生消耗,为保证数据一致性,我们会帮您创建一条同名子任务记录该消耗。';
$lang->task->error = new stdclass();
$lang->task->error->totalNumber = '"总计消耗"必须为数字';
+74 -3
View File
@@ -719,7 +719,6 @@ class taskModel extends model
$task->assignedTo = $childTask->assignedTo;
$task->assignedDate = $now;
}
$task->finishedBy = '';
$task->finishedDate = '';
$task->closedBy = '';
@@ -749,6 +748,7 @@ class taskModel extends model
if($status == 'doing' and $parentTask->status == 'wait') $action = 'Started';
if($status == 'doing' and $parentTask->status == 'pause') $action = 'Restarted';
if($status == 'doing' and $parentTask->status != 'wait' and $parentTask->status != 'pause') $action = 'Activated';
if($status == 'wait' and $parentTask->status != 'wait') $action = 'Adjusttasktowait';
if($action)
{
$actionID = $this->loadModel('action')->create('task', $parentID, $action, '', '', '', false);
@@ -833,7 +833,8 @@ class taskModel extends model
$currentTask->consumed += (float)$member->consumed;
$currentTask->left += (float)$member->left;
}
if(empty($oldTask->team) and isset($oldTask->consumed)) $currentTask->consumed += (float)$oldTask->consumed;
if($this->app->rawMethod == 'edit' and empty($oldTask->team) and isset($oldTask->consumed)) $currentTask->consumed += (float)$oldTask->consumed;
if(!empty($task))
{
@@ -2834,7 +2835,19 @@ class taskModel extends model
$data->consumed = $consumed;
$data->left = $left;
$data->status = ($left == 0 && $consumed != 0) ? 'done' : $task->status;
if($left == 0 and $consumed != 0)
{
$data->status = 'done';
}
elseif($consumed == 0)
{
$data->status = 'wait';
$data->left = $task->left + $estimate->consumed;
}
else
{
$data->status = $task->status;
}
if(!empty($task->team))
{
$oldConsumed = $task->team[$estimate->account]->consumed;
@@ -3734,6 +3747,64 @@ class taskModel extends model
->fetchPairs('actor');
}
/**
* Build nested list.
*
* @param objecct $execution
* @param object $task
* @param bool $isChild
* @param bool $showmore
* @access public
* @return string
*/
public function buildNestedList($execution, $task, $isChild = false, $showmore = false, $users)
{
$showmore = $showmore ? 'showmore' : '';
$trAttrs = "data-id=$task->id";
if(!$isChild)
{
$path = $execution->grade == 2 ? "$execution->parent,$execution->id,$task->id," : ",$execution->id,$task->id,";
$trAttrs .= " data-parent=$execution->id data-nest-parent=$execution->id data-nest-path=$path";
if(empty($task->children)) $trAttrs .= " data-nested='false'";
$trClass = empty($task->children) ? '' : " has-nest-child";
}
else
{
$path = $execution->grade == 2 ? "$execution->parent,$execution->id,$task->parent,$task->id," : ",$execution->id,$task->parent,$task->id,";
$trClass = 'is-nest-child no-nest';
$trAttrs .= " data-nested='false' data-parent=$task->parent data-nest-parent=$task->parent data-nest-path=$path";
}
$list = "<tr $trAttrs class='$trClass $showmore'>";
$list .= '<td>';
if($task->parent > 0) $list .= '<span class="label label-badge label-light" title="' . $this->lang->task->children . '">' . $this->lang->task->childrenAB . '</span> ';
$list .= html::a(helper::createLink('task', 'view', "id=$task->id"), $task->name, '', "data-app='project'");
$list .= '</td>';
$list .= '<td>' . zget($users, $task->assignedTo, '') . '</td>';
$list .= "<td class='status-{$task->status}'>" . $this->processStatus('task', $task) . '</td>';
$list .= '<td></td>';
$list .= '<td>' . $task->estStarted . '</td>';
$list .= '<td>' . $task->deadline . '</td>';
$list .= '<td>' . $task->estimate . $this->lang->execution->workHourUnit . '</td>';
$list .= '<td>' . $task->consumed . $this->lang->execution->workHourUnit . '</td>';
$list .= '<td>' . $task->left . $this->lang->execution->workHourUnit . '</td>';
$list .= '<td></td>';
$list .= '<td class="c-actions">';
$list .= $this->buildOperateMenu($task, 'browse');
$list .= '</td></tr>';
if(!empty($task->children))
{
foreach($task->children as $child)
{
$showmore = (count($task->children) == 50) && ($child == end($task->children));
$list .= $this->buildNestedList($execution, $child, true, $showmore, $users);
}
}
return $list;
}
/**
* Build task menu.
*
+1 -1
View File
@@ -118,7 +118,7 @@ $colspan = count($visibleFields) + 3;
<tr>
<td class='text-left c-id'><?php echo $i;?></td>
<td class="<?php echo zget($visibleFields, 'module', 'hidden')?> moduleBox" style='overflow:visible'>
<?php echo html::select("module[$i]", $modules, $moduleID, "class='form-control chosen'")?>
<?php echo html::select("module[$i]", $modules, $moduleID, "class='form-control chosen' onchange='setStories(this.value, $execution->id)'")?>
<?php echo html::hidden("parent[$i]", $parent);?>
</td>
<?php if($execution->type != 'ops'):?>
+6 -2
View File
@@ -13,17 +13,21 @@
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php include '../../common/view/sortable.html.php';?>
<?php js::set('executionID', $execution->id);?>
<?php js::set('toTaskList', !empty($task->id));?>
<?php js::set('blockID', $blockID);?>
<?php js::set('teamMemberError', $lang->task->error->teamMember);?>
<?php js::set('vision', $config->vision);?>
<?php js::set('requiredFields', $config->task->create->requiredFields);?>
<?php js::set('estimateNotEmpty', sprintf($lang->error->notempty, $lang->task->estimate))?>
<?php if(!empty($storyID)):?>
<style> .title-group.required > .required:after {right: 110px;}</style>
<?php endif;?>
<?php
$requiredFields = array();
foreach(explode(',', $config->task->create->requiredFields) as $field)
{
if($field) $requiredFields[$field] = '';
if($field and strpos($showFields, $field) === false) $showFields .= ',' . $field;
}
?>
@@ -56,7 +60,7 @@ foreach(explode(',', $config->task->create->requiredFields) as $field)
</tr>
<tr>
<th><?php echo $lang->task->module;?></th>
<td id='moduleIdBox'><?php echo html::select('module', $moduleOptionMenu, $task->module, "class='form-control chosen'");?></td>
<td id='moduleIdBox'><?php echo html::select('module', $moduleOptionMenu, $task->module, "class='form-control chosen' onchange='setStories(this.value)'");?></td>
<td>
<div class="checkbox-primary c-modulel">
<input type="checkbox" id="showAllModule" <?php if($showAllModule) echo 'checked';?>><label for="showAllModule" class="no-margin"><?php echo $lang->task->allModule;?></label>
@@ -283,7 +287,7 @@ foreach(explode(',', $config->task->create->requiredFields) as $field)
<tbody class='sortable'>
<tr class='template'>
<td><?php echo html::select("team[]", $members, '', "class='form-control chosen'");?></td>
<td>
<td class="<?php echo zget($requiredFields, 'estimate', '', ' required')?>">
<div class='input-group'>
<?php echo html::input("teamEstimate[]", '', "class='form-control text-center' placeholder='{$lang->task->estimateAB}'") ?>
<span class='input-group-addon'><?php echo $lang->task->hour;?></span>
+11 -2
View File
@@ -27,6 +27,15 @@
<?php js::set('newRowCount', count($task->team) < 6 ? 6 - count($task->team) : 1);?>
<?php js::set('teamMemberError', $lang->task->error->teamMember);?>
<?php js::set('totalLeftError', sprintf($this->lang->task->error->leftEmptyAB, $this->lang->task->statusList[$task->status]));?>
<?php js::set('estimateNotEmpty', sprintf($lang->error->notempty, $lang->task->estimate))?>
<?php js::set('requiredFields', $config->task->edit->requiredFields);?>
<?php
$requiredFields = array();
foreach(explode(',', $config->task->edit->requiredFields) as $field)
{
if($field) $requiredFields[$field] = '';
}
?>
<div class='main-content' id='mainContent'>
<form method='post' enctype='multipart/form-data' target='hiddenwin' id='dataform'>
<div class='main-header'>
@@ -275,7 +284,7 @@
<td class='w-250px'><?php echo html::select("team[]", $members, $member->account, "class='form-control chosen'")?></td>
<td>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->task->estimate?></span>
<span class="input-group-addon <?php echo zget($requiredFields, 'estimate', '', ' required');?>"><?php echo $lang->task->estimate?></span>
<?php echo html::input("teamEstimate[]", (float)$member->estimate, "class='form-control text-center' placeholder='{$lang->task->hour}'")?>
<span class='input-group-addon fix-border'><?php echo $lang->task->consumed?></span>
<?php echo html::input("teamConsumed[]", (float)$member->consumed, "class='form-control text-center' readonly placeholder='{$lang->task->hour}'")?>
@@ -294,7 +303,7 @@
<td class='w-250px'><?php echo html::select("team[]", $members, '', "class='form-control chosen'")?></td>
<td>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->task->estimate?></span>
<span class="input-group-addon <?php echo zget($requiredFields, 'estimate', '', ' required');?>"><?php echo $lang->task->estimate?></span>
<?php echo html::input("teamEstimate[]", '', "class='form-control text-center' placeholder='{$lang->task->hour}'")?>
<span class='input-group-addon fix-border'><?php echo $lang->task->consumed?></span>
<?php echo html::input("teamConsumed[]", '', "class='form-control text-center' placeholder='{$lang->task->hour}'")?>
+24
View File
@@ -1403,10 +1403,33 @@ class testcase extends control
$case = $this->testcase->getById($caseID);
$libCase = $this->testcase->getById($libcaseID);
$version = $case->version + 1;
$this->dao->delete()->from(TABLE_FILE)->where('objectType')->eq('testcase')->andWhere('objectID')->eq($caseID)->exec();
foreach($libCase->files as $fileID => $file)
{
$fileName = pathinfo($file->pathname, PATHINFO_FILENAME);
$datePath = substr($file->pathname, 0, 6);
$realPath = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . "{$datePath}/" . $fileName;
$rand = rand();
$newFileName = $fileName . 'copy' . $rand;
$newFilePath = $this->app->getAppRoot() . "www/data/upload/{$this->app->company->id}/" . "{$datePath}/" . $newFileName;
copy($realPath, $newFilePath);
$newFileName = $file->pathname;
$newFileName = str_replace('.', "copy$rand.", $newFileName);
unset($file->id, $file->realPath, $file->webPath);
$file->objectID = $caseID;
$file->pathname = $newFileName;
$this->dao->insert(TABLE_FILE)->data($file)->exec();
}
$this->dao->update(TABLE_CASE)
->set('version')->eq($version)
->set('fromCaseVersion')->eq($version)
->set('precondition')->eq($libCase->precondition)
->set('title')->eq($libCase->title)
->where('id')->eq($caseID)
->exec();
@@ -1417,6 +1440,7 @@ class testcase extends control
$step->version = $version;
$this->dao->insert(TABLE_CASESTEP)->data($step)->exec();
}
echo js::locate($this->createLink('testcase', 'view', "caseID=$caseID&version=$version"), 'parent');
}
+11
View File
@@ -1,5 +1,16 @@
$(document).ready(function()
{
if(isLibCase)
{
$('#subNavbar [data-id=testcase]').removeClass('active');
$('#navbar [data-id=testcase]').removeClass('active');
}
else
{
$('#subNavbar [data-id=testcase]').addClass('active');
$('#navbar [data-id=testcase]').addClass('active');
}
if(config.onlybody != 'yes')$(".runCase").modalTrigger({width:'90%', type:'iframe', afterHide:function(){parent.location.href=parent.location.href;}});
if(config.onlybody != 'yes')$(".results").modalTrigger({width:'90%', type:'iframe'});
+22 -12
View File
@@ -285,7 +285,7 @@ class testcaseModel extends model
->leftJoin(TABLE_STORY)->alias('t4')->on('t3.story=t4.id')
->where('t1.project')->in($executions)
->beginIF(!empty($productID))->andWhere('t2.product')->eq((int)$productID)->fi()
->beginIF($branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi()
->beginIF(!empty($productID) and $branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi()
->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
->beginIF($browseType == 'wait')->andWhere('t2.status')->eq($browseType)->fi()
->beginIF($auto == 'unit')->andWhere('t2.auto')->eq('unit')->fi()
@@ -342,7 +342,7 @@ class testcaseModel extends model
->where('t1.project')->eq((int)$executionID)
->beginIF(!empty($productID))->andWhere('t1.product')->eq($productID)->fi()
->beginIF(!empty($moduleID))->andWhere('t4.path')->like("%,$moduleID,%")->fi()
->andWhere('t2.branch')->eq($branchID)
->beginIF(!empty($productID))->andWhere('t2.branch')->eq($branchID)->fi()
->andWhere('t2.deleted')->eq('0')
->andWhere('t3.version > t2.storyVersion')
->andWhere("t3.status")->eq('active')
@@ -358,7 +358,7 @@ class testcaseModel extends model
->beginIF($browseType != 'all' and $browseType != 'byModule')->andWhere('t2.status')->eq($browseType)->fi()
->beginIF(!empty($productID))->andWhere('t1.product')->eq($productID)->fi()
->beginIF(!empty($moduleID))->andWhere('t3.path')->like("%,$moduleID,%")->fi()
->andWhere('t2.branch')->eq($branchID)
->beginIF(!empty($productID))->andWhere('t2.branch')->eq($branchID)->fi()
->andWhere('t2.deleted')->eq('0')
->orderBy($orderBy)
->page($pager)
@@ -520,7 +520,7 @@ class testcaseModel extends model
->andWhere('t2.version > t1.storyVersion')
->beginIF(!empty($productID))->andWhere('t1.product')->eq($productID)->fi()
->beginIF($this->app->tab == 'project')->andWhere('t3.project')->eq($this->session->project)->fi()
->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi()
->beginIF($branch !== 'all' and !empty($productID))->andWhere('t1.branch')->eq($branch)->fi()
->beginIF($modules)->andWhere('t1.module')->in($modules)->fi()
->beginIF($auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi()
->beginIF($auto == 'unit')->andWhere('t1.auto')->eq('unit')->fi()
@@ -799,15 +799,12 @@ class testcaseModel extends model
$this->dao->update(TABLE_CASE)->data($case)->autoCheck()->batchCheck($requiredFields, 'notempty')->checkFlow()->where('id')->eq((int)$caseID)->exec();
if(!$this->dao->isError())
{
$isLibCase = ($oldCase->lib and empty($oldCase->product));
$titleChanged = ($case->title != $oldCase->title);
if($isLibCase and $titleChanged) $this->dao->update(TABLE_CASE)->set('`title`')->eq($case->title)->where('`fromCaseID`')->eq($caseID)->exec();
$this->updateCase2Project($oldCase, $case, $caseID);
if($stepChanged)
{
$parentStepID = 0;
$isLibCase = ($oldCase->lib and empty($oldCase->product));
if($isLibCase)
{
$fromcaseVersion = $this->dao->select('fromCaseVersion')->from(TABLE_CASE)->where('fromCaseID')->eq($caseID)->fetch('fromCaseVersion');
@@ -1103,6 +1100,11 @@ class testcaseModel extends model
foreach($cases as $caseID => $case)
{
$oldCase = $this->getByID($caseID);
$caseChanged = false;
if($oldCase->title != $case->title) $caseChanged = true;
if($oldCase->precondition != $case->precondition) $caseChanged = true;
$this->dao->update(TABLE_CASE)->data($case)
->autoCheck()
->batchCheck($this->config->testcase->edit->requiredFields, 'notempty')
@@ -1112,11 +1114,7 @@ class testcaseModel extends model
if(!dao::isError())
{
$isLibCase = ($oldCase->lib and empty($oldCase->product));
$titleChanged = ($case->title != $oldCase->title);
$case->product = $oldCase->product;
if($isLibCase and $titleChanged) $this->dao->update(TABLE_CASE)->set('`title`')->eq($case->title)->where('`fromCaseID`')->eq($caseID)->exec();
$this->updateCase2Project($oldCase, $case, $caseID);
$this->executeHooks($caseID);
@@ -1138,6 +1136,14 @@ class testcaseModel extends model
}
}
}
$isLibCase = ($oldCase->lib and empty($oldCase->product));
if($isLibCase and $caseChanged)
{
$fromcaseVersion = $this->dao->select('fromCaseVersion')->from(TABLE_CASE)->where('fromCaseID')->eq($caseID)->fetch('fromCaseVersion');
$fromcaseVersion += 1;
$this->dao->update(TABLE_CASE)->set('`fromCaseVersion`')->eq($fromcaseVersion)->where('`fromCaseID`')->eq($caseID)->exec();
}
}
else
{
@@ -2357,6 +2363,10 @@ class testcaseModel extends model
if(!$this->forceNotReview() and $stepChanged) $status = 'wait';
if(!empty($_POST['title']) and $case->title != $this->post->title) $stepChanged = true;
if(!empty($_POST['precondition']) and $case->precondition != $this->post->precondition) $stepChanged = true;
if(!empty($_POST['labels'][0])) $stepChanged = true;
return array($stepChanged, $status);
}
+1 -9
View File
@@ -366,14 +366,6 @@
<?php
js::set('fullscreen', $lang->fullscreen);
js::set('retrack', $lang->retrack);
js::set('isLibCase', $isLibCase);
?>
<?php if(!$isLibCase):?>
<script>
$(function()
{
$('#subNavbar [data-id=testcase]').addClass('active');
$('#navbar [data-id=testcase]').addClass('active');
})
</script>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>
+1
View File
@@ -517,6 +517,7 @@ class tree extends control
{
$changeFunc = '';
if($viewType == 'bug' or $viewType == 'case') $changeFunc = "onchange='loadModuleRelated()'";
if($viewType == 'task') $changeFunc = "onchange='setStories(this.value)'";
$field = $fieldID ? "modules[$fieldID]" : 'module';
$currentModule = $this->tree->getById($currentModuleID);
+44
View File
@@ -528,6 +528,10 @@ class upgradeModel extends model
$this->moveProjectAdmins();
$this->addStoryViewPriv();
break;
case '17_2':
$this->addReviewIssusApprovalData();
break;
}
$this->deletePatch();
@@ -6509,6 +6513,46 @@ class upgradeModel extends model
return true;
}
/*
* Add review issue approval data.
*
* @access public
* @return bool
*/
public function addReviewIssusApprovalData()
{
$reviewIssues = $this->dao->select('id,review,type')->from(TABLE_REVIEWISSUE)
->where('type')->eq('review')
->andWhere('approval')->eq(0)
->andWhere('deleted')->eq('0')
->fetchAll('review');
if(empty($reviewIssues)) return false;
$reviewIds = array_unique(array_column($reviewIssues, 'review'));
$approvalsPairs = $this->dao->select('objectID, max(id) as approval')->from(TABLE_APPROVAL)
->where('objectID')->in($reviewIds)
->andWhere('objectType')->eq('review')
->andWhere('result')->eq('fail')
->andWhere('deleted')->eq(0)
->groupBy('objectID')
->fetchAll('objectID');
/* Add approval data. */
foreach($reviewIssues as $reviewIssue)
{
if(!isset($approvalsPairs[$reviewIssue->review]->approval)) continue;
$this->dao->update(TABLE_REVIEWISSUE)
->set('approval')->eq($approvalsPairs[$reviewIssue->review]->approval)
->where('review')->eq($reviewIssue->review)
->andWhere('type')->eq('review')
->andWhere('approval')->eq(0)
->andWhere('deleted')->eq('0')
->exec();
}
}
/**
* Xuan: Add `index` column to all message partition tables.
*
+2
View File
@@ -9,3 +9,5 @@ body {background: #1183fb linear-gradient(-90deg, #0a48d1 0%, #1183fb 100%); bac
#resetForm .form-control:focus {border-color: #2272eb; background: #fff;}
#resetForm .form-actions {padding-bottom: 20px;}
.required:after {top: 32px; right: -15px;}
#resetPanel .form-group .input-group {width: 100%}
#resetPanel .form-group .input-group.required:after {top: 6px;}

Some files were not shown because too many files have changed in this diff Show More