control.php 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773
  1. <?php
  2. /**
  3. * The control file of bug currentModule 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 bug
  9. * @version $Id: control.php 5107 2013-07-12 01:46:12Z chencongzhi520@gmail.com $
  10. * @link https://www.zentao.net
  11. */
  12. class bug extends control
  13. {
  14. /**
  15. * 所有产品。
  16. * All products.
  17. *
  18. * @var array
  19. * @access public
  20. */
  21. public $products = array();
  22. /**
  23. * 当前项目编号。
  24. * Project id.
  25. *
  26. * @var int
  27. * @access public
  28. */
  29. public $projectID = 0;
  30. /**
  31. * 构造函数
  32. *
  33. * 1.加载其他模块model类。
  34. * 2.获取产品,并输出到视图
  35. *
  36. * The construct function.
  37. *
  38. * 1. Load model of other modules.
  39. * 2. Get products and assign to view.
  40. *
  41. * @param string $moduleName
  42. * @param string $methodName
  43. * @access public
  44. * @return void
  45. */
  46. public function __construct($moduleName = '', $methodName = '')
  47. {
  48. parent::__construct($moduleName, $methodName);
  49. $this->loadModel('product');
  50. $this->loadModel('tree');
  51. $this->loadModel('user');
  52. $this->loadModel('action');
  53. $this->loadModel('story');
  54. $this->loadModel('task');
  55. $this->loadModel('qa');
  56. /* Get product data. */
  57. $products = array();
  58. if(!isInModal())
  59. {
  60. $tab = ($this->app->tab == 'project' or $this->app->tab == 'execution') ? $this->app->tab : 'qa';
  61. $mode = (strpos(',create,edit,', ",{$this->app->methodName},") !== false and empty($this->config->CRProduct)) ? 'noclosed' : '';
  62. $objectID = ($tab == 'project' or $tab == 'execution') ? $this->session->{$tab} : 0;
  63. if($tab == 'project' or $tab == 'execution')
  64. {
  65. if(common::isTutorialMode())
  66. {
  67. $products = $this->loadModel('tutorial')->getProductPairs();
  68. }
  69. else
  70. {
  71. $products = $this->product->getProducts($objectID, $mode, '', false);
  72. }
  73. }
  74. else
  75. {
  76. $products = $this->product->getPairs($mode, 0, '', 'all');
  77. }
  78. if(empty($products) && (helper::isAjaxRequest('zin') || helper::isAjaxRequest('fetch'))) $this->locate($this->createLink('product', 'showErrorNone', "moduleName=$tab&activeMenu=bug&objectID=$objectID"));
  79. }
  80. else
  81. {
  82. $mode = empty($this->config->CRProduct) ? 'noclosed' : '';
  83. $products = $this->product->getPairs($mode, 0, '', 'all');
  84. }
  85. $this->view->products = $this->products = $products;
  86. }
  87. /**
  88. * Bug 列表。
  89. * Browse bugs.
  90. *
  91. * @param int $productID
  92. * @param string $branch
  93. * @param string $browseType
  94. * @param int $param
  95. * @param string $orderBy
  96. * @param int $recTotal
  97. * @param int $recPerPage
  98. * @param int $pageID
  99. * @access public
  100. * @return void
  101. * @param string $from
  102. * @param int $blockID
  103. */
  104. public function browse($productID = 0, $branch = '', $browseType = '', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 20, $pageID = 1, $from = 'bug', $blockID = 0)
  105. {
  106. if($from == 'doc' || $from === 'ai')
  107. {
  108. $this->app->loadLang('doc');
  109. $realProducts = $this->product->getPairs('nodeleted', 0, '', 'all');
  110. if(empty($realProducts)) return $this->send(array('result' => 'fail', 'message' => $this->lang->doc->tips->noProduct));
  111. }
  112. /* 把访问的产品ID等状态信息保存到session和cookie中。*/
  113. /* Save the product id user last visited to session and cookie. */
  114. $productID = $this->product->checkAccess($productID, $this->products);
  115. $product = $this->product->getByID($productID);
  116. $branch = $this->bugZen->getBrowseBranch($branch, (string)$product->type);
  117. $browseType = $browseType ? strtolower($browseType) : 'unclosed';
  118. /* 设置1.5级导航相关信息。*/
  119. /* Set the 1.5 nav. */
  120. $this->qa->setMenu($productID, $branch);
  121. /* 设置排序字段。*/
  122. /* Set the order field. */
  123. if(!$orderBy) $orderBy = $this->cookie->qaBugOrder ? $this->cookie->qaBugOrder : 'id_desc';
  124. $this->bugZen->setBrowseCookie((object)$product, $branch, $browseType, $param, $orderBy);
  125. $this->bugZen->setBrowseSession($browseType);
  126. /* 处理列表页面的参数。*/
  127. /* Processing browse params. */
  128. $modules = $this->tree->getOptionMenu((int)$product->id, 'bug', 0, $branch);
  129. list($moduleID, $queryID, $realOrderBy, $pager) = $this->bugZen->prepareBrowseParams($browseType, $param, $orderBy, $recTotal, $recPerPage, $pageID);
  130. if(!isset($modules[$moduleID])) $moduleID = 0;
  131. $actionURL = $this->createLink('bug', 'browse', "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID&from=$from&blockID=$blockID");
  132. $this->bugZen->buildBrowseSearchForm($productID, $branch, $queryID, $actionURL);
  133. $executions = $this->loadModel('execution')->fetchPairs($this->projectID, 'all', false);
  134. $bugs = $this->bugZen->getBrowseBugs((int)$product->id, $branch, $browseType, array_keys($executions), $moduleID, $queryID, $realOrderBy, $pager);
  135. $this->bugZen->buildBrowseView($bugs, (object)$product, $branch, $browseType, $moduleID, $executions, $param, $orderBy, $pager);
  136. $this->view->from = $from;
  137. $this->view->blockID = $blockID;
  138. $this->view->idList = '';
  139. if($from === 'doc')
  140. {
  141. $docBlock = $this->loadModel('doc')->getDocBlock($blockID);
  142. $this->view->docBlock = $docBlock;
  143. if($docBlock)
  144. {
  145. $content = json_decode($docBlock->content, true);
  146. if(isset($content['idList'])) $this->view->idList = $content['idList'];
  147. }
  148. }
  149. $this->view->modules = $modules;
  150. $this->display();
  151. }
  152. /**
  153. * 查看一个 bug。
  154. * View a bug.
  155. *
  156. * @param int $bugID
  157. * @access public
  158. * @return void
  159. */
  160. public function view($bugID)
  161. {
  162. $bug = $this->bug->getByID($bugID, true);
  163. /* 判断 bug 是否存在。*/
  164. /* Judge bug exits or not. */
  165. if(!$bug) return $this->send(array('result' => 'success', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('qa', 'index'))));
  166. /* 检查用户是否拥有所属执行的权限。*/
  167. /* Check bug execution priv. */
  168. $this->bugZen->checkBugExecutionPriv($bug);
  169. /* Update action. */
  170. if($bug->assignedTo == $this->app->user->account) $this->loadModel('action')->read('bug', $bugID);
  171. if(!isInModal()) $this->bugZen->setViewMenu($bug);
  172. $product = $this->product->getByID($bug->product);
  173. $branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($bug->product);
  174. $projects = $this->product->getProjectPairsByProduct($bug->product, (string)$bug->branch);
  175. $projectID = isset($projects[$bug->project]) ? $bug->project : key($projects);
  176. if(in_array($this->app->tab, array('project', 'execution')))
  177. {
  178. $objectType = $this->app->tab == 'project' ? 'projectID' : 'executionID';
  179. $this->view->{$objectType} = $this->session->{$this->app->tab};
  180. }
  181. if($this->app->tab == 'devops') $this->bugZen->processRepoIssueActions((int)$bug->repo);
  182. $builds = $this->loadModel('build')->getBuildPairs(array($bug->product), 'all', 'hasdeleted');
  183. if($bug->resolvedBuild && !isset($builds[$bug->resolvedBuild]))
  184. {
  185. $build = $this->build->fetchByID((int)$bug->resolvedBuild);
  186. $builds[$build->id] = $build->name;
  187. }
  188. $this->session->set('project', $projectID, 'project');
  189. $this->session->set('storyList', '', 'product');
  190. $this->session->set('projectList', $this->app->getURI(true) . "#app={$this->app->tab}", 'project');
  191. $this->view->title = "BUG #$bug->id $bug->title - " . $product->name;
  192. $this->view->branchID = $bug->branch;
  193. $this->view->product = $product;
  194. $this->view->project = $this->loadModel('project')->getByID($bug->project);
  195. $this->view->projects = $projects;
  196. $this->view->executions = $this->product->getExecutionPairsByProduct($bug->product, (string)$bug->branch, (int)$projectID, 'noclosed');
  197. $this->view->bug = $bug;
  198. $this->view->bugModule = empty($bug->module) ? '' : $this->tree->getByID($bug->module);
  199. $this->view->modulePath = $this->loadModel('tree')->getParents($bug->module);
  200. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  201. $this->view->branches = $branches;
  202. $this->view->branchName = $product->type == 'normal' ? '' : zget($branches, $bug->branch, '');
  203. $this->view->builds = $builds;
  204. $this->view->linkCommits = $this->loadModel('repo')->getCommitsByObject($bug->id, 'bug');
  205. $this->view->actions = $this->loadModel('action')->getList('bug', $bug->id);
  206. $this->view->preAndNext = $this->loadModel('common')->getPreAndNextObject('bug', $bugID);
  207. $this->display();
  208. }
  209. /**
  210. * 创建一个 bug。
  211. * Create a bug.
  212. *
  213. * @param int $productID
  214. * @param string $branch
  215. * @param string $extras Other params, for example, executionID=10,moduleID=10.
  216. * @access public
  217. * @return void
  218. */
  219. public function create($productID, $branch = '', $extras = '')
  220. {
  221. if(isset($_POST['stepIdList']))
  222. {
  223. $extras .= "&stepIdList={$this->post->stepIdList}";
  224. unset($_POST['stepIdList']);
  225. }
  226. $extras = str_replace(array(',', ' ', '*'), array('&', '', '-'), $extras);
  227. parse_str($extras, $params);
  228. $from = isset($params['from']) ? $params['from'] : '';
  229. if(!empty($_POST))
  230. {
  231. if(!empty($params['productID'])) $product = $this->bug->fetchByID((int)$params['productID'], 'product');
  232. $formData = form::data($this->config->bug->form->create, 0, !empty($product->workflowGroup) ? $product->workflowGroup : 0);
  233. $bug = $this->bugZen->prepareCreateExtras($formData);
  234. $bugID = $this->bug->create($bug, $from);
  235. if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
  236. $bug->id = $bugID;
  237. $this->bugZen->afterCreate($bug, $params, $from);
  238. $message = $this->executeHooks($bugID);
  239. return $this->responseAfterCreate($bug, $params, $message);
  240. }
  241. $productID = $this->product->checkAccess($productID, $this->products);
  242. $product = $this->product->getByID($productID);
  243. if($branch === '') $branch = (string)$this->cookie->preBranch;
  244. $this->bugZen->setCreateMenu($productID, $branch, $params);
  245. /* 初始化一个bug对象,尽可能把属性都绑定到bug对象上,extract() 出来的变量除外。 */
  246. /* Init bug, give bug as many variables as possible, except for extract variables. */
  247. $fields = array('id' => 0, 'productID' => $productID, 'branch' => $branch, 'title' => ($from == 'sonarqube' ? $_COOKIE['sonarqubeIssue'] : ''), 'assignedTo' => (isset($product->QD) ? $product->QD : ''));
  248. $fields = array_merge($fields, $params);
  249. $fields['product'] = $fields['productID'];
  250. $bug = $this->bugZen->initBug($fields);
  251. /* 处理复制 bug,从用例、测试单、日志转 bug。 */
  252. /* Handle copy bug, bug from case, testtask, todo. */
  253. $bug = $this->bugZen->extractObjectFromExtras($bug, $params);
  254. /* 获取分支、版本、需求、项目、执行、产品、项目的模式、用例,构造$this->view。*/
  255. /* Get branches, builds, stories, project, projects, executions, products, project model, cases and build create form. */
  256. $this->bugZen->buildCreateForm($bug, $params, $from);
  257. extract($params);
  258. $extrasValue = 'productID={product},moduleID={module},projectID={project},executionID={execution},regionID={region},allBuilds={allBuilds},allUsers={allUsers}' . (empty($from) ? '' : "&from=$from");
  259. if(!empty($fromType)) $extrasValue .= ",fromType={$fromType}";
  260. if(!empty($fromID)) $extrasValue .= ",fromID={$fromID}";
  261. $this->view->loadUrl = $this->createLink('bug', 'create', "productID={$productID}&branch={branch}&extras={$extrasValue}");
  262. $this->display();
  263. }
  264. /**
  265. * 更新 bug 信息。
  266. * Edit a bug.
  267. *
  268. * @param int $bugID
  269. * @param bool $comment true|false
  270. * @access public
  271. * @return void
  272. */
  273. public function edit($bugID, $comment = false)
  274. {
  275. $oldBug = $this->bug->getByID($bugID);
  276. if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  277. if(!empty($_POST))
  278. {
  279. $formData = form::data($this->config->bug->form->edit, $bugID);
  280. $bug = $this->bugZen->prepareEditExtras($formData, $oldBug);
  281. $this->bugZen->checkRquiredForEdit($bug);
  282. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  283. $changes = array();
  284. if(!$comment)
  285. {
  286. $changes = $this->bug->update($bug);
  287. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  288. $this->bugZen->afterUpdate($bug, $oldBug);
  289. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  290. }
  291. /* Get response after editing bug. */
  292. $message = $this->executeHooks($bugID);
  293. return $this->bugZen->responseAfterOperate($bugID, $changes, $message);
  294. }
  295. $this->bugZen->checkBugExecutionPriv($oldBug);
  296. $this->bugZen->setEditMenu($oldBug);
  297. $this->bugZen->buildEditForm($oldBug);
  298. $this->display();
  299. }
  300. /**
  301. * 指派 bug。
  302. * Assign bug.
  303. *
  304. * @param int $bugID
  305. * @access public
  306. * @return void
  307. */
  308. public function assignTo($bugID)
  309. {
  310. /* 获取更新前的 bug,并且检查所属执行的权限。*/
  311. /* Get old bug and check privilege of the execution. */
  312. $oldBug = $this->bug->getByID($bugID);
  313. if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  314. $this->bugZen->checkBugExecutionPriv($oldBug);
  315. if(!empty($_POST))
  316. {
  317. /* 初始化 bug 数据。*/
  318. /* Init bug data. */
  319. $bug = form::data($this->config->bug->form->assignTo, $bugID)->add('id', $bugID)->get();
  320. if($this->app->rawMethod == 'batchassignto') unset($bug->mailto);
  321. if($oldBug->status != 'closed') $this->bug->assign($bug, $oldBug);
  322. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  323. $message = $this->executeHooks($bugID);
  324. if(!$message) $message = $this->lang->saveSuccess;
  325. return $this->send(array('result' => 'success', 'message' => $message, 'closeModal' => true, 'load' => true));
  326. }
  327. $this->qa->setMenu($oldBug->product, $oldBug->branch);
  328. /* 获取指派给的用户列表。*/
  329. /* Get users that can be assigned to. */
  330. if($this->app->tab == 'project' || $this->app->tab == 'execution')
  331. {
  332. $users = $this->bugZen->getAssignedToPairs($oldBug);
  333. }
  334. else
  335. {
  336. $users = $this->loadModel('user')->getPairs('devfirst|noclosed');
  337. }
  338. $this->view->title = $this->lang->bug->assignTo;
  339. $this->view->bug = $oldBug;
  340. $this->view->users = $users;
  341. $this->display();
  342. }
  343. /**
  344. * 确认 bug。
  345. * confirm a bug.
  346. *
  347. * @param int $bugID
  348. * @param string $kanbanParams fromColID=,toColID=,fromLaneID=,toLaneID=,regionID=
  349. * @param string $from taskkanban
  350. * @access public
  351. * @return void
  352. */
  353. public function confirm($bugID, $kanbanParams = '', $from = '')
  354. {
  355. $oldBug = $this->bug->getByID($bugID);
  356. if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  357. /* 检查 bug 所属执行的权限。*/
  358. /* Check privilege for execution of the bug. */
  359. $this->bugZen->checkBugExecutionPriv($oldBug);
  360. if(!empty($_POST))
  361. {
  362. /* 处理看板相关的参数。*/
  363. /* Process the params related to kanban. */
  364. $kanbanParams = str_replace(array(',', ' '), array('&', ''), $kanbanParams);
  365. parse_str($kanbanParams, $kanbanData);
  366. /* 构造 bug 的表单数据。*/
  367. /* Structure the bug form data. */
  368. $bug = form::data($this->config->bug->form->confirm, $bugID)->add('id', $bugID)->setDefault('confirmed', 1)->get();
  369. $this->bug->confirm($bug, $kanbanData);
  370. /* 返回确认 bug 后的响应。 */
  371. /* Return response after confirming bug. */
  372. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  373. $message = $this->executeHooks($bugID);
  374. return $this->bugZen->responseAfterOperate($bugID, array(), $message, (!empty($kanbanParams) || $from));
  375. }
  376. $this->qa->setMenu($oldBug->product, $oldBug->branch);
  377. /* 展示相关变量。 */
  378. /* Show the variables associated. */
  379. $this->view->title = $this->lang->bug->confirm;
  380. $this->view->bug = $oldBug;
  381. $this->view->users = $this->loadModel('user')->getPairs('noclosed', $oldBug->assignedTo);
  382. $this->view->actions = $this->loadModel('action')->getList('bug', $bugID);
  383. $this->display();
  384. }
  385. /**
  386. * 解决bug。
  387. * Resolve a bug.
  388. *
  389. * @param int $bugID
  390. * @param string $extra
  391. * @access public
  392. * @return void
  393. */
  394. public function resolve($bugID, $extra = '')
  395. {
  396. /* 获取 bug 信息,并检查 bug 所属执行的权限。*/
  397. /* Get bug info, and check privilege of bug 所属执行的权限。*/
  398. $oldBug = $this->bug->getById($bugID);
  399. if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  400. $this->bugZen->checkBugExecutionPriv($oldBug);
  401. if(!empty($_POST))
  402. {
  403. /* 解析信息,并获取变量。 */
  404. /* Parse extra, and get variables. */
  405. $extra = str_replace(array(',', ' '), array('&', ''), $extra);
  406. parse_str($extra, $output);
  407. /* 初始化 bug 数据。 */
  408. /* Init bug data. */
  409. $bug = $this->bugZen->buildBugForResolve($oldBug);
  410. /* 检查解决 bug 的必填项。 */
  411. /* Check required fields. */
  412. $this->bugZen->checkRequiredForResolve($bug, $oldBug->execution);
  413. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  414. /* 如果需要创建版本,创建版本。 */
  415. /* If you need to create a build, create one. */
  416. if($bug->createBuild == 'on')
  417. {
  418. $this->bug->createBuild($bug, $oldBug);
  419. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  420. }
  421. /* 只有非关闭状态的 bug 可以解决。 */
  422. /* Only unclosed bug can be resolved. */
  423. if($oldBug->status != 'closed') $this->bug->resolve($bug, $output);
  424. /* 返回解决 bug 后的响应。 */
  425. /* Return response after resolving bug. */
  426. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  427. $message = $this->executeHooks($bugID);
  428. return $this->bugZen->responseAfterOperate($bugID, common::createChanges($oldBug, $bug), $message);
  429. }
  430. /* 移除解决方案“转需求”。 */
  431. /* Remove 'Convert to story' from the solution list. */
  432. unset($this->lang->bug->resolutionList['tostory']);
  433. /* 设置菜单。 */
  434. /* Set menu. */
  435. $this->qa->setMenu($oldBug->product, $oldBug->branch);
  436. $users = $this->loadModel('user')->getPairs('noclosed');
  437. $builds = $this->loadModel('build')->getBuildPairs(array($oldBug->product), $oldBug->branch, 'withbranch,noreleased,nofail');
  438. /* 展示相关变量。 */
  439. /* Show the variables associated. */
  440. $this->view->title = $this->lang->bug->resolve;
  441. $this->view->bug = $oldBug;
  442. $this->view->execution = $oldBug->execution ? $this->loadModel('execution')->getByID($oldBug->execution) : '';
  443. $this->view->users = $users;
  444. $this->view->assignedTo = isset($users[$oldBug->openedBy]) ? $oldBug->openedBy : $this->bug->getModuleOwner($oldBug->module, $oldBug->product);
  445. $this->view->executions = $this->loadModel('product')->getExecutionPairsByProduct($oldBug->product, $oldBug->branch ? "0,{$oldBug->branch}" : '0', (int)$oldBug->project, 'stagefilter');
  446. $this->view->builds = $this->build->addReleaseLabelForBuilds($oldBug->product, $builds);
  447. $this->view->actions = $this->loadModel('action')->getList('bug', $bugID);
  448. $this->display();
  449. }
  450. /**
  451. * 激活一个bug。
  452. * Activate a bug.
  453. *
  454. * @param int $bugID
  455. * @param string $kanbanInfo a string of kanban info, for example, 'fromColID=1,toColID=2,fromLaneID=1,toLaneID=2,regionID=1'.
  456. * @access public
  457. * @return void
  458. */
  459. public function activate($bugID, $kanbanInfo = '')
  460. {
  461. /* 获取 bug 信息,并检查 bug 所属执行的权限。*/
  462. /* Get bug info, and check privilege of bug 所属执行的权限。*/
  463. $oldBug = $this->bug->getByID($bugID);
  464. if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  465. $this->bugZen->checkBugExecutionPriv($oldBug);
  466. if(!empty($_POST))
  467. {
  468. /* 只有状态为解决或者关闭的bug才可以激活。 */
  469. /* Only bugs whose status is resolved or closed can be activated. */
  470. if($oldBug->status != 'resolved' && $oldBug->status != 'closed')
  471. {
  472. dao::$errors[] = $this->lang->bug->error->cannotActivate;
  473. return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  474. }
  475. /* 获取bug信息。 */
  476. /* Build bug data. */
  477. $bug = form::data($this->config->bug->form->activate, $bugID)->setDefault('assignedTo', $oldBug->resolvedBy)->add('activatedCount', $oldBug->activatedCount + 1)->add('id', $bugID)->add('resolvedDate', null)->add('closedDate', null)->get();
  478. /* 解析看板信息。 */
  479. /* Parse kanban information. */
  480. $kanbanInfo = str_replace(array(',', ' '), array('&', ''), $kanbanInfo);
  481. parse_str($kanbanInfo, $kanbanParams);
  482. $this->bug->activate($bug, $kanbanParams);
  483. /* 返回激活 bug 后的响应。 */
  484. /* Return response after activating bug. */
  485. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  486. $message = $this->executeHooks($bugID);
  487. return $this->bugZen->responseAfterOperate($bugID, array(), $message);
  488. }
  489. $productID = $oldBug->product;
  490. $this->qa->setMenu($productID, $oldBug->branch);
  491. /* 展示相关变量。 */
  492. /* Show the variables associated. */
  493. $this->view->title = $this->lang->bug->activate;
  494. $this->view->bug = $oldBug;
  495. $this->view->users = $this->loadModel('user')->getPairs('noclosed', $oldBug->resolvedBy);
  496. $this->view->builds = $this->loadModel('build')->getBuildPairs(array($productID), $oldBug->branch, 'noempty,noreleased,nofail', 0, 'execution', $oldBug->openedBuild);
  497. $this->view->actions = $this->loadModel('action')->getList('bug', $bugID);
  498. $this->display();
  499. }
  500. /**
  501. * 根据Bug的ID来关闭Bug。
  502. * Close a bug.
  503. *
  504. * @param int $bugID
  505. * @param string $extra
  506. * @access public
  507. * @return void
  508. */
  509. public function close($bugID, $extra = '')
  510. {
  511. /* 获取 bug 信息,并检查 bug 所属执行的权限。*/
  512. /* Get bug info, and check privilege of bug 所属执行的权限。*/
  513. $oldBug = $this->bug->getByID($bugID);
  514. if(!$oldBug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  515. $this->bugZen->checkBugExecutionPriv($oldBug);
  516. if(!empty($_POST))
  517. {
  518. /* 设置bug信息。 */
  519. /* Set bug information. */
  520. $bug = form::data($this->config->bug->form->close, $bugID)->add('id', $bugID)->get();
  521. /* 设置额外变量。 */
  522. /* Set extra variables. */
  523. $extra = str_replace(array(',', ' '), array('&', ''), $extra);
  524. parse_str($extra, $output);
  525. $this->bug->close($bug, $output);
  526. /* 返回关闭 bug 后的响应。 */
  527. /* Return response after closing bug. */
  528. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  529. $message = $this->executeHooks($bugID);
  530. return $this->bugZen->responseAfterOperate($bugID, array(), $message);
  531. }
  532. $this->view->title = $this->lang->bug->close;
  533. $this->view->bug = $oldBug;
  534. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  535. $this->view->actions = $this->loadModel('action')->getList('bug', $oldBug->id);
  536. $this->display();
  537. }
  538. /**
  539. * 删除 bug。
  540. * Delete a bug.
  541. *
  542. * @param int $bugID
  543. * @param string $from taskkanban
  544. * @access public
  545. * @return void
  546. */
  547. public function delete($bugID, $from = '')
  548. {
  549. /* 删除 bug。 */
  550. /* Delete bug. */
  551. $bug = $this->bug->getByID($bugID);
  552. if(!$bug) return $this->send(array('result' => 'fail', 'message' => $this->lang->bug->error->notExist));
  553. $this->bug->delete(TABLE_BUG, $bugID);
  554. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  555. /* 返回删除 bug 后的响应。 */
  556. /* Return response after deleting bug. */
  557. $message = $this->executeHooks($bugID);
  558. return $this->bugZen->responseAfterDelete($bug, $from, $message);
  559. }
  560. /**
  561. * 导出bug数据。
  562. * Get data to export
  563. *
  564. * @param int $productID
  565. * @param string $browseType
  566. * @param int $executionID
  567. * @access public
  568. * @return void
  569. */
  570. public function export($productID, $browseType = '', $executionID = 0)
  571. {
  572. if($_POST)
  573. {
  574. /* 设置bug导出的参数。 */
  575. /* Set bug export params. */
  576. if($executionID)
  577. {
  578. $productIdList = $this->loadModel('product')->getProductPairsByProject($executionID);
  579. $productIdList = array_keys($productIdList);
  580. }
  581. else
  582. {
  583. $productIdList = array($productID);
  584. }
  585. $this->session->set('bugTransferParams', array('productID' => $productID, 'executionID' => $executionID, 'branch' => 'all', 'productIdList' => $productIdList));
  586. /* 设置导出数据源。 */
  587. /* Set export data source. */
  588. if(!$productID || $browseType == 'bysearch')
  589. {
  590. /* 设置模块数据源。 */
  591. /* Set module data source. */
  592. $this->config->bug->dtable->fieldList['module']['dataSource'] = array('module' => 'tree', 'method' => 'getAllModulePairs', 'params' => 'bug');
  593. /* 如果导出执行的bug,设置数据源。 */
  594. /* In execution, set data source. */
  595. if($executionID)
  596. {
  597. $object = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch();
  598. $projectID = $object->type == 'project' ? $object->id : $object->parent;
  599. $this->config->bug->dtable->fieldList['project']['dataSource'] = array('module' => 'project', 'method' => 'getPairsByIdList', 'params' => array('projectIdList' => array($projectID)));
  600. $this->config->bug->dtable->fieldList['execution']['dataSource'] = array('module' => 'execution', 'method' => 'getPairs', 'params' => $projectID);
  601. }
  602. }
  603. $this->config->bug->dtable->fieldList['assignedTo']['dataSource'] = array('module' => 'user', 'method' => 'getPairs', 'params' => array());
  604. $this->loadModel('transfer')->export('bug');
  605. $this->fetch('file', 'export2' . $this->post->fileType, $_POST);
  606. }
  607. /* 获取产品信息。 */
  608. /* Get product information. */
  609. $product = $this->loadModel('product')->getByID($productID);
  610. /* 展示相关变量。 */
  611. /* Show the variables associated. */
  612. $this->view->fileName = $this->bugZen->getExportFileName($executionID, $browseType, $product);
  613. $this->view->allExportFields = $this->bugZen->getExportFields($executionID, $product);
  614. $this->view->customExport = true;
  615. $this->display('file', 'export');
  616. }
  617. /**
  618. * Bug 的统计报表。
  619. * The report page.
  620. *
  621. * @param int $productID
  622. * @param string $browseType
  623. * @param int $branchID
  624. * @param int $moduleID
  625. * @param string $chartType
  626. * @access public
  627. * @return void
  628. */
  629. public function report($productID, $browseType, $branchID, $moduleID, $chartType = 'pie')
  630. {
  631. $this->loadModel('report');
  632. $charts = $datas = array();
  633. if(!empty($_POST))
  634. {
  635. foreach($this->post->charts as $chart)
  636. {
  637. $chartFunc = 'getDataOf' . $chart;
  638. $chartData = $this->bug->$chartFunc();
  639. $charts[$chart] = $this->bugZen->mergeChartOption($chart, $chartType);
  640. $datas[$chart] = $this->report->computePercent($chartData);
  641. }
  642. }
  643. /* 如果是影子产品并且对应的项目不是多迭代项目,删掉迭代 Bug 数量报表。*/
  644. /* Unset execution bugs report if the product is shadow and corresponding project is not multiple. */
  645. $project = $this->loadModel('project')->getByShadowProduct($productID);
  646. if(!empty($project) && !$project->multiple) unset($this->lang->bug->report->charts['bugsPerExecution']);
  647. $this->qa->setMenu($productID, $branchID);
  648. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->bug->common . $this->lang->hyphen . $this->lang->bug->reportChart;
  649. $this->view->productID = $productID;
  650. $this->view->browseType = $browseType;
  651. $this->view->branchID = $branchID;
  652. $this->view->moduleID = $moduleID;
  653. $this->view->chartType = $chartType;
  654. $this->view->checkedCharts = $this->post->charts ? implode(',', $this->post->charts) : '';
  655. $this->view->charts = $charts;
  656. $this->view->datas = $datas;
  657. $this->display();
  658. }
  659. /**
  660. * 关联相关 bug。
  661. * Link related bugs.
  662. *
  663. * @param int $bugID
  664. * @param string $bySearch
  665. * @param string $excludeBugs
  666. * @param int $queryID
  667. * @param int $recTotal
  668. * @param int $recPerPage
  669. * @param int $pageID
  670. * @access public
  671. * @return void
  672. */
  673. public function linkBugs($bugID, $bySearch = 'false', $excludeBugs = '', $queryID = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
  674. {
  675. $bug = $this->bug->getByID($bugID);
  676. $bySearch = $bySearch === 'true';
  677. /* 检查 bug 所属执行的权限。*/
  678. /* Check privilege of bug 所属执行的权限。*/
  679. $this->bugZen->checkBugExecutionPriv($bug);
  680. $this->qa->setMenu($bug->product, $bug->branch);
  681. $this->bugZen->buildSearchFormForLinkBugs($bug, $excludeBugs, $queryID);
  682. /* Load pager. */
  683. $this->app->loadClass('pager', true);
  684. $pager = new pager($recTotal, $recPerPage, $pageID);
  685. /* Assign. */
  686. $this->view->title = $this->lang->bug->linkBugs . "BUG #$bug->id $bug->title {$this->lang->dash} " . $this->products[$bug->product];
  687. $this->view->bug = $bug;
  688. $this->view->bugs2Link = $this->bug->getBugs2Link($bugID, $bySearch, $excludeBugs, $queryID, $pager);
  689. $this->view->users = $this->user->getPairs('noletter');
  690. $this->view->pager = $pager;
  691. $this->display();
  692. }
  693. /**
  694. * 批量创建 bug。
  695. * Batch create bug.
  696. *
  697. * @param int $productID
  698. * @param string $branch
  699. * @param int $executionID
  700. * @param int $moduleID
  701. * @param string $extra
  702. * @access public
  703. * @return void
  704. */
  705. public function batchCreate($productID, $branch = '', $executionID = 0, $moduleID = 0, $extra = '')
  706. {
  707. $extra = str_replace(array(',', ' '), array('&', ''), $extra);
  708. parse_str($extra, $output);
  709. $bugImagesFile = $this->session->bugImagesFile ? $this->session->bugImagesFile : array();
  710. if(!empty($_POST))
  711. {
  712. $this->config->bug->create->requiredFields = $this->config->bug->batchcreate->requiredFields;
  713. $bugs = $this->bugZen->buildBugsForBatchCreate($productID, $branch, $bugImagesFile);
  714. $bugs = $this->bugZen->checkBugsForBatchCreate($bugs);
  715. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  716. $message = '';
  717. $bugIdList = array();
  718. foreach($bugs as $bug)
  719. {
  720. if(!empty($bug->assignedTo)) $bug->assignedDate = helper::now();
  721. $bug->id = $this->bug->create($bug);
  722. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  723. /* 批量创建后的一些其他操作。*/
  724. /* Processing other operations after batch creation. */
  725. $this->bugZen->afterBatchCreate($bug, $output);
  726. $message = $this->executeHooks($bug->id);
  727. $bugIdList[] = $bug->id;
  728. }
  729. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  730. $this->loadModel('score')->create('ajax', 'batchCreate');
  731. return $this->bugZen->responseAfterBatchCreate($productID, $branch, $executionID, $bugIdList, $message);
  732. }
  733. /* 把访问的产品ID等状态信息保存到session和cookie中。*/
  734. /* Save the product id user last visited to session and cookie. */
  735. $productID = $this->product->checkAccess($productID, $this->products);
  736. $product = $this->product->getByID($productID);
  737. /* 设置当前分支,并且设置导航。*/
  738. /* Get branch and set menu. */
  739. if($branch === '') $branch = $this->cookie->preBranch;
  740. $this->qa->setMenu($productID, $branch);
  741. /* 展示批量创建bug的相关变量。*/
  742. /* Show the variables associated with the batch creation bugs. */
  743. $this->bugZen->assignBatchCreateVars($executionID, $product, $branch, $output, $bugImagesFile);
  744. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->bug->batchCreate;
  745. $this->view->moduleID = $moduleID;
  746. $this->view->product = $product;
  747. $this->view->users = $this->loadModel('user')->getPairs('noclosed');
  748. $this->display();
  749. }
  750. /**
  751. * 批量编辑 bugs。
  752. * Batch edit bugs.
  753. *
  754. * @param int $productID
  755. * @param string $branch
  756. * @access public
  757. * @return void
  758. */
  759. public function batchEdit($productID = 0, $branch = '0')
  760. {
  761. /* 如果没有选择的 bug,跳转到前一个页面。*/
  762. /* If there is no bug ID, locate to the previous step. */
  763. if(!$this->post->bugIdList && !$this->post->id)
  764. {
  765. $url = !empty($this->session->bugList) ? $this->session->bugList : $this->createLink('qa', 'index');
  766. $this->locate($url);
  767. }
  768. if($this->post->id)
  769. {
  770. /* 为批量编辑 bug 构造数据。*/
  771. /* Build bugs. */
  772. $oldBugs = $this->bug->getByIdList($this->post->id);
  773. $bugs = $this->bugZen->buildBugsForBatchEdit($oldBugs);
  774. /* 检查数据。*/
  775. /* Check bugs. */
  776. $this->bugZen->checkBugsForBatchUpdate($bugs);
  777. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  778. $message = '';
  779. foreach($bugs as $bugID => $bug)
  780. {
  781. $this->bug->update($bug);
  782. $oldBug = $oldBugs[$bugID];
  783. $bug->status = $oldBug->status;
  784. /* 批量编辑 bug 后的一些操作。*/
  785. /* Operate after batch edit bugs. */
  786. $this->bugZen->operateAfterBatchEdit($bug, $oldBug);
  787. /* 获取待处理的任务和计划列表。*/
  788. /* Get toTaskIdList, unlinkPlans and link2Plans to be processed. */
  789. list($toTaskIdList, $unlinkPlans, $link2Plans) = $this->bugZen->getToBeProcessedData($bug, $oldBug);
  790. $message = $this->executeHooks($bug->id);
  791. }
  792. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  793. $this->loadModel('score')->create('ajax', 'batchEdit');
  794. $this->loadModel('action');
  795. foreach($unlinkPlans as $planID => $bugs) $this->action->create('productplan', $planID, 'unlinkbug', '', $bugs);
  796. foreach($link2Plans as $planID => $bugs) $this->action->create('productplan', $planID, 'linkbug', '', $bugs);
  797. /* 批量编辑的返回。*/
  798. /* Response of batch edit. */
  799. return $this->bugZen->responseAfterBatchEdit($toTaskIdList, $message);
  800. }
  801. $this->bugZen->assignBatchEditVars($productID, $branch);
  802. $this->display();
  803. }
  804. /**
  805. * 批量修改 bug 的分支。
  806. * Batch change branch of the bug.
  807. *
  808. * @param int $branchID
  809. * @access public
  810. * @return void
  811. */
  812. public function batchChangeBranch($branchID)
  813. {
  814. if($this->post->bugIdList)
  815. {
  816. /* 准备 bug 修改分支需要的数据。 */
  817. /* Prepare data to change branch. */
  818. $bugIdList = array_unique($this->post->bugIdList);
  819. $oldBugs = $this->bug->getByIdList($bugIdList);
  820. /* 更新 bug 的分支,获取跳过的 bug id 列表。 */
  821. /* Update the branch of the bug, and get the list of bug id to skip. */
  822. $skipBugIdList = '';
  823. foreach($bugIdList as $bugID)
  824. {
  825. $oldBug = $oldBugs[$bugID];
  826. if($branchID != $oldBug->branch)
  827. {
  828. if(empty($oldBug->module))
  829. {
  830. $bug = new stdclass();
  831. $bug->id = (int)$bugID;
  832. $bug->branch = $branchID;
  833. $this->bug->update($bug);
  834. }
  835. else
  836. {
  837. $skipBugIdList .= '[' . $bugID . ']';
  838. }
  839. }
  840. }
  841. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  842. $this->loadModel('score')->create('ajax', 'batchOther');
  843. }
  844. /* 返回批量修改分支 bugs 后的响应。 */
  845. /* Return response after batch changing branch of the bugs. */
  846. $load = $this->session->bugList;
  847. if(!empty($skipBugIdList)) $load = array('confirm' => sprintf($this->lang->bug->notice->noSwitchBranch, $skipBugIdList), 'confirmed' => true, 'canceled' => true);
  848. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $load));
  849. }
  850. /**
  851. * 批量修改 bug 的所属模块。
  852. * Batch change the module of the bug.
  853. *
  854. * @param int $moduleID
  855. * @access public
  856. * @return void
  857. */
  858. public function batchChangeModule($moduleID)
  859. {
  860. if($this->post->bugIdList)
  861. {
  862. /* 获取要修改计划的 bug id 列表。 */
  863. /* Get bug id list to change plan. */
  864. $bugIdList = array_unique($this->post->bugIdList);
  865. foreach($bugIdList as $bugID)
  866. {
  867. /* 更新 bug 的模块。 */
  868. /* Update the module of the bug. */
  869. $bug = new stdclass();
  870. $bug->id = (int)$bugID;
  871. $bug->module = $moduleID;
  872. $this->bug->update($bug);
  873. }
  874. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  875. $this->loadModel('score')->create('ajax', 'batchOther');
  876. }
  877. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->session->bugList));
  878. }
  879. /**
  880. * 批量修改 bug 的计划。
  881. * Batch change the plan of bug.
  882. *
  883. * @param int $planID
  884. * @access public
  885. * @return void
  886. */
  887. public function batchChangePlan($planID)
  888. {
  889. if($this->post->bugIdList)
  890. {
  891. /* 准备 bug 修改计划需要的数据。 */
  892. /* Prepare data to change plan. */
  893. $bugIdList = array_unique($this->post->bugIdList);
  894. $oldBugs = $this->bug->getByIdList($bugIdList);
  895. /* 修改 bugs 的计划。 */
  896. /* Change the plan of bugs. */
  897. $unlinkPlans = array();
  898. $link2Plans = array();
  899. foreach($bugIdList as $bugID)
  900. {
  901. $oldBug = $oldBugs[$bugID];
  902. /* 如果新老计划一致,跳过。 */
  903. /* If the new plan is the same as the old one, skip it. */
  904. if($planID == $oldBug->plan) continue;
  905. /* 关联 bug 到新计划,并取消与旧计划关联。 */
  906. /* Link the bug to the new plan and unlink the bug from the old plan. */
  907. $unlinkPlans[$oldBug->plan] = empty($unlinkPlans[$oldBug->plan]) ? $bugID : "{$unlinkPlans[$oldBug->plan]},$bugID";
  908. $link2Plans[$planID] = empty($link2Plans[$planID]) ? $bugID : "{$link2Plans[$planID]},$bugID";
  909. /* 更新 bug 的计划。 */
  910. /* Update the plan of the bug. */
  911. $bug = new stdclass();
  912. $bug->id = (int)$bugID;
  913. $bug->plan = $planID;
  914. $this->bug->update($bug);
  915. }
  916. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  917. /* 记录 bug 的计划变更动态。 */
  918. /* Record plan action. */
  919. $this->loadModel('action');
  920. foreach($unlinkPlans as $planID => $bugs) $this->action->create('productplan', $planID, 'unlinkbug', '', $bugs);
  921. foreach($link2Plans as $planID => $bugs) $this->action->create('productplan', $planID, 'linkbug', '', $bugs);
  922. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  923. $this->loadModel('score')->create('ajax', 'batchOther');
  924. }
  925. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->session->bugList));
  926. }
  927. /**
  928. * 批量变更 bugs 的指派人。
  929. * Batch update assign of bugs.
  930. *
  931. * @param string $assignedTo
  932. * @param int $objectID projectID|executionID
  933. * @param string $type execution|project|product|my
  934. * @access public
  935. * @return void
  936. */
  937. public function batchAssignTo($assignedTo, $objectID, $type = 'execution')
  938. {
  939. $assignedTo = base64_decode($assignedTo);
  940. if($this->post->bugIdList)
  941. {
  942. /* 获取指派给的 bug id 列表。 */
  943. /* Get bug id list to assign. */
  944. $bugIdList = array_unique($this->post->bugIdList);
  945. $oldBugList = $this->bug->getByIdList($bugIdList);
  946. /* 初始化 bug 数据。 */
  947. /* Init bug data. */
  948. $bug = form::data($this->config->bug->form->assignTo)->remove('mailto')->get();
  949. foreach($bugIdList as $bugID)
  950. {
  951. $oldBug = $oldBugList[$bugID];
  952. if($oldBug->status == 'closed') continue;
  953. /* 构建 bug。 */
  954. /* Build bug. */
  955. $bug->id = (int)$bugID;
  956. $bug->assignedTo = $assignedTo;
  957. $this->bug->assign($bug, $oldBug);
  958. }
  959. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  960. $this->loadModel('score')->create('ajax', 'batchOther');
  961. }
  962. /* 返回批量指派 bugs 后的响应。 */
  963. /* Return response after batch assigning bugs. */
  964. if($type == 'execution') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('execution', 'bug', "executionID=$objectID")));
  965. if($type == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'bug', "projectID=$objectID")));
  966. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
  967. }
  968. /**
  969. * 批量确认 bugs。
  970. * Batch confirm bugs.
  971. *
  972. * @access public
  973. * @return void
  974. */
  975. public function batchConfirm()
  976. {
  977. if($this->post->bugIdList)
  978. {
  979. /* 准备 bug 确认需要的数据。 */
  980. /* Prepare resolve data. */
  981. $bugIdList = array_unique($this->post->bugIdList);
  982. $bugs = $this->bug->getByIdList($bugIdList);
  983. /* 初始化 bug 数据。 */
  984. /* Init bug data. */
  985. $bug = form::data($this->config->bug->form->confirm)->setDefault('confirmed', 1)->remove('pri,type,status,mailto')->get();
  986. foreach($bugIdList as $bugID)
  987. {
  988. $bugID = (int)$bugID;
  989. /* 如果 bug 已经确认过,跳过。 */
  990. /* If bug has been confirmed, skip it. */
  991. if(!empty($bugs[$bugID]->confirmed)) continue;
  992. /* 构建 bug。 */
  993. /* Build bug. */
  994. $bug->id = $bugID;
  995. $bug->confirmed = 1;
  996. $bug->assignedTo = $this->app->user->account;
  997. unset($bug->deadline);
  998. $this->bug->confirm($bug);
  999. $message = $this->executeHooks($bugID);
  1000. }
  1001. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  1002. $this->loadModel('score')->create('ajax', 'batchOther');
  1003. }
  1004. /* 返回批量确认 bugs 后的响应。 */
  1005. /* Return response after batch confirming bugs. */
  1006. if(empty($message)) $message = $this->lang->saveSuccess;
  1007. return $this->send(array('result' => 'success', 'message' => $message, 'load' => true));
  1008. }
  1009. /**
  1010. * 批量解决 bugs。
  1011. * Batch resolve bugs.
  1012. *
  1013. * @param string $resolution
  1014. * @param string $resolvedBuild
  1015. * @access public
  1016. * @return void
  1017. */
  1018. public function batchResolve($resolution, $resolvedBuild = '')
  1019. {
  1020. if($this->post->bugIdList)
  1021. {
  1022. /* 准备 bug 解决 需要的数据。 */
  1023. /* Prepare resolve data. */
  1024. $bugIdList = array_unique($this->post->bugIdList);
  1025. $bugs = $this->bug->getByIdList($bugIdList);
  1026. list($modules, $productQD) = $this->bugZen->getBatchResolveVars($bugs);
  1027. $users = $this->loadModel('user')->getPairs();
  1028. $now = helper::now();
  1029. $skipBugList = array();
  1030. foreach($bugIdList as $bugID)
  1031. {
  1032. /* 只有激活的bug或者解决方案不为已解决的bug可以解决。 */
  1033. /* Only active bugs or bugs whose resolution is not fixed can be resolve. */
  1034. $oldBug = $bugs[$bugID];
  1035. if($oldBug->resolution == 'fixed' || $oldBug->status != 'active')
  1036. {
  1037. $skipBugList[] = '#' . $bugID;
  1038. continue;
  1039. }
  1040. /* 获取 bug 的指派给人员。 */
  1041. /* Get bug assignedTo. */
  1042. $assignedTo = $oldBug->openedBy;
  1043. if(!isset($users[$assignedTo]))
  1044. {
  1045. $assignedTo = '';
  1046. $module = isset($modules[$oldBug->module]) ? $modules[$oldBug->module] : '';
  1047. while($module)
  1048. {
  1049. if($module->owner and isset($users[$module->owner]))
  1050. {
  1051. $assignedTo = $module->owner;
  1052. break;
  1053. }
  1054. $module = isset($modules[$module->parent]) ? $modules[$module->parent] : '';
  1055. }
  1056. if(empty($assignedTo)) $assignedTo = $productQD;
  1057. }
  1058. /* 构建 bug。 */
  1059. /* Build bug. */
  1060. $bug = new stdClass();
  1061. $bug->id = (int)$bugID;
  1062. $bug->resolution = $resolution;
  1063. $bug->resolvedBuild = $resolution == 'fixed' ? $resolvedBuild : '';
  1064. $bug->resolvedBy = $this->app->user->account;
  1065. $bug->resolvedDate = $now;
  1066. $bug->status = 'resolved';
  1067. $bug->confirmed = 1;
  1068. $bug->assignedTo = $assignedTo;
  1069. $bug->assignedDate = $now;
  1070. $this->bug->resolve($bug);
  1071. $message = $this->executeHooks($bug->id);
  1072. }
  1073. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  1074. $this->loadModel('score')->create('ajax', 'batchOther');
  1075. }
  1076. /* 返回批量解决 bugs 后的响应。 */
  1077. /* Return response after batch resolving bugs. */
  1078. if(!empty($skipBugList)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->bug->notice->skipNotActive, implode(', ', $skipBugList)), 'load' => true));
  1079. if(empty($message)) $message = $this->lang->saveSuccess;
  1080. return $this->send(array('result' => 'success', 'message' => $message, 'load' => true));
  1081. }
  1082. /**
  1083. * 批量关闭BUG。
  1084. * Batch close bugs.
  1085. *
  1086. * @param int $releaseID
  1087. * @param string $viewType
  1088. * @access public
  1089. * @return void
  1090. */
  1091. public function batchClose($releaseID = 0, $viewType = '')
  1092. {
  1093. /* 关闭解决的 bugs。 */
  1094. /* Close resolved bugs. */
  1095. $bugIdList = $releaseID ? $this->post->unlinkBugs : $this->post->bugIdList;
  1096. $skipBugs = array();
  1097. if($bugIdList)
  1098. {
  1099. $bugIdList = array_unique($bugIdList);
  1100. $bugs = $this->bug->getByIdList($bugIdList);
  1101. $bug = form::data($this->config->bug->form->close)->get();
  1102. foreach($bugIdList as $bugID)
  1103. {
  1104. $oldBug = $bugs[$bugID];
  1105. if($oldBug->status == 'resolved')
  1106. {
  1107. $bug->id = (int)$bugID;
  1108. $this->bug->close($bug);
  1109. }
  1110. elseif($oldBug->status != 'closed')
  1111. {
  1112. $skipBugs[$bugID] = $bugID;
  1113. }
  1114. }
  1115. $this->loadModel('score')->create('ajax', 'batchOther');
  1116. }
  1117. /* 返回批量关闭 bugs 后的响应。 */
  1118. /* Return response after batch closing bugs. */
  1119. $load = true;
  1120. if($skipBugs) $load = array('confirm' => sprintf($this->lang->bug->notice->skipClose, implode(',', $skipBugs)), 'confirmed' => true, 'canceled' => true);
  1121. if($viewType) $load = $this->createLink($viewType, 'view', "releaseID=$releaseID&type=bug");
  1122. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $load, 'closeModal' => true));
  1123. }
  1124. /**
  1125. * 批量激活bug。
  1126. * Batch activate bugs.
  1127. *
  1128. * @param int $productID
  1129. * @param string $branch
  1130. * @access public
  1131. * @return void
  1132. */
  1133. public function batchActivate($productID, $branch = '0')
  1134. {
  1135. if($this->post->id)
  1136. {
  1137. /* 获取原有 bug 信息。 */
  1138. /* Get old bugs. */
  1139. $oldBugs = $this->bug->getByIdList($this->post->id);
  1140. /* 激活状态是解决或关闭的 bugs。 */
  1141. /* Activate bugs whose status is resolved or closed. */
  1142. $activateBugs = form::batchData($this->config->bug->form->batchActivate)->get();
  1143. foreach($activateBugs as $bugID => $bug)
  1144. {
  1145. if($bug->status != 'resolved' && $bug->status != 'closed') continue;
  1146. $oldBug = $oldBugs[$bugID];
  1147. $bug->status = 'active';
  1148. $bug->activatedCount = $oldBug->activatedCount + 1;
  1149. $this->bug->activate($bug);
  1150. $message = $this->executeHooks($bug->id);
  1151. }
  1152. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  1153. $this->loadModel('score')->create('ajax', 'batchOther');
  1154. /* 返回批量激活 bugs 后的响应。 */
  1155. /* Return response after batch activating bugs. */
  1156. if(empty($message)) $message = $this->lang->saveSuccess;
  1157. return $this->send(array('result' => 'success', 'message' => $message, 'load' => $this->session->bugList));
  1158. }
  1159. /* 如果没有要激活的bugs,跳转到上一个界面。 */
  1160. /* If there are no bugs to activate, locate to the previous interface. */
  1161. if(!$this->post->bugIdList)
  1162. {
  1163. $url = !empty($this->session->bugList) ? $this->session->bugList : $this->createLink('qa', 'index');
  1164. $this->locate($url);
  1165. }
  1166. $this->qa->setMenu($productID, $branch);
  1167. /* 展示关联的变量。 */
  1168. /* Show the variables associated. */
  1169. $bugIdList = array_unique($this->post->bugIdList);
  1170. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->bug->batchActivate;
  1171. $this->view->bugs = $this->bug->getByIdList($bugIdList);
  1172. $this->view->users = $this->user->getPairs('noclosed');
  1173. $this->view->builds = $this->loadModel('build')->getBuildPairs(array($productID), $branch, 'noempty,noreleased,nofail');
  1174. $this->view->productID = $productID;
  1175. $this->display();
  1176. }
  1177. /**
  1178. * 确认 bug 关联的需求变更。
  1179. * Confirm story change.
  1180. *
  1181. * @param int $bugID
  1182. * @access public
  1183. * @return void
  1184. */
  1185. public function confirmStoryChange($bugID)
  1186. {
  1187. /* 获取更新前的 bug,并且检查所属执行的权限。*/
  1188. /* Get old bug and check privilege of the execution. */
  1189. $oldBug = $this->bug->getById($bugID);
  1190. $this->bugZen->checkBugExecutionPriv($oldBug);
  1191. /* 更新 bug 的需求版本。 */
  1192. /* Update the version of the story. */
  1193. $bug = new stdclass();
  1194. $bug->id = $bugID;
  1195. $bug->storyVersion = $oldBug->latestStoryVersion;
  1196. $this->bug->update($bug);
  1197. $this->loadModel('action')->create('bug', $bugID, 'confirmed', '', $oldBug->latestStoryVersion);
  1198. $message = $this->executeHooks($bugID);
  1199. /* 返回确认 bug 需求变更后的响应。 */
  1200. /* Return response after confirming the version change of the story in the bug. */
  1201. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  1202. if(empty($message)) $message = $this->lang->saveSuccess;
  1203. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('view', "bugID={$bugID}")));
  1204. }
  1205. /**
  1206. * AJAX: 获取指派给指定用户的BUG。
  1207. * AJAX: get bugs of a user in html select.
  1208. *
  1209. * @param int $userID
  1210. * @param string $id the id of the select control.
  1211. * @param int $appendID
  1212. * @access public
  1213. * @return string
  1214. */
  1215. public function ajaxGetUserBugs($userID = 0, $id = '' , $appendID = 0)
  1216. {
  1217. /* 如果user id是0,把它设置为当前用户的id。 */
  1218. /* If user id is zero, set it to current user id. */
  1219. if($userID == 0) $userID = $this->app->user->id;
  1220. /* 获取指派给user id的bug。 */
  1221. /* Get bugs of user id. */
  1222. $user = $this->loadModel('user')->getById($userID, 'id');
  1223. $account = $user->account;
  1224. $bugs = $this->bug->getUserBugPairs($account, true, 0, array(), array(), $appendID);
  1225. $items = array();
  1226. foreach($bugs as $bugID => $bugTitle)
  1227. {
  1228. if(empty($bugID)) continue;
  1229. $items[] = array('text' => $bugTitle, 'value' => $bugID);
  1230. }
  1231. $fieldName = $id ? "bugs[$id]" : 'bug';
  1232. return print(json_encode(array('name' => $fieldName, 'items' => $items)));
  1233. }
  1234. /**
  1235. * AJAX:获取 bug 模块的负责人。
  1236. * AJAX: Get bug owner of a module.
  1237. *
  1238. * @param int $moduleID
  1239. * @param int $productID
  1240. * @access public
  1241. * @return int
  1242. */
  1243. public function ajaxGetModuleOwner($moduleID, $productID = 0)
  1244. {
  1245. /* 获取bug模块负责人。 */
  1246. /* Get module owner. */
  1247. list($account, $realname) = $this->bug->getModuleOwner($moduleID, $productID);
  1248. return print(json_encode(array('account' => $account, 'realname' => $realname)));
  1249. }
  1250. /**
  1251. * Ajax 方式加载指派给表单。
  1252. * AJAX: get team members of the executions as assignedTo list.
  1253. *
  1254. * @param int $executionID
  1255. * @access public
  1256. * @return string
  1257. */
  1258. public function ajaxLoadAssignedTo($executionID)
  1259. {
  1260. /* 获取执行团队成员,并将它转为picker组件需要的数据。 */
  1261. /* Get execution team members, and convert it to picker items. */
  1262. $members = $this->user->getTeamMemberPairs($executionID, 'execution');
  1263. $items = array();
  1264. foreach($members as $account => $member)
  1265. {
  1266. if($account) $items[] = array('text' => $member, 'value' => $account);
  1267. }
  1268. return print(json_encode($items));
  1269. }
  1270. /**
  1271. * Ajax 方式加载所有的用户。
  1272. * AJAX: get all users as assignedTo list.
  1273. *
  1274. * @param string $params noletter|noempty|nodeleted|noclosed|withguest|pofirst|devfirst|qafirst|pmfirst|realname|outside|inside|all, can be sets of theme
  1275. * @access public
  1276. * @return array
  1277. */
  1278. public function ajaxLoadAllUsers($params = 'devfirst|noclosed')
  1279. {
  1280. /* 获取所有用户,并将它转为picker组件需要的数据。 */
  1281. /* Get all users, and convert it to picker items. */
  1282. $allUsers = $this->loadModel('user')->getPairs($params);
  1283. $userList = array();
  1284. foreach($allUsers as $account => $user) $userList[] = array('text' => $user, 'value' => $account, 'key' => $user . $account);
  1285. return print(json_encode($userList));
  1286. }
  1287. /**
  1288. * Ajax 方式获取产品下拉菜单。
  1289. * Drop menu page.
  1290. *
  1291. * @param int $productID
  1292. * @param string $module
  1293. * @param string $method
  1294. * @param string $extra
  1295. * @access public
  1296. * @return void
  1297. */
  1298. public function ajaxGetDropMenu($productID, $module, $method, $extra = '')
  1299. {
  1300. /* 如果extra不为空,通过extra获取产品。 */
  1301. /* IF extra is not empty, get products by extra. */
  1302. $products = array();
  1303. if(!empty($extra)) $products = $this->product->getProducts($extra, 'all', 'program desc, line desc, ');
  1304. /* 如果当前系统模式是全生命周期管理模式,设置项目集和产品线信息。 */
  1305. /* If system mode is ALM, show programs and product lines. */
  1306. if($this->config->systemMode == 'ALM')
  1307. {
  1308. $this->view->programs = $this->loadModel('program')->getPairs(true);
  1309. $this->view->lines = $this->product->getLinePairs();
  1310. }
  1311. /* 设置要展示的相关变量。 */
  1312. /* Show the variables associated. */
  1313. $this->view->productID = $productID;
  1314. $this->view->module = $module;
  1315. $this->view->method = $method;
  1316. $this->view->extra = $extra;
  1317. $this->view->products = $products;
  1318. $this->view->projectID = $this->session->project;
  1319. $this->view->link = $this->product->getProductLink($module, $method, $extra);
  1320. $this->display();
  1321. }
  1322. /**
  1323. * AJAX: 获取产品的团队成员。
  1324. * Ajax get product members.
  1325. *
  1326. * @param int $productID
  1327. * @param string $selectedUser
  1328. * @param string $branchID
  1329. * @access public
  1330. * @return string
  1331. */
  1332. public function ajaxGetProductMembers($productID, $selectedUser = '', $branchID = '0')
  1333. {
  1334. /* 获取产品的成员。如果产品团队成员为空,返回未关闭的用户。 */
  1335. /* Get product members. If product members is empty, get noclosed users. */
  1336. $productMembers = $this->bug->getProductMemberPairs($productID, $branchID);
  1337. $productMembers = array_filter($productMembers);
  1338. if(empty($productMembers)) $productMembers = $this->loadModel('user')->getPairs('devfirst|noclosed');
  1339. $items = array();
  1340. foreach($productMembers as $account => $member)
  1341. {
  1342. if($account) $items[] = array('text' => $member, 'value' => $account);
  1343. }
  1344. return print(json_encode($items));
  1345. }
  1346. /**
  1347. * AJAX: 获产品的 bugs。
  1348. * AJAX: Get product bugs.
  1349. *
  1350. * @param int $productID
  1351. * @param int $bugID
  1352. * @param string $type
  1353. * @access public
  1354. * @return int
  1355. * @param string $search
  1356. */
  1357. public function ajaxGetProductBugs($productID, $bugID, $search = '', $type = 'html')
  1358. {
  1359. /* 获取除了这个 bugID 的产品 bugs。 */
  1360. /* Get product bugs exclude this bugID. */
  1361. $limit = $this->get->limit ? $this->get->limit : $this->config->maxCount;
  1362. $productBugs = $this->bug->getProductBugPairs($productID, '', $search, $limit, $productID ? 'single' : 'all');
  1363. unset($productBugs[$bugID]);
  1364. if($type == 'json') return print(helper::jsonEncode($productBugs));
  1365. $bugList = array();
  1366. foreach($productBugs as $bugID => $bugName) $bugList[] = array('value' => $bugID, 'text' => $bugName);
  1367. return $this->send($bugList);
  1368. }
  1369. /**
  1370. * ajax方式获取项目团队成员。
  1371. * Ajax get project team members.
  1372. *
  1373. * @param int $projectID
  1374. * @access public
  1375. * @return string
  1376. */
  1377. public function ajaxGetProjectTeamMembers($projectID)
  1378. {
  1379. /* 获取项目团队成员。 */
  1380. /* Get project team members. */
  1381. $teamMembers = empty($projectID) ? array() : $this->loadModel('project')->getTeamMemberPairs($projectID);
  1382. $items = array();
  1383. foreach($teamMembers as $account => $member)
  1384. {
  1385. if($account)
  1386. {
  1387. $userName = ucfirst(mb_substr($account, 0, 1)) . ':' . ($member ? $member : $account);
  1388. $items[] = array('text' => $userName, 'value' => $account);
  1389. }
  1390. }
  1391. return print(json_encode($items));
  1392. }
  1393. /**
  1394. * ajax方式获取执行类别。
  1395. * Ajax get execution lang.
  1396. *
  1397. * @param int $projectID
  1398. * @access public
  1399. * @return string
  1400. */
  1401. public function ajaxGetExecutionLang($projectID)
  1402. {
  1403. /* 获取项目信息,返回执行的语言项。 */
  1404. /* Get project information and then return language item of the execution. */
  1405. $project = $this->loadModel('project')->getByID($projectID);
  1406. return $project->model == 'kanban' ? print($this->lang->bug->kanban) : print($this->lang->bug->execution);
  1407. }
  1408. /**
  1409. * Ajax 方式获取已发布的版本。
  1410. * Ajax get released builds.
  1411. *
  1412. * @param int $productID
  1413. * @param int|string $branch
  1414. * @access public
  1415. * @return string
  1416. */
  1417. public function ajaxGetReleasedBuilds($productID, $branch = 'all')
  1418. {
  1419. /* 获取已发布的版本。 */
  1420. /* Get released builds. */
  1421. $releasedBuilds = $this->loadModel('release')->getReleasedBuilds($productID, $branch);
  1422. return print(helper::jsonEncode($releasedBuilds));
  1423. }
  1424. /**
  1425. * Ajax 方式获取 bug 所在产品的用例。
  1426. * Ajax get relation cases.
  1427. *
  1428. * @param int $productID
  1429. * @param int $branchID
  1430. * @access public
  1431. * @return string
  1432. */
  1433. public function ajaxGetProductCases($productID, $branchID = 0)
  1434. {
  1435. $items = array();
  1436. $cases = $this->loadModel('testcase')->getPairsByProduct($productID, array(0, $branchID));
  1437. foreach($cases as $caseID => $caseTitle)
  1438. {
  1439. if(empty($caseID)) continue;
  1440. $items[] = array('text' => $caseTitle, 'value' => $caseID);
  1441. }
  1442. return print(helper::jsonEncode($items));
  1443. }
  1444. /**
  1445. * 创建代码分支。
  1446. * Create repo branch.
  1447. *
  1448. * @param int $bugID
  1449. * @param int $repoID
  1450. * @access public
  1451. * @return void
  1452. */
  1453. public function createBranch($bugID, $repoID = 0)
  1454. {
  1455. return print($this->fetch('repo', 'createBranch', array('objectID' => $bugID, 'repoID' => $repoID)));
  1456. }
  1457. /**
  1458. * 取消代码分支的关联。
  1459. * Unlink code branch.
  1460. *
  1461. * @access public
  1462. * @return void
  1463. */
  1464. public function unlinkBranch()
  1465. {
  1466. return print($this->fetch('repo', 'unlinkBranch'));
  1467. }
  1468. /**
  1469. * 获取项目信息。
  1470. *
  1471. * @param int $projectID
  1472. * @access public
  1473. * @return void
  1474. */
  1475. public function ajaxGetProjectInfo($projectID)
  1476. {
  1477. $project = $this->loadModel('project')->fetchByID($projectID);
  1478. return print(json_encode($project));
  1479. }
  1480. /**
  1481. * 获取项目型项目关联产品的ID。
  1482. * Get product id by project.
  1483. *
  1484. * @param int $projectID
  1485. * @access public
  1486. * @return void
  1487. */
  1488. public function ajaxGetProductIDByProject($projectID)
  1489. {
  1490. $productID = $this->loadModel('product')->getProductIDByProject($projectID);
  1491. return print($productID);
  1492. }
  1493. /**
  1494. * 获取关联产品的项目下拉数据。
  1495. * AJAX: get projects of a product in html select.
  1496. *
  1497. * @param int $productID
  1498. * @param string $branch ''|'all'|int
  1499. * @param int $projectID
  1500. * @param string $pageType old
  1501. * @access public
  1502. * @return void
  1503. */
  1504. public function ajaxGetProjects($productID, $branch = '', $projectID = 0, $pageType = '')
  1505. {
  1506. $product = $this->loadModel('product')->fetchByID($productID);
  1507. $projectPairs = $product->shadow ? $this->loadModel('project')->getPairs(false, 'noproduct,noclosed,haspriv') : array();
  1508. $projects = $this->product->getProjectPairsByProduct($productID, $branch, array_keys($projectPairs));
  1509. if($this->app->getViewType() == 'json') return print(json_encode($projects));
  1510. if($pageType == 'old') return print(html::select('project', array(0 => '') + $projects, $projectID, "class='form-control' onchange='loadProductExecutions({$productID}, this.value)'"));
  1511. $items = array();
  1512. foreach($projects as $projectID => $projectName) $items[] = array('text' => $projectName, 'value' => $projectID, 'keys' => $projectName);
  1513. return print(json_encode($items));
  1514. }
  1515. /**
  1516. * AJAX: 获重复Bug。
  1517. * AJAX: Get duplicate bugs.
  1518. *
  1519. * @param int $bugID
  1520. * @param int $duplicateBug
  1521. * @access public
  1522. * @return void
  1523. */
  1524. public function ajaxGetDuplicateBugs($bugID, $duplicateBug = 0)
  1525. {
  1526. /* 获取除了这个 bugID 的重复Bug。 */
  1527. /* Get duplicate bugs exclude this bugID. */
  1528. $limit = $this->get->limit ? $this->get->limit : $this->config->maxCount;
  1529. $productBugs = $this->bug->getProductBugPairs(0, '', '', $limit, 'all');
  1530. unset($productBugs[$bugID]);
  1531. /* 如果选中的重复Bug不在列表中,加入列表。*/
  1532. if($duplicateBug && empty($productBugs[$duplicateBug]))
  1533. {
  1534. $duplicateBugInfo = $this->bug->fetchByID($duplicateBug);
  1535. $productBugs[$duplicateBug] = $this->lang->productCommon . '#' . $duplicateBugInfo->product . '@'. $duplicateBugInfo->id . ':' . $duplicateBugInfo->title;
  1536. }
  1537. $bugItems = array();
  1538. foreach($productBugs as $bugID => $bugName) $bugItems[] = array('value' => $bugID, 'text' => $bugName);
  1539. return print(json_encode($bugItems));
  1540. }
  1541. }