* @package project
* @version $Id
* @link http://www.zentao.net
*/
class project extends control
{
/**
* Construct
*
* @param string $moduleName
* @param string $methodName
* @param string $appName
* @access public
* @return void
*/
public function __construct($moduleName = '', $methodName = '', $appName = '')
{
parent::__construct($moduleName, $methodName, $appName);
$this->view->globalDisableProgram = $this->config->systemMode == 'light';
}
/**
* Project create guide.
*
* @param int $programID
* @param string $from
* @param int $productID
* @param int $branchID
* @access public
* @return void
*/
public function createGuide($programID = 0, $from = 'project', $productID = 0, $branchID = 0)
{
$this->view->from = $from;
$this->view->programID = $programID;
$this->view->productID = $productID;
$this->view->branchID = $branchID;
$this->display();
}
/**
* Export project.
*
* @param string $status
* @param string $orderBy
* @access public
* @return void
*/
public function export($status, $orderBy)
{
if($_POST)
{
$projectLang = $this->lang->project;
$projectConfig = $this->config->project;
/* Create field lists. */
$fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $projectConfig->list->exportFields);
foreach($fields as $key => $fieldName)
{
$fieldName = trim($fieldName);
$fields[$fieldName] = zget($projectLang, $fieldName);
unset($fields[$key]);
}
if(!isset($this->config->setCode) or empty($this->config->setCode)) unset($fields['code']);
if(isset($fields['hasProduct'])) $fields['hasProduct'] = $projectLang->type;
$projects = $this->project->getList($status, $orderBy);
$users = $this->loadModel('user')->getPairs('noletter');
$this->loadModel('product');
foreach($projects as $i => $project)
{
$hasProduct = $project->hasProduct;
$project->PM = zget($users, $project->PM);
$project->status = $this->processStatus('project', $project);
$project->model = zget($projectLang->modelList, $project->model);
$project->budget = $project->budget != 0 ? $project->budget . zget($projectLang->unitList, $project->budgetUnit) : $this->lang->project->future;
$project->parent = $project->parentName;
$project->hasProduct = zget($projectLang->projectTypeList, $project->hasProduct);
$linkedProducts = $this->product->getProducts($project->id, 'all', '', false);
$project->linkedProducts = implode(',', $linkedProducts);
if(!$hasProduct) $project->linkedProducts = '';
if($this->post->exportType == 'selected')
{
$checkedItem = $this->cookie->checkedItem;
if(strpos(",$checkedItem,", ",{$project->id},") === false) unset($projects[$i]);
}
}
if($this->config->edition != 'open') list($fields, $projects) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $projects);
$this->post->set('fields', $fields);
$this->post->set('rows', $projects);
$this->post->set('kind', $this->lang->project->common);
$this->fetch('file', 'export2' . $this->post->fileType, $_POST);
}
$this->display();
}
/**
* Ajax get project drop menu.
*
* @access public
* @return void
*/
public function ajaxGetDropMenu(string $projectID, string $module, string $method)
{
/* Set cookie for show all project. */
$_COOKIE['showClosed'] = 1;
$this->projectZen->getDropMenu(intval($projectID), $module, $method);
}
/**
* 获取可复制看板项目通过页面检索。
* Ajax retrieve cloneable Kanban projects based on search.
*
* @access public
* @return void
*/
public function ajaxRetrieveCloneableProject()
{
/* 获取可复制的看板项目键值对。*/
/* Get cloneable kanban project pairs by model. */
$projectKeys = $this->project->getCopyProjectPairs($this->post->name, $this->post->model);
$projects = $this->project->getPairsByModel('', 0, '', $projectKeys);
/* 页面渲染出可复制的项目。*/
/* Rendering cloneable projects on page. */
$html = empty($projects) ? "
{$this->lang->noData}
" : '';
foreach($projects as $id => $name)
{
$active = $this->post->cpoyProjectID == $id ? 'active' : '';
$html .= "';
}
echo $html;
}
/**
* 移除项目团队成员时进行提示。
* Ajax prompts when removing project team members.
*
* @param string $projectID
* @param string $account
* @access public
* @return void
*/
public function ajaxGetRemoveMemberTips(string $projectID, string $account)
{
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
if(!$project->multiple) return;
/* 获取项目下相关执行的团队成员。*/
/* Get team members of execution under the project. */
$executions = $this->loadModel('execution')->getByProject($projectID, 'undone', 0, true);
$executionMembers = $this->project->getExecutionMembers($account, array_keys($executions));
if(empty($executionMembers)) return;
/* 根据移除的成员参与执行的数量,输出对应的提示语。*/
/* Output corresponding prompts based on the number of removed members participating in execution. */
$count = count($executionMembers);
$executionNames = $count != 0 ? current($executionMembers) : '';
if($count > 1) $executionNames .= ',' . next($executionMembers);
if($count <= 2) $this->lang->project->etc = ' ';
if(strpos($this->app->getClientLang(), 'zh') !== false)
{
echo sprintf($this->lang->project->unlinkExecutionMember, $executionNames, $this->lang->project->etc, $count);
}
else
{
echo sprintf($this->lang->project->unlinkExecutionMember, $count, $executionNames, $this->lang->project->etc);
}
}
/**
* 产品关联项目时获取被关联产品的分支数量
* AJAX: Get linked products with branch.
*
* @param string $projectID
*
* @access public
* @return string
*/
public function ajaxGetLinkedProducts(string $projectID)
{
$productsWithBranch = array();
$linkedProducts = $this->project->getBranchesByProject((int)$projectID);
foreach($linkedProducts as $productID => $branches)
{
foreach($branches as $branchID => $branchInfo) $productsWithBranch[$productID][$branchID] = $branchID;
}
echo json_encode($productsWithBranch);
}
/**
* 选中项目或项目集时,获取项目表单数据:创建或编辑。
* Ajax: Get selected object's form information: create or edit.
*
* @param string $objectType project|program
* @param string $objectID
* @param string $selectedProgramID
* @access public
* @return void
*/
public function ajaxGetProjectFormInfo(string $objectType, string $objectID, string $selectedProgramID)
{
$objectID = (int)$objectID;
$selectedProgramID = (int)$selectedProgramID;
$projectFormInfo = array();
/* 如果选择项目集,获取项目集相关信息。*/
/* If selectedProgramID exist, get the info for the program. */
if($selectedProgramID)
{
$selectedProgram = $this->loadModel('program')->getByID($selectedProgramID);
if($selectedProgram->budget) $availableBudget = $this->program->getBudgetLeft($selectedProgram);
$projectFormInfo['selectedProgramBegin'] = $selectedProgram->begin;
$projectFormInfo['selectedProgramEnd'] = $selectedProgram->end;
$projectFormInfo['budgetUnit'] = $selectedProgram->budgetUnit;
$projectFormInfo['selectedProgramPath'] = explode(',', $selectedProgram->path);
}
/* 获取可用预算和项目集时间段。*/
/* Get the available budget and program time range. */
if(!empty($objectID))
{
/* 获取最后选中的最后项目集。*/
/* Get the path of the last selected program. */
$object = $objectType == 'project' ? $this->project->getByID($objectID) : $this->loadModel('program')->getByID($objectID);
$projectFormInfo['objectPath'] = explode(',', $object->path);
if(isset($availableBudget)) $availableBudget = $object->parent == $selectedProgramID ? $availableBudget + (int)$object->budget : $availableBudget;
if($objectType == 'program')
{
$progranChildDate = $this->project->getProgramDate($objectID);
$projectFormInfo['minChildBegin'] = isset($progranChildDate) ? $progranChildDate['minChildBegin'] : '';
$projectFormInfo['maxChildEnd'] = isset($progranChildDate) ? $progranChildDate['maxChildEnd'] : '';
}
}
if($objectType == 'program')
{
$withProgram = $this->config->systemMode == 'ALM' ? true : false;
$allProducts = array('') + $this->program->getProductPairs($selectedProgramID, 'all', 'noclosed', '', 0, $withProgram);
$projectFormInfo['allProducts'] = html::select('products[]', $allProducts, '', "class='form-control chosen' onchange='loadBranches(this)'");
$projectFormInfo['plans'] = html::select('plans[][][]', '', '', 'class=\'form-control chosen\' multiple');
}
if(isset($availableBudget)) $projectFormInfo['availableBudget'] = $availableBudget;
echo json_encode($projectFormInfo);
}
/**
* Project index view.
*
* @param int $projectID
* @param string $browseType
* @access public
* @return void
*/
public function index($projectID = 0, $browseType = 'all', $recTotal = 0, $recPerPage = 15, $pageID = 1)
{
$projectID = $this->project->saveState($projectID, $this->project->getPairsByProgram());
if($projectID == 0 and common::hasPriv('project', 'create')) $this->locate($this->createLink('project', 'create'));
if($projectID == 0 and !common::hasPriv('project', 'create')) $this->locate($this->createLink('project', 'browse'));
$this->project->setMenu($projectID);
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
/* Locate to task when set no execution. */
if(!$project->multiple)
{
$executions = $this->loadModel('execution')->getList($project->id);
foreach($executions as $execution)
{
if(!$execution->multiple) $this->locate($this->createLink('execution', 'task', "executionID={$execution->id}"));
}
}
if(empty($project) || $project->type != 'project') return print(js::error($this->lang->notFound) . js::locate('back'));
if(!$projectID) $this->locate($this->createLink('project', 'browse'));
helper::setcookie("lastProject", strVal($projectID));
if($project->model == 'kanban' and $this->config->vision != 'lite')
{
/* Load pager and get kanban list. */
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$kanbanList = $this->loadModel('execution')->getList($projectID, 'all', $browseType, 0, 0, 0, $pager);
$executionActions = array();
foreach($kanbanList as $kanbanID => $kanban)
{
foreach($this->config->execution->statusActions as $action)
{
if($this->execution->isClickable($kanban, $action)) $executionActions[$kanbanID][] = $action;
}
if($this->execution->isClickable($kanban, 'delete')) $executionActions[$kanbanID][] = 'delete';
}
$this->view->kanbanList = $kanbanList;
$this->view->browseType = $browseType;
$this->view->memberGroup = $this->execution->getMembersByIdList(array_keys($kanbanList));
$this->view->usersAvatar = $this->loadModel('user')->getAvatarPairs('all');
$this->view->executionActions = $executionActions;
$this->view->pager = $pager;
$this->view->title = $this->lang->project->common . $this->lang->colon . $this->lang->project->index;
$this->view->project = $project;
$this->view->userIdPairs = $this->loadModel('user')->getPairs('nodeleted|showid|all');
$this->display();
}
else
{
echo $this->fetch('block', 'dashboard', "dashboard={$project->model}project");
}
}
/**
* 渲染项目列表页面数据。
* Display project list page.
*
* @param int $programID
* @param string $browseType
* @param int $param
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browse(int $programID = 0, string $browseType = 'doing', string $param = '', string $orderBy = 'order_asc', int $recTotal = 0, int $recPerPage = 15, int $pageID = 1)
{
$this->session->set('projectList', $this->app->getURI(true), 'project');
$browseType = strtolower($browseType);
if(!in_array($browseType, array('all', 'undone'))) unset($this->config->project->dtable->fieldList['status']);
/* Load pager. */
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$actionURL = $this->createLink('project', 'browse', "&programID=$programID&browseType=bySearch&queryID=myQueryID");
$this->project->buildSearchForm($queryID, $actionURL);
$programTitle = $this->loadModel('setting')->getItem("owner={$this->app->user->account}&module=project&key=programTitle");
$projectStats = $this->loadModel('program')->getProjectStats($programID, $browseType, $queryID, $orderBy, $pager, $programTitle);
$this->view->title = $this->lang->project->browse;
$this->view->projectStats = $this->projectZen->processProjectListData($projectStats);
$this->view->pager = $pager;
$this->view->programID = $programID;
$this->view->programTree = $this->project->getProgramTree();
$this->view->browseType = $browseType;
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->showBatchEdit = $this->cookie->showProjectBatchEdit;
$this->display();
}
/**
* Project kanban.
*
* @access public
* @return void
*/
public function kanban()
{
extract($this->project->getStats4Kanban());
$this->view->title = $this->lang->project->kanban;
$this->view->kanbanGroup = array_filter($kanbanGroup);
$this->view->latestExecutions = $latestExecutions;
$this->view->programPairs = array(0 => $this->lang->project->noProgram) + $this->loadModel('program')->getPairs(true, 'order_asc');
$this->display();
}
/**
* Set module display mode.
*
* @access public
* @return void
*/
public function programTitle()
{
$this->loadModel('setting');
if($_POST)
{
$programTitle = $this->post->programTitle;
$this->setting->setItem($this->app->user->account . '.project.programTitle', $programTitle);
return print(js::reload('parent.parent'));
}
$status = $this->setting->getItem('owner=' . $this->app->user->account . '&module=project&key=programTitle');
$this->view->status = empty($status) ? '0' : $status;
$this->display();
}
/**
* Create a project.
*
* @param string $model
* @param int $programID
* @param int $copyProjectID
* @param string $extra
* @access public
* @return void
*/
public function create($model = 'scrum', $programID = 0, $copyProjectID = 0, $extra = '')
{
$this->session->set('projectModel', $model);
if($model == 'kanban') unset($this->lang->project->authList['reset']);
if($_POST)
{
$postData = form::data($this->config->project->form->create);
$project = $this->projectZen->prepareCreateExtras($postData);
$projectID = $this->project->create($project, $postData);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if($project->model == 'kanban') $this->project->addTeamMembers($projectID, $project, (array)$this->post->teamMembers);
$this->loadModel('action')->create('project', $projectID, 'opened');
/* Link the plan stories. */
if(($project->hasProduct) && !empty($this->post->plans)) $this->project->addPlans($projectID, $this->post->plans);
$message = $this->executeHooks($projectID);
if($message) $this->lang->saveSuccess = $message;
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $projectID));
if($this->app->tab != 'project' and $this->session->createProjectLocate)
{
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->session->createProjectLocate));
}
else
{
if($model == 'waterfall' or $model == 'waterfallplus')
{
$productID = $this->loadModel('product')->getProductIDByProject($projectID, true);
$this->session->set('projectPlanList', $this->createLink('programplan', 'browse', "projectID=$projectID&productID=$productID&type=lists", '', '', $projectID), 'project');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('programplan', 'create', "projectID=$projectID", '', '', $projectID)));
}
$parent = (int)$project->parent;
$systemMode = $this->loadModel('setting')->getItem('owner=system&module=common§ion=global&key=mode');
if(!empty($systemMode) and $systemMode == 'light') $parent = 0;
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'browse', "programID=$parent&browseType=all", '', false, $projectID)));
}
}
$this->projectZen->buildCreateForm((string)$model, (int)$programID, (int)$copyProjectID, (string)$extra);
}
/**
* Edit a project.
*
* @param int $projectID
* @param string $from
* @access public
*/
public function edit(string $projectID, string $from = '')
{
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
$this->project->setMenu($projectID);
if($project->model == 'kanban')
{
unset($this->lang->project->authList['reset']);
$this->lang->project->aclList = $this->lang->project->kanbanAclList;
$this->lang->project->subAclList = $this->lang->project->kanbanSubAclList;
}
if($_POST)
{
$postData = form::data($this->config->project->form->edit);
$newProject = $this->projectZen->prepareProject($postData, $project->hasProduct);
$changes = $this->project->update($newProject, $project, $this->post->uid);
if($changes)
{
$actionID = $this->loadModel('action')->create('project', $projectID, 'edited');
$this->action->logHistory($actionID, $changes);
}
$this->project->updatePlans($projectID, (array)$this->post->plans); // 更新关联的计划列表。
$this->project->updateProducts($projectID, (array)$this->post->products); // 更新关联的项目列表。
$this->project->updateTeamMembers($newProject, $project, (array)$this->post->teamMembers); // 更新关联的用户信息。
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$message = $this->executeHooks($projectID);
if($message) $this->lang->saveSuccess = $message;
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
$locateLink = ($this->session->projectList and $from != 'view') ? $this->session->projectList : inLink('view', "projectID=$projectID");
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink));
}
unset($this->lang->project->modelList['']);
$this->projectZen->buildEditForm($projectID, $project);
}
/**
* Batch edit projects.
*
* @access public
* @return void
*/
public function batchEdit()
{
$this->loadModel('action');
$this->loadModel('execution');
if($this->post->names)
{
$allChanges = $this->project->batchUpdate();
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(!empty($allChanges))
{
foreach($allChanges as $projectID => $changes)
{
if(empty($changes)) continue;
$actionID = $this->action->create('project', $projectID, 'Edited');
$this->action->logHistory($actionID, $changes);
}
}
$locateLink = $this->session->projectList;
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink));
}
if(!$this->post->projectIdList) return print(js::locate($this->session->projectList, 'parent'));
$projectIdList = $this->post->projectIdList;
$projects = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->in($projectIdList)->fetchAll('id');
/* Get program list. */
$programs = $this->loadModel('program')->getParentPairs();
$unauthorizedIDList = array();
foreach($projects as $project)
{
if(!isset($programs[$project->parent]) and !in_array($project->parent, $unauthorizedIDList)) $unauthorizedIDList[] = $project->parent;
$appendPMUsers[$project->PM] = $project->PM;
}
$unauthorizedPrograms = $this->program->getPairsByList($unauthorizedIDList);
$this->view->title = $this->lang->project->batchEdit;
$this->view->projects = $projects;
$this->view->programs = $programs;
$this->view->unauthorizedPrograms = $unauthorizedPrograms;
$this->view->PMUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst', $appendPMUsers);
$this->display();
}
/**
* View a project.
*
* @param int $projectID
* @access public
* @return void
*/
public function view($projectID = 0)
{
if(!defined('RUN_MODE') || RUN_MODE != 'api') $projectID = $this->project->saveState((int)$projectID, $this->project->getPairsByProgram());
$this->session->set('teamList', $this->app->getURI(true), 'project');
$projectID = $this->project->setMenu($projectID);
$project = $this->project->getById($projectID);
if($this->config->systemMode == 'ALM')
{
$programList = array_filter(explode(',', $project->path));
array_pop($programList);
$this->view->programList = $this->loadModel('program')->getPairsByList($programList);
}
if(empty($project) || strpos('scrum,waterfall,kanban,agileplus,waterfallplus', $project->model) === false)
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'code' => 404, 'message' => '404 Not found'));
return print(js::error($this->lang->notFound) . js::locate($this->createLink('project', 'browse')));
}
$products = $this->loadModel('product')->getProducts($projectID);
$linkedBranches = array();
foreach($products as $product)
{
if(isset($product->branches))
{
foreach($product->branches as $branchID) $linkedBranches[$branchID] = $branchID;
}
}
/* Load pager. */
$this->app->loadClass('pager', true);
$pager = new pager(0, 30, 1);
/* Check exist extend fields. */
$isExtended = false;
if($this->config->edition != 'open')
{
$extend = $this->loadModel('workflowaction')->getByModuleAndAction('project', 'view');
if(!empty($extend) and $extend->extensionType == 'extend') $isExtended = true;
}
$this->executeHooks($projectID);
$this->view->title = $this->lang->project->view;
$this->view->position = $this->lang->project->view;
$this->view->projectID = $projectID;
$this->view->project = $project;
$this->view->products = $products;
$this->view->actions = $this->loadModel('action')->getList('project', $projectID);
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->teamMembers = $this->project->getTeamMembers($projectID);
$this->view->statData = $this->project->getStatData($projectID);
$this->view->workhour = $this->project->getWorkhour($projectID);
$this->view->planGroup = $this->loadModel('execution')->getPlans($products);
$this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), '', $linkedBranches);
$this->view->dynamics = $this->loadModel('action')->getDynamic('all', 'all', 'date_desc', $pager, 'all', $projectID);
$this->view->isExtended = $isExtended;
$this->display();
}
/**
* Project browse groups.
*
* @param int $projectID
* @param int $programID
* @access public
* @return void
*/
public function group($projectID = 0, $programID = 0)
{
$this->loadModel('group');
$this->project->setMenu($projectID);
$groups = $this->group->getList($projectID);
$groupUsers = array();
foreach($groups as $group)
{
$group->users = '';
$groupUsers = $this->group->getUserPairs($group->id);
foreach($groupUsers as $realname) $group->users .= $realname . ' ';
}
$this->view->title = $this->lang->company->orgView . $this->lang->colon . $this->lang->group->browse;
$this->view->groups = $groups;
$this->view->project = $this->dao->findById($projectID)->from(TABLE_PROJECT)->fetch();
$this->view->projectID = $projectID;
$this->view->programID = $programID;
$this->view->groupUsers = $groupUsers;
$this->display();
}
/**
* Project create a group.
*
* @param int $projectID
* @access public
* @return void
*/
public function createGroup($projectID = 0)
{
$this->loadModel('group');
if(!empty($_POST))
{
$_POST['project'] = $projectID;
$groupID = $this->group->create();
if(dao::isError()) return print(js::error(dao::getError()));
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $groupID));
return print(js::closeModal('parent.parent'));
}
$this->view->title = $this->lang->company->orgView . $this->lang->colon . $this->lang->group->create;
$this->display('group', 'create');
}
/**
* Project dynamic.
*
* @param int $projectID
* @param string $type
* @param string $param
* @param int $recTotal
* @param string $date
* @param string $direction next|pre
* @access public
* @return void
*/
public function dynamic($projectID = 0, $type = 'today', $param = '', $recTotal = 0, $date = '', $direction = 'next')
{
$projectID = (int)$projectID;
$this->loadModel('execution');
$this->project->setMenu($projectID);
/* 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('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');
if($this->config->edition == 'max')
{
$this->session->set('riskList', $uri, 'project');
$this->session->set('issueList', $uri, 'project');
}
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
/* Set the pager. */
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, 50, 1);
/* Set the user and type. */
$account = 'all';
if($type == 'account')
{
$user = $this->loadModel('user')->getById($param, 'account');
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', $projectID, 'all', $date, $direction);
if(empty($recTotal)) $recTotal = count($actions);
/* The header and position. */
$project = $this->project->getByID($projectID);
$this->view->title = $project->name . $this->lang->colon . $this->lang->project->dynamic;
$this->view->userIdPairs = $this->loadModel('user')->getTeamMemberPairs($projectID, 'project');
$this->view->accountPairs = $this->user->getPairs('noletter|nodeleted');
/* Assign. */
$this->view->projectID = $projectID;
$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->view->recTotal = $recTotal;
$this->display();
}
/**
* Execution list.
*
* @param string $status
* @param int $projectID
* @param string $orderBy
* @param int $productID
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function execution(string $status = 'undone', int $projectID = 0, string $orderBy = 'order_asc', int $productID = 0, int $recTotal = 0, int $recPerPage = 100, int $pageID = 1)
{
$this->loadModel('execution');
$this->loadModel('task');
$this->loadModel('programplan');
$this->session->set('executionList', $this->app->getURI(true), 'project');
if($this->cookie->showTask) $this->session->set('taskList', $this->app->getURI(true), 'project');
$projectID = (int)$projectID;
$projects = $this->project->getPairsByProgram();
$projectID = $this->project->saveState($projectID, $projects);
$project = $this->project->getByID($projectID);
$this->project->setMenu($projectID);
if(!$projectID) return print(js::locate($this->createLink('project', 'browse')));
if(!empty($project->model) and $project->model == 'kanban' and !(defined('RUN_MODE') and RUN_MODE == 'api')) return print(js::locate($this->createLink('project', 'index', "projectID=$projectID")));
/* Load pager and get tasks. */
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$allExecution = $this->execution->getStatData($projectID, 'all');
$this->view->allExecutionNum = empty($allExecution);
$executionStats = $this->execution->getStatData($projectID, $status, $productID, 0, $this->cookie->showTask, '', $orderBy, $pager);
$this->view->title = $this->lang->execution->allExecutions;
$this->view->executionStats = $executionStats;
$this->view->productList = $this->loadModel('product')->getProductPairsByProject($projectID, 'all', '', false);
$this->view->productID = $productID;
$this->view->product = $this->product->getByID($productID);
$this->view->projectID = $projectID;
$this->view->project = $project;
$this->view->projects = $projects;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->status = $status;
$this->view->isStage = isset($project->model) && ($project->model == 'waterfall' || $project->model == 'waterfallplus');
$this->view->avatarList = $this->user->getAvatarPairs('');
$this->display();
}
/**
* Project bug list.
*
* @param int $projectID
* @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(int $projectID = 0, int $productID = 0, $branchID = '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), 'project');
$this->project->setMenu($projectID);
$projectID = (int)$projectID;
$product = $this->product->getById($productID);
$project = $this->project->getByID($projectID);
$type = strtolower($type);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
$products = $this->product->getProducts($projectID);
if(!$project->multiple) unset($this->config->bug->datatable->fieldList['execution']);
if(!$project->hasProduct)
{
unset($this->config->bug->search['fields']['product']);
if($project->model != 'scrum') unset($this->config->bug->search['fields']['plan']);
}
if(!$project->multiple and !$project->hasProduct) unset($this->config->bug->search['fields']['plan']);
$productPairs = array('0' => $this->lang->product->all);
foreach($products as $productData) $productPairs[$productData->id] = $productData->name;
if($project->hasProduct) $this->lang->modulePageNav = $this->product->select($productPairs, $productID, 'project', 'bug', (string)$projectID, $branchID);
/* Header and position. */
$title = $project->name . $this->lang->colon . $this->lang->bug->common;
$position[] = html::a($this->createLink('project', 'browse', "projectID=$projectID"), $project->name);
$position[] = $this->lang->bug->common;
$executions = $this->loadModel('execution')->getPairs($projectID, 'all', 'empty|withdelete');
/* Load pager and get bugs, user. */
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$sort = common::appendOrder($orderBy);
/* team member pairs. */
$memberPairs = array();
$memberPairs[] = "";
$teamMembers = $this->project->getTeamMembers($projectID);
foreach($teamMembers as $key => $member) $memberPairs[$key] = $member->realname;
/* Build the search form. */
$actionURL = $this->createLink('project', 'bug', "projectID=$projectID&productID=$productID&branchID=$branchID&orderBy=$orderBy&build=$build&type=bysearch&queryID=myQueryID");
$this->loadModel('execution')->buildBugSearchForm($products, $queryID, $actionURL, 'project');
$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->loadModel('branch')->getList($productID, $projectID, 'all');
foreach($branches as $branchInfo)
{
$branchOption[$branchInfo->id] = $branchInfo->name;
$branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
}
}
$moduleID = $type != 'bysearch' ? $param : 0;
$modules = $this->tree->getAllModulePairs('bug');
/* Get module tree.*/
$extra = array('projectID' => $projectID, 'orderBy' => $orderBy, 'type' => $type, 'build' => $build, 'branchID' => $branchID);
if($projectID and empty($productID) and count($products) > 1)
{
$moduleTree = $this->tree->getBugTreeMenu($projectID, $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('productID' => $productID, 'branchID' => $branchID), $branchID);
}
else
{
$moduleTree = array();
}
$tree = $moduleID ? $this->tree->getByID($moduleID) : '';
/* Process the openedBuild and resolvedBuild fields. */
$bugs = $this->bug->getProjectBugs($projectID, $productID, $branchID, $build, $type, $param, $sort, '', $pager);
$bugs = $this->bug->processBuildForBugs($bugs);
$bugs = $this->bug->batchAppendDelayedDays($bugs);
/* 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')->getByIdList($taskIdList) : array();
$showModule = !empty($this->config->datatable->projectBug->showModule) ? $this->config->datatable->projectBug->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->productID = $productID;
$this->view->project = $project;
$this->view->branchID = empty($this->view->build->branch) ? $branchID : $this->view->build->branch;
$this->view->memberPairs = $memberPairs;
$this->view->type = $type;
$this->view->param = $param;
$this->view->builds = $this->loadModel('build')->getBuildPairs($productID);
$this->view->users = $this->user->getPairs('noletter');
$this->view->branchOption = $branchOption;
$this->view->branchTagOption = $branchTagOption;
$this->view->executions = $executions;
$this->view->plans = $this->loadModel('productplan')->getPairs($productID ? $productID : array_keys($products));
$this->view->stories = $storyList;
$this->view->tasks = $taskList;
$this->view->projectPairs = $this->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();
}
/**
* Project case list.
*
* @param int $projectID
* @param int $productID
* @param string|int $branch
* @param string $browseType
* @param int $param
* @param string $caseType
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function testcase(int $projectID = 0, int $productID = 0, string $branch = 'all', string $browseType = 'all', int $param = 0, string $caseType = '', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
{
$this->loadModel('product');
$this->session->set('bugList', $this->app->getURI(true), 'project');
$this->session->set('reviewList', $this->app->getURI(true), 'project');
$products = array('0' => $this->lang->product->all) + $this->product->getProducts($projectID, 'all', '', false);
$extra = "$projectID,$browseType";
$hasProduct = $this->dao->findByID($projectID)->from(TABLE_PROJECT)->fetch('hasProduct');
if($hasProduct) $this->lang->modulePageNav = $this->product->select($products, $productID, 'project', 'testcase', $extra, $branch);
echo $this->fetch('testcase', 'browse', "productID=$productID&branch=$branch&browseType=$browseType¶m=$param&caseType=$caseType&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID&projectID=$projectID");
}
/**
* List of test reports for the project.
*
* @param int $projectID
* @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($projectID = 0, $objectType = 'project', $extra = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
echo $this->fetch('testreport', 'browse', "objectID=$projectID&objectType=$objectType&extra=$extra&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
}
/**
* Project test task list.
*
* @param int $projectID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function testtask($projectID = 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), 'qa');
$this->session->set('buildList', $this->app->getURI(true), 'execution');
$projectID = (int)$projectID;
$this->project->setMenu($projectID);
/* Load pager. */
$this->app->loadClass('pager', true);
$pager = pager::init($recTotal, $recPerPage, $pageID);
$project = $this->project->getByID($projectID);
$tasks = $this->testtask->getProjectTasks($projectID, $orderBy, $pager);
$waitCount = 0;
$testingCount = 0;
$blockedCount = 0;
$doneCount = 0;
foreach($tasks as $task)
{
if($task->status == 'wait') $waitCount ++;
if($task->status == 'doing') $testingCount ++;
if($task->status == 'blocked') $blockedCount ++;
if($task->status == 'done') $doneCount ++;
if($task->build == 'trunk' || empty($task->buildName)) $task->buildName = $this->lang->trunk;
}
$this->view->title = $project->name . $this->lang->colon . $this->lang->project->common;
$this->view->project = $project;
$this->view->projectID = $projectID;
$this->view->projectName = $project->name;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->tasks = $tasks;
$this->view->waitCount = $waitCount;
$this->view->testingCount = $testingCount;
$this->view->blockedCount = $blockedCount;
$this->view->doneCount = $doneCount;
$this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter');
$this->view->products = $this->loadModel('product')->getPairs('', 0);
$this->view->canBeChanged = common::canModify('project', $project); // Determines whether an object is editable.
$this->view->actions = $this->loadModel('action')->getList('testtask', $projectID);
$this->display();
}
/**
* Browse builds of a project.
*
* @param int $projectID
* @param string $type all|product|bysearch
* @param int $param
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function build($projectID = 0, $type = 'all', $param = 0, $orderBy = 't1.date_desc,t1.id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* Load module and get project. */
$this->loadModel('build');
$this->loadModel('product');
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
$this->project->setMenu($projectID);
$this->session->set('buildList', $this->app->getURI(true), 'project');
if($project->multiple)
{
$executionPairs = $this->loadModel('execution')->getByProject($project->id, 'all', '', true, $project->model == 'waterfall');
$this->config->build->search['fields']['execution'] = zget($this->lang->project->executionList, $project->model);
$this->config->build->search['params']['execution'] = array('operator' => '=', 'control' => 'select', 'values' => $executionPairs);
}
if(!$project->hasProduct) unset($this->config->build->search['fields']['product']);
$product = $param ? $this->loadModel('product')->getById((int)$param) : '';
if($product and $product->type != 'normal')
{
$branches = array(BRANCH_MAIN => $this->lang->branch->main) + $this->loadModel('branch')->getPairs($product->id, '', $projectID);
$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);
}
/* Build the search form. */
$type = strtolower($type);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
$actionURL = $this->createLink($this->app->rawModule, $this->app->rawMethod, "projectID=$projectID&type=bysearch&queryID=myQueryID");
$products = $this->product->getProducts($projectID, 'all', '', false);
$this->project->buildProjectBuildSearchForm($products, $queryID, $actionURL, 'project', $project);
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
if($type == 'bysearch')
{
$builds = $this->build->getProjectBuildsBySearch((int)$projectID, (int)$param, $orderBy, $pager);
}
else
{
$builds = $this->build->getProjectBuilds((int)$projectID, $type, $param, $orderBy, $pager);
}
/* Header and position. */
$this->view->title = $project->name . $this->lang->colon . $this->lang->execution->build;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->builds = $this->projectZen->processBuildListData($builds, $projectID);
$this->view->product = $type == 'product' ? $param : 'all';
$this->view->project = $project;
$this->view->products = $products;
$this->view->buildPairs = $this->loadModel('build')->getBuildPairs(0);
$this->view->type = $type;
$this->view->orderBy = $orderBy;
$this->view->param = $param;
$this->view->pager = $pager;
$this->view->recTotal = $pager->recTotal;
$this->display();
}
/**
* Manage privleges of a group.
*
* @param int $projectID
* @param string $type
* @param int $param
* @param string $menu
* @param string $version
* @access public
* @return void
*/
public function managePriv($projectID, $type = 'byGroup', $param = 0, $menu = '', $version = '')
{
$this->loadModel('group');
if($type == 'byGroup')
{
$groupID = $param;
$group = $this->group->getById($groupID);
}
$this->view->type = $type;
foreach($this->lang->resource as $moduleName => $action)
{
if($this->group->checkMenuModule($menu, $moduleName) or $type != 'byGroup') $this->app->loadLang($moduleName);
}
if(!empty($_POST))
{
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('group', "projectID=$group->project")));
}
$this->project->setMenu($projectID);
if($type == 'byGroup')
{
$this->group->sortResource();
$groupPrivs = $this->group->getPrivs($groupID);
$this->view->title = $group->name . $this->lang->colon . $this->lang->group->managePriv;
/* Join changelog when be equal or greater than this version.*/
$realVersion = str_replace('_', '.', $version);
$changelog = array();
foreach($this->lang->changelog as $currentVersion => $currentChangeLog)
{
if(version_compare($currentVersion, $realVersion, '>=')) $changelog[] = implode(',', $currentChangeLog);
}
$this->view->group = $group;
$this->view->changelogs = ',' . implode(',', $changelog) . ',';
$this->view->groupPrivs = $groupPrivs;
$this->view->groupID = $groupID;
$this->view->projectID = $projectID;
$this->view->menu = $menu;
$this->view->version = $version;
/* Unset not project privs. */
$project = $this->project->getByID((int)$group->project);
if($project->hasProduct)
{
if($this->config->URAndSR) unset($this->lang->resource->requirement);
unset($this->lang->resource->productplan);
unset($this->lang->resource->tree);
}
else
{
$this->lang->productplan->common = $this->lang->productplan->plan;
$this->lang->projectstory->common = $this->lang->projectstory->storyCommon;
$this->lang->projectstory->story = $this->lang->projectstory->storyList;
$this->lang->projectstory->view = $this->lang->projectstory->storyView;
unset($this->lang->resource->project->manageProducts);
unset($this->lang->resource->projectstory->linkStory);
unset($this->lang->resource->projectstory->importplanstories);
unset($this->lang->resource->projectstory->unlinkStory);
unset($this->lang->resource->projectstory->batchUnlinkStory);
unset($this->lang->resource->story->view);
if($this->config->URAndSR) unset($this->lang->resource->requirement->view);
if($this->config->URAndSR) unset($this->lang->resource->requirement->batchChangeBranch);
unset($this->lang->resource->tree->browseTask);
unset($this->lang->resource->tree->browsehost);
unset($this->lang->resource->tree->editHost);
unset($this->lang->resource->tree->fix);
}
if($project->model == 'waterfall' or $project->model == 'waterfallplus')
{
unset($this->lang->resource->productplan);
unset($this->lang->resource->projectplan);
}
if($project->model == 'scrum') unset($this->lang->resource->projectstory->track);
if(!$project->multiple and !$project->hasProduct)
{
unset($this->lang->resource->story->batchChangePlan);
unset($this->lang->resource->execution->importplanstories);
}
$this->view->project = $project;
$this->lang->resource = $this->project->getPrivsByModel($project->multiple ? $project->model : 'noSprint');
}
$this->display();
}
/**
* Browse team of a project.
*
* @param int $projectID
* @access public
* @return void
*/
public function team(string $projectID = '0')
{
$projectID = (int)$projectID;
$this->session->set('teamList', $this->app->getURI(true), 'project');
$this->app->loadLang('execution');
$this->project->setMenu($projectID);
$project = $this->project->getById($projectID);
$deptID = $this->app->user->admin ? 0 : $this->app->user->dept;
$this->view->title = $project->name . $this->lang->colon . $this->lang->project->team;
$this->view->projectID = $projectID;
$this->view->teamMembers = $this->project->getTeamMembers($projectID);
$this->view->deptUsers = $this->loadModel('dept')->getDeptUserPairs($deptID, 'id');
$this->view->canBeChanged = common::canModify('project', $project);
$this->display();
}
/**
* Unlink a memeber.
*
* @param int $projectID
* @param int $userID
* @param string $confirm yes|no
* @param string $removeExecution yes|no
* @access public
* @return void
*/
public function unlinkMember($projectID, $userID, $confirm = 'no', $removeExecution = 'no')
{
if($confirm == 'no') return print(js::confirm($this->lang->project->confirmUnlinkMember, $this->inlink('unlinkMember', "projectID=$projectID&userID=$userID&confirm=yes")));
$user = $this->loadModel('user')->getById($userID, 'id');
$account = $user->account;
$this->project->unlinkMember($projectID, $account, $removeExecution);
if(!dao::isError()) $this->loadModel('action')->create('team', $projectID, '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);
}
echo js::locate($this->inlink('team', "projectID=$projectID"), 'parent');
}
/**
* Manage project members.
*
* @param int $projectID
* @param int $dept
* @param int $copyProjectID
* @access public
* @return void
*/
public function manageMembers($projectID, $dept = '', $copyProjectID = 0)
{
/* Load model. */
$this->loadModel('user');
$this->loadModel('dept');
$this->loadModel('execution');
$this->project->setMenu($projectID);
$project = $this->project->getById($projectID);
if(!empty($_POST))
{
$this->project->manageMembers($projectID);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(empty($project->multiple))
{
$executionID = $this->execution->getNoMultipleID($projectID);
if($executionID) $this->execution->manageMembers($executionID);
}
$this->loadModel('action')->create('team', $projectID, 'ManagedTeam');
$link = $this->session->teamList ? $this->session->teamList : $this->createLink('project', 'team', "projectID=$projectID");
return $this->send(array('message' => $this->lang->saveSuccess, 'result' => 'success', 'locate' => $link));
}
$users = $this->user->getPairs('noclosed|nodeleted|devfirst');
$roles = $this->user->getUserRoles(array_keys($users));
$deptUsers = $dept === '' ? array() : $this->dept->getDeptUserPairs($dept);
$userInfoList = $this->user->getUserDisplayInfos(array_keys($users), $dept);
$currentMembers = $this->project->getTeamMembers($projectID);
$members2Import = $this->project->getMembers2Import($copyProjectID, array_keys($currentMembers));
$this->view->title = $this->lang->project->manageMembers . $this->lang->colon . $project->name;
$this->view->project = $project;
$this->view->users = $users;
$this->view->deptUsers = $deptUsers;
$this->view->userInfoList = $userInfoList;
$this->view->roles = $roles;
$this->view->dept = $dept;
$this->view->depts = array('' => '') + $this->dept->getOptionMenu();
$this->view->currentMembers = $currentMembers;
$this->view->members2Import = $members2Import;
$this->view->teams2Import = array('' => '') + $this->loadModel('personnel')->getCopiedObjects($projectID, 'project', true);
$this->view->copyProjectID = $copyProjectID;
$this->display();
}
/**
* Manage members of a group.
*
* @param int $groupID
* @param int $deptID
* @access public
* @return void
*/
public function manageGroupMember($groupID, $deptID = 0)
{
$this->loadModel('group');
if(!empty($_POST))
{
$this->group->updateUser($groupID);
if(isonlybody()) return print(js::closeModal('parent.parent', 'this'));
return print(js::locate($this->createLink('group', 'browse'), 'parent'));
}
$group = $this->group->getById($groupID);
$project = $this->project->getByID((int)$group->project);
$groupUsers = $this->group->getUserPairs($groupID);
$allUsers = $this->loadModel('dept')->getDeptUserPairs($deptID);
$otherUsers = array_diff_assoc($allUsers, $groupUsers);
if($project->acl != 'open')
{
$canViewMembers = $this->dao->select('account')->from(TABLE_USERVIEW)->where("CONCAT(',', projects, ',')")->like("%,$group->project,%")->fetchPairs();
foreach($otherUsers as $account => $otherUser)
{
if(!isset($canViewMembers[$account])) unset($otherUsers[$account]);
}
}
$outsideUsers = $this->loadModel('user')->getPairs('outside|noclosed|noletter|noempty');
if($project->acl != 'open')
{
foreach($outsideUsers as $account => $outsideUser)
{
if(!isset($canViewMembers[$account])) unset($outsideUsers[$account]);
}
}
$this->view->outsideUsers = array_diff_assoc($outsideUsers, $groupUsers);
$title = $group->name . $this->lang->colon . $this->lang->group->manageMember;
$position[] = $group->name;
$position[] = $this->lang->group->manageMember;
$this->view->title = $title;
$this->view->position = $position;
$this->view->group = $group;
$this->view->deptTree = $this->loadModel('dept')->getTreeMenu(0, array('deptModel', 'createGroupManageMemberLink'), $groupID);
$this->view->groupUsers = $groupUsers;
$this->view->otherUsers = $otherUsers;
$this->display('group', 'manageMember');
}
/**
* Project copy a group.
*
* @param int $groupID
* @access public
* @return void
*/
public function copyGroup($groupID)
{
$this->loadModel('group');
if(!empty($_POST))
{
$group = $this->group->getByID($groupID);
$_POST['project'] = $group->project;
$this->group->copy($groupID);
if(dao::isError()) return print(js::error(dao::getError()));
return print(js::closeModal('parent.parent', 'this'));
}
$this->view->title = $this->lang->company->orgView . $this->lang->colon . $this->lang->group->copy;
$this->view->group = $this->group->getById($groupID);
$this->display('group', 'copy');
}
/**
* Project edit a group.
*
* @param string $groupID
*
* @access public
* @return void
*/
public function editGroup($groupID)
{
$groupID = (int)$groupID;
$this->loadModel('group');
if(!empty($_POST))
{
$this->group->update($groupID);
return print(js::closeModal('parent.parent', 'this'));
}
$this->view->title = $this->lang->company->orgView . $this->lang->colon . $this->lang->group->edit;
$this->view->group = $this->group->getById($groupID);
$this->display('group', 'edit');
}
/**
* Start a project.
*
* @param string $projectID
* @access public
* @return void
*/
public function start(string $projectID)
{
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
if(!empty($_POST))
{
$postData = form::data($this->config->project->form->start);
$postData = $this->projectZen->prepareStartExtras($postData);
$changes = $this->project->start($projectID, $postData);
if(dao::isError()) return print(js::error(dao::getError()));
$this->projectZen->responseAfterStart($project, $changes, $this->post->comment);
return print(js::reload('parent.parent'));
}
$this->projectZen->buildStartForm($project);
}
/**
* 挂起一个项目
* Suspend a project.
*
* @param string $projectID
*
* @access public
* @return void
*/
public function suspend(string $projectID)
{
$projectID = (int)$projectID;
/* Processing parameter passing while suspend project. */
if(!empty($_POST))
{
$postData = form::data($this->config->project->form->suspend);
$postData = $this->projectZen->prepareSuspendExtras($projectID, $postData);
/* Update the database status to suspended. */
$changes = $this->project->suspend($projectID, $postData);
if(dao::isError()) return print(js::error(dao::getError()));
/* Process the data returned after the suspended. */
$comment = strip_tags($this->post->comment, $this->config->allowedTags);
$this->projectZen->responseAfterSuspend($projectID, $changes, $comment);
return print(js::reload('parent.parent'));
}
$this->projectZen->buildSuspendForm($projectID);
}
/**
* 关闭一个项目
* Close a project.
*
* @param string $projectID
* @access public
*
* @return void
*/
public function close(string $projectID)
{
$projectID = (int)$projectID;
/* Processing parameter passing while close project. */
if(!empty($_POST))
{
$postData = form::data($this->config->project->form->close);
$postData = $this->projectZen->prepareClosedExtras($projectID, $postData);
/* Update the database status to closed. */
$changes = $this->project->close($projectID, $postData);
if(dao::isError()) return print(js::error(dao::getError()));
/* Process the data returned after the closed. */
$comment = strip_tags($this->post->comment, $this->config->allowedTags);
$this->projectZen->responseAfterClose($projectID, $changes, $comment);
return print(js::reload('parent.parent'));
}
$this->projectZen->buildClosedForm($projectID);
}
/**
* 激活项目并更新其状态
* Activate a project.
*
* @param string $projectID
* @access public
*
* @return void
*/
public function activate(string $projectID)
{
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
if(!empty($_POST))
{
$postData = form::data($this->config->project->form->activate);
$postData = $this->projectZen->prepareActivateExtras($projectID, $postData);
$changes = $this->project->activate($projectID, $postData);
if(dao::isError()) return print(js::error(dao::getError()));
$this->projectZen->responseAfterActivate($projectID, $changes);
return print(js::reload('parent.parent'));
}
$this->projectZen->buildActivateForm($project);
}
/**
* 删除一个项目,并弹窗确认
* Delete a project and confirm.
*
* @param string $projectID
* @param string $confirm
* @param string $from browse|view
*
* @access public
* @return int
*/
public function delete(string $projectID, string $confirm = 'no', string $from = 'browse'): int
{
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
if($confirm == 'no')
{
return print(js::confirm(sprintf($this->lang->project->confirmDelete, $project->name), $this->createLink('project', 'delete', "projectID=$projectID&confirm=yes&from=$from")));
}
else
{
$this->project->delete(TABLE_PROJECT, $projectID);
$this->project->deleteByTableName('zt_doclib', $projectID);
$this->loadModel('user')->updateUserView($projectID, 'project');
$message = $this->executeHooks($projectID);
if($message) $this->lang->saveSuccess = $message;
/* Delete the execution and product under the project. */
$executionIdList = $this->loadModel('execution')->getPairs($projectID);
if(empty($executionIdList))
{
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
if($from == 'view') return print(js::locate($this->createLink('project', 'browse'), 'parent'));
return print(js::reload('parent'));
}
$this->projectZen->removeAssociatedExecutions($executionIdList);
$this->projectZen->removeAssociatedProducts($project);
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
$this->session->set('project', '');
if($from == 'view') return print(js::locate($this->createLink('project', 'browse'), 'parent'));
return print(js::reload('parent'));
}
}
/**
* 更新项目排列序号
* Update projects order.
*
* @access public
* @return void
*/
public function updateOrder()
{
$idList = explode(',', trim($this->post->projects, ','));
$orderBy = $this->post->orderBy;
if(strpos($orderBy, 'order') === false) return false;
$this->project->updateOrder($idList, $orderBy);
}
/**
* 获取项目白名单列表
* Get white list personnel.
*
* @param string $projectID
* @param string $from project|program|programProject
* @param string $recTotal
* @param string $recPerPage
* @param string $pageID
*
* @access public
* @return void
*/
public function whitelist(string $projectID = '0', string $from = 'project', string $recTotal = '0', string $recPerPage = '20', string $pageID = '1')
{
$projectID = (int)$projectID;
$projectID = $this->project->setMenu($projectID);
$project = $this->project->getByID($projectID);
if(isset($project->acl) and $project->acl == 'open') $this->locate($this->createLink('project', 'index', "projectID=$projectID"));
echo $this->fetch('personnel', 'whitelist', "objectID=$projectID&module=project&browseType=project&orderBy=id_desc&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID&projectID=$projectID&from=$from");
}
/**
* 添加用户到项目白名单中
* Adding users to the white list.
*
* @param string $projectID
* @param int $deptID
* @param int $copyID
* @param int $programID
* @param string $from
*
* @access public
* @return void
*/
public function addWhitelist(string $projectID = '0', int $deptID = 0, int $copyID = 0, int $programID = 0, string $from = 'project')
{
$projectID = (int)$projectID;
$projectID = $this->project->setMenu($projectID);
$project = $this->project->getByID($projectID);
if(isset($project->acl) and $project->acl == 'open') $this->locate($this->createLink('project', 'index', "projectID=$projectID"));
echo $this->fetch('personnel', 'addWhitelist', "objectID=$projectID&dept=$deptID©ID=$copyID&objectType=project&module=project&programID=$programID&from=$from");
}
/*
* 移除项目白名单人员
* Removing users from the white list.
*
* @param string $id
* @param string $confirm
*
* @access public
* @return void
*/
public function unbindWhitelist(string $id = '0', $confirm = 'no')
{
echo $this->fetch('personnel', 'unbindWhitelist', "id=$id&confirm=$confirm");
}
/**
* 管理项目关联产品。
* Manage products of project.
*
* @param string $projectID
* @param string $from project|program|programproject
* @access public
* @return void
*/
public function manageProducts(string $projectID, string $from = 'project')
{
/* 如果是无产品项目,则返回。*/
/* If hasProduct is 0, return. */
$projectID = (int)$projectID;
$project = $this->project->getById($projectID);
if(!$project->hasProduct) return print(js::error($this->lang->project->cannotManageProducts) . js::locate('back'));
$executions = $this->loadModel('execution')->getPairs($projectID);
$IdList = array_keys($executions);
if(!empty($_POST))
{
/* 如果没有选择产品,则提示错误*/
/* If no product is selected, prompt error. */
$postProducts = $this->post->products;
$postOtherProduct = $this->post->otherProducts;
if(!isset($postProducts) and !isset($postOtherProduct))
{
return $this->send(array('result' => 'fail', 'message' => $this->lang->project->errorNoProducts));
}
$this->projectZen->updateLinkedProducts($projectID, $project, $IdList);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
/* 成功关联产品后,跳转页面。*/
/* After successfully associating the product, jump to the page. */
$locateLink = inLink('manageProducts', "projectID=$projectID");
if($from == 'program') $locateLink = $this->session->projectList;
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink));
}
/* Set menu. */
$this->setProjectMenu($projectID, $project->parent);
/* 提取无法被移除的关联产品及分支。*/
/* Extract associated products and branches that cannot be removed. */
$this->projectZen->extractUnModifyForm($projectID, $project);
/* Organizing render pages requires data. */
$this->view->title = $this->lang->project->manageProducts . $this->lang->colon . $project->name;
$this->view->project = $project;
$this->view->executions = $executions;
$this->view->branches = $this->project->getBranchesByProject($projectID);
$this->display();
}
/**
* 获取项目页面中下拉执行列表
* AJAX: get executions of a project in html select.
*
* @param string $projectID
* @param string $executionID
* @param string $mode
* @param string $type all|sprint|stage|kanban
*
* @access public
* @return string
*/
public function ajaxGetExecutions(string $projectID, string $executionID = '0', string $mode = '', string $type = 'all'): string
{
$disabled = '';
$executions = array('' => '');
$projectID = (int)$projectID;
if($projectID)
{
$project = $this->project->getByID($projectID);
$executions += (array)$this->loadModel('execution')->getPairs($projectID, $type, $mode);
if(!empty($project->multiple)) $disabled = 'disabled';
}
if($this->app->getViewType() == 'json') return print(json_encode($executionList));
return print(html::select('execution', $executions, $executionID, "class='form-control $disabled' $disabled"));
}
/**
* 根据执行ID获取对应的项目下拉列表
* AJAX: get a project by execution id.
*
* @param string $executionID
* @access public
* @return string
*/
public function ajaxGetPairsByExecution(string $executionID)
{
$execution = $this->loadModel('execution')->getByID((int)$executionID);
$projectPairs = $this->loadModel('project')->getPairsByIdList($execution->project);
if($this->app->getViewType() == 'json')
{
$project = array('id' => key($projectPairs), 'name' => reset($projectPairs));
$pinyin = common::convert2Pinyin(array(reset($projectPairs)));
$project['namePinyin'] = zget($pinyin, $project['name']);
return print(json_encode($project));
}
else
{
return print(html::select('project', $projectPairs, $execution->project, "class='form-control' onchange='loadProductExecutions({$execution->project}, this.value)'"));
}
}
}