control.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <?php
  2. /**
  3. * The control file of design module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Shujie Tian <tianshujie@easycorp.ltd>
  8. * @package design
  9. * @version $Id: control.php 5107 2020-09-02 09:46:12Z tianshujie@easycorp.ltd $
  10. * @link https://www.zentao.net
  11. */
  12. class design extends control
  13. {
  14. /**
  15. * 构造函数,加载通用的模块。
  16. * Construct function, load module auto.
  17. *
  18. * @param string $moduleName
  19. * @param string $methodName
  20. * @access public
  21. * @return void
  22. */
  23. public function __construct($moduleName = '', $methodName = '')
  24. {
  25. parent::__construct($moduleName, $methodName);
  26. $this->loadModel('product');
  27. $this->loadModel('project');
  28. $this->loadModel('task');
  29. }
  30. /**
  31. * 设置页面公共的数据以及导航。
  32. * Design common action.
  33. *
  34. * @param int $projectID
  35. * @param int $productID
  36. * @param int $designID
  37. * @param string $type
  38. * @access public
  39. * @return void
  40. */
  41. public function commonAction($projectID = 0, $productID = 0, $designID = 0, $type = 'all')
  42. {
  43. $products = $this->product->getProductPairsByProject($projectID);
  44. $products[0] = $this->lang->product->all;
  45. ksort($products);
  46. $productID = $this->product->getAccessibleProductID($productID, $products);
  47. $this->designZen->setMenu($projectID, $productID, strtolower($type));
  48. $this->project->setMenu($projectID);
  49. $project = $this->project->getByID($projectID);
  50. if(!$project->hasProduct) $this->config->hasSwitcherModules = array();
  51. $this->view->products = $products;
  52. $this->view->switcherParams = "projectID={$projectID}&productID={$productID}";
  53. $this->view->switcherText = zget($products, $productID);
  54. $this->view->switcherObjectID = $productID;
  55. $this->lang->design->typeList = in_array($project->model, array('waterfall', 'ipd')) ? $this->lang->design->typeList : $this->lang->design->plusTypeList;
  56. $this->config->design->dtable->fieldList['type']['statusMap'] = $this->lang->design->typeList;
  57. return $productID;
  58. }
  59. /**
  60. * 设计列表页面。
  61. * Browse designs.
  62. *
  63. * @param int $projectID
  64. * @param int $productID
  65. * @param string $type all|bySearch|HLDS|DDS|DBDS|ADS
  66. * @param int $param
  67. * @param string $orderBy
  68. * @param int $recTotal
  69. * @param int $recPerPage
  70. * @param int $pageID
  71. * @access public
  72. * @return void
  73. */
  74. public function browse($projectID = 0, $productID = 0, $type = 'all', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
  75. {
  76. $productID = $this->commonAction($projectID, $productID, 0, $type);
  77. $project = $this->project->getByID($projectID);
  78. /* Save session for design list. */
  79. $this->session->set('designList', $this->app->getURI(true), 'project');
  80. $this->session->set('reviewList', $this->app->getURI(true), 'project');
  81. $products = $this->product->getProductPairsByProject($projectID);
  82. $productIdList = $productID ? $productID : array_keys($products);
  83. $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full');
  84. $queryID = $type == 'bySearch' ? $param : 0;
  85. /* Build Search Form. */
  86. $this->config->design->search['params']['story']['values'] = $stories;
  87. $this->config->design->search['params']['type']['values'] = $this->lang->design->typeList;
  88. $this->config->design->search['actionURL'] = $this->createLink('design', 'browse', "projectID={$projectID}&productID={$productID}&type=bySearch&queryID=myQueryID");
  89. $this->config->design->search['queryID'] = $queryID;
  90. $this->loadModel('search')->setSearchParams($this->config->design->search);
  91. /* Init pager and set table field and actions. */
  92. $this->app->loadClass('pager', true);
  93. $pager = pager::init($recTotal, $recPerPage, $pageID);
  94. if(isset($project->hasProduct) && !$project->hasProduct) unset($this->config->design->dtable->fieldList['product']);
  95. if(isset($project->hasProduct) && $project->hasProduct) $this->config->design->dtable->fieldList['product']['map'] = $this->view->products;
  96. if(!helper::hasFeature('devops')) $this->config->design->dtable->fieldList['actions']['menu'] = array('edit', 'delete');
  97. $designs = $this->design->getList($projectID, $productID, $type, $queryID, $orderBy, $pager);
  98. if($this->config->edition != 'open')
  99. {
  100. $designRelatedObjectList = $this->loadModel('custom')->getRelatedObjectList(array_keys($designs), 'design', 'byRelation', true);
  101. foreach($designs as $design) $design->relatedObject = zget($designRelatedObjectList, $design->id, 0);
  102. }
  103. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->browse;
  104. $this->view->designs = $designs;
  105. $this->view->projectID = $projectID;
  106. $this->view->productID = $productID;
  107. $this->view->type = $type;
  108. $this->view->param = $param;
  109. $this->view->orderBy = $orderBy;
  110. $this->view->pager = $pager;
  111. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  112. $this->view->project = $project;
  113. $this->display();
  114. }
  115. /**
  116. * 创建一个设计。
  117. * Create a design.
  118. *
  119. * @param int $projectID
  120. * @param int $productID
  121. * @param string $type all|bySearch|HLDS|DDS|DBDS|ADS
  122. * @access public
  123. * @return void
  124. */
  125. public function create($projectID = 0, $productID = 0, $type = 'all')
  126. {
  127. $productID = $this->commonAction($projectID, $productID, 0, $type);
  128. if($_POST)
  129. {
  130. $designData = form::data()
  131. ->add('project', $projectID)
  132. ->setDefault('createdBy', $this->app->user->account)
  133. ->get();
  134. $story = $this->loadModel('story')->getById($designData->story);
  135. if($story) $designData->storyVersion = $story->version;
  136. $designID = $this->design->create($designData);
  137. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  138. $this->loadModel('action')->create('design', $designID, 'created');
  139. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink('design', 'browse', "projectID={$projectID}&productID={$productID}")));
  140. }
  141. $products = $this->product->getProductPairsByProject($projectID);
  142. $productIdList = $productID ? $productID : array_keys($products);
  143. $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full');
  144. $frozenType = $this->design->getFrozenDesignType($projectID);
  145. foreach($frozenType as $type) unset($this->lang->design->typeList[$type]);
  146. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->create;
  147. $this->view->users = $this->loadModel('user')->getPairs('noclosed');
  148. $this->view->stories = $this->story->addGradeLabel($stories);
  149. $this->view->productID = $productID;
  150. $this->view->projectID = $projectID;
  151. $this->view->type = $type;
  152. $this->view->project = $this->loadModel('project')->getByID($projectID);
  153. $this->display();
  154. }
  155. /**
  156. * 批量创建设计。
  157. * Batch create designs.
  158. *
  159. * @param int $projectID
  160. * @param int $productID
  161. * @param string $type all|bySearch|HLDS|DDS|DBDS|ADS
  162. * @access public
  163. * @return void
  164. */
  165. public function batchCreate($projectID = 0, $productID = 0, $type = 'all')
  166. {
  167. $productID = $this->commonAction($projectID, $productID, 0, $type);
  168. if($_POST)
  169. {
  170. $designs = form::batchData()->get();
  171. $this->design->batchCreate($projectID, $productID, $designs);
  172. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  173. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => inlink('browse', "projectID={$projectID}&productID={$productID}")));
  174. }
  175. $products = $this->product->getProductPairsByProject($projectID);
  176. $productIdList = $productID ? $productID : array_keys($products);
  177. $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full');
  178. $project = $this->loadModel('project')->getByID($projectID);
  179. $frozenType = $this->design->getFrozenDesignType($projectID);
  180. foreach($frozenType as $type) unset($this->lang->design->typeList[$type]);
  181. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->batchCreate;
  182. $this->view->stories = $this->story->addGradeLabel($stories);
  183. $this->view->users = $this->loadModel('user')->getPairs('noclosed');
  184. $this->view->type = $type;
  185. $this->view->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList;
  186. $this->view->projectID = $projectID;
  187. $this->display();
  188. }
  189. /**
  190. * 设计详情页面。
  191. * View a design.
  192. *
  193. * @param int $designID
  194. * @param int $version
  195. * @access public
  196. * @return void
  197. */
  198. public function view($designID = 0, $version = 0)
  199. {
  200. $design = $this->design->getByID($designID, $version);
  201. $this->commonAction($design->project, $design->product, $designID, $design->type);
  202. $this->session->set('revisionList', $this->app->getURI(true));
  203. $this->session->set('storyList', $this->app->getURI(true), 'product');
  204. $products = $this->product->getProductPairsByProject($design->project);
  205. $productIdList = $design->product ? $design->product : array_keys($products);
  206. $project = $this->loadModel('project')->getByID($design->project);
  207. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->view;
  208. $this->view->design = $design;
  209. $this->view->stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full');
  210. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  211. $this->view->actions = $this->loadModel('action')->getList('design', $design->id);
  212. $this->view->repos = $this->loadModel('repo')->getRepoPairs('project', $design->project);
  213. $this->view->project = $project;
  214. $this->view->version = $version == 0 ? $design->version : $version;
  215. $this->view->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList;
  216. $this->display();
  217. }
  218. /**
  219. * 编辑一个设计。
  220. * Edit a design.
  221. *
  222. * @param int $designID
  223. * @access public
  224. * @return void
  225. */
  226. public function edit($designID = 0)
  227. {
  228. $design = $this->design->getByID($designID);
  229. $design = $this->design->getAffectedScope($design);
  230. $this->commonAction($design->project, $design->product, $designID, $design->type);
  231. if($_POST)
  232. {
  233. $designData = form::data()->setDefault('editedBy', $this->app->user->account)->get();
  234. if($design->story != $designData->story)
  235. {
  236. $story = $this->loadModel('story')->getById($designData->story);
  237. if($story) $designData->storyVersion = $story->version;
  238. }
  239. $changes = $this->design->update($designID, $designData);
  240. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  241. if(!empty($changes))
  242. {
  243. $actionID = $this->loadModel('action')->create('design', $designID, 'changed');
  244. $this->action->logHistory($actionID, $changes);
  245. }
  246. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink('design', 'view', "id={$designID}")));
  247. }
  248. $products = $this->product->getProductPairsByProject($design->project);
  249. $productIdList = $design->product ? $design->product : array_keys($products);
  250. $project = $this->loadModel('project')->getByID($design->project);
  251. $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active,launched,developing', 'id_desc', 0, 'full', 'full');
  252. $frozenType = $this->design->getFrozenDesignType($design->project);
  253. foreach($frozenType as $type) unset($this->lang->design->typeList[$type]);
  254. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->edit;
  255. $this->view->design = $design;
  256. $this->view->project = $project;
  257. $this->view->stories = $this->story->addGradeLabel($stories);
  258. $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter');
  259. $this->view->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList;
  260. $this->display();
  261. }
  262. /**
  263. * 关联代码提交页面。
  264. * Design link commits.
  265. *
  266. * @param int $designID
  267. * @param int $repoID
  268. * @param string $begin
  269. * @param string $end
  270. * @param int $recPerPage
  271. * @param int $pageID
  272. * @access public
  273. * @return void
  274. * @param int $recTotal
  275. */
  276. public function linkCommit($designID = 0, $repoID = 0, $begin = '', $end = '', $recTotal = 0, $recPerPage = 20, $pageID = 1)
  277. {
  278. if($_POST)
  279. {
  280. $this->design->linkCommit($designID, $repoID, $this->post->revision);
  281. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true, 'closeModal' => true));
  282. }
  283. $design = $this->design->getByID($designID);
  284. $this->commonAction($design->project, $design->product, $designID, $design->type);
  285. /* Get project and date. */
  286. $project = $this->loadModel('project')->getByID($design->project);
  287. $begin = $begin ? date('Y-m-d', strtotime($begin)) : $project->begin;
  288. $end = $end ? date('Y-m-d', strtotime($end)) : helper::today();
  289. /* Get the repository information through the repoID. */
  290. $repos = $this->loadModel('repo')->getRepoPairs('project', $design->project);
  291. $repoID = $repoID ? $repoID : key($repos);
  292. $repo = $this->loadModel('repo')->getByID((int)$repoID);
  293. /* Init pager. */
  294. $this->app->loadClass('pager', true);
  295. $pager = new pager(0, $recPerPage, $pageID);
  296. $revisions = $this->repo->getCommits($repo, '', 'HEAD', 'dir', $pager, $begin, date('Y-m-d 23:59:59', strtotime($end)));
  297. $this->session->set('designRevisions', $revisions);
  298. /* Get linked submission. */
  299. $linkedRevisions = array();
  300. $relations = $this->loadModel('common')->getRelations('design', $designID, 'commit');
  301. foreach($relations as $relation) $linkedRevisions[$relation->BID] = $relation->BID;
  302. foreach($revisions as $id => $commit)
  303. {
  304. if(isset($linkedRevisions[$commit->id])) unset($revisions[$id]);
  305. }
  306. $this->config->design->linkcommit->dtable->fieldList['revision']['link'] = sprintf($this->config->design->linkcommit->dtable->fieldList['revision']['link'], $repoID, $design->project);
  307. if(empty($repo->SCM) || $repo->SCM != 'Git') unset($this->config->design->linkcommit->dtable->fieldList['commit']);
  308. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->linkCommit;
  309. $this->view->repos = $repos;
  310. $this->view->repoID = $repoID;
  311. $this->view->repo = $repo;
  312. $this->view->revisions = $revisions;
  313. $this->view->designID = $designID;
  314. $this->view->begin = $begin;
  315. $this->view->end = $end;
  316. $this->view->design = $design;
  317. $this->view->pager = $pager;
  318. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  319. $this->display();
  320. }
  321. /**
  322. * 设计解除代码提交关联。
  323. * Design unlink a commit.
  324. *
  325. * @param int $designID
  326. * @param int $commitID
  327. * @access public
  328. * @return void
  329. */
  330. public function unlinkCommit($designID = 0, $commitID = 0)
  331. {
  332. $this->design->unlinkCommit($designID, $commitID);
  333. $link = inlink('viewCommit', "designID={$designID}");
  334. return $this->sendSuccess(array('callback' => "loadModal(\"$link\", 'viewCommitModal');"));
  335. }
  336. /**
  337. * 查看设计关联的代码提交。
  338. * View a design's commit.
  339. *
  340. * @param int $designID
  341. * @param int $recTotal
  342. * @param int $recPerPage
  343. * @param int $pageID
  344. * @access public
  345. * @return void
  346. */
  347. public function viewCommit($designID = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
  348. {
  349. /* Init pager. */
  350. $this->app->loadClass('pager', true);
  351. $pager = pager::init(0, $recPerPage, $pageID);
  352. $design = $this->design->getCommit($designID, $pager);
  353. $this->commonAction($design->project, $design->product, $designID, $design->type);
  354. $this->config->design->viewcommit->dtable->fieldList['actions']['list']['unlinkCommit']['url'] = sprintf($this->config->design->viewcommit->actionList['unlinkCommit']['url'], $designID);
  355. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->submission;
  356. $this->view->design = $design;
  357. $this->view->pager = $pager;
  358. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  359. $this->view->repos = $this->loadModel('repo')->getRepoPairs('project', $design->project);
  360. $this->display();
  361. }
  362. /**
  363. * 单个代码提交记录。
  364. * A version of the repository.
  365. *
  366. * @param int $revisionID
  367. * @param int $projectID
  368. * @access public
  369. * @return void
  370. */
  371. public function revision($revisionID = 0, $projectID = 0)
  372. {
  373. $revision = $this->design->getCommitByID($revisionID);
  374. $this->locate(helper::createLink('repo', 'revision', "repoID={$revision->repo}&objectID={$projectID}&revistion={$revision->revision}"));
  375. }
  376. /**
  377. * Ajax: 设置2.5级产品下拉菜单。
  378. * Ajax: Set the dropdown menu of 2.5 level product.
  379. *
  380. * @param int $projectID
  381. * @param int $productID
  382. * @access public
  383. * @return void
  384. */
  385. public function ajaxSwitcherMenu($projectID, $productID)
  386. {
  387. $this->view->link = helper::createLink('design', 'browse', "projectID={$projectID}&productID={id}");
  388. $this->view->projectID = $projectID;
  389. $this->view->productID = $productID;
  390. $this->view->products = $this->loadModel('product')->getProducts($projectID);
  391. $this->display();
  392. }
  393. /**
  394. * Ajax:通过产品ID获取产品下的需求。
  395. * Ajax: Get stories by productID and projectID.
  396. *
  397. * @param int $productID
  398. * @param int $projectID
  399. * @param string $status
  400. * @param string $hasParent
  401. * @access public
  402. * @return void
  403. */
  404. public function ajaxGetProductStories($productID, $projectID, $status = 'all', $hasParent = 'true')
  405. {
  406. $hasParent = $hasParent == 'true';
  407. $products = $this->product->getProductPairsByProject($projectID);
  408. $productIdList = $productID ? $productID : array_keys($products);
  409. $stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, $status, 'id_desc', 0, 'full', 'full', $hasParent);
  410. $items = $this->story->addGradeLabel($stories);
  411. return print(json_encode($items));
  412. }
  413. /**
  414. * 删除一个设计。
  415. * Delete a design.
  416. *
  417. * @param int $designID
  418. * @access public
  419. * @return void
  420. */
  421. public function delete($designID = 0)
  422. {
  423. $design = $this->design->getByID($designID);
  424. $this->design->delete(TABLE_DESIGN, $designID);
  425. $this->design->unlinkCommit($designID);
  426. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  427. return $this->send(array('result' => 'success', 'load' => isInModal() ? true : inlink('browse', "projectID={$design->project}")));
  428. }
  429. /**
  430. * 更新设计的指派人。
  431. * Update assign of design.
  432. *
  433. * @param int $designID
  434. * @access public
  435. * @return void
  436. */
  437. public function assignTo($designID = 0)
  438. {
  439. if($_POST)
  440. {
  441. $designData = form::data()->setDefault('editedBy', $this->app->user->account)->get();
  442. $changes = $this->design->assign($designID, $designData);
  443. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  444. $this->loadModel('action');
  445. if(!empty($changes))
  446. {
  447. $actionID = $this->action->create('design', $designID, 'Assigned', $this->post->comment, $this->post->assignedTo);
  448. $this->action->logHistory($actionID, $changes);
  449. }
  450. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
  451. }
  452. $design = $this->design->getByID($designID);
  453. $this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->assignedTo;
  454. $this->view->design = $design;
  455. $this->view->users = $this->loadModel('project')->getTeamMemberPairs($design->project);
  456. $this->display();
  457. }
  458. /**
  459. * 确认设计的需求变更。
  460. * Confirm story change of design.
  461. *
  462. * @param int $designID
  463. * @access public
  464. * @return void
  465. */
  466. public function confirmStoryChange($designID = 0)
  467. {
  468. $this->design->confirmStoryChange($designID);
  469. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  470. $design = $this->design->fetchByID($designID);
  471. $this->loadModel('action')->create('design', $designID, 'confirmed', '', $design->storyVersion);
  472. return $this->send(array('result' => 'success', 'load' => true));
  473. }
  474. }