* @package execution
* @version $Id: control.php 5094 2013-07-10 08:46:15Z chencongzhi520@gmail.com $
* @link http://www.zentao.net
*/
class execution extends control
{
/**
* The global $executions.
*
* @var array
* @access public
*/
public $executions;
/**
* The global $objectType.
*
* @var string
* @access public
*/
public $objectType = 'execution';
/**
* Construct function, Set executions.
*
* @param string $moduleName
* @param string $methodName
* @access public
* @return void
*/
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
$this->loadModel('project');
if(defined('IN_UPGRADE') and IN_UPGRADE) return false;
if(!isset($this->app->user)) return;
$mode = $this->app->tab == 'execution' ? 'multiple' : '';
$this->executions = $this->execution->getPairs(0, 'all', "nocode,{$mode}");
$skipCreateStep = array('computeburn', 'ajaxgetdropmenu', 'executionkanban', 'ajaxgetteammembers', 'all');
if(!in_array($this->methodName, $skipCreateStep) and $this->app->tab == 'execution')
{
if(!$this->executions and $this->methodName != 'index' and $this->methodName != 'create' and $this->app->getViewType() != 'mhtml') $this->locate($this->createLink('execution', 'create'));
}
}
/**
* Browse a execution.
*
* @param int $executionID
* @access public
* @return void
*/
public function browse($executionID = 0)
{
$this->locate($this->createLink($this->moduleName, 'task', "executionID=$executionID"));
}
/**
* Common actions.
*
* @param int $executionID
* @param string $extra
* @access public
* @return object current object
*/
public function commonAction($executionID = 0, $extra = '')
{
$this->loadModel('product');
/* Get executions and products info. */
$executionID = $this->execution->saveState($executionID, $this->executions);
$execution = $this->execution->getById($executionID);
$products = $this->product->getProducts($executionID);
$childExecutions = $this->execution->getChildExecutions($executionID);
$teamMembers = $this->execution->getTeamMembers($executionID);
$actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$project = $this->loadModel('project')->getByID($execution->project);
/* Set menu. */
$this->execution->setMenu($executionID, $buildID = 0, $extra);
/* Assign. */
$this->view->hidden = !empty($project->hasProduct) ? "" : 'hide';
$this->view->executions = $this->executions;
$this->view->execution = $execution;
$this->view->childExecutions = $childExecutions;
$this->view->products = $products;
$this->view->teamMembers = $teamMembers;
return $execution;
}
/**
* Tasks of a execution.
*
* @param int $executionID
* @param string $status
* @param string $param
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function task($executionID = 0, $status = 'unclosed', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
$this->loadModel('tree');
$this->loadModel('search');
$this->loadModel('task');
$this->loadModel('datatable');
$this->loadModel('setting');
$this->loadModel('product');
$this->loadModel('user');
if(common::hasPriv('execution', 'create')) $this->lang->TRActions = html::a($this->createLink('execution', 'create'), " " . $this->lang->execution->create, '', "class='btn btn-primary'");
if(!isset($_SESSION['limitedExecutions'])) $this->execution->getLimitedExecution();
if($executionID) $this->session->set("storyList", $this->createLink("execution", "story", "&executionID=" . $executionID));
/* Set browse type. */
$browseType = strtolower($status);
$execution = $this->commonAction($executionID, $status);
$executionID = $execution->id;
if($execution->type == 'kanban' and $this->config->vision != 'lite' and $this->app->getViewType() != 'json') $this->locate($this->createLink('execution', 'kanban', "executionID=$executionID"));
/* Get products by execution. */
$products = $this->product->getProductPairsByProject($executionID);
setcookie('preExecutionID', $executionID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
/* Save the recently five executions visited in the cookie. */
$recentExecutions = isset($this->config->execution->recentExecutions) ? explode(',', $this->config->execution->recentExecutions) : array();
array_unshift($recentExecutions, $executionID);
$recentExecutions = array_unique($recentExecutions);
$recentExecutions = array_slice($recentExecutions, 0, 5);
$recentExecutions = join(',', $recentExecutions);
if($this->session->multiple)
{
if(!isset($this->config->execution->recentExecutions) or $this->config->execution->recentExecutions != $recentExecutions) $this->setting->updateItem($this->app->user->account . 'common.execution.recentExecutions', $recentExecutions);
if(!isset($this->config->execution->lastExecution) or $this->config->execution->lastExecution != $executionID) $this->setting->updateItem($this->app->user->account . 'common.execution.lastExecution', $executionID);
}
if($this->cookie->preExecutionID != $executionID)
{
$_COOKIE['moduleBrowseParam'] = $_COOKIE['productBrowseParam'] = 0;
setcookie('moduleBrowseParam', 0, 0, $this->config->webRoot, '', false, false);
setcookie('productBrowseParam', 0, 0, $this->config->webRoot, '', false, true);
}
if($browseType == 'bymodule')
{
setcookie('moduleBrowseParam', (int)$param, 0, $this->config->webRoot, '', false, false);
setcookie('productBrowseParam', 0, 0, $this->config->webRoot, '', false, true);
}
elseif($browseType == 'byproduct')
{
setcookie('moduleBrowseParam', 0, 0, $this->config->webRoot, '', false, false);
setcookie('productBrowseParam', (int)$param, 0, $this->config->webRoot, '', false, true);
}
else
{
$this->session->set('taskBrowseType', $browseType);
}
if($browseType == 'bymodule' and $this->session->taskBrowseType == 'bysearch') $this->session->set('taskBrowseType', 'unclosed');
/* Set queryID, moduleID and productID. */
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$moduleID = ($browseType == 'bymodule') ? (int)$param : (($browseType == 'bysearch' or $browseType == 'byproduct') ? 0 : $this->cookie->moduleBrowseParam);
$productID = ($browseType == 'byproduct') ? (int)$param : (($browseType == 'bysearch' or $browseType == 'bymodule') ? 0 : $this->cookie->productBrowseParam);
/* Save to session. */
$uri = $this->app->getURI(true);
$this->app->session->set('taskList', $uri . "#app={$this->app->tab}", 'execution');
/* Process the order by field. */
if(!$orderBy) $orderBy = $this->cookie->executionTaskOrder ? $this->cookie->executionTaskOrder : 'status,id_desc';
setcookie('executionTaskOrder', $orderBy, 0, $this->config->webRoot, '', false, true);
/* Append id for secend sort. */
$sort = common::appendOrder($orderBy);
/* Header and position. */
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->task;
/* Load pager and get tasks. */
$this->app->loadClass('pager', $static = true);
if($this->app->getViewType() == 'mhtml' || $this->app->getViewType() == 'xhtml') $recPerPage = 10;
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Get tasks. */
$allTasks = $this->task->getExecutionTasks($executionID);
$tasks = $this->execution->getTasks($productID, $executionID, $this->executions, $browseType, $queryID, $moduleID, $sort, $pager);
if(empty($tasks) and $pageID > 1)
{
$pager = pager::init(0, $recPerPage, 1);
$tasks = $this->execution->getTasks($productID, $executionID, $this->executions, $browseType, $queryID, $moduleID, $sort, $pager);
}
/* Get product. */
$product = $this->product->getById($productID);
/* Display of branch label. */
$showBranch = $this->loadModel('branch')->showBranch($productID, $moduleID, $executionID);
/* Build the search form. */
$actionURL = $this->createLink('execution', 'task', "executionID=$executionID&status=bySearch¶m=myQueryID");
$this->config->execution->search['onMenuBar'] = 'yes';
if(!$execution->multiple) unset($this->config->execution->search['fields']['execution']);
$this->execution->buildTaskSearchForm($executionID, $this->executions, $queryID, $actionURL);
/* team member pairs. */
$memberPairs = array();
foreach($this->view->teamMembers as $key => $member) $memberPairs[$key] = $member->realname;
$memberPairs = $this->user->processAccountSort($memberPairs);
$showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
$extra = (isset($this->config->execution->task->allModule) && $this->config->execution->task->allModule == 1) ? 'allModule' : '';
$showModule = !empty($this->config->datatable->executionTask->showModule) ? $this->config->datatable->executionTask->showModule : '';
$this->view->modulePairs = $showModule ? $this->tree->getModulePairs($executionID, 'task', $showModule) : array();
/* Assign. */
$this->view->tasks = $tasks;
$this->view->allTasks = $allTasks;
$this->view->summary = $this->execution->summary($tasks);
$this->view->tabID = 'task';
$this->view->pager = $pager;
$this->view->recTotal = $pager->recTotal;
$this->view->recPerPage = $pager->recPerPage;
$this->view->orderBy = $orderBy;
$this->view->browseType = $browseType;
$this->view->status = $status;
$this->view->users = $this->user->getPairs('noletter|all');
$this->view->param = $param;
$this->view->executionID = $executionID;
$this->view->execution = $execution;
$this->view->productID = $productID;
$this->view->product = $product;
$this->view->modules = $this->tree->getTaskOptionMenu($executionID, 0, 0, $showAllModule ? 'allModule' : '');
$this->view->moduleID = $moduleID;
$this->view->moduleTree = $this->tree->getTaskTreeMenu($executionID, $productID, $startModuleID = 0, array('treeModel', 'createTaskLink'), $extra);
$this->view->memberPairs = $memberPairs;
$this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products));
$this->view->setModule = !$execution->multiple ? false : true;
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
$this->view->showBranch = $showBranch;
$this->view->projectName = $this->loadModel('project')->getById($execution->project)->name . ' / ' . $execution->name;
$this->display();
}
/**
* Browse tasks in group.
*
* @param int $executionID
* @param string $groupBy the field to group by
* @param string $filter
* @access public
* @return void
*/
public function grouptask($executionID = 0, $groupBy = 'story', $filter = '')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
/* Save session. */
$this->app->session->set('taskList', $this->app->getURI(true), 'execution');
/* Header and session. */
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->task;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$this->view->position[] = $this->lang->execution->task;
/* Get tasks and group them. */
if(empty($groupBy))$groupBy = 'story';
if($groupBy == 'story' and $execution->lifetime == 'ops')
{
$groupBy = 'status';
unset($this->lang->execution->groups['story']);
}
$sort = common::appendOrder($groupBy);
$tasks = $this->loadModel('task')->getExecutionTasks($executionID, $productID = 0, $status = 'all', $modules = 0, $sort);
$groupBy = str_replace('`', '', $groupBy);
$taskLang = $this->lang->task;
$groupByList = array();
$groupTasks = array();
$groupTasks = array();
$allCount = 0;
foreach($tasks as $task)
{
if($task->mode == 'multi') $task->assignedToRealName = $this->lang->task->team;
$groupTasks[] = $task;
$allCount++;
if(isset($task->children))
{
foreach($task->children as $child)
{
$groupTasks[] = $child;
$allCount++;
}
$task->children = true;
unset($task->children);
}
}
/* Get users. */
$users = $this->loadModel('user')->getPairs('noletter');
$tasks = $groupTasks;
$groupTasks = array();
foreach($tasks as $task)
{
if($groupBy == 'story')
{
$groupTasks[$task->story][] = $task;
$groupByList[$task->story] = $task->storyTitle;
}
elseif($groupBy == 'status')
{
$groupTasks[$taskLang->statusList[$task->status]][] = $task;
}
elseif($groupBy == 'assignedTo')
{
if(isset($task->team))
{
foreach($task->team as $team)
{
$cloneTask = clone $task;
$cloneTask->assignedTo = $team->account;
$cloneTask->estimate = $team->estimate;
$cloneTask->consumed = $team->consumed;
$cloneTask->left = $team->left;
if($team->left == 0) $cloneTask->status = 'done';
$realname = zget($users, $team->account);
$cloneTask->assignedToRealName = $realname;
$groupTasks[$realname][] = $cloneTask;
}
}
else
{
$groupTasks[$task->assignedToRealName][] = $task;
}
}
elseif($groupBy == 'finishedBy')
{
if(isset($task->team))
{
$task->consumed = $task->estimate = $task->left = 0;
foreach($task->team as $team)
{
if($team->left != 0)
{
$task->estimate += $team->estimate;
$task->consumed += $team->consumed;
$task->left += $team->left;
continue;
}
$cloneTask = clone $task;
$cloneTask->finishedBy = $team->account;
$cloneTask->estimate = $team->estimate;
$cloneTask->consumed = $team->consumed;
$cloneTask->left = $team->left;
$cloneTask->status = 'done';
$realname = zget($users, $team->account);
$groupTasks[$realname][] = $cloneTask;
}
if(!empty($task->left)) $groupTasks[$users[$task->finishedBy]][] = $task;
}
else
{
$groupTasks[$users[$task->finishedBy]][] = $task;
}
}
elseif($groupBy == 'closedBy')
{
$groupTasks[$users[$task->closedBy]][] = $task;
}
elseif($groupBy == 'type')
{
$groupTasks[$taskLang->typeList[$task->type]][] = $task;
}
else
{
$groupTasks[$task->$groupBy][] = $task;
}
}
/* Process closed data when group by assignedTo. */
if($groupBy == 'assignedTo' and isset($groupTasks['Closed']))
{
$closedTasks = $groupTasks['Closed'];
unset($groupTasks['Closed']);
$groupTasks['closed'] = $closedTasks;
}
/* Remove task by filter and group. */
$filter = (empty($filter) and isset($this->lang->execution->groupFilter[$groupBy])) ? key($this->lang->execution->groupFilter[$groupBy]) : $filter;
if($filter != 'all')
{
if($groupBy == 'story' and $filter == 'linked' and isset($groupTasks[0]))
{
$allCount -= count($groupTasks[0]);
unset($groupTasks[0]);
}
elseif($groupBy == 'pri' and $filter == 'noset')
{
foreach($groupTasks as $pri => $tasks)
{
if($pri)
{
$allCount -= count($tasks);
unset($groupTasks[$pri]);
}
}
}
elseif($groupBy == 'assignedTo' and $filter == 'undone')
{
$multiTaskCount = array();
foreach($groupTasks as $assignedTo => $tasks)
{
foreach($tasks as $i => $task)
{
if($task->status != 'wait' and $task->status != 'doing')
{
if($task->mode == 'multi')
{
if(!isset($multiTaskCount[$task->id]))
{
$multiTaskCount[$task->id] = true;
$allCount -= 1;
}
}
else
{
$allCount -= 1;
}
unset($groupTasks[$assignedTo][$i]);
}
}
}
}
elseif(($groupBy == 'finishedBy' or $groupBy == 'closedBy') and isset($tasks['']))
{
$allCount -= count($tasks['']);
unset($tasks['']);
}
}
/* Assign. */
$this->app->loadLang('tree');
$this->view->members = $this->execution->getTeamMembers($executionID);
$this->view->tasks = $groupTasks;
$this->view->tabID = 'task';
$this->view->groupByList = $groupByList;
$this->view->browseType = 'group';
$this->view->groupBy = $groupBy;
$this->view->orderBy = $groupBy;
$this->view->executionID = $executionID;
$this->view->users = $users;
$this->view->moduleID = 0;
$this->view->moduleName = $this->lang->tree->all;
$this->view->features = $this->execution->getExecutionFeatures($execution);
$this->view->filter = $filter;
$this->view->allCount = $allCount;
$this->display();
}
/**
* Import tasks undoned from other executions.
*
* @param int $executionID
* @param int $fromExecution
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function importTask($toExecution, $fromExecution = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
if(!empty($_POST))
{
$this->execution->importTask($toExecution);
/* If link from no head then reload. */
if(isonlybody())
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($toExecution, $this->session->execLaneType ? $this->session->execLaneType : 'all');
return print(js::reload('parent'));
}
return print(js::locate(inlink('importTask', "toExecution=$toExecution&fromExecution=$fromExecution"), 'parent'));
}
$execution = $this->commonAction($toExecution);
$toExecution = $execution->id;
$project = $this->loadModel('project')->getByID($execution->project);
$branches = $this->execution->getBranches($toExecution);
$tasks = $this->execution->getTasks2Imported($toExecution, $branches);
$executions = $this->execution->getToImport(array_keys($tasks), $execution->type, $project->model);
unset($executions[$toExecution]);
unset($tasks[$toExecution]);
if($fromExecution == 0)
{
$tasks2Imported = array();
foreach($executions as $id => $executionName)
{
$tasks2Imported = array_merge($tasks2Imported, $tasks[$id]);
}
}
else
{
$tasks2Imported = zget($tasks, $fromExecution, array());
}
/* Pager. */
$this->app->loadClass('pager', $static = true);
$recTotal = count($tasks2Imported);
$pager = new pager($recTotal, $recPerPage, $pageID);
$tasks2ImportedList = array_chunk($tasks2Imported, $pager->recPerPage, true);
$tasks2ImportedList = empty($tasks2ImportedList) ? $tasks2ImportedList : $tasks2ImportedList[$pageID - 1];
$tasks2ImportedList = $this->loadModel('task')->processTasks($tasks2ImportedList);
/* Save session. */
$this->app->session->set('taskList', $this->app->getURI(true), 'execution');
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->importTask;
$this->view->pager = $pager;
$this->view->tasks2Imported = $tasks2ImportedList;
$this->view->executions = $executions;
$this->view->executionID = $execution->id;
$this->view->fromExecution = $fromExecution;
$this->display();
}
/**
* Import from Bug.
*
* @param int $executionID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function importBug($executionID = 0, $browseType = 'all', $param = 0, $recTotal = 0, $recPerPage = 30, $pageID = 1)
{
$this->app->loadConfig('task');
if(!empty($_POST))
{
$mails = $this->execution->importBug($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
/* If link from no head then reload. */
if(isonlybody())
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $this->session->execLaneType ? $this->session->execLaneType : 'all');
return print(js::reload('parent'));
}
return print(js::locate($this->createLink('execution', 'importBug', "executionID=$executionID"), 'parent'));
}
/* Set browseType, productID, moduleID and queryID. */
$browseType = strtolower($browseType);
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$this->loadModel('bug');
$executions = $this->execution->getPairs(0, 'all', 'nocode');
$this->execution->setMenu($executionID);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$title = $executions[$executionID] . $this->lang->colon . $this->lang->execution->importBug;
$position[] = html::a($this->createLink('execution', 'task', "executionID=$executionID"), $executions[$executionID]);
$position[] = $this->lang->execution->importBug;
/* Get users, products and executions.*/
$users = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');
$products = $this->dao->select('t1.product, t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PRODUCT)->alias('t2')
->on('t1.product = t2.id')
->where('t1.project')->eq($executionID)
->fetchPairs('product');
if(!empty($products))
{
unset($executions);
$executions = $this->dao->select('t1.project, t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_EXECUTION)->alias('t2')
->on('t1.project = t2.id')
->where('t1.product')->in(array_keys($products))
->fetchPairs('project');
}
else
{
$executionName = $executions[$executionID];
unset($executions);
$executions[$executionID] = $executionName;
}
/* Get bugs.*/
$bugs = array();
if($browseType != "bysearch")
{
$bugs = $this->bug->getActiveAndPostponedBugs(array_keys($products), $executionID, $pager);
}
else
{
if($queryID)
{
$query = $this->loadModel('search')->getQuery($queryID);
if($query)
{
$this->session->set('importBugQuery', $query->sql);
$this->session->set('importBugForm', $query->form);
}
else
{
$this->session->set('importBugQuery', ' 1 = 1');
}
}
else
{
if($this->session->importBugQuery == false) $this->session->set('importBugQuery', ' 1 = 1');
}
$bugQuery = str_replace("`product` = 'all'", "`product`" . helper::dbIN(array_keys($products)), $this->session->importBugQuery); // Search all execution.
$bugs = $this->execution->getSearchBugs($products, $executionID, $bugQuery, $pager, 'id_desc');
}
$execution = $this->execution->getByID($executionID);
$project = $this->loadModel('project')->getByID($execution->project);
/* Build the search form. */
$this->config->bug->search['actionURL'] = $this->createLink('execution', 'importBug', "executionID=$executionID&browseType=bySearch¶m=myQueryID");
$this->config->bug->search['queryID'] = $queryID;
if(!empty($products))
{
$this->config->bug->search['params']['product']['values'] = array(''=>'') + $products + array('all'=>$this->lang->execution->aboveAllProduct);
}
else
{
$this->config->bug->search['params']['product']['values'] = array(''=>'');
}
$this->config->bug->search['params']['execution']['values'] = array(''=>'') + $executions + array('all'=>$this->lang->execution->aboveAllExecution);
$this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs(array_keys($products));
$this->config->bug->search['module'] = 'importBug';
$this->config->bug->search['params']['confirmed']['values'] = array('' => '') + $this->lang->bug->confirmedList;
$this->loadModel('tree');
$bugModules = array();
foreach($products as $productID => $productName)
{
$productModules = $this->tree->getOptionMenu($productID, 'bug', 0, 'all');
foreach($productModules as $moduleID => $moduleName)
{
if(empty($moduleID))
{
$bugModules[$moduleID] = $moduleName;
continue;
}
$bugModules[$moduleID] = $productName . $moduleName;
}
}
$this->config->bug->search['params']['module']['values'] = $bugModules;
unset($this->config->bug->search['fields']['resolvedBy']);
unset($this->config->bug->search['fields']['closedBy']);
unset($this->config->bug->search['fields']['status']);
unset($this->config->bug->search['fields']['toTask']);
unset($this->config->bug->search['fields']['toStory']);
unset($this->config->bug->search['fields']['severity']);
unset($this->config->bug->search['fields']['resolution']);
unset($this->config->bug->search['fields']['resolvedBuild']);
unset($this->config->bug->search['fields']['resolvedDate']);
unset($this->config->bug->search['fields']['closedDate']);
unset($this->config->bug->search['fields']['branch']);
if(empty($execution->multiple) and empty($execution->hasProduct)) unset($this->config->bug->search['fields']['plan']);
if(empty($project->hasProduct))
{
unset($this->config->bug->search['fields']['product']);
if($project->model !== 'scrum') unset($this->config->bug->search['fields']['plan']);
}
unset($this->config->bug->search['params']['resolvedBy']);
unset($this->config->bug->search['params']['closedBy']);
unset($this->config->bug->search['params']['status']);
unset($this->config->bug->search['params']['toTask']);
unset($this->config->bug->search['params']['toStory']);
unset($this->config->bug->search['params']['severity']);
unset($this->config->bug->search['params']['resolution']);
unset($this->config->bug->search['params']['resolvedBuild']);
unset($this->config->bug->search['params']['resolvedDate']);
unset($this->config->bug->search['params']['closedDate']);
unset($this->config->bug->search['params']['branch']);
$this->loadModel('search')->setSearchParams($this->config->bug->search);
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->pager = $pager;
$this->view->bugs = $bugs;
$this->view->recTotal = $pager->recTotal;
$this->view->recPerPage = $pager->recPerPage;
$this->view->browseType = $browseType;
$this->view->param = $param;
$this->view->users = $users;
$this->view->execution = $execution;
$this->view->executionID = $executionID;
$this->view->requiredFields = explode(',', $this->config->task->create->requiredFields);
$this->display();
}
/**
* Browse stories of a execution.
*
* @param int $executionID
* @param string $storyType story|requirement
* @param string $orderBy
* @param string $type
* @param string $param
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function story($executionID = 0, $storyType = 'story', $orderBy = 'order_desc', $type = 'all', $param = 0, $recTotal = 0, $recPerPage = 50, $pageID = 1)
{
/* Load these models. */
$this->loadModel('story');
$this->loadModel('user');
$this->loadModel('product');
$this->loadModel('datatable');
$this->app->loadLang('datatable');
$this->app->loadLang('testcase');
/* Change for requirement story title. */
if($storyType == 'requirement')
{
$this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
$this->lang->story->noStory = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->noStory);
$this->lang->execution->createStory = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->execution->createStory);
$this->config->product->search['fields']['title'] = $this->lang->story->title;
unset($this->config->product->search['fields']['plan']);
unset($this->config->product->search['fields']['stage']);
$this->story->replaceURLang($storyType);
}
$type = strtolower($type);
$param = $param;
$productID = 0;
setcookie('storyPreExecutionID', $executionID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
if($this->cookie->storyPreExecutionID != $executionID)
{
$_COOKIE['storyModuleParam'] = $_COOKIE['storyProductParam'] = $_COOKIE['storyBranchParam'] = 0;
setcookie('storyModuleParam', 0, 0, $this->config->webRoot, '', false, true);
setcookie('storyProductParam', 0, 0, $this->config->webRoot, '', false, true);
setcookie('storyBranchParam', 0, 0, $this->config->webRoot, '', false, true);
}
if($type == 'bymodule')
{
$module = $this->loadModel('tree')->getByID($param);
$productID = isset($module->root) ? $module->root : 0;
$_COOKIE['storyModuleParam'] = $param;
$_COOKIE['storyProductParam'] = 0;
$_COOKIE['storyBranchParam'] = 0;
setcookie('storyModuleParam', $param, 0, $this->config->webRoot, '', false, true);
setcookie('storyProductParam', 0, 0, $this->config->webRoot, '', false, true);
setcookie('storyBranchParam', 0, 0, $this->config->webRoot, '', false, true);
}
elseif($type == 'byproduct')
{
$productID = $param;
$_COOKIE['storyModuleParam'] = 0;
$_COOKIE['storyProductParam'] = $param;
$_COOKIE['storyBranchParam'] = 0;
setcookie('storyModuleParam', 0, 0, $this->config->webRoot, '', false, true);
setcookie('storyProductParam', $param, 0, $this->config->webRoot, '', false, true);
setcookie('storyBranchParam', 0, 0, $this->config->webRoot, '', false, true);
}
elseif($type == 'bybranch')
{
$_COOKIE['storyModuleParam'] = 0;
$_COOKIE['storyProductParam'] = 0;
$_COOKIE['storyBranchParam'] = $param;
setcookie('storyModuleParam', 0, 0, $this->config->webRoot, '', false, true);
setcookie('storyProductParam', 0, 0, $this->config->webRoot, '', false, true);
setcookie('storyBranchParam', $param, 0, $this->config->webRoot, '', false, true);
}
else
{
$this->session->set('executionStoryBrowseType', $type);
$this->session->set('storyBrowseType', $type, 'execution');
}
/* Save session. */
$this->session->set('storyList', $this->app->getURI(true), $this->app->tab);
$this->session->set('executionStoryList', $this->app->getURI(true), 'execution');
/* Process the order by field. */
if(!$orderBy) $orderBy = $this->cookie->executionStoryOrder ? $this->cookie->executionStoryOrder : 'pri';
setcookie('executionStoryOrder', $orderBy, 0, $this->config->webRoot, '', false, true);
/* Append id for secend sort. */
$sort = common::appendOrder($orderBy);
if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
$queryID = ($type == 'bysearch') ? $param : 0;
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
/* Load pager. */
$this->app->loadClass('pager', $static = true);
if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
$pager = new pager($recTotal, $recPerPage, $pageID);
$stories = $this->story->getExecutionStories($executionID, 0, 0, $sort, $type, $param, $storyType, '', $pager);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false);
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
$users = $this->user->getPairs('noletter');
/* Build the search form. */
$modules = array();
$productModules = array();
$executionModules = $this->loadModel('tree')->getTaskTreeModules($executionID, true);
$products = $this->product->getProducts($executionID);
if($productID)
{
$product = $products[$productID];
$productModules = $this->tree->getOptionMenu($productID, 'story', 0, $product->branches);
}
else
{
foreach($products as $product) $productModules += $this->tree->getOptionMenu($product->id, 'story', 0, $product->branches);
}
if(defined('TUTORIAL'))
{
$modules = $this->loadModel('tutorial')->getModulePairs();
}
else
{
foreach($productModules as $branchID => $moduleList)
{
foreach($moduleList as $moduleID => $moduleName)
{
if($moduleID and !isset($executionModules[$moduleID])) continue;
$modules[$moduleID] = ((count($products) >= 2 and $moduleID) ? $product->name : '') . $moduleName;
}
}
}
$actionURL = $this->createLink('execution', 'story', "executionID=$executionID&storyType=$storyType&orderBy=$orderBy&type=bySearch&queryID=myQueryID");
$branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products));
$branchOption = array();
foreach($branchGroups as $branches)
{
foreach($branches as $branchID => $name)
{
$branchOption[$branchID] = $name;
}
}
$this->execution->buildStorySearchForm($products, $branchGroups, $modules, $queryID, $actionURL, 'executionStory', $execution);
/* Header and position. */
$title = $execution->name . $this->lang->colon . $this->lang->execution->story;
$position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$position[] = $this->lang->execution->story;
/* Get related tasks, bugs, cases count of each story. */
$storyIdList = array();
foreach($stories as $story)
{
$storyIdList[$story->id] = $story->id;
if(!empty($story->children))
{
foreach($story->children as $child) $storyIdList[$child->id] = $child->id;
}
}
/* Count T B C */
$storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList, $executionID);
$storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList, $executionID);
$storyCases = $this->loadModel('testcase')->getStoryCaseCounts($storyIdList);
$plans = $this->execution->getPlans($products, 'skipParent|withMainPlan|unexpired|noclosed', $executionID);
$allPlans = array('' => '');
if(!empty($plans))
{
foreach($plans as $plan) $allPlans += $plan;
}
if($this->cookie->storyModuleParam) $this->view->module = $this->loadModel('tree')->getById($this->cookie->storyModuleParam);
if($this->cookie->storyProductParam) $this->view->product = $this->loadModel('product')->getById($this->cookie->storyProductParam);
if($this->cookie->storyBranchParam)
{
$branchID = $this->cookie->storyBranchParam;
if(strpos($branchID, ',') !== false) list($productID, $branchID) = explode(',', $branchID);
$this->view->branch = $this->loadModel('branch')->getById($branchID, $productID);
}
/* Display of branch label. */
$showBranch = $this->loadModel('branch')->showBranch($this->cookie->storyProductParam, $this->cookie->storyModuleParam, $executionID);
/* Get execution's product. */
$productPairs = $this->loadModel('product')->getProductPairsByProject($executionID);
if(empty($productID)) $productID = key($productPairs);
$showModule = !empty($this->config->datatable->executionStory->showModule) ? $this->config->datatable->executionStory->showModule : '';
$modulePairs = $showModule ? $this->tree->getModulePairs($type == 'byproduct' ? $param : 0, 'story', $showModule) : array();
$createModuleLink = $storyType == 'story' ? 'createStoryLink' : 'createRequirementLink';
if(!$execution->hasProduct and !$execution->multiple)
{
$moduleTree = $this->tree->getTreeMenu($productID, 'story', $startModuleID = 0, array('treeModel', $createModuleLink), array('executionID' => $executionID, 'productID' => $productID), '', "¶m=$param&storyType=$storyType");
}
else
{
$moduleTree = $this->tree->getProjectStoryTreeMenu($executionID, 0, array('treeModel', $createModuleLink));
}
$executionProductList = $this->loadModel('product')->getProducts($executionID);
$multiBranch = false;
foreach($executionProductList as $executionProduct)
{
if($executionProduct->type != 'normal')
{
$multiBranch = true;
break;
}
}
$summary = $this->product->summary($stories);
if($storyType == 'requirement') $summary = str_replace($this->lang->SRCommon, $this->lang->URCommon, $summary);
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->productID = $productID;
$this->view->execution = $execution;
$this->view->stories = $stories;
$this->view->linkedTaskStories = $this->story->getIdListWithTask($executionID);
$this->view->allPlans = $allPlans;
$this->view->summary = $summary;
$this->view->orderBy = $orderBy;
$this->view->storyType = $storyType;
$this->view->type = $this->session->executionStoryBrowseType;
$this->view->param = $param;
$this->view->isAllProduct = ($this->cookie->storyProductParam or $this->cookie->storyModuleParam or $this->cookie->storyBranchParam) ? false : true;
$createModuleLink = $storyType == 'story' ? 'createStoryLink' : 'createRequirementLink';
$this->view->moduleTree = $moduleTree;
$this->view->modulePairs = $modulePairs;
$this->view->tabID = 'story';
$this->view->storyTasks = $storyTasks;
$this->view->storyBugs = $storyBugs;
$this->view->storyCases = $storyCases;
$this->view->users = $users;
$this->view->pager = $pager;
$this->view->setModule = true;
$this->view->branchGroups = $branchGroups;
$this->view->branchOption = $branchOption;
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
$this->view->showBranch = $showBranch;
$this->view->storyStages = $this->product->batchGetStoryStage($stories);
$this->view->multiBranch = $multiBranch;
$this->display();
}
/**
* View a story.
*
* @param int $storyID
* @param int $executionID
* @access public
* @return void
*/
public function storyView($storyID, $executionID = 0)
{
$this->session->set('productList', $this->app->getURI(true), 'product');
$story = $this->loadModel('story')->getByID($storyID);
echo $this->fetch('story', 'view', "storyID=$storyID&version=$story->version¶m=" . ($executionID ? $executionID : $this->session->execution));
}
/**
* Browse bugs of a execution.
*
* @param int $executionID
* @param int $productID
* @param int $branchID
* @param string $orderBy
* @param int $build
* @param string $type
* @param int $param
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function bug($executionID = 0, $productID = 0, $branch = 'all', $orderBy = 'status,id_desc', $build = 0, $type = 'all', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* Load these two models. */
$this->loadModel('bug');
$this->loadModel('user');
$this->loadModel('product');
$this->loadModel('datatable');
$this->loadModel('tree');
/* Save session. */
$this->session->set('bugList', $this->app->getURI(true), 'execution');
$this->session->set('buildList', $this->app->getURI(true), 'execution');
$type = strtolower($type);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
$execution = $this->commonAction($executionID);
$project = $this->loadModel('project')->getByID($execution->project);
$executionID = $execution->id;
$products = $this->product->getProducts($execution->id);
if(count($products) === 1) $productID = current($products)->id;
if($execution->hasProduct)
{
unset($this->config->bug->search['fields']['product']);
if($project->model != 'scrum')
{
unset($this->config->bug->search['fields']['plan']);
}
}
$productPairs = array('0' => $this->lang->product->all);
foreach($products as $productData) $productPairs[$productData->id] = $productData->name;
if($execution->hasProduct) $this->lang->modulePageNav = $this->product->select($productPairs, $productID, 'execution', 'bug', $executionID, $branch);
/* Header and position. */
$title = $execution->name . $this->lang->colon . $this->lang->execution->bug;
$position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$position[] = $this->lang->execution->bug;
/* Load pager and get bugs, user. */
$this->app->loadClass('pager', $static = true);
if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
$pager = new pager($recTotal, $recPerPage, $pageID);
$sort = common::appendOrder($orderBy);
$bugs = $this->bug->getExecutionBugs($executionID, $productID, $branch, $build, $type, $param, $sort, '', $pager);
$bugs = $this->bug->checkDelayedBugs($bugs);
$users = $this->user->getPairs('noletter');
/* team member pairs. */
$memberPairs = array();
$memberPairs[] = "";
foreach($this->view->teamMembers as $key => $member)
{
$memberPairs[$key] = $member->realname;
}
$memberPairs = $this->user->processAccountSort($memberPairs);
/* Build the search form. */
$actionURL = $this->createLink('execution', 'bug', "executionID=$executionID&productID=$productID&branch=$branch&orderBy=$orderBy&build=$build&type=bysearch&queryID=myQueryID");
$this->execution->buildBugSearchForm($products, $queryID, $actionURL);
$product = $this->product->getById($productID);
$showBranch = false;
$branchOption = array();
$branchTagOption = array();
if($product and $product->type != 'normal')
{
/* Display of branch label. */
$showBranch = $this->loadModel('branch')->showBranch($productID);
/* Display status of branch. */
$branches = $this->branch->getList($productID, $executionID, 'all');
foreach($branches as $branchInfo)
{
$branchOption[$branchInfo->id] = $branchInfo->name;
$branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
}
}
/* Get story and task id list. */
$storyIdList = $taskIdList = array();
foreach($bugs as $bug)
{
if($bug->story) $storyIdList[$bug->story] = $bug->story;
if($bug->task) $taskIdList[$bug->task] = $bug->task;
if($bug->toTask) $taskIdList[$bug->toTask] = $bug->toTask;
}
$storyList = $storyIdList ? $this->loadModel('story')->getByList($storyIdList) : array();
$taskList = $taskIdList ? $this->loadModel('task')->getByList($taskIdList) : array();
$showModule = !empty($this->config->datatable->bugBrowse->showModule) ? $this->config->datatable->bugBrowse->showModule : '';
/* Process the openedBuild and resolvedBuild fields. */
$bugs = $this->bug->processBuildForBugs($bugs);
$moduleID = $type != 'bysearch' ? $param : 0;
$modules = $this->tree->getAllModulePairs('bug');
/* Get module tree.*/
$extra = array('projectID' => $executionID, 'orderBy' => $orderBy, 'type' => $type, 'build' => $build, 'branchID' => $branch);
if($executionID and empty($productID) and count($products) > 1)
{
$moduleTree = $this->tree->getBugTreeMenu($executionID, $productID, 0, array('treeModel', 'createBugLink'), $extra);
}
elseif(!empty($products))
{
$productID = empty($productID) ? reset($products)->id : $productID;
$moduleTree = $this->tree->getTreeMenu($productID, 'bug', 0, array('treeModel', 'createBugLink'), $extra + array('branchID' => $branch, 'productID' => $productID), $branch);
}
else
{
$moduleTree = '';
}
$tree = $moduleID ? $this->tree->getByID($moduleID) : '';
$showModule = !empty($this->config->datatable->executionBug->showModule) ? $this->config->datatable->executionBug->showModule : '';
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->bugs = $bugs;
$this->view->tabID = 'bug';
$this->view->build = $this->loadModel('build')->getById($build);
$this->view->buildID = $this->view->build ? $this->view->build->id : 0;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $users;
$this->view->productID = $productID;
$this->view->project = $project;
$this->view->branchID = empty($this->view->build->branch) ? $branch : $this->view->build->branch;
$this->view->memberPairs = $memberPairs;
$this->view->type = $type;
$this->view->summary = $this->bug->summary($bugs);
$this->view->param = $param;
$this->view->defaultProduct = (empty($productID) and !empty($products)) ? current(array_keys($products)) : $productID;
$this->view->builds = $this->build->getBuildPairs($productID);
$this->view->branchOption = $branchOption;
$this->view->branchTagOption = $branchTagOption;
$this->view->plans = $this->loadModel('productplan')->getPairs($productID ? $productID : array_keys($products));
$this->view->stories = $storyList;
$this->view->tasks = $taskList;
$this->view->projectPairs = $this->loadModel('project')->getPairsByProgram();
$this->view->moduleTree = $moduleTree;
$this->view->modules = $modules;
$this->view->moduleID = $moduleID;
$this->view->moduleName = $moduleID ? $tree->name : $this->lang->tree->all;
$this->view->modulePairs = $showModule ? $this->tree->getModulePairs($productID, 'bug', $showModule) : array();
$this->view->setModule = true;
$this->view->showBranch = false;
$this->display();
}
/**
* Execution case list.
*
* @param int $executionID
* @param int $productID
* @param int $branchID
* @param string $type
* @param int $moduleID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function testcase($executionID = 0, $productID = 0, $branchID = 'all', $type = 'all', $moduleID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->loadModel('testcase');
$this->loadModel('testtask');
$this->loadModel('tree');
$this->commonAction($executionID);
$uri = $this->app->getURI(true);
$this->session->set('caseList', $uri, 'execution');
$this->session->set('bugList', $uri, 'execution');
$products = $this->product->getProducts($executionID, 'all', '', false);
if(count($products) == 1) $productID = key($products);
$execution = $this->execution->getByID($executionID);
$hasProduct = $this->dao->findByID($execution->project)->from(TABLE_PROJECT)->fetch('hasProduct');
$extra = $executionID;
if($hasProduct) $this->lang->modulePageNav = $this->product->select(array('0' => $this->lang->product->all) + $products, $productID, 'execution', 'testcase', $extra, $branchID);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$cases = $this->loadModel('testcase')->getExecutionCases($executionID, $productID, $branchID, $moduleID, $orderBy, $pager, $type);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false);
$cases = $this->testcase->appendData($cases, 'case');
$cases = $this->loadModel('story')->checkNeedConfirm($cases);
$modules = $this->tree->getAllModulePairs('case');
/* Get module tree.*/
if($executionID and empty($productID))
{
$moduleTree = $this->tree->getCaseTreeMenu($executionID, $productID, 0, array('treeModel', 'createCaseLink'));
}
else
{
$moduleTree = $this->tree->getTreeMenu($productID, 'case', 0, array('treeModel', 'createCaseLink'), array('projectID' => $executionID, 'productID' => $productID), $branchID);
}
$tree = $moduleID ? $this->tree->getByID($moduleID) : '';
$this->view->title = $this->lang->execution->testcase;
$this->view->executionID = $executionID;
$this->view->productID = $productID;
$this->view->cases = $cases;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->type = $type;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->execution = $execution;
$this->view->moduleTree = $moduleTree;
$this->view->modules = $modules;
$this->view->moduleID = $moduleID;
$this->view->moduleName = $moduleID ? $tree->name : $this->lang->tree->all;
$this->view->branchID = $branchID;
$this->display();
}
/**
* List of test reports for the execution.
*
* @param int $executionID
* @param string $objectType project|execution|product
* @param string $extra
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function testreport($executionID = 0, $objectType = 'execution', $extra = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
echo $this->fetch('testreport', 'browse', "objectID=$executionID&objectType=$objectType&extra=$extra&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
}
/**
* Browse builds of a execution.
*
* @param int $executionID
* @param string $type all|product|bysearch
* @param int $param
* @param string $orderBy
* @access public
* @return void
*/
public function build($executionID = 0, $type = 'all', $param = 0, $orderBy = 't1.date_desc,t1.id_desc')
{
/* Load module and set session. */
$this->loadModel('testtask');
$this->session->set('buildList', $this->app->getURI(true), 'execution');
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
/* Get products' list. */
$products = $this->product->getProducts($executionID, 'all', '', false);
$products = array('' => '') + $products;
/* Build the search form. */
$type = strtolower($type);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
$actionURL = $this->createLink('execution', 'build', "executionID=$executionID&type=bysearch&queryID=myQueryID");
$this->loadModel('build');
$product = $param ? $this->loadModel('product')->getById($param) : '';
if($product and $product->type != 'normal')
{
$this->loadModel('branch');
$branches = array(BRANCH_MAIN => $this->lang->branch->main) + $this->branch->getPairs($product->id, '', $executionID);
$this->config->build->search['fields']['branch'] = sprintf($this->lang->build->branchName, $this->lang->product->branchName[$product->type]);
$this->config->build->search['params']['branch'] = array('operator' => '=', 'control' => 'select', 'values' => $branches);
}
if(!$execution->hasProduct) unset($this->config->build->search['fields']['product']);
$this->project->buildProjectBuildSearchForm($products, $queryID, $actionURL, 'execution');
/* Get builds. */
if($type == 'bysearch')
{
$builds = $this->loadModel('build')->getExecutionBuildsBySearch((int)$executionID, $queryID);
}
else
{
$builds = $this->loadModel('build')->getExecutionBuilds((int)$executionID, $type, $param, $orderBy);
}
/* Set execution builds. */
$executionBuilds = array();
$productList = $this->product->getProducts($executionID);
$showBranch = false;
if(!empty($builds))
{
foreach($builds as $build) $executionBuilds[$build->product][] = $build;
/* Get branch name. */
$branchGroups = $this->loadModel('branch')->getByProducts(array_keys($executionBuilds));
foreach($builds as $build)
{
$build->branchName = '';
if(isset($branchGroups[$build->product]))
{
$showBranch = true;
$branchPairs = $branchGroups[$build->product];
foreach(explode(',', trim($build->branch, ',')) as $branchID)
{
if(isset($branchPairs[$branchID])) $build->branchName .= "{$branchPairs[$branchID]},";
}
$build->branchName = trim($build->branchName, ',');
}
}
}
/* Header and position. */
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->build;
$this->view->position[] = html::a(inlink('browse', "executionID=$executionID"), $execution->name);
$this->view->position[] = $this->lang->execution->build;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->buildsTotal = count($builds);
$this->view->executionBuilds = $executionBuilds;
$this->view->executionID = $executionID;
$this->view->product = $type == 'product' ? $param : 'all';
$this->view->products = $products;
$this->view->type = $type;
$this->view->showBranch = $showBranch;
$this->display();
}
/**
* Browse test tasks of execution.
*
* @param int $executionID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function testtask($executionID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->loadModel('testtask');
$this->app->loadLang('testreport');
/* Save session. */
$this->session->set('testtaskList', $this->app->getURI(true), 'execution');
$this->session->set('buildList', $this->app->getURI(true), 'execution');
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$productTasks = array();
$tasks = $this->testtask->getExecutionTasks($executionID, 'execution', $orderBy, $pager);
foreach($tasks as $key => $task) $productTasks[$task->product][] = $task;
$this->view->title = $this->executions[$executionID] . $this->lang->colon . $this->lang->testtask->common;
$this->view->position[] = html::a($this->createLink('execution', 'testtask', "executionID=$executionID"), $this->executions[$executionID]);
$this->view->position[] = $this->lang->testtask->common;
$this->view->execution = $execution;
$this->view->project = $this->loadModel('project')->getByID($execution->project);
$this->view->executionID = $executionID;
$this->view->executionName = $this->executions[$executionID];
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->tasks = $productTasks;
$this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter');
$this->view->products = $this->loadModel('product')->getPairs('', 0);
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
$this->display();
}
/**
* Browse burndown chart of a execution.
*
* @param int $executionID
* @param string $type
* @param int $interval
* @access public
* @return void
*/
public function burn($executionID = 0, $type = 'noweekend', $interval = 0, $burnBy = 'left')
{
$this->loadModel('report');
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
$burnBy = $this->cookie->burnBy ? $this->cookie->burnBy : $burnBy;
/* Header and position. */
$title = $execution->name . $this->lang->colon . $this->lang->execution->burn;
$position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$position[] = $this->lang->execution->burn;
/* Get date list. */
if(((strpos('closed,suspended', $execution->status) === false and helper::today() > $execution->end)
or ($execution->status == 'closed' and substr($execution->closedDate, 0, 10) > $execution->end)
or ($execution->status == 'suspended' and $execution->suspendedDate > $execution->end))
and strpos($type, 'delay') === false)
$type .= ',withdelay';
$deadline = $execution->status == 'closed' ? substr($execution->closedDate, 0, 10) : $execution->suspendedDate;
$deadline = strpos('closed,suspended', $execution->status) === false ? helper::today() : $deadline;
$endDate = strpos($type, 'withdelay') !== false ? $deadline : $execution->end;
list($dateList, $interval) = $this->execution->getDateList($execution->begin, $endDate, $type, $interval, 'Y-m-d', $execution->end);
$executionEnd = strpos($type, 'withdelay') !== false ? $execution->end : '';
$chartData = $this->execution->buildBurnData($executionID, $dateList, $type, $burnBy, $executionEnd);
$dayList = array_fill(1, floor((int)$execution->days / $this->config->execution->maxBurnDay) + 5, '');
foreach($dayList as $key => $val) $dayList[$key] = $this->lang->execution->interval . ($key + 1) . $this->lang->day;
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->tabID = 'burn';
$this->view->burnBy = $burnBy;
$this->view->executionID = $executionID;
$this->view->executionName = $execution->name;
$this->view->type = $type;
$this->view->interval = $interval;
$this->view->chartData = $chartData;
$this->view->dayList = array('full' => $this->lang->execution->interval . '1' . $this->lang->day) + $dayList;
unset($this->lang->TRActions);
$this->display();
}
/**
* Compute burndown datas.
*
* @param string $reload
* @access public
* @return void
*/
public function computeBurn($reload = 'no')
{
$this->view->burns = $this->execution->computeBurn();
if($reload == 'yes') return print(js::reload('parent'));
$this->display();
}
/**
* Kanban CFD.
*
* @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', $begin = '', $end = '')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
$this->loadModel('kanban');
$this->app->loadClass('date');
$minDate = !helper::isZeroDate($execution->openedDate) ? date('Y-m-d', strtotime($execution->openedDate)) : date('Y-m-d', strtotime($execution->begin));
$maxDate = !helper::isZeroDate($execution->closedDate) ? date('Y-m-d', strtotime($execution->closedDate)) : helper::today();
if($execution->lifetime == 'ops' or in_array($execution->attribute, array('request', 'review'))) $type = 'task';
if(!empty($_POST))
{
$begin = htmlspecialchars($this->post->begin, ENT_QUOTES);
$end = htmlspecialchars($this->post->end, ENT_QUOTES);
$dateError = array();
if(empty($begin)) $dateError[] = sprintf($this->lang->error->notempty, $this->lang->execution->charts->cfd->begin);
if(empty($end)) $dateError[] = sprintf($this->lang->error->notempty, $this->lang->execution->charts->cfd->end);
if(empty($dateError))
{
if($begin < $minDate) $dateError[] = sprintf($this->lang->error->gt, $this->lang->execution->charts->cfd->begin, $minDate);
if($begin > $maxDate) $dateError[] = sprintf($this->lang->error->lt, $this->lang->execution->charts->cfd->begin, $maxDate);
if($end < $minDate) $dateError[] = sprintf($this->lang->error->gt, $this->lang->execution->charts->cfd->end, $minDate);
if($end > $maxDate) $dateError[] = sprintf($this->lang->error->lt, $this->lang->execution->charts->cfd->end, $maxDate);
}
if(!empty($dateError))
{
foreach($dateError as $index => $error) $dateError[$index] = str_replace(array('。', '.'), array('', ''), $error) . '
';
return $this->sendError($dateError);
}
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 $this->send(array('result' => 'success', 'locate' => $this->createLink('execution', 'cfd', "executionID=$executionID&type=$type&withWeekend=$withWeekend&begin=" . helper::safe64Encode(urlencode($begin)) . "&end=" . helper::safe64Encode(urlencode($end)))));
}
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' : '');
$chartData = $this->execution->buildCFDData($executionID, $dateList, $type);
if(isset($chartData['line'])) $chartData['line'] = array_reverse($chartData['line']);
$this->view->title = $this->lang->execution->CFD;
$this->view->type = $type;
$this->view->execution = $execution;
$this->view->withWeekend = $withWeekend;
$this->view->executionName = $execution->name;
$this->view->executionID = $executionID;
$this->view->chartData = $chartData;
$this->view->features = $this->execution->getExecutionFeatures($execution);
$this->view->begin = $begin;
$this->view->end = $end;
$this->view->minDate = $minDate;
$this->view->maxDate = $maxDate;
$this->display();
}
/**
* Compute cfd datas.
*
* @param string $reload
* @param int $executionID
* @access public
* @return void
*/
public function computeCFD($reload = 'no', $executionID = 0)
{
$this->execution->computeCFD($executionID);
if($reload == 'yes') return print(js::reload('parent'));
}
/**
* Fix burn for first date.
*
* @param int $executionID
* @access public
* @return void
*/
public function fixFirst($executionID)
{
if($_POST)
{
$this->execution->fixFirst($executionID);
return print(js::reload('parent.parent'));
}
$execution = $this->execution->getById($executionID);
$this->view->firstBurn = $this->dao->select('*')->from(TABLE_BURN)->where('execution')->eq($executionID)->andWhere('date')->eq($execution->begin)->fetch();
$this->view->execution = $execution;
$this->display();
}
/**
* Browse team of a execution.
*
* @param int $executionID
* @access public
* @return void
*/
public function team($executionID = 0)
{
$this->app->session->set('teamList', $this->app->getURI(true), 'execution');
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$title = $execution->name . $this->lang->colon . $this->lang->execution->team;
$position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$position[] = $this->lang->execution->team;
$this->view->title = $title;
$this->view->position = $position;
$this->view->deptUsers = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
$this->display();
}
/**
* Create a execution.
*
* @param string $projectID
* @param int $executionID
* @param string $copyExecutionID
* @param int $planID
* @param string $confirm
* @param string $productID
* @param string $extra
*
* @access public
* @return void
*/
public function create($projectID = '', $executionID = 0, $copyExecutionID = '', $planID = 0, $confirm = 'no', $productID = 0, $extra = '')
{
if($this->app->tab == 'doc') unset($this->lang->doc->menu->execution['subMenu']);
if($this->app->tab == 'project') $this->project->setMenu($projectID);
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$type = !empty($output['type']) ? $output['type'] : 'sprint';
$name = '';
$code = '';
$team = '';
$products = array();
$whitelist = '';
$acl = 'private';
$plan = new stdClass();
$productPlan = array();
$productPlans = array();
if($copyExecutionID)
{
$copyExecution = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($copyExecutionID)->fetch();
$type = $copyExecution->type;
$name = $copyExecution->name;
$code = $copyExecution->code;
$team = $copyExecution->team;
$acl = $copyExecution->acl;
$whitelist = $copyExecution->whitelist;
$projectID = $copyExecution->project;
$project = $this->project->getByID($projectID);
$products = $this->loadModel('product')->getProducts($copyExecutionID);
$branches = $this->project->getBranchesByProject($copyExecutionID);
$plans = $this->loadModel('productplan')->getGroupByProduct(array_keys($products), 'skipParent|unexpired');
$branchGroups = $this->execution->getBranchByProduct(array_keys($products), $projectID);
$linkedBranches = array();
foreach($products as $productIndex => $product)
{
$productPlans[$productIndex] = array();
foreach($branches[$productIndex] as $branchID => $branch)
{
$linkedBranches[$productIndex][$branchID] = $branchID;
$productPlans[$productIndex] += isset($plans[$productIndex][$branchID]) ? $plans[$productIndex][$branchID] : array();
}
}
$this->view->branches = $branches;
$this->view->linkedBranches = $linkedBranches;
}
$project = $this->project->getByID($projectID);
if(!empty($project) and ($project->model == 'kanban' or ($project->model == 'agileplus' and $type == 'kanban')))
{
global $lang;
$executionLang = $lang->execution->common;
$executionCommonLang = $lang->executionCommon;
$lang->executionCommon = $lang->execution->kanban;
$lang->execution->common = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
$lang->execution->common = $executionLang;
$lang->executionCommon = $executionCommonLang;
$lang->execution->typeList['sprint'] = $executionCommonLang;
}
elseif(!empty($project) and ($project->model == 'waterfall' or $project->model == 'waterfallplus'))
{
global $lang;
$lang->executionCommon = $lang->execution->stage;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
$this->config->execution->create->requiredFields .= ',products0';
}
$this->app->loadLang('program');
$this->app->loadLang('stage');
$this->app->loadLang('programplan');
if($executionID)
{
$execution = $this->execution->getById($executionID);
if(!empty($planID) and $execution->lifetime != 'ops')
{
if($confirm == 'yes')
{
$this->execution->linkStories($executionID);
}
else
{
$executionProductList = $this->loadModel('product')->getProducts($executionID);
$multiBranchProduct = false;
foreach($executionProductList as $executionProduct)
{
if($executionProduct->type != 'normal')
{
$multiBranchProduct = true;
break;
}
}
$importPlanStoryTips = $multiBranchProduct ? $this->lang->execution->importBranchPlanStory : $this->lang->execution->importPlanStory;
return print(js::confirm($importPlanStoryTips, inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=$planID&confirm=yes"), inlink('create', "projectID=$projectID&executionID=$executionID")));
}
}
if(!empty($projectID) and $execution->type == 'kanban' and $this->app->tab == 'project') return $this->send(array('result' => 'success', 'locate' => $this->createLink('project', 'index', "projectID=$projectID")));
if(!empty($projectID) and $execution->type == 'kanban') return $this->send(array('result' => 'success', 'locate' => inlink('kanban', "executionID=$executionID")));
$this->execution->setMenu($executionID); // Fix bug #22897.
$this->view->title = $this->lang->execution->tips;
$this->view->tips = $this->fetch('execution', 'tips', "executionID=$executionID");
$this->view->executionID = $executionID;
$this->view->projectID = $projectID;
$this->view->project = $project;
return $this->display();
}
if(!empty($planID))
{
$plan = $this->dao->select('*')->from(TABLE_PRODUCTPLAN)->where('id')->eq($planID)->fetch();
$products = $this->dao->select('t1.id, t1.name, t1.type, t2.branch')->from(TABLE_PRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')->on('t1.id = t2.product')
->where('t1.id')->eq($plan->product)
->fetchAll('id');
$productPlan = $this->loadModel('productplan')->getPairsForStory($plan->product, $plan->branch, 'skipParent|unexpired|withMainPlan');
$linkedBranches = array();
$linkedBranches[$plan->product][$plan->branch] = $plan->branch;
$this->view->linkedBranches = $linkedBranches;
}
if(!empty($project) and !$project->division)
{
$products = $this->loadModel('product')->getProducts($projectID);
$branches = $this->project->getBranchesByProject($projectID);
$plans = $this->loadModel('productplan')->getGroupByProduct(array_keys($products), 'skipParent|unexpired');
$linkedBranches = array();
foreach($products as $productIndex => $product)
{
$productPlans[$productIndex] = array();
if(isset($branches[$productIndex]))
{
foreach($branches[$productIndex] as $branchID => $branch)
{
$linkedBranches[$productIndex][$branchID] = $branchID;
$productPlans[$productIndex] += isset($plans[$productIndex][$branchID]) ? $plans[$productIndex][$branchID] : array();
}
}
}
$this->view->branches = $branches;
$this->view->linkedBranches = $linkedBranches;
}
if(isset($project->hasProduct) and empty($project->hasProduct))
{
$shadowProduct = $this->loadModel('product')->getShadowProductByProject($project->id);
$productPlan = $this->loadModel('productplan')->getPairs($shadowProduct->id, '0,0', 'noclosed,unexpired', true);
}
if(!empty($_POST))
{
if(isset($_POST['attribute']) and in_array($_POST['attribute'], array('request', 'design', 'review'))) unset($_POST['plans']);
/* Filter empty plans. */
if(!empty($_POST['plans']))
{
foreach($_POST['plans'] as $key => $planItem) $_POST['plans'][$key] = array_filter($_POST['plans'][$key]);
$_POST['plans'] = array_filter($_POST['plans']);
}
$executionID = $this->execution->create($copyExecutionID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->execution->updateProducts($executionID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$comment = $project->hasProduct ? join(',', $_POST['products']) : '';
$this->loadModel('action')->create($this->objectType, $executionID, 'opened', '', $comment);
$this->loadModel('programplan')->computeProgress($executionID, 'create');
$message = $this->executeHooks($executionID);
if($message) $this->lang->saveSuccess = $message;
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $executionID));
if($this->app->tab == 'doc')
{
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('doc', 'objectLibs', "type=execution")));
}
if(!empty($projectID) and strpos(',kanban,agileplus,waterfallplus,', ",$project->model,") !== false)
{
$execution = $this->execution->getById($executionID);
if($execution->type == 'kanban') $this->loadModel('kanban')->createRDKanban($execution);
}
if(!empty($_POST['plans']))
{
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID©ExecutionID=&planID=1&confirm=no")));
}
else
{
if(!empty($projectID) and $project->model == 'kanban')
{
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$link = $this->config->vision != 'lite' ? $this->createLink('project', 'index', "projectID=$projectID") : $this->createLink('project', 'execution', "status=all&projectID=$projectID");
if($this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID")));
}
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID")));
}
}
$this->loadModel('user');
$poUsers = $this->user->getPairs('noclosed|nodeleted|pofirst', '', $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PM"] = $this->config->user->moreLink;
$pmUsers = $this->user->getPairs('noclosed|nodeleted|pmfirst', '', $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PO"] = $this->config->user->moreLink;
$qdUsers = $this->user->getPairs('noclosed|nodeleted|qdfirst', '', $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["QD"] = $this->config->user->moreLink;
$rdUsers = $this->user->getPairs('noclosed|nodeleted|devfirst', '', $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
$this->loadModel('product');
$allProducts = $this->product->getProductPairsByProject($projectID, 'noclosed');
$projectModel = isset($project->model) ? $project->model : '';
if($projectModel == 'agileplus') $projectModel = array('scrum', 'agileplus');
if($projectModel == 'waterfallplus') $projectModel = array('waterfall', 'waterfallplus');
$copyProjects = $this->loadModel('project')->getPairsByProgram(isset($project->parent) ? $project->parent : '', 'noclosed', '', 'order_asc', '', $projectModel, 'multiple');
$copyProjectID = ($projectID == 0) ? key($copyProjects) : $projectID;
if(!empty($project->hasProduct)) $allProducts = array(0 => '') + $allProducts;
if(isset($project->hasProduct) and $project->hasProduct == 0) $this->lang->execution->PO = $this->lang->common->story . $this->lang->execution->owner;
$this->view->title = $this->app->tab == 'execution' ? $this->lang->execution->createExec : $this->lang->execution->create;
$this->view->position[] = $this->view->title;
$this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('execution', 'all') : '';
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->allProducts = $allProducts;
$this->view->acl = $acl;
$this->view->plan = $plan;
$this->view->name = $name;
$this->view->code = $code;
$this->view->team = $team;
$this->view->teams = array(0 => '') + $this->execution->getCanCopyObjects((int)$projectID);
$this->view->allProjects = array(0 => '') + $this->project->getPairsByModel('all', 0, 'noclosed,multiple');
$this->view->copyProjects = $copyProjects;
$this->view->copyExecutions = array('' => '') + $this->execution->getList($copyProjectID, 'all', 'all', 0, 0, 0, null, false);
$this->view->executionID = $executionID;
$this->view->productID = $productID;
$this->view->projectID = $projectID;
$this->view->products = $products;
$this->view->multiBranchProducts = $this->product->getMultiBranchPairs();
$this->view->productPlan = array(0 => '') + $productPlan;
$this->view->productPlans = array(0 => '') + $productPlans;
$this->view->whitelist = $whitelist;
$this->view->copyExecutionID = $copyExecutionID;
$this->view->branchGroups = isset($branchGroups) ? $branchGroups : $this->execution->getBranchByProduct(array_keys($products), $projectID);
$this->view->poUsers = $poUsers;
$this->view->pmUsers = $pmUsers;
$this->view->qdUsers = $qdUsers;
$this->view->rdUsers = $rdUsers;
$this->view->users = $this->loadModel('user')->getPairs('nodeleted|noclosed');
$this->view->copyExecution = isset($copyExecution) ? $copyExecution : '';
$this->view->from = $this->app->tab;
$this->view->isStage = (isset($project->model) and ($project->model == 'waterfall' or $project->model == 'waterfallplus')) ? true : false;
$this->view->project = $project;
$this->view->division = !empty($project) ? $project->division : 1;
$this->view->type = $type;
$this->display();
}
/**
* Edit a execution.
*
* @param int $executionID
* @param string $action
* @param string $extra
*
* @access public
* @return void
*/
public function edit($executionID, $action = 'edit', $extra = '', $newPlans = '', $confirm = 'no')
{
/* Load language files and get browseExecutionLink. */
$this->loadModel('product');
$this->app->loadLang('program');
$this->app->loadLang('stage');
$this->app->loadLang('programplan');
$browseExecutionLink = $this->createLink('execution', 'browse', "executionID=$executionID");
$execution = $this->execution->getById($executionID);
$project = $this->project->getById($execution->project);
$branches = $this->project->getBranchesByProject($executionID);
$linkedProductIdList = empty($branches) ? '' : array_keys($branches);
if(!empty($newPlans) and $confirm == 'yes')
{
$newPlans = explode(',', $newPlans);
$projectID = $this->dao->select('project')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch('project');
$this->loadModel('productplan')->linkProject($executionID, $newPlans);
$this->productplan->linkProject($projectID, $newPlans);
return $this->send(array('result' => 'success', 'locate' => inlink('view', "executionID=$executionID")));
}
elseif(!empty($newPlans))
{
$executionProductList = $this->loadModel('product')->getProducts($executionID);
$multiBranchProduct = false;
foreach($executionProductList as $executionProduct)
{
if($executionProduct->type != 'normal')
{
$multiBranchProduct = true;
break;
}
}
$importEditPlanStoryTips = $multiBranchProduct ? $this->lang->execution->importBranchEditPlanStory : $this->lang->execution->importEditPlanStory;
return print(js::confirm($importEditPlanStoryTips, inlink('edit', "executionID=$executionID&action=edit&extra=&newPlans=$newPlans&confirm=yes"), inlink('view', "executionID=$executionID")));
}
/* Set menu. */
$this->execution->setMenu($executionID);
if(!empty($_POST))
{
$oldPlans = $this->dao->select('plan')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($executionID)->andWhere('plan')->ne(0)->fetchPairs('plan');
$oldProducts = $this->product->getProducts($executionID, 'all', '', true, $linkedProductIdList);
$changes = $this->execution->update($executionID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->execution->updateProducts($executionID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if($action == 'undelete')
{
$this->loadModel('action');
$this->dao->update(TABLE_EXECUTION)->set('deleted')->eq(0)->where('id')->eq($executionID)->exec();
$this->dao->update(TABLE_ACTION)->set('extra')->eq(ACTIONMODEL::BE_UNDELETED)->where('id')->eq($extra)->exec();
$this->action->create($this->objectType, $executionID, 'undeleted');
}
$oldProducts = array_keys($oldProducts);
$newProducts = $this->product->getProducts($executionID, 'all', '', true, $linkedProductIdList);
$newProducts = array_keys($newProducts);
$diffProducts = array_merge(array_diff($oldProducts, $newProducts), array_diff($newProducts, $oldProducts));
$products = $diffProducts ? join(',', $newProducts) : '';
if($changes or $diffProducts)
{
$actionID = $this->loadModel('action')->create($this->objectType, $executionID, 'edited', '', $products);
$this->action->logHistory($actionID, $changes);
}
$project = $this->loadModel('project')->getById($execution->project);
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'edit');
/* Link the plan stories. */
$oldPlans = explode(',', implode(',' ,$oldPlans));
$newPlans = array();
if(isset($_POST['plans']))
{
foreach($_POST['plans'] as $plans)
{
foreach($plans as $planID)
{
if(array_search($planID, $oldPlans) === false) $newPlans[$planID] = $planID;
}
}
}
$newPlans = array_filter($newPlans);
if(!empty($newPlans))
{
$newPlans = implode(',', $newPlans);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('edit', "executionID=$executionID&action=edit&extra=&newPlans=$newPlans&confirm=no")));
}
$message = $this->executeHooks($executionID);
if($message) $this->lang->saveSuccess = $message;
if($_POST['status'] == 'doing') $this->loadModel('common')->syncPPEStatus($executionID);
/* If link from no head then reload. */
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('view', "executionID=$executionID")));
}
$executions = array('' => '') + $this->executions;
$managers = $this->execution->getDefaultManagers($executionID);
/* Remove current execution from the executions. */
unset($executions[$executionID]);
$title = $this->lang->execution->edit . $this->lang->colon . $execution->name;
$position[] = html::a($browseExecutionLink, $execution->name);
$position[] = $this->lang->execution->edit;
$allProducts = $this->product->getProducts($execution->project, 'noclosed', '', false, $linkedProductIdList);
$allProducts = array(0 => '') + $allProducts;
$this->loadModel('productplan');
$productPlans = array(0 => '');
$linkedBranches = array();
$linkedBranchList = array();
$linkedProducts = $this->product->getProducts($executionID, 'all', '', true, $linkedProductIdList, false);
$plans = $this->productplan->getGroupByProduct(array_keys($linkedProducts), 'skipParent|unexpired');
$executionStories = $this->project->getStoriesByProject($executionID);
/* If the story of the product which linked the execution, you don't allow to remove the product. */
$unmodifiableProducts = array();
$unmodifiableBranches = array();
$linkedStoryIDList = array();
foreach($linkedProducts as $productID => $linkedProduct)
{
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->deleted ? $linkedProduct->name . "({$this->lang->product->deleted})" : $linkedProduct->name;
$productPlans[$productID] = array();
foreach($branches[$productID] as $branchID => $branch)
{
$productPlans[$productID] += isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array();
$linkedBranchList[$branchID] = $branchID;
$linkedBranches[$productID][$branchID] = $branchID;
if($branchID != BRANCH_MAIN and isset($plans[$productID][BRANCH_MAIN])) $productPlans[$productID] += $plans[$productID][BRANCH_MAIN];
if(!empty($executionStories[$productID][$branchID]))
{
array_push($unmodifiableProducts, $productID);
array_push($unmodifiableBranches, $branchID);
$linkedStoryIDList[$productID][$branchID] = $executionStories[$productID][$branchID]->storyIDList;
}
}
}
$this->loadModel('user');
$poUsers = $this->user->getPairs('noclosed|nodeleted|pofirst', $execution->PO, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PM"] = $this->config->user->moreLink;
$pmUsers = $this->user->getPairs('noclosed|nodeleted|pmfirst', $execution->PM, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PO"] = $this->config->user->moreLink;
$qdUsers = $this->user->getPairs('noclosed|nodeleted|qdfirst', $execution->QD, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["QD"] = $this->config->user->moreLink;
$rdUsers = $this->user->getPairs('noclosed|nodeleted|devfirst', $execution->RD, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
$project = $this->project->getById($execution->project);
if(!$project->hasProduct) $this->lang->execution->PO = $this->lang->common->story . $this->lang->execution->owner;
if($project->model == 'waterfall' or $project->model == 'waterfallplus')
{
$parentStage = $this->project->getByID($execution->parent, 'stage');
$this->view->enableOptionalAttr = (empty($parentStage) or (!empty($parentStage) and $parentStage->attribute == 'mix'));
}
if($this->app->tab == 'project' and $project->model == 'waterfallplus')
{
$productID = $this->product->getProductIDByProject($executionID);
$parentStageList = $this->loadModel('programplan')->getParentStageList($execution->project, $executionID, $productID);
unset($parentStageList[0]);
}
$this->view->title = $title;
$this->view->position = $position;
$this->view->executions = $executions;
$this->view->execution = $execution;
$this->view->project = $project;
$this->view->poUsers = $poUsers;
$this->view->pmUsers = $pmUsers;
$this->view->qdUsers = $qdUsers;
$this->view->rdUsers = $rdUsers;
$this->view->users = $this->user->getPairs('nodeleted|noclosed');
$this->view->project = $project;
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->allProducts = $allProducts;
$this->view->linkedProducts = $linkedProducts;
$this->view->linkedBranches = $linkedBranches;
$this->view->linkedStoryIDList = $linkedStoryIDList;
$this->view->branches = $branches;
$this->view->unmodifiableProducts = $unmodifiableProducts;
$this->view->unmodifiableBranches = $unmodifiableBranches;
$this->view->multiBranchProducts = $this->product->getMultiBranchPairs();
$this->view->productPlans = $productPlans;
$this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($linkedProducts), $execution->project, 'noclosed', $linkedBranchList);
$this->view->teamMembers = $this->execution->getTeamMembers($executionID);
$this->view->allProjects = $this->project->getPairsByModel($project->model, 0, 'noclosed', $project->id);
$this->view->parentStageList = isset($parentStageList) ? $parentStageList : array();
$this->display();
}
/**
* Batch edit.
*
* @param int $executionID
* @access public
* @return void
*/
public function batchEdit($executionID = 0)
{
$this->app->loadLang('stage');
$this->app->loadLang('programplan');
if($this->post->names)
{
$allChanges = $this->execution->batchUpdate();
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(!empty($allChanges))
{
foreach($allChanges as $executionID => $changes)
{
if(empty($changes)) continue;
$actionID = $this->loadModel('action')->create($this->objectType, $executionID, 'Edited');
$this->action->logHistory($actionID, $changes);
}
}
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->session->executionList));
}
if($this->app->tab == 'project')
{
$projectID = $this->session->project;
$project = $this->project->getById($projectID);
$allProjects = $this->project->getPairsByModel($project->model, 0, 'noclosed', isset($projectID) ? $projectID : 0);
$this->project->setMenu($projectID);
$this->view->project = $project;
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->lang->execution->common = $this->lang->execution->stage;
}
else
{
$allProjects = $this->project->getPairsByModel('all', 0, 'noclosed', isset($projectID) ? $projectID : 0);
}
if(!$this->post->executionIDList) return print(js::locate($this->session->executionList, 'parent'));
$executionIDList = $this->post->executionIDList;
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->in($executionIDList)->fetchAll('id');
$projects = $this->dao->select('id,project')->from(TABLE_PROJECT)->where('id')->in($executionIDList)->fetchPairs();
$projects = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->in($projects)->fetchAll('id');
$appendPoUsers = $appendPmUsers = $appendQdUsers = $appendRdUsers = array();
foreach($executions as $execution)
{
$appendPoUsers[$execution->PO] = $execution->PO;
$appendPmUsers[$execution->PM] = $execution->PM;
$appendQdUsers[$execution->QD] = $execution->QD;
$appendRdUsers[$execution->RD] = $execution->RD;
}
/* Set custom. */
foreach(explode(',', $this->config->execution->customBatchEditFields) as $field) $customFields[$field] = str_replace($this->lang->executionCommon, $this->lang->execution->common, $this->lang->execution->$field);
$this->view->customFields = $customFields;
$this->view->showFields = $this->config->execution->custom->batchEditFields;
$this->loadModel('user');
$pmUsers = $this->user->getPairs('noclosed|nodeleted|pmfirst', $appendPmUsers, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PM"] = $this->config->user->moreLink;
$poUsers = $this->user->getPairs('noclosed|nodeleted|pofirst', $appendPoUsers, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PO"] = $this->config->user->moreLink;
$qdUsers = $this->user->getPairs('noclosed|nodeleted|qdfirst', $appendQdUsers, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["QD"] = $this->config->user->moreLink;
$rdUsers = $this->user->getPairs('noclosed|nodeleted|devfirst', $appendRdUsers, $this->config->maxCount);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
$this->view->title = $this->lang->execution->batchEdit;
$this->view->position[] = $this->lang->execution->batchEdit;
$this->view->executions = $executions;
$this->view->allProjects = $allProjects;
$this->view->projects = $projects;
$this->view->pmUsers = $pmUsers;
$this->view->poUsers = $poUsers;
$this->view->qdUsers = $qdUsers;
$this->view->rdUsers = $rdUsers;
$this->view->from = $this->app->tab;
$this->display();
}
/**
* Batch change status.
*
* @param string $status
* @param int $projectID
* @access public
* @return void
*/
public function batchChangeStatus($status, $projectID = 0)
{
$executionIdList = $this->post->executionIDList;
if(is_string($executionIdList)) $executionIdList = explode(',', $executionIdList);
$pointOutStages = $this->execution->batchChangeStatus($executionIdList, $status);
$project = $this->loadModel('project')->getById($projectID);
if($pointOutStages)
{
$alertLang = '';
if($status == 'wait')
{
/* In execution-all list or waterfall, waterfallplus project's execution list. */
if(empty($project) or (!empty($project) and strpos($project->model, 'waterfall') !== false))
{
$executionLang = (empty($project) or (!empty($project) and $project->model == 'waterfallplus')) ? $this->lang->execution->common : $this->lang->stage->common;
$alertLang = sprintf($this->lang->execution->hasStartedTaskOrSubStage, $executionLang, $pointOutStages);
}
if(!empty($project) and strpos('agileplus,scrum', $project->model) !== false)
{
$executionLang = $project->model == 'scrum' ? $this->lang->executionCommon : $this->lang->execution->common;
$alertLang = sprintf($this->lang->execution->hasStartedTask, $executionLang, $pointOutStages);
}
}
if($status == 'suspended') $alertLang = sprintf($this->lang->execution->hasSuspendedOrClosedChildren, $pointOutStages);
if($status == 'closed') $alertLang = sprintf($this->lang->execution->hasNotClosedChildren, $pointOutStages);
return print(js::alert($alertLang) . js::locate($this->session->executionList, 'parent'));
}
return print(js::locate($this->session->executionList, 'parent'));
}
/**
* Start execution.
*
* @param int $executionID
* @param string $from
* @access public
* @return void
*/
public function start($executionID, $from = 'execution')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->execution->start($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
if($this->post->comment != '' or !empty($changes))
{
$actionID = $this->action->create($this->objectType, $executionID, 'Started', $this->post->comment);
$this->action->logHistory($actionID, $changes);
}
$project = $this->loadModel('project')->getById($execution->project);
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'start');
$this->loadModel('common')->syncPPEStatus($executionID);
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('doing')"));
}
else
{
return print(js::reload('parent.parent'));
}
}
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->start;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $this->view->execution->name);
$this->view->position[] = $this->lang->execution->start;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$this->display();
}
/**
* Delay execution.
*
* @param int $executionID
* @param string $from
* @access public
* @return void
*/
public function putoff($executionID, $from = 'execution')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->execution->putoff($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
if($this->post->comment != '' or !empty($changes))
{
$actionID = $this->action->create($this->objectType, $executionID, 'Delayed', $this->post->comment);
$this->action->logHistory($actionID, $changes);
}
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('doing')"));
}
else
{
return print(js::reload('parent.parent'));
}
}
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->putoff;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $this->view->execution->name);
$this->view->position[] = $this->lang->execution->putoff;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$this->display();
}
/**
* Suspend execution.
*
* @param int $executionID
* @param string $from
* @access public
* @return void
*/
public function suspend($executionID, $from = 'execution')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
$this->loadModel('action');
$this->execution->computeBurn($executionID);
$changes = $this->execution->suspend($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
if($this->post->comment != '' or !empty($changes))
{
$actionID = $this->action->create($this->objectType, $executionID, 'Suspended', $this->post->comment);
$this->action->logHistory($actionID, $changes);
}
$project = $this->loadModel('project')->getById($execution->project);
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'suspend');
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('suspended')"));
}
else
{
return print(js::reload('parent.parent'));
}
}
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->suspend;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $this->view->execution->name);
$this->view->position[] = $this->lang->execution->suspend;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$this->display();
}
/**
* Activate execution.
*
* @param int $executionID
* @param string $frim
* @access public
* @return void
*/
public function activate($executionID, $from = 'execution')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
$this->execution->activate($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
$project = $this->loadModel('project')->getById($execution->project);
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'activate');
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('doing')"));
}
else
{
return print(js::reload('parent.parent'));
}
}
$newBegin = date('Y-m-d');
$dateDiff = helper::diffDate($newBegin, $execution->begin);
$newEnd = date('Y-m-d', strtotime($execution->end) + $dateDiff * 24 * 3600);
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->activate;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $this->view->execution->name);
$this->view->position[] = $this->lang->execution->activate;
$this->view->execution = $execution;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$this->view->newBegin = $newBegin;
$this->view->newEnd = $newEnd;
$this->display();
}
/**
* Close execution.
*
* @param int $executionID
* @param string $from
* @access public
* @return void
*/
public function close($executionID, $from = 'execution')
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
$this->execution->computeBurn($executionID);
$this->execution->close($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
$project = $this->loadModel('project')->getById($execution->project);
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'close');
$this->executeHooks($executionID);
if(isonlybody() and $from == 'kanban')
{
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('closed')"));
}
else
{
return print(js::reload('parent.parent'));
}
}
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->close;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $this->view->execution->name);
$this->view->position[] = $this->lang->execution->close;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$this->display();
}
/**
* View a execution.
*
* @param int $executionID
* @access public
* @return void
*/
public function view($executionID)
{
$this->app->session->set('teamList', $this->app->getURI(true), 'execution');
$executionID = $this->execution->saveState((int)$executionID, $this->executions);
$execution = $this->execution->getById($executionID, true);
$type = $this->config->vision == 'lite' ? 'kanban' : 'stage,sprint,kanban';
if(empty($execution) || strpos($type, $execution->type) === false) return print(js::error($this->lang->notFound) . js::locate('back'));
$execution->projectInfo = $this->loadModel('project')->getByID($execution->project);
$programList = array_filter(explode(',', $execution->projectInfo->path));
array_pop($programList);
$this->view->programList = $this->loadModel('program')->getPairsByList($programList);
if($execution->type == 'kanban' and defined('RUN_MODE') and RUN_MODE == 'api') return print($this->fetch('execution', 'kanban', "executionID=$executionID"));
$this->app->loadLang('program');
/* Execution not found to prevent searching for .*/
if(!isset($this->executions[$execution->id])) $this->executions = $this->execution->getPairs($execution->project, 'all', 'nocode');
$products = $this->loadModel('product')->getProducts($execution->id);
$linkedBranches = array();
foreach($products as $product)
{
if(isset($product->branches))
{
foreach($product->branches as $branchID) $linkedBranches[$branchID] = $branchID;
}
}
/* Set menu. */
$this->execution->setMenu($execution->id);
$this->app->loadLang('bug');
if($execution->type == 'kanban')
{
$this->app->loadClass('date');
list($begin, $end) = $this->execution->getBeginEnd4CFD($execution);
$dateList = date::getDateList($begin, $end, 'Y-m-d', 'noweekend');
$chartData = $this->execution->buildCFDData($executionID, $dateList, 'task');
if(isset($chartData['line'])) $chartData['line'] = array_reverse($chartData['line']);
$this->view->begin = helper::safe64Encode(urlencode($begin));
$this->view->end = helper::safe64Encode(urlencode($end));
}
else
{
$type = 'noweekend';
if(((strpos('closed,suspended', $execution->status) === false and helper::today() > $execution->end)
or ($execution->status == 'closed' and substr($execution->closedDate, 0, 10) > $execution->end)
or ($execution->status == 'suspended' and $execution->suspendedDate > $execution->end))
and strpos($type, 'delay') === false)
{
$type .= ',withdelay';
}
$deadline = $execution->status == 'closed' ? substr($execution->closedDate, 0, 10) : $execution->suspendedDate;
$deadline = strpos('closed,suspended', $execution->status) === false ? helper::today() : $deadline;
$endDate = strpos($type, 'withdelay') !== false ? $deadline : $execution->end;
list($dateList, $interval) = $this->execution->getDateList($execution->begin, $endDate, $type, 0, 'Y-m-d', $execution->end);
$executionEnd = strpos($type, 'withdelay') !== false ? $execution->end : '';
$chartData = $this->execution->buildBurnData($executionID, $dateList, $type, 'left', $executionEnd);
}
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager(0, 30, 1);
$this->executeHooks($executionID);
if(!$execution->projectInfo->hasProduct) $this->lang->execution->PO = $this->lang->common->story . $this->lang->execution->owner;
$this->view->title = $this->lang->execution->view;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$this->view->position[] = $this->view->title;
$this->view->execution = $execution;
$this->view->products = $products;
$this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), '', $linkedBranches);
$this->view->planGroups = $this->execution->getPlans($products);
$this->view->actions = $this->loadModel('action')->getList($this->objectType, $executionID);
$this->view->dynamics = $this->loadModel('action')->getDynamic('all', 'all', 'date_desc', $pager, 'all', 'all', $executionID);
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->teamMembers = $this->execution->getTeamMembers($executionID);
$this->view->docLibs = $this->loadModel('doc')->getLibsByObject('execution', $executionID);
$this->view->statData = $this->execution->statRelatedData($executionID);
$this->view->chartData = $chartData;
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
$this->view->type = $type;
$this->view->features = $this->execution->getExecutionFeatures($execution);
$this->view->project = $this->loadModel('project')->getByID($execution->project);
$this->display();
}
/**
* Kanban.
*
* @param int $executionID
* @param string $browseType
* @param string $orderBy
* @param string $groupBy
* @access public
* @return void
*/
public function kanban($executionID, $browseType = 'all', $orderBy = 'id_asc', $groupBy = 'default')
{
$this->app->loadLang('bug');
$this->app->loadLang('kanban');
if(empty($groupBy)) $groupBy = 'default';
$this->lang->execution->menu = new stdclass();
$execution = $this->commonAction($executionID);
if($execution->type != 'kanban') return print(js::locate(inlink('view', "executionID=$executionID")));
/* Set Session. */
$this->session->set('execGroupBy', $groupBy);
$this->session->set('storyList', $this->app->getURI(true), 'execution');
$this->session->set('rdSearchValue', '');
$features = $this->execution->getExecutionFeatures($execution);
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy);
$executionActions = array();
foreach($this->config->execution->statusActions as $action)
{
if($this->execution->isClickable($execution, $action)) $executionActions[] = $action;
}
/* Set lane type. */
if(!$features['story'] and !$features['qa']) $browseType = 'task';
if(!$features['story']) unset($this->lang->kanban->group->task['story']);
$this->session->set('execLaneType', $browseType);
$userList = array();
$users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$avatarPairs = $this->user->getAvatarPairs('all');
foreach($avatarPairs as $account => $avatar)
{
if(!isset($users[$account])) continue;
$userList[$account]['realname'] = $users[$account];
$userList[$account]['avatar'] = $avatar;
}
$userList['closed']['account'] = 'Closed';
$userList['closed']['realname'] = 'Closed';
$userList['closed']['avatar'] = '';
/* Get execution's product. */
$productID = 0;
$branchID = 0;
$products = $this->loadModel('product')->getProducts($executionID);
$productNames = array();
if($products)
{
$productID = key($products);
$branches = $this->loadModel('branch')->getPairs($productID, '', $executionID);
if($branches) $branchID = key($branches);
}
foreach($products as $product) $productNames[$product->id] = $product->name;
$plans = $this->execution->getPlans($products, 'skipParent', $executionID);
$allPlans = array('' => '');
if(!empty($plans))
{
foreach($plans as $plan) $allPlans += $plan;
}
$projectID = $this->loadModel('task')->getProjectID($execution->id);
$taskToOpen = !empty($_COOKIE['taskToOpen']) ? $_COOKIE['taskToOpen'] : 0;
setcookie('taskToOpen', 0, time() - 3600, $this->config->webRoot, '', $this->config->cookieSecure, true);
$this->view->title = $this->lang->kanban->view;
$this->view->users = $users;
$this->view->regions = $kanbanData;
$this->view->execution = $execution;
$this->view->executionID = $executionID;
$this->view->userList = $userList;
$this->view->browseType = $browseType;
$this->view->orderBy = $orderBy;
$this->view->groupBy = $groupBy;
$this->view->productID = $productID;
$this->view->productNames = $productNames;
$this->view->productNum = count($products);
$this->view->branchID = $branchID;
$this->view->projectID = $projectID;
$this->view->project = $this->loadModel('project')->getByID($projectID);
$this->view->allPlans = $allPlans;
$this->view->features = $features;
$this->view->kanbanData = $kanbanData;
$this->view->executionActions = $executionActions;
$this->view->kanban = $this->lang->execution->kanban;
$this->view->taskToOpen = $taskToOpen;
$this->display();
}
/**
* Task kanban.
*
* @param int $executionID
* @param string $browseType story|bug|task|all
* @param string $orderBy
* @param string $groupBy
* @access public
* @return void
*/
public function taskKanban($executionID, $browseType = 'all', $orderBy = 'order_asc', $groupBy = '')
{
if(empty($groupBy)) $groupBy = 'default';
/* Save to session. */
$this->session->set('taskSearchValue', '');
$uri = $this->app->getURI(true);
$this->app->session->set('taskList', $uri, 'execution');
$this->app->session->set('bugList', $uri, 'qa');
$this->app->session->set('execGroupBy', $groupBy);
/* Load language. */
$this->app->loadLang('task');
$this->app->loadLang('bug');
$this->loadModel('kanban');
/* Compatibility IE8. */
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7");
$this->execution->setMenu($executionID);
$execution = $this->execution->getById($executionID);
if($execution->lifetime == 'ops' or in_array($execution->attribute, array('request', 'review')))
{
$browseType = 'task';
unset($this->lang->kanban->group->task['story']);
}
$this->app->session->set('execLaneType', $browseType);
if($groupBy == 'story' and $browseType == 'task' and !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'id_asc';
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
if(empty($kanbanGroup))
{
$this->kanban->createExecutionLane($executionID, $browseType);
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
}
/* Show lanes of the attribute: no story&bug in request, no bug in design. */
if(!isset($this->lang->execution->menu->story)) unset($kanbanGroup['story']);
if(!isset($this->lang->execution->menu->qa)) unset($kanbanGroup['bug']);
/* Determines whether an object is editable. */
$canBeChanged = common::canModify('execution', $execution);
/* Get execution's product. */
$productID = 0;
$productNames = array();
$products = $this->loadModel('product')->getProducts($executionID);
if($products) $productID = key($products);
foreach($products as $product) $productNames[$product->id] = $product->name;
$plans = $this->execution->getPlans($products);
$allPlans = array('' => '');
if(!empty($plans))
{
foreach($plans as $plan) $allPlans += $plan;
}
$userList = $this->dao->select('account, realname, avatar')->from(TABLE_USER)->where('deleted')->eq(0)->fetchAll('account');
$userList['closed'] = new stdclass();
$userList['closed']->account = 'Closed';
$userList['closed']->realname = 'Closed';
$userList['closed']->avatar = '';
$projectID = $execution->project;
$project = $this->project->getByID($projectID);
$hiddenPlan = $project->model !== 'scrum';
$this->view->title = $this->lang->execution->kanban;
$this->view->realnames = $this->loadModel('user')->getPairs('noletter');
$this->view->storyOrder = $orderBy;
$this->view->orderBy = 'id_asc';
$this->view->executionID = $executionID;
$this->view->productID = $productID;
$this->view->productNames = $productNames;
$this->view->productNum = count($products);
$this->view->allPlans = $allPlans;
$this->view->browseType = $browseType;
$this->view->features = $this->execution->getExecutionFeatures($execution);
$this->view->kanbanGroup = $kanbanGroup;
$this->view->execution = $execution;
$this->view->groupBy = $groupBy;
$this->view->canBeChanged = $canBeChanged;
$this->view->userList = $userList;
$this->view->hiddenPlan = $hiddenPlan;
$this->display();
}
/**
* Execution kanban.
*
* @access public
* @return void
*/
public function executionKanban()
{
$this->loadModel('project');
$projects = $this->project->getPairsByProgram('', 'noclosed');
$executions = $this->execution->getStatData(0, 'all', 0, 0, false, 'withchild', 'id_desc');
foreach($executions as $execution)
{
$execution->name = htmlspecialchars_decode($execution->name);
$execution->team = htmlspecialchars_decode($execution->team);
}
$teams = $this->dao->select('root,account')->from(TABLE_TEAM)
->where('root')->in($this->app->user->view->sprints)
->andWhere('type')->eq('execution')
->fetchGroup('root', 'account');
$projectCount = 0;
$statusCount = array();
$myExecutions = array();
$kanbanGroup = array();
foreach(array_keys($projects) as $projectID)
{
foreach(array_keys($this->lang->execution->statusList) as $status)
{
if(!isset($statusCount[$status])) $statusCount[$status] = 0;
foreach($executions as $execution)
{
if($execution->status == $status)
{
if(isset($teams[$execution->id][$this->app->user->account])) $myExecutions[$status][$execution->id] = $execution;
if($execution->project == $projectID) $kanbanGroup[$projectID][$status][$execution->id] = $execution;
}
}
$statusCount[$status] += isset($kanbanGroup[$projectID][$status]) ? count($kanbanGroup[$projectID][$status]) : 0;
/* Max 2 closed executions. */
if($status == 'closed')
{
if(isset($myExecutions[$status]) and count($myExecutions[$status]) > 2)
{
foreach($myExecutions[$status] as $executionID => $execution)
{
unset($myExecutions[$status][$executionID]);
$myExecutions[$status][$execution->closedDate] = $execution;
}
krsort($myExecutions[$status]);
$myExecutions[$status] = array_slice($myExecutions[$status], 0, 2, true);
}
if(isset($kanbanGroup[$projectID][$status]) and count($kanbanGroup[$projectID][$status]) > 2)
{
foreach($kanbanGroup[$projectID][$status] as $executionID => $execution)
{
unset($kanbanGroup[$projectID][$status][$executionID]);
$kanbanGroup[$projectID][$status][$execution->closedDate] = $execution;
}
krsort($kanbanGroup[$projectID][$status]);
$kanbanGroup[$projectID][$status] = array_slice($kanbanGroup[$projectID][$status], 0, 2);
}
}
}
if(empty($kanbanGroup[$projectID])) continue;
$projectCount++;
}
$this->view->title = $this->lang->execution->executionKanban;
$this->view->kanbanGroup = empty($myExecutions) ? $kanbanGroup : array($myExecutions) + $kanbanGroup;
$this->view->projects = $projects;
$this->view->projectCount = $projectCount;
$this->view->statusCount = $statusCount;
$this->display();
}
/**
* Set Kanban.
*
* @param int $executionID
* @access public
* @return void
*/
public function setKanban($executionID)
{
$execution = $this->execution->getByID($executionID);
if($_POST)
{
$this->execution->setKanban($executionID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
$this->view->title = $this->lang->execution->setKanban;
$this->view->execution = $execution;
$this->view->laneCount = $this->loadModel('kanban')->getLaneCount($executionID, $execution->type);
$this->view->heightType = $execution->displayCards > 2 ? 'custom' : 'auto';
$this->view->displayCards = $execution->displayCards ? $execution->displayCards : '';
$this->display();
}
/**
* Tree view.
* Product
*
* @param int $executionID
* @param string $type
* @access public
* @return void
*/
public function tree($executionID, $type = 'task')
{
$this->execution->setMenu($executionID);
$execution = $this->loadModel('execution')->getById($executionID);
$project = $this->loadModel('project')->getById($execution->project);
$tree = $this->execution->getTree($executionID);
/* Save to session. */
$uri = $this->app->getURI(true);
$this->app->session->set('taskList', $uri, 'execution');
$this->app->session->set('storyList', $uri, 'execution');
$this->app->session->set('executionList', $uri, 'execution');
$this->app->session->set('caseList', $uri, 'qa');
$this->app->session->set('bugList', $uri, 'qa');
if($type === 'json') return print(helper::jsonEncode4Parse($tree, JSON_HEX_QUOT | JSON_HEX_APOS));
if($execution->lifetime == 'ops') unset($this->lang->execution->treeLevel['story']);
$this->view->title = $this->lang->execution->tree;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$this->view->position[] = $this->lang->execution->tree;
$this->view->execution = $execution;
$this->view->executionID = $executionID;
$this->view->level = $type;
$this->view->tree = $this->execution->printTree($tree, $project->hasProduct);
$this->view->features = $this->execution->getExecutionFeatures($execution);
$this->display();
}
/**
* Print kanban.
*
* @param int $executionID
* @param string $orderBy
* @access public
* @return void
*/
public function printKanban($executionID, $orderBy = 'id_asc')
{
$this->view->title = $this->lang->execution->printKanban;
$contents = array('story', 'wait', 'doing', 'done', 'cancel');
if($_POST)
{
$stories = $this->loadModel('story')->getExecutionStories($executionID, 0, 0, $orderBy);
$storySpecs = $this->story->getStorySpecs(array_keys($stories));
$order = 1;
foreach($stories as $story) $story->order = $order++;
$kanbanTasks = $this->execution->getKanbanTasks($executionID, "id");
$kanbanBugs = $this->loadModel('bug')->getExecutionBugs($executionID);
$users = array();
$taskAndBugs = array();
foreach($kanbanTasks as $task)
{
$storyID = $task->storyID;
$status = $task->status;
$users[] = $task->assignedTo;
$taskAndBugs[$status]["task{$task->id}"] = $task;
}
foreach($kanbanBugs as $bug)
{
$storyID = $bug->story;
$status = $bug->status;
$status = $status == 'active' ? 'wait' : ($status == 'resolved' ? ($bug->resolution == 'postponed' ? 'cancel' : 'done') : $status);
$users[] = $bug->assignedTo;
$taskAndBugs[$status]["bug{$bug->id}"] = $bug;
}
$datas = array();
foreach($contents as $content)
{
if($content != 'story' and !isset($taskAndBugs[$content])) continue;
$datas[$content] = $content == 'story' ? $stories : $taskAndBugs[$content];
}
unset($this->lang->story->stageList['']);
unset($this->lang->story->stageList['wait']);
unset($this->lang->story->stageList['planned']);
unset($this->lang->story->stageList['projected']);
unset($this->lang->story->stageList['released']);
unset($this->lang->task->statusList['']);
unset($this->lang->task->statusList['wait']);
unset($this->lang->task->statusList['closed']);
unset($this->lang->bug->statusList['']);
unset($this->lang->bug->statusList['closed']);
$originalDatas = $datas;
if($this->post->content == 'increment')
{
$prevKanbans = $this->execution->getPrevKanban($executionID);
foreach($datas as $type => $data)
{
if(isset($prevKanbans[$type]))
{
$prevData = $prevKanbans[$type];
foreach($prevData as $id)
{
if(isset($data[$id])) unset($datas[$type][$id]);
}
}
}
}
/* Close the page when there is no data. */
$hasData = false;
foreach($datas as $data)
{
if(!empty($data)) $hasData = true;
}
if(!$hasData) return print(js::alert($this->lang->execution->noPrintData) . js::close());
$this->execution->saveKanbanData($executionID, $originalDatas);
$hasBurn = $this->post->content == 'all';
if($hasBurn)
{
/* Get date list. */
$executionInfo = $this->execution->getByID($executionID);
list($dateList) = $this->execution->getDateList($executionInfo->begin, $executionInfo->end, 'noweekend');
$chartData = $this->execution->buildBurnData($executionID, $dateList, 'noweekend');
}
$this->view->hasBurn = $hasBurn;
$this->view->datas = $datas;
$this->view->chartData = isset($chartData) ? $chartData : array();
$this->view->storySpecs = $storySpecs;
$this->view->realnames = $this->loadModel('user')->getRealNameAndEmails($users);
$this->view->executionID = $executionID;
return $this->display();
}
$this->execution->setMenu($executionID);
$execution = $this->execution->getById($executionID);
$this->view->executionID = $executionID;
$this->display();
}
/**
* Story kanban.
*
* @param int $executionID
* @access public
* @return void
*/
public function storyKanban($executionID)
{
/* Compatibility IE8*/
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7");
$this->execution->setMenu($executionID);
$execution = $this->loadModel('execution')->getById($executionID);
$stories = $this->loadModel('story')->getExecutionStories($executionID);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false);
/* Get execution's product. */
$productID = 0;
$productPairs = $this->loadModel('product')->getProductPairsByProject($executionID);
if($productPairs) $productID = key($productPairs);
$this->app->session->set('executionStoryList', $this->app->getURI(true), 'execution');
$this->view->title = $this->lang->execution->storyKanban;
$this->view->position[] = html::a($this->createLink('execution', 'story', "executionID=$executionID"), $execution->name);
$this->view->position[] = $this->lang->execution->storyKanban;
$this->view->stories = $this->story->getKanbanGroupData($stories);
$this->view->realnames = $this->loadModel('user')->getPairs('noletter');
$this->view->executionID = $executionID;
$this->view->execution = $execution;
$this->view->productID = $productID;
$this->view->canBeChanged = common::canModify('execution', $execution); // Determines whether an object is editable.
$this->display();
}
/**
* Delete a execution.
*
* @param int $executionID
* @param string $confirm yes|no
* @access public
* @return void
*/
public function delete($executionID, $confirm = 'no')
{
if($confirm == 'no')
{
/* Get the number of unfinished tasks and unresolved bugs. */
$unfinishedTasks = $this->dao->select('COUNT(id) AS count')->from(TABLE_TASK)
->where('execution')->eq($executionID)
->andWhere('deleted')->eq(0)
->andWhere('status')->in('wait,doing,pause')
->fetch();
$unresolvedBugs = $this->dao->select('COUNT(id) AS count')->from(TABLE_BUG)
->where('execution')->eq($executionID)
->andWhere('deleted')->eq(0)
->andWhere('status')->eq('active')
->fetch();
/* Set prompt information. */
$tips = '';
if($unfinishedTasks->count) $tips = sprintf($this->lang->execution->unfinishedTask, $unfinishedTasks->count);
if($unresolvedBugs->count) $tips .= sprintf($this->lang->execution->unresolvedBug, $unresolvedBugs->count);
if($tips) $tips = $this->lang->execution->unfinishedExecution . $tips;
$type = $this->dao->select('type')->from(TABLE_EXECUTION)->where('id')->eq($executionID)->fetch('type');
if($type == 'stage')
{
if($tips) $tips = str_replace($this->lang->executionCommon, $this->lang->project->stage, $tips);
$this->lang->execution->confirmDelete = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->execution->confirmDelete);
}
elseif($type == 'kanban')
{
global $lang;
$lang->executionCommon = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
}
return print(js::confirm($tips . sprintf($this->lang->execution->confirmDelete, $this->executions[$executionID]), $this->createLink('execution', 'delete', "executionID=$executionID&confirm=yes")));
}
else
{
/* Delete execution. */
$execution = $this->execution->getByID($executionID);
$this->dao->update(TABLE_EXECUTION)->set('deleted')->eq(1)->where('id')->eq($executionID)->exec();
$this->loadModel('action')->create('execution', $executionID, 'deleted', '', ACTIONMODEL::CAN_UNDELETED);
$this->execution->updateUserView($executionID);
$this->loadModel('common')->syncPPEStatus($executionID);
$project = $this->loadModel('project')->getById($execution->project);
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID);
$this->session->set('execution', '');
$message = $this->executeHooks($executionID);
if($message) $this->lang->saveSuccess = $message;
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
return print(js::reload('parent'));
}
}
/**
* Manage products.
*
* @param int $executionID
* @param from $from
* @access public
* @return void
*/
public function manageProducts($executionID, $from = '')
{
/* use first execution if executionID does not exist. */
if(!isset($this->executions[$executionID])) $executionID = key($this->executions);
$browseExecutionLink = $this->createLink('execution', 'browse', "executionID=$executionID");
$this->loadModel('product');
$execution = $this->execution->getById($executionID);
$project = $this->loadModel('project')->getByID($execution->project);
if(!$project->hasProduct) return print(js::error($this->lang->project->cannotManageProducts) . js::locate('back'));
if($project->model == 'waterfall' or $project->model == 'waterfallplus') return print(js::error(sprintf($this->lang->execution->cannotManageProducts, zget($this->lang->project->modelList, $project->model))) . js::locate('back'));
if(!empty($_POST))
{
$oldProducts = $this->product->getProducts($executionID);
if($from == 'buildCreate' && $this->session->buildCreate) $browseExecutionLink = $this->session->buildCreate;
$this->execution->updateProducts($executionID);
if(dao::isError()) return print(js::error(dao::getError()));
$oldProducts = array_keys($oldProducts);
$newProducts = $this->product->getProducts($executionID);
$newProducts = array_keys($newProducts);
$diffProducts = array_merge(array_diff($oldProducts, $newProducts), array_diff($newProducts, $oldProducts));
if($diffProducts) $this->loadModel('action')->create($this->objectType, $executionID, 'Managed', '', !empty($_POST['products']) ? join(',', $_POST['products']) : '');
if(isonlybody())
{
unset($_GET['onlybody']);
return print(js::locate($this->createLink('build', 'create', "executionID=$executionID&productID=0&projectID=$execution->project"), 'parent'));
}
}
/* Set menu. */
$this->execution->setMenu($execution->id);
/* Title and position. */
$title = $this->lang->execution->manageProducts . $this->lang->colon . $execution->name;
$position[] = html::a($browseExecutionLink, $execution->name);
$position[] = $this->lang->execution->manageProducts;
$branches = $this->project->getBranchesByProject($executionID);
$linkedProductIdList = empty($branches) ? '' : array_keys($branches);
$allProducts = $this->product->getProductPairsByProject($execution->project, 'all', $linkedProductIdList);
$linkedProducts = $this->product->getProducts($execution->id, 'all', '', true, $linkedProductIdList);
$linkedBranches = array();
$executionStories = $this->project->getStoriesByProject($executionID);
/* If the story of the product which linked the execution, you don't allow to remove the product. */
$unmodifiableProducts = array();
$unmodifiableBranches = array();
$linkedStoryIDList = array();
$linkedBranchIdList = array();
foreach($linkedProducts as $productID => $linkedProduct)
{
$linkedBranches[$productID] = array();
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->name;
foreach($branches[$productID] as $branchID => $branch)
{
$linkedBranches[$productID][$branchID] = $branchID;
$linkedBranchIdList[$branchID] = $branchID;
if(!empty($executionStories[$productID][$branchID]))
{
array_push($unmodifiableProducts, $productID);
array_push($unmodifiableBranches, $branchID);
$linkedStoryIDList[$productID][$branchID] = $executionStories[$productID][$branchID]->storyIDList;
}
}
}
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->allProducts = $allProducts;
$this->view->execution = $execution;
$this->view->linkedProducts = $linkedProducts;
$this->view->unmodifiableProducts = $unmodifiableProducts;
$this->view->unmodifiableBranches = $unmodifiableBranches;
$this->view->linkedBranches = $linkedBranches;
$this->view->linkedStoryIDList = $linkedStoryIDList;
$this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($allProducts), $execution->project, 'ignoreNormal|noclosed', $linkedBranchIdList);
$this->view->allBranches = $this->execution->getBranchByProduct(array_keys($allProducts), $execution->project, 'ignoreNormal');
$this->display();
}
/**
* Manage members of the execution.
*
* @param int $executionID
* @param int $team2Import the team to import.
* @param int $dept
* @access public
* @return void
*/
public function manageMembers($executionID = 0, $team2Import = 0, $dept = 0)
{
if(!empty($_POST))
{
$this->execution->manageMembers($executionID);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action')->create('team', $executionID, 'managedTeam');
$link = $this->session->teamList ? $this->session->teamList : $this->createLink('execution', 'team', "executionID=$executionID");
return $this->send(array('message' => $this->lang->saveSuccess, 'result' => 'success', 'locate' => $link));
}
/* Load model. */
$this->loadModel('user');
$this->loadModel('dept');
$execution = $this->execution->getById($executionID);
$users = $this->user->getPairs('noclosed|nodeleted|devfirst');
$roles = $this->user->getUserRoles(array_keys($users));
$deptUsers = empty($dept) ? array() : $this->dept->getDeptUserPairs($dept);
$currentMembers = $this->execution->getTeamMembers($executionID);
$members2Import = $this->execution->getMembers2Import($team2Import, array_keys($currentMembers));
$teams2Import = $this->loadModel('personnel')->getCopiedObjects($executionID, 'sprint', true);
$teams2Import = array('' => '') + $teams2Import;
/* Append users for get users. */
$appendUsers = array();
foreach($currentMembers as $member) $appendUsers[$member->account] = $member->account;
foreach($members2Import as $member) $appendUsers[$member->account] = $member->account;
foreach($deptUsers as $deptAccount => $userName) $appendUsers[$deptAccount] = $deptAccount;
$users = $this->user->getPairs('noclosed|nodeleted|devfirst', $appendUsers);
$roles = $this->user->getUserRoles(array_keys($users));
/* Set menu. */
$this->execution->setMenu($execution->id);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["accounts[]"] = $this->config->user->moreLink;
if($execution->type == 'kanban') $this->lang->execution->copyTeamTitle = str_replace($this->lang->execution->common, $this->lang->execution->kanban, $this->lang->execution->copyTeamTitle);
$title = $this->lang->execution->manageMembers . $this->lang->colon . $execution->name;
$position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$position[] = $this->lang->execution->manageMembers;
$this->view->title = $title;
$this->view->position = $position;
$this->view->execution = $execution;
$this->view->users = $users;
$this->view->deptUsers = $deptUsers;
$this->view->roles = $roles;
$this->view->dept = $dept;
$this->view->depts = array('' => '') + $this->loadModel('dept')->getOptionMenu();
$this->view->currentMembers = $currentMembers;
$this->view->members2Import = $members2Import;
$this->view->teams2Import = $teams2Import;
$this->view->team2Import = $team2Import;
$this->display();
}
/**
* Unlink a memeber.
*
* @param int $executionID
* @param int $userID
* @param string $confirm yes|no
* @access public
* @return void
*/
public function unlinkMember($executionID, $userID, $confirm = 'no')
{
if($confirm == 'no') return print(js::confirm($this->lang->execution->confirmUnlinkMember, $this->inlink('unlinkMember', "executionID=$executionID&userID=$userID&confirm=yes")));
$user = $this->loadModel('user')->getById($userID, 'id');
$account = $user->account;
$this->execution->unlinkMember($executionID, $account);
if(!dao::isError()) $this->loadModel('action')->create('team', $executionID, 'managedTeam');
/* if ajax request, send result. */
if($this->server->ajax)
{
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
}
else
{
$response['result'] = 'success';
$response['message'] = '';
}
return $this->send($response);
}
return print(js::locate($this->inlink('team', "executionID=$executionID"), 'parent'));
}
/**
* Link stories to an execution.
*
* @param int $objectID
* @param string $browseType
* @param int $param
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @param string $extra
* @access public
* @return void
*/
public function linkStory($objectID = 0, $browseType = '', $param = 0, $recTotal = 0, $recPerPage = 50, $pageID = 1, $extra = '')
{
$this->loadModel('story');
$this->loadModel('product');
$this->loadModel('tree');
$this->loadModel('branch');
/* Init objectID */
$originObjectID = $objectID;
/* Transfer object id when version lite */
if($this->config->vision == 'lite')
{
$kanban = $this->project->getByID($objectID, 'kanban');
$objectID = $kanban->project;
}
/* Get projects, executions and products. */
$object = $this->project->getByID($objectID, 'project,sprint,stage,kanban');
$products = $this->product->getProducts($objectID);
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
$browseLink = $this->session->executionStoryList;
if($this->app->tab == 'project' and $object->multiple) $browseLink = $this->createLink('projectstory', 'story', "objectID=$objectID");
if($object->type == 'kanban' && !$object->hasProduct) $this->lang->productCommon = $this->lang->project->common;
$this->session->set('storyList', $this->app->getURI(true), $this->app->tab); // Save session.
/* Only execution can have no products. */
if(empty($products))
{
echo js::alert($this->lang->execution->errorNoLinkedProducts);
return print(js::locate($this->createLink('execution', 'manageproducts', "executionID=$objectID")));
}
if(!empty($_POST))
{
if($object->type != 'project' and $object->project != 0) $this->execution->linkStory($object->project);
$this->execution->linkStory($objectID, array(), array(), $extra);
if(isonlybody())
{
if($this->app->tab == 'execution')
{
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
if($object->type == 'kanban')
{
$kanbanData = $this->loadModel('kanban')->getRDKanban($objectID, $execLaneType, 'id_desc', 0, $execGroupBy);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent', '', "parent.updateKanban($kanbanData)"));
}
else
{
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($objectID, $execLaneType, $execGroupBy);
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent', '', "parent.updateKanban(\"story\", $kanbanData)"));
}
}
else
{
return print(js::reload('parent'));
}
}
return print(js::locate($browseLink));
}
if($object->type == 'project')
{
$this->project->setMenu($object->id);
}
elseif($object->type == 'sprint' or $object->type == 'stage' or $object->type == 'kanban')
{
$this->execution->setMenu($object->id);
}
/* Set modules and branches. */
$modules = array();
$branchIDList = array(BRANCH_MAIN);
$branches = $this->project->getBranchesByProject($objectID);
$productType = 'normal';
if(defined('TUTORIAL'))
{
$modules = $this->loadModel('tutorial')->getModulePairs();
}
else
{
foreach($products as $product)
{
$productModules = $this->tree->getOptionMenu($product->id, 'story', 0, array_keys($branches[$product->id]));
foreach($productModules as $branch => $branchModules)
{
foreach($branchModules as $moduleID => $moduleName) $modules[$moduleID] = ((count($products) >= 2 and $moduleID != 0) ? $product->name : '') . $moduleName;
}
if($product->type != 'normal')
{
$productType = $product->type;
if(isset($branches[$product->id]))
{
foreach($branches[$product->id] as $branchID => $branch) $branchIDList[$branchID] = $branchID;
}
}
}
}
/* Build the search form. */
$actionURL = $this->createLink($this->app->rawModule, 'linkStory', "objectID=$objectID&browseType=bySearch&queryID=myQueryID");
$branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products));
$this->execution->buildStorySearchForm($products, $branchGroups, $modules, $queryID, $actionURL, 'linkStory', $object);
if($browseType == 'bySearch')
{
$allStories = $this->story->getBySearch('', '', $queryID, 'id', $objectID);
}
else
{
$allStories = $this->story->getProductStories(array_keys($products), $branchIDList, $moduleID = '0', $status = 'active', 'story', 'id_desc', $hasParent = false, '', $pager = null);
}
$linkedStories = $this->story->getExecutionStoryPairs($objectID);
foreach($allStories as $id => $story)
{
if(isset($linkedStories[$story->id])) unset($allStories[$id]);
if($story->parent < 0) unset($allStories[$id]);
if(!isset($modules[$story->module]))
{
$storyModule = $this->tree->getModulesName($story->module);
$productName = count($products) > 1 ? $products[$story->product]->name : '';
$modules[$story->module] = $productName . zget($storyModule, $story->module, '');
}
}
/* Pager. */
$this->app->loadClass('pager', $static = true);
$recTotal = count($allStories);
$pager = new pager($recTotal, $recPerPage, $pageID);
$allStories = array_chunk($allStories, $pager->recPerPage);
$project = $object;
if(strpos('sprint,stage,kanban', $object->type) !== false) $project = $this->loadModel('project')->getByID($object->project);
/* Assign. */
$this->view->title = $object->name . $this->lang->colon . $this->lang->execution->linkStory;
$this->view->position[] = html::a($browseLink, $object->name);
$this->view->position[] = $this->lang->execution->linkStory;
$this->view->objectID = $originObjectID;
$this->view->object = $object;
$this->view->products = $products;
$this->view->allStories = empty($allStories) ? $allStories : $allStories[$pageID - 1];
$this->view->pager = $pager;
$this->view->browseType = $browseType;
$this->view->productType = $productType;
$this->view->modules = $modules;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->branchGroups = $branchGroups;
$this->view->browseLink = $browseLink;
$this->view->project = $project;
$this->display();
}
/**
* Unlink a story.
*
* @param int $executionID
* @param int $storyID
* @param string $confirm yes|no
* @param string $from taskkanban
* @param int $laneID
* @param int $columnID
* @access public
* @return void
*/
public function unlinkStory($executionID, $storyID, $confirm = 'no', $from = '', $laneID = 0, $columnID = 0)
{
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&from=$from&laneID=$laneID&columnID=$columnID")));
}
else
{
$execution = $this->execution->getByID($executionID);
$this->execution->unlinkStory($executionID, $storyID, $laneID, $columnID);
if($execution->type == 'kanban')
{
/* Fix bug #29171. */
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('parent')->eq($execution->parent)->fetchAll('id');
$executionStories = $this->dao->select('project,story')->from(TABLE_PROJECTSTORY)->where('story')->eq($storyID)->andWhere('project')->in(array_keys($executions))->fetchAll();
if(empty($executionStories)) $this->execution->unlinkStory($execution->parent, $storyID, $laneID, $columnID);
}
/* if kanban then reload and if ajax request then send result. */
if(helper::isAjaxRequest())
{
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
}
else
{
$response['result'] = 'success';
$response['message'] = '';
}
return $this->send($response);
}
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
if($this->app->tab == 'execution' and $execution->type == 'kanban')
{
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$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')
{
$taskSearchValue = $this->session->taskSearchValue ? $this->session->taskSearchValue : '';
$kanbanData = $this->loadModel('kanban')->getExecutionKanban($executionID, $execLaneType, $execGroupBy, $taskSearchValue);
$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'));
}
}
/**
* Batch unlink story.
*
* @param int $executionID
* @access public
* @return void
*/
public function batchUnlinkStory($executionID)
{
if(isset($_POST['storyIdList']))
{
$storyIdList = $this->post->storyIdList;
$_POST = array();
$this->loadModel('gitlab');
foreach($storyIdList as $storyID)
{
/* Delete related issue in gitlab. */
$relation = $this->gitlab->getRelationByObject('story', $storyID);
if(!empty($relation)) $this->gitlab->deleteIssue('story', $storyID, $relation->issueID);
$this->execution->unlinkStory($executionID, $storyID);
}
}
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchOther');
return print(js::locate($this->createLink('execution', 'story', "executionID=$executionID")));
}
/**
* Execution dynamic.
*
* @param int $executionID
* @param string $type
* @param string $param
* @param int $recTotal
* @param string $date
* @param string $direction next|pre
* @access public
* @return void
*/
public function dynamic($executionID = 0, $type = 'today', $param = '', $recTotal = 0, $date = '', $direction = 'next')
{
/* Save session. */
$uri = $this->app->getURI(true);
$this->session->set('productList', $uri, 'product');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('releaseList', $uri, 'product');
$this->session->set('storyList', $uri, 'product');
$this->session->set('executionList', $uri, 'execution');
$this->session->set('taskList', $uri, 'execution');
$this->session->set('buildList', $uri, 'execution');
$this->session->set('bugList', $uri, 'qa');
$this->session->set('caseList', $uri, 'qa');
$this->session->set('testtaskList', $uri, 'qa');
$this->session->set('reportList', $uri, 'qa');
/* use first execution if executionID does not exist. */
if(!isset($this->executions[$executionID])) $executionID = key($this->executions);
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
/* Set the menu. If the executionID = 0, use the indexMenu instead. */
$this->execution->setMenu($executionID);
/* Set the pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage = 50, $pageID = 1);
/* Set the user and type. */
$account = 'all';
if($type == 'account')
{
$user = $this->loadModel('user')->getById((int)$param, 'id');
if($user) $account = $user->account;
}
$period = $type == 'account' ? 'all' : $type;
$date = empty($date) ? '' : date('Y-m-d', $date);
$actions = $this->loadModel('action')->getDynamic($account, $period, $orderBy, $pager, 'all', 'all', $executionID, $date, $direction);
/* The header and position. */
$execution = $this->execution->getByID($executionID);
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->dynamic;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
$this->view->position[] = $this->lang->execution->dynamic;
$this->view->userIdPairs = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted|useid');
$this->view->accountPairs = $this->loadModel('user')->getPairs('noletter|nodeleted');
/* Assign. */
$this->view->executionID = $executionID;
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->view->account = $account;
$this->view->param = $param;
$this->view->dateGroups = $this->action->buildDateGroup($actions, $direction, $type);
$this->view->direction = $direction;
$this->display();
}
/**
* AJAX: get products of a execution in html select.
*
* @param int $executionID
* @access public
* @return void
*/
public function ajaxGetProducts($executionID)
{
$products = $this->loadModel('product')->getProducts($executionID, 'all', '', false);
return print(html::select('product', $products, '', 'class="form-control"'));
}
/**
* AJAX: get team members of the execution.
*
* @param int $executionID
* @param string $assignedTo
* @access public
* @return void
*/
public function ajaxGetMembers($executionID, $assignedTo = '')
{
$users = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution');
if($this->app->getViewType() === 'json')
{
return print(json_encode($users));
}
else
{
$assignedTo = isset($users[$assignedTo]) ? $assignedTo : '';
return print(html::select('assignedTo', $users, $assignedTo, "class='form-control'"));
}
}
/**
* AJAX: get team members by projectID/executionID.
*
* @param int $objectID
* @access public
* @return string
*/
public function ajaxGetTeamMembers($objectID)
{
$type = $this->dao->findById($objectID)->from(TABLE_PROJECT)->fetch('type');
if($type != 'project') $type = 'execution';
$users = $this->loadModel('user')->getPairs('nodeleted|noclosed');
$members = $this->user->getTeamMemberPairs($objectID, $type);
return print(html::select('teamMembers[]', $users, array_keys($members), "class='form-control picker-select' multiple"));
}
/**
* When create a execution, help the user.
*
* @param int $executionID
* @access public
* @return void
*/
public function tips($executionID)
{
$execution = $this->execution->getById($executionID);
$projectID = $execution->project;
$this->view->execution = $execution;
$this->view->executionID = $executionID;
$this->view->projectID = $projectID;
$this->display('execution', 'tips');
}
/**
* Drop menu page.
*
* @param int $executionID
* @param string $module
* @param string $method
* @param mix $extra
* @access public
* @return void
*/
public function ajaxGetDropMenu($executionID, $module, $method, $extra)
{
$orderedExecutions = array();
$projects = $this->loadModel('program')->getProjectList(0, 'all', 0, 'order_asc', null, 0, 0, true);
$executionGroups = $this->dao->select('*')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->andWhere('multiple')->eq('1')
->andWhere('type')->in('sprint,stage,kanban')
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
->andWhere('project')->in(array_keys($projects))
->orderBy('order_asc')
->fetchGroup('project', 'id');
$teams = $this->dao->select('root,account')->from(TABLE_TEAM)
->where('root')->in($this->app->user->view->sprints)
->andWhere('type')->eq('execution')
->fetchGroup('root', 'account');
$projectPairs = array();
foreach($projects as $project)
{
$executions = zget($executionGroups, $project->id, array());
if(isset($project->model) and $project->model == 'waterfall') ksort($executions);
$parents = array();
$firstGradeExecs = array();
foreach($executions as $execution)
{
$parents[$execution->parent] = $execution->parent;
if($execution->grade == 1) $firstGradeExecs[$execution->id] = $execution->id;
}
$executions = $this->execution->resetExecutionSorts($executions, $firstGradeExecs);
foreach($executions as $execution)
{
/* Only show leaf executions. */
if(isset($parents[$execution->id])) continue;
$execution->teams = zget($teams, $execution->id, array());
$orderedExecutions[$execution->id] = $execution;
}
$projectPairs[$project->id] = $project->name;
}
$nameList = $this->execution->getFullNameList($orderedExecutions);
$projectExecutions = array();
foreach($orderedExecutions as $execution)
{
$execution->name = $nameList[$execution->id];
$projectExecutions[$execution->project][] = $execution;
}
$this->view->link = $this->execution->getLink($module, $method, $extra);
$this->view->module = $module;
$this->view->method = $method;
$this->view->executionID = $executionID;
$this->view->extra = $extra;
$this->view->projects = $projectPairs;
$this->view->projectExecutions = $projectExecutions;
$this->display();
}
/**
* Update order.
*
* @access public
* @return void
*/
public function updateOrder()
{
$idList = explode(',', trim($this->post->executions, ','));
$orderBy = $this->post->orderBy;
if(strpos($orderBy, 'order') === false) return false;
$executions = $this->dao->select('id,`order`')->from(TABLE_EXECUTION)->where('id')->in($idList)->orderBy($orderBy)->fetchPairs('order', 'id');
foreach($executions as $order => $id)
{
$newID = array_shift($idList);
if($id == $newID) continue;
$this->dao->update(TABLE_EXECUTION)->set('`order`')->eq($order)->where('id')->eq($newID)->exec();
}
}
/**
* Story sort.
*
* @param int $executionID
* @access public
* @return void
*/
public function storySort($executionID)
{
$idList = explode(',', trim($this->post->storys, ','));
$orderBy = $this->post->orderBy;
$order = $this->dao->select('*')->from(TABLE_PROJECTSTORY)->where('story')->in($idList)->andWhere('project')->eq($executionID)->orderBy('order_asc')->fetch('order');
if(strpos($orderBy, 'order_desc') !== false) $idList = array_reverse($idList);
foreach($idList as $storyID)
{
$this->dao->update(TABLE_PROJECTSTORY)->set('`order`')->eq($order)->where('story')->eq($storyID)->andWhere('project')->eq($executionID)->exec();
$order++;
}
}
/**
* Story estimate.
*
* @param int $executionID
* @param int $storyID
* @param int $round
* @access public
* @return void
*/
public function storyEstimate($executionID, $storyID, $round = 0)
{
$this->loadModel('story');
if($_POST)
{
$this->story->saveEstimateInfo($storyID);
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
return $this->send($response);
}
$this->loadModel('action')->create('story', $storyID, 'estimated', '', $executionID);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('execution', 'storyEstimate', "executionID=$executionID&storyID=$storyID")));
}
$estimateInfo = $this->story->getEstimateInfo($storyID, $round);
$team = $this->execution->getTeamMembers($executionID);
/* If user has been removed from team and has estimate record, join him into team.*/
if(!empty($estimateInfo->estimate))
{
foreach($estimateInfo->estimate as $account => $estimate)
{
if(!in_array($account, array_keys($team)))
{
$team[$account] = new stdclass();
$team[$account]->account = $account;
}
}
}
$this->view->estimateInfo = $estimateInfo;
$this->view->round = !empty($estimateInfo->round) ? $estimateInfo->round : 0;
$this->view->rounds = $this->story->getEstimateRounds($storyID);
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->team = $team;
$this->view->executionID = $executionID;
$this->view->storyID = $storyID;
$this->view->story = $this->story->getById($storyID);
$this->display();
}
/**
* All execution.
*
* @param string $status
* @param string $orderBy
* @param int $productID
* @param string $param
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function all($status = 'undone', $orderBy = 'order_asc', $productID = 0, $param = '', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
$this->app->loadLang('my');
$this->app->loadLang('product');
$this->app->loadLang('stage');
$this->app->loadLang('programplan');
$this->loadModel('datatable');
$from = $this->app->tab;
if($from == 'execution') $this->session->set('executionList', $this->app->getURI(true), 'execution');
if($this->app->viewType == 'mhtml')
{
$executionID = $this->execution->saveState(0, $this->executions);
$this->execution->setMenu($executionID);
}
/* Load pager and get tasks. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$queryID = ($status == 'bySearch') ? (int)$param : 0;
$actionURL = $this->createLink('execution', 'all', "status=bySearch&orderBy=$orderBy&productID=$productID¶m=myQueryID");
$this->execution->buildSearchForm($queryID, $actionURL);
$this->view->title = $this->lang->execution->allExecutions;
$this->view->position[] = $this->lang->execution->allExecutions;
$executionStats = $this->execution->getStatData(0, $status, $productID, 0, false, $queryID, $orderBy, $pager);
$allExecutionsNum = $this->execution->getStatData(0, 'all');
$this->view->allExecutionsNum = count($allExecutionsNum);
$this->view->executionStats = $executionStats;
$this->view->productList = $this->loadModel('product')->getProductPairsByProject(0);
$this->view->productID = $productID;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->projects = array('') + $this->project->getPairsByProgram();
$this->view->status = $status;
$this->view->from = $from;
$this->view->param = $param;
$this->view->showBatchEdit = $this->cookie->showExecutionBatchEdit;
$this->display();
}
/**
* Get white list personnel.
*
* @param int $executionID
* @param string $module
* @param string $objectType
* @param string $orderby
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function whitelist($executionID = 0, $module='execution', $objectType = 'sprint', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* use first execution if executionID does not exist. */
if(!isset($this->executions[$executionID])) $executionID = key($this->executions);
/* Set the menu. If the executionID = 0, use the indexMenu instead. */
$this->execution->setMenu($executionID);
$execution = $this->execution->getByID($executionID);
if(!empty($execution->acl) and $execution->acl != 'private') $this->locate($this->createLink('execution', 'task', "executionID=$executionID"));
echo $this->fetch('personnel', 'whitelist', "objectID=$executionID&module=$module&browseType=$objectType&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
}
/**
* Adding users to the white list.
*
* @param int $executionID
* @param int $deptID
* @param int $copyID
* @access public
* @return void
*/
public function addWhitelist($executionID = 0, $deptID = 0, $copyID = 0)
{
/* use first execution if executionID does not exist. */
if(!isset($this->executions[$executionID])) $executionID = key($this->executions);
/* Set the menu. If the executionID = 0, use the indexMenu instead. */
$this->execution->setMenu($executionID);
$execution = $this->execution->getByID($executionID);
if(!empty($execution->acl) and $execution->acl != 'private') $this->locate($this->createLink('execution', 'task', "executionID=$executionID"));
echo $this->fetch('personnel', 'addWhitelist', "objectID=$executionID&dept=$deptID©ID=$copyID&objectType=sprint&module=execution");
}
/*
* Removing users from the white list.
*
* @param int $id
* @param string $confirm
* @access public
* @return void
*/
public function unbindWhitelist($id = 0, $confirm = 'no')
{
echo $this->fetch('personnel', 'unbindWhitelist', "id=$id&confirm=$confirm");
}
/**
* Export execution.
*
* @param string $status
* @param int $productID
* @param string $orderBy
* @param string $from
* @access public
* @return void
*/
public function export($status, $productID, $orderBy, $from)
{
if($_POST)
{
$executionLang = $this->lang->execution;
$executionConfig = $this->config->execution;
$projectID = $from == 'project' ? $this->session->project : 0;
$project = $this->project->getByID($projectID);
if($projectID) $this->project->setMenu($projectID);
/* Create field lists. */
$fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $executionConfig->list->exportFields);
foreach($fields as $key => $fieldName)
{
if($fieldName == 'name' and $this->app->tab == 'project' and ($project->model == 'agileplus' or $project->model == 'waterfallplus')) $fields['method'] = $executionLang->method;
$fieldName = trim($fieldName);
$fields[$fieldName] = zget($executionLang, $fieldName);
unset($fields[$key]);
}
$executionStats = $this->execution->getStatData($projectID, $status == 'byproduct' ? 'all' : $status, $productID, 0, false, 'hasParentName', 'order_asc');
$users = $this->loadModel('user')->getPairs('noletter');
foreach($executionStats as $i => $execution)
{
$execution->PM = zget($users, $execution->PM);
$execution->status = isset($execution->delay) ? $executionLang->delayed : $this->processStatus('execution', $execution);
$execution->totalEstimate = $execution->hours->totalEstimate;
$execution->totalConsumed = $execution->hours->totalConsumed;
$execution->totalLeft = $execution->hours->totalLeft;
$execution->progress = $execution->hours->progress . '%';
$execution->name = isset($execution->title) ? $execution->title : $execution->name;
if($this->app->tab == 'project' and ($project->model == 'agileplus' or $project->model == 'waterfallplus')) $execution->method = zget($executionLang->typeList, $execution->type);
if($this->post->exportType == 'selected')
{
$checkedItem = $this->cookie->checkedItem;
if(strpos(",$checkedItem,", ",{$execution->id},") === false) unset($executionStats[$i]);
}
}
if($this->config->edition != 'open') list($fields, $executionStats) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $executionStats);
$this->post->set('fields', $fields);
$this->post->set('rows', $executionStats);
$this->post->set('kind', $this->lang->execution->common);
$this->fetch('file', 'export2' . $this->post->fileType, $_POST);
}
$this->loadModel('project');
$project = $this->project->getByID($this->session->project);
if(!empty($project->model) and $project->model == 'waterfall') $this->lang->executionCommon = $this->lang->project->stage;
$this->view->fileName = (in_array($status, array('all', 'undone')) ? $this->lang->execution->$status : $this->lang->execution->statusList[$status]) . $this->lang->execution->common;
$this->display();
}
/**
* Doc for compatible.
*
* @param int $executionID
* @access public
* @return void
*/
public function doc($executionID)
{
$this->locate($this->createLink('doc', 'objectLibs', "type=execution&objectID=$executionID&from=execution"));
}
/**
* Kanban setting.
*
* @param int $executionID
* @access public
* @return void
*/
public function ajaxKanbanSetting($executionID)
{
if($_POST)
{
$this->loadModel('setting');
$data = fixer::input('post')->get();
if(common::hasPriv('execution', 'kanbanHideCols'))
{
$allCols = $data->allCols;
$this->setting->setItem("system.execution.kanbanSetting.allCols", $allCols);
}
if(common::hasPriv('execution', 'kanbanColsColor')) $this->setting->setItem("system.execution.kanbanSetting.colorList", json_encode($data->colorList));
return print(js::reload('parent.parent'));
}
$this->app->loadLang('task');
$this->view->setting = $this->execution->getKanbanSetting();
$this->view->executionID = $executionID;
$this->display();
}
/**
* Ajax reset kanban setting
*
* @param int $executionID
* @param string $confirm
* @access public
* @return void
*/
public function ajaxResetKanban($executionID, $confirm = 'no')
{
if($confirm != 'yes') return print(js::confirm($this->lang->kanbanSetting->noticeReset, inlink('ajaxResetKanban', "executionID=$executionID&confirm=yes")));
$this->loadModel('setting');
if(common::hasPriv('execution', 'kanbanHideCols') and isset($this->config->execution->kanbanSetting->allCols))
{
$allCols = json_decode($this->config->execution->kanbanSetting->allCols, true);
unset($allCols[$executionID]);
$this->setting->setItem("system.execution.kanbanSetting.allCols", json_encode($allCols));
}
$account = $this->app->user->account;
$this->setting->deleteItems("owner={$account}&module=execution§ion=kanbanSetting&key=showOption");
if(common::hasPriv('execution', 'kanbanColsColor')) $this->setting->deleteItems("owner=system&module=execution§ion=kanbanSetting&key=colorList");
return print(js::reload('parent.parent'));
}
/**
* Import stories by plan.
*
* @param int $executionID
* @param int $planID
* @param int $productID
* @param string $fromMethod
* @param string $extra
* @param string $param
* @access public
* @return void
*/
public function importPlanStories($executionID, $planID, $productID = 0, $fromMethod = 'story', $extra = '', $param = '')
{
$planStories = $planProducts = array();
$planStory = $this->loadModel('story')->getPlanStories($planID);
$execution = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch();
$count = 0;
if(!empty($planStory))
{
$projectProducts = $this->loadModel('project')->getBranchesByProject($executionID);
foreach($planStory as $id => $story)
{
$projectBranches = zget($projectProducts, $story->product, array());
if($story->status != 'active' or (!empty($story->branch) and !empty($projectBranches) and !isset($projectBranches[$story->branch])))
{
$count++;
unset($planStory[$id]);
continue;
}
$planProducts[$story->id] = $story->product;
}
$projectID = $this->dao->findByID($executionID)->from(TABLE_EXECUTION)->fetch('project');
$planStories = array_keys($planStory);
if($executionID != $projectID) $this->execution->linkStory($projectID, $planStories, $planProducts);
$this->execution->linkStory($executionID, $planStories, $planProducts, $extra);
}
$moduleName = 'execution';
if(empty($param)) $param = "executionID=$executionID";
if(!empty($param)) $param = str_replace(array(',', ' '), array('&', ''), $param);
if($execution->type == 'project')
{
$moduleName = 'projectstory';
$param = "projectID=$executionID&productID=$productID";
}
if($execution->type == 'kanban')
{
global $lang;
$lang->executionCommon = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
}
$multiBranchProduct = false;
if($productID)
{
$product = $this->loadModel('product')->getByID($productID);
if($product->type != 'normal') $multiBranchProduct = true;
}
else
{
$executionProductList = $this->loadModel('product')->getProducts($executionID);
foreach($executionProductList as $executionProduct)
{
if($executionProduct->type != 'normal')
{
$multiBranchProduct = true;
break;
}
}
}
$importPlanStoryTips = $multiBranchProduct ? $this->lang->execution->haveBranchDraft : $this->lang->execution->haveDraft;
$haveDraft = sprintf($importPlanStoryTips, $count);
if(!$execution->multiple or $moduleName == 'projectstory') $haveDraft = str_replace($this->lang->executionCommon, $this->lang->projectCommon, $haveDraft);
if($count != 0) echo js::alert($haveDraft) . js::locate($this->createLink($moduleName, $fromMethod, $param));
return print(js::locate(helper::createLink($moduleName, $fromMethod, $param)));
}
/**
* Story info for tree list.
*
* @param int $storyID
* @param int $version
*
* @access public
* @return void
*/
public function treeStory($storyID, $version = 0)
{
$this->loadModel('story');
$story = $this->story->getById($storyID, $version, true);
$story->files = $this->loadModel('file')->getByObject('story', $storyID);
$product = $this->dao->findById($story->product)->from(TABLE_PRODUCT)->fields('name, id, type, shadow')->fetch();
$plan = $this->dao->findById($story->plan)->from(TABLE_PRODUCTPLAN)->fetch('title');
$bugs = $this->dao->select('id,title')->from(TABLE_BUG)->where('story')->eq($storyID)->andWhere('deleted')->eq(0)->fetchAll();
$fromBug = $this->dao->select('id,title')->from(TABLE_BUG)->where('toStory')->eq($storyID)->fetch();
$cases = $this->dao->select('id,title')->from(TABLE_CASE)->where('story')->eq($storyID)->andWhere('deleted')->eq(0)->fetchAll();
$modulePath = $this->loadModel('tree')->getParents($story->module);
$users = $this->loadModel('user')->getPairs('noletter');
$this->view->product = $product;
$this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($product->id);
$this->view->plan = $plan;
$this->view->bugs = $bugs;
$this->view->fromBug = $fromBug;
$this->view->cases = $cases;
$this->view->story = $story;
$this->view->users = $users;
$this->view->executions = $this->loadModel('execution')->getPairs(0, 'all', 'nocode');
$this->view->actions = $this->loadModel('action')->getList('story', $storyID);
$this->view->modulePath = $modulePath;
$this->view->version = $version == 0 ? $story->version : $version;
$this->view->preAndNext = $this->loadModel('common')->getPreAndNextObject('story', $storyID);
$this->display();
}
/**
* Task info for tree list.
*
* @param int $taskID
*
* @access public
* @return void
*/
public function treeTask($taskID)
{
$this->loadModel('task');
$task = $this->task->getById($taskID, true);
if($task->fromBug != 0)
{
$bug = $this->loadModel('bug')->getById($task->fromBug);
$task->bugSteps = '';
if($bug)
{
$task->bugSteps = $this->loadModel('file')->setImgSize($bug->steps);
foreach($bug->files as $file) $task->files[] = $file;
}
$this->view->fromBug = $bug;
}
else
{
$story = $this->loadModel('story')->getById($task->story);
$task->storySpec = empty($story) ? '' : $this->loadModel('file')->setImgSize($story->spec);
$task->storyVerify = empty($story) ? '' : $this->loadModel('file')->setImgSize($story->verify);
$task->storyFiles = $this->loadModel('file')->getByObject('story', $task->story);
}
if($task->team) $this->lang->task->assign = $this->lang->task->transfer;
/* Update action. */
if($task->assignedTo == $this->app->user->account) $this->loadModel('action')->read('task', $taskID);
$execution = $this->execution->getById($task->execution);
$this->view->task = $task;
$this->view->execution = $execution;
$this->view->actions = $this->loadModel('action')->getList('task', $taskID);
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->display();
}
/**
*Ajax get group menu of lanes.
*
* @param string $type all|syory|task|bug
* @param string $group
* @access public
* @return void
*/
public function ajaxGetGroup($type, $group = 'default')
{
$this->app->loadLang('kanban');
$groups = array();
$groups = $this->lang->kanban->group->$type;
return print(html::select("group", $groups, $group, 'class="form-control chosen" data-max_drop_width="215"'));
}
/**
* Ajax update kanban.
*
* @param int $executionID
* @param string $enterTime
* @param string $browseType
* @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 = '', $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');
if($from == 'execution') $this->session->set('taskSearchValue', $searchValue);
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->kanban->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue, $orderBy) : $this->kanban->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy, $searchValue);
return print(json_encode($kanbanGroup));
}
}
/**
* AJAX: Update the execution name.
*
* @param int $executionID
* @param string $newExecutionName
* @access public
* @return bool
*/
public function ajaxUpdateExecutionName($executionID, $newExecutionName)
{
$this->dao->update(TABLE_EXECUTION)->set('name')->eq($newExecutionName)->where('id')->eq($executionID)->exec();
if(dao::isError()) echo false;
echo true;
}
/**
* Ajax get copy project executions.
*
* @param int $projectID
* @access public
* @return void
*/
public function ajaxGetCopyProjectExecutions($projectID = 0)
{
$executions = $this->execution->getList($projectID, 'all', 'all', 0, 0, 0, null, false);
echo json_encode($executions);
}
/**
* AJAX: Get execution kanban data.
*
* @param int $executionID
* @access public
* @return void
*/
public function ajaxGetExecutionKanban($executionID)
{
$execution = $this->execution->getByID($executionID);
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 : '';
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
echo json_encode($kanbanData);
}
}
}