* @package build * @version $Id: control.php 4992 2013-07-03 07:21:59Z chencongzhi520@gmail.com $ * @link https://www.zentao.net */ class build extends control { /** * 公共函数,设置产品型项目属性。 * Common actions. * * @param int $projectID * @access public * @return void */ public function commonActions(int $projectID = 0) { $hidden = ''; if($projectID) { $project = $this->loadModel('project')->getByID($projectID); if(!$project->hasProduct) $hidden = 'hide'; $this->view->multipleProject = $project->multiple; } $this->view->hidden = $hidden; $this->view->projectID = $projectID; } /** * 创建一个版本。 * Create a build. * * @param int $executionID * @param int $productID * @param int $projectID * @access public * @return void */ public function create(int $executionID = 0, int $productID = 0, int $projectID = 0) { $this->loadModel('execution'); $this->loadModel('project'); $this->loadModel('file'); /* Set menu. */ if($this->app->tab == 'project') { $this->project->setMenu($projectID); } elseif(in_array($this->app->tab, array('execution', 'qa'))) { $execution = $this->execution->getByID($executionID); $projectID = $execution ? $execution->project : 0; } if($this->app->tab == 'execution') $this->execution->setMenu($executionID); if(!empty($_POST)) { $build = $this->buildZen->buildBuildForCreate(); if(!empty($_FILES['buildFiles'])) $_FILES['files'] = $_FILES['buildFiles']; unset($_FILES['buildFiles']); if(dao::isError()) return $this->sendError(dao::getError()); if(commonModel::isTutorialMode()) return $this->sendSuccess(array('closeModal' => true)); // Fix bug #21095. $buildID = $this->build->create($build); if(dao::isError()) return $this->sendError(dao::getError()); $message = $this->executeHooks($buildID); if($message) $this->lang->saveSuccess = $message; if(helper::isAjaxRequest('modal')) return $this->sendSuccess(array('closeModal' => true, 'callback' => 'refreshExecutionBuild()')); return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID"), 'id' => $buildID)); } if(in_array($this->app->tab, array('execution', 'project'))) $this->session->set('project', $projectID); $status = empty($this->config->CRProduct) ? 'noclosed' : ''; $this->buildZen->assignCreateData($productID, $executionID, $projectID, $status); } /** * 编辑一个版本。 * Edit a build. * * @param int $buildID * @access public * @return void */ public function edit(int $buildID) { if(!empty($_POST)) { $build = $this->buildZen->buildBuildForEdit($buildID); $changes = $this->build->update($buildID, $build); if(dao::isError()) return $this->sendError(dao::getError()); $change[$buildID] = $changes; $this->unlinkOldBranch($change); if($changes) { $actionID = $this->loadModel('action')->create('build', $buildID, 'Edited'); if(!empty($changes)) $this->action->logHistory($actionID, $changes); } $message = $this->executeHooks($buildID); if($message) $this->lang->saveSuccess = $message; return $this->sendSuccess(array('locate' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID") . "#app={$this->app->tab}")); } $this->loadModel('execution'); $this->loadModel('product'); $build = $this->build->getById($buildID); /* Set menu. */ if($this->app->tab == 'project') $this->loadModel('project')->setMenu($build->project); if($this->app->tab == 'execution') { $this->execution->setMenu((int)$build->execution); $this->view->executionID = $build->execution; } $this->commonActions($build->project); $this->buildZen->assignEditData($build); } /** * 版本详情。 * View a build. * * @param int $buildID * @param string $type * @param string $link * @param string $param * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function view(int $buildID, string $type = 'story', string $link = 'false', string $param = '', string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 100, int $pageID = 1) { $build = $this->build->getByID($buildID, true); if(!$build) { 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('execution', 'all')))); } /* Load pager. */ $this->app->loadClass('pager', true); if($this->app->getViewType() == 'mhtml') $recPerPage = 10; $sort = common::appendOrder($orderBy); if(strpos($sort, 'pri_') !== false && $type == 'story') $sort = str_replace('pri_', 'priOrder_', $sort); $bugPager = new pager($type == 'bug' ? $recTotal : 0, $recPerPage, $type == 'bug' ? $pageID : 1); $generatedBugPager = new pager($type == 'generatedBug' ? $recTotal : 0, $recPerPage, $type == 'generatedBug' ? $pageID : 1); $this->buildZen->assignBugVarsForView($build, $type, $sort, $param, $bugPager, $generatedBugPager); $storyPager = new pager($type == 'story' ? $recTotal : 0, $recPerPage, $type == 'story' ? $pageID : 1); $this->buildZen->assignProductVarsForView($build, $type, $sort, $storyPager); /* Set menu. */ $this->buildZen->setMenuForView($build); $this->commonActions($build->project); $this->executeHooks($buildID); /* Assign. */ $this->view->canBeChanged = common::canBeChanged('build', $build); // Determines whether an object is editable. $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->build = $build; $this->view->actions = $this->loadModel('action')->getList('build', $buildID); $this->view->link = $link; $this->view->orderBy = $orderBy; $this->view->grades = $this->loadModel('story')->getGradePairs('story', 'all'); $this->view->showGrade = $this->config->edition == 'ipd'; $this->view->execution = $this->loadModel('execution')->getByID((int)$build->execution); $this->view->childBuilds = empty($build->builds) ? array() : $this->build->getByList(explode(',', $build->builds)); $this->view->productID = $build->product; $this->view->systemList = $this->loadModel('system')->getPairs(); $this->display(); } /** * 删除一个版本。 * Delete a build. * * @param int $buildID * @param string $from execution|project * @access public * @return void */ public function delete(int $buildID, string $from = 'execution') { $build = $this->build->getById($buildID); $this->build->delete(TABLE_BUILD, $buildID); $message = $this->executeHooks($buildID); if($message) $this->lang->saveSuccess = $message; if(dao::isError()) return $this->sendError(dao::getError()); $moduleName = $from == 'project' ? 'projectbuild' : $from; $methodName = $from == 'project' ? 'browse' : 'build'; return $this->sendSuccess(array('load' => $this->createLink($moduleName, $methodName, "executionID={$build->$from}"))); } /** * 重构获取产品下的版本下拉列表。 * AJAX: get builds of a product in html select. * * @param int $productID * @param string $varName the name of the select object to create * @param string $build build to selected * @param string $branch * @param string $type get all builds or some builds belong to normal releases and executions are not done. * @access public * @return string */ public function ajaxGetProductBuilds(int $productID, string $varName, string $build = '', string $branch = 'all', string $type = 'normal') { $isJsonView = $this->app->getViewType() == 'json'; if($varName == 'openedBuild' ) { $params = $type == 'all' ? 'withbranch,noreleased,noreplace' : 'noterminate,nodone,withbranch,noreleased'; $builds = $this->build->getBuildPairs(array($productID), $branch, $params, 0, 'project', $build); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } if($varName == 'openedBuilds' ) { $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,noreleased', 0, 'project', $build); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return $this->send($builds); } if($varName == 'resolvedBuild') { $params = $type == 'all' ? 'withbranch,noreleased' : 'noterminate,nodone,withbranch,noreleased'; $builds = $this->build->getBuildPairs(array($productID), $branch, $params, 0, 'project', $build); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } $builds = $this->build->getBuildPairs(array($productID), $branch, $type, 0, 'project', $build, false); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } /** * 获取项目下的版本下拉列表。 * AJAX: get builds of a project in html select. * * @param int $projectID * @param string $varName the name of the select object to create * @param string $build build to selected * @param string|int $branch * @param bool $needCreate if need to append the link of create build * @param string $type get all builds or some builds belong to normal releases and executions are not done. * @access public * @return string */ public function ajaxGetProjectBuilds(int $projectID, int $productID, string $varName, string $build = '', string|int $branch = 'all', $needCreate = false, $type = 'normal', int $system = 0) { $isJsonView = $this->app->getViewType() == 'json'; if($varName == 'openedBuild') { if(empty($projectID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type); $params = $type == 'all' ? 'noempty,withbranch,noreleased' : 'noempty,noterminate,nodone,withbranch,noreleased'; $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $projectID, 'project', $build); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } if($varName == 'resolvedBuild') { if(empty($projectID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type); $params = ($type == 'all') ? 'withbranch,noreleased' : 'noterminate,nodone,withbranch,noreleased'; $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $projectID, 'project', $build); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } if(empty($projectID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type); $builds = $this->build->getBuildPairs(array($productID), $branch, $type, $projectID, 'project', $build, false, (int)$system); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } /** * 获取执行下的版本下拉列表。 * AJAX: get builds of an execution in html select. * * @param int $executionID * @param int $productID * @param string $varName the name of the select object to create * @param string $build build to selected * @param string $branch * @param bool $needCreate if need to append the link of create build * @param string $type get all builds or some builds belong to normal releases and executions are not done. * @access public * @return string */ public function ajaxGetExecutionBuilds(int $executionID, int $productID, string $varName, string $build = '', string $branch = 'all', bool $needCreate = false, string $type = 'normal') { $isJsonView = $this->app->getViewType() == 'json'; if($varName == 'openedBuild') { if(empty($executionID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type); $params = ($type == 'all') ? 'noempty,noreleased' : 'noempty,noterminate,nodone,noreleased'; $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $executionID, 'execution', $build); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } if($varName == 'openedBuilds') { if(empty($executionID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type); $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,noreleased', $executionID, 'execution', $build); $buildList = array(); foreach($builds as $buildID => $buildName) $buildList[] = array('value' => $buildID, 'text' => $buildName); return $this->send($buildList); } if($varName == 'resolvedBuild') { if(empty($executionID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type); $params = ($type == 'all') ? ',noreleased' : 'noterminate,nodone,noreleased'; $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $executionID, 'execution', $build); if($isJsonView) return print(json_encode($builds)); return print(html::select($varName, $builds, $build, "class='form-control'")); } if($varName == 'testTaskBuild') { $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,notrunk', $executionID, 'execution', '', false); if($isJsonView) return print(json_encode($builds)); $builds = $this->build->addReleaseLabelForBuilds($productID, $builds); return print(json_encode($builds)); } if($varName == 'dropdownList') { $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,notrunk', $executionID, 'execution'); if($isJsonView) return print(json_encode($builds)); $list = "
"; foreach($builds as $buildID => $buildName) $list .= html::a(inlink('view', "buildID={$buildID}"), $buildName); $list .= '
'; return print($list); } } /** * 获取最后一次创建的版本。 * Ajax get last build. * * @param int $projectID * @param int $executionID * @access public * @return string */ public function ajaxGetLastBuild(int $projectID, int $executionID) { $lastBuild = $this->build->getLast($executionID, $projectID); if(!$lastBuild) return print(''); echo "
  " . $this->lang->build->last . ": " . $lastBuild->name . "
"; } /** * 版本关联需求。 * Link stories to build. * * @param int $buildID * @param string $browseType * @param int $param * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function linkStory(int $buildID = 0, string $browseType = '', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 100, int $pageID = 1) { if(!empty($_POST['stories'])) { if($this->post->stories) $this->build->linkStory($buildID, $this->post->stories); if(dao::isError()) return $this->sendError(dao::getError()); return $this->send(array('result' => 'success', 'load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story"))); } $this->session->set('storyList', $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story&link=true¶m=" . helper::safe64Encode("&browseType=$browseType&queryID=$param")), $this->app->tab); $build = $this->build->getById($buildID); $product = $this->loadModel('product')->getById($build->product); if($this->app->tab == 'project') { $this->loadModel('project')->setMenu($build->project); $this->view->projectID = $build->project; } if($build->execution) $this->loadModel('execution')->setMenu((int)$build->execution); /* Load pager. */ $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); /* Build search form. */ $this->buildZen->buildLinkStorySearchForm($build, $browseType == 'bySearch' ? (int)$param : 0, isset($product->type) ? $product->type : 'normal'); $this->loadModel('story'); $executionID = $build->execution ? (int)$build->execution : (int)$build->project; $excludeStoryIdList = $this->buildZen->getExcludeStoryIdList($build); if($browseType == 'bySearch') { $allStories = $this->story->getBySearch($build->product, $build->branch, (int)$param, $orderBy, $executionID, 'story', $excludeStoryIdList, '', $pager); } else { $allStories = $this->story->getExecutionStories($executionID, $build->product, $orderBy, 'byBranch', $build->branch, 'story', $excludeStoryIdList, $pager); } $checkedRows = array(); foreach($allStories as $story) { if(in_array($story->stage, array('developed', 'tested', 'closed'))) $checkedRows[] = $story->id; } $this->view->allStories = $allStories; $this->view->checkedRows = $checkedRows; $this->view->build = $build; $this->view->buildStories = empty($build->stories) ? array() : $this->story->getByList($build->stories); $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->browseType = $browseType; $this->view->param = $param; $this->view->pager = $pager; $this->view->grades = $this->story->getGradePairs('story', 'all'); $this->view->showGrade = $this->config->edition == 'ipd'; $this->view->orderBy = $orderBy; $this->display(); } /** * 解除需求关联。 * Unlink story. * * @param int $buildID * @param int $storyID * @access public * @return bool */ public function unlinkStory(int $buildID, int $storyID) { $this->build->unlinkStory($buildID, $storyID); $this->loadModel('action')->create($this->app->rawModule, $buildID, 'unlinkstory', '', $storyID); return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story"))); } /** * 检查并输出移除移除需求和Bug分支的提示信息。 * AJAX: Get unlinkBranch story and bug. * * @param int $buildID * @param int $newBranch * @access public * @return void */ public function ajaxGetBranch(int $buildID, int $newBranch) { $build = $this->build->getByID($buildID); if(!$build) return ''; $buildStories = $build->allStories ? $this->loadModel('story')->getByList($build->allStories) : array(); $buildBugs = $build->allBugs ? $this->loadModel('bug')->getByIdList($build->allBugs) : array(); $branchPairs = $this->loadModel('branch')->getPairs($build->product); $typeName = $this->lang->product->branchName[$build->productType]; $removeBranches = ''; foreach(explode(',', $build->branch) as $oldBranchID) { if($oldBranchID && strpos(",$newBranch,", ",$oldBranchID,") === false) $removeBranches .= "{$branchPairs[$oldBranchID]},"; } $unlinkStoryCounts = 0; $unlinkBugCounts = 0; if($build->branch) { foreach($buildStories as $storyID => $story) { if($story->branch && strpos(",$newBranch,", ",$story->branch,") === false) $unlinkStoryCounts ++; } foreach($buildBugs as $bugID => $bug) { if($bug->branch && strpos(",$newBranch,", ",$bug->branch,") === false) $unlinkBugCounts ++; } } if($unlinkStoryCounts && $unlinkBugCounts) { printf($this->lang->build->confirmChangeBuild, $typeName, trim($removeBranches, ','), $typeName, $unlinkStoryCounts, $unlinkBugCounts); } elseif($unlinkStoryCounts) { printf($this->lang->build->confirmRemoveStory, $typeName, trim($removeBranches, ','), $typeName, $unlinkStoryCounts); } elseif($unlinkBugCounts) { printf($this->lang->build->confirmRemoveBug, $typeName, trim($removeBranches, ','), $typeName, $unlinkBugCounts); } } /** * 批量解除需求关联。 * Batch unlink story. * * @param int $buildID * @access public * @return bool */ public function batchUnlinkStory(int $buildID) { if($this->post->storyIdList) $this->build->batchUnlinkStory($buildID, $this->post->storyIdList); return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story"))); } /** * 编辑版本时,解除跟未关联分支的需求和Bug的关联。 * Unlink story and bug when edit branch of build. * * @param array $changes * @access pulic * @return void */ public function unlinkOldBranch(array $changes) { foreach($changes as $buildID => $changeList) { $oldBranch = ''; $newBranch = ''; foreach($changeList as $change) { if($change['field'] == 'branch') { $oldBranch = $change['old']; $newBranch = $change['new']; break; } } $build = $this->build->getByID($buildID); $planStories = $build->allStories ? $this->loadModel('story')->getByList($build->allStories) : array(); $planBugs = $build->allBugs ? $this->loadModel('bug')->getByIdList($build->allBugs) : array(); if($oldBranch) { foreach($planStories as $storyID => $story) { if($story->branch and strpos(",$newBranch,", ",$story->branch,") === false) $this->build->unlinkStory($buildID, $storyID); } foreach($planBugs as $bugID => $bug) { if($bug->branch and strpos(",$newBranch,", ",$bug->branch,") === false) $this->build->unlinkBug($buildID, $bugID); } } } } /** * 版本关联Bug。 * Link bug. * * @param int $buildID * @param string $browseType * @param int $param * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function linkBug(int $buildID = 0, string $browseType = '', int $param = 0, int $recTotal = 0, int $recPerPage = 100, int $pageID = 1) { if(!empty($_POST['bugs'])) { $this->build->linkBug($buildID, $this->post->bugs, (array)$this->post->resolvedBy); return $this->send(array('result' => 'success', 'load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug"))); } $this->session->set('bugList', $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug&link=true¶m=" . helper::safe64Encode("&browseType=$browseType&queryID=$param")), 'qa'); /* Set menu. */ $build = $this->build->getByID($buildID); $product = $this->loadModel('product')->getByID($build->product); if($this->app->tab == 'project') { $this->loadModel('project')->setMenu($build->project); $this->view->projectID = $build->project; } if($build->execution) $this->loadModel('execution')->setMenu($build->execution); /* Build the search form. */ $queryID = $browseType == 'bySearch' ? $param : 0; $this->buildZen->buildLinkBugSearchForm($build, $queryID, isset($product->type) ? $product->type : 'normal'); /* Load pager. */ $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); $this->loadModel('bug'); $executionID = $build->execution ? $build->execution : $build->project; if($browseType == 'bySearch') { $allBugs = $this->bug->getBySearch('bug', $build->product, $build->branch, $build->project, $build->execution, $queryID, $build->allBugs, 'id_desc', $pager); } else { $allBugs = $this->bug->getExecutionBugs($executionID, 0, 'all', array($buildID), 'noclosed', 0, 'status_desc,id_desc', $build->allBugs, $pager); } $this->view->allBugs = $allBugs; $this->view->build = $build; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->browseType = $browseType; $this->view->param = $param; $this->view->pager = $pager; $this->display(); } /** * 解除Bug跟版本的关联关系。 * Unlink bug. * * @param int $buildID * @param int $bugID * @access public * @return void */ public function unlinkBug(int $buildID, int $bugID) { $this->build->unlinkBug($buildID, $bugID); return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug"))); } /** * 批量解除Bug跟版本的关联关系。 * Batch unlink bugs. * * @param int $buildID * @access public * @return void */ public function batchUnlinkBug(int $buildID) { if($this->post->bugIdList) $this->build->batchUnlinkBug($buildID, $this->post->bugIdList); return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug"))); } /** * 根据选择的产品获取应用列表。 * Get system list by product. * * @param int $productID * @access public * @return string */ public function ajaxGetSystemList(int $productID) { $systemList = $this->loadModel('system')->getPairs($productID, '0', 'active'); $system = array(); foreach($systemList as $systemID => $systemName) $system[] = array('text' => $systemName, 'value' => $systemID); echo json_encode($system); } /** * 根据选择的系统获取版本列表。 * Get build list by system. * * @param int $productID * @param int $systemID * @access public * @return void */ public function ajaxGetSystemBuilds(int $productID, int $systemID) { $builds = $this->build->getBuildPairs(array($productID), 'all', 'noterminate, nodone, notrunk', 0, 'product', '', false, (int)$systemID); $buildList = array(); foreach($builds as $buildID => $buildName) $buildList[] = array('text' => $buildName, 'value' => $buildID); echo json_encode($buildList); } }