control.php 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355
  1. <?php
  2. /**
  3. * The control file of product module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://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 product
  9. * @link https://www.zentao.net
  10. */
  11. class product extends control
  12. {
  13. public $products = array();
  14. /**
  15. * Construct function.
  16. *
  17. * @param string moduleName
  18. * @param string methodName
  19. * @access public
  20. * @return void
  21. */
  22. public function __construct($moduleName = '', $methodName = '')
  23. {
  24. parent::__construct($moduleName, $methodName);
  25. if(!isset($this->app->user)) return;
  26. /* Load need modules. */
  27. $this->loadModel('user');
  28. /* Get all products, if no, goto the create page. */
  29. $this->products = $this->product->getPairs('all', 0, '', 'all');
  30. if($this->product->checkLocateCreate($this->products) && $this->app->tab != 'doc') $this->locate($this->createLink('product', 'create'));
  31. $this->view->products = $this->products;
  32. }
  33. /**
  34. * 产品首页,浏览仪表盘。
  35. * Index page, browse dashboard.
  36. *
  37. * @param int $productID
  38. * @access public
  39. * @return void
  40. */
  41. public function index($productID = 0)
  42. {
  43. /* Check product id and get product branch. */
  44. $productID = $this->product->checkAccess($productID, $this->products);
  45. $branch = (int)$this->cookie->preBranch;
  46. /* Set Menu. */
  47. if($this->app->getViewType() != 'mhtml') unset($this->lang->product->menu->index);
  48. if($this->app->viewType == 'mhtml') $this->product->setMenu($productID, $branch);
  49. $this->view->title = $this->lang->product->index;
  50. $this->display();
  51. }
  52. /**
  53. * 产品下项目列表。
  54. * The projects which linked the product.
  55. *
  56. * @param string $status
  57. * @param int $productID
  58. * @param string $branch
  59. * @param string $involved
  60. * @param string $orderBy
  61. * @param int $recTotal
  62. * @param int $recPerPage
  63. * @param int $pageID
  64. * @access public
  65. * @return void
  66. */
  67. public function project($status = 'all', $productID = 0, $branch = '', $involved = '0', $orderBy = 'order_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
  68. {
  69. if(!$involved) $involved = $this->cookie->involved;
  70. if($branch === '') $branch = '0';
  71. $productID = $this->product->checkAccess($productID, $this->products);
  72. $this->productZen->setProjectMenu($productID, $branch, (string)$this->cookie->preBranch);
  73. /* Load pager. */
  74. $this->app->loadClass('pager', true);
  75. $pager = new pager($recTotal, $recPerPage, $pageID);
  76. /* Get projects linked product with statistic. */
  77. $projectStats = $this->product->getProjectStatsByProduct($productID, $status, $branch, (bool)$involved, $orderBy, $pager);
  78. /* Get project pairs of same program. */
  79. $product = $this->product->getByID($productID);
  80. $projects = $this->loadModel('project')->getPairsByProgram($product->program, 'all', false, 'order_asc', '', '', 'product');
  81. foreach($projectStats as $project) unset($projects[$project->id]);
  82. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->product->project;
  83. $this->view->projectStats = $this->productZen->processProjectListData($projectStats);
  84. $this->view->PMList = $this->loadModel('user')->getListByAccounts(helper::arrayColumn($projectStats, 'PM'), 'account');
  85. $this->view->product = $product;
  86. $this->view->projects = $projects;
  87. $this->view->status = $status;
  88. $this->view->users = $this->user->getPairs('noletter');
  89. $this->view->branchID = $branch;
  90. $this->view->branchStatus = $this->loadModel('branch')->getByID($branch, 0, 'status');
  91. $this->view->pager = $pager;
  92. $this->view->involved = $involved;
  93. $this->view->orderBy = $orderBy;
  94. $this->display();
  95. }
  96. /**
  97. * 浏览产品研发/用户需求列表。
  98. * Browse requirements list of product.
  99. *
  100. * @param int $productID
  101. * @param string $branch all|''|0
  102. * @param string $browseType
  103. * @param int $param Story Module ID
  104. * @param string $storyType requirement|story
  105. * @param string $orderBy
  106. * @param int $recTotal
  107. * @param int $recPerPage
  108. * @param int $pageID
  109. * @param int $projectID
  110. * @access public
  111. * @return void
  112. * @param string $from
  113. * @param int $blockID
  114. */
  115. public function browse($productID = 0, $branch = 'all', $browseType = '', $param = 0, $storyType = 'story', $orderBy = '', $recTotal = 0, $recPerPage = 20, $pageID = 1, $projectID = 0, $from = 'product', $blockID = 0)
  116. {
  117. $this->app->loadLang('doc');
  118. $products = $this->product->getPairs('nodeleted', 0, '', 0);
  119. if(($from == 'doc' || $from == 'ai') && empty($products)) return $this->send(array('result' => 'fail', 'message' => $this->lang->doc->tips->noProduct));
  120. if(($from == 'doc' || $from == 'ai') && $productID && !$this->product->checkPriv($productID)) return $this->send(array('result'=> 'fail', 'message' => $this->lang->product->accessDenied, 'closeModal' => true));
  121. $browseType = strtolower($browseType);
  122. /* Pre process. */
  123. $this->loadModel('requirement');
  124. $this->loadModel('epic');
  125. $this->loadModel('tree');
  126. $isProjectStory = $this->app->rawModule == 'projectstory';
  127. $cookieOrderBy = $this->cookie->productStoryOrder ? $this->cookie->productStoryOrder : 'id_desc';
  128. /* Load pager. */
  129. $this->app->loadClass('pager', true);
  130. if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
  131. $pager = new pager($recTotal, $recPerPage, $pageID);
  132. /* Get the product can access. */
  133. if($this->app->tab != 'project' || $from == 'doc' || $from == 'ai')
  134. {
  135. $accessProductID = $this->product->checkAccess($productID, $products);
  136. if($this->app->apiVersion == 'v2' && $accessProductID != $productID) return $this->sendError($this->lang->product->accessDenied);
  137. $productID = $accessProductID;
  138. }
  139. /* Generate data. */
  140. $productID = ($this->app->tab != 'project' || $from == 'doc' || $from == 'ai') ? $this->product->checkAccess($productID, $products) : $productID;
  141. $product = $this->productZen->getBrowseProduct($productID);
  142. $project = $projectID ? $this->loadModel('project')->getByID($projectID) : null;
  143. $branchID = $this->productZen->getBranchID($product, $branch);
  144. $orderBy = $orderBy ? $orderBy : $cookieOrderBy;
  145. $branch = $branchID ? $branchID : $branch;
  146. /* ATTENTION: be careful to change the order of follow sentences. */
  147. $this->session->set('storyType', $storyType, 'product');
  148. $this->productZen->setMenu4Browse($projectID, $productID, $branch, $storyType);
  149. $this->productZen->saveAndModifyCookie4Browse($productID, $branch, $param, $browseType, $orderBy);
  150. if($browseType == '')
  151. {
  152. $browseType = 'unclosed';
  153. if($this->config->vision == 'or') $browseType = 'assignedtome';
  154. }
  155. /* Generate data. */
  156. $moduleID = $this->productZen->getModuleId($param, $browseType);
  157. $stories = $this->productZen->getStories($projectID, $productID, $branchID, $moduleID, $param, $storyType, $browseType, $orderBy, $pager);
  158. /* Process the sql, get the condition partition, save it to session. */
  159. $this->loadModel('common')->saveQueryCondition($this->dao->get(), $storyType, (strpos('bysearch,reviewbyme,bymodule', $browseType) === false && !$isProjectStory));
  160. /* Append children. */
  161. if($storyType != 'story') $stories = $this->loadModel('story')->appendChildren($productID, $stories, $storyType);
  162. /* Save session. */
  163. $this->productZen->saveSession4Browse($product, $browseType);
  164. /* Build search form. */
  165. $this->productZen->buildSearchFormForBrowse($project, $projectID, $productID, $branch, $param, $storyType, $browseType, $isProjectStory, $from, $blockID);
  166. /* Build confirmeObject. */
  167. if($this->config->edition == 'ipd' && $storyType == 'story') $this->loadModel('story')->getAffectObject($stories, 'story');
  168. /* Assign. */
  169. $this->view->moduleID = $moduleID;
  170. $this->view->pager = $pager;
  171. $this->view->orderBy = $orderBy;
  172. $this->view->param = $param;
  173. $this->view->moduleTree = $this->productZen->getModuleTree($projectID, $productID, $branch, $param, $storyType, $browseType);
  174. $this->view->from = $from;
  175. $this->view->blockID = $blockID;
  176. $this->view->docBlock = false;
  177. $this->view->idList = '';
  178. if($from === 'doc')
  179. {
  180. $this->view->products = $products;
  181. $docBlock = $this->loadModel('doc')->getDocBlock($blockID);
  182. $this->view->docBlock = $docBlock;
  183. if($docBlock)
  184. {
  185. $content = json_decode($docBlock->content, true);
  186. if(isset($content['idList'])) $this->view->idList = $content['idList'];
  187. }
  188. }
  189. $this->productZen->assignBrowseData($stories, $browseType, $storyType, $isProjectStory, $product, $project, $branch, $branchID, $from);
  190. }
  191. /**
  192. * 创建产品。可以是顶级产品,也可以是项目集下的产品。
  193. * Create a product.
  194. *
  195. * @param int $programID
  196. * @param string $extra
  197. * @access public
  198. * @return void
  199. */
  200. public function create($programID = 0, $extra = '')
  201. {
  202. $extra = str_replace(array(',', ' '), array('&', ''), $extra);
  203. parse_str($extra, $output);
  204. $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0;
  205. if(!empty($_POST))
  206. {
  207. $productData = $this->productZen->buildProductForCreate($workflowGroup);
  208. $productID = $this->product->create($productData, (string) $this->post->lineName);
  209. if(dao::isError()) return $this->sendError(dao::getError());
  210. $response = $this->productZen->responseAfterCreate($productID, !empty($productData->program) ? $productData->program : 0);
  211. return $this->send($response);
  212. }
  213. $this->productZen->setCreateMenu($programID);
  214. if($workflowGroup)
  215. {
  216. $product = new stdclass();
  217. $product->id = 0;
  218. $product->workflowGroup = $workflowGroup;
  219. $this->view->product = $product;
  220. }
  221. $this->view->title = $this->lang->product->create;
  222. $this->view->gobackLink = $this->productZen->getBackLink4Create($extra);
  223. $this->view->programID = $programID;
  224. $this->view->fields = $this->productZen->getFormFields4Create($programID, $extra);
  225. $this->view->loadUrl = $this->createLink('product', 'create', "programID={program}&extra={$extra}");
  226. $this->display();
  227. }
  228. /**
  229. * 编辑产品。
  230. * Edit a product.
  231. *
  232. * @param int $productID
  233. * @param string $action
  234. * @param string $extra
  235. * @param int $programID
  236. * @access public
  237. * @return void
  238. */
  239. public function edit($productID, $action = 'edit', $extra = '', $programID = 0)
  240. {
  241. $extra = str_replace(array(',', ' '), array('&', ''), $extra);
  242. parse_str($extra, $output);
  243. $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0;
  244. if(!empty($_POST))
  245. {
  246. $productData = $this->productZen->buildProductForEdit($productID, $workflowGroup);
  247. $this->product->update($productID, $productData);
  248. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  249. if($action == 'undelete') $this->loadModel('action')->undelete((int)$extra);
  250. $response = $this->productZen->responseAfterEdit($productID, $programID);
  251. return $this->send($response);
  252. }
  253. $productID = $this->product->checkAccess($productID, $this->products);
  254. $this->productZen->setEditMenu($productID, $programID);
  255. $product = $this->product->getByID($productID);
  256. if($workflowGroup) $product->workflowGroup = $workflowGroup;
  257. $this->view->title = $this->lang->product->edit . $this->lang->hyphen . $product->name;
  258. $this->view->product = $product;
  259. $this->view->fields = $this->productZen->getFormFields4Edit($product);
  260. $this->view->action = $action;
  261. $this->view->programID = $programID;
  262. $this->display();
  263. }
  264. /**
  265. * 根据POST过来的ID列表,批量编辑相应产品。
  266. * Batch edit products.
  267. *
  268. * @param int $programID
  269. * @access public
  270. * @return void
  271. */
  272. public function batchEdit($programID = 0)
  273. {
  274. if($this->post->name)
  275. {
  276. /* 从POST中获取数据。 */
  277. $products = form::batchData($this->config->product->form->batchEdit)->get();
  278. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  279. $result = $this->product->batchUpdate($products);
  280. if(dao::isError()) return $this->send($result);
  281. $response = $this->productZen->responseAfterBatchEdit($programID);
  282. return $this->send($response);
  283. }
  284. /* 获取要修改的产品ID列表。*/
  285. $productIdList = $this->post->productIDList;
  286. if(empty($productIdList)) return $this->locate($this->session->productList);
  287. /* Set menu when page come from program. */
  288. if($this->app->tab == 'program' && $programID) common::setMenuVars('program', $programID);
  289. if($this->config->vision == 'or') unset($this->lang->product->statusList['normal']);
  290. /* Set custom. */
  291. foreach(explode(',', $this->config->product->list->customBatchEditFields) as $field) $customFields[$field] = $this->lang->product->$field;
  292. $this->view->customFields = $customFields;
  293. $this->view->showFields = $this->config->product->custom->batchEditFields;
  294. /* 构造批量编辑页面表单配置数据。*/
  295. $this->productZen->buildBatchEditForm($programID, $productIdList);
  296. }
  297. /**
  298. * 激活产品。
  299. * Activate product.
  300. *
  301. * @param int $productID
  302. * @access public
  303. * @return void
  304. */
  305. public function activate($productID)
  306. {
  307. if(!empty($_POST))
  308. {
  309. $productData = $this->productZen->buildProductForActivate($productID);
  310. $this->product->activate($productID, $productData, $this->post->comment);
  311. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  312. $this->executeHooks($productID);
  313. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
  314. }
  315. $this->product->setMenu($productID);
  316. $product = $this->product->getByID($productID);
  317. $this->view->title = $product->name . $this->lang->hyphen .$this->lang->close;
  318. $this->view->product = $product;
  319. $this->view->actions = $this->loadModel('action')->getList('product', $productID);
  320. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  321. $this->view->fields = $this->productZen->getFormFields4Activate();
  322. $this->display();
  323. }
  324. /**
  325. * 关闭产品。
  326. * Close product.
  327. *
  328. * @param int $productID
  329. * @access public
  330. * @return void
  331. */
  332. public function close($productID)
  333. {
  334. if(!empty($_POST))
  335. {
  336. $productData = $this->productZen->buildProductForClose($productID);
  337. $this->product->close($productID, $productData, $this->post->comment);
  338. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  339. $this->executeHooks($productID);
  340. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
  341. }
  342. $this->product->setMenu($productID);
  343. $product = $this->product->getByID($productID);
  344. $this->view->title = $product->name . $this->lang->hyphen .$this->lang->close;
  345. $this->view->product = $product;
  346. $this->view->actions = $this->loadModel('action')->getList('product', $productID);
  347. $this->view->users = $this->loadModel('user')->getPairs('noletter');
  348. $this->view->fields = $this->productZen->getFormFields4Close();
  349. $this->display();
  350. }
  351. /**
  352. * 查看产品。
  353. * View a product.
  354. *
  355. * @param int $productID
  356. * @access public
  357. * @return void
  358. */
  359. public function view($productID)
  360. {
  361. /* Get product. */
  362. $product = $this->product->getStatByID($productID);
  363. if(!$product) return $this->productZen->responseNotFound4View();
  364. if($product->status == 'wait') $product = $this->product->getStatByID($product->id, 'requirement');
  365. $product->desc = $this->loadModel('file')->setImgSize($product->desc);
  366. if($product->line) $product->lineName = $this->loadModel('tree')->getByID($product->line)->name;
  367. if($product->program) $product->programName = $this->loadModel('program')->getByID($product->program)->name;
  368. /* Set navigation menu. */
  369. $this->product->setMenu($productID);
  370. /* Execute hooks. */
  371. $this->executeHooks($productID);
  372. if($this->config->edition != 'open') $this->view->workflowGroups = $this->loadModel('workflowgroup')->getPairs('product', 'scrum', 1, 'all');
  373. $this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->view;
  374. $this->view->product = $product;
  375. $this->view->actions = $this->loadModel('action')->getList('product', $productID);
  376. $this->view->dynamics = $this->action->getDynamic('all', 'all', 'date_desc', 50, $productID);
  377. $this->view->users = $this->user->getPairs('noletter');
  378. $this->view->groups = $this->loadModel('group')->getPairs();
  379. $this->view->branches = $this->loadModel('branch')->getPairs($productID);
  380. $this->view->reviewers = !empty($product->reviewer) ? explode(',', $product->reviewer) : array();
  381. $this->view->members = $this->loadModel('user')->getListByAccounts(array($product->PO, $product->RD, $product->QD, $product->feedback, $product->ticket, $product->createdBy), 'account');
  382. $this->display();
  383. }
  384. /**
  385. * 删除产品。
  386. * Delete a product.
  387. *
  388. * @param int $productID
  389. * @access public
  390. * @return void
  391. */
  392. public function delete($productID)
  393. {
  394. /* Delete product. */
  395. $this->product->deleteByID($productID);
  396. /* Reset session. */
  397. $this->session->set('product', '');
  398. /* Response JSON message. */
  399. $message = $this->executeHooks($productID);
  400. if($message) $this->lang->saveSuccess = $message;
  401. return $this->sendSuccess(array('load' => $this->createLink('product', 'all')));
  402. }
  403. /**
  404. * 产品路线图。
  405. * Road map of a product.
  406. *
  407. * @param int $productID
  408. * @param string $branch
  409. * @access public
  410. * @return void
  411. */
  412. public function roadmap($productID, $branch = 'all')
  413. {
  414. /* Set env variables. */
  415. $this->product->setMenu($productID, $branch);
  416. $this->productZen->saveSession4Roadmap();
  417. /* Generate data. */
  418. $productID = $this->product->checkAccess($productID, $this->products);
  419. $product = $this->product->getByID($productID);
  420. if(empty($product)) $this->locate($this->createLink('product', 'showErrorNone', 'fromModule=product'));
  421. $roadmaps = $this->product->getRoadmap($productID, $branch);
  422. $branches = $product->type == 'normal' ? array(0 => '') : $this->loadModel('branch')->getPairs($productID);
  423. /* Assign view data. */
  424. $this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->roadmap;
  425. $this->view->product = $product;
  426. $this->view->roadmaps = $roadmaps;
  427. $this->view->branches = $branches;
  428. $this->display();
  429. }
  430. /**
  431. * 产品动态。
  432. * Product dynamic.
  433. *
  434. * @param int $productID
  435. * @param string $type
  436. * @param int $param userID
  437. * @param int $recTotal
  438. * @param string $date
  439. * @param string $direction next|pre
  440. * @access public
  441. * @return void
  442. */
  443. public function dynamic($productID = 0, $type = 'today', $param = 0, $recTotal = 0, $date = '', $direction = 'next')
  444. {
  445. if(empty($type)) $type = 'today';
  446. $this->loadModel('action');
  447. /* Save env data. */
  448. $this->productZen->saveBackUriSessionForDynamic();
  449. $productID = $this->product->checkAccess($productID, $this->products);
  450. $this->product->setMenu($productID, 0, $type);
  451. /* Generate orderBy string. */
  452. $orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
  453. /* Get user account. */
  454. $account = 'all';
  455. if($type == 'account')
  456. {
  457. $user = $this->user->getByID($param, 'id');
  458. if($user) $account = $user->account;
  459. }
  460. /* Get actions. */
  461. list($actions, $dateGroups) = $this->productZen->getActionsForDynamic($account, $orderBy, $productID, $type, $date, $direction);
  462. if(empty($recTotal) && $dateGroups) $recTotal = $this->action->getDynamicCount($type == 'account' ? 'all' : $type);
  463. /* Assign. */
  464. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->product->dynamic;
  465. $this->view->userIdPairs = $this->user->getPairs('noletter|nodeleted|noclosed|useid');
  466. $this->view->accountPairs = $this->user->getPairs('noletter|nodeleted|noclosed');
  467. $this->view->productID = $productID;
  468. $this->view->type = $type;
  469. $this->view->orderBy = $orderBy;
  470. $this->view->account = $account;
  471. $this->view->user = isset($user) ? $user : '';
  472. $this->view->param = $param;
  473. $this->view->dateGroups = $dateGroups;
  474. $this->view->direction = $direction;
  475. $this->view->recTotal = $recTotal;
  476. $this->display();
  477. }
  478. /**
  479. * 单个产品的仪表盘。
  480. * The dashboard for one product.
  481. *
  482. * @param int $productID
  483. * @access public
  484. * @return void
  485. */
  486. public function dashboard($productID = 0)
  487. {
  488. /* Check and get product ID. */
  489. $productID = $this->product->checkAccess($productID, $this->products);
  490. /* Set productID to menu. */
  491. $this->product->setMenu($productID);
  492. /* Get product. */
  493. $product = $this->product->getStatByID($productID);
  494. if(!$product) return $this->locate('product', 'all');
  495. $this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->view;
  496. echo $this->fetch('block', 'dashboard', 'dashboard=singleproduct');
  497. }
  498. /**
  499. * 产品排序。
  500. * Sort product.
  501. *
  502. * @access public
  503. * @return void
  504. */
  505. public function updateOrder()
  506. {
  507. /* Can only be order by order sorting. */
  508. if($this->post->orderBy != 'order_asc') return false;
  509. /* Get sorted id list. */
  510. $products = json_decode($this->post->products, true);
  511. asort($products);
  512. $products = array_flip($products);
  513. /* Sort by sorted id list. */
  514. $this->product->updateOrder($products);
  515. }
  516. /**
  517. * 访问与测试相关模块或方法时,又没有相关产品,或者没有产品,会跳转到该方法。例如:qa。
  518. * Show error no product when visit qa.
  519. *
  520. * @param string $moduleName project|qa|execution
  521. * @param string $activeMenu
  522. * @param int $objectID The format of this parameter should be integer.
  523. * @access public
  524. * @return void
  525. */
  526. public function showErrorNone($moduleName = 'qa', $activeMenu = 'index', $objectID = 0)
  527. {
  528. $this->productZen->setShowErrorNoneMenu($moduleName, $activeMenu, $objectID);
  529. $this->view->title = $this->lang->$moduleName->common;
  530. $this->view->objectID = $objectID;
  531. $this->display();
  532. }
  533. /**
  534. * 产品列表。
  535. * Product list.
  536. *
  537. * @param string $browseType
  538. * @param string $orderBy
  539. * @param int $param
  540. * @param int $recTotal
  541. * @param int $recPerPage
  542. * @param int $pageID
  543. * @param int $programID
  544. * @access public
  545. * @return void
  546. */
  547. public function all($browseType = 'noclosed', $orderBy = 'order_asc', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1, $programID = 0)
  548. {
  549. /* Set env data. */
  550. $this->productZen->setMenu4All();
  551. /* Generate statistics of products. */
  552. if($this->config->systemMode == 'light' && $orderBy == 'program_asc') $orderBy = 'order_asc';
  553. if(strpos($orderBy, 'productLine') !== false) $orderBy = str_replace('productLine', 'line', $orderBy);
  554. $this->app->loadClass('pager', true);
  555. $pager = new pager($recTotal, $recPerPage, $pageID);
  556. $productStatList = $this->productZen->getExportData($programID, $browseType, $orderBy, $param, $pager);
  557. /* Generate root program list. */
  558. $rootProgramList = $this->loadModel('program')->getRootProgramList();
  559. /* Save search form. */
  560. $actionURL = $this->createLink('product', 'all', "browseType=bySearch&orderBy=order_asc&queryID=myQueryID");
  561. $this->product->buildProductSearchForm($param, $actionURL);
  562. /* Assign. */
  563. $this->view->title = $this->lang->productCommon;
  564. $this->view->recTotal = $pager->recTotal;
  565. $this->view->productStats = $productStatList;
  566. $this->view->programList = $rootProgramList;
  567. $this->view->users = $this->user->getPairs('noletter');
  568. $this->view->userIdPairs = $this->user->getPairs('noletter|showid');
  569. $this->view->avatarList = $this->user->getAvatarPairs('');
  570. $this->view->orderBy = $orderBy;
  571. $this->view->browseType = $browseType;
  572. $this->view->pager = $pager;
  573. $this->view->showBatchEdit = $this->cookie->showProductBatchEdit;
  574. $this->view->param = $param;
  575. $this->view->recPerPage = $recPerPage;
  576. $this->view->pageID = $pageID;
  577. $this->view->programID = $programID;
  578. $this->display();
  579. }
  580. /**
  581. * 产品看板。
  582. * Product kanban.
  583. *
  584. * @param string $browseType
  585. * @access public
  586. * @return void
  587. */
  588. public function kanban($browseType = 'my')
  589. {
  590. /* Assign. */
  591. $this->view->title = $this->lang->product->kanban;
  592. $this->view->kanbanList = $this->productZen->getKanbanList($browseType);
  593. $this->view->browseType = $browseType;
  594. $this->display();
  595. }
  596. /**
  597. * 维护产品线。
  598. * Manage product line.
  599. *
  600. * @access public
  601. * @return void
  602. */
  603. public function manageLine()
  604. {
  605. $this->app->loadLang('tree');
  606. if($_POST)
  607. {
  608. /* 从POST中获取数据,并预处理数据。 */
  609. /* Get data form post and prepare data. */
  610. $data = form::data($this->config->product->form->manageLine);
  611. $lines = $this->productZen->prepareManageLineExtras($data);
  612. if($lines === false) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  613. /* 添加或更新产品线。 */
  614. /* Add or update product line. */
  615. $response = $this->product->manageLine($lines);
  616. return $this->send($response);
  617. }
  618. $this->loadModel('program');
  619. $linePrograms = array();
  620. $lines = $this->product->getLines();
  621. $userPrograms = array(0 => $this->lang->null) + $this->program->getTopPairs();
  622. $allPrograms = $this->program->getTopPairs('', false, array_column($lines, 'root'));
  623. foreach($lines as $line)
  624. {
  625. $linePrograms[$line->id] = $userPrograms;
  626. if(!isset($userPrograms[$line->root])) $linePrograms[$line->id][$line->root] = $allPrograms[$line->root];
  627. }
  628. $this->view->title = $this->lang->product->manageLine;
  629. $this->view->programs = $userPrograms;
  630. $this->view->linePrograms = $linePrograms;
  631. $this->view->lines = $lines;
  632. $this->view->fields = $this->config->product->form->manageLine;
  633. $this->display();
  634. }
  635. /**
  636. * 白名单列表。
  637. * Get white list personnel.
  638. *
  639. * @param int $productID
  640. * @param string $module
  641. * @param string $objectType
  642. * @param string $orderBy
  643. * @param int $recTotal
  644. * @param int $recPerPage
  645. * @param int $pageID
  646. * @access public
  647. * @return void
  648. */
  649. public function whitelist($productID = 0, $module = 'product', $objectType = 'product', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
  650. {
  651. $this->product->setMenu($productID, 0);
  652. $this->lang->modulePageNav = '';
  653. echo $this->fetch('personnel', 'whitelist', "objectID=$productID&module=$module&browseType=$objectType&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
  654. }
  655. /**
  656. * 添加用户到白名单。
  657. * Adding users to the white list.
  658. *
  659. * @param int $productID
  660. * @param int $deptID
  661. * @param int $copyID
  662. * @param string $branch
  663. * @access public
  664. * @return void
  665. */
  666. public function addWhitelist($productID = 0, $deptID = 0, $copyID = 0)
  667. {
  668. $this->product->setMenu($productID);
  669. $this->lang->modulePageNav = '';
  670. echo $this->fetch('personnel', 'addWhitelist', "objectID=$productID&dept=$deptID&copyID=$copyID&objectType=product&module=product");
  671. }
  672. /*
  673. * 从白名单移除用户。
  674. * Removing users from the white list.
  675. *
  676. * @param int $id
  677. * @param string $confirm
  678. * @access public
  679. * @return void
  680. */
  681. /**
  682. * @param int $id
  683. * @param string $confirm
  684. */
  685. public function unbindWhitelist($id = 0, $confirm = 'no')
  686. {
  687. echo $this->fetch('personnel', 'unbindWhitelist', "id=$id&confirm=$confirm");
  688. }
  689. /**
  690. * 导出产品。
  691. * Export product.
  692. *
  693. * @param int $programID
  694. * @param string $status
  695. * @param string $orderBy
  696. * @param int $param
  697. * @access public
  698. * @return void
  699. */
  700. public function export($programID, $status, $orderBy, $param = 0)
  701. {
  702. if($_POST)
  703. {
  704. /* 获取导出字段和数据。 */
  705. $fields = $this->productZen->getExportFields();
  706. $productStats = $this->productZen->getExportData($programID, $status, $orderBy, $param);
  707. $productStats = $this->productZen->formatExportData($productStats);
  708. /* 如果只导出选中产品,删除非选中产品。 */
  709. if($this->post->exportType == 'selected')
  710. {
  711. $checkedItem = $this->cookie->checkedItem;
  712. foreach($productStats as $i => $product)
  713. {
  714. if(strpos(",$checkedItem,", ",{$product->id},") === false) unset($productStats[$i]);
  715. }
  716. }
  717. if($this->config->edition != 'open') list($fields, $productStats) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $productStats);
  718. $this->post->set('fields', $fields);
  719. $this->post->set('rows', $productStats);
  720. $this->post->set('kind', 'product');
  721. $this->fetch('file', 'export2' . $this->post->fileType, $_POST);
  722. }
  723. /* Get filename to export. */
  724. $fileName = '';
  725. if($programID)
  726. {
  727. $program = $this->loadModel('program')->getByID($programID);
  728. if($program) $fileName = $program->name;
  729. }
  730. if(!$programID || !$fileName) $fileName = $this->lang->product->common;
  731. $fileName .= isset($this->lang->product->featureBar['all'][$status]) ? '-' . $this->lang->product->featureBar['all'][$status] : '';
  732. $this->view->fileName = $fileName;
  733. $this->display();
  734. }
  735. /**
  736. * 需求矩阵。
  737. * Story track.
  738. *
  739. * @param int $productID
  740. * @param string $branch
  741. * @param int $projectID
  742. * @param string $browseType
  743. * @param int $param
  744. * @param string $storyType
  745. * @param string $orderBy
  746. * @param int $recTotal
  747. * @param int $recPerPage
  748. * @param int $pageID
  749. * @access public
  750. * @return void
  751. */
  752. public function track($productID, $branch = '', $projectID = 0, $browseType = 'allstory', $param = 0, $storyType = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
  753. {
  754. $this->config->morphUpdate = false;
  755. $trackOrder = "type,grade,{$orderBy}";
  756. $browseType = strtolower($browseType);
  757. $branch = ($this->cookie->preBranch !== '' and $branch === '') ? $this->cookie->preBranch : $branch;
  758. if(is_bool($branch)) $branch = (string)(int)$branch;
  759. $rawModule = $this->app->rawModule;
  760. if($browseType == 'bysearch' && isset($_SESSION[$rawModule . 'TrackQuery']))
  761. {
  762. if($rawModule == 'product')
  763. {
  764. $queryVar = in_array($storyType, array('requirement', 'epic')) ? "{$storyType}Query" : 'storyQuery';
  765. $this->session->set($queryVar, $_SESSION[$rawModule . 'TrackQuery']);
  766. }
  767. if($rawModule == 'projectstory') $this->session->set('projectstoryQuery', $_SESSION[$rawModule . 'TrackQuery']);
  768. }
  769. /* Set menu. The projectstory module does not execute. */
  770. $this->productZen->setTrackMenu($productID, $branch, $projectID);
  771. /* Init storyType param. */
  772. $storyTypeList = $this->productZen->getActiveStoryTypeForTrack($projectID, $productID);
  773. if(empty($storyType)) $storyType = key($storyTypeList);
  774. /* Load pager. */
  775. if($browseType == 'bysearch' || $this->app->rawModule == 'projectstory')
  776. {
  777. $stories = $this->productZen->getStories($projectID, $productID, $branch, 0, (int)$param, ($browseType == 'bysearch' ? $storyType : 'all'), $browseType, $trackOrder);
  778. }
  779. else
  780. {
  781. $queryStoryType = 'all';
  782. if($storyType == 'requirement') $queryStoryType = 'requirement,story';
  783. if($storyType == 'story') $queryStoryType = 'story';
  784. if($browseType == 'bysearch') $queryStoryType = $storyType;
  785. $this->app->loadClass('pager', true);
  786. $pager = new pager($recTotal, $recPerPage, $pageID);
  787. $stories = $this->productZen->getStoriesByStoryType($productID, $branch, $queryStoryType, $trackOrder, $pager);
  788. $this->view->pager = $pager;
  789. }
  790. $tracks = $this->loadModel('story')->getTracksByStories($stories, $storyType);
  791. $customFields = $this->productZen->getCustomFieldsForTrack($storyType);
  792. $mergeCells = $this->story->getMergeTrackCells($tracks, $customFields['show']);
  793. if(isset($tracks['cols']))
  794. {
  795. $showFields = $customFields['show'];
  796. $cols = array();
  797. foreach($tracks['cols'] as $col)
  798. {
  799. if(in_array($col['name'], $showFields) || (isset($col['parentName']) && in_array($col['parentName'], $showFields))) $cols[] = $col;
  800. }
  801. $tracks['cols'] = $cols;
  802. }
  803. $tasks = array();
  804. if(!empty($tracks['items']))
  805. {
  806. foreach($tracks['items'] as $lane)
  807. {
  808. $taskIdList = array_column($lane['task'], 'id');
  809. $taskList = array_combine($taskIdList, $lane['task']);
  810. $tasks += $taskList;
  811. }
  812. }
  813. /* Build search form. */
  814. $this->productZen->buildSearchFormForTrack($productID, $branch, $projectID, $browseType, $param, $storyType);
  815. $this->view->title = $this->lang->story->track;
  816. $this->view->tracks = $tracks;
  817. $this->view->storyIdList = array_keys($stories);
  818. $this->view->productID = $productID;
  819. $this->view->branch = $branch;
  820. $this->view->projectID = $projectID;
  821. $this->view->browseType = $browseType;
  822. $this->view->param = $param;
  823. $this->view->storyType = $storyType;
  824. $this->view->orderBy = $orderBy;
  825. $this->view->customFields = $customFields;
  826. $this->view->mergeCells = $mergeCells;
  827. $this->view->storyTypeList = $storyTypeList;
  828. $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
  829. $this->view->projectProducts = $this->product->getProductPairsByProject($projectID);
  830. $this->view->tasks = $tasks;
  831. $this->display();
  832. }
  833. /**
  834. * 设置是否展示非当前项目集的项目信息。
  835. * Ajax set show setting.
  836. *
  837. * @access public
  838. * @return void
  839. */
  840. public function ajaxSetShowSetting()
  841. {
  842. $this->loadModel('setting')->updateItem("{$this->app->user->account}.product.showAllProjects", $this->post->showAllProjects);
  843. }
  844. /**
  845. * 设置需求列表展示的需求等级。
  846. * Ajax set show grades.
  847. *
  848. * @param string $module
  849. * @param string $showGrades
  850. * @access public
  851. * @return void
  852. */
  853. public function ajaxSetShowGrades($module, $showGrades)
  854. {
  855. if(strpos($showGrades, $module) === false && in_array($module, array('story', 'epic', 'requirement')))
  856. {
  857. $this->loadModel('setting')->deleteItems("owner={$this->app->user->account}&module={$module}&key=showGrades", $showGrades);
  858. }
  859. else
  860. {
  861. $this->loadModel('setting')->updateItem("{$this->app->user->account}.$module.showGrades", $showGrades);
  862. }
  863. }
  864. /**
  865. * 获取关联某个执行的产品下拉数据。
  866. * Ajax get products.
  867. *
  868. * @param int $executionID
  869. * @access public
  870. * @return void
  871. */
  872. public function ajaxGetProducts($executionID)
  873. {
  874. $items = array();
  875. if(empty($executionID)) return print(json_encode($items));
  876. $this->app->loadLang('build');
  877. $status = empty($this->config->CRProduct) ? 'noclosed' : 'all';
  878. $products = $this->product->getProductPairsByProject($executionID, $status);
  879. foreach($products as $productID => $productName) $items[] = array('text' => $productName, 'value' => $productID);
  880. return print(json_encode($items));
  881. }
  882. /**
  883. * 通过产品ID获取产品信息。
  884. * Ajax get product by id.
  885. *
  886. * @param int $productID
  887. * @access public
  888. * @return void
  889. */
  890. public function ajaxGetProductByID($productID)
  891. {
  892. $product = $this->product->getByID($productID);
  893. $product->branchSourceName = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
  894. $product->branchName = $this->lang->product->branchName[$product->type];
  895. echo json_encode($product);
  896. }
  897. /**
  898. * 获取关联产品的项目下拉数据。
  899. * AJAX: get projects of a product in html select.
  900. *
  901. * @param int $productID
  902. * @param string $branch ''|'all'|int
  903. * @param int $projectID
  904. * @param string $pageType old
  905. * @access public
  906. * @return void
  907. */
  908. public function ajaxGetProjects($productID, $branch = '', $projectID = 0, $pageType = '')
  909. {
  910. $projects = $this->product->getProjectPairsByProduct($productID, $branch);
  911. if($this->app->getViewType() == 'json') return print(json_encode($projects));
  912. if($pageType == 'old') return print(html::select('project', array(0 => '') + $projects, $projectID, "class='form-control' onchange='loadProductExecutions({$productID}, this.value)'"));
  913. $items = array();
  914. foreach($projects as $projectID => $projectName) $items[] = array('text' => $projectName, 'value' => $projectID, 'keys' => $projectName);
  915. return print(json_encode($items));
  916. }
  917. /**
  918. * 获取关联产品的项目下拉数据。
  919. * AJAX: get projects of a product in html select.
  920. *
  921. * @param int $productID
  922. * @param string $branch
  923. * @access public
  924. * @return void
  925. */
  926. public function ajaxGetProjectsByBranch($productID, $branch = '')
  927. {
  928. $projects = $this->product->getProjectPairsByProduct($productID, $branch);
  929. $projectList = array();
  930. foreach($projects as $projectID => $projectName) $projectList[] = array('value' => $projectID, 'text' => $projectName);
  931. return $this->send($projectList);
  932. }
  933. /**
  934. * 获取关联产品的执行下拉数据。
  935. * AJAX: get executions of a product in html select.
  936. *
  937. * @param int $productID
  938. * @param int $projectID
  939. * @param string $branch
  940. * @param string $pageType
  941. * @param int $executionID
  942. * @param string $from showImport
  943. * @param string mode
  944. * @access public
  945. * @return void
  946. */
  947. public function ajaxGetExecutions($productID, $projectID = 0, $branch = '', $pageType = '', $executionID = 0, $from = '', $mode = '')
  948. {
  949. if($this->app->tab == 'execution' && $this->session->execution)
  950. {
  951. $execution = $this->loadModel('execution')->getByID($this->session->execution);
  952. if($execution->type == 'kanban') $projectID = $execution->project;
  953. }
  954. if($projectID) $project = $this->loadModel('project')->getByID($projectID);
  955. $mode .= ($from == 'bugToTask' || empty($this->config->CRExecution)) ? 'noclosed' : '';
  956. $mode .= !$projectID ? ',multiple' : '';
  957. $executions = $this->product->getExecutionPairsByProduct($productID, $branch, $projectID, $from == 'showImport' ? '' : $mode);
  958. if($this->app->getViewType() == 'json') return print(json_encode($executions));
  959. if($pageType == 'old')
  960. {
  961. $datamultiple = !empty($project) ? "data-multiple={$project->multiple}" : '';
  962. return print(html::select('execution', array(0 => '') + $executions, $executionID, "class='form-control' $datamultiple"));
  963. }
  964. $executionList = array();
  965. if($pageType == 'batch')
  966. {
  967. foreach($executions as $executionID => $executionName) $executionList[] = array('value' => $executionID, 'text' => $executionName);
  968. return $this->send($executionList);
  969. }
  970. else
  971. {
  972. foreach($executions as $executionID => $executionName) $executionList[] = array('text' => $executionName, 'value' => $executionID, 'keys' => $executionName);
  973. return print(json_encode($executionList));
  974. }
  975. }
  976. /**
  977. * 获取关联产品的执行下拉数据。
  978. * AJAX: get executions of a product in html select.
  979. *
  980. * @param int $productID
  981. * @param int $projectID
  982. * @param string $branch
  983. * @access public
  984. * @return void
  985. */
  986. public function ajaxGetExecutionsByProject($productID, $projectID = 0, $branch = '')
  987. {
  988. $noMultipleExecutionID = $projectID ? $this->loadModel('execution')->getNoMultipleID($projectID) : '';
  989. $executions = $this->product->getExecutionPairsByProduct($productID, $branch, $projectID, 'multiple,stagefilter');
  990. $executionList = array();
  991. foreach($executions as $executionID => $executionName) $executionList[] = array('value' => $executionID, 'text' => $executionName);
  992. return $this->send(array('executions' => $executionList, 'noMultipleExecutionID' => $noMultipleExecutionID));
  993. }
  994. /**
  995. * 获取产品下的计划下拉数据。
  996. * Get plan drop-down data under product.
  997. *
  998. * @param int $productID
  999. * @param string $branch
  1000. * @param string $params
  1001. * @param bool $skipParent
  1002. * @access public
  1003. * @return void
  1004. */
  1005. public function ajaxGetPlans($productID, $branch = '', $params = '', $skipParent = false)
  1006. {
  1007. $plans = $this->loadModel('productplan')->getPairs($productID, empty($branch) ? 'all' : $branch, $params, $skipParent);
  1008. $items = array();
  1009. foreach($plans as $id => $name)
  1010. {
  1011. if(!$id) continue;
  1012. $items[] = array('text' => $name, 'value' => $id, 'keys' => $name);
  1013. }
  1014. return print(json_encode($items));
  1015. }
  1016. /**
  1017. * 获取产品线的下拉组件数据。
  1018. * Ajax get product lines.
  1019. *
  1020. * @param int $programID
  1021. * @param int $productID
  1022. * @access public
  1023. * @return void
  1024. */
  1025. public function ajaxGetLine($programID, $productID = 0)
  1026. {
  1027. $lines = array();
  1028. if(empty($productID) || $programID) $lines = $this->product->getLinePairs($programID, true);
  1029. $items = array();
  1030. foreach($lines as $lineID => $lineName) $items[] = array('text' => $lineName, 'value' => $lineID);
  1031. if($productID) return print(json_encode(array('multiple' => false, 'defaultValue' => '', 'name' => "lines[$productID]", 'items' => $items)));
  1032. if(!$productID) return print(json_encode(array('multiple' => false, 'defaultValue' => '', 'name' => "line", 'items' => $items)));
  1033. }
  1034. /**
  1035. * 获取产品的评审人下拉列表。
  1036. * Ajax: Get a drop-down list of reviewers for the product..
  1037. *
  1038. * @param int $productID
  1039. * @param int $storyID
  1040. * @access public
  1041. * @return void
  1042. */
  1043. public function ajaxGetReviewers($productID, $storyID = 0)
  1044. {
  1045. /* Get product reviewers. */
  1046. $product = $this->product->getByID($productID);
  1047. $productReviewers = $product->reviewer;
  1048. if(!$productReviewers and $product->acl != 'open') $productReviewers = $this->user->getProductViewListUsers($product);
  1049. $storyReviewers = '';
  1050. if($storyID)
  1051. {
  1052. $story = $this->loadModel('story')->getByID($storyID);
  1053. $storyReviewers = $this->story->getReviewerPairs($story->id, $story->version);
  1054. $storyReviewers = implode(',', array_keys($storyReviewers));
  1055. }
  1056. $reviewers = $this->user->getPairs('noclosed|nodeleted', $storyReviewers, 0, $productReviewers);
  1057. $items = array();
  1058. foreach($reviewers as $account => $realname) $items[] = array('text' => $realname, 'value' => $account, 'keys' => $realname);
  1059. return print(json_encode(array('multiple' => true, 'defaultValue' => $storyReviewers, 'name' => "reviewer", 'items' => $items)));
  1060. }
  1061. /**
  1062. * 获取产品下1.5级下拉数据。
  1063. * Get 1.5 level drop-down data under product.
  1064. *
  1065. * @param int $productID
  1066. * @param string $module
  1067. * @param string $method
  1068. * @param string $extra
  1069. * @param string $from
  1070. * @access public
  1071. * @return void
  1072. * @param int $useLink
  1073. */
  1074. public function ajaxGetDropMenu($productID, $module = 'product', $method = 'browse', $extra = '', $from = '', $useLink = 1)
  1075. {
  1076. $shadow = '0';
  1077. if($this->app->tab == 'qa' || $from == 'qa') $shadow = 'all';
  1078. $products = $this->productZen->getProducts4DropMenu($shadow, $module);
  1079. $programProducts = array();
  1080. foreach($products as $product) $programProducts[$product->program][] = $product;
  1081. $this->view->link = $useLink == 1 ? $this->product->getProductLink($module, $method, $extra) : '#';
  1082. $this->view->productID = $productID;
  1083. $this->view->module = $module;
  1084. $this->view->method = $method;
  1085. $this->view->extra = $extra;
  1086. $this->view->products = $programProducts;
  1087. $this->view->projectID = $this->app->tab == 'project' ? $this->session->project : 0;
  1088. $this->view->programs = $this->loadModel('program')->getPairs(true);
  1089. $this->view->lines = $this->product->getLinePairs();
  1090. $this->display();
  1091. }
  1092. /**
  1093. * 获取产品下1.5级下拉数据。
  1094. * Get 1.5 level drop-down data under product.
  1095. *
  1096. * @param int $productID
  1097. * @param string $module
  1098. * @param string $method
  1099. * @param string $extra
  1100. * @param string $from
  1101. * @access public
  1102. * @return void
  1103. */
  1104. public function ajaxGetOldDropMenu($productID, $module, $method, $extra = '', $from = '')
  1105. {
  1106. $shadow = '0';
  1107. if($this->app->tab == 'qa' || $from == 'qa') $shadow = 'all';
  1108. $products = $this->productZen->getProducts4DropMenu($shadow);
  1109. $programProducts = array();
  1110. foreach($products as $product) $programProducts[$product->program][] = $product;
  1111. $this->view->link = $this->product->getProductLink($module, $method, $extra);
  1112. $this->view->productID = $productID;
  1113. $this->view->module = $module;
  1114. $this->view->method = $method;
  1115. $this->view->extra = $extra;
  1116. $this->view->products = $programProducts;
  1117. $this->view->projectID = $this->app->tab == 'project' ? $this->session->project : 0;
  1118. $this->view->programs = $this->loadModel('program')->getPairs(true);
  1119. $this->view->lines = $this->product->getLinePairs();
  1120. $this->display();
  1121. }
  1122. /**
  1123. * 保存产品ID到session中。
  1124. * Save the product ID to the session.
  1125. *
  1126. * @param int $productID
  1127. * @access public
  1128. * @return void
  1129. */
  1130. public function ajaxSetState($productID)
  1131. {
  1132. $this->session->set('product', $productID, $this->app->tab);
  1133. return $this->send(array('result' => 'success', 'productID' => $this->session->product));
  1134. }
  1135. /**
  1136. * 删除一个产品线。
  1137. * Delete a product line.
  1138. *
  1139. * @param int $lineID
  1140. * @access public
  1141. * @return void
  1142. */
  1143. public function ajaxDeleteLine($lineID)
  1144. {
  1145. $this->product->deleteLine($lineID);
  1146. $link = inlink('manageLine');
  1147. return $this->send(array('result' => 'success', 'callback' => "loadModal(\"$link\", 'manageLineModal');"));
  1148. }
  1149. /**
  1150. * 根据产品线获取产品。
  1151. * AJAX: get products by line.
  1152. *
  1153. * @param int $lineID
  1154. * @access public
  1155. * @return void
  1156. */
  1157. public function ajaxGetProductByLine($lineID)
  1158. {
  1159. $products = $this->product->getList(0, 'all', 1, $lineID);
  1160. return print($products ? json_encode($products) : '');
  1161. }
  1162. /**
  1163. * 刷新产品统计数据。
  1164. * Refresh product stats.
  1165. *
  1166. * @access public
  1167. * @return void
  1168. */
  1169. public function refreshStats()
  1170. {
  1171. $this->product->refreshStats();
  1172. if(dao::isError())
  1173. {
  1174. echo json_encode(dao::getError());
  1175. return true;
  1176. }
  1177. echo 'success';
  1178. }
  1179. }