* @package release * @link https://www.zentao.net */ class releaseZen extends release { /** * 构造待创建的发布数据。 * Build the release data to be create. * * @param int $productID * @param int $branch * @param int $projectID * @access protected * @return object|false */ protected function buildReleaseForCreate(int $productID, int $branch, int $projectID = 0): object|false { $productID = $this->post->product ? $this->post->product : $productID; $branch = $this->post->branch ? $this->post->branch : $branch; $newSystem = $this->post->newSystem; if(empty($projectID)) { $product = $this->loadModel('product')->getById($productID); if($product->shadow) { $projectID = $this->dao->select('t2.id')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2') ->on('t1.project=t2.id') ->where('t1.product')->eq($productID) ->andWhere('t2.type')->eq('project') ->fetch('id'); } } if(!$newSystem && !$this->post->system) $this->config->release->form->create['system']['required'] = true; if($newSystem && !$this->post->systemName) { $this->config->release->form->create['systemName'] = array('type' => 'string', 'required' => true, 'filter' => 'trim'); $this->lang->release->systemName = $this->lang->release->system; } $release = form::data() ->add('product', (int)$productID) ->add('branch', (int)$branch) ->setIF($projectID, 'project', $projectID) ->setIF($this->post->build === false, 'build', 0) ->setIF($this->post->status != 'normal', 'releasedDate', null) ->get(); /* Check build if build is required. */ if(strpos($this->config->release->create->requiredFields, 'build') !== false && empty($release->build)) dao::$errors['build'] = sprintf($this->lang->error->notempty, $this->lang->release->build); if(!$newSystem && $this->post->system) { $system = $this->loadModel('system')->fetchByID((int)$this->post->system); if(!$system) dao::$errors['system'][] = sprintf($this->lang->error->notempty, $this->lang->release->system); if($system->integrated == '1') { $releases = (array)$this->post->releases; $release->build = ''; $release->releases = trim(implode(',', array_filter($releases)), ','); if(!$release->releases) dao::$errors['releases[' . key($releases) . ']'][] = sprintf($this->lang->error->notempty, $this->lang->release->name); } } if(dao::isError()) return false; if($newSystem && $this->post->systemName) { $system = new stdclass(); $system->name = trim($this->post->systemName); $system->product = $productID; $system->createdBy = $this->app->user->account; $system->createdDate = helper::now(); $release->system = $this->loadModel('system')->create($system); } return $release; } /** * 构建搜索表单字段。 * Build search form fields. * * @param int $queryID * @param string $actionURL * @param object $product * @param string $branch * @access protected * @return void */ protected function buildSearchForm(int $queryID, string $actionURL, object $product, string $branch): void { $this->config->release->search['queryID'] = $queryID; $this->config->release->search['actionURL'] = $actionURL; if($product->type != 'normal') $this->config->release->search['params']['branch']['values'] = $this->loadModel('branch')->getPairs($product->id, 'all'); $this->config->release->search['params']['build']['values'] = $this->loadmodel('build')->getBuildPairs(array($product->id), $branch, 'notrunk|withbranch|hasproject', 0, 'execution', '', false); $this->loadModel('search')->setSearchParams($this->config->release->search); } /** * 获取发布列表的搜索条件。 * Get the search condition of release list. * * @param int $queryID * @access protected * @return string */ protected function getSearchQuery(int $queryID): string { if($queryID) { $query = $this->loadModel('search')->getQuery($queryID); if($query) { $this->session->set('releaseQuery', $query->sql); $this->session->set('releaseForm', $query->form); } } if($this->session->releaseQuery === false) $this->session->set('releaseQuery', ' 1 = 1'); $releaseQuery = $this->session->releaseQuery; /* Replace the condition of all branch to 1. */ $allBranch = "`branch` = 'all'"; if(strpos($releaseQuery, $allBranch) !== false) $releaseQuery = str_replace($allBranch, '1', $releaseQuery); $releaseQuery = preg_replace('/`(\w+)`/', 't1.`$1`', $releaseQuery); return $releaseQuery; } /** * 获取发布不可关联的需求ID列表。 * Get the story id list that can not be linked to the release. * * @param object $release * @access protected * @return array */ public function getExcludeStoryIdList(object $release): array { $parentIdList = $this->dao->select('id')->from(TABLE_STORY) ->where('product')->eq($release->product) ->andWhere('type')->eq('story') ->andWhere('isParent')->eq('1') ->andWhere('status')->notIN('draft,reviewing,changing') ->fetchPairs(); foreach(explode(',', $release->stories) as $storyID) { if(!$storyID) continue; if(!isset($parentIdList[$storyID])) $parentIdList[$storyID] = $storyID; } return $parentIdList; } /** * 生成的发布详情页面的需求数据。 * Generate the story data for the release view page. * * @param object $release * @param string $type * @param string $link * @param string $param * @param string $orderBy * @param object $storyPager * @param object $bugPager * @param object $leftBugPager * @access protected * @return void */ protected function assignVarsForView(object $release, string $type, string $link, string $param, string $orderBy, ?object $storyPager = null, ?object $bugPager = null, ?object $leftBugPager = null): void { $sort = common::appendOrder($orderBy); if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort); $sort .= ',buildID_asc'; $storyIdList = trim($release->stories, ','); $bugIdList = trim($release->bugs, ','); $leftBugIdList = trim($release->leftBugs, ','); if($release->releases) { $linkedReleases = $this->release->getListByCondition(explode(',', $release->releases)); foreach($linkedReleases as $linkedRelease) { $storyIdList .= ',' . $linkedRelease->stories; $bugIdList .= ',' . $linkedRelease->bugs; $leftBugIdList .= ',' . $linkedRelease->leftBugs; } } $stories = $this->release->getStoryList($storyIdList, $release->branch, $type == 'story' ? $sort : '', $storyPager); $sort = common::appendOrder($orderBy); $bugs = $this->release->getBugList($bugIdList, $type == 'bug' ? $sort : '', $bugPager); if($type == 'leftBug' && strpos($orderBy, 'severity_') !== false) $sort = str_replace('severity_', 'severityOrder_', $sort); $leftBugs = $this->release->getBugList($leftBugIdList, $type == 'leftBug' ? $sort : '', $leftBugPager, 'left'); $product = $this->loadModel('product')->getByID($release->product); $this->view->title = "RELEASE #$release->id $release->name/" . $product->name; $this->view->actions = $this->loadModel('action')->getList('release', $release->id); $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->storyPager = $storyPager; $this->view->stories = $stories; $this->view->release = $release; $this->view->orderBy = $orderBy; $this->view->type = $type; $this->view->link = $link; $this->view->grades = $this->loadModel('story')->getGradePairs('story', 'all'); $this->view->showGrade = $this->config->edition == 'ipd'; $this->view->param = $param; $this->view->storyCases = $this->loadModel('testcase')->getStoryCaseCounts(array_column($stories, 'id')); $this->view->summary = $this->product->summary($stories); $this->view->builds = $this->loadModel('build')->getBuildPairs(array($release->product), 'all', 'withbranch|hasproject|hasdeleted', 0, 'execution', '', true); $this->view->bugs = $bugs; $this->view->leftBugs = $leftBugs; $this->view->bugPager = $bugPager; $this->view->leftBugPager = $leftBugPager; if($this->app->getViewType() == 'json') { unset($this->view->storyPager); unset($this->view->bugPager); unset($this->view->leftBugPager); } } /** * 构造关联需求的搜索表单。 * Build the search form of link story. * * @param object $release * @param int $queryID * @access protected * @return void */ protected function buildLinkStorySearchForm(object $release, int $queryID): void { $this->app->loadLang('story'); $this->loadModel('product'); unset($this->config->product->search['fields']['product']); unset($this->config->product->search['fields']['project']); unset($this->config->product->search['fields']['grade']); unset($this->config->product->search['params']['product']); unset($this->config->product->search['params']['project']); unset($this->config->product->search['params']['grade']); $this->config->product->search['actionURL'] = $this->createLink($this->app->rawModule, 'view', "releaseID={$release->id}&type=story&link=true¶m=" . helper::safe64Encode('&browseType=bySearch&queryID=myQueryID')); $this->config->product->search['queryID'] = $queryID; $this->config->product->search['style'] = 'simple'; $this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($release->product, $release->branch, 'withMainPlan', true); $this->config->product->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $this->lang->story->statusList); $searchModules = array(); $moduleGroups = $this->loadModel('tree')->getOptionMenu($release->product, 'story', 0, explode(',', $release->branch)); foreach($moduleGroups as $modules) $searchModules += $modules; $this->config->product->search['params']['module']['values'] = $searchModules; if($release->productType == 'normal') { unset($this->config->product->search['fields']['branch']); unset($this->config->product->search['params']['branch']); } else { $branches = $this->loadModel('branch')->getPairsByIdList(explode(',', trim($release->branch, ','))); $this->config->product->search['fields']['branch'] = sprintf($this->lang->product->branch, $this->lang->product->branchName[$release->productType]); $this->config->product->search['params']['branch']['values'] = array('' => '', BRANCH_MAIN => $this->lang->branch->main) + $branches; } $this->loadModel('search')->setSearchParams($this->config->product->search); } /** * 构造关联Bug的搜索表单。 * Build the search form of link bug. * * @param object $release * @param int $queryID * @param string $type * @access protected * @return void */ protected function buildLinkBugSearchForm(object $release, int $queryID, string $type): void { $this->loadModel('bug'); unset($this->config->bug->search['fields']['product']); unset($this->config->bug->search['fields']['project']); unset($this->config->bug->search['params']['product']); unset($this->config->bug->search['params']['project']); $this->config->bug->search['actionURL'] = $this->createLink($this->app->rawModule, 'view', "releaseID={$release->id}&type={$type}&link=true¶m=" . helper::safe64Encode('&browseType=bySearch&queryID=0')); $this->config->bug->search['queryID'] = $queryID; $this->config->bug->search['style'] = 'simple'; $this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($release->product, $release->branch, 'withMainPlan', true); $this->config->bug->search['params']['execution']['values'] = $this->loadModel('product')->getExecutionPairsByProduct($release->product, $release->branch); $this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs(array($release->product), 'all', 'releasetag'); $this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values']; $searchModules = array(); $moduleGroups = $this->loadModel('tree')->getOptionMenu($release->product, 'bug', 0, explode(',', $release->branch)); foreach($moduleGroups as $modules) $searchModules += $modules; $this->config->bug->search['params']['module']['values'] = $searchModules; if($release->productType == 'normal') { unset($this->config->bug->search['fields']['branch']); unset($this->config->bug->search['params']['branch']); } else { $branches = $this->loadModel('branch')->getPairsByIdList(explode(',', trim($release->branch, ','))); $this->config->bug->search['fields']['branch'] = sprintf($this->lang->product->branch, $this->lang->product->branchName[$release->productType]); $this->config->bug->search['params']['branch']['values'] = array('' => '', BRANCH_MAIN => $this->lang->branch->main) + $branches; } $this->loadModel('search')->setSearchParams($this->config->bug->search); } /** * 构造导出的需求列表数据。 * Build the story list data for export. * * @param object $release * @access protected * @return string */ protected function buildStoryDataForExport(object $release): string { $this->loadModel('story'); $html = "

{$this->lang->release->stories}

"; $fields = array('id' => $this->lang->story->id, 'title' => $this->lang->story->title); $stories = $this->release->getStoryList($release->stories, (int)$release->branch); if(empty($stories)) return $html; $html .= ''; foreach($fields as $fieldLabel) $html .= "\n"; $html .= ''; $stories = array_map(function($story){$story->title = "id") . "' target='_blank'>$story->title"; return $story;}, $stories); foreach($stories as $row) { $html .= "\n"; foreach($fields as $fieldName => $fieldLabel) $html .= "\n"; $html .= "\n"; } $html .= '
$fieldLabel
" . zget($row, $fieldName, '') . "
'; return $html; } /** * 构造导出的解决的Bug或遗留Bug列表数据。 * Build the resolved or generated bug list data for export. * * @param object $release * @param string $type bug|leftbug * @access protected * @return string */ protected function buildBugDataForExport(object $release, string $type = 'bug'): string { $this->loadModel('bug'); $title = $type == 'bug' ? $this->lang->release->bugs : $this->lang->release->generatedBugs; $html = "

{$title}

"; $fields = array('id' => $this->lang->bug->id, 'title' => $this->lang->bug->title); $bugIdList = $type == 'bug' ? $release->bugs : $release->leftBugs; $bugs = $this->release->getBugList($bugIdList); if(empty($bugs)) return $html; $html .= ''; foreach($fields as $fieldLabel) $html .= "\n"; $html .= ''; $bugs = array_map(function($bug){$bug->title = "id") . "' target='_blank'>$bug->title"; return $bug;}, $bugs); foreach($bugs as $row) { $html .= "\n"; foreach($fields as $fieldName => $fieldLabel) $html .= "\n"; $html .= "\n"; } $html .= '
$fieldLabel
" . zget($row, $fieldName, '') . "
'; return $html; } }