| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967 |
- <?php
- /**
- * The model file of build module of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
- * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author Chunsheng Wang <chunsheng@cnezsoft.com>
- * @package build
- * @version $Id: model.php 4970 2013-07-02 05:58:11Z wyd621@gmail.com $
- * @link https://www.zentao.net
- */
- ?>
- <?php
- class buildModel extends model
- {
- /**
- * 通过版本ID获取版本信息。
- * Get build info.
- *
- * @param int $buildID
- * @param bool $setImgSize
- * @access public
- * @return object|bool
- */
- public function getByID($buildID, $setImgSize = false)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getBuild();
- $build = $this->dao->select('t1.*, t2.name as executionName, t3.name as productName, t3.type as productType')
- ->from(TABLE_BUILD)->alias('t1')
- ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution = t2.id')
- ->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product = t3.id')
- ->where('t1.id')->eq((int)$buildID)
- ->fetch();
- if(!$build) return false;
- $build = $this->joinChildBuilds($build);
- $build = $this->loadModel('file')->replaceImgURL($build, 'desc');
- $build->files = $this->file->getByObject('build', $buildID);
- if($setImgSize) $build->desc = $this->file->setImgSize($build->desc);
- $build->isChild = false;
- if($this->app->rawMethod == 'edit')
- {
- if($this->dao->select('id')->from(TABLE_BUILD)->where("FIND_IN_SET($buildID, `builds`)")->fetch()) $build->isChild = true;
- if(!$build->isChild && $this->dao->select('id')->from(TABLE_RELEASE)->where("FIND_IN_SET($buildID, `build`)")->fetch()) $build->isChild = true;
- }
- return $build;
- }
- /**
- * 通过版本ID列表获取版本信息。
- * Get builds by id list.
- *
- * @param array $idList
- * @access public
- * @return array
- */
- public function getByList($idList)
- {
- return $this->dao->select('*')->from(TABLE_BUILD)->where('id')->in($idList)->fetchAll('id', false);
- }
- /**
- * 通过项目ID获取版本信息。
- * Get builds of a project.
- *
- * @param int $projectID
- * @param string $type
- * @param string $param
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return array
- */
- public function getProjectBuilds($projectID = 0, $type = 'all', $param = '', $orderBy = 't1.date_desc,t1.id_desc', $pager = null)
- {
- $shadows = $this->dao->select('shadow')->from(TABLE_RELEASE)->where("FIND_IN_SET({$projectID}, project)")->fetchPairs('shadow', 'shadow');
- $builds = $this->dao->select('t1.*, t2.name as productName')
- ->from(TABLE_BUILD)->alias('t1')
- ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
- ->where('t1.deleted')->eq(0)
- ->andWhere('t1.project')->ne(0)
- ->beginIF(!empty($shadows))->andWhere('t1.id')->notIN($shadows)->fi()
- ->beginIF($projectID)->andWhere('t1.project')->eq((int)$projectID)->fi()
- ->beginIF($type == 'product' && $param)->andWhere('t1.product')->eq((int)$param)->fi()
- ->beginIF($type == 'bysearch')->andWhere($param)->fi()
- ->beginIF($type == 'review')->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")->fi()
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- $executionIdList = array();
- foreach($builds as $build)
- {
- $build->builds = $this->getByList(explode(',', $build->builds));
- if(!empty($build->builds))
- {
- foreach($build->builds as $child)
- {
- if(!isset($executionIdList[$child->execution])) $executionIdList[$child->execution] = $child->execution;
- }
- }
- if(!isset($executionIdList[$build->execution])) $executionIdList[$build->execution] = $build->execution;
- }
- $executions = $this->loadModel('execution')->getByIdList($executionIdList, 'all');
- foreach($builds as $build)
- {
- $build->executionDeleted = $build->execution ? $executions[$build->execution]->deleted : 0;
- $build->executionName = $build->execution ? $executions[$build->execution]->name : '';
- if(!empty($build->builds))
- {
- foreach($build->builds as $child) $child->executionName = $child->execution ? $executions[$child->execution]->name : '';
- }
- }
- return $builds;
- }
- /**
- * 根据搜索条件获取项目版本。
- * Get builds of a project by search.
- *
- * @param int $projectID
- * @param int $queryID
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return array
- */
- public function getProjectBuildsBySearch($projectID, $queryID, $orderBy = 't1.date_desc,t1.id_desc', $pager = null)
- {
- /* If there are saved query conditions, reset the session. */
- if((int)$queryID)
- {
- $query = $this->loadModel('search')->getQuery($queryID);
- if($query)
- {
- $this->session->set('projectBuildQuery', $query->sql);
- $this->session->set('projectBuildForm', $query->form);
- }
- }
- if($this->session->projectBuildQuery == false) $this->session->set('projectBuildQuery', ' 1 = 1');
- $buildQuery = $this->session->projectBuildQuery;
- /* Distinguish between repeated fields. */
- $fields = array('id' => '`id`', 'name' => '`name`', 'product' => '`product`', 'desc' => '`desc`', 'project' => '`project`');
- foreach($fields as $field)
- {
- if(strpos($buildQuery, $field) !== false)
- {
- $buildQuery = str_replace($field, "t1." . $field, $buildQuery);
- }
- }
- return $this->getProjectBuilds($projectID, 'bysearch', (string)$buildQuery, $orderBy, $pager);
- }
- /**
- * 根据执行ID获取版本信息。
- * Get builds of a execution.
- *
- * @param int $executionID
- * @param string $type all|product|bysearch
- * @param string $param productID|buildQuery
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return array
- */
- public function getExecutionBuilds($executionID, $type = '', $param = '', $orderBy = 't1.date_desc,t1.id_desc', $pager = null)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getBuilds();
- return $this->dao->select('t1.*, t2.name as executionName, t3.name as productName')
- ->from(TABLE_BUILD)->alias('t1')
- ->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.execution = t2.id')
- ->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product = t3.id')
- ->where('t1.deleted')->eq(0)
- ->beginIF($executionID)->andWhere('t1.execution')->eq((int)$executionID)->fi()
- ->beginIF($type == 'product' && $param)->andWhere('t1.product')->eq((int)$param)->fi()
- ->beginIF($type == 'bysearch')->andWhere($param)->fi()
- ->beginIF($type == 'review')->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")->fi()
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- }
- /**
- * 通过条件获取执行下版本列表。
- * Get builds of a execution by search.
- *
- * @param int $executionID
- * @param int $queryID
- * @param object $pager
- * @access public
- * @return object[]
- */
- public function getExecutionBuildsBySearch($executionID, $queryID, $pager = null)
- {
- /* If there are saved query conditions, reset the session. */
- if($queryID)
- {
- $query = $this->loadModel('search')->getQuery($queryID);
- if($query)
- {
- $this->session->set('executionBuildQuery', $query->sql);
- $this->session->set('executionBuildForm', $query->form);
- }
- }
- if($this->session->executionBuildQuery === false) $this->session->set('executionBuildQuery', ' 1 = 1');
- $buildQuery = $this->session->executionBuildQuery;
- /* Distinguish between repeated fields. */
- $fields = array('id' => '`id`', 'name' => '`name`', 'product' => '`product`', 'desc' => '`desc`');
- foreach($fields as $field)
- {
- if(strpos($this->session->executionBuildQuery, $field) !== false)
- {
- $buildQuery = str_replace($field, "t1." . $field, $buildQuery);
- }
- }
- return $this->getExecutionBuilds($executionID, 'bysearch', (string)$buildQuery, 't1.date_desc,t1.id_desc', $pager);
- }
- /**
- * 根据需求ID获取版本列表。
- * Get builds by story ID.
- *
- * @param int $storyID
- * @access public
- * @return array
- */
- public function getStoryBuilds($storyID)
- {
- if(empty($storyID)) return array();
- return $this->dao->select('*')->from(TABLE_BUILD)
- ->where('deleted')->eq(0)
- ->andWhere("CONCAT(',', stories, ',')")->like("%,$storyID,%")
- ->orderBy('id_desc')
- ->fetchAll('id');
- }
- /**
- * 通过条件获取版本id:name的键值对。
- * Get build pairs by condition.
- *
- * @param array|int $productIdList
- * @param string|int $branch
- * @param string $params noempty|notrunk|noterminate|withbranch|hasproject|noDeleted|singled|noreleased|releasedtag, can be a set of them
- * @param int $objectID
- * @param string $objectType
- * @param string $buildIdList
- * @param bool $replace
- * @access public
- * @return array
- * @param int $system
- */
- public function getBuildPairs($productIdList, $branch = 'all', $params = 'noterminate, nodone', $objectID = 0, $objectType = 'execution', $buildIdList = '', $replace = true, $system = 0)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getBuildPairs();
- $sysBuilds = array();
- if(strpos($params, 'notrunk') === false) $sysBuilds = array('trunk' => $this->lang->trunk);
- if(is_string($buildIdList)) $buildIdList = str_replace('trunk', '0', $buildIdList);
- $shadows = array();
- if(!empty($productIdList)) $shadows = $this->dao->select('shadow')->from(TABLE_RELEASE)->where('product')->in($productIdList)->fetchPairs('shadow', 'shadow'); // Get the buildID under the shadow product.
- if($objectType == 'project') $shadows = $this->dao->select('shadow')->from(TABLE_RELEASE)->where("FIND_IN_SET({$objectID}, project)")->fetchPairs('shadow', 'shadow');
- $selectedBuilds = $this->buildTao->selectedBuildPairs($buildIdList, $productIdList, $params, $objectID, $objectType);
- $allBuilds = $this->buildTao->fetchBuilds($productIdList, $params, $objectID, $objectType, $shadows, $system);
- /* Set builds and filter done executions and terminate releases. */
- list($builds, $excludedReleaseIdList) = $this->setBuildDateGroup($allBuilds, $branch, $params);
- if(empty($builds) && empty($shadows)) return $sysBuilds + $selectedBuilds;
- /* if the build has been released and replace is true, replace build name with release name. */
- if($replace)
- {
- $releases = $this->getRelatedReleases($productIdList, $buildIdList, $shadows, $objectType, $objectID, $params);
- $builds = $this->replaceNameWithRelease($allBuilds, $builds, $releases, $branch, $params, $excludedReleaseIdList);
- }
- krsort($builds);
- $buildPairs = array();
- foreach($builds as $childBuilds) $buildPairs += $childBuilds;
- return $sysBuilds + $buildPairs + $selectedBuilds;
- }
- /**
- * 根据版本日期分组设置版本信息。
- * Set build date group.
- *
- * @param array $allBuilds
- * @param string|int $branch
- * @param string $params nodone|noterminate|withexecution|withbranch
- * @access public
- * @return array
- */
- public function setBuildDateGroup($allBuilds, $branch, $params)
- {
- $this->app->loadLang('branch');
- $deletedProjects = $this->dao->select('id, deleted')->from(TABLE_PROJECT)->where('type')->eq('project')->andWhere('deleted')->eq('1')->fetchPairs();
- $deletedExecutions = $this->dao->select('id, deleted')->from(TABLE_EXECUTION)->where('type')->in('sprint,stage,kanban')->andWhere('deleted')->eq('1')->fetchPairs();
- $branchPairs = $this->dao->select('id,name')->from(TABLE_BRANCH)->fetchPairs();
- $builds = array();
- $excludedReleaseIdList = array();
- foreach($allBuilds as $id => $build)
- {
- if($build->branch === '') $build->branch = 0;
- $isDeleted = false;
- if(strpos($params, 'withexecution') !== false && $build->execution && isset($deletedExecutions[$build->execution])) $isDeleted = true;
- if($build->project && !$build->execution && isset($deletedProjects[$build->project])) $isDeleted = true;
- $isDone = empty($build->releaseID) && strpos($params, 'nodone') !== false && !empty($build->objectStatus) && $build->objectStatus === 'done';
- $isTerminate = strpos($params, 'noterminate') !== false && $build->releaseStatus === 'terminate';
- $isNotInBranch = $branch !== '' && $branch !== 'all' && strpos(",{$build->branch},", ",{$branch},") === false;
- if(in_array(true, array($isDone, $isTerminate, $isDeleted, $isNotInBranch)))
- {
- $excludedReleaseIdList[] = $build->releaseID;
- continue;
- }
- if($build->deleted == 1) $build->name .= ' (' . $this->lang->build->deleted . ')';
- if(!empty($build->branch))
- {
- $branchName = '';
- foreach(explode(',', $build->branch) as $buildBranch)
- {
- if(empty($buildBranch))
- {
- $branchName .= $this->lang->branch->main;
- }
- else
- {
- $branchName .= isset($branchPairs[$buildBranch]) ? $branchPairs[$buildBranch] : '';
- }
- $branchName .= ',';
- }
- $branchName = trim($branchName, ',');
- }
- else
- {
- $branchName = $this->lang->branch->main;
- }
- $buildName = $build->name;
- if(strpos($params, 'withbranch') !== false && $build->productType != 'normal') $buildName = $branchName . '/' . $buildName;
- $builds[$build->date][$id] = $buildName;
- }
- return array($builds, $excludedReleaseIdList);
- }
- /**
- * 将版本名称替换为发布名称。
- * Replace the build name with release name.
- *
- * @param array $allBuilds
- * @param array $builds
- * @param array $releases
- * @param string|int $branch
- * @param string $params separate|noterminate|withbranch|releasetag|noreleased
- * @param array $excludedReleaseIdList
- * @access public
- * @return array
- */
- public function replaceNameWithRelease($allBuilds, $builds, $releases, $branch, $params, $excludedReleaseIdList)
- {
- $this->app->loadLang('branch');
- $branches = strpos($params, 'separate') === false ? "0,$branch" : $branch;
- foreach($releases as $release)
- {
- if(strpos($params, 'noterminate') !== false && in_array($release->id, $excludedReleaseIdList)) continue;
- if($branch !== 'all')
- {
- $inBranch = false;
- foreach(explode(',', trim($release->branch, ',')) as $branchID)
- {
- if($branchID === '') continue;
- if(strpos(",{$branches},", ",{$branchID},") !== false) $inBranch = true;
- }
- if(!$inBranch) continue;
- }
- /* Set release name based on the condition. */
- $releaseName = $release->name;
- $branchName = $release->branchName ? $release->branchName : $this->lang->branch->main;
- if($release->productType != 'normal') $releaseName = (strpos($params, 'withbranch') !== false ? $branchName . '/' : '') . $releaseName;
- if(strpos($params, 'releasetag') !== false) $releaseName = $releaseName . " [{$this->lang->build->released}]";
- $builds[$release->date][$release->shadow] = $releaseName;
- if(strpos($params, 'noreplace') === false) // 勾选所有版本时,展示所有发布,不替换版本名称。
- {
- foreach(explode(',', trim($release->build, ',')) as $buildID)
- {
- if(!isset($allBuilds[$buildID])) continue;
- $build = $allBuilds[$buildID];
- if(strpos($params, 'noreleased') !== false) unset($builds[$build->date][$buildID]);
- }
- }
- }
- return $builds;
- }
- /**
- * 获取关联的发布。
- * Get releated release.
- *
- * @param array|int $productIdList
- * @param string|int $buildIdList
- * @param array|bool $shadows
- * @param string $objectType
- * @param int $objectID
- * @param string $params
- * @access public
- * @return array
- */
- public function getRelatedReleases($productIdList, $buildIdList = '', $shadows = false, $objectType = '', $objectID = 0, $params = '')
- {
- $releases = $this->dao->select('DISTINCT t1.id,t1.shadow,t1.product,t1.branch,t1.build,t1.name,t1.date,t1.status,t3.name as branchName,t4.type as productType')->from(TABLE_RELEASE)->alias('t1')
- ->leftJoin(TABLE_RELEASERELATED)->alias('t5')->on("t1.id=t5.release AND t5.objectType='build'")
- ->leftJoin(TABLE_BUILD)->alias('t2')->on('t5.objectID=t2.id')
- ->leftJoin(TABLE_RELEASERELATED)->alias('t6')->on("t1.id=t6.release AND t6.objectType='branch'")
- ->leftJoin(TABLE_BRANCH)->alias('t3')->on('t6.objectID=t3.id')
- ->leftJoin(TABLE_PRODUCT)->alias('t4')->on('t1.product=t4.id')
- ->where('t1.product')->in($productIdList)
- ->beginIF($objectType === 'project' && $objectID)->andWhere("(FIND_IN_SET('$objectID', t1.project)")->orWhere('t1.project')->eq('0')->markRight(1)->fi()
- ->beginIF($objectType === 'execution' && $objectID)->andWhere('t2.execution')->eq($objectID)->fi()
- ->beginIF(strpos($params, 'nowaitrelease') !== false)->andWhere('t1.status')->ne('wait')->fi()
- ->beginIF(strpos($params, 'nofail') !== false)->andWhere('t1.status')->ne('fail')->fi()
- ->andWhere('((t1.deleted')->eq(0)
- ->andWhere('t1.shadow')->ne(0)
- ->markRight(true)
- ->beginIF(!empty($buildIdList))->orWhere('t2.id')->in($buildIdList)->fi()
- ->markRight(true)
- ->fetchAll('id');
- if($shadows === false) $shadows = $this->dao->select('shadow')->from(TABLE_RELEASE)->where('product')->in($productIdList)->fetchPairs('shadow', 'shadow'); // Get the buildID under the shadow product.
- if($shadows)
- {
- /* Append releases of only shadow and not link build. */
- $releases += $this->dao->select('DISTINCT t1.id,t1.shadow,t1.product,t1.branch,t1.build,t1.name,t1.date,t2.name as branchName,t3.type as productType')->from(TABLE_RELEASE)->alias('t1')
- ->leftJoin(TABLE_RELEASERELATED)->alias('t4')->on("t1.id=t4.release AND t4.objectType='branch'")
- ->leftJoin(TABLE_BRANCH)->alias('t2')->on('t4.objectID=t2.id')
- ->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product=t3.id')
- ->where('t1.shadow')->in($shadows)
- ->beginIF($objectType === 'project' && $objectID)->andWhere("(FIND_IN_SET('$objectID', t1.project)")->orWhere('t1.project')->eq('0')->markRight(1)->fi()
- ->andWhere('t1.build')->eq(0)
- ->andWhere('t1.deleted')->eq(0)
- ->fetchAll('id');
- }
- return $releases;
- }
- /**
- * 获取最后一次创建的版本信息。
- * Get the last build.
- *
- * @param int $executionID
- * @param int $projectID
- * @access public
- * @return object|false
- */
- public function getLast($executionID = 0, $projectID = 0)
- {
- return $this->dao->select('id, name')->from(TABLE_BUILD)
- ->where('deleted')->eq(0)
- ->andWhere('execution')->eq($executionID)
- ->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
- ->orderBy('date DESC,id DESC')
- ->fetch();
- }
- /**
- * 创建一个版本。
- * Create a build.
- *
- * @param object $build
- * @access public
- * @return int|false
- */
- public function create($build)
- {
- /* Integrated version merging branch. */
- if($this->post->isIntegrated == 'yes')
- {
- $build->execution = 0;
- $branchPairs = $this->dao->select('branch')->from(TABLE_BUILD)->where('id')->in($build->builds)->fetchPairs();
- $relationBranch = array();
- foreach($branchPairs as $branches)
- {
- foreach(explode(',', $branches) as $branch)
- {
- if(!isset($relationBranch[$branch])) $relationBranch[$branch] = $branch;
- }
- }
- if($relationBranch) $build->branch = implode(',', $relationBranch);
- $this->config->build->create->requiredFields = str_replace('execution,', '', $this->config->build->create->requiredFields);
- }
- else
- {
- $product = $this->loadModel('product')->getByID((int)$build->product);
- if(!empty($product) && $product->type != 'normal'&& $this->post->branch === false)
- {
- $this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
- dao::$errors['branch'] = sprintf($this->lang->error->notempty, $this->lang->product->branch);
- }
- }
- if(dao::isError()) return false;
- /* Process and insert build data. */
- $requiredFields = $this->config->build->create->requiredFields;
- $project = $this->loadModel('project')->getByID((int)$build->project);
- if($project && !$project->hasProduct) $requiredFields = str_replace('product,', '', $requiredFields);
- $build = $this->loadModel('file')->processImgURL($build, $this->config->build->editor->create['id'], (string)$this->post->uid);
- $this->dao->insert(TABLE_BUILD)->data($build)
- ->autoCheck()
- ->batchCheck($requiredFields, 'notempty')
- ->check('name', 'unique', "product = {$build->product} AND branch = '{$build->branch}' AND deleted = '0'")
- ->checkFlow()
- ->exec();
- if(dao::isError()) return false;
- /* Set file linkage and score info. */
- $buildID = $this->dao->lastInsertID();
- $this->file->updateObjectID($this->post->uid, $buildID, 'build');
- $this->file->saveUpload('build', $buildID);
- $this->loadModel('score')->create('build', 'create', $buildID);
- $this->loadModel('action')->create('build', $buildID, 'opened');
- return $buildID;
- }
- /**
- * 更新一个版本。
- * Update a build.
- *
- * @param int $buildID
- * @param object $build
- * @access public
- * @return array|false
- */
- public function update($buildID, $build)
- {
- $oldBuild = $this->fetchByID($buildID);
- $product = $this->loadModel('product')->getByID((int) $build->product);
- $branch = $this->post->branch === false || ($product && $product->type) == 'normal' ? 0 : $oldBuild->branch;
- if(empty($oldBuild->execution)) $build = $this->processBuildForUpdate($build, $oldBuild);
- $product = $this->loadModel('product')->getByID($build->product);
- if(!empty($product) && $product->type != 'normal' && !isset($_POST['branch']) && isset($_POST['product']))
- {
- $this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
- dao::$errors['branch'] = sprintf($this->lang->error->notempty, $this->lang->product->branch);
- }
- if(dao::isError()) return false;
- $project = $this->loadModel('project')->getByID((int)$oldBuild->project);
- $requiredFields = $this->config->build->edit->requiredFields;
- if(!$project->hasProduct) $requiredFields = str_replace('product,', '', $requiredFields);
- $build = $this->loadModel('file')->processImgURL($build, $this->config->build->editor->edit['id'], (string)$this->post->uid);
- $this->dao->update(TABLE_BUILD)->data($build, 'deleteFiles,renameFiles,files')
- ->autoCheck()
- ->batchCheck($requiredFields, 'notempty')
- ->where('id')->eq($buildID)
- ->check('name', 'unique', "id != $buildID AND product = {$build->product} AND branch = '{$build->branch}' AND deleted = '0'")
- ->checkFlow()
- ->exec();
- if(dao::isError()) return false;
- if(isset($build->branch) && $oldBuild->branch != $build->branch) $this->dao->update(TABLE_RELEASE)->set('branch')->eq($build->branch)->where('build')->eq($buildID)->exec();
- if(dao::isError()) return false;
- $oldBuild->files = $this->file->getByObject('build', $buildID);
- $this->file->processFileDiffsForObject('build', $oldBuild, $build);
- return common::createChanges($oldBuild, $build);
- }
- /**
- * 更新关联Bug的解决原因。
- * Update linked bug to resolved.
- *
- * @param object $build
- * @param array $bugIdList
- * @param array $resolvedByList
- * @access public
- * @return bool
- */
- public function updateLinkedBug($build, $bugIdList = array(), $resolvedByList = array())
- {
- $bugs = empty($bugIdList) ? array() : $this->dao->select('*')->from(TABLE_BUG)->where('id')->in($bugIdList)->fetchAll();
- if(!$bugs) return false;
- $this->loadModel('action');
- $now = helper::now();
- foreach($bugs as $bug)
- {
- if($bug->status == 'resolved' || $bug->status == 'closed') continue;
- if(helper::isZeroDate($bug->activatedDate)) unset($bug->activatedDate);
- if(helper::isZeroDate($bug->closedDate)) unset($bug->closedDate);
- $bug->resolvedBy = zget($resolvedByList, $bug->id, '');
- $bug->resolvedDate = $now;
- $bug->status = 'resolved';
- $bug->confirmed = 1;
- $bug->assignedDate = $now;
- $bug->assignedTo = $bug->openedBy;
- $bug->lastEditedBy = $this->app->user->account;
- $bug->lastEditedDate = $now;
- $bug->resolution = 'fixed';
- $bug->resolvedBuild = $build->id;
- $bug->deadline = !empty($bug->deadline) ? $bug->deadline : null;
- $this->dao->update(TABLE_BUG)->data($bug)->where('id')->eq($bug->id)->exec();
- $this->action->create('bug', $bug->id, 'Resolved', '', 'fixed', $bug->resolvedBy);
- }
- return !dao::isError();
- }
- /**
- * 版本关联需求。
- * Link stories to a build.
- *
- * @param int $buildID
- * @param array $storyIdList
- * @access public
- * @return void
- */
- public function linkStory($buildID, $storyIdList)
- {
- if(empty($storyIdList)) return false;
- $build = $this->getByID($buildID);
- foreach($storyIdList as $i => $storyID)
- {
- if(strpos(",{$build->stories},", ",{$storyID},") !== false) unset($storyIdList[$i]);
- }
- $build->stories .= ',' . implode(',', $storyIdList);
- $this->dao->update(TABLE_BUILD)->set('stories')->eq($build->stories)->where('id')->eq((int)$buildID)->exec();
- $this->loadModel('action');
- foreach($storyIdList as $storyID) $this->action->create('story', (int)$storyID, 'linked2build', '', $buildID);
- $this->action->create('build', $buildID, 'linkstory', '', implode(',', $storyIdList));
- return !dao::isError();
- }
- /**
- * 解除需求关联。
- * Unlink story.
- *
- * @param int $buildID
- * @param int $storyID
- * @access public
- * @return bool
- */
- public function unlinkStory($buildID, $storyID)
- {
- $build = $this->getByID($buildID);
- $build->stories = trim(str_replace(",$storyID,", ',', ",$build->stories,"), ',');
- if($build->stories) $build->stories = ',' . $build->stories;
- $this->dao->update(TABLE_BUILD)->set('stories')->eq($build->stories)->where('id')->eq($buildID)->exec();
- $this->loadModel('action')->create('story', $storyID, 'unlinkedfrombuild', '', $buildID, '', false);
- return !dao::isError();
- }
- /**
- * 批量解除需求关联。
- * Batch unlink story.
- *
- * @param int $buildID
- * @param array $storyIDList
- * @access public
- * @return bool
- */
- public function batchUnlinkStory($buildID, $storyIDList)
- {
- if(empty($storyIDList)) return true;
- $build = $this->getByID($buildID);
- $build->stories = ",$build->stories,";
- foreach($storyIDList as $storyID) $build->stories = str_replace(",$storyID,", ',', $build->stories);
- $build->stories = trim($build->stories, ',');
- $this->dao->update(TABLE_BUILD)->set('stories')->eq($build->stories)->where('id')->eq((int)$buildID)->exec();
- $this->loadModel('action');
- foreach($storyIDList as $storyID) $this->action->create('story', (int)$storyID, 'unlinkedfrombuild', '', $buildID);
- return !dao::isError();
- }
- /**
- * 版本关联Bug。
- * Link bugs.
- *
- * @param int $buildID
- * @param array $bugIdList
- * @param array $resolvedList
- * @access public
- * @return bool
- */
- public function linkBug($buildID, $bugIdList, $resolvedList = array())
- {
- $build = $this->getByID($buildID);
- if(!$build) return false;
- foreach($bugIdList as $i => $bugID)
- {
- if(strpos(",{$build->bugs},", ",{$bugID},") !== false) unset($bugIdList[$i]);
- }
- $build->bugs .= ',' . implode(',', $bugIdList);
- $this->updateLinkedBug($build, $bugIdList, $resolvedList);
- $this->dao->update(TABLE_BUILD)->set('bugs')->eq($build->bugs)->where('id')->eq((int)$buildID)->exec();
- $this->loadModel('action');
- foreach($bugIdList as $bugID) $this->action->create('bug', (int)$bugID, 'linked2bug', '', $buildID);
- return !dao::isError();
- }
- /**
- * 解除Bug跟版本的关联关系。
- * Unlink bug.
- *
- * @param int $buildID
- * @param int $bugID
- * @access public
- * @return void
- */
- public function unlinkBug($buildID, $bugID)
- {
- $build = $this->getByID($buildID);
- if(!$build) return;
- $build->bugs = trim(str_replace(",$bugID,", ',', ",$build->bugs,"), ',');
- if($build->bugs) $build->bugs = ',' . $build->bugs;
- $this->dao->update(TABLE_BUILD)->set('bugs')->eq($build->bugs)->where('id')->eq($buildID)->exec();
- $this->loadModel('action')->create('bug', $bugID, 'unlinkedfrombuild', '', $buildID, '', false);
- }
- /**
- * 批量解除Bug跟版本的关联关系。
- * Batch unlink bug.
- *
- * @param int $buildID
- * @param array $bugIdList
- * @access public
- * @return bool
- */
- public function batchUnlinkBug($buildID, $bugIdList)
- {
- if(empty($bugIdList)) return true;
- $build = $this->getByID($buildID);
- if(!$build) return false;
- $build->bugs = ",$build->bugs,";
- foreach($bugIdList as $bugID) $build->bugs = str_replace(",$bugID,", ',', $build->bugs);
- $build->bugs = trim($build->bugs, ',');
- $this->dao->update(TABLE_BUILD)->set('bugs')->eq($build->bugs)->where('id')->eq((int)$buildID)->exec();
- $this->loadModel('action');
- foreach($bugIdList as $unlinkBugID) $this->action->create('bug', (int)$unlinkBugID, 'unlinkedfrombuild', '', $buildID);
- return !dao::isError();
- }
- /**
- * 更新子版本关联的Bug。
- * Bugs and stories associated with child builds.
- *
- * @param object $build
- * @access public
- * @return object
- */
- public function joinChildBuilds($build)
- {
- $build->allBugs = $build->bugs;
- $build->allStories = $build->stories;
- $childBuilds = $this->getByList(explode(',', $build->builds));
- foreach($childBuilds as $childBuild)
- {
- if($childBuild->bugs) $build->allBugs .= ",{$childBuild->bugs}";
- if($childBuild->stories) $build->allStories .= ",{$childBuild->stories}";
- }
- $build->allBugs = explode(',', $build->allBugs);
- $build->allBugs = implode(',', array_unique(array_filter($build->allBugs)));
- $build->allStories = explode(',', $build->allStories);
- $build->allStories = implode(',', array_unique(array_filter($build->allStories)));
- return $build;
- }
- /**
- * 检查按钮是否可用。
- * Adjust the action is clickable.
- *
- * @param object $buikd
- * @param string $action
- * @param string $module
- * @access public
- * @return bool
- * @param object $build
- */
- public static function isClickable($build, $action, $module = 'bug')
- {
- $action = strtolower($action);
- if($module == 'testtask' && $action == 'create') return !$build->executionDeleted;
- return true;
- }
- /**
- * 为区块获取版本数据。
- * Get build's data for block.
- *
- * @param int $projectID
- * @param string $orderBy
- * @param int $limit
- * @access public
- * @return object[]
- */
- public function getBuildBlockData($projectID = 0, $orderBy = 'id_desc', $limit = 10)
- {
- return $this->dao->select('*')->from(TABLE_BUILD)
- ->where('deleted')->eq('0')
- ->beginIF(!$this->app->user->admin)->andWhere('execution')->in($this->app->user->view->sprints)->fi()
- ->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
- ->orderBy($orderBy)
- ->limit($limit)
- ->fetchAll();
- }
- /**
- * 处理版本编辑前没有执行的情况。
- * Process build for update when the build has no execution.
- *
- * @param object $build
- * @param object $oldBuild
- * @access public
- * @return object
- */
- public function processBuildForUpdate($build, $oldBuild)
- {
- if(!empty($oldBuild->execution)) return $build;
- $buildBranch = array();
- foreach(explode(',', trim($build->branch, ',')) as $branchID) $buildBranch[$branchID] = $branchID;
- /* Get delete builds. */
- $deleteBuilds = array();
- $newBuilds = isset($build->builds) ? explode(',', $build->builds) : array();
- foreach($newBuilds as $oldBuildID)
- {
- if(empty($oldBuildID)) continue;
- if(!in_array($oldBuildID, $newBuilds)) $deleteBuilds[$oldBuildID] = $oldBuildID;
- }
- /* Delete the branch when the branch of the deleted build has no linked stories. */
- $storyBranches = $this->dao->select('branch')->from(TABLE_STORY)->where('id')->in($oldBuild->stories)->fetchPairs('branch');
- $branches = $this->dao->select('branch')->from(TABLE_BUILD)->where('id')->in($newBuilds + $deleteBuilds)->fetchPairs();
- foreach($branches as $branch)
- {
- foreach(explode(',', $branch) as $branchID)
- {
- if(empty($branchID)) continue;
- if(in_array($branchID, $deleteBuilds) && isset($storyBranches[$branchID])) continue;
- if(in_array($branchID, $newBuilds) && isset($buildBranch[$branchID])) continue;
- if(in_array($branchID, $deleteBuilds)) unset($buildBranch[$branchID]);
- if(in_array($branchID, $newBuilds)) $buildBranch[$branchID] = $branchID;
- }
- }
- $build->branch = implode(',', $buildBranch);
- return $build;
- }
- /**
- * 获取版本关联的bug列表。
- * Get bug list of build.
- *
- * @param string $bugIdList
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return array
- */
- public function getBugList($bugIdList, $orderBy = '', $pager = null)
- {
- return $this->dao->select('*')->from(TABLE_BUG)
- ->where('id')->in($bugIdList)
- ->andWhere('deleted')->eq(0)
- ->beginIF($orderBy)->orderBy($orderBy)->fi()
- ->page($pager)
- ->fetchAll();
- }
- /**
- * 获取版本关联的story列表。
- * Get story list of build.
- *
- * @param string $storyIdList
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return array
- * @param int $branch
- */
- public function getStoryList($storyIdList, $branch = 0, $orderBy = '', $pager = null)
- {
- $stories = $this->dao->select("*, IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder")->from(TABLE_STORY)
- ->where('id')->in($storyIdList)
- ->andWhere('deleted')->eq(0)
- ->beginIF($orderBy)->orderBy($orderBy)->fi()
- ->page($pager)
- ->fetchAll('id');
- $stages = $this->dao->select('*')->from(TABLE_STORYSTAGE)->where('story')->in(array_keys($stories))->andWhere('branch')->eq($branch)->fetchPairs('story', 'stage');
- foreach($stages as $storyID => $stage) $stories[$storyID]->stage = $stage;
- return $stories;
- }
- /**
- * 给版本中的发布增加标识。
- * Add label for the release in the builds.
- *
- * @param int $productID
- * @param array $builds
- * @access protected
- * @return array
- */
- public function addReleaseLabelForBuilds($productID, $builds)
- {
- $releases = $this->getRelatedReleases(array($productID));
- $buildItems = array();
- $buildsPinyin = common::convert2Pinyin($builds);
- foreach($builds as $buildID => $buildName) $buildItems[$buildID] = array('value' => $buildID, 'text' => $buildName, 'keys' => $buildID . $buildName . (!empty($buildsPinyin[$buildID]) ? $buildsPinyin[$buildID] : ''));
- foreach($releases as $release)
- {
- if(isset($buildItems[$release->shadow])) $buildItems[$release->shadow]['content'] = array('html' => "<div class='flex clip'>{$buildItems[$release->shadow]['text']}</div><label class='label bg-primary-50 text-primary ml-1 flex-none'>{$this->lang->release->common}</label>", 'class' => 'w-full flex nowrap');
- }
- return array_values($buildItems);
- }
- }
|