control.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. <?php
  2. /**
  3. * The control file of build module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package build
  9. * @version $Id: control.php 4992 2013-07-03 07:21:59Z chencongzhi520@gmail.com $
  10. * @link https://www.zentao.net
  11. */
  12. class build extends control
  13. {
  14. /**
  15. * 公共函数,设置产品型项目属性。
  16. * Common actions.
  17. *
  18. * @param int $projectID
  19. * @access public
  20. * @return void
  21. */
  22. public function commonActions($projectID = 0)
  23. {
  24. $hidden = '';
  25. if($projectID)
  26. {
  27. $project = $this->loadModel('project')->getByID($projectID);
  28. if(!$project->hasProduct) $hidden = 'hide';
  29. $this->view->multipleProject = $project->multiple;
  30. }
  31. $this->view->hidden = $hidden;
  32. $this->view->projectID = $projectID;
  33. }
  34. /**
  35. * 创建一个版本。
  36. * Create a build.
  37. *
  38. * @param int $executionID
  39. * @param int $productID
  40. * @param int $projectID
  41. * @access public
  42. * @return void
  43. */
  44. public function create($executionID = 0, $productID = 0, $projectID = 0)
  45. {
  46. $this->loadModel('execution');
  47. $this->loadModel('project');
  48. $this->loadModel('file');
  49. /* Set menu. */
  50. if($this->app->tab == 'project')
  51. {
  52. $this->project->setMenu($projectID);
  53. }
  54. elseif(in_array($this->app->tab, array('execution', 'qa')))
  55. {
  56. $execution = $this->execution->getByID($executionID);
  57. $projectID = $execution ? $execution->project : 0;
  58. }
  59. if($this->app->tab == 'execution') $this->execution->setMenu($executionID);
  60. if(!empty($_POST))
  61. {
  62. $build = $this->buildZen->buildBuildForCreate();
  63. if(!empty($_FILES['buildFiles'])) $_FILES['files'] = $_FILES['buildFiles'];
  64. if(strpos($this->config->build->create->requiredFields, 'files') !== false && empty($_FILES['files']['name'][0]))
  65. {
  66. return $this->sendError(array('message' => sprintf($this->lang->error->notempty, $this->lang->build->files)));
  67. }
  68. unset($_FILES['buildFiles']);
  69. if(dao::isError()) return $this->sendError(dao::getError());
  70. if(commonModel::isTutorialMode()) return $this->sendSuccess(array('closeModal' => true)); // Fix bug #21095.
  71. $buildID = $this->build->create($build);
  72. if(dao::isError()) return $this->sendError(dao::getError());
  73. $message = $this->executeHooks($buildID);
  74. if($message) $this->lang->saveSuccess = $message;
  75. if(helper::isAjaxRequest('modal')) return $this->sendSuccess(array('closeModal' => true, 'callback' => 'refreshExecutionBuild()'));
  76. return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID"), 'id' => $buildID));
  77. }
  78. if(in_array($this->app->tab, array('execution', 'project'))) $this->session->set('project', $projectID);
  79. $status = empty($this->config->CRProduct) ? 'noclosed' : '';
  80. $this->buildZen->assignCreateData($productID, $executionID, $projectID, $status);
  81. }
  82. /**
  83. * 编辑一个版本。
  84. * Edit a build.
  85. *
  86. * @param int $buildID
  87. * @access public
  88. * @return void
  89. */
  90. public function edit($buildID)
  91. {
  92. if(!empty($_POST))
  93. {
  94. $build = $this->buildZen->buildBuildForEdit($buildID);
  95. $changes = $this->build->update($buildID, $build);
  96. if(dao::isError()) return $this->sendError(dao::getError());
  97. $change[$buildID] = $changes;
  98. $this->unlinkOldBranch($change);
  99. if($changes)
  100. {
  101. $actionID = $this->loadModel('action')->create('build', $buildID, 'Edited');
  102. if(!empty($changes)) $this->action->logHistory($actionID, $changes);
  103. }
  104. $message = $this->executeHooks($buildID);
  105. if($message) $this->lang->saveSuccess = $message;
  106. return $this->sendSuccess(array('locate' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID") . "#app={$this->app->tab}"));
  107. }
  108. $this->loadModel('execution');
  109. $this->loadModel('product');
  110. $build = $this->build->getById($buildID);
  111. /* Set menu. */
  112. if($this->app->tab == 'project') $this->loadModel('project')->setMenu($build->project);
  113. if($this->app->tab == 'execution')
  114. {
  115. $this->execution->setMenu((int)$build->execution);
  116. $this->view->executionID = $build->execution;
  117. }
  118. $this->commonActions($build->project);
  119. $this->buildZen->assignEditData($build);
  120. }
  121. /**
  122. * 版本详情。
  123. * View a build.
  124. *
  125. * @param int $buildID
  126. * @param string $type
  127. * @param string $link
  128. * @param string $param
  129. * @param string $orderBy
  130. * @param int $recTotal
  131. * @param int $recPerPage
  132. * @param int $pageID
  133. * @access public
  134. * @return void
  135. */
  136. public function view($buildID, $type = 'story', $link = 'false', $param = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
  137. {
  138. $build = $this->build->getByID($buildID, true);
  139. if(!$build)
  140. {
  141. if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'code' => 404, 'message' => '404 Not found'));
  142. return $this->send(array('result' => 'success', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('execution', 'all'))));
  143. }
  144. /* Load pager. */
  145. $this->app->loadClass('pager', true);
  146. if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
  147. $sort = common::appendOrder($orderBy);
  148. if(strpos($sort, 'pri_') !== false && $type == 'story') $sort = str_replace('pri_', 'priOrder_', $sort);
  149. $bugPager = new pager($type == 'bug' ? $recTotal : 0, $recPerPage, $type == 'bug' ? $pageID : 1);
  150. $generatedBugPager = new pager($type == 'generatedBug' ? $recTotal : 0, $recPerPage, $type == 'generatedBug' ? $pageID : 1);
  151. $this->buildZen->assignBugVarsForView($build, $type, $sort, $param, $bugPager, $generatedBugPager);
  152. $storyPager = new pager($type == 'story' ? $recTotal : 0, $recPerPage, $type == 'story' ? $pageID : 1);
  153. $this->buildZen->assignProductVarsForView($build, $type, $sort, $storyPager);
  154. /* Set menu. */
  155. $this->buildZen->setMenuForView($build);
  156. $this->commonActions($build->project);
  157. $this->executeHooks($buildID);
  158. /* Assign. */
  159. $this->view->canBeChanged = common::canBeChanged('build', $build); // Determines whether an object is editable.
  160. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  161. $this->view->build = $build;
  162. $this->view->actions = $this->loadModel('action')->getList('build', $buildID);
  163. $this->view->link = $link;
  164. $this->view->orderBy = $orderBy;
  165. $this->view->grades = $this->loadModel('story')->getGradePairs('story', 'all');
  166. $this->view->showGrade = $this->config->edition == 'ipd';
  167. $this->view->execution = $this->loadModel('execution')->getByID((int)$build->execution);
  168. $this->view->childBuilds = empty($build->builds) ? array() : $this->build->getByList(explode(',', $build->builds));
  169. $this->view->productID = $build->product;
  170. $this->view->systemList = $this->loadModel('system')->getPairs();
  171. $this->display();
  172. }
  173. /**
  174. * 删除一个版本。
  175. * Delete a build.
  176. *
  177. * @param int $buildID
  178. * @param string $from execution|project
  179. * @access public
  180. * @return void
  181. */
  182. public function delete($buildID, $from = 'execution')
  183. {
  184. $build = $this->build->getById($buildID);
  185. $this->build->delete(TABLE_BUILD, $buildID);
  186. $message = $this->executeHooks($buildID);
  187. if($message) $this->lang->saveSuccess = $message;
  188. if(dao::isError()) return $this->sendError(dao::getError());
  189. $moduleName = $from == 'project' ? 'projectbuild' : $from;
  190. $methodName = $from == 'project' ? 'browse' : 'build';
  191. return $this->sendSuccess(array('load' => $this->createLink($moduleName, $methodName, "executionID={$build->$from}")));
  192. }
  193. /**
  194. * 重构获取产品下的版本下拉列表。
  195. * AJAX: get builds of a product in html select.
  196. *
  197. * @param int $productID
  198. * @param string $varName the name of the select object to create
  199. * @param string $build build to selected
  200. * @param string $branch
  201. * @param string $type get all builds or some builds belong to normal releases and executions are not done.
  202. * @access public
  203. * @return string
  204. */
  205. public function ajaxGetProductBuilds($productID, $varName, $build = '', $branch = 'all', $type = 'normal')
  206. {
  207. $isJsonView = $this->app->getViewType() == 'json';
  208. if($varName == 'openedBuild' )
  209. {
  210. $params = $type == 'all' ? 'withbranch,noreleased,noreplace' : 'noterminate,nodone,withbranch,noreleased';
  211. $builds = $this->build->getBuildPairs(array($productID), $branch, $params, 0, 'project', $build);
  212. if($isJsonView) return print(json_encode($builds));
  213. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  214. return print(json_encode($builds));
  215. }
  216. if($varName == 'openedBuilds' )
  217. {
  218. $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,noreleased', 0, 'project', $build);
  219. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  220. return $this->send($builds);
  221. }
  222. if($varName == 'resolvedBuild')
  223. {
  224. $params = $type == 'all' ? 'withbranch,noreleased' : 'noterminate,nodone,withbranch,noreleased';
  225. $builds = $this->build->getBuildPairs(array($productID), $branch, $params, 0, 'project', $build);
  226. if($isJsonView) return print(json_encode($builds));
  227. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  228. return print(json_encode($builds));
  229. }
  230. $builds = $this->build->getBuildPairs(array($productID), $branch, $type, 0, 'project', $build, false);
  231. if($isJsonView) return print(json_encode($builds));
  232. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  233. return print(json_encode($builds));
  234. }
  235. /**
  236. * 获取项目下的版本下拉列表。
  237. * AJAX: get builds of a project in html select.
  238. *
  239. * @param int $projectID
  240. * @param string $varName the name of the select object to create
  241. * @param string $build build to selected
  242. * @param string|int $branch
  243. * @param bool $needCreate if need to append the link of create build
  244. * @param string $type get all builds or some builds belong to normal releases and executions are not done.
  245. * @access public
  246. * @return string
  247. * @param int $productID
  248. * @param int $system
  249. */
  250. public function ajaxGetProjectBuilds($projectID, $productID, $varName, $build = '', $branch = 'all', $needCreate = false, $type = 'normal', $system = 0)
  251. {
  252. $isJsonView = $this->app->getViewType() == 'json';
  253. if($varName == 'openedBuild')
  254. {
  255. if(empty($projectID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type);
  256. $params = $type == 'all' ? 'noempty,withbranch,noreleased' : 'noempty,noterminate,nodone,withbranch,noreleased';
  257. $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $projectID, 'project', $build);
  258. if($isJsonView) return print(json_encode($builds));
  259. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  260. return print(json_encode($builds));
  261. }
  262. if($varName == 'resolvedBuild')
  263. {
  264. if(empty($projectID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type);
  265. $params = ($type == 'all') ? 'withbranch,noreleased' : 'noterminate,nodone,withbranch,noreleased';
  266. $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $projectID, 'project', $build);
  267. if($isJsonView) return print(json_encode($builds));
  268. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  269. return print(json_encode($builds));
  270. }
  271. if(empty($projectID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type);
  272. $builds = $this->build->getBuildPairs(array($productID), $branch, $type, $projectID, 'project', $build, false, (int)$system);
  273. if($isJsonView) return print(json_encode($builds));
  274. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  275. return print(json_encode($builds));
  276. }
  277. /**
  278. * 获取执行下的版本下拉列表。
  279. * AJAX: get builds of an execution in html select.
  280. *
  281. * @param int $executionID
  282. * @param int $productID
  283. * @param string $varName the name of the select object to create
  284. * @param string $build build to selected
  285. * @param string $branch
  286. * @param bool $needCreate if need to append the link of create build
  287. * @param string $type get all builds or some builds belong to normal releases and executions are not done.
  288. * @access public
  289. * @return string
  290. */
  291. public function ajaxGetExecutionBuilds($executionID, $productID, $varName, $build = '', $branch = 'all', $needCreate = false, $type = 'normal')
  292. {
  293. $isJsonView = $this->app->getViewType() == 'json';
  294. if($varName == 'openedBuild')
  295. {
  296. if(empty($executionID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type);
  297. $params = ($type == 'all') ? 'noempty,noreleased' : 'noempty,noterminate,nodone,noreleased';
  298. $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $executionID, 'execution', $build);
  299. if($isJsonView) return print(json_encode($builds));
  300. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  301. return print(json_encode($builds));
  302. }
  303. if($varName == 'openedBuilds')
  304. {
  305. if(empty($executionID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type);
  306. $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,noreleased', $executionID, 'execution', $build);
  307. $buildList = array();
  308. foreach($builds as $buildID => $buildName) $buildList[] = array('value' => $buildID, 'text' => $buildName);
  309. return $this->send($buildList);
  310. }
  311. if($varName == 'resolvedBuild')
  312. {
  313. if(empty($executionID)) return $this->ajaxGetProductBuilds($productID, $varName, $build, $branch, $type);
  314. $params = ($type == 'all') ? ',noreleased' : 'noterminate,nodone,noreleased';
  315. $builds = $this->build->getBuildPairs(array($productID), $branch, $params, $executionID, 'execution', $build);
  316. if($isJsonView) return print(json_encode($builds));
  317. return print(html::select($varName, $builds, $build, "class='form-control'"));
  318. }
  319. if($varName == 'testTaskBuild')
  320. {
  321. $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,notrunk', $executionID, 'execution', '', false);
  322. if($isJsonView) return print(json_encode($builds));
  323. $builds = $this->build->addReleaseLabelForBuilds($productID, $builds);
  324. return print(json_encode($builds));
  325. }
  326. if($varName == 'dropdownList')
  327. {
  328. $builds = $this->build->getBuildPairs(array($productID), $branch, 'noempty,notrunk', $executionID, 'execution');
  329. if($isJsonView) return print(json_encode($builds));
  330. $list = "<div class='list-group'>";
  331. foreach($builds as $buildID => $buildName) $list .= html::a(inlink('view', "buildID={$buildID}"), $buildName);
  332. $list .= '</div>';
  333. return print($list);
  334. }
  335. }
  336. /**
  337. * 获取最后一次创建的版本。
  338. * Ajax get last build.
  339. *
  340. * @param int $projectID
  341. * @param int $executionID
  342. * @access public
  343. * @return string
  344. */
  345. public function ajaxGetLastBuild($projectID, $executionID)
  346. {
  347. $lastBuild = $this->build->getLast($executionID, $projectID);
  348. if(!$lastBuild) return print('');
  349. echo "<div class='help-block'> &nbsp; " . $this->lang->build->last . ": <a class='code label light' id='lastBuildBtn'>" . $lastBuild->name . "</a></div>";
  350. }
  351. /**
  352. * 版本关联需求。
  353. * Link stories to build.
  354. *
  355. * @param int $buildID
  356. * @param string $browseType
  357. * @param int $param
  358. * @param string $orderBy
  359. * @param int $recTotal
  360. * @param int $recPerPage
  361. * @param int $pageID
  362. * @access public
  363. * @return void
  364. */
  365. public function linkStory($buildID = 0, $browseType = '', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
  366. {
  367. if(!empty($_POST['stories']))
  368. {
  369. if($this->post->stories) $this->build->linkStory($buildID, $this->post->stories);
  370. if(dao::isError()) return $this->sendError(dao::getError());
  371. return $this->send(array('result' => 'success', 'load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story")));
  372. }
  373. $this->session->set('storyList', $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story&link=true&param=" . helper::safe64Encode("&browseType=$browseType&queryID=$param")), $this->app->tab);
  374. $build = $this->build->getById($buildID);
  375. $product = $this->loadModel('product')->getById($build->product);
  376. if($this->app->tab == 'project')
  377. {
  378. $this->loadModel('project')->setMenu($build->project);
  379. $this->view->projectID = $build->project;
  380. }
  381. if($build->execution) $this->loadModel('execution')->setMenu((int)$build->execution);
  382. /* Load pager. */
  383. $this->app->loadClass('pager', $static = true);
  384. $pager = new pager($recTotal, $recPerPage, $pageID);
  385. /* Build search form. */
  386. $this->buildZen->buildLinkStorySearchForm($build, $browseType == 'bySearch' ? (int)$param : 0, isset($product->type) ? $product->type : 'normal');
  387. $this->loadModel('story');
  388. $executionID = $build->execution ? (int)$build->execution : (int)$build->project;
  389. $excludeStoryIdList = $this->buildZen->getExcludeStoryIdList($build);
  390. if($browseType == 'bySearch')
  391. {
  392. $allStories = $this->story->getBySearch($build->product, $build->branch, (int)$param, $orderBy, $executionID, 'story', $excludeStoryIdList, '', $pager);
  393. }
  394. else
  395. {
  396. $allStories = $this->story->getExecutionStories($executionID, $build->product, $orderBy, 'byBranch', $build->branch, 'story', $excludeStoryIdList, $pager);
  397. }
  398. $checkedRows = array();
  399. foreach($allStories as $story)
  400. {
  401. if(in_array($story->stage, array('developed', 'tested', 'closed'))) $checkedRows[] = $story->id;
  402. }
  403. $this->view->allStories = $allStories;
  404. $this->view->checkedRows = $checkedRows;
  405. $this->view->build = $build;
  406. $this->view->buildStories = empty($build->stories) ? array() : $this->story->getByList($build->stories);
  407. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  408. $this->view->browseType = $browseType;
  409. $this->view->param = $param;
  410. $this->view->pager = $pager;
  411. $this->view->grades = $this->story->getGradePairs('story', 'all');
  412. $this->view->showGrade = $this->config->edition == 'ipd';
  413. $this->view->orderBy = $orderBy;
  414. $this->display();
  415. }
  416. /**
  417. * 解除需求关联。
  418. * Unlink story.
  419. *
  420. * @param int $buildID
  421. * @param int $storyID
  422. * @access public
  423. * @return bool
  424. */
  425. public function unlinkStory($buildID, $storyID)
  426. {
  427. $this->build->unlinkStory($buildID, $storyID);
  428. $this->loadModel('action')->create($this->app->rawModule, $buildID, 'unlinkstory', '', $storyID);
  429. return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story")));
  430. }
  431. /**
  432. * 检查并输出移除移除需求和Bug分支的提示信息。
  433. * AJAX: Get unlinkBranch story and bug.
  434. *
  435. * @param int $buildID
  436. * @param int $newBranch
  437. * @access public
  438. * @return void
  439. */
  440. public function ajaxGetBranch($buildID, $newBranch)
  441. {
  442. $build = $this->build->getByID($buildID);
  443. if(!$build) return '';
  444. $buildStories = $build->allStories ? $this->loadModel('story')->getByList($build->allStories) : array();
  445. $buildBugs = $build->allBugs ? $this->loadModel('bug')->getByIdList($build->allBugs) : array();
  446. $branchPairs = $this->loadModel('branch')->getPairs($build->product);
  447. $typeName = $this->lang->product->branchName[$build->productType];
  448. $removeBranches = '';
  449. foreach(explode(',', $build->branch) as $oldBranchID)
  450. {
  451. if($oldBranchID && strpos(",$newBranch,", ",$oldBranchID,") === false) $removeBranches .= "{$branchPairs[$oldBranchID]},";
  452. }
  453. $unlinkStoryCounts = 0;
  454. $unlinkBugCounts = 0;
  455. if($build->branch)
  456. {
  457. foreach($buildStories as $storyID => $story)
  458. {
  459. if($story->branch && strpos(",$newBranch,", ",$story->branch,") === false) $unlinkStoryCounts ++;
  460. }
  461. foreach($buildBugs as $bugID => $bug)
  462. {
  463. if($bug->branch && strpos(",$newBranch,", ",$bug->branch,") === false) $unlinkBugCounts ++;
  464. }
  465. }
  466. if($unlinkStoryCounts && $unlinkBugCounts)
  467. {
  468. printf($this->lang->build->confirmChangeBuild, $typeName, trim($removeBranches, ','), $typeName, $unlinkStoryCounts, $unlinkBugCounts);
  469. }
  470. elseif($unlinkStoryCounts)
  471. {
  472. printf($this->lang->build->confirmRemoveStory, $typeName, trim($removeBranches, ','), $typeName, $unlinkStoryCounts);
  473. }
  474. elseif($unlinkBugCounts)
  475. {
  476. printf($this->lang->build->confirmRemoveBug, $typeName, trim($removeBranches, ','), $typeName, $unlinkBugCounts);
  477. }
  478. }
  479. /**
  480. * 批量解除需求关联。
  481. * Batch unlink story.
  482. *
  483. * @param int $buildID
  484. * @access public
  485. * @return bool
  486. */
  487. public function batchUnlinkStory($buildID)
  488. {
  489. if($this->post->storyIdList) $this->build->batchUnlinkStory($buildID, $this->post->storyIdList);
  490. return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=story")));
  491. }
  492. /**
  493. * 编辑版本时,解除跟未关联分支的需求和Bug的关联。
  494. * Unlink story and bug when edit branch of build.
  495. *
  496. * @param array $changes
  497. * @access pulic
  498. * @return void
  499. */
  500. public function unlinkOldBranch($changes)
  501. {
  502. foreach($changes as $buildID => $changeList)
  503. {
  504. $oldBranch = '';
  505. $newBranch = '';
  506. foreach($changeList as $change)
  507. {
  508. if($change['field'] == 'branch')
  509. {
  510. $oldBranch = $change['old'];
  511. $newBranch = $change['new'];
  512. break;
  513. }
  514. }
  515. $build = $this->build->getByID($buildID);
  516. $planStories = $build->allStories ? $this->loadModel('story')->getByList($build->allStories) : array();
  517. $planBugs = $build->allBugs ? $this->loadModel('bug')->getByIdList($build->allBugs) : array();
  518. if($oldBranch)
  519. {
  520. foreach($planStories as $storyID => $story)
  521. {
  522. if($story->branch and strpos(",$newBranch,", ",$story->branch,") === false) $this->build->unlinkStory($buildID, $storyID);
  523. }
  524. foreach($planBugs as $bugID => $bug)
  525. {
  526. if($bug->branch and strpos(",$newBranch,", ",$bug->branch,") === false) $this->build->unlinkBug($buildID, $bugID);
  527. }
  528. }
  529. }
  530. }
  531. /**
  532. * 版本关联Bug。
  533. * Link bug.
  534. *
  535. * @param int $buildID
  536. * @param string $browseType
  537. * @param int $param
  538. * @param int $recTotal
  539. * @param int $recPerPage
  540. * @param int $pageID
  541. * @access public
  542. * @return void
  543. */
  544. public function linkBug($buildID = 0, $browseType = '', $param = 0, $recTotal = 0, $recPerPage = 100, $pageID = 1)
  545. {
  546. if(!empty($_POST['bugs']))
  547. {
  548. $this->build->linkBug($buildID, $this->post->bugs, (array)$this->post->resolvedBy);
  549. return $this->send(array('result' => 'success', 'load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug")));
  550. }
  551. $this->session->set('bugList', $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug&link=true&param=" . helper::safe64Encode("&browseType=$browseType&queryID=$param")), 'qa');
  552. /* Set menu. */
  553. $build = $this->build->getByID($buildID);
  554. $product = $this->loadModel('product')->getByID($build->product);
  555. if($this->app->tab == 'project')
  556. {
  557. $this->loadModel('project')->setMenu($build->project);
  558. $this->view->projectID = $build->project;
  559. }
  560. if($build->execution) $this->loadModel('execution')->setMenu($build->execution);
  561. /* Build the search form. */
  562. $queryID = $browseType == 'bySearch' ? $param : 0;
  563. $this->buildZen->buildLinkBugSearchForm($build, $queryID, isset($product->type) ? $product->type : 'normal');
  564. /* Load pager. */
  565. $this->app->loadClass('pager', $static = true);
  566. $pager = new pager($recTotal, $recPerPage, $pageID);
  567. $this->loadModel('bug');
  568. $executionID = $build->execution ? $build->execution : $build->project;
  569. if($browseType == 'bySearch')
  570. {
  571. $allBugs = $this->bug->getBySearch('bug', $build->product, $build->branch, $build->project, $build->execution, $queryID, $build->allBugs, 'id_desc', $pager);
  572. }
  573. else
  574. {
  575. $allBugs = $this->bug->getExecutionBugs($executionID, 0, 'all', array($buildID), 'noclosed', 0, 'status_desc,id_desc', $build->allBugs, $pager);
  576. }
  577. $this->view->allBugs = $allBugs;
  578. $this->view->build = $build;
  579. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  580. $this->view->browseType = $browseType;
  581. $this->view->param = $param;
  582. $this->view->pager = $pager;
  583. $this->display();
  584. }
  585. /**
  586. * 解除Bug跟版本的关联关系。
  587. * Unlink bug.
  588. *
  589. * @param int $buildID
  590. * @param int $bugID
  591. * @access public
  592. * @return void
  593. */
  594. public function unlinkBug($buildID, $bugID)
  595. {
  596. $this->build->unlinkBug($buildID, $bugID);
  597. return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug")));
  598. }
  599. /**
  600. * 批量解除Bug跟版本的关联关系。
  601. * Batch unlink bugs.
  602. *
  603. * @param int $buildID
  604. * @access public
  605. * @return void
  606. */
  607. public function batchUnlinkBug($buildID)
  608. {
  609. if($this->post->bugIdList) $this->build->batchUnlinkBug($buildID, $this->post->bugIdList);
  610. return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID&type=bug")));
  611. }
  612. /**
  613. * 根据选择的产品获取应用列表。
  614. * Get system list by product.
  615. *
  616. * @param int $productID
  617. * @access public
  618. * @return string
  619. */
  620. public function ajaxGetSystemList($productID)
  621. {
  622. $systemList = $this->loadModel('system')->getPairs($productID, '0', 'active');
  623. $system = array();
  624. foreach($systemList as $systemID => $systemName) $system[] = array('text' => $systemName, 'value' => $systemID);
  625. echo json_encode($system);
  626. }
  627. /**
  628. * 根据选择的系统获取版本列表。
  629. * Get build list by system.
  630. *
  631. * @param int $productID
  632. * @param int $systemID
  633. * @access public
  634. * @return void
  635. */
  636. public function ajaxGetSystemBuilds($productID, $systemID)
  637. {
  638. $builds = $this->build->getBuildPairs(array($productID), 'all', 'noterminate, nodone, notrunk, withbranch', 0, 'product', '', false, (int)$systemID);
  639. $buildList = array();
  640. foreach($builds as $buildID => $buildName) $buildList[] = array('text' => $buildName, 'value' => $buildID);
  641. echo json_encode($buildList);
  642. }
  643. }