control.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <?php
  2. /**
  3. * The control file of projectStory module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  7. * @package projectStory
  8. * @version $Id: control.php 5094 2013-07-10 08:46:15Z chencongzhi520@gmail.com $
  9. * @link https://www.zentao.net
  10. */
  11. class projectStory extends control
  12. {
  13. /**
  14. * All products.
  15. *
  16. * @var array
  17. * @access public
  18. */
  19. public $products = array();
  20. /**
  21. * Get software requirements from product.
  22. *
  23. * @param int $projectID
  24. * @param int $productID
  25. * @param string $branch
  26. * @param string $browseType
  27. * @param int $param
  28. * @param string $storyType
  29. * @param string $orderBy
  30. * @param int $recTotal
  31. * @param int $recPerPage
  32. * @param int $pageID
  33. * @param string $from
  34. * @param int $blockID
  35. * @access public
  36. * @return void
  37. */
  38. public function story($projectID = 0, $productID = 0, $branch = '0', $browseType = '', $param = 0, $storyType = 'story', $orderBy = '', $recTotal = 0, $recPerPage = 20, $pageID = 1, $from = 'project', $blockID = 0)
  39. {
  40. if($from == 'doc' || $from == 'ai')
  41. {
  42. $this->app->loadLang('doc');
  43. $projects = $this->loadModel('project')->getPairsByProgram();
  44. if(empty($projects)) return $this->send(array('result' => 'fail', 'message' => $this->lang->doc->tips->noProject));
  45. if(!$projectID)
  46. {
  47. $projectProducts = $this->dao->select('project, count(1) AS productCount')->from(TABLE_PROJECTPRODUCT)
  48. ->where('project')->in(array_keys($projects))
  49. ->groupBy('project')
  50. ->fetchPairs();
  51. if(!empty($projectProducts)) $projectID = key($projectProducts);
  52. }
  53. }
  54. /* Get productID for none-product project. */
  55. if($projectID)
  56. {
  57. $project = $this->loadModel('project')->getByID($projectID);
  58. if(!$project->hasProduct) $productID = $this->loadModel('product')->getShadowProductByProject($projectID)->id;
  59. }
  60. $this->products = $this->loadModel('product')->getProductPairsByProject($projectID);
  61. if(empty($productID) && count($this->products) == 1) $productID = key($this->products);
  62. /* Set product list for export. */
  63. $this->session->set('exportProductList', $this->products);
  64. $this->session->set('executionStoryList', $this->app->getURI(true));
  65. $this->session->set('productList', $this->app->getURI(true));
  66. if($storyType == 'requirement')
  67. {
  68. unset($this->lang->projectstory->featureBar['story']['linkedExecution']);
  69. unset($this->lang->projectstory->featureBar['story']['unlinkedExecution']);
  70. $this->lang->projectstory->unlinkStory = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->projectstory->unlinkStory);
  71. }
  72. if($from != 'doc' && $from != 'ai' && empty($this->products)) $this->locate($this->createLink('product', 'showErrorNone', 'moduleName=project&activeMenu=story&projectID=' . $projectID));
  73. if($from === 'doc' || $from == 'ai')
  74. {
  75. $this->loadModel('epic');
  76. $this->loadModel('requirement');
  77. $browseType = strtolower($browseType);
  78. if($browseType == '') $browseType = 'unclosed';
  79. if($this->config->edition == 'ipd' && $storyType == 'story') unset($this->config->product->search['fields']['roadmap']);
  80. if(isset($project->hasProduct) && empty($project->hasProduct))
  81. {
  82. /* The none-product project don't need display the product in the search form. */
  83. unset($this->config->product->search['fields']['product']);
  84. unset($this->config->product->search['params']['product']);
  85. /* The none-product and none-scrum project don't need display the plan in the search form. */
  86. if($project->model != 'scrum')
  87. {
  88. unset($this->config->product->search['fields']['plan']);
  89. unset($this->config->product->search['params']['plan']);
  90. }
  91. }
  92. if($this->config->edition == 'ipd' && $storyType != 'story') $this->config->product->search['params']['roadmap']['values'] = $this->loadModel('roadmap')->getPairs($productID);
  93. /* Build search form. */
  94. $actionURL = $this->createLink($this->app->rawModule, $this->app->rawMethod, "projectID={$project->id}&productID=0&branch=$branch&browseType=bySearch&queryID=myQueryID&storyType=$storyType&orderBy=&recTotal=0&recPerPage=20&pageID=1&projectID={$project->id}&from=doc&blockID=$blockID");
  95. $this->config->product->search['module'] = 'projectstory';
  96. $queryID = ($browseType == 'bysearch') ? $param : 0;
  97. $this->product->buildSearchForm($productID, $this->products, $queryID, $actionURL, $storyType, $branch, $project->id);
  98. $this->app->loadClass('pager', true);
  99. $pager = new pager($recTotal, $recPerPage, $pageID);
  100. $sort = common::appendOrder($orderBy);
  101. if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
  102. $stories = $this->loadModel('story')->getExecutionStories($projectID, $productID, $sort, $browseType, (string)$param, 'story', '', $pager);
  103. /* Process the sql, get the condition partition, save it to session. */
  104. $this->loadModel('common')->saveQueryCondition($this->dao->get(), $storyType, false);
  105. /* Build confirmeObject. */
  106. if($this->config->edition == 'ipd' && $storyType == 'story') $this->loadModel('story')->getAffectObject($stories, 'story');
  107. $idList = '';
  108. $docBlock = $this->loadModel('doc')->getDocBlock($blockID);
  109. if($docBlock)
  110. {
  111. $content = json_decode($docBlock->content, true);
  112. if(isset($content['idList'])) $idList = $content['idList'];
  113. }
  114. $product = $this->product->getByID($productID);
  115. $branchOptions = array();
  116. if(empty($product))
  117. {
  118. $projectProducts = $this->product->getProducts($projectID);
  119. $this->loadModel('branch');
  120. foreach($projectProducts as $projectProduct)
  121. {
  122. if(!$projectProduct || $projectProduct->type == 'normal') continue;
  123. $branches = $this->branch->getList($projectProduct->id, $projectID, 'all');
  124. foreach($branches as $branchInfo) $branchOptions[$projectProduct->id][$branchInfo->id] = $branchInfo->name;
  125. }
  126. }
  127. elseif($product && $product->type != 'normal')
  128. {
  129. $branches = $this->loadModel('branch')->getList($product->id, $projectID, 'all');
  130. foreach($branches as $branchInfo)
  131. {
  132. $branchOptions[$product->id][$branchInfo->id] = $branchInfo->name;
  133. }
  134. }
  135. $gradeList = $this->loadModel('story')->getGradeList('');
  136. $gradeGroup = array();
  137. foreach($gradeList as $grade) $gradeGroup[$grade->type][$grade->grade] = $grade->name;
  138. $storyIdList = array_keys($stories);
  139. $this->view->projectID = $projectID;
  140. $this->view->productID = $productID;
  141. $this->view->project = $project;
  142. $this->view->branch = $branch;
  143. $this->view->param = $param;
  144. $this->view->storyType = $storyType;
  145. $this->view->browseType = $browseType;
  146. $this->view->stories = $stories;
  147. $this->view->projects = $projects;
  148. $this->view->branchOptions = $branchOptions;
  149. $this->view->modules = $this->loadModel('tree')->getOptionMenu($productID, 'story', 0, $branch);
  150. $this->view->plans = $this->loadModel('productplan')->getPairs($productID, $branch, 'unexpired,noclosed', true);
  151. $this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted');
  152. $this->view->storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList);
  153. $this->view->storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList);
  154. $this->view->storyCases = $this->loadModel('testcase')->getStoryCaseCounts($storyIdList);
  155. $this->view->reports = in_array($this->config->edition, array('max', 'ipd')) ? $this->loadModel('researchreport')->getPairs() : array();
  156. $this->view->roadmaps = ($this->config->edition == 'ipd' && $storyType != 'story') ? array(0 => '') + $this->loadModel('roadmap')->getPairs($productID): array();
  157. $this->view->maxGradeGroup = $this->story->getMaxGradeGroup();
  158. $this->view->blockID = $blockID;
  159. $this->view->idList = $idList;
  160. $this->view->gradeGroup = $gradeGroup;
  161. $this->view->pager = $pager;
  162. $this->view->orderBy = $orderBy;
  163. $this->view->from = $from;
  164. return $this->display();
  165. }
  166. echo $this->fetch('product', 'browse', array
  167. (
  168. 'productID' => $productID,
  169. 'branch' => $branch,
  170. 'browseType' => $browseType,
  171. 'param' => $param,
  172. 'storyType' => $storyType,
  173. 'orderBy' => $orderBy,
  174. 'recTotal' => $recTotal,
  175. 'recPerPage' => $recPerPage,
  176. 'pageID' => $pageID,
  177. 'projectID' => $projectID
  178. ));
  179. }
  180. /**
  181. * Obtain the tracking matrix through the product.
  182. *
  183. * @param int $projectID
  184. * @param int $productID
  185. * @param string $branch
  186. * @param string $browseType allstory|bysearch
  187. * @param int $param
  188. * @param string $storyType
  189. * @param string $orderBy
  190. * @param int $recTotal
  191. * @param int $recPerPage
  192. * @param int $pageID
  193. * @access public
  194. * @return void
  195. */
  196. public function track($projectID = 0, $productID = 0, $branch = '', $browseType = 'allstory', $param = 0, $storyType = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
  197. {
  198. $this->loadModel('product');
  199. $this->app->loadLang('story');
  200. $project = $this->loadModel('project')->getByID($projectID);
  201. $this->session->set('hasProduct', $project->hasProduct);
  202. $this->config->project->showGrades = null;
  203. /* Insert execution field. */
  204. $insertIndex = array_search('plan', array_keys($this->config->product->search['fields']));
  205. $searchFields = array_chunk($this->config->product->search['fields'], $insertIndex + 1, true);
  206. $searchFields[0]['execution'] = $this->lang->story->execution;
  207. $this->config->product->search['fields'] = array();
  208. foreach($searchFields as $fields) $this->config->product->search['fields'] += $fields;
  209. $insertIndex = array_search('plan', array_keys($this->config->product->search['params']));
  210. $searchParams = array_chunk($this->config->product->search['params'], $insertIndex + 1, true);
  211. $searchParams[0]['execution'] = array('operator' => '=', 'control' => 'select', 'values' => array('' => '') + $this->loadModel('execution')->getPairs($projectID));
  212. $this->config->product->search['params'] = array();
  213. foreach($searchParams as $params) $this->config->product->search['params'] += $params;
  214. echo $this->fetch('product', 'track', "productID=$productID&branch=$branch&projectID=$projectID&browseType=$browseType&param=$param&storyType=$storyType&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
  215. }
  216. /**
  217. * View a story.
  218. *
  219. * @param int $storyID
  220. * @param int $projectID
  221. * @access public
  222. * @return void
  223. */
  224. public function view($storyID, $projectID = 0)
  225. {
  226. if($projectID) $this->session->set('project', $projectID, 'project');
  227. $this->session->set('productList', $this->app->getURI(true), 'product');
  228. $story = $this->loadModel('story')->getByID((int)$storyID);
  229. echo $this->fetch($story->type, 'view', "storyID=$storyID&version=$story->version&param=" . $this->session->project);
  230. }
  231. /**
  232. * Link stories to a project.
  233. *
  234. * @param int $projectID
  235. * @param string $browseType
  236. * @param int $param
  237. * @param string $orderBy
  238. * @param int $recPerPage
  239. * @param int $pageID
  240. * @param string $extra
  241. * @param string $storyType
  242. * @access public
  243. * @return void
  244. */
  245. public function linkStory($projectID = 0, $browseType = '', $param = 0, $orderBy = 'id_desc', $recPerPage = 50, $pageID = 1, $extra = '', $storyType = 'story')
  246. {
  247. echo $this->fetch('execution', 'linkStory', "projectID={$projectID}&browseType={$browseType}&param={$param}&orderBy={$orderBy}&recPerPage={$recPerPage}&pageID={$pageID}&extra={$extra}&storyType=$storyType");
  248. }
  249. /**
  250. * Unlink a story.
  251. *
  252. * @param int $projectID
  253. * @param int $storyID
  254. * @param string $confirm
  255. * @access public
  256. * @return void
  257. */
  258. public function unlinkStory($projectID, $storyID, $confirm = 'no')
  259. {
  260. echo $this->fetch('execution', 'unlinkStory', "projectID=$projectID&storyID=$storyID&confirm=$confirm");
  261. }
  262. /**
  263. * Batch unlink story.
  264. *
  265. * @param int $projectID
  266. * @param string $storyIdList
  267. * @access public
  268. * @return void
  269. */
  270. public function batchUnlinkStory($projectID, $storyIdList = '')
  271. {
  272. $storyIdList = empty($storyIdList) ? array() : array_filter(explode(',', $storyIdList));
  273. $storyIdList = array_unique($storyIdList);
  274. foreach($storyIdList as $index => $storyID)
  275. {
  276. /* 处理选中的子需求的ID,截取-后的子需求ID。*/
  277. /* Process selected child story ID. */
  278. if(strpos((string)$storyID, '-') !== false) $storyIdList[$index] = substr($storyID, strpos($storyID, '-') + 1);
  279. }
  280. if(empty($storyIdList)) $this->send(array('result' => 'success', 'load' => true, 'closeModal' => true));
  281. $this->loadModel('execution');
  282. $executionStories = $this->projectstory->getExecutionStories($projectID, $storyIdList);
  283. $errors = array();
  284. foreach($storyIdList as $storyID)
  285. {
  286. if(isset($executionStories[$storyID])) continue;
  287. $this->execution->unlinkStory($projectID, (int)$storyID);
  288. if(dao::isError()) $errors[$storyID] = dao::getError();
  289. }
  290. if(empty($errors)) $this->loadModel('score')->create('ajax', 'batchOther');
  291. if(empty($executionStories)) $this->send(array('result' => 'success', 'load' => true, 'closeModal' => true));
  292. $this->view->executionStories = $executionStories;
  293. $this->display();
  294. }
  295. /**
  296. * Import plan stories.
  297. *
  298. * @param int $projectID
  299. * @param int $planID
  300. * @param int $productID
  301. * @access public
  302. * @return void
  303. */
  304. public function importPlanStories($projectID = 0, $planID = 0, $productID = 0)
  305. {
  306. echo $this->fetch('execution', 'importPlanStories', "projectID=$projectID&planID=$planID&productID=$productID");
  307. }
  308. /**
  309. * 查看需求的报告。
  310. * The report page.
  311. *
  312. * @param int $productID
  313. * @param int $branchID
  314. * @param string $storyType
  315. * @param string $browseType
  316. * @param int $moduleID
  317. * @param string $chartType
  318. * @param int $projectID
  319. * @access public
  320. * @return void
  321. */
  322. public function report($productID, $branchID, $storyType = 'story', $browseType = 'unclosed', $moduleID = 0, $chartType = 'pie', $projectID = 0)
  323. {
  324. echo $this->fetch('story', 'report', "productID=$productID&branchID=$branchID&storyType=$storyType&browseType=$browseType&moduleID=$moduleID&chartType=$chartType&projectID=$projectID");
  325. }
  326. /**
  327. * 导出需求数据。
  328. * Get the data of the stories to export.
  329. *
  330. * @param int $productID
  331. * @param string $orderBy
  332. * @param int $executionID
  333. * @param string $browseType
  334. * @param string $storyType requirement|story
  335. * @access public
  336. * @return void
  337. */
  338. public function export($productID, $orderBy, $executionID = 0, $browseType = '', $storyType = 'story')
  339. {
  340. echo $this->fetch('story', 'export', "productID=$productID&orderBy=$orderBy&executionID=$executionID&browseType=$browseType&storyType=$storyType");
  341. }
  342. /**
  343. * Batch review stories.
  344. *
  345. * @param string $result
  346. * @param string $reason
  347. * @param string $storyType story|requirement
  348. * @access public
  349. * @return void
  350. */
  351. public function batchReview($result, $reason = '', $storyType = 'story')
  352. {
  353. echo $this->fetch('story', 'batchReview', "result=$result&reason=$reason&storyType=$storyType");
  354. }
  355. /**
  356. * 批量关闭需求。
  357. * Batch close the stories.
  358. *
  359. * @param int $productID
  360. * @param int $executionID
  361. * @param string $storyType story|requirement
  362. * @param string $from contribute|work
  363. * @access public
  364. * @return void
  365. */
  366. public function batchClose($productID = 0, $executionID = 0, $storyType = 'story', $from = '')
  367. {
  368. echo $this->fetch('story', 'batchClose', "productID=$productID&executionID=$executionID&storyType=$storyType&from=$from");
  369. }
  370. /**
  371. * Batch change the plan of story.
  372. *
  373. * @param int $planID
  374. * @param int $oldPlanID
  375. * @access public
  376. * @return void
  377. */
  378. public function batchChangePlan($planID, $oldPlanID = 0)
  379. {
  380. echo $this->fetch('story', 'batchChangePlan', "planID=$planID&oldPlanID=$oldPlanID");
  381. }
  382. /**
  383. * Batch assign to.
  384. *
  385. * @param string $storyType story|requirement
  386. * @access public
  387. * @return void
  388. * @param string $assignedTo
  389. */
  390. public function batchAssignTo($storyType = 'story', $assignedTo = '')
  391. {
  392. echo $this->fetch('story', 'batchAssignTo', "storyType=$storyType&assignedTo=$assignedTo");
  393. }
  394. /**
  395. * Batch edit story.
  396. *
  397. * @param int $productID
  398. * @param int $executionID
  399. * @param string $branch
  400. * @param string $storyType
  401. * @param string $from
  402. * @access public
  403. * @return void
  404. */
  405. public function batchEdit($productID = 0, $executionID = 0, $branch = '', $storyType = 'story', $from = '')
  406. {
  407. echo $this->fetch('story', 'batchEdit', "productID=$productID&executionID=$executionID&branch=$branch&storyType=$storyType&from=$from");
  408. }
  409. /**
  410. * 导入需求数据。
  411. * Import the data of the requiremens.
  412. *
  413. * @param int $productID
  414. * @param int $branch
  415. * @param string $storyType
  416. * @param int $projectID
  417. * @access public
  418. * @return void
  419. */
  420. public function import($productID, $branch = 0, $storyType = 'story', $projectID = 0)
  421. {
  422. echo $this->fetch('story', 'import', "productID=$productID&branch=$branch&storyType=$storyType&projectID=$projectID");
  423. }
  424. /**
  425. * 显示导入需求数据的页面。
  426. * Show the page of importing the data of the requiremens.
  427. *
  428. * @param int $productID
  429. * @param int $branch
  430. * @param string $type
  431. * @param int $projectID
  432. * @param int $pagerID
  433. * @param int $maxImport
  434. * @param string $insert
  435. * @access public
  436. * @return void
  437. */
  438. public function showImport($productID, $branch = 0, $type = 'story', $projectID = 0, $pagerID = 1, $maxImport = 0, $insert = '')
  439. {
  440. echo $this->fetch('story', 'showImport', "productID=$productID&branch=$branch&type=$type&projectID=$projectID&pagerID=$pagerID&maxImport=$maxImport&insert=$insert");
  441. }
  442. /**
  443. * 导出需求模板。
  444. * Export the template of the requiremens.
  445. *
  446. * @param int $productID
  447. * @param int $branch
  448. * @param string $storyType
  449. * @access public
  450. * @return void
  451. */
  452. public function exportTemplate($productID, $branch = 0, $storyType = 'story')
  453. {
  454. echo $this->fetch('story', 'exportTemplate', "productID=$productID&branch=$branch&storyType=$storyType");
  455. }
  456. /**
  457. * 批量导入需求到需求库。
  458. * Batch import the data of the requiremens to the library.
  459. *
  460. * @access public
  461. * @return void
  462. */
  463. public function batchImportToLib()
  464. {
  465. echo $this->fetch('story', 'batchImportToLib');
  466. }
  467. /**
  468. * 导入需求到需求库。
  469. * Import the data of the requiremens to the library.
  470. *
  471. * @param int $storyID
  472. * @access public
  473. * @return void
  474. */
  475. public function importToLib($storyID)
  476. {
  477. echo $this->fetch('story', 'importToLib', "storyID=$storyID");
  478. }
  479. }