* @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'; } }