* @package project * @version $Id * @link https://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(int $programID = 0, string $from = 'project', int $productID = 0, int $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(string $status, string $orderBy) { $this->lang->project->id = $this->lang->idAB; $this->lang->project->hasProduct = $this->lang->project->category; $this->lang->project->consumed = $this->lang->project->consume; if($_POST) { /* Get export field lists. */ $fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $this->config->project->list->exportFields); /* Process export field titie. */ foreach($fields as $key => $fieldName) { $fields[$fieldName] = zget($this->lang->project, trim($fieldName)); unset($fields[$key]); } if(!isset($this->config->setCode) || empty($this->config->setCode)) unset($fields['code']); if(isset($fields['hasProduct'])) $fields['hasProduct'] = $this->lang->project->type; /* Format the export project data. */ $projects = $this->projectZen->formatExportProjects($status, $orderBy); /* Set export data. */ 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->view->fileName = zget(arrayUnion($this->lang->project->featureBar['index'], $this->lang->project->moreSelects), $status, '') . '.' . $this->lang->projectCommon; $this->view->customExport = true; $this->view->allExportFields = !isset($this->config->setCode) || empty($this->config->setCode) ? str_replace(',code,', ',', $this->config->project->list->exportFields) : $this->config->project->list->exportFields; $this->display(); } /** * 设置1.5级项目下拉菜单。 * Ajax get project drop menu. * * @param int $projectID * @param string $module * @param string $method * @access public * @return void */ public function ajaxGetDropMenu(int $projectID, string $module = 'project', string $method = 'index', string $extra = '', int $useLink = 1) { /* Set cookie for show all project. */ $_COOKIE['showClosed'] = 1; $project = $this->project->fetchByID($projectID); /* Query user's project and program. */ $projects = $this->project->getListByCurrentUser('*', !empty($project->isTpl) ? 'onlyTpl' : ''); $involvedProjects = $this->project->getInvolvedListByCurrentUser(); $programs = $this->loadModel('program')->getPairs(true); /* Generate project tree. */ $orderedProjects = array(); foreach($projects as $project) { $project->parent = $this->program->getTopByID($project->parent); $project->parent = isset($programs[$project->parent]) ? $project->parent : $project->id; $orderedProjects[$project->parent][] = $project; } $this->view->link = $useLink == 1 ? $this->project->getProjectLink($module, $method, $projectID, $extra) : '#'; // Create the link from module,method. $this->view->projectID = $projectID; $this->view->module = $module; $this->view->method = $method; $this->view->programs = $programs; $this->view->extra = $extra; $this->view->useLink = $useLink; $this->view->project = $project; $this->view->projects = $orderedProjects; $this->view->involvedProjects = $involvedProjects; $this->display(); } /** * 旧页面:设置1.5级项目下拉菜单。 * Ajax get project drop menu. * * @param int $projectID * @param string $module * @param string $method * @access public * @return void */ public function ajaxGetOldDropMenu(int $projectID, string $module, string $method, string $extra = '') { /* Set cookie for show all project. */ $_COOKIE['showClosed'] = 1; $project = $this->project->fetchByID($projectID); /* Query user's project and program. */ $projects = $this->project->getListByCurrentUser('*', !empty($project->isTpl) ? 'onlyTpl' : ''); $involvedProjects = $this->project->getInvolvedListByCurrentUser(); $programs = $this->loadModel('program')->getPairs(true); /* Generate project tree. */ $orderedProjects = array(); foreach($projects as $project) { $project->parent = $this->program->getTopByID($project->parent); $project->parent = isset($programs[$project->parent]) ? $project->parent : $project->id; $orderedProjects[$project->parent][] = $project; } $this->view->link = $this->project->getProjectLink($module, $method, $projectID, $extra); // Create the link from module,method. $this->view->projectID = $projectID; $this->view->module = $module; $this->view->method = $method; $this->view->programs = $programs; $this->view->projects = $orderedProjects; $this->view->involvedProjects = $involvedProjects; $this->display(); } /** * 移除项目团队成员时进行提示。 * 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 int $objectID * @param int $selectedProgramID * @access public * @return void */ public function ajaxGetProjectFormInfo(string $objectType, int $objectID, int $selectedProgramID) { /* If selectedProgramID exist, get the info for the program. */ $projectFormInfo = array(); 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' && helper::hasFeature('program') ? true : false; $allProducts = array('') + $this->loadModel('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 dashboard view. * * @param int $projectID * @param string $browseType * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function index(int $projectID = 0, string $browseType = 'all', int $recTotal = 0, int $recPerPage = 15, int $pageID = 1) { $projectID = $this->project->checkAccess($projectID, $this->project->getPairsByProgram()); if(!$projectID) { if(is_bool($projectID)) return $this->sendError($this->lang->project->accessDenied, inLink('browse')); if(common::hasPriv('project', 'create')) $this->locate(inLink('create')); $this->locate(inLink('browse')); } $this->session->set('executionList', $this->app->getURI(true), 'execution'); $project = $this->project->getByID($projectID); if(empty($project) || $project->type != 'project') return $this->sendError($this->lang->notFound, inLink('browse')); $this->project->setMenu($projectID); $this->loadModel('execution'); /* Locate to task when set no execution. */ if(!$project->multiple) { $executions = $this->execution->getList($project->id); foreach($executions as $execution) { if(!$execution->multiple) $this->locate($this->createLink('execution', 'task', "executionID={$execution->id}") . '#app=project'); } } if($project->model != 'kanban' || $this->config->vision == 'lite') return print($this->fetch('block', 'dashboard', "dashboard={$project->model}project&projectID={$projectID}")); /* Load pager and get kanban list. */ $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); $this->view->title = $this->lang->project->common . $this->lang->hyphen . $this->lang->project->index; $this->view->pager = $pager; $this->view->project = $project; $this->view->browseType = $browseType; $this->view->actionList = array_merge(array('edit'), $this->config->execution->statusActions, array('delete')); $this->view->kanbanList = $this->execution->getList($projectID, 'all', $browseType, 0, 0, 0, $pager); $this->view->usersAvatar = $this->loadModel('user')->getAvatarPairs('all'); $this->view->userIdPairs = $this->user->getPairs('nodeleted|showid|all'); $this->view->memberGroup = $this->execution->getMembersByIdList(array_keys($this->view->kanbanList)); $this->display(); } /** * 渲染项目列表页面数据。 * 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', 'bysearch'))) 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, $programTitle, false, $pager); $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($browseType); $this->view->browseType = $browseType; $this->view->param = $param; $this->view->orderBy = $orderBy; $this->view->showBatchEdit = $this->cookie->showProjectBatchEdit; $this->view->projectType = $this->cookie->projectType && !commonModel::isTutorialMode() ? $this->cookie->projectType : 'bylist'; $this->view->programs = array(0 => $this->lang->program->all) + $this->program->getParentPairs(); $this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted'); $this->view->userIdPairs = $this->loadModel('user')->getPairs('nodeleted|showid'); $this->view->usersAvatar = $this->user->getAvatarPairs(); $this->display(); } /** * 项目看板。 * Project kanban. * * @access public * @return void */ public function kanban() { $this->view->title = $this->lang->project->kanban; $this->view->kanbanList = $this->projectZen->getKanbanData(); $this->display(); } /** * 设置展示项目集的层级。 * Sets the level of the display program. * * @access public * @return void */ public function programTitle() { $this->loadModel('setting'); if($_POST) { $this->setting->setItem($this->app->user->account . '.project.programTitle', $this->post->programTitle); return $this->send(array('result' => 'success', 'load' => true)); } $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(string $model = 'scrum', int $programID = 0, int $copyProjectID = 0, string $extra = '') { $this->session->set('projectModel', $model); if($model == 'kanban') unset($this->lang->project->authList['reset']); $extra = str_replace(array(',', ' '), array('&', ''), $extra); parse_str($extra, $output); if(!empty($output['showTips']) && !empty($output['project'])) { $this->displayAfterCreated((int)$output['project']); return; } $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0; if($_POST) { if($this->post->longTime || $this->post->LONG_TIME) $this->config->project->form->create['end']['skipRequired'] = true; if(!empty($_POST['products']) && $_POST['products'][0] != '') $_POST['hasProduct'] = '1'; $postData = form::data($this->config->project->form->create, 0, $workflowGroup); $project = $this->projectZen->prepareCreateExtras($postData, $copyProjectID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $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) && $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(in_array($model, array('waterfall', 'waterfallplus', 'ipd'))) { $productID = $this->loadModel('product')->getProductIDByProject($projectID, true); $session = $this->createLink('programplan', 'browse', "projectID=$projectID&productID=$productID&type=lists", '', false, $projectID); if(in_array($this->config->edition, array('max', 'ipd'))) $session = $this->createLink('project', 'execution', "status=undone&projectID=$projectID", '', false); $this->session->set('projectPlanList', $session, 'project'); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink('programplan', 'create', "projectID=$projectID&productID=0&planID=0&executionType=stage&from=projectCreate", '', false, $projectID))); } return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('create', "model=$model&programID=$programID©ProjectID=$projectID&extra=showTips=1,project=$projectID"))); } $this->projectZen->buildCreateForm((string)$model, (int)$programID, (int)$copyProjectID, (string)$extra); $this->display('project', 'create'); } /** * Edit a project. * * @param int $projectID * @param string $from * @param int $programID * @param string $extra * @access public */ public function edit(int $projectID, string $from = '', int $programID = 0, string $extra = '') { $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; } $extra = str_replace(array(',', ' '), array('&', ''), $extra); parse_str($extra, $output); $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0; if($_POST) { if($this->post->longTime) $this->config->project->form->edit['end']['skipRequired'] = true; if($this->post->delta == '999') $this->config->project->form->edit['end']['skipRequired'] = true; $postData = form::data($this->config->project->form->edit, $projectID, $workflowGroup); $postProductData = !empty($project->hasProduct) ? form::data($this->config->project->form->edit)->get('products,plans,branch') : new stdclass(); $newProject = $this->projectZen->prepareProject($postData, $project->hasProduct); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $changes = $this->project->update($newProject, $project, $postProductData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes) { $actionID = $this->loadModel('action')->create('project', $projectID, 'edited'); $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($projectID); if($message) $this->lang->saveSuccess = $message; if(isInModal()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true, 'closeModal' => true)); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink('project', 'view', "projectID=$projectID"))); } if(!empty($output['workflowGroup'])) $project->workflowGroup = $workflowGroup; if(!empty($output['model'])) $project->model = $output['model']; if(!empty($output['category'])) $project->category = $output['category']; $this->projectZen->buildEditForm($projectID, $project, $from, $programID); $this->display(); } /** * Batch edit projects. * * @access public * @return void */ public function batchEdit() { $this->loadModel('action'); $this->loadModel('execution'); if($this->post->name) { $projects = form::batchData($this->config->project->form->batchedit)->get(); $allChanges = $this->project->batchUpdate($projects); 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, 'load' => $locateLink)); } if(!$this->post->projectIdList) { /* Use a fallback link to locate in case session has no related data. */ $locateLink = !empty($this->session->projectList) ? $this->session->projectList : $this->createLink('project', 'browse'); return $this->locate($locateLink); } $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(); $appendPMUsers = array(); foreach($projects as $project) { if(!isset($programs[$project->parent]) and !in_array($project->parent, $unauthorizedIDList)) $unauthorizedIDList[] = $project->parent; $appendPMUsers[$project->PM] = $project->PM; } $this->view->title = $this->lang->project->batchEdit; $this->view->projects = $projects; $this->view->programs = $programs; $this->view->unauthorizedPrograms = $this->program->getPairsByList($unauthorizedIDList); $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(int $projectID = 0) { $this->app->loadLang('build'); $project = $this->project->fetchByID($projectID); if(!empty($project->deleted)) return $this->sendError($this->lang->project->deletedTip, $this->createLink('project', 'browse')); if(!defined('RUN_MODE') || RUN_MODE != 'api') $projectID = $this->project->checkAccess((int)$projectID, $this->project->getPairsByProgram()); if(is_bool($projectID)) return $this->send(array('result' => 'success', 'load' => array('alert' => $this->lang->project->accessDenied, 'locate' => $this->createLink('project', 'browse')))); $this->session->set('teamList', $this->app->getURI(true), 'project'); $projectID = $this->project->setMenu($projectID); $project = $this->project->getById((int)$projectID); if(empty($project) || strpos('scrum,waterfall,kanban,agileplus,waterfallplus,ipd', $project->model) === false) { if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'code' => 404, 'message' => '404 Not found')); return $this->send(array('result' => 'success', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('project', 'browse')))); } $projectBranches = $this->project->getBranchesByProject($projectID); $linkedProductIdList = empty($projectBranches) ? '' : array_keys($projectBranches); $products = $this->loadModel('product')->getProducts($projectID, 'all', '', true, $linkedProductIdList, false); $linkedBranches = array(); foreach($products as $product) { $product->roadmaps = ''; if(isset($product->branches)) { foreach($product->branches as $branchID) { $linkedBranches[$branchID] = $branchID; $product->roadmaps .= !empty($projectBranches[$product->id][$branchID]->roadmap) ? ',' . trim($projectBranches[$product->id][$branchID]->roadmap, ',') : ''; } } $product->roadmaps = trim($product->roadmaps, ','); } if($this->config->edition != 'open') { $charter = $this->loadModel('charter')->getByID($project->charter); $this->view->charter = $charter; if($project->linkType == 'roadmap') { $productRoadmaps = $this->charter->getGroupDataByID($project->charter); $this->view->roadmaps = !empty($charter) ? $productRoadmaps : array(); } } $this->executeHooks($projectID); list($userPairs, $userList) = $this->projectZen->buildUsers(); if($this->config->edition != 'open' && !empty($project->workflowGroup)) { $this->view->workflowGroup = $this->loadModel('workflowgroup')->getByID((int)$project->workflowGroup); } $this->view->title = $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->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(array_keys($products)); $this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), 'ignoreNormal', $linkedBranches); $this->view->dynamics = $this->loadModel('action')->getDynamic('all', 'all', 'date_desc', 50, 'all', $projectID); $this->view->programList = $this->loadModel('program')->getPairsByList(toIntArray($project->path)); $this->view->users = $userPairs; $this->view->userList = $userList; $this->display(); } /** * Project browse groups. * * @param int $projectID * @param int $programID * @access public * @return void */ public function group(int $projectID = 0, int $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->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(int $projectID) { $this->loadModel('group'); if(!empty($_POST)) { $this->lang->project->name = $this->lang->group->name; $group = form::data($this->config->group->form->create)->get(); $group->project = $projectID; $this->group->create($group); if(dao::isError()) return $this->sendError(dao::getError()); return $this->sendSuccess(array('load' => true)); } $this->view->title = $this->lang->group->create; $this->display('group', 'create'); } /** * 项目动态。 * Project dynamic. * * @param int $projectID * @param string $type * @param string $param * @param int $recTotal * @param int $date * @param string $direction next|pre * @access public * @return void */ public function dynamic(int $projectID = 0, string $type = 'today', string $param = '', int $recTotal = 0, int $date = 0, string $direction = 'next') { if(empty($type)) $type = 'today'; $this->loadModel('execution'); $this->project->setMenu($projectID); /* Append id for second sort. */ $orderBy = $direction == 'next' ? 'date_desc' : 'date_asc'; /* 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')->getDynamicByProject($projectID, $account, $period, $orderBy, 50, $date, $direction); $dateGroups = $this->action->buildDateGroup($actions, $direction); if(empty($recTotal) && $dateGroups) $recTotal = $this->action->getDynamicCount($period); /* The header and position. */ $project = $this->project->getByID($projectID); $this->view->title = $project->name . $this->lang->hyphen . $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->account = $account; $this->view->param = $param; $this->view->dateGroups = $dateGroups; $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 * @param int $queryID * @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, int $queryID = 0) { $projects = $this->project->getPairsByProgram(); $projectID = $this->project->checkAccess($projectID, $projects); if(is_bool($projectID)) return $this->send(array('result' => 'fail', 'message' => $this->lang->project->accessDenied, 'locate' => inlink('browse'))); $this->loadModel('execution'); $this->loadModel('task'); $this->loadModel('programplan'); $this->loadModel('product'); $this->session->set('executionList', $this->app->getURI(true), 'project'); $this->session->set('projectPlanList', $this->app->getURI(true), 'project'); if($this->cookie->showTask) $this->session->set('taskList', $this->app->getURI(true), 'project'); $project = $this->project->getByID($projectID); $this->project->setMenu($projectID); if(!$projectID) return $this->locate(inlink('browse')); if(!$project->multiple) { $executionID = $this->execution->getNoMultipleID($projectID); if(defined('RUN_MODE') && RUN_MODE == 'api') { $this->view->executionStats = array($this->execution->getByID($executionID)); return $this->display(); } return $this->locate($this->createLink('execution', 'task', "executionID=$executionID")); } if(!empty($project->model) && $project->model == 'kanban' && !(defined('RUN_MODE') && RUN_MODE == 'api')) return $this->locate(inlink('index', "projectID=$projectID")); /* Load pager and get tasks. */ $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); $sort = $orderBy; if(strpos($sort, 'rawID_') !== false) $sort = str_replace('rawID_', 'id_', $sort); if(strpos($sort, 'nameCol_') !== false) $sort = str_replace('nameCol_', 'name_', $sort); if($this->cookie->showTask) { /* Build the search form. */ $actionURL = $this->createLink('project', 'execution', "status=bysearch&projectID=$projectID&orderBy=$orderBy&productID=$productID&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID&queryID=myQueryID"); $executions = $this->execution->fetchExecutionList($projectID, 'all', $productID); $executions = $this->execution->getPairsByList(array_keys($executions)); $this->execution->buildTaskSearchForm($projectID, $executions, $queryID, $actionURL, 'projectTask'); } $this->view->title = $this->lang->execution->allExecutions; $this->view->executionStats = $this->projectZen->getExecutionStats($status, $projectID, isset($executions) ? array_keys($executions) : array(), $productID, $queryID, $sort, $pager); $this->view->productList = array(0 => $this->lang->product->all) + $this->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->status = $status; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->isStage = isset($project->model) && (in_array($project->model, array('waterfall', 'waterfallplus', 'ipd'))); $this->view->avatarList = $this->user->getAvatarPairs(''); $this->view->queryID = $queryID; $this->view->hasFrozenExecutions = $this->project->hasFrozenObject($project->id, 'PP'); $this->display(); } /** * Project bug list. * * @param int $projectID * @param int $productID * @param string $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, string $branchID = 'all', string $orderBy = 'status,id_desc', int $build = 0, string $type = 'all', int $param = 0, int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { /* Load models. */ $this->loadModel('product'); $this->loadModel('datatable'); $this->loadModel('search'); $this->project->setMenu($projectID); $this->session->set('bugList', $this->app->getURI(true), 'project'); $projectID = (int)$projectID; $product = $this->product->getById($productID); $project = $this->project->getByID($projectID); $type = strtolower($type); $products = $this->product->getProducts($projectID); $this->session->set('currentProductType', !isset($product->type) ? 'normal' : $product->type); if($this->session->currentProductType != 'normal') $this->config->bug->dtable->fieldList['branch']['title'] = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]); if($this->session->currentProductType == 'normal') unset($this->config->bug->dtable->fieldList['branch']); /* 项目型项目不展示2.5级产品下拉菜单。 */ if(!$project->hasProduct) $this->config->excludeSwitcherList[] = 'project-bug'; /* 处理无产品项目、无迭代项目的bug搜索参数,构造搜索表单。 */ $this->projectZen->processBugSearchParams($project, $type, $param, $projectID, $productID, $branchID, $orderBy, $build, $products); /* team member pairs. */ $memberPairs = array(); $memberPairs[''] = $this->lang->search->null; $teamMembers = $this->project->getTeamMembers($projectID); foreach($teamMembers as $key => $member) $memberPairs[$key] = $member->realname; $this->view->memberPairs = $memberPairs; /* 准备分支数据。 */ $this->projectZen->prepareBranchForBug($products, $productID); /* 准备模块数据。 */ $this->projectZen->prepareModuleForBug($productID, $projectID, $type, $param, $orderBy, $build, $branchID, $products); /* 构造必要的变量到视图。 */ $this->projectZen->buildBugView($productID, $projectID, $project, $type, $param, $orderBy, $build, $branchID, $products, $recTotal, $recPerPage, $pageID); } /** * 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) { /* 项目型项目不展示2.5级产品下拉菜单。 */ $project = $this->project->getByID($projectID); if(!$project->hasProduct) { $this->config->excludeSwitcherList[] = 'project-testcase'; $productPairs = $this->loadModel('product')->getProductPairsByProject($projectID); $productID = key($productPairs); } 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(int $projectID = 0, string $objectType = 'project', string $extra = '', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $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 int $productID * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function testtask(int $projectID = 0, int $productID = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { $this->session->set('testtaskList', $this->app->getURI(true), 'project'); $this->project->setMenu($projectID); /* Load pager. */ $this->app->loadClass('pager', true); $pager = pager::init($recTotal, $recPerPage, $pageID); $project = $this->project->getByID($projectID); $tasks = $this->loadModel('testtask')->getProjectTasks($projectID, $productID, $orderBy, $pager); $this->projectZen->assignTesttaskVars($tasks); $this->view->title = $project->name . $this->lang->hyphen . $this->lang->project->common; $this->view->project = $project; $this->view->productID = $productID; $this->view->projectID = $projectID; $this->view->pager = $pager; $this->view->orderBy = $orderBy; $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); $this->view->products = $this->loadModel('product')->getProducts($projectID, 'all', '', false); $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(int $projectID = 0, string $type = 'all', int $param = 0, string $orderBy = 't1.date_desc,t1.id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { /* Set menu lang. */ $this->project->setMenu($projectID); $project = $this->project->getByID($projectID); if($type == 'product') $this->session->set('buildProductID', $param); if(!$this->session->buildProductID) $this->session->set('buildProductID', $param); /* Build the search form. */ $type = strtolower($type); $products = $this->loadModel('product')->getProducts($projectID, 'all', '', false); $this->project->buildProjectBuildSearchForm($products, $type == 'bysearch' ? (int)$param : 0, $projectID, $this->session->buildProductID, 'project'); /* Build the search form. */ $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); /* Get builds. */ $this->loadModel('build'); if($type == 'bysearch') { $builds = $this->build->getProjectBuildsBySearch((int)$projectID, (int)$param, $orderBy, $pager); } else { $builds = $this->build->getProjectBuilds((int)$projectID, $type, (string)$param, $orderBy, $pager); } /* Set view data. */ $this->view->title = $project->name . $this->lang->hyphen . $this->lang->execution->build; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->builds = $this->projectZen->processBuildListData($builds, $projectID); $this->view->productID = $this->session->buildProductID; $this->view->project = $project; $this->view->products = $products; $this->view->system = $this->loadModel('system')->getPairs(); $this->view->type = $type; $this->view->orderBy = $orderBy; $this->view->param = $param; $this->view->pager = $pager; $this->display(); } /** * Manage privleges of a group. * * @param int $projectID * @param int $groupID * @param string $type * @access public * @return void */ public function managePriv(int $projectID, int $groupID = 0, string $type = 'byPackage') { $this->loadModel('group'); foreach($this->lang->resource as $moduleName => $action) $this->app->loadLang($moduleName); if(!empty($_POST)) { $result = $this->group->updatePrivByGroup($groupID, '', ''); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('group', "projectID={$projectID}"))); } $group = $this->group->getById($groupID); $this->project->setMenu($projectID); $this->group->sortResource(); $project = $this->project->getByID((int)$projectID); $this->projectZen->processGroupPrivs($project); $this->lang->resource = $this->project->getPrivsByModel($project->multiple ? $project->model : 'noSprint', $projectID, $project->hasProduct); $getPrivs = $this->group->getPrivs($groupID); foreach($getPrivs as $moduleName => $actions) { if(!isset($this->lang->resource->$moduleName)) { unset($getPrivs[$moduleName]); } else { foreach($actions as $method => $value) { if(!isset($this->lang->resource->{$moduleName}->{$method})) unset($getPrivs[$moduleName][$method]); } } } /* Subsets . */ $subsets = array(); foreach($this->config->group->subset as $subsetName => $subset) { $subset->code = $subsetName; $subset->allCount = 0; $subset->selectCount = 0; $subsets[$subset->code] = $subset; } $selectPrivs = $this->group->getPrivsByGroup($groupID); $allPrivList = $this->group->getPrivsByNav(); $selectedPrivList = array(); $packages = array(); foreach($allPrivList as $privCode => $priv) { $subsetCode = $priv->subset; $packageCode = $priv->package; if(!isset($packages[$subsetCode])) $packages[$subsetCode] = array(); if(!isset($subsets[$subsetCode])) { $subset = new stdclass(); $subset->code = $subsetCode; $subset->allCount = 0; $subset->selectCount = 0; $subsets[$subsetCode] = $subset; } if(!isset($packages[$subsetCode][$packageCode])) { $package = new stdclass(); $package->allCount = 0; $package->selectCount = 0; $package->subset = $subsetCode; $package->privs = array(); $packages[$subsetCode][$packageCode] = $package; } $packages[$subsetCode][$packageCode]->privs[$privCode] = $priv; $packages[$subsetCode][$packageCode]->allCount ++; $subsets[$subsetCode]->allCount ++; if(isset($selectPrivs[$privCode])) { $packages[$subsetCode][$packageCode]->selectCount ++; $subsets[$subsetCode]->selectCount ++; $selectedPrivList[] = $privCode; } } $allPrivList = array_keys($allPrivList); $relatedPrivData = $this->group->getRelatedPrivs($allPrivList, $selectedPrivList); $this->view->allPrivList = $allPrivList; $this->view->selectedPrivList = $selectedPrivList; $this->view->relatedPrivData = $relatedPrivData; $this->view->title = $group->name . $this->lang->hyphen . $this->lang->group->managePriv; $this->view->subsets = $subsets; $this->view->packages = $packages; $this->view->group = $group; $this->view->groupPrivs = $getPrivs; $this->view->groupID = $groupID; $this->view->projectID = $projectID; $this->view->project = $project; $this->view->type = $type; $this->display(); } /** * Browse team of a project. * * @param int $projectID * @access public * @return void */ public function team(int $projectID = 0) { $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->hyphen . $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->view->recTotal = count($this->view->teamMembers); $this->display(); } /** * 移除项目成员。 * Unlink a memeber. * * @param int $projectID * @param int $userID * @param string $removeExecution yes|no * @access public * @return void */ public function unlinkMember(int $projectID, int $userID, string $removeExecution = 'no') { $user = $this->loadModel('user')->getById($userID, 'id'); $account = $user->account; $this->project->unlinkMember($projectID, $account, $removeExecution == 'yes'); /* if ajax request, send result. */ if(dao::isError()) return $this->sendError(dao::getError()); $this->loadModel('execution')->getLimitedExecution(); return $this->send(array('result' => 'success', 'load' => $this->createLink($this->config->vision == 'or' ? 'marketresearch' : 'project', 'team', "projectID={$projectID}"))); } /** * Manage project members. * * @param int $projectID * @param mixed $dept * @param int $copyProjectID * @access public * @return void */ public function manageMembers(int $projectID, mixed $dept = '', int $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)) { $members = form::batchData($this->config->project->form->manageMembers)->get(); $this->project->manageMembers($projectID, $members); if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError())); $this->execution->getLimitedExecution(); if(empty($project->multiple)) { $executionID = $this->execution->getNoMultipleID($projectID); $execution = $this->execution->getByID($executionID); if($executionID) $this->execution->manageMembers($execution, $members); } return $this->send(array('message' => $this->lang->saveSuccess, 'result' => 'success', 'load' => $this->createLink('project', 'team', "projectID=$projectID"))); } $users = $this->user->getPairs('noclosed|nodeleted|devfirst'); $roles = $this->user->getUserRoles(array_keys($users)); $deptUsers = $dept === '' ? array() : $this->dept->getDeptUserPairs((int)$dept); $executions = $this->project->getExecutionList(array($projectID)); $executionTeams = $this->execution->getMembersByIdList(array_keys($executions)); $executionMembers = array(); foreach($executionTeams as $executionID => $executionTeam) { $executionMembers = array_merge($executionMembers, array_keys($executionTeam)); } $currentMembers = $this->project->getTeamMembers($projectID); $members2Import = $this->project->getMembers2Import($copyProjectID, array_keys($currentMembers)); $userItems = array(); foreach($users as $account => $realName) $userItems[$account] = array('value' => $account, 'text' => $realName, 'keys' => $account, 'disabled' => false); $this->view->title = $this->lang->project->manageMembers . $this->lang->hyphen . $project->name; $this->view->project = $project; $this->view->users = $users; $this->view->userItems = $userItems; $this->view->roles = $roles; $this->view->dept = $dept; $this->view->depts = $this->dept->getOptionMenu(); $this->view->teams2Import = $this->loadModel('personnel')->getCopiedObjects($projectID, 'project', true); $this->view->currentMembers = $currentMembers; $this->view->copyProjectID = $copyProjectID; $this->view->teamMembers = $this->projectZen->buildMembers($currentMembers, $members2Import, $deptUsers, $project->days); $this->view->executionMembers = array_unique($executionMembers); $this->display(); } /** * Manage members of a group. * * @param int $groupID * @param int $deptID * @access public * @return void */ public function manageGroupMember(int $groupID, int $deptID = 0) { $this->loadModel('group'); if($this->server->request_method == 'POST') { $this->group->updateUser($groupID); return $this->send(array('result' => 'success', 'load' => true, 'closeModal' => true)); } $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->title = $group->name . $this->lang->hyphen . $this->lang->group->manageMember; $this->view->group = $group; $this->view->deptTree = $this->loadModel('dept')->getTreeMenu(0, array('deptModel', 'createGroupManageMemberLink'), (int)$groupID); $this->view->groupUsers = $groupUsers; $this->view->otherUsers = $otherUsers; $this->view->deptID = $deptID; $this->view->noUsers = empty($groupUsers) && empty($otherUsers); $this->view->outsideUsers = array_diff_assoc($outsideUsers, $groupUsers); $this->display('group', 'manageMember'); } /** * 复制项目权限组。 * Project copy a group. * * @param int $groupID * @access public * @return void */ public function copyGroup(int $groupID) { $group = $this->loadModel('group')->getByID($groupID); if(!empty($_POST)) { $this->lang->project->name = $this->lang->group->name; $newGroup = form::data($this->config->group->form->copy)->get(); $newGroup->project = $group->project; $this->group->copy($groupID, $newGroup, (array)$this->post->options); if(dao::isError()) return $this->sendError(dao::getError()); return $this->sendSuccess(array('load' => true)); } $this->view->title = $this->lang->company->orgView . $this->lang->hyphen . $this->lang->group->copy; $this->view->group = $group; $this->display('group', 'copy'); } /** * 编辑项目权限组。 * Project edit a group. * * @param string $groupID * * @access public * @return void */ public function editGroup(int $groupID) { $this->loadModel('group'); if(!empty($_POST)) { $this->lang->project->name = $this->lang->group->name; $group = form::data($this->config->group->form->edit)->get(); $this->group->update($groupID, $group); if(dao::isError()) return $this->sendError(dao::getError()); return $this->sendSuccess(array('load' => true)); } $this->view->title = $this->lang->company->orgView . $this->lang->hyphen . $this->lang->group->edit; $this->view->group = $this->group->getByID($groupID); $this->display('group', 'edit'); } /** * 开始一个项目。 * Start a project. * * @param int $projectID * @access public * @return void */ public function start(int $projectID) { $project = $this->project->getByID($projectID); if(!empty($_POST)) { $postData = form::data($this->config->project->form->start, $projectID); $postData = $this->projectZen->prepareStartExtras($postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $changes = $this->project->start($projectID, $postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $this->projectZen->responseAfterStart($project, $changes, $this->post->comment); return $this->sendSuccess(array('closeModal' => true, 'load' => true)); } $this->projectZen->buildStartForm($project); } /** * 挂起一个项目。 * Suspend a project. * * @param int $projectID * * @access public * @return void */ public function suspend(int $projectID) { /* Processing parameter passing while suspend project. */ if(!empty($_POST)) { $postData = form::data($this->config->project->form->suspend, $projectID); $postData = $this->projectZen->prepareSuspendExtras($projectID, $postData); /* Update the database status to suspended. */ $changes = $this->project->suspend($projectID, $postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => 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 $this->sendSuccess(array('closeModal' => true, 'load' => true)); } $this->app->loadLang('build'); $this->projectZen->buildSuspendForm($projectID); } /** * 关闭一个项目。 * Close a project. * * @param int $projectID * @access public * * @return void */ public function close(int $projectID) { /* Processing parameter passing while close project. */ if(!empty($_POST)) { $postData = form::data($this->config->project->form->close, $projectID); $postData = $this->projectZen->prepareClosedExtras($projectID, $postData); /* Update the database status to closed. */ $changes = $this->project->close($projectID, $postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); /* Process the data returned after the closed. */ $comment = strip_tags((string)$this->post->comment, $this->config->allowedTags); $this->projectZen->responseAfterClose($projectID, $changes, $comment); return $this->sendSuccess(array('closeModal' => true, 'load' => 'table')); } $this->app->loadLang('build'); $this->projectZen->buildClosedForm($projectID); } /** * 激活项目并更新其状态。 * Activate a project. * * @param int $projectID * @access public * * @return void */ public function activate(int $projectID) { $project = $this->project->getByID($projectID); if(!empty($_POST)) { $postData = form::data($this->config->project->form->activate, $projectID); $postData = $this->projectZen->prepareActivateExtras($projectID, $postData); $changes = $this->project->activate($projectID, $postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $this->projectZen->responseAfterActivate($projectID, $changes); return $this->sendSuccess(array('closeModal' => true, 'load' => true)); } $this->app->loadLang('build'); $this->projectZen->buildActivateForm($project); } /** * 删除一个项目,并弹窗确认。 * Delete a project and confirm. * * @param string $projectID * * @access public * @return void */ public function delete(int $projectID) { $projectID = (int)$projectID; $project = $this->project->getByID($projectID); $this->project->delete(TABLE_PROJECT, $projectID); $this->project->deleteByTableName('zt_doclib', $projectID); $this->loadModel('user')->updateUserView(array($projectID), 'project'); $response['result'] = 'success'; $response['closeModal'] = true; $response['load'] = $this->app->tab == 'my' ? true : inlink('browse'); $message = $this->executeHooks($projectID); if($message) $response['message'] = $message; /* Delete the execution and product under the project. */ $executionIdList = $this->loadModel('execution')->getPairs($projectID); if(!empty($executionIdList)) $this->projectZen->removeAssociatedExecutions($executionIdList); $this->projectZen->removeAssociatedProducts($project); $this->session->set('project', ''); return $this->send($response); } /** * 更新项目排列序号。 * 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 int $projectID * @param string $from project|program|programProject * @param int $recTotal * @param int $recPerPage * @param int $pageID * * @access public * @return void */ public function whitelist(int $projectID = 0, string $from = 'project', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { $projectID = $this->project->setMenu($projectID); if(!$projectID) return $this->locate(inLink('browse')); $project = $this->project->getByID($projectID); if(isset($project->acl) and $project->acl == 'open') return $this->sendError($this->lang->whitelistNotNeed, inLink('browse')); 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 int $projectID * @param int $deptID * @param int $copyID * @param int $programID * @param string $from * * @access public * @return void */ public function addWhitelist(int $projectID = 0, int $deptID = 0, int $copyID = 0, int $programID = 0, string $from = 'project') { $projectID = $this->project->setMenu($projectID); $project = $this->project->getByID($projectID); if(isset($project->acl) and $project->acl == 'open') return $this->sendError($this->lang->whitelistNotNeed, $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 int $id * @param string $confirm * * @access public * @return void */ public function unbindWhitelist(int $id = 0, $confirm = 'no') { echo $this->fetch('personnel', 'unbindWhitelist', "id=$id&confirm=$confirm"); } /** * 管理项目关联产品。 * Manage products of project. * * @param string $projectID * @access public * @return void */ public function manageProducts(string $projectID) { /* 如果是无产品项目,则返回。*/ /* If hasProduct is 0, return. */ $projectID = (int)$projectID; $project = $this->project->getById($projectID); if(!$project->hasProduct) return $this->sendError($this->lang->project->cannotManageProducts, true); $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(empty($postProducts) && empty($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. */ return $this->sendSuccess(array('closeModal' => true, 'load' => true)); } /* 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->hyphen . $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 int $projectID * @param string $mode * @param string $type all|sprint|stage|kanban * @param string $pageType old * * @access public * @return void */ public function ajaxGetExecutions(int $projectID, string $mode = '', string $type = 'all', string $pageType = '') { $executions = array(); if($projectID) $executions = (array)$this->loadModel('execution')->getPairs($projectID, $type, $mode); $project = $this->project->getByID($projectID); if($pageType == 'old') { $disabled = empty($project->multiple) ? 'disabled' : ''; return print(html::select('execution', array(0 => '') + $executions, '', "class='form-control $disabled' $disabled")); } $data = array(); $data['items'] = array(); $data['multiple'] = empty($project->multiple) ? false : true; foreach($executions as $id => $name) $data['items'][] = array('text' => $name, 'value' => $id, 'keys' => $name); echo json_encode($data); } /** * 根据执行ID获取对应的项目下拉列表。 * AJAX: get a project by execution id. * * @param string $executionID * @access public * @return string */ public function ajaxGetPairsByExecution(int $executionID) { $execution = $this->loadModel('execution')->getByID($executionID); $projectPairs = $this->loadModel('project')->getPairsByIdList(array($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)'")); } } /** * 判断项目下是否有对应概念的需求。 * AJAX: check if the project has the concept of story. * * @param string $projectID * @param string $storyType * @access public * @return bool */ public function ajaxGetStoryByType($projectID, $storyType) { $story = $this->dao->select('t1.id')->from(TABLE_STORY)->alias('t1') ->leftJoin(TABLE_PROJECTSTORY)->alias('t2')->on('t1.id = t2.story') ->where('t1.deleted')->eq(0) ->andWhere('t2.project')->eq($projectID) ->andWhere('t1.type')->eq($storyType) ->fetch('id'); return print(!empty($story)); } /** * Ajax get product drop menu. * * @param int $projectID * @param int $productID * @access public * @return void */ public function ajaxSwitcherMenu(int $projectID, int $productID, string $currentMethod = '') { $currentModule = $currentMethod == 'zerocase' ? 'testcase' : 'project'; $params = $currentMethod == 'zerocase' ? "productID={id}&branchID=0&orderBy=id_desc&projectID={$projectID}" : "projectID={$projectID}&productID={id}"; $this->view->link = helper::createLink($currentModule, $currentMethod, $params); $this->view->productID = $productID; $this->view->products = $this->loadModel('product')->getProducts($projectID); $this->view->projectID = $projectID; $this->view->currentMethod = $currentMethod; $this->display(); } /** * 检查是否同步阶段数据。 * AJAX: check whether stage data is sync. * * @param int $executionID * @access public * @return void */ public function ajaxCheckHasStageData(int $executionID) { return print($this->project->hasStageData($executionID)); } }