| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628 |
- <?php
- /**
- * The zen 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 chen.tao<chentao@easycorp.ltd>
- * @package product
- * @link https://www.zentao.net
- */
- class productZen extends product
- {
- /**
- * 为控制层的all函数设置共享环境数据。
- * Set shared environment data for all function of control layer.
- *
- * @access protected
- * @return void
- */
- protected function setMenu4All()
- {
- /* Set redirect URI. */
- $this->session->set('productList', $this->app->getURI(true), 'product');
- /* Set activated menu for mobile view. */
- if($this->app->viewType == 'mhtml')
- {
- $productID = $this->product->checkAccess(0, $this->products);
- $this->product->setMenu($productID);
- }
- }
- /**
- * 设置项目页面导航菜单。
- * Set navigation menus for project page.
- *
- * @param int $productID
- * @param string $branch
- * @param string $preBranch
- * @access protected
- * @return void
- */
- protected function setProjectMenu($productID, $branch, $preBranch)
- {
- $branch = ($preBranch !== '' and $branch === '') ? $preBranch : $branch;
- helper::setcookie('preBranch', $branch);
- $this->session->set('createProjectLocate', $this->app->getURI(true), 'product');
- $this->product->setMenu($productID, $branch);
- }
- /**
- * 为创建产品设置导航数据,主要是替换占位符。
- * Set menu for create product page.
- *
- * @param int $programID
- * @access protected
- * @return void
- */
- protected function setCreateMenu($programID)
- {
- if($this->app->tab == 'program') common::setMenuVars('program', $programID);
- if($this->app->tab == 'doc') unset($this->lang->doc->menu->product['subMenu']);
- if($this->app->getViewType() != 'mhtml') return;
- if($this->app->rawModule == 'projectstory' and $this->app->rawMethod == 'story') return $this->loadModel('project')->setMenu();
- $this->product->setMenu();
- }
- /**
- * 为编辑产品设置导航数据,主要是替换占位符。
- * Set menu for edit product page.
- *
- * @param int $productID
- * @param int $programID
- * @access protected
- * @return void
- */
- protected function setEditMenu($productID, $programID)
- {
- if($programID) return common::setMenuVars('program', $programID);
- $this->product->setMenu($productID);
- }
- /**
- * 设置跟踪矩阵导航
- * Set menu for track
- *
- * @param int $productID
- * @param string $branch
- * @param int $projectID
- * @access protected
- * @return void
- */
- protected function setTrackMenu($productID, $branch, $projectID)
- {
- helper::setcookie('preBranch', $branch);
- /* Save session. */
- $uri = $this->app->getURI(true);
- $this->session->set('storyList', $uri, 'product');
- $this->session->set('taskList', $uri, 'execution');
- $this->session->set('designList', $uri, 'project');
- $this->session->set('bugList', $uri, 'qa');
- $this->session->set('caseList', $uri, 'qa');
- $this->session->set('revisionList', $uri, 'repo');
- if($projectID) return $this->loadModel('project')->setMenu($projectID);
- $productID = $this->product->checkAccess($productID, $this->products);
- $this->product->setMenu($productID, $branch);
- }
- /**
- * 为showErrorNone方法,根据不同模块,设置不同的二级或三级导航配置。
- * Set menu for showErrorNone page.
- *
- * @param string $moduleName project|qa|execution
- * @param string $activeMenu
- * @param int $objectID
- * @access protected
- * @return void
- */
- protected function setShowErrorNoneMenu($moduleName, $activeMenu, $objectID)
- {
- if($this->app->getViewType() == 'mhtml') return $this->product->setMenu();
- if($moduleName == 'qa') $this->setShowErrorNoneMenu4QA($activeMenu);
- if($moduleName == 'project') $this->setShowErrorNoneMenu4Project($activeMenu, $objectID);
- if($moduleName == 'execution') $this->setShowErrorNoneMenu4Execution($activeMenu, $objectID);
- }
- /**
- * 为showErrorNone方法,设置在测试视图中的二级导航配置。
- * Set menu for showErrorNone page in qa.
- *
- * @param string $activeMenu
- * @access private
- * @return void
- */
- private function setShowErrorNoneMenu4QA($activeMenu)
- {
- $this->loadModel('qa')->setMenu();
- $this->view->moduleName = 'qa';
- $this->app->rawModule = $activeMenu;
- if($activeMenu == 'testcase') unset($this->lang->qa->menu->testcase['subMenu']);
- if($activeMenu == 'testsuite') unset($this->lang->qa->menu->testcase['subMenu']);
- if($activeMenu == 'testtask') unset($this->lang->qa->menu->testtask['subMenu']);
- if($activeMenu == 'testreport') unset($this->lang->qa->menu->testtask['subMenu']);
- }
- /**
- * 为showErrorNone方法,设置在项目视图中的二级或三级导航配置。
- * Set menu for showErrorNone page in project.
- *
- * @param string $activeMenu
- * @param int $projectID
- * @access private
- * @return void
- */
- private function setShowErrorNoneMenu4Project($activeMenu, $projectID)
- {
- $this->loadModel('project')->setMenu($projectID);
- $this->app->rawModule = $activeMenu;
- $project = $this->project->getByID($projectID);
- $model = zget($project, 'model', 'scrum');
- $this->lang->project->menu = $this->lang->{$model}->menu;
- $this->lang->project->menuOrder = $this->lang->{$model}->menuOrder;
- if($activeMenu == 'bug') $this->lang->{$model}->menu->qa['subMenu']->bug['subModule'] = 'product';
- if($activeMenu == 'testcase') $this->lang->{$model}->menu->qa['subMenu']->testcase['subModule'] = 'product';
- if($activeMenu == 'testtask') $this->lang->{$model}->menu->qa['subMenu']->testtask['subModule'] = 'product';
- if($activeMenu == 'testreport') $this->lang->{$model}->menu->qa['subMenu']->testreport['subModule'] = 'product';
- if($activeMenu == 'projectrelease') $this->lang->{$model}->menu->release['subModule'] = 'projectrelease';
- }
- /**
- * 为showErrorNone方法,设置在执行视图中的三级导航配置。
- * Set menu for showErrorNone page in execution.
- *
- * @param string $activeMenu
- * @param int $executionID
- * @access private
- * @return void
- */
- private function setShowErrorNoneMenu4Execution($activeMenu, $executionID)
- {
- $this->loadModel('execution')->setMenu($executionID);
- $this->app->rawModule = $activeMenu;
- if($activeMenu == 'bug') $this->lang->execution->menu->qa['subMenu']->bug['subModule'] = 'product';
- if($activeMenu == 'testcase') $this->lang->execution->menu->qa['subMenu']->testcase['subModule'] = 'product';
- if($activeMenu == 'testtask') $this->lang->execution->menu->qa['subMenu']->testtask['subModule'] = 'product';
- if($activeMenu == 'testreport') $this->lang->execution->menu->qa['subMenu']->testreport['subModule'] = 'product';
- }
- /**
- * 通过extra获取返回链接。
- * Get goback link for create by extra.
- *
- * @param string $extra
- * @access private
- * @return string
- */
- protected function getBackLink4Create($extra)
- {
- $extra = str_replace(array(',', ' '), array('&', ''), $extra);
- parse_str($extra, $output);
- $backLink = '';
- $from = zget($output, 'from', '');
- if($from == 'qa') $backLink = $this->createLink('qa', 'index');
- if($from == 'global') $backLink = $this->createLink('product', 'all');
- return $backLink;
- }
- /**
- * 设置表单字段下拉选项。
- * Set form select options.
- *
- * @param int $programID
- * @param array $fields
- * @access private
- * @return array
- */
- private function setSelectFormOptions($programID, $fields)
- {
- $users = $this->loadModel('user')->getPairs('nodeleted|noclosed');
- /* 追加字段的name、title属性,展开user数据。 */
- foreach($fields as $field => $attr)
- {
- if(isset($attr['options']) and $attr['options'] == 'users') $fields[$field]['options'] = $users;
- if(!isset($fields[$field]['name'])) $fields[$field]['name'] = $field;
- if(!isset($fields[$field]['title'])) $fields[$field]['title'] = zget($this->lang->product, $field);
- }
- /* 设置下拉菜单内容。 */
- if(isset($fields['groups'])) $fields['groups']['options'] = $this->loadModel('group')->getPairs();
- if(isset($fields['program'])) $fields['program']['options'] = $this->loadModel('program')->getTopPairs('noclosed');
- if(isset($fields['line'])) $fields['line']['options'] = $this->product->getLinePairs($programID, true);
- if($this->config->edition != 'open' && isset($fields['workflowGroup']))
- {
- $groupPairs = $this->loadModel('workflowGroup')->getPairs('product', 'scrum', 1, 'normal', '0');
- $fields['workflowGroup']['options'] = $this->workflowGroup->appendBuildinLabel($groupPairs);
- }
- return $fields;
- }
- /**
- * 获取创建产品页面的表单配置。
- * Get form fields for create.
- *
- * @param int $programID
- * @param string $extra
- * @access protected
- * @return array
- */
- protected function getFormFields4Create($programID = 0, $extra = '')
- {
- $extra = str_replace(array(',', ' '), array('&', ''), $extra);
- parse_str($extra, $output);
- $fields = $this->setSelectFormOptions($programID, $this->config->product->form->create);
- $fields['program']['default'] = $programID ? $programID : '';
- $fields['PO']['default'] = $this->app->user->account;
- /* Set required. */
- foreach($fields as $field => $attr)
- {
- if(!empty($output[$field])) $fields[$field]['default'] = $output[$field];
- if(strpos(",{$this->config->product->create->requiredFields},", ",$field,") !== false) $fields[$field]['required'] = true;
- }
- return $fields;
- }
- /**
- * 获取编辑产品页面的表单配置。
- * Get form fields for create.
- *
- * @param object $product
- * @access private
- * @return array
- */
- protected function getFormFields4Edit($product)
- {
- /* Init fields. */
- $programID = (int)$product->program;
- $fields = $this->setSelectFormOptions($programID, $this->config->product->form->edit);
- $fields['changeProjects'] = array('type' => 'string', 'control' => 'hidden', 'required' => false, 'default' => '');
- /* Check program priv, and append to program list that is not exist product's program. */
- $hasPrivPrograms = $this->app->user->view->programs;
- if($programID and strpos(",{$hasPrivPrograms},", ",{$programID},") === false) $fields['program']['control'] = 'hidden';
- if(isset($fields['program']) and !isset($fields['program']['options'][$programID]) and $programID)
- {
- $program = $this->loadModel('program')->getByID($programID);
- $fields['program']['options'] += array($programID => $program->name);
- }
- /* Set default value by product. */
- foreach($fields as $field => $attr)
- {
- if(isset($product->{$field})) $fields[$field]['default'] = $product->{$field};
- if(strpos(",{$this->config->product->edit->requiredFields},", ",$field,") !== false) $fields[$field]['required'] = true;
- }
- return $fields;
- }
- /**
- * 获取关闭产品页面的表单配置。
- * Get form fields for close product page.
- *
- * @access private
- * @return array
- */
- protected function getFormFields4Close()
- {
- /* Init fields. */
- $fields = $this->config->product->form->close;
- $fields['comment'] = array('type' => 'string', 'control' => 'editor', 'required' => false, 'default' => '', 'width' => 'full');
- return $fields;
- }
- /**
- * 获取激活产品页面的表单配置。
- * Get form fields for activate product page.
- *
- * @access private
- * @return array
- */
- protected function getFormFields4Activate()
- {
- /* Init fields. */
- $fields = $this->config->product->form->activate;
- $fields['comment'] = array('type' => 'string', 'control' => 'editor', 'required' => false, 'default' => '', 'width' => 'full');
- return $fields;
- }
- /**
- * Get product lines and product lines of program.
- *
- * @param array $programIdList
- * @access protected
- * @return array
- */
- protected function getProductLines($programIdList = array())
- {
- /* Get all product lines. */
- $productLines = $this->product->getLines($programIdList);
- /* Collect product lines of program lines. */
- $linePairs = array();
- foreach($programIdList as $programID) $linePairs[$programID] = array();
- foreach($productLines as $line) $linePairs[$line->root][$line->id] = $line->name;
- return array($productLines, $linePairs);
- }
- /**
- * 获取产品导出字段。
- * Get export fields.
- *
- * @access protected
- * @return array
- */
- protected function getExportFields()
- {
- $fieldList = $this->loadModel('datatable') ->getSetting('product', 'all');
- $extendFieldList = $this->product->getFlowExtendFields();
- foreach($extendFieldList as $field => $name)
- {
- if(strpos(strtolower($fieldName), 'epic') !== false && !$this->config->enableER) continue;
- if(strpos(strtolower($fieldName), 'requirement') !== false && !$this->config->URAndSR) continue;
- $fieldName = trim($fieldName);
- $fieldPairs[$fieldName] = zget($productLang, $fieldName);
- $extCol = $config->product->dtable->extendField;
- $extCol['name'] = $field;
- $extCol['title'] = $name;
- $fieldList[$field] = $extCol;
- }
- $fieldPairs = array();
- foreach($fieldList as $fieldKey => $field)
- {
- if(isset($field['headerGroup'])) $field['title'] = $field['headerGroup'] . ' - ' . $field['title'];
- $fieldPairs[$fieldKey] = $field['title'];
- }
- if($this->config->systemMode == 'light') unset($fieldPairs['productLine'], $fieldPairs['program']);
- return $fieldPairs;
- }
- /**
- * 获取导出产品数据。
- * Get export product data.
- *
- * @param int $programID
- * @param string $browseType
- * @param string $orderBy
- * @param int $param
- * @param object $pager
- * @access protected
- * @return array
- */
- protected function getExportData($programID, $browseType, $orderBy, $param = 0, $pager = null)
- {
- $users = $this->user->getPairs('noletter');
- $products = strtolower($browseType) == 'bysearch' ? $this->product->getListBySearch((int)$param) : $this->product->getList($programID, $browseType);
- $productStats = $this->product->getStats(array_keys($products), $orderBy, $pager, 'story', $programID);
- $data = array();
- foreach($productStats as $product) $data[] = $this->product->formatDataForList($product, $users);
- return $data;
- }
- /**
- * 根据产品和已授权项目集,获取关联产品的未授权项目集。
- * Get unauthorized programs by products and authorized programs.
- *
- * @param array $products
- * @param array $authPrograms
- * @access private
- * @return array
- */
- private function getUnauthProgramsOfProducts($products, $authPrograms)
- {
- $unauthPrograms = array();
- $programIdList = array();
- foreach($products as $product)
- {
- if($product->program and !isset($authPrograms[$product->program])) $programIdList[$product->program] = $product->program;
- }
- if($programIdList) $unauthPrograms = $this->program->getPairsByList($programIdList);
- return $unauthPrograms;
- }
- /**
- * 获取在ajaxGetDropMenu方法中使用的产品。
- * Get products for ajaxGetDropMenu method.
- *
- * @param string $shadow 0|all
- * @param string $module
- * @access protected
- * @return array
- */
- protected function getProducts4DropMenu($shadow = '0', $module = '')
- {
- if($this->config->edition != 'open')
- {
- $flow = $this->loadModel('workflow')->getByModule($module);
- if($flow && $flow->belong == 'product') return $this->product->getList(0, 'all', 0, 0, $shadow);
- }
- if($this->app->tab == 'project') return $this->product->getProducts($this->session->project);
- if($this->app->tab == 'feedback') return $this->loadModel('feedback')->getGrantProducts(false, false, 'all');
- return $this->product->getList(0, 'all', 0, 0, $shadow);
- }
- /**
- * 创建完成后,做页面跳转。
- * Locate after create product.
- *
- * @param int $productID
- * @param int $programID
- * @access private
- * @return array
- */
- private function getCreatedLocate($productID, $programID)
- {
- $tab = $this->app->tab;
- $moduleName = $tab == 'program' ? 'program' : $this->moduleName;
- $methodName = $tab == 'program' ? 'product' : 'browse';
- $param = $tab == 'program' ? "programID=$programID" : "productID=$productID";
- $location = isInModal() ? true : $this->createLink($moduleName, $methodName, $param);
- if($tab == 'doc') $location = $this->createLink('doc', 'productSpace', "objectID=$productID");
- return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $location, 'closeModal' => true);
- }
- /**
- * 编辑完成后,做页面跳转。
- * Locate after edit product.
- *
- * @param int $productID
- * @param int $programID
- * @access private
- * @return array
- */
- private function getEditedLocate($productID, $programID)
- {
- $moduleName = $programID ? 'program' : 'product';
- $methodName = $programID ? 'product' : 'view';
- $param = $programID ? "programID=$programID" : "product=$productID";
- $location = $this->createLink($moduleName, $methodName, $param);
- if(!$programID) $this->session->set('productList', $this->createLink('product', 'browse', $param), 'product');
- return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $location);
- }
- /**
- * 构建批量编辑产品页面数据。
- * Build form for batch edit page
- *
- * @param int $programID
- * @param array $productIdList
- * @access protected
- * @return void
- */
- protected function buildBatchEditForm($programID, $productIdList)
- {
- $products = $this->product->getByIdList($productIdList);
- /* 获取项目集和产品线,并将项目集分成授权和非授权。以便view的下拉菜单使用对应的数据。 */
- $authPrograms = array();
- $unauthPrograms = array();
- $lines = array();
- if(in_array($this->config->systemMode, array('ALM', 'PLM')))
- {
- $authPrograms = $this->loadModel('program')->getTopPairs();
- $unauthPrograms = $this->getUnauthProgramsOfProducts($products, $authPrograms);
- /* Get product lines by programs.*/
- $programIdList = array_merge(array(0 => ''), array_keys($authPrograms), array_keys($unauthPrograms));
- list(, $lines) = $this->getProductLines($programIdList);
- }
- /* 给view层赋值。 */
- $this->view->title = $this->lang->product->batchEdit;
- $this->view->lines = $lines;
- $this->view->products = $products;
- $this->view->fields = $this->setSelectFormOptions(0, $this->config->product->form->batchEdit);
- $this->view->programID = $programID;
- $this->view->authPrograms = $authPrograms;
- $this->view->unauthPrograms = $unauthPrograms;
- unset($this->lang->product->typeList['']);
- $this->display();
- }
- /**
- * 构建创建产品的数据。
- * Build product data for create.
- *
- * @param int $workflowGroup
- * @access protected
- * @return object
- */
- protected function buildProductForCreate($workflowGroup = 0)
- {
- $productData = form::data($this->config->product->form->create, 0, $workflowGroup)
- ->setIF($this->config->systemMode == 'light', 'program', (int)zget($this->config->global, 'defaultProgram', 0))
- ->setIF($this->post->acl == 'open', 'whitelist', '')
- ->setDefault('vision', $this->config->vision)
- ->get();
- return $this->loadModel('file')->processImgURL($productData, $this->config->product->editor->create['id'], $this->post->uid);
- }
- /**
- * 构建编辑产品的数据。
- * Build product data for edit.
- *
- * @param int $productID
- * @param int $workflowGroup
- * @access protected
- * @return object
- */
- protected function buildProductForEdit($productID, $workflowGroup = 0)
- {
- $productData = form::data($this->config->product->form->edit, $productID, $workflowGroup)
- ->setIF($this->post->acl == 'open', 'whitelist', '')
- ->get();
- return $this->loadModel('file')->processImgURL($productData, $this->config->product->editor->edit['id'], $this->post->uid);
- }
- /**
- * 构建激活产品数据。
- * Build product data for activate.
- *
- * @param int $productID
- * @access protected
- * @return object
- */
- protected function buildProductForActivate($productID)
- {
- $productData = form::data($this->config->product->form->activate, $productID)
- ->setIF($this->config->vision == 'or', 'status', 'normal')
- ->get();
- return $this->loadModel('file')->processImgURL($productData, $this->config->product->editor->activate['id'], $this->post->uid);
- }
- /**
- * 构建关闭产品数据。
- * Build product data for close.
- *
- * @param int $productID
- * @access protected
- * @return object
- */
- protected function buildProductForClose($productID)
- {
- $productData = form::data($this->config->product->form->close, $productID)->get();
- return $this->loadModel('file')->processImgURL($productData, $this->config->product->editor->close['id'], $this->post->uid);
- }
- /**
- * 预处理产品线数据。
- * Prepare manage line extras.
- *
- * @param form $form
- * @access protected
- * @return array|false
- */
- protected function prepareManageLineExtras($form)
- {
- $data = $form->get();
- /* 拼装产品线列表。列表项目集编号为键,该项目集下的产品线名称列表为值。 */
- /* Build product line list. The program id as key, line name list as value. */
- $lines = array();
- foreach($data->modules as $id => $name)
- {
- if(empty($name)) continue;
- if(!helper::hasFeature('program'))
- {
- $lines[0][$id] = $name;
- continue;
- }
- $programID = $data->programs[$id];
- if(!isset($lines[$programID])) $lines[$programID] = array();
- if(in_array($name, $lines[$programID]))
- {
- dao::$errors["modules[$id]"] = sprintf($this->lang->product->nameIsDuplicate, $name);
- return false;
- }
- $lines[$programID][$id] = $name;
- }
- return $lines;
- }
- /**
- * 成功插入产品数据后,其他的额外操作。
- * Process after create product.
- *
- * @param int $productID
- * @param int $programID
- * @access protected
- * @return array
- */
- protected function responseAfterCreate($productID, $programID)
- {
- $message = $this->executeHooks($productID);
- if($message) $this->lang->saveSuccess = $message;
- /* 移动到control。*/
- if($this->viewType == 'json') return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $productID);
- return $this->getCreatedLocate($productID, $programID);
- }
- /**
- * 成功更新产品数据后,其他的额外操作。
- * Process after edit product.
- *
- * @param int $productID
- * @param int $programID
- * @access protected
- * @return array
- */
- protected function responseAfterEdit($productID, $programID)
- {
- $message = $this->executeHooks($productID);
- if($message) $this->lang->saveSuccess = $message;
- return $this->getEditedLocate($productID, $programID);
- }
- /**
- * 成功批量更新产品数据后,其他的额外操作。
- * Response after batch edit products.
- *
- * @param int $programID
- * @access protected
- * @return array
- */
- protected function responseAfterBatchEdit($programID)
- {
- /* Get location. */
- $location = $this->createLink('program', 'product', "programID=$programID");
- if(empty($programID)) $location = $this->createLink('program', 'productView');
- if($this->app->tab == 'product') $location = $this->createLink('product', 'all');
- return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $location);
- }
- /**
- * 获取研发需求列表页面关联的产品信息。
- * Get the product of the browse page.
- *
- * @param int $productID
- * @access protected
- * @return object|null
- */
- protected function getBrowseProduct($productID)
- {
- $product = $this->product->getById($productID);
- if(empty($product)) return null;
- /* If product does not exist in $this->products list, then attach it to the list. */
- if(!isset($this->products[$product->id])) $this->products[$product->id] = $product->name;
- return $product;
- }
- /**
- * 获取产品分支ID。
- * Get branchID.
- *
- * @param object|null $product
- * @param string $branch
- * @access protected
- * @return string
- */
- protected function getBranchID($product, $branch)
- {
- if(empty($product) || $product->type == 'normal') return 'all';
- /* 获取分支和分支ID。*/
- $branchPairs = $this->loadModel('branch')->getPairs($product->id, 'all');
- return ($this->cookie->preBranch !== '' and $branch === '' and isset($branchPairs[$this->cookie->preBranch])) ? (string)$this->cookie->preBranch : $branch;
- }
- /**
- * 设置导航菜单。
- * Set navigation menu.
- *
- * @param int $projectID
- * @param int $productID
- * @param string $branch
- * @param string $storyType
- * @access protected
- * @return void
- */
- protected function setMenu4Browse($projectID, $productID, $branch, $storyType)
- {
- if($this->app->tab == 'project')
- {
- $this->loadModel('project')->setMenu($projectID);
- return;
- }
- $this->product->setMenu($productID, $branch, $storyType);
- }
- /**
- * browse方法保存并修改cookie数据。
- * Save and modify cookies for browse function.
- *
- * @param int $productID
- * @param string $branch
- * @param int $param
- * @param string $storyType
- * @param string $orderBy
- * @access protected
- * @return void
- * @param string $browseType
- */
- protected function saveAndModifyCookie4Browse($productID, $branch, $param, $browseType, $orderBy)
- {
- /* Set module ID of story. */
- if($this->cookie->preProductID != $productID or $this->cookie->preBranch != $branch or $browseType == 'bybranch')
- {
- $_COOKIE['storyModule'] = 0;
- helper::setcookie('storyModule', '0', 0);
- }
- if($browseType == 'bymodule' or $browseType == '')
- {
- helper::setcookie('storyModule', (string)$param, 0);
- /* The module ID from project app. */
- if($this->app->tab == 'project') helper::setcookie('storyModuleParam', (string)$param, 0);
- /* Re-init the story branch. */
- $_COOKIE['storyBranch'] = 'all';
- helper::setcookie('storyBranch', 'all', 0);
- /* For rendering module tree. */
- if($browseType == '') helper::setcookie('treeBranch', $branch, 0);
- }
- if($browseType == 'bybranch') helper::setcookie('storyBranch', $branch, 0);
- /* Save sort order of product stories list. */
- helper::setcookie('productStoryOrder', $orderBy, 0);
- /* Set product ID and branch of the pre visited product. */
- helper::setcookie('preProductID', (string)$productID);
- helper::setcookie('preBranch', $branch, 0);
- }
- /**
- * 获取模块ID。
- * Get module ID.
- *
- * @param int $param
- * @param string $browseType
- * @access protected
- * @return int
- */
- protected function getModuleId($param, $browseType)
- {
- if($browseType == 'bymodule') return $param;
- $cookieModule = $this->app->tab == 'project' ? $this->cookie->storyModuleParam : $this->cookie->storyModule;
- if($browseType != 'bysearch' && $browseType != 'bybranch' && $cookieModule) return (int)$cookieModule;
- return 0;
- }
- /**
- * 获取模块的树形数据结构。
- * Get the tree structure of modules.
- *
- * @param int $projectID
- * @param int $productID
- * @param string $branch
- * @param int $param
- * @param string $storyType
- * @param string $browseType
- * @access protected
- * @return array|string
- */
- protected function getModuleTree($projectID, $productID, &$branch, $param, $storyType, $browseType)
- {
- if(common::isTutorialMode()) return array();
- /* Set moduleTree. */
- $createModuleLink = 'createStoryLink';
- if($storyType == 'requirement') $createModuleLink = 'createRequirementLink';
- if($storyType == 'epic') $createModuleLink = 'createEpicLink';
- if($browseType == '')
- {
- $browseType = 'unclosed';
- $branch = is_bool($this->cookie->treeBranch) && empty($this->cookie->treeBranch) ? 'all' : $this->cookie->treeBranch;
- }
- /* If invoked by projectstory module and not choose product, then get the modules of project story. */
- if(!isset($this->tree)) $this->loadModel('tree');
- $project = $projectID ? $this->loadModel('project')->getByID($projectID) : null;
- if(!empty($projectID) && !empty($project->hasProduct) && $this->app->rawModule == 'projectstory')
- {
- return $this->tree->getProjectStoryTreeMenu($projectID, 0, array('treeModel', $createModuleLink), array('storyType' => $storyType));
- }
- /* Pre generate parameters. */
- $userFunc = array('treeModel', $createModuleLink);
- $extra = array('projectID' => $projectID, 'productID' => $productID);
- return $this->tree->getTreeMenu($productID, 'story', 0, $userFunc, $extra, $branch, "¶m=$param&storyType=$storyType");
- }
- /**
- * 获取项目下的产品列表。
- * Get products of the project.
- *
- * @param int $projectID
- * @param bool $isProjectStory
- * @access protected
- * @return array
- */
- protected function getProjectProductList($projectID, $isProjectStory)
- {
- if($isProjectStory) return $this->product->getProducts($projectID);
- return array();
- }
- /**
- * 获取产品计划。
- * Get product plans of the project.
- *
- * @param array $projectProducts
- * @param int $projectID
- * @param string $storyType
- * @param bool $isProjectStory
- * @access protected
- * @return array
- */
- protected function getProductPlans($projectProducts, $projectID, $storyType, $isProjectStory)
- {
- if($isProjectStory && $storyType == 'story') return $this->loadModel('execution')->getPlans(array_keys($projectProducts), 'skipParent,unexpired,noclosed', $projectID);
- return array();
- }
- /**
- * 获取需求列表及分页对象。
- * Get stories.
- *
- * @param int $projectID
- * @param int $productID
- * @param string $branchID
- * @param int $moduleID
- * @param int $param
- * @param string $storyType requirement|story
- * @param string $browseType
- * @param string $orderBy
- * @param object $pager
- * @access protected
- * @return array
- */
- public function getStories($projectID, $productID, $branchID = '', $moduleID = 0, $param = 0, $storyType = 'all', $browseType = 'allstory', $orderBy = 'id_desc', $pager = null)
- {
- /* Append id for second sort. */
- $sort = common::appendOrder($orderBy);
- if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
- $isProjectStory = $this->app->rawModule == 'projectstory';
- if(!isset($this->story)) $this->loadModel('story');
- /* Get stories. */
- if($isProjectStory)
- {
- $this->products = $this->product->getProducts($projectID, 'all', '', false);
- if($browseType == 'bybranch') $param = $branchID;
- $stories = $this->story->getExecutionStories($projectID, $productID, $sort, $browseType, (string)$param, 'all', '', $pager);
- }
- else
- {
- $queryID = ($browseType == 'bysearch') ? $param : 0;
- $stories = $this->product->getStories($productID, $branchID, $browseType, (int)$queryID, $moduleID, $storyType, $sort, $pager);
- }
- if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
- return $stories;
- }
- /**
- * 根据需求类型获取需求。
- * Get stories by story type.
- *
- * @param int $productID
- * @param string $branch
- * @param string $storyType
- * @param string $orderBy
- * @param object $pager
- * @access public
- * @return array
- */
- public function getStoriesByStoryType($productID, $branch = '', $storyType = 'all', $orderBy = 'id_desc', $pager = null)
- {
- /* Append id for second sort. */
- $sort = common::appendOrder($orderBy);
- if(strpos($sort, 'pri_') !== false) $sort = str_replace('pri_', 'priOrder_', $sort);
- $parentIdList = array();
- if($storyType == 'requirement,story' && !empty($this->config->enableER)) $parentIdList = $this->dao->select('id')->from(TABLE_STORY)->where('type')->eq('epic')->andWhere('isParent')->eq(1)->fetchPairs('id', 'id');
- if($storyType == 'story' && (!empty($this->config->URAndSR) || $this->config->edition == 'ipd')) $parentIdList = $this->dao->select('id')->from(TABLE_STORY)->where('type')->eq('requirement')->andWhere('isParent')->eq(1)->fetchPairs('id', 'id');
- $productQuery = $this->loadModel('story')->buildProductsCondition($productID, $branch);
- $stories = $this->dao->select("*,plan,path")->from(TABLE_STORY)
- ->where('deleted')->eq(0)
- ->andWhere($productQuery)
- ->andWhere('parent', true)->eq(0)
- ->beginIF($parentIdList)->orWhere("parent")->in($parentIdList)->fi()
- ->markRight(1)
- ->andWhere("FIND_IN_SET('{$this->config->vision}', vision)")
- ->beginIF($storyType != 'all')->andWhere('type')->in($storyType)->fi()
- ->beginIF(empty($this->config->enableER))->andWhere('type')->ne('epic')->fi()
- ->beginIF(empty($this->config->URAndSR) && $this->config->edition != 'ipd')->andWhere('type')->ne('requirement')->fi()
- ->orderBy($orderBy)
- ->page($pager)
- ->fetchAll('id');
- return $stories;
- }
- /**
- * 获取分支的配置数据。
- * Get branch options.
- *
- * @param array $projectProducts
- * @param int $projectID
- * @access protected
- * @return array
- */
- protected function getBranchOptions($projectProducts, $projectID)
- {
- $this->loadModel('branch');
- $branchOptions = array();
- foreach($projectProducts as $product)
- {
- if(!$product || $product->type == 'normal') continue;
- $branches = $this->branch->getList($product->id, $projectID, 'all');
- foreach($branches as $branchInfo) $branchOptions[$product->id][$branchInfo->id] = $branchInfo->name;
- }
- return $branchOptions;
- }
- /**
- * 获取分支和分支标签的显示项。
- * Get options of branch and branch tag.
- *
- * @param int $productID
- * @param object|null $product
- * @param bool $isProjectStory
- * @access protected
- * @return array[]
- * @param int $projectID
- */
- protected function getBranchAndTagOption($projectID, $product, $isProjectStory)
- {
- $branchOption = array();
- $branchTagOption = array();
- if(empty($product) and $isProjectStory) return [$branchOption, $branchTagOption];
- if($product and $product->type != 'normal')
- {
- $branches = $this->loadModel('branch')->getList($product->id, $projectID, 'all');
- foreach($branches as $branchInfo)
- {
- $branchOption[$branchInfo->id] = $branchInfo->name;
- $branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
- }
- }
- return [$branchOption, $branchTagOption];
- }
- /**
- * 获取产品看板。
- * Get kanban list for product.
- *
- * @param string $browseType
- * @access protected
- * @return array
- */
- protected function getKanbanList($browseType = 'my')
- {
- $kanbanList = array();
- list($productList, $planList, $projectList, $projectProduct, $latestExecutions, $releaseList) = $this->product->getStats4Kanban($browseType);
- $programPairs = array(0 => helper::hasFeature('program') ? $this->lang->project->noProgram : '') + $this->loadModel('program')->getPairs(true, 'order_asc');
- $productList = $this->getProductList4Kanban($productList, $planList, $projectList, $releaseList, $projectProduct);
- foreach($productList as $programID => $productList)
- {
- $region = array();
- $heading = new stdclass();
- $heading->title = zget($programPairs, $programID, $programID);
- $region['key'] = $programID;
- $region['id'] = $programID;
- $region['heading'] = $heading;
- $lanes = array();
- $items = array();
- $columnCards = array();
- foreach($productList as $laneKey => $laneData)
- {
- $lanes[] = array('name' => $laneKey, 'title' => $laneData->name);
- $columns = array();
- foreach(array('unexpiredPlan', 'doing', 'doingProject', 'doingExecution', 'normalRelease') as $columnKey)
- {
- $column = array('name' => $columnKey, 'title' => $this->lang->product->{$columnKey});
- if($columnKey == 'doingExecution' || $columnKey == 'doingProject') $column['parentName'] = 'doing';
- $columns[] = $column;
- $cardList = !empty($laneData->{$columnKey}) ? $laneData->{$columnKey} : array();
- foreach($cardList as $card)
- {
- $items[$laneKey][$columnKey][] = array('id' => $card->id, 'name' => $card->id, 'title' => isset($card->name) ? $card->name : $card->title, 'status' => isset($card->status) ? $card->status : '', 'cardType' => $columnKey, 'delay' => !empty($card->delay) ? $card->delay : 0, 'progress' => isset($card->progress) ? $card->progress : 0, 'marker' => isset($card->marker) ? $card->marker : 0);
- if(!isset($columnCards[$columnKey])) $columnCards[$columnKey] = 0;
- $columnCards[$columnKey] ++;
- if($columnKey == 'doingProject')
- {
- if(!empty($latestExecutions[$card->id]))
- {
- $execution = $latestExecutions[$card->id];
- $items[$laneKey]['doingExecution'][] = array('id' => $execution->id, 'name' => $execution->id, 'title' => $execution->name, 'status' => $execution->status, 'cardType' => 'doingExecution', 'delay' => !empty($execution->delay) ? $execution->delay : 0, 'progress' => $execution->progress);
- if(!isset($columnCards['doingExecution'])) $columnCards['doingExecution'] = 0;
- $columnCards['doingExecution'] ++;
- }
- }
- }
- }
- }
- foreach($columns as $key => $column) $columns[$key]['cards'] = !empty($columnCards[$column['name']]) ? $columnCards[$column['name']] : 0;
- $groupData['key'] = $programID;
- $groupData['data']['lanes'] = $lanes;
- $groupData['data']['cols'] = $columns;
- $groupData['data']['items'] = $items;
- $region['items'] = array($groupData);
- $kanbanList[] = $region;
- }
- return $kanbanList;
- }
- /**
- * 保存需求页面session变量。
- * Save session variables for browse page.
- *
- * @param object|null $product
- * @param string $browseType
- * @access protected
- * @return void
- */
- protected function saveSession4Browse($product, $browseType)
- {
- $uri = $this->app->getURI(true);
- /* For setMenu. */
- if($this->app->tab == 'project')
- {
- $this->session->set('storyList', $uri, 'project');
- }
- else
- {
- $this->session->set('productList', $uri, 'product');
- $this->session->set('storyList', $uri, 'product');
- }
- if(!empty($product)) $this->session->set('currentProductType', $product->type);
- /* Save browse type into session for buildSearchForm. */
- if($browseType != 'bymodule' && $browseType != 'bybranch') $this->session->set('storyBrowseType', $browseType);
- }
- /**
- * 构建搜索表单。
- * Build search form.
- *
- * @param object|bool $project
- * @param int $projectID
- * @param int $productID
- * @param string $branch
- * @param int $param
- * @param string $storyType
- * @param string $browseType
- * @param bool $isProjectStory
- * @access protected
- * @return void
- * @param string $from
- * @param int $blockID
- */
- protected function buildSearchFormForBrowse($project, $projectID, &$productID, $branch, $param, $storyType, $browseType, $isProjectStory, $from, $blockID)
- {
- if($isProjectStory && !$productID && !empty($this->products)) $productID = (int)key($this->products); // If toggle a project by the #swapper component on the story page of the projectstory module, the $productID may be empty. Make sure it has value.
- if($this->config->edition == 'ipd' && $storyType == 'story') unset($this->config->product->search['fields']['roadmap']);
- if(isset($project->hasProduct) && empty($project->hasProduct))
- {
- /* The none-product project don't need display the product in the search form. */
- unset($this->config->product->search['fields']['product']);
- unset($this->config->product->search['params']['product']);
- /* The none-product and none-scrum project don't need display the plan in the search form. */
- if($project->model != 'scrum')
- {
- unset($this->config->product->search['fields']['plan']);
- unset($this->config->product->search['params']['plan']);
- }
- }
- if($this->config->edition == 'ipd' && $storyType == 'requirement')
- {
- $this->config->product->search['params']['roadmap']['values'] = $this->loadModel('roadmap')->getPairs($productID);
- }
- /* Build search form. */
- $params = $isProjectStory ? "projectID=$projectID&productID=0" : "productID=$productID";
- $actionURL = $this->createLink($this->app->rawModule, $this->app->rawMethod, $params . "&branch=$branch&browseType=bySearch&queryID=myQueryID&storyType=$storyType&orderBy=&recTotal=0&recPerPage=20&pageID=1&projectID=$projectID&from=$from&blockID=$blockID");
- $this->config->product->search['onMenuBar'] = 'yes';
- $this->config->product->search['module'] = $storyType;
- if($this->app->rawModule != 'product') $this->config->product->search['module'] = $this->app->rawModule;
- $queryID = ($browseType == 'bysearch') ? $param : 0;
- $this->product->buildSearchForm($productID, $this->products, $queryID, $actionURL, $storyType, $branch, $projectID);
- }
- /**
- * 构建矩阵搜索表单。
- * Build search form for track.
- *
- * @param int $productID
- * @param string $branch
- * @param int $projectID
- * @param string $browseType
- * @param int $param
- * @param string $storyType
- * @access protected
- * @return void
- */
- protected function buildSearchFormForTrack(&$productID, $branch, $projectID, $browseType, $param, $storyType)
- {
- if($this->config->edition == 'ipd' && $storyType == 'story') unset($this->config->product->search['fields']['roadmap']);
- if($this->config->edition == 'ipd' && $storyType == 'requirement')
- {
- $this->config->product->search['params']['roadmap']['values'] = $this->loadModel('roadmap')->getPairs($productID);
- }
- /* Build search form. */
- $actionURL = $this->createLink('product', 'track', "productID={$productID}&branch={$branch}&projectID={$projectID}&browseType=bySearch¶m=myQueryID&storyType=$storyType");
- if($this->app->rawModule == 'projectstory') $actionURL = $this->createLink('projectstory', 'track', "projectID={$projectID}&productID={$productID}&branch={$branch}&browseType=bySearch¶m=myQueryID&storyType=$storyType");
- $this->config->product->search['module'] = $this->app->rawModule . 'Track';
- $queryID = ($browseType == 'bysearch') ? (int)$param : 0;
- $this->product->buildSearchForm($productID, $this->products, $queryID, $actionURL, $storyType, $branch, $projectID);
- }
- /**
- * 获取需求的ID列表。
- * Get ID list of stories.
- *
- * @param array $stories
- * @access protected
- * @return array
- */
- protected function getStoryIdList($stories)
- {
- $storyIdList = array();
- foreach($stories as $story)
- {
- $storyIdList[$story->id] = $story->id;
- if(!empty($story->children))
- {
- foreach($story->children as $child) $storyIdList[$child->id] = $child->id;
- }
- }
- return $storyIdList;
- }
- /**
- * 路线图页面保存session数据。
- * Save session variables data.
- *
- * @access protected
- * @return void
- */
- protected function saveSession4Roadmap()
- {
- $this->session->set('releaseList', $this->app->getURI(true), 'product');
- $this->session->set('productPlanList', $this->app->getURI(true), 'product');
- }
- /**
- * 回复产品不存在提示消息。
- * Response product not found message.
- *
- * @access protected
- * @return void
- */
- protected function responseNotFound4View()
- {
- if(defined('RUN_MODE') && RUN_MODE == 'api')
- {
- $this->send(array('status' => 'fail', 'code' => 404, 'message' => '404 Not found'));
- return;
- }
- $this->send(array('result' => 'success', 'load' => array('alert' => $this->lang->notFound, 'locate' => $this->createLink('product', 'all'))));
- return;
- }
- /**
- * 将返回链接保存到session中。
- * Save back uri in session.
- *
- * @access protected
- * @return void
- */
- protected function saveBackUriSessionForDynamic()
- {
- $uri = $this->app->getURI(true);
- $this->session->set('productList', $uri, 'product');
- $this->session->set('productPlanList', $uri, 'product');
- $this->session->set('releaseList', $uri, 'product');
- $this->session->set('storyList', $uri, 'product');
- $this->session->set('projectList', $uri, 'project');
- $this->session->set('executionList', $uri, 'execution');
- $this->session->set('taskList', $uri, 'execution');
- $this->session->set('buildList', $uri, 'execution');
- $this->session->set('bugList', $uri, 'qa');
- $this->session->set('caseList', $uri, 'qa');
- $this->session->set('testtaskList', $uri, 'qa');
- }
- /**
- * 获取操作记录。
- * Get actions of the product.
- *
- * @param string $account
- * @param string $orderBy
- * @param int $productID
- * @param string $type
- * @param string $date
- * @param string $direction next|pre
- * @access protected
- * @return [array, object]
- */
- protected function getActionsForDynamic($account, $orderBy, $productID, $type, $date, $direction)
- {
- /* Build parameters. */
- $period = $type == 'account' ? 'all' : $type;
- $date = empty($date) ? '' : date('Y-m-d', (int)$date);
- $actions = $this->loadModel('action')->getDynamicByProduct($productID, $account, $period, $orderBy, 50, $date, $direction);
- $dateGroups = $this->action->buildDateGroup($actions, $direction);
- return array($actions, $dateGroups);
- }
- /**
- * 获取产品仪表盘操作记录。
- * Get actions of the product for dashboard.
- *
- * @param int $productID
- * @access protected
- * @return array
- */
- protected function getActions4Dashboard($productID)
- {
- /* Load pager. */
- $this->app->loadClass('pager', true);
- $pager = new pager(0, 30, 1);
- return $this->loadModel('action')->getDynamic('all', 'all', 'date_desc', $pager, $productID);
- }
- /**
- * 保存产品仪表盘的返回链接session。
- * Save back uri to session.
- *
- * @access protected
- * @return void
- */
- protected function saveBackUriSession4Dashboard()
- {
- $uri = $this->app->getURI(true);
- $this->session->set('productPlanList', $uri, 'product');
- $this->session->set('releaseList', $uri, 'product');
- }
- /**
- * 获取产品看板页面的产品列表。
- * Get product list for Kanban.
- *
- * @param array $productList
- * @param array $planList
- * @param array $projectList
- * @param array $releaseList
- * @param array $projectProduct
- * @access protected
- * @return array
- */
- protected function getProductList4Kanban($productList, $planList, $projectList, $releaseList, $projectProduct)
- {
- $kanbanList = array();
- foreach($productList as $productID => $product)
- {
- if($product->status != 'normal') continue;
- $projects = array();
- if(isset($projectProduct[$productID]))
- {
- foreach($projectProduct[$productID] as $projectID => $project)
- {
- if(isset($projectList[$projectID])) $projects[$projectID] = $projectList[$projectID];
- }
- }
- $product->unexpiredPlan = zget($planList, $productID, array());
- $product->normalRelease = zget($releaseList, $productID, array());
- $product->doingProject = $projects;
- if(!helper::hasFeature('program')) $product->program = 0;
- $kanbanList[$product->program][$productID] = $product;
- }
- return $kanbanList;
- }
- /**
- * 获取空的小时对象数据。
- * Get empty hour data object.
- *
- * @access protected
- * @return object
- */
- protected function getEmptyHour()
- {
- $hour = new stdclass();
- $hour->totalEstimate = 0;
- $hour->totalConsumed = 0;
- $hour->totalLeft = 0;
- $hour->progress = 0;
- return $hour;
- }
- /**
- * 设置需求列表数据。
- * Set the data of requirement list.
- *
- * @param array $stories
- * @param string $browseType
- * @param string $storyType
- * @param bool $isProjectStory
- * @param object|null $product
- * @param object|null $project
- * @param string $branch
- * @param string $branchID
- * @param string $from
- * @access protected
- * @return void
- */
- protected function assignBrowseData($stories, $browseType, $storyType, $isProjectStory, $product, $project, $branch, $branchID, $from = '')
- {
- $productID = $product ? (int)$product->id : 0;
- $projectID = $project ? (int)$project->id : 0;
- $productName = ($isProjectStory && empty($product)) ? $this->lang->product->all : $this->products[$productID];
- $storyIdList = $this->getStoryIdList($stories);
- $projectProducts = $this->getProjectProductList($projectID, $isProjectStory);
- list($branchOpt, $branchTagOpt) = $this->getBranchAndTagOption($projectID, $product, $isProjectStory);
- $gradeList = $this->loadModel('story')->getGradeList('');
- $gradeGroup = array();
- foreach($gradeList as $grade) $gradeGroup[$grade->type][$grade->grade] = $grade->name;
- /* Set show module by config. */
- $showModule = empty($this->config->product->browse->showModule) ? 0 : $this->config->product->browse->showModule;
- if($isProjectStory) $showModule = empty($this->config->projectstory->story->showModule) ? 0 : $this->config->projectstory->story->showModule;
- $this->view->title = $productName . $this->lang->hyphen . ($storyType === 'story' ? $this->lang->product->browse : $this->lang->product->requirement);
- $this->view->productID = $productID;
- $this->view->product = $product;
- $this->view->projectID = $projectID;
- $this->view->project = $project;
- $this->view->stories = $stories;
- $this->view->gradeGroup = $gradeGroup;
- $this->view->gradeMenu = $this->loadModel('story')->getGradeMenu($storyType, $project);
- $this->view->gradePairs = $this->story->getGradePairs($storyType);
- $this->view->maxGradeGroup = $this->story->getMaxGradeGroup();
- $this->view->storyType = $storyType;
- $this->view->browseType = $browseType;
- $this->view->isProjectStory = $isProjectStory;
- $this->view->branch = $branch;
- $this->view->branchID = $branchID;
- $this->view->modulePairs = !empty($showModule) ? $this->loadModel('tree')->getModulePairs($productID, 'story', $showModule) : array();
- $this->view->branchOptions = (empty($product) && $isProjectStory) ? $this->getBranchOptions($projectProducts, $projectID) : array($productID => $branchOpt);
- $this->view->branchTagOption = $branchTagOpt;
- $this->view->projectProducts = $projectProducts;
- $module = ($this->app->tab == 'product' || $from == 'doc' || $from == 'ai') ? $storyType : $this->app->tab;
- $this->view->showGrades = !empty($this->config->{$module}->showGrades) ? $this->config->{$module}->showGrades : $this->story->getDefaultShowGrades($this->view->gradeMenu);
- $storyType = $isProjectStory ? 'all' : $storyType;
- $this->view->summary = $this->product->summary($stories, $storyType);
- $this->view->plans = $this->loadModel('productplan')->getPairs($productID, isset($projectProducts[$productID]) ? array(BRANCH_MAIN) + $projectProducts[$productID]->branches : (($branch === 'all' || empty($branch)) ? '' : $branch), 'unexpired,noclosed', true);
- $this->view->users = $this->loadModel('user')->getPairs('noletter|pofirst|nodeleted');
- $this->view->modules = $this->loadModel('tree')->getOptionMenu($productID, 'story', 0, $branchID);
- $this->view->storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList);
- $this->view->storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList);
- $this->view->storyCases = $this->loadModel('testcase')->getStoryCaseCounts($storyIdList);
- $this->view->reports = in_array($this->config->edition, array('max', 'ipd')) ? $this->loadModel('researchreport')->getPairs() : array();
- $this->view->roadmaps = ($this->config->edition == 'ipd' && $storyType != 'story') ? array(0 => '') + $this->loadModel('roadmap')->getPairs($productID) : array();
- $this->view->noclosedRoadmaps = ($this->config->edition == 'ipd' && $storyType != 'story') ? array(0 => '') + $this->loadModel('roadmap')->getPairs($productID, '', 'noclosed') : array();
- $this->view->hasFrozenStories = $isProjectStory ? $this->loadModel('project')->hasFrozenObject($projectID, 'SRS') : false;
- $this->display();
- }
- /**
- * 处理项目列表展示数据。
- * Process project list display data.
- *
- * @param array $projectList
- * @access protected
- * @return array
- */
- protected function processProjectListData($projectList)
- {
- $userPairs = $this->loadModel('user')->getPairs();
- $userList = $this->user->getListByAccounts(array_keys($userPairs), 'account');
- $this->loadModel('story');
- $this->loadModel('execution');
- $this->loadModel('project');
- foreach($projectList as $project)
- {
- $project = $this->project->formatDataForList($project, $userList);
- $projectStories = $this->story->getExecutionStories($project->id);
- $project->storyCount = count($projectStories);
- $project->storyPoints = 0;
- foreach($projectStories as $story) $project->storyPoints += $story->estimate;
- $project->storyPoints .= ' ' . $this->config->hourUnit;
- $executions = $this->execution->getStatData($project->id, 'all', 0, 0, false, 'skipParent');
- $project->executionCount = count($executions);
- $project->from = 'project';
- $project->actions = $this->project->buildActionList($project);
- }
- return array_values($projectList);
- }
- /**
- * 为跟踪矩阵设置自定义列。
- * Get custom fields for track.
- *
- * @param string $storyType epic|requirement|story
- * @access protected
- * @return array
- */
- public function getCustomFieldsForTrack($storyType)
- {
- $listFields = array();
- $listFields['requirement'] = $this->lang->URCommon;
- $listFields['story'] = $this->lang->SRCommon;
- $listFields['project'] = $this->lang->story->project;
- $listFields['execution'] = $this->lang->story->execution;
- $listFields['design'] = $this->lang->story->design;
- $listFields['commit'] = $this->lang->story->repoCommit;
- $listFields['task'] = $this->lang->story->tasks;
- $listFields['bug'] = $this->lang->story->bugs;
- $listFields['case'] = $this->lang->story->cases;
- if($storyType == 'requirement' || $storyType == 'story') unset($listFields['requirement']);
- if($storyType == 'story') unset($listFields['story']);
- $systemShowFields = $this->loadModel('setting')->getItem("owner=system&module=product§ion=trackFields&key={$storyType}");
- $accountShowFields = $this->setting->getItem("owner={$this->app->user->account}&module=product§ion=trackFields&key={$storyType}");
- $showFields = empty($accountShowFields) ? $systemShowFields : $accountShowFields;
- $showFields = empty($showFields) ? array_keys($listFields) : explode(',', $showFields);
- return array('list' => $listFields, 'show' => array_merge(array($storyType), $showFields));
- }
- /**
- * 获取根据矩阵可用的需求类型。
- * Get active storyType list for track.
- *
- * @param int $projectID
- * @param int $productID
- * @access protected
- * @return array
- */
- public function getActiveStoryTypeForTrack($projectID = 0, $productID = 0)
- {
- $storyTypeList = $this->lang->story->typeList;
- /* Check story type by linked story's type. */
- if($this->app->rawModule == 'projectstory' && $projectID)
- {
- $stories = $this->getStories($projectID, $productID);
- if($stories)
- {
- $havingType = array();
- foreach($stories as $story) $havingType[$story->type] = $story->type;
- if(!isset($havingType['epic'])) unset($storyTypeList['epic']);
- if(!isset($havingType['epic']) && !isset($havingType['requirement'])) unset($storyTypeList['requirement']);
- }
- }
- return $storyTypeList;
- }
- /**
- * 格式化导出数据。
- * Format export data.
- *
- * @param array $products
- * @access public
- * @return array
- */
- public function formatExportData($products)
- {
- if(empty($products) || $this->config->vision == 'or') return $products;
- foreach($products as $product)
- {
- $product->testCaseCoverage = isset($product->testCaseCoverage) ? $product->testCaseCoverage . '%' : '0%';
- $product->epicCompleteRate = isset($product->epicCompleteRate) ? $product->epicCompleteRate . '%' : '0%';
- $product->requirementCompleteRate = isset($product->requirementCompleteRate) ? $product->requirementCompleteRate . '%' : '0%';
- $product->storyCompleteRate = isset($product->storyCompleteRate) ? $product->storyCompleteRate . '%' : '0%';
- $product->bugFixedRate = isset($product->bugFixedRate) ? $product->bugFixedRate . '%' : '0%';
- }
- return $products;
- }
- }
|