| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355 |
- <?php
- /**
- * The control file of product module of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
- * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author Chunsheng Wang <chunsheng@cnezsoft.com>
- * @package product
- * @link https://www.zentao.net
- */
- class product extends control
- {
- public $products = array();
- /**
- * Construct function.
- *
- * @param string moduleName
- * @param string methodName
- * @access public
- * @return void
- */
- public function __construct($moduleName = '', $methodName = '')
- {
- parent::__construct($moduleName, $methodName);
- if(!isset($this->app->user)) return;
- /* Load need modules. */
- $this->loadModel('user');
- /* Get all products, if no, goto the create page. */
- $this->products = $this->product->getPairs('all', 0, '', 'all');
- if($this->product->checkLocateCreate($this->products) && $this->app->tab != 'doc') $this->locate($this->createLink('product', 'create'));
- $this->view->products = $this->products;
- }
- /**
- * 产品首页,浏览仪表盘。
- * Index page, browse dashboard.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function index($productID = 0)
- {
- /* Check product id and get product branch. */
- $productID = $this->product->checkAccess($productID, $this->products);
- $branch = (int)$this->cookie->preBranch;
- /* Set Menu. */
- if($this->app->getViewType() != 'mhtml') unset($this->lang->product->menu->index);
- if($this->app->viewType == 'mhtml') $this->product->setMenu($productID, $branch);
- $this->view->title = $this->lang->product->index;
- $this->display();
- }
- /**
- * 产品下项目列表。
- * The projects which linked the product.
- *
- * @param string $status
- * @param int $productID
- * @param string $branch
- * @param string $involved
- * @param string $orderBy
- * @param int $recTotal
- * @param int $recPerPage
- * @param int $pageID
- * @access public
- * @return void
- */
- public function project($status = 'all', $productID = 0, $branch = '', $involved = '0', $orderBy = 'order_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
- {
- if(!$involved) $involved = $this->cookie->involved;
- if($branch === '') $branch = '0';
- $productID = $this->product->checkAccess($productID, $this->products);
- $this->productZen->setProjectMenu($productID, $branch, (string)$this->cookie->preBranch);
- /* Load pager. */
- $this->app->loadClass('pager', true);
- $pager = new pager($recTotal, $recPerPage, $pageID);
- /* Get projects linked product with statistic. */
- $projectStats = $this->product->getProjectStatsByProduct($productID, $status, $branch, (bool)$involved, $orderBy, $pager);
- /* Get project pairs of same program. */
- $product = $this->product->getByID($productID);
- $projects = $this->loadModel('project')->getPairsByProgram($product->program, 'all', false, 'order_asc', '', '', 'product');
- foreach($projectStats as $project) unset($projects[$project->id]);
- $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->product->project;
- $this->view->projectStats = $this->productZen->processProjectListData($projectStats);
- $this->view->PMList = $this->loadModel('user')->getListByAccounts(helper::arrayColumn($projectStats, 'PM'), 'account');
- $this->view->product = $product;
- $this->view->projects = $projects;
- $this->view->status = $status;
- $this->view->users = $this->user->getPairs('noletter');
- $this->view->branchID = $branch;
- $this->view->branchStatus = $this->loadModel('branch')->getByID($branch, 0, 'status');
- $this->view->pager = $pager;
- $this->view->involved = $involved;
- $this->view->orderBy = $orderBy;
- $this->display();
- }
- /**
- * 浏览产品研发/用户需求列表。
- * Browse requirements list of product.
- *
- * @param int $productID
- * @param string $branch all|''|0
- * @param string $browseType
- * @param int $param Story Module ID
- * @param string $storyType requirement|story
- * @param string $orderBy
- * @param int $recTotal
- * @param int $recPerPage
- * @param int $pageID
- * @param int $projectID
- * @access public
- * @return void
- * @param string $from
- * @param int $blockID
- */
- 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)
- {
- $this->app->loadLang('doc');
- $products = $this->product->getPairs('nodeleted', 0, '', 0);
- if(($from == 'doc' || $from == 'ai') && empty($products)) return $this->send(array('result' => 'fail', 'message' => $this->lang->doc->tips->noProduct));
- if(($from == 'doc' || $from == 'ai') && $productID && !$this->product->checkPriv($productID)) return $this->send(array('result'=> 'fail', 'message' => $this->lang->product->accessDenied, 'closeModal' => true));
- $browseType = strtolower($browseType);
- /* Pre process. */
- $this->loadModel('requirement');
- $this->loadModel('epic');
- $this->loadModel('tree');
- $isProjectStory = $this->app->rawModule == 'projectstory';
- $cookieOrderBy = $this->cookie->productStoryOrder ? $this->cookie->productStoryOrder : 'id_desc';
- /* Load pager. */
- $this->app->loadClass('pager', true);
- if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
- $pager = new pager($recTotal, $recPerPage, $pageID);
- /* Get the product can access. */
- if($this->app->tab != 'project' || $from == 'doc' || $from == 'ai')
- {
- $accessProductID = $this->product->checkAccess($productID, $products);
- if($this->app->apiVersion == 'v2' && $accessProductID != $productID) return $this->sendError($this->lang->product->accessDenied);
- $productID = $accessProductID;
- }
- /* Generate data. */
- $productID = ($this->app->tab != 'project' || $from == 'doc' || $from == 'ai') ? $this->product->checkAccess($productID, $products) : $productID;
- $product = $this->productZen->getBrowseProduct($productID);
- $project = $projectID ? $this->loadModel('project')->getByID($projectID) : null;
- $branchID = $this->productZen->getBranchID($product, $branch);
- $orderBy = $orderBy ? $orderBy : $cookieOrderBy;
- $branch = $branchID ? $branchID : $branch;
- /* ATTENTION: be careful to change the order of follow sentences. */
- $this->session->set('storyType', $storyType, 'product');
- $this->productZen->setMenu4Browse($projectID, $productID, $branch, $storyType);
- $this->productZen->saveAndModifyCookie4Browse($productID, $branch, $param, $browseType, $orderBy);
- if($browseType == '')
- {
- $browseType = 'unclosed';
- if($this->config->vision == 'or') $browseType = 'assignedtome';
- }
- /* Generate data. */
- $moduleID = $this->productZen->getModuleId($param, $browseType);
- $stories = $this->productZen->getStories($projectID, $productID, $branchID, $moduleID, $param, $storyType, $browseType, $orderBy, $pager);
- /* Process the sql, get the condition partition, save it to session. */
- $this->loadModel('common')->saveQueryCondition($this->dao->get(), $storyType, (strpos('bysearch,reviewbyme,bymodule', $browseType) === false && !$isProjectStory));
- /* Append children. */
- if($storyType != 'story') $stories = $this->loadModel('story')->appendChildren($productID, $stories, $storyType);
- /* Save session. */
- $this->productZen->saveSession4Browse($product, $browseType);
- /* Build search form. */
- $this->productZen->buildSearchFormForBrowse($project, $projectID, $productID, $branch, $param, $storyType, $browseType, $isProjectStory, $from, $blockID);
- /* Build confirmeObject. */
- if($this->config->edition == 'ipd' && $storyType == 'story') $this->loadModel('story')->getAffectObject($stories, 'story');
- /* Assign. */
- $this->view->moduleID = $moduleID;
- $this->view->pager = $pager;
- $this->view->orderBy = $orderBy;
- $this->view->param = $param;
- $this->view->moduleTree = $this->productZen->getModuleTree($projectID, $productID, $branch, $param, $storyType, $browseType);
- $this->view->from = $from;
- $this->view->blockID = $blockID;
- $this->view->docBlock = false;
- $this->view->idList = '';
- if($from === 'doc')
- {
- $this->view->products = $products;
- $docBlock = $this->loadModel('doc')->getDocBlock($blockID);
- $this->view->docBlock = $docBlock;
- if($docBlock)
- {
- $content = json_decode($docBlock->content, true);
- if(isset($content['idList'])) $this->view->idList = $content['idList'];
- }
- }
- $this->productZen->assignBrowseData($stories, $browseType, $storyType, $isProjectStory, $product, $project, $branch, $branchID, $from);
- }
- /**
- * 创建产品。可以是顶级产品,也可以是项目集下的产品。
- * Create a product.
- *
- * @param int $programID
- * @param string $extra
- * @access public
- * @return void
- */
- public function create($programID = 0, $extra = '')
- {
- $extra = str_replace(array(',', ' '), array('&', ''), $extra);
- parse_str($extra, $output);
- $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0;
- if(!empty($_POST))
- {
- $productData = $this->productZen->buildProductForCreate($workflowGroup);
- $productID = $this->product->create($productData, (string) $this->post->lineName);
- if(dao::isError()) return $this->sendError(dao::getError());
- $response = $this->productZen->responseAfterCreate($productID, !empty($productData->program) ? $productData->program : 0);
- return $this->send($response);
- }
- $this->productZen->setCreateMenu($programID);
- if($workflowGroup)
- {
- $product = new stdclass();
- $product->id = 0;
- $product->workflowGroup = $workflowGroup;
- $this->view->product = $product;
- }
- $this->view->title = $this->lang->product->create;
- $this->view->gobackLink = $this->productZen->getBackLink4Create($extra);
- $this->view->programID = $programID;
- $this->view->fields = $this->productZen->getFormFields4Create($programID, $extra);
- $this->view->loadUrl = $this->createLink('product', 'create', "programID={program}&extra={$extra}");
- $this->display();
- }
- /**
- * 编辑产品。
- * Edit a product.
- *
- * @param int $productID
- * @param string $action
- * @param string $extra
- * @param int $programID
- * @access public
- * @return void
- */
- public function edit($productID, $action = 'edit', $extra = '', $programID = 0)
- {
- $extra = str_replace(array(',', ' '), array('&', ''), $extra);
- parse_str($extra, $output);
- $workflowGroup = !empty($output['workflowGroup']) ? (int)$output['workflowGroup'] : 0;
- if(!empty($_POST))
- {
- $productData = $this->productZen->buildProductForEdit($productID, $workflowGroup);
- $this->product->update($productID, $productData);
- if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
- if($action == 'undelete') $this->loadModel('action')->undelete((int)$extra);
- $response = $this->productZen->responseAfterEdit($productID, $programID);
- return $this->send($response);
- }
- $productID = $this->product->checkAccess($productID, $this->products);
- $this->productZen->setEditMenu($productID, $programID);
- $product = $this->product->getByID($productID);
- if($workflowGroup) $product->workflowGroup = $workflowGroup;
- $this->view->title = $this->lang->product->edit . $this->lang->hyphen . $product->name;
- $this->view->product = $product;
- $this->view->fields = $this->productZen->getFormFields4Edit($product);
- $this->view->action = $action;
- $this->view->programID = $programID;
- $this->display();
- }
- /**
- * 根据POST过来的ID列表,批量编辑相应产品。
- * Batch edit products.
- *
- * @param int $programID
- * @access public
- * @return void
- */
- public function batchEdit($programID = 0)
- {
- if($this->post->name)
- {
- /* 从POST中获取数据。 */
- $products = form::batchData($this->config->product->form->batchEdit)->get();
- if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
- $result = $this->product->batchUpdate($products);
- if(dao::isError()) return $this->send($result);
- $response = $this->productZen->responseAfterBatchEdit($programID);
- return $this->send($response);
- }
- /* 获取要修改的产品ID列表。*/
- $productIdList = $this->post->productIDList;
- if(empty($productIdList)) return $this->locate($this->session->productList);
- /* Set menu when page come from program. */
- if($this->app->tab == 'program' && $programID) common::setMenuVars('program', $programID);
- if($this->config->vision == 'or') unset($this->lang->product->statusList['normal']);
- /* Set custom. */
- foreach(explode(',', $this->config->product->list->customBatchEditFields) as $field) $customFields[$field] = $this->lang->product->$field;
- $this->view->customFields = $customFields;
- $this->view->showFields = $this->config->product->custom->batchEditFields;
- /* 构造批量编辑页面表单配置数据。*/
- $this->productZen->buildBatchEditForm($programID, $productIdList);
- }
- /**
- * 激活产品。
- * Activate product.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function activate($productID)
- {
- if(!empty($_POST))
- {
- $productData = $this->productZen->buildProductForActivate($productID);
- $this->product->activate($productID, $productData, $this->post->comment);
- if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
- $this->executeHooks($productID);
- return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
- }
- $this->product->setMenu($productID);
- $product = $this->product->getByID($productID);
- $this->view->title = $product->name . $this->lang->hyphen .$this->lang->close;
- $this->view->product = $product;
- $this->view->actions = $this->loadModel('action')->getList('product', $productID);
- $this->view->users = $this->loadModel('user')->getPairs('noletter');
- $this->view->fields = $this->productZen->getFormFields4Activate();
- $this->display();
- }
- /**
- * 关闭产品。
- * Close product.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function close($productID)
- {
- if(!empty($_POST))
- {
- $productData = $this->productZen->buildProductForClose($productID);
- $this->product->close($productID, $productData, $this->post->comment);
- if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
- $this->executeHooks($productID);
- return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
- }
- $this->product->setMenu($productID);
- $product = $this->product->getByID($productID);
- $this->view->title = $product->name . $this->lang->hyphen .$this->lang->close;
- $this->view->product = $product;
- $this->view->actions = $this->loadModel('action')->getList('product', $productID);
- $this->view->users = $this->loadModel('user')->getPairs('noletter');
- $this->view->fields = $this->productZen->getFormFields4Close();
- $this->display();
- }
- /**
- * 查看产品。
- * View a product.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function view($productID)
- {
- /* Get product. */
- $product = $this->product->getStatByID($productID);
- if(!$product) return $this->productZen->responseNotFound4View();
- if($product->status == 'wait') $product = $this->product->getStatByID($product->id, 'requirement');
- $product->desc = $this->loadModel('file')->setImgSize($product->desc);
- if($product->line) $product->lineName = $this->loadModel('tree')->getByID($product->line)->name;
- if($product->program) $product->programName = $this->loadModel('program')->getByID($product->program)->name;
- /* Set navigation menu. */
- $this->product->setMenu($productID);
- /* Execute hooks. */
- $this->executeHooks($productID);
- if($this->config->edition != 'open') $this->view->workflowGroups = $this->loadModel('workflowgroup')->getPairs('product', 'scrum', 1, 'all');
- $this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->view;
- $this->view->product = $product;
- $this->view->actions = $this->loadModel('action')->getList('product', $productID);
- $this->view->dynamics = $this->action->getDynamic('all', 'all', 'date_desc', 50, $productID);
- $this->view->users = $this->user->getPairs('noletter');
- $this->view->groups = $this->loadModel('group')->getPairs();
- $this->view->branches = $this->loadModel('branch')->getPairs($productID);
- $this->view->reviewers = !empty($product->reviewer) ? explode(',', $product->reviewer) : array();
- $this->view->members = $this->loadModel('user')->getListByAccounts(array($product->PO, $product->RD, $product->QD, $product->feedback, $product->ticket, $product->createdBy), 'account');
- $this->display();
- }
- /**
- * 删除产品。
- * Delete a product.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function delete($productID)
- {
- /* Delete product. */
- $this->product->deleteByID($productID);
- /* Reset session. */
- $this->session->set('product', '');
- /* Response JSON message. */
- $message = $this->executeHooks($productID);
- if($message) $this->lang->saveSuccess = $message;
- return $this->sendSuccess(array('load' => $this->createLink('product', 'all')));
- }
- /**
- * 产品路线图。
- * Road map of a product.
- *
- * @param int $productID
- * @param string $branch
- * @access public
- * @return void
- */
- public function roadmap($productID, $branch = 'all')
- {
- /* Set env variables. */
- $this->product->setMenu($productID, $branch);
- $this->productZen->saveSession4Roadmap();
- /* Generate data. */
- $productID = $this->product->checkAccess($productID, $this->products);
- $product = $this->product->getByID($productID);
- if(empty($product)) $this->locate($this->createLink('product', 'showErrorNone', 'fromModule=product'));
- $roadmaps = $this->product->getRoadmap($productID, $branch);
- $branches = $product->type == 'normal' ? array(0 => '') : $this->loadModel('branch')->getPairs($productID);
- /* Assign view data. */
- $this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->roadmap;
- $this->view->product = $product;
- $this->view->roadmaps = $roadmaps;
- $this->view->branches = $branches;
- $this->display();
- }
- /**
- * 产品动态。
- * Product dynamic.
- *
- * @param int $productID
- * @param string $type
- * @param int $param userID
- * @param int $recTotal
- * @param string $date
- * @param string $direction next|pre
- * @access public
- * @return void
- */
- public function dynamic($productID = 0, $type = 'today', $param = 0, $recTotal = 0, $date = '', $direction = 'next')
- {
- if(empty($type)) $type = 'today';
- $this->loadModel('action');
- /* Save env data. */
- $this->productZen->saveBackUriSessionForDynamic();
- $productID = $this->product->checkAccess($productID, $this->products);
- $this->product->setMenu($productID, 0, $type);
- /* Generate orderBy string. */
- $orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
- /* Get user account. */
- $account = 'all';
- if($type == 'account')
- {
- $user = $this->user->getByID($param, 'id');
- if($user) $account = $user->account;
- }
- /* Get actions. */
- list($actions, $dateGroups) = $this->productZen->getActionsForDynamic($account, $orderBy, $productID, $type, $date, $direction);
- if(empty($recTotal) && $dateGroups) $recTotal = $this->action->getDynamicCount($type == 'account' ? 'all' : $type);
- /* Assign. */
- $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->product->dynamic;
- $this->view->userIdPairs = $this->user->getPairs('noletter|nodeleted|noclosed|useid');
- $this->view->accountPairs = $this->user->getPairs('noletter|nodeleted|noclosed');
- $this->view->productID = $productID;
- $this->view->type = $type;
- $this->view->orderBy = $orderBy;
- $this->view->account = $account;
- $this->view->user = isset($user) ? $user : '';
- $this->view->param = $param;
- $this->view->dateGroups = $dateGroups;
- $this->view->direction = $direction;
- $this->view->recTotal = $recTotal;
- $this->display();
- }
- /**
- * 单个产品的仪表盘。
- * The dashboard for one product.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function dashboard($productID = 0)
- {
- /* Check and get product ID. */
- $productID = $this->product->checkAccess($productID, $this->products);
- /* Set productID to menu. */
- $this->product->setMenu($productID);
- /* Get product. */
- $product = $this->product->getStatByID($productID);
- if(!$product) return $this->locate('product', 'all');
- $this->view->title = $product->name . $this->lang->hyphen . $this->lang->product->view;
- echo $this->fetch('block', 'dashboard', 'dashboard=singleproduct');
- }
- /**
- * 产品排序。
- * Sort product.
- *
- * @access public
- * @return void
- */
- public function updateOrder()
- {
- /* Can only be order by order sorting. */
- if($this->post->orderBy != 'order_asc') return false;
- /* Get sorted id list. */
- $products = json_decode($this->post->products, true);
- asort($products);
- $products = array_flip($products);
- /* Sort by sorted id list. */
- $this->product->updateOrder($products);
- }
- /**
- * 访问与测试相关模块或方法时,又没有相关产品,或者没有产品,会跳转到该方法。例如:qa。
- * Show error no product when visit qa.
- *
- * @param string $moduleName project|qa|execution
- * @param string $activeMenu
- * @param int $objectID The format of this parameter should be integer.
- * @access public
- * @return void
- */
- public function showErrorNone($moduleName = 'qa', $activeMenu = 'index', $objectID = 0)
- {
- $this->productZen->setShowErrorNoneMenu($moduleName, $activeMenu, $objectID);
- $this->view->title = $this->lang->$moduleName->common;
- $this->view->objectID = $objectID;
- $this->display();
- }
- /**
- * 产品列表。
- * Product list.
- *
- * @param string $browseType
- * @param string $orderBy
- * @param int $param
- * @param int $recTotal
- * @param int $recPerPage
- * @param int $pageID
- * @param int $programID
- * @access public
- * @return void
- */
- public function all($browseType = 'noclosed', $orderBy = 'order_asc', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1, $programID = 0)
- {
- /* Set env data. */
- $this->productZen->setMenu4All();
- /* Generate statistics of products. */
- if($this->config->systemMode == 'light' && $orderBy == 'program_asc') $orderBy = 'order_asc';
- if(strpos($orderBy, 'productLine') !== false) $orderBy = str_replace('productLine', 'line', $orderBy);
- $this->app->loadClass('pager', true);
- $pager = new pager($recTotal, $recPerPage, $pageID);
- $productStatList = $this->productZen->getExportData($programID, $browseType, $orderBy, $param, $pager);
- /* Generate root program list. */
- $rootProgramList = $this->loadModel('program')->getRootProgramList();
- /* Save search form. */
- $actionURL = $this->createLink('product', 'all', "browseType=bySearch&orderBy=order_asc&queryID=myQueryID");
- $this->product->buildProductSearchForm($param, $actionURL);
- /* Assign. */
- $this->view->title = $this->lang->productCommon;
- $this->view->recTotal = $pager->recTotal;
- $this->view->productStats = $productStatList;
- $this->view->programList = $rootProgramList;
- $this->view->users = $this->user->getPairs('noletter');
- $this->view->userIdPairs = $this->user->getPairs('noletter|showid');
- $this->view->avatarList = $this->user->getAvatarPairs('');
- $this->view->orderBy = $orderBy;
- $this->view->browseType = $browseType;
- $this->view->pager = $pager;
- $this->view->showBatchEdit = $this->cookie->showProductBatchEdit;
- $this->view->param = $param;
- $this->view->recPerPage = $recPerPage;
- $this->view->pageID = $pageID;
- $this->view->programID = $programID;
- $this->display();
- }
- /**
- * 产品看板。
- * Product kanban.
- *
- * @param string $browseType
- * @access public
- * @return void
- */
- public function kanban($browseType = 'my')
- {
- /* Assign. */
- $this->view->title = $this->lang->product->kanban;
- $this->view->kanbanList = $this->productZen->getKanbanList($browseType);
- $this->view->browseType = $browseType;
- $this->display();
- }
- /**
- * 维护产品线。
- * Manage product line.
- *
- * @access public
- * @return void
- */
- public function manageLine()
- {
- $this->app->loadLang('tree');
- if($_POST)
- {
- /* 从POST中获取数据,并预处理数据。 */
- /* Get data form post and prepare data. */
- $data = form::data($this->config->product->form->manageLine);
- $lines = $this->productZen->prepareManageLineExtras($data);
- if($lines === false) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
- /* 添加或更新产品线。 */
- /* Add or update product line. */
- $response = $this->product->manageLine($lines);
- return $this->send($response);
- }
- $this->loadModel('program');
- $linePrograms = array();
- $lines = $this->product->getLines();
- $userPrograms = array(0 => $this->lang->null) + $this->program->getTopPairs();
- $allPrograms = $this->program->getTopPairs('', false, array_column($lines, 'root'));
- foreach($lines as $line)
- {
- $linePrograms[$line->id] = $userPrograms;
- if(!isset($userPrograms[$line->root])) $linePrograms[$line->id][$line->root] = $allPrograms[$line->root];
- }
- $this->view->title = $this->lang->product->manageLine;
- $this->view->programs = $userPrograms;
- $this->view->linePrograms = $linePrograms;
- $this->view->lines = $lines;
- $this->view->fields = $this->config->product->form->manageLine;
- $this->display();
- }
- /**
- * 白名单列表。
- * Get white list personnel.
- *
- * @param int $productID
- * @param string $module
- * @param string $objectType
- * @param string $orderBy
- * @param int $recTotal
- * @param int $recPerPage
- * @param int $pageID
- * @access public
- * @return void
- */
- public function whitelist($productID = 0, $module = 'product', $objectType = 'product', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
- {
- $this->product->setMenu($productID, 0);
- $this->lang->modulePageNav = '';
- echo $this->fetch('personnel', 'whitelist', "objectID=$productID&module=$module&browseType=$objectType&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
- }
- /**
- * 添加用户到白名单。
- * Adding users to the white list.
- *
- * @param int $productID
- * @param int $deptID
- * @param int $copyID
- * @param string $branch
- * @access public
- * @return void
- */
- public function addWhitelist($productID = 0, $deptID = 0, $copyID = 0)
- {
- $this->product->setMenu($productID);
- $this->lang->modulePageNav = '';
- echo $this->fetch('personnel', 'addWhitelist', "objectID=$productID&dept=$deptID©ID=$copyID&objectType=product&module=product");
- }
- /*
- * 从白名单移除用户。
- * Removing users from the white list.
- *
- * @param int $id
- * @param string $confirm
- * @access public
- * @return void
- */
- /**
- * @param int $id
- * @param string $confirm
- */
- public function unbindWhitelist($id = 0, $confirm = 'no')
- {
- echo $this->fetch('personnel', 'unbindWhitelist', "id=$id&confirm=$confirm");
- }
- /**
- * 导出产品。
- * Export product.
- *
- * @param int $programID
- * @param string $status
- * @param string $orderBy
- * @param int $param
- * @access public
- * @return void
- */
- public function export($programID, $status, $orderBy, $param = 0)
- {
- if($_POST)
- {
- /* 获取导出字段和数据。 */
- $fields = $this->productZen->getExportFields();
- $productStats = $this->productZen->getExportData($programID, $status, $orderBy, $param);
- $productStats = $this->productZen->formatExportData($productStats);
- /* 如果只导出选中产品,删除非选中产品。 */
- if($this->post->exportType == 'selected')
- {
- $checkedItem = $this->cookie->checkedItem;
- foreach($productStats as $i => $product)
- {
- if(strpos(",$checkedItem,", ",{$product->id},") === false) unset($productStats[$i]);
- }
- }
- if($this->config->edition != 'open') list($fields, $productStats) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $productStats);
- $this->post->set('fields', $fields);
- $this->post->set('rows', $productStats);
- $this->post->set('kind', 'product');
- $this->fetch('file', 'export2' . $this->post->fileType, $_POST);
- }
- /* Get filename to export. */
- $fileName = '';
- if($programID)
- {
- $program = $this->loadModel('program')->getByID($programID);
- if($program) $fileName = $program->name;
- }
- if(!$programID || !$fileName) $fileName = $this->lang->product->common;
- $fileName .= isset($this->lang->product->featureBar['all'][$status]) ? '-' . $this->lang->product->featureBar['all'][$status] : '';
- $this->view->fileName = $fileName;
- $this->display();
- }
- /**
- * 需求矩阵。
- * Story track.
- *
- * @param int $productID
- * @param string $branch
- * @param int $projectID
- * @param string $browseType
- * @param int $param
- * @param string $storyType
- * @param string $orderBy
- * @param int $recTotal
- * @param int $recPerPage
- * @param int $pageID
- * @access public
- * @return void
- */
- public function track($productID, $branch = '', $projectID = 0, $browseType = 'allstory', $param = 0, $storyType = '', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
- {
- $this->config->morphUpdate = false;
- $trackOrder = "type,grade,{$orderBy}";
- $browseType = strtolower($browseType);
- $branch = ($this->cookie->preBranch !== '' and $branch === '') ? $this->cookie->preBranch : $branch;
- if(is_bool($branch)) $branch = (string)(int)$branch;
- $rawModule = $this->app->rawModule;
- if($browseType == 'bysearch' && isset($_SESSION[$rawModule . 'TrackQuery']))
- {
- if($rawModule == 'product')
- {
- $queryVar = in_array($storyType, array('requirement', 'epic')) ? "{$storyType}Query" : 'storyQuery';
- $this->session->set($queryVar, $_SESSION[$rawModule . 'TrackQuery']);
- }
- if($rawModule == 'projectstory') $this->session->set('projectstoryQuery', $_SESSION[$rawModule . 'TrackQuery']);
- }
- /* Set menu. The projectstory module does not execute. */
- $this->productZen->setTrackMenu($productID, $branch, $projectID);
- /* Init storyType param. */
- $storyTypeList = $this->productZen->getActiveStoryTypeForTrack($projectID, $productID);
- if(empty($storyType)) $storyType = key($storyTypeList);
- /* Load pager. */
- if($browseType == 'bysearch' || $this->app->rawModule == 'projectstory')
- {
- $stories = $this->productZen->getStories($projectID, $productID, $branch, 0, (int)$param, ($browseType == 'bysearch' ? $storyType : 'all'), $browseType, $trackOrder);
- }
- else
- {
- $queryStoryType = 'all';
- if($storyType == 'requirement') $queryStoryType = 'requirement,story';
- if($storyType == 'story') $queryStoryType = 'story';
- if($browseType == 'bysearch') $queryStoryType = $storyType;
- $this->app->loadClass('pager', true);
- $pager = new pager($recTotal, $recPerPage, $pageID);
- $stories = $this->productZen->getStoriesByStoryType($productID, $branch, $queryStoryType, $trackOrder, $pager);
- $this->view->pager = $pager;
- }
- $tracks = $this->loadModel('story')->getTracksByStories($stories, $storyType);
- $customFields = $this->productZen->getCustomFieldsForTrack($storyType);
- $mergeCells = $this->story->getMergeTrackCells($tracks, $customFields['show']);
- if(isset($tracks['cols']))
- {
- $showFields = $customFields['show'];
- $cols = array();
- foreach($tracks['cols'] as $col)
- {
- if(in_array($col['name'], $showFields) || (isset($col['parentName']) && in_array($col['parentName'], $showFields))) $cols[] = $col;
- }
- $tracks['cols'] = $cols;
- }
- $tasks = array();
- if(!empty($tracks['items']))
- {
- foreach($tracks['items'] as $lane)
- {
- $taskIdList = array_column($lane['task'], 'id');
- $taskList = array_combine($taskIdList, $lane['task']);
- $tasks += $taskList;
- }
- }
- /* Build search form. */
- $this->productZen->buildSearchFormForTrack($productID, $branch, $projectID, $browseType, $param, $storyType);
- $this->view->title = $this->lang->story->track;
- $this->view->tracks = $tracks;
- $this->view->storyIdList = array_keys($stories);
- $this->view->productID = $productID;
- $this->view->branch = $branch;
- $this->view->projectID = $projectID;
- $this->view->browseType = $browseType;
- $this->view->param = $param;
- $this->view->storyType = $storyType;
- $this->view->orderBy = $orderBy;
- $this->view->customFields = $customFields;
- $this->view->mergeCells = $mergeCells;
- $this->view->storyTypeList = $storyTypeList;
- $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
- $this->view->projectProducts = $this->product->getProductPairsByProject($projectID);
- $this->view->tasks = $tasks;
- $this->display();
- }
- /**
- * 设置是否展示非当前项目集的项目信息。
- * Ajax set show setting.
- *
- * @access public
- * @return void
- */
- public function ajaxSetShowSetting()
- {
- $this->loadModel('setting')->updateItem("{$this->app->user->account}.product.showAllProjects", $this->post->showAllProjects);
- }
- /**
- * 设置需求列表展示的需求等级。
- * Ajax set show grades.
- *
- * @param string $module
- * @param string $showGrades
- * @access public
- * @return void
- */
- public function ajaxSetShowGrades($module, $showGrades)
- {
- if(strpos($showGrades, $module) === false && in_array($module, array('story', 'epic', 'requirement')))
- {
- $this->loadModel('setting')->deleteItems("owner={$this->app->user->account}&module={$module}&key=showGrades", $showGrades);
- }
- else
- {
- $this->loadModel('setting')->updateItem("{$this->app->user->account}.$module.showGrades", $showGrades);
- }
- }
- /**
- * 获取关联某个执行的产品下拉数据。
- * Ajax get products.
- *
- * @param int $executionID
- * @access public
- * @return void
- */
- public function ajaxGetProducts($executionID)
- {
- $items = array();
- if(empty($executionID)) return print(json_encode($items));
- $this->app->loadLang('build');
- $status = empty($this->config->CRProduct) ? 'noclosed' : 'all';
- $products = $this->product->getProductPairsByProject($executionID, $status);
- foreach($products as $productID => $productName) $items[] = array('text' => $productName, 'value' => $productID);
- return print(json_encode($items));
- }
- /**
- * 通过产品ID获取产品信息。
- * Ajax get product by id.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function ajaxGetProductByID($productID)
- {
- $product = $this->product->getByID($productID);
- $product->branchSourceName = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
- $product->branchName = $this->lang->product->branchName[$product->type];
- echo json_encode($product);
- }
- /**
- * 获取关联产品的项目下拉数据。
- * AJAX: get projects of a product in html select.
- *
- * @param int $productID
- * @param string $branch ''|'all'|int
- * @param int $projectID
- * @param string $pageType old
- * @access public
- * @return void
- */
- public function ajaxGetProjects($productID, $branch = '', $projectID = 0, $pageType = '')
- {
- $projects = $this->product->getProjectPairsByProduct($productID, $branch);
- if($this->app->getViewType() == 'json') return print(json_encode($projects));
- if($pageType == 'old') return print(html::select('project', array(0 => '') + $projects, $projectID, "class='form-control' onchange='loadProductExecutions({$productID}, this.value)'"));
- $items = array();
- foreach($projects as $projectID => $projectName) $items[] = array('text' => $projectName, 'value' => $projectID, 'keys' => $projectName);
- return print(json_encode($items));
- }
- /**
- * 获取关联产品的项目下拉数据。
- * AJAX: get projects of a product in html select.
- *
- * @param int $productID
- * @param string $branch
- * @access public
- * @return void
- */
- public function ajaxGetProjectsByBranch($productID, $branch = '')
- {
- $projects = $this->product->getProjectPairsByProduct($productID, $branch);
- $projectList = array();
- foreach($projects as $projectID => $projectName) $projectList[] = array('value' => $projectID, 'text' => $projectName);
- return $this->send($projectList);
- }
- /**
- * 获取关联产品的执行下拉数据。
- * AJAX: get executions of a product in html select.
- *
- * @param int $productID
- * @param int $projectID
- * @param string $branch
- * @param string $pageType
- * @param int $executionID
- * @param string $from showImport
- * @param string mode
- * @access public
- * @return void
- */
- public function ajaxGetExecutions($productID, $projectID = 0, $branch = '', $pageType = '', $executionID = 0, $from = '', $mode = '')
- {
- if($this->app->tab == 'execution' && $this->session->execution)
- {
- $execution = $this->loadModel('execution')->getByID($this->session->execution);
- if($execution->type == 'kanban') $projectID = $execution->project;
- }
- if($projectID) $project = $this->loadModel('project')->getByID($projectID);
- $mode .= ($from == 'bugToTask' || empty($this->config->CRExecution)) ? 'noclosed' : '';
- $mode .= !$projectID ? ',multiple' : '';
- $executions = $this->product->getExecutionPairsByProduct($productID, $branch, $projectID, $from == 'showImport' ? '' : $mode);
- if($this->app->getViewType() == 'json') return print(json_encode($executions));
- if($pageType == 'old')
- {
- $datamultiple = !empty($project) ? "data-multiple={$project->multiple}" : '';
- return print(html::select('execution', array(0 => '') + $executions, $executionID, "class='form-control' $datamultiple"));
- }
- $executionList = array();
- if($pageType == 'batch')
- {
- foreach($executions as $executionID => $executionName) $executionList[] = array('value' => $executionID, 'text' => $executionName);
- return $this->send($executionList);
- }
- else
- {
- foreach($executions as $executionID => $executionName) $executionList[] = array('text' => $executionName, 'value' => $executionID, 'keys' => $executionName);
- return print(json_encode($executionList));
- }
- }
- /**
- * 获取关联产品的执行下拉数据。
- * AJAX: get executions of a product in html select.
- *
- * @param int $productID
- * @param int $projectID
- * @param string $branch
- * @access public
- * @return void
- */
- public function ajaxGetExecutionsByProject($productID, $projectID = 0, $branch = '')
- {
- $noMultipleExecutionID = $projectID ? $this->loadModel('execution')->getNoMultipleID($projectID) : '';
- $executions = $this->product->getExecutionPairsByProduct($productID, $branch, $projectID, 'multiple,stagefilter');
- $executionList = array();
- foreach($executions as $executionID => $executionName) $executionList[] = array('value' => $executionID, 'text' => $executionName);
- return $this->send(array('executions' => $executionList, 'noMultipleExecutionID' => $noMultipleExecutionID));
- }
- /**
- * 获取产品下的计划下拉数据。
- * Get plan drop-down data under product.
- *
- * @param int $productID
- * @param string $branch
- * @param string $params
- * @param bool $skipParent
- * @access public
- * @return void
- */
- public function ajaxGetPlans($productID, $branch = '', $params = '', $skipParent = false)
- {
- $plans = $this->loadModel('productplan')->getPairs($productID, empty($branch) ? 'all' : $branch, $params, $skipParent);
- $items = array();
- foreach($plans as $id => $name)
- {
- if(!$id) continue;
- $items[] = array('text' => $name, 'value' => $id, 'keys' => $name);
- }
- return print(json_encode($items));
- }
- /**
- * 获取产品线的下拉组件数据。
- * Ajax get product lines.
- *
- * @param int $programID
- * @param int $productID
- * @access public
- * @return void
- */
- public function ajaxGetLine($programID, $productID = 0)
- {
- $lines = array();
- if(empty($productID) || $programID) $lines = $this->product->getLinePairs($programID, true);
- $items = array();
- foreach($lines as $lineID => $lineName) $items[] = array('text' => $lineName, 'value' => $lineID);
- if($productID) return print(json_encode(array('multiple' => false, 'defaultValue' => '', 'name' => "lines[$productID]", 'items' => $items)));
- if(!$productID) return print(json_encode(array('multiple' => false, 'defaultValue' => '', 'name' => "line", 'items' => $items)));
- }
- /**
- * 获取产品的评审人下拉列表。
- * Ajax: Get a drop-down list of reviewers for the product..
- *
- * @param int $productID
- * @param int $storyID
- * @access public
- * @return void
- */
- public function ajaxGetReviewers($productID, $storyID = 0)
- {
- /* Get product reviewers. */
- $product = $this->product->getByID($productID);
- $productReviewers = $product->reviewer;
- if(!$productReviewers and $product->acl != 'open') $productReviewers = $this->user->getProductViewListUsers($product);
- $storyReviewers = '';
- if($storyID)
- {
- $story = $this->loadModel('story')->getByID($storyID);
- $storyReviewers = $this->story->getReviewerPairs($story->id, $story->version);
- $storyReviewers = implode(',', array_keys($storyReviewers));
- }
- $reviewers = $this->user->getPairs('noclosed|nodeleted', $storyReviewers, 0, $productReviewers);
- $items = array();
- foreach($reviewers as $account => $realname) $items[] = array('text' => $realname, 'value' => $account, 'keys' => $realname);
- return print(json_encode(array('multiple' => true, 'defaultValue' => $storyReviewers, 'name' => "reviewer", 'items' => $items)));
- }
- /**
- * 获取产品下1.5级下拉数据。
- * Get 1.5 level drop-down data under product.
- *
- * @param int $productID
- * @param string $module
- * @param string $method
- * @param string $extra
- * @param string $from
- * @access public
- * @return void
- * @param int $useLink
- */
- public function ajaxGetDropMenu($productID, $module = 'product', $method = 'browse', $extra = '', $from = '', $useLink = 1)
- {
- $shadow = '0';
- if($this->app->tab == 'qa' || $from == 'qa') $shadow = 'all';
- $products = $this->productZen->getProducts4DropMenu($shadow, $module);
- $programProducts = array();
- foreach($products as $product) $programProducts[$product->program][] = $product;
- $this->view->link = $useLink == 1 ? $this->product->getProductLink($module, $method, $extra) : '#';
- $this->view->productID = $productID;
- $this->view->module = $module;
- $this->view->method = $method;
- $this->view->extra = $extra;
- $this->view->products = $programProducts;
- $this->view->projectID = $this->app->tab == 'project' ? $this->session->project : 0;
- $this->view->programs = $this->loadModel('program')->getPairs(true);
- $this->view->lines = $this->product->getLinePairs();
- $this->display();
- }
- /**
- * 获取产品下1.5级下拉数据。
- * Get 1.5 level drop-down data under product.
- *
- * @param int $productID
- * @param string $module
- * @param string $method
- * @param string $extra
- * @param string $from
- * @access public
- * @return void
- */
- public function ajaxGetOldDropMenu($productID, $module, $method, $extra = '', $from = '')
- {
- $shadow = '0';
- if($this->app->tab == 'qa' || $from == 'qa') $shadow = 'all';
- $products = $this->productZen->getProducts4DropMenu($shadow);
- $programProducts = array();
- foreach($products as $product) $programProducts[$product->program][] = $product;
- $this->view->link = $this->product->getProductLink($module, $method, $extra);
- $this->view->productID = $productID;
- $this->view->module = $module;
- $this->view->method = $method;
- $this->view->extra = $extra;
- $this->view->products = $programProducts;
- $this->view->projectID = $this->app->tab == 'project' ? $this->session->project : 0;
- $this->view->programs = $this->loadModel('program')->getPairs(true);
- $this->view->lines = $this->product->getLinePairs();
- $this->display();
- }
- /**
- * 保存产品ID到session中。
- * Save the product ID to the session.
- *
- * @param int $productID
- * @access public
- * @return void
- */
- public function ajaxSetState($productID)
- {
- $this->session->set('product', $productID, $this->app->tab);
- return $this->send(array('result' => 'success', 'productID' => $this->session->product));
- }
- /**
- * 删除一个产品线。
- * Delete a product line.
- *
- * @param int $lineID
- * @access public
- * @return void
- */
- public function ajaxDeleteLine($lineID)
- {
- $this->product->deleteLine($lineID);
- $link = inlink('manageLine');
- return $this->send(array('result' => 'success', 'callback' => "loadModal(\"$link\", 'manageLineModal');"));
- }
- /**
- * 根据产品线获取产品。
- * AJAX: get products by line.
- *
- * @param int $lineID
- * @access public
- * @return void
- */
- public function ajaxGetProductByLine($lineID)
- {
- $products = $this->product->getList(0, 'all', 1, $lineID);
- return print($products ? json_encode($products) : '');
- }
- /**
- * 刷新产品统计数据。
- * Refresh product stats.
- *
- * @access public
- * @return void
- */
- public function refreshStats()
- {
- $this->product->refreshStats();
- if(dao::isError())
- {
- echo json_encode(dao::getError());
- return true;
- }
- echo 'success';
- }
- }
|