* @package custom * @version $Id$ * @link https://www.zentao.net */ class customModel extends model { /** * 获取自定义语言项。 * Get all custom lang. * * @access public * @return array|false */ public function getAllLang() { $currentLang = $this->app->getClientLang(); $allCustomLang = $this->customTao->getCustomLang(); if(!$allCustomLang) return false; $sectionLang = array(); foreach($allCustomLang as $customLang) { $sectionLang[$customLang->module][$customLang->section][$customLang->lang] = $customLang->lang; } $processedLang = array(); foreach($allCustomLang as $customLang) { if(isset($sectionLang[$customLang->module][$customLang->section]['all']) && isset($sectionLang[$customLang->module][$customLang->section][$currentLang]) && $customLang->lang == 'all') continue; /* Process list featureBar and more language. */ if(strpos($customLang->section, 'featureBar-') !== false || strpos($customLang->section, 'moreSelects-') !== false) { $sections = explode('-', $customLang->section); $sections = array_reverse($sections); if(!isset($processedLang[$customLang->module])) $processedLang[$customLang->module] = array(); $sectionArr = array($customLang->key => $customLang->value); foreach($sections as $section) { $sectionKey = key($sectionArr); $sectionArr[$section] = $sectionArr; if($sectionKey != $section) unset($sectionArr[$sectionKey]); } if(!empty($sectionArr)) $processedLang[$customLang->module] = array_merge_recursive($processedLang[$customLang->module], $sectionArr); } else { if($customLang->module == 'productMenu' && $customLang->key == 'system') $this->lang->product->system = $customLang->value; $processedLang[$customLang->module][$customLang->section][$customLang->key] = $customLang->value; } } return $processedLang; } /** * 设置自定义语言项。 * Set value of an item. * * @param string $path zh-cn.story.soucreList.customer.1 * @param string $value * @access public * @return bool */ public function setItem($path, $value = '') { $level = substr_count($path, '.'); $section = ''; $system = 1; if($level <= 1) return false; if($level == 2) list($lang, $module, $key) = explode('.', $path); if($level == 3) list($lang, $module, $section, $key) = explode('.', $path); if($level == 4) list($lang, $module, $section, $key, $system) = explode('.', $path); $item = new stdclass(); $item->lang = $lang; $item->module = $module; $item->section = $section; $item->key = $key; $item->value = $value; $item->system = $system; if(!$this->app->upgrading) { $item->vision = $this->config->vision; if(in_array("{$item->module}-{$item->section}", $this->config->custom->nonInterface->lang)) $item->vision = 'all'; if($item->module == 'feedback') $item->vision = 'all'; } $this->dao->replace(TABLE_LANG)->data($item)->exec(); return !dao::isError(); } /** * 获取自定义语言项。 * Get value of custom items. * * @param string $paramString see parseItemParam(); * @access public * @return array */ public function getItems($paramString) { return $this->prepareSQL($this->parseItemParam($paramString), 'select')->orderBy('lang,id')->fetchAll('key', false); } /** * 删除自定义项。 * Delete items. * * @param string $paramString see parseItemParam(); * @access public * @return bool */ public function deleteItems($paramString) { $this->prepareSQL($this->parseItemParam($paramString), 'delete')->exec(); return !dao::isError(); } /** * 解析选择或删除项的参数字符串。 * Parse the param string for select or delete items. * * @param string $paramString lang=xxx&module=story§ion=sourceList&key=customer and so on. * @access public * @return array */ public function parseItemParam($paramString) { /* Parse the param string into array. */ parse_str($paramString, $params); /* Init fields not set in the param string. */ $fields = 'lang,module,section,key,vision'; $fields = explode(',', $fields); foreach($fields as $field) { if(isset($params[$field])) continue; $params[$field] = ''; } return $params; } /** * 创建一个DAO对象来选择或删除一条或多条记录。 * Create a DAO object to select or delete one or more records. * * @param array $params the params parsed by parseItemParam() method. * @param string $method select|delete. * @access public * @return object */ public function prepareSQL($params, $method = 'select') { return $this->dao->$method('*')->from(TABLE_LANG)->where('1 = 1') ->beginIF($params['lang'])->andWhere('lang')->in($params['lang'])->fi() ->beginIF($params['module'])->andWhere('module')->in($params['module'])->fi() ->beginIF($params['section'])->andWhere('section')->in($params['section'])->fi() ->beginIF($params['key'])->andWhere('`key`')->in($params['key'])->fi() ->beginIF($params['vision'])->andWhere('`vision`')->in('all,' . $params['vision'])->fi(); } /** * 通过配置文件设置菜单。 * Build menu data from config * * @param object|array $allMenu * @param string|array $customMenu * @param string $module * @access public * @return array */ public static function setMenuByConfig($allMenu, $customMenu, $module = '') { global $app, $lang; $tab = $app->tab; list($customMenuMap, $order) = static::buildCustomMenuMap($allMenu, $customMenu, $module); /* Merge fileMenu && customMenu. */ foreach($customMenuMap as $name => &$item) { if(is_object($allMenu) && !isset($allMenu->{$name})) $allMenu->{$name} = $item; if(is_array($allMenu) && !isset($allMenu[$name])) $allMenu[$name] = $item; if(is_object($allMenu) && isset($allMenu->{$name})) { $menuItem = (array)$allMenu->{$name}; if(isset($menuItem['icon'])) $item->icon = $menuItem['icon']; } } $menu = static::buildMenuItems($allMenu, $customMenuMap, $module, $order); ksort($menu, SORT_NUMERIC); if(!isset($lang->{$tab})) return array(); /* Set divider in main && module menu. */ if(!isset($lang->{$tab}->menuOrder)) { if(!isset($lang->{$tab})) $tab == 'my'; $lang->{$tab}->menuOrder = array(); } ksort($lang->{$tab}->menuOrder, SORT_NUMERIC); $group = 0; $dividerOrders = array(); foreach($lang->{$tab}->menuOrder as $name) { if(isset($lang->{$tab}->dividerMenu) && strpos($lang->{$tab}->dividerMenu, ",{$name},") !== false) $group++; $dividerOrders[$name] = $group; } $isFirst = true; // No divider before First item. $group = 0; foreach($menu as $item) { $isNormalItem = $module == 'main' && isset($item->name) && $item->name != 'other'; if($isNormalItem && isset($dividerOrders[$item->name]) && $dividerOrders[$item->name] > $group) { $menu[$item->order]->divider = $isFirst ? false : true; $group = $dividerOrders[$item->name]; } else { $isFirst = false; if(!isset($menu[$item->order]->divider)) $menu[$item->order]->divider = false; } } return array_values($menu); } /** * 构造自定义导航数据。 * Build custom menu data. * * @param array|string $customMenu * @param string $module * @static * @access public * @return array * @param object|mixed[] $allMenu */ public static function buildCustomMenuMap($allMenu, $customMenu = '', $module = '') { global $lang; $customMenuMap = array(); $order = 1; if($customMenu && is_array($customMenu)) { $prev = ''; foreach($customMenu as $customMenuItem) { if(empty($customMenuItem->name)) continue; $name = $customMenuItem->name; if(!isset($customMenuItem->order)) $customMenuItem->order = $order; if($prev == 'divider') $customMenuItem->divider = true; if($name != 'divider') $customMenuMap[$name] = $customMenuItem; $prev = $name; $order ++; } } elseif($module) { $menuOrder = array(); if($module == 'main' && isset($lang->menuOrder)) { $menuOrder = $lang->menuOrder; } elseif(isset($lang->menu->{$module})) { $moduleMenu = is_object($lang->menu->$module) ? (array)$lang->menu->$module : $lang->menu->$module; if(is_array($moduleMenu) && isset($moduleMenu['menuOrder'])) $menuOrder = $moduleMenu['menuOrder']; } if($menuOrder) { ksort($menuOrder); foreach($menuOrder as $name) { /* If menu is removed, delete the menuOrder. */ if(!isset($allMenu->$name)) continue; $item = new stdclass(); $item->name = $name; $item->hidden = false; $item->order = $order ++; $customMenuMap[$name] = $item; } } } return array($customMenuMap, $order); } /** * 构造菜单数据。 * Build menu data. * * @param object|array $allMenu * @param array $customMenuMap * @param string $menuModuleName * @param int $order * @static * @access public * @return array */ public static function buildMenuItems($allMenu, $customMenuMap, $menuModuleName, $order = 1) { global $config, $app; $menu = array(); $isTutorialMode = commonModel::isTutorialMode(); $module = $menuModuleName; foreach($allMenu as $name => $item) { $name = (string)$name; if(is_object($item)) $item = (array)$item; /* The variable of item has not link && is not link then ignore it. */ $link = (is_array($item) && isset($item['link'])) ? $item['link'] : $item; if(!is_string($link)) continue; $subMenu = array(); $label = $link; $hasPriv = true; $method = ''; if(strpos($link, '|') !== false) { $link = explode('|', $link); list($label, $module, $method) = $link; $methodName = $method; /* 需求列表按照对应的需求模块鉴权。 */ if($module == 'product' && $method == 'browse') { $params = zget($link, 3, ''); preg_match('/storyType=([^&]*)/', $params, $matches); $methodName = !empty($matches[1]) ? $matches[1] : $methodName; } $hasPriv = commonModel::hasPriv($module, $methodName, null, zget($link, 3, '')); /* Fix bug #20464 */ if(isset($vars)) unset($vars); if(!$hasPriv && is_array($item) && isset($item['subMenu'])) { foreach($item['subMenu'] as $subMenu) { if(!isset($subMenu['link']) || strpos($subMenu['link'], '|') === false) continue; if(strpos("|program|product|project|execution|qa|", "|{$app->tab}|") === false && strpos($subMenu['link'], '%s') !== false) continue; list($subLabel, $module, $method) = explode('|', $subMenu['link']); if(count(explode('|', $subMenu['link'])) > 3) list($subLabel, $module, $method, $vars) = explode('|', $subMenu['link']); $hasPriv = commonModel::hasPriv($module, $method); if($hasPriv) break; } } if($module == 'execution' && $method == 'more') $hasPriv = true; if($module == 'project' && $method == 'other') $hasPriv = true; if(!$hasPriv && isset($vars)) unset($vars); } if($isTutorialMode || $hasPriv) { $itemLink = ''; if($module && $method) { $itemLink = array('module' => $module, 'method' => $method); if(isset($link[3])) $itemLink['vars'] = $link[3]; if(isset($vars)) $itemLink['vars'] = $vars; if(is_array($item) && isset($item['target'])) $itemLink['target'] = $item['target']; } /* Process menu item's order and hidden attirbute. */ $menuItem = static::buildMenuItem($item, $customMenuMap, $name, $label, $itemLink, $isTutorialMode, $subMenu); $menuItem->order = (isset($customMenuMap[$name]) && isset($customMenuMap[$name]->order) ? $customMenuMap[$name]->order : $order ++); if(!empty($customMenuMap) && !empty($customMenuMap[$name]->hidden)) $menuItem->hidden = true; // 自定义过滤掉的菜单不显示。 if(isset($customMenuMap[$name]) && isset($customMenuMap[$name]->divider)) $menuItem->divider = true; if($app->viewType == 'mhtml' && isset($config->custom->moblieHidden[$menuModuleName]) && in_array($name, $config->custom->moblieHidden[$menuModuleName])) $menuItem->hidden = 1; // Hidden menu by config in mobile. while(isset($menu[$menuItem->order])) $menuItem->order ++; $menu[$menuItem->order] = $menuItem; } } return $menu; } /** * 构造菜单数据项。 * Build menu item. * * @param array|string $item * @param int $customMenuMap * @param string $name * @param string $label * @param string|array $itemLink * @param bool $isTutorialMode * @param array $subMenu * @static * @access public * @return object */ public static function buildMenuItem($item, $customMenuMap, $name = '', $label = '', $itemLink = '', $isTutorialMode = false, $subMenu = array()) { /* 用例列表featureBar的分割线。 */ if($item === '-') { $menuItem = new stdclass(); $menuItem->type = 'divider'; return $menuItem; } if(is_array($item) && (isset($item['subMenu']) || isset($item['dropMenu']))) { foreach(array('subMenu', 'dropMenu') as $key) { if(!isset($item[$key])) continue; foreach($item[$key] as $subItem) { if(isset($subItem->link['module']) && isset($subItem->link['method'])) $subItem->hidden = !common::hasPriv($subItem->link['module'], $subItem->link['method']); } } } $menuItem = new stdclass(); $menuItem->name = $name; $menuItem->link = $itemLink; $menuItem->text = $label; if(is_array($item) && isset($item['showInMainMenu'])) $menuItem->showInMainMenu = $item['showInMainMenu']; if($isTutorialMode) $menuItem->tutorial = true; if(isset($customMenuMap[$name]) && isset($customMenuMap[$name]->icon)) $menuItem->icon = $customMenuMap[$name]->icon; $attrList = array('class', 'subModule', 'dropMenu', 'alias', 'exclude', 'divider'); $hidden = strpos($name, 'QUERY') === 0 && !isset($customMenuMap[$name]) ? false : isset($customMenuMap[$name]) && isset($customMenuMap[$name]->hidden) && $customMenuMap[$name]->hidden; foreach($attrList as $attr) { if(!empty($item[$attr])) $menuItem->$attr = $item[$attr]; } if($hidden) $menuItem->hidden = $hidden; if($subMenu) $menuItem->subMenu = $subMenu; return $menuItem; } /** * 获取模块菜单数据,如果模块是'main'则返回主菜单。 * Get module menu data, if module is 'main' then return main menu. * * @param string $module * @param bool $isHomeMenu * @access public * @return array */ public static function getModuleMenu($module = 'main', $isHomeMenu = false) { global $app, $lang, $config; if(empty($module)) $module = 'main'; $allMenu = new stdclass(); if($module == 'main' and !empty($lang->menu)) $allMenu = $lang->menu; if($module != 'main' and isset($lang->menu->$module)) { $moduleMenu = is_object($lang->menu->$module) ? (array)$lang->menu->$module : $lang->menu->$module; if(is_array($moduleMenu) && isset($moduleMenu['subMenu'])) $allMenu = $moduleMenu['subMenu']; } if($module == 'product' and isset($allMenu->branch)) $allMenu->branch = str_replace('@branch@', $lang->custom->branch, $allMenu->branch); /* 获取自定义过的导航。 */ $customKey = $isHomeMenu ? $app->tab . '-home' : ($module == 'main' ? $app->tab : $app->tab . '-' . $module); /* 项目自定义导航的key特殊获取。*/ if($app->tab == 'project' && !$isHomeMenu && $module == 'main') { $projectID = isset($_SESSION['project']) ? $_SESSION['project'] : 0; $projectModel = $app->dbh->query("SELECT `model` FROM " . TABLE_PROJECT . " WHERE `id` = '$projectID'")->fetch(); if($projectModel) $customKey = 'project-' . $projectModel->model; } $customMenu = (isset($config->customMenu->{$customKey}) && !commonModel::isTutorialMode()) ? $config->customMenu->{$customKey}: array(); if(!empty($customMenu) && is_string($customMenu) && substr($customMenu, 0, 1) === '[') $customMenu = json_decode($customMenu); if($module == 'my' && empty($config->global->scoreStatus)) unset($allMenu->score); return static::setMenuByConfig($allMenu, $customMenu, $module); } /** * 获取主菜单数据。 * Get main menu data. * * @param bool $isHomeMenu * @access public * @return array */ public static function getMainMenu($isHomeMenu = false) { return static::getModuleMenu('main', $isHomeMenu); } /** * 获取模块的筛选标签。 * Get feature menu. * * @param string $module * @param string $method * @access public * @return array|null */ public static function getFeatureMenu($module, $method) { global $app, $lang, $config; $app->loadLang($module); customModel::mergeFeatureBar($module, $method); $configKey = $config->global->flow . '_feature_' . $module . '_' . $method; $allMenu = isset($lang->$module->featureBar[$method]) ? $lang->$module->featureBar[$method] : null; $customMenu = ''; if(!commonModel::isTutorialMode() && isset($config->customMenu->$configKey)) $customMenu = $config->customMenu->$configKey; if(!empty($customMenu) && is_string($customMenu)) $customMenu = json_decode($customMenu); return $allMenu ? static::setMenuByConfig($allMenu, $customMenu) : null; } /** * 将查询条件合并到筛选标签中。 * Merge shortcut query in featureBar. * * @param string $module * @param string $method * @access public * @return void */ public static function mergeFeatureBar($module, $method) { global $lang, $app, $config; if(!isset($lang->$module->featureBar[$method])) return; $queryModule = $module == 'execution' && $method == 'task' ? 'task' : $module; if($module == 'product') $queryModule = !empty($_SESSION['storyType']) ? $_SESSION['storyType'] : 'story'; if(isset($config->$module->queryModule[$method])) $queryModule = $config->$module->queryModule[$method]; if($module == 'execution' && $method == 'story') $queryModule = 'executionStory'; $shortcuts = $app->dbQuery('select id, title from ' . TABLE_USERQUERY . " where (`account` = '{$app->user->account}' or `common` = '1') AND `module` = '{$queryModule}' AND `shortcut` = '1' order by id")->fetchAll(); if($shortcuts) { $lang->$module->featureBar[$method]['QUERY'] = $lang->custom->common; foreach($shortcuts as $shortcut) $lang->custom->queryList[$shortcut->id] = $shortcut->title; } } /** * 获取必填字段。 * Get required fields by config. * * @param object $moduleConfig * @access public * @return array */ public function getRequiredFields($moduleConfig) { $requiredFields = array(); foreach($moduleConfig as $method => $subConfig) { if(is_object($subConfig) && isset($subConfig->requiredFields)) $requiredFields[$method] = trim(str_replace(' ', '', $subConfig->requiredFields)); } return $requiredFields; } /** * 获取表单必填字段。 * Get form required fields. * * @param string $moduleName * @param string $method * @access public * @return array */ public function getFormFields($moduleName, $method = '') { $fields = array(); $moduleLang = $this->lang->{$moduleName}; $customFields = $this->config->custom->fieldList; if(isset($customFields[$moduleName])) { $fieldList = isset($customFields[$moduleName][$method]) ? $customFields[$moduleName][$method] : $customFields[$moduleName]; if(!is_string($fieldList)) return $fields; if($moduleName == 'user' && $method == 'edit') $this->app->loadConfig('user'); foreach(explode(',', $fieldList) as $fieldName) { if($moduleName == 'user' && $method == 'edit' && strpos($this->config->user->contactField, $fieldName) === false && !in_array($fieldName, array('dept', 'role', 'group', 'email', 'commiter'))) continue; if($moduleName == 'project' && $fieldName == 'budget' && $this->config->vision == 'lite') continue; if($fieldName == 'comment') $fields[$fieldName] = $this->lang->comment; if(isset($moduleLang->{$fieldName}) && is_string($moduleLang->{$fieldName})) $fields[$fieldName] = $moduleLang->$fieldName; if($moduleName == 'program') { $fieldKey = substr($method, 0, 3) . ucfirst($fieldName); if(isset($moduleLang->{$fieldKey}) && is_string($moduleLang->{$fieldKey})) $fields[$fieldName] = $moduleLang->$fieldKey; } if($moduleName == 'execution' && in_array($fieldName, array('desc', 'PM', 'code'))) { $fieldKey = 'exec' . ucfirst($fieldName); $fields[$fieldName] = $this->lang->execution->{$fieldKey}; } } } return $fields; } /** * 获取需求概念。 * Get UR and SR concept. * * @param int $key * @param string $lang * @access public * @return string|false */ public function getURSRConcept($key, $lang = '') { if(empty($lang)) $lang = $this->app->getClientLang(); return $this->dao->select('`value`')->from(TABLE_LANG) ->where('lang')->eq($lang) ->andWhere('module')->eq('custom') ->andWhere('section')->eq('URSRList') ->andWhere('`key`')->eq($key) ->fetch('value'); } /** * 获取需求概念集合。 * Get UR and SR pairs. * * @access public * @return array */ public function getURSRPairs() { $lang = $this->app->getClientLang(); $langData = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG) ->where('lang')->eq($lang) ->andWhere('module')->eq('custom') ->andWhere('section')->eq('URSRList') ->fetchAll(); if(empty($langData)) { $URSR = $this->loadModel('setting')->getURSR(); $langData = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG)->where('`key`')->eq($URSR)->andWhere('module')->eq('custom')->andWhere('section')->eq('URSRList')->fetchAll(); } $URSRPairs = array(); foreach($langData as $content) { $value = json_decode($content->value); if(!$this->config->URAndSR && !$this->config->enableER) $URSRPairs[$content->key] = $value->SRName; if($this->config->URAndSR && !$this->config->enableER) $URSRPairs[$content->key] = $value->URName . '/' . $value->SRName; if($this->config->URAndSR && $this->config->enableER) $URSRPairs[$content->key] = $value->ERName . '/' . $value->URName . '/' . $value->SRName; } return $URSRPairs; } /** * 获取用需概念集合。 * Get UR pairs. * * @access public * @return array */ public function getURPairs() { $URSRList = $this->dao->select('`key`,`value`')->from(TABLE_LANG)->where('module')->eq('custom')->andWhere('section')->eq('URSRList')->andWhere('lang')->eq($this->app->clientLang)->fetchPairs(); if(empty($URSRList)) { $URSR = $this->loadModel('setting')->getURSR(); $URSRList = $this->dao->select('`key`,`value`')->from(TABLE_LANG)->where('module')->eq('custom')->andWhere('section')->eq('URSRList')->andWhere('`key`')->eq($URSR)->fetchPairs(); } $URPairs = array(); foreach($URSRList as $key => $value) { $URSR = json_decode($value); $URPairs[$key] = $URSR->URName; } return $URPairs; } /** * 获取软需概念集合。 * Get SR pairs. * * @access public * @return array */ public function getSRPairs() { $URSRList = $this->dao->select('`key`,`value`')->from(TABLE_LANG)->where('module')->eq('custom')->andWhere('section')->eq('URSRList')->andWhere('lang')->eq($this->app->clientLang)->fetchPairs(); if(empty($URSRList)) { $URSR = $this->loadModel('setting')->getURSR(); $URSRList = $this->dao->select('`key`,`value`')->from(TABLE_LANG)->where('module')->eq('custom')->andWhere('section')->eq('URSRList')->andWhere('`key`')->eq($URSR)->fetchPairs(); } $SRPairs = array(); foreach($URSRList as $key => $value) { $URSR = json_decode($value); $SRPairs[$key] = $URSR->SRName; } return $SRPairs; } /** * 获取需求概念列表。 * Get UR and SR list. * * @access public * @return array */ public function getURSRList() { $this->app->loadLang('custom'); $lang = $this->app->getClientLang(); $URSRDataList = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG) ->where('lang')->eq($lang) ->andWhere('module')->eq('custom') ->andWhere('section')->eq('URSRList') ->fetchAll(); if(empty($URSRDataList)) { $URSR = $this->loadModel('setting')->getURSR(); $URSRDataList = $this->dao->select('`key`, `value`, `system`')->from(TABLE_LANG)->where('`key`')->eq($URSR)->andWhere('module')->eq('custom')->andWhere('section')->eq('URSRList')->fetchAll(); } $URSRList = array(); foreach($URSRDataList as $URSRData) { $value = json_decode($URSRData->value); $URSRList[$URSRData->key] = new stdclass(); $URSRList[$URSRData->key]->key = $URSRData->key; $URSRList[$URSRData->key]->ERName = $value->ERName; $URSRList[$URSRData->key]->SRName = $value->SRName; $URSRList[$URSRData->key]->URName = $value->URName; $URSRList[$URSRData->key]->system = $URSRData->system; } return $URSRList; } /** * 保存表单必填字段设置。 * Save required fields. * * @param string $moduleName product|story|productplan|release|execution|task|bug|testcase|testsuite|testtask|testreport|caselib|doc|user|project|build * @param array $data * @access public * @return void */ public function saveRequiredFields($moduleName, $data) { if(isset($this->config->systemDB->$moduleName)) unset($this->config->systemDB->$moduleName); if(isset($this->config->personalDB->$moduleName)) unset($this->config->personalDB->$moduleName); $this->loadModel($moduleName); $systemFields = $this->getRequiredFields($this->config->$moduleName); $requiredFields = array(); if(!empty($systemFields)) { foreach($systemFields as $method => $fields) { $optionFields = isset($this->config->custom->fieldList[$moduleName][$method]) ? explode(',', $this->config->custom->fieldList[$moduleName][$method]) : array(); $systemFieldList = explode(',', $this->config->$moduleName->$method->requiredFields); foreach($optionFields as $field) { if(in_array($field, $systemFieldList)) { $key = array_search($field, $systemFieldList); unset($systemFieldList[$key]); } } $systemField = implode(',', $systemFieldList); /* Keep the original required fields when the fields is empty. */ if(!isset($data['requiredFields'][$method])) { $requiredFields[$method]['requiredFields'] = $systemField; continue; } $fields = implode(',', $data['requiredFields'][$method]); $systemFields = array_reverse(explode(',', $systemField)); foreach($systemFields as $field) { $field = trim($field); if(strpos(",$fields,", ",$field,") === false) $fields = "{$field},{$fields}"; } $requiredFields[$method]['requiredFields'] = trim($fields, ','); } } $this->loadModel('setting')->setItems("system.{$moduleName}@{$this->config->vision}", $requiredFields); } /** * 设置产品、项目和迭代概念。 * Set product and project and sprint concept. * * @param string $sprintConcept * @access public * @return bool */ public function setConcept($sprintConcept) { $this->loadModel('setting'); $this->setting->setItem('system.custom.sprintConcept', $sprintConcept); $this->setting->setItem('system.custom.productProject', '0_' . $sprintConcept); /* Change block title. */ $oldConfig = isset($this->config->custom->sprintConcept) ? $this->config->custom->sprintConcept : '0'; $newConfig = $sprintConcept; foreach($this->config->executionCommonList as $executionCommonList) { if(!isset($executionCommonList[$oldConfig]) || !isset($executionCommonList[$newConfig])) { $this->setting->setItem('system.custom.sprintConcept', '0'); $this->setting->setItem('system.custom.productProject', '0_0'); continue; } $this->dao->update(TABLE_BLOCK)->set("`title` = REPLACE(`title`, '{$executionCommonList[$oldConfig]}', '{$executionCommonList[$newConfig]}')")->where('dashboard')->eq('execution')->exec(); } return !dao::isError(); } /** * 设置需求概念。 * Set UR and SR concept. * * @param array $data * @access public * @return bool */ public function setURAndSR($data) { $lang = $this->app->getClientLang(); $maxKey = $this->dao->select('max(cast(`key` as DECIMAL)) as maxKey')->from(TABLE_LANG) ->where('section')->eq('URSRList') ->andWhere('module')->eq('custom') ->andWhere('lang')->eq($lang) ->fetch('maxKey'); $maxKey = $maxKey ? (int)$maxKey : 1; /* If has custom UR and SR name. */ foreach($data['SRName'] as $key => $SRName) { $URName = zget($data['URName'], $key, $this->lang->URCommon); $ERName = zget($data['ERName'], $key, $this->lang->ERCommon); if(!$URName || !$SRName || !$ERName) continue; $URSRList = new stdclass(); $URSRList->ERName = $ERName; $URSRList->SRName = $SRName; $URSRList->URName = $URName; $value = json_encode($URSRList); $maxKey += 1; $this->setItem("$lang.custom.URSRList.{$maxKey}.0", $value); } return true; } /** * 编辑需求概念。 * Edit UR and SR concept. * * @param int $key * @param string $lang zh-cn|zh-tw|en|fr|de * @param array $data * @access public * @return bool */ public function updateURAndSR($key = 0, $lang = '', $data = array()) { if(empty($lang)) $lang = $this->app->getClientLang(); if(empty($data['SRName']) || empty($data['URName']) || empty($data['ERName'])) return false; $oldValue = $this->getURSRConcept($key, $lang); $oldValue = json_decode($oldValue); if(!$oldValue) return false; $URSRList = new stdclass(); $URSRList->defaultERName = zget($oldValue, 'defaultERName', $oldValue->ERName); $URSRList->defaultSRName = zget($oldValue, 'defaultSRName', $oldValue->SRName); $URSRList->defaultURName = zget($oldValue, 'defaultURName', $oldValue->URName); $URSRList->ERName = empty($data['ERName']) ? $URSRList->defaultERName : $data['ERName']; $URSRList->SRName = empty($data['SRName']) ? $URSRList->defaultSRName : $data['SRName']; $URSRList->URName = empty($data['URName']) ? $URSRList->defaultURName : $data['URName']; $value = json_encode($URSRList); $this->dao->update(TABLE_LANG)->set('value')->eq($value) ->where('`key`')->eq($key) ->andWhere('section')->eq('URSRList') ->andWhere('lang')->eq($lang) ->andWhere('module')->eq('custom') ->exec(); return true; } /** * 计算启用和不启用的功能。 * Compute the enabled and disabled features. * * @access public * @return array */ public function computeFeatures() { /* Check that the project features are enabled. */ $disabledFeatures = array('program', 'productLine'); foreach($this->config->custom->dataFeatures as $feature) { $function = 'has' . ucfirst($feature) . 'Data'; if(!$this->$function()) { if(in_array($feature, $this->config->custom->projectFeatures)) { if(!isset($disabledFeatures['project'])) $disabledFeatures['project'] = array(); $disabledFeatures['project'][] = $feature; } elseif(in_array($feature, array('waterfall', 'waterfallplus'))) { $disabledFeatures[] = 'project' . ucfirst($feature); } else { $disabledFeatures[] = $feature; } } } if(!isset($disabledFeatures['project'])) $disabledFeatures['project'] = array(); /* Check that the project features are enabled. */ $enabledProjectFeatures = array(); $disabledProjectFeatures = array(); if(in_array($this->config->edition, array('max', 'ipd'))) { foreach($this->config->custom->projectFeatures as $projectFeature) { $label = $this->lang->custom->projectFeatures[$projectFeature]; if(in_array($projectFeature, $disabledFeatures['project'])) { $disabledProjectFeatures[] = $label; } else { $enabledProjectFeatures[] = $label; } } } return array($disabledFeatures, $enabledProjectFeatures, $disabledProjectFeatures); } /** * 处理项目权限为继承项目集的项目权限。 * process project priv within a program set. * * @access public * @return bool */ public function processProjectAcl() { list($projectGroup, $programPM, $stakeholders) = $this->customTao->getDataForUpdateProjectAcl(); $projectIDList = array(); foreach($projectGroup as $projects) $projectIDList = array_merge($projectIDList, array_keys($projects)); $executionGroup = $this->dao->select('project,id')->from(TABLE_EXECUTION)->where('project')->in($projectIDList)->fetchGroup('project', 'id'); $this->loadModel('user'); $this->loadModel('action'); $this->loadModel('personnel'); foreach($projectGroup as $projects) { foreach($projects as $project) { $PM = zget($programPM, $project->parent, ''); $stakeholder = zget($stakeholders, $project->parent, ''); if($stakeholder) $stakeholder = implode(',', array_keys($stakeholder)); $whitelist = rtrim($project->whitelist . ',' . $PM . ',' . $stakeholder); $whitelist = explode(',', $whitelist); $whitelist = array_filter(array_unique($whitelist)); $whitelist = implode(',', $whitelist); $data = new stdclass(); $data->acl = 'private'; $data->whitelist = $whitelist; $this->dao->update(TABLE_PROJECT)->data($data)->where('id')->eq($project->id)->exec(); $whitelist = explode(',', $whitelist); $this->personnel->updateWhitelist($whitelist, 'project', $project->id); $this->user->updateUserView(array($project->id), 'project'); if(zget($executionGroup, $project->id, '')) { $executions = zget($executionGroup, $project->id); $executionPairs = array(); foreach($executions as $executionID => $execution) $executionPairs[$executionID] = $executionID; $this->user->updateUserView($executionPairs, 'sprint'); } $changes = common::createChanges($project, $data); $actionID = $this->action->create('project', $project->id, 'SwitchToLight'); $this->action->logHistory($actionID, $changes); } } return !dao::isError(); } /** * 根据管理模式禁用相关功能。 * Disable related features based on the management mode. * * @param string $mode * @access public * @return void */ public function disableFeaturesByMode($mode) { $disabledFeatures = ''; if($mode == 'light') { /* Check whether the product or project data in the system is empty. */ foreach($this->config->custom->dataFeatures as $feature) { $function = 'has' . ucfirst($feature) . 'Data'; if(!$this->$function()) { if(in_array($feature, $this->config->custom->projectFeatures)) $feature = 'project' . ucfirst($feature); /* If the data is empty, this feature is disabled. */ $disabledFeatures .= "$feature,"; } } $disabledFeatures .= 'productTrack,productRoadmap'; } /* Save the features that are disable to the config. */ $disabledFeatures = rtrim($disabledFeatures, ','); $this->loadModel('setting')->setItem('system.common.disabledFeatures', $disabledFeatures); $URAndSR = strpos(",$disabledFeatures,", ',productUR,') === false ? '1' : '0'; $this->setting->setItem('system.custom.URAndSR', $URAndSR); $disableER = strpos(",$disabledFeatures,", ',productER,') !== false; $enableER = (!$URAndSR || $disableER) ? '0' : '1'; $this->setting->setItem('system.custom.enableER', $enableER); $this->processMeasrecordCron(); } /** * 检查系统中是否有业务需求数据。 * Check whether there is epic data in the system. * * @access public * @return int */ public function hasProductERData() { return (int)$this->dao->select('*')->from(TABLE_STORY)->where('type')->eq('epic')->andWhere('deleted')->eq('0')->count(); } /** * 检查系统中是否有用户需求数据。 * Check whether there is requirement data in the system. * * @access public * @return int */ public function hasProductURData() { return (int)$this->dao->select('*')->from(TABLE_STORY)->where('type')->eq('requirement')->andWhere('deleted')->eq('0')->count(); } /** * 检查系统中是否有瀑布项目数据。 * Check whether there is waterfall project data in the system. * * @access public * @return int */ public function hasWaterfallData() { return (int)$this->dao->select('*')->from(TABLE_PROJECT)->where('model')->eq('waterfall')->andWhere('deleted')->eq('0')->count(); } /** * 检查系统中是否有融合项目数据。 * Check whether there is waterfallplus project data in the system. * * @access public * @return int */ public function hasWaterfallplusData() { return (int)$this->dao->select('*')->from(TABLE_PROJECT)->where('model')->eq('waterfallplus')->andWhere('deleted')->eq('0')->count(); } /** * 检查系统中是否有资产库数据。 * Check whether there is assetlib data in the system. * * @access public * @return int */ public function hasAssetlibData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_ASSETLIB)->where('deleted')->eq(0)->count(); return 0; } /** * 检查系统中是否有问题数据。 * Check whether there is issue data in the system. * * @access public * @return int */ public function hasIssueData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_ISSUE)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有风险数据。 * Check whether there is risk data in the system. * * @access public * @return int */ public function hasRiskData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_RISK)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有培训计划数据。 * Check whether there is gapanalysis data in the system. * * @access public * @return int */ public function hasGapanalysisData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_GAPANALYSIS)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有调研计划数据。 * Check whether there is researchplan data in the system. * * @access public * @return int */ public function hasResearchplanData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_RESEARCHPLAN)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有机会数据。 * Check whether there is opportunity data in the system. * * @access public * @return int */ public function hasOpportunityData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_OPPORTUNITY)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有敏捷项目的会议数据。 * Check whether there is meeting data in the system. * * @access public * @return int */ public function hasMeetingData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_MEETING)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有度量数据。 * Check whether there is measrecord data in the system. * * @access public * @return int */ public function hasMeasrecordData() { return 0; } /** * 检查系统中是否有审计数据。 * Check whether there is auditplan data in the system. * * @access public * @return int */ public function hasAuditplanData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_AUDITPLAN)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有项目活动数据。 * Check whether there is process data in the system. * * @access public * @return int */ public function hasProcessData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_PROGRAMACTIVITY)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有交付物数据。 * Check whether there is deliverable data in the system. * * @access public * @return int */ public function hasDeliverableData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_PROJECTDELIVERABLE)->count(); return 0; } /** * 检查系统中是否有评审数据,有交付物就算有评审。 * Check whether there is review data in the system. * * @access public * @return int */ public function hasReviewData() { if(in_array($this->config->edition, array('max', 'ipd'))) { $reviewCount = (int)$this->dao->select('*')->from(TABLE_REVIEW)->where('deleted')->eq('0')->count(); $deliverableCount = (int)$this->dao->select('*')->from(TABLE_DELIVERABLE)->where('deleted')->eq('0')->count(); return $reviewCount + $deliverableCount; } return 0; } /** * 检查系统中是否有变更管理数据。 * Check whether there is projectchange data in the system. * * @access public * @return int */ public function hasChangeData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_PROJECTCHANGE)->where('deleted')->eq('0')->count(); return 0; } /** * 检查系统中是否有基线数据。 * Check whether there is cm data in the system. * * @access public * @return int */ public function hasCmData() { if(in_array($this->config->edition, array('max', 'ipd'))) return (int)$this->dao->select('*')->from(TABLE_OBJECT)->where('deleted')->eq('0')->andWhere('type')->eq('taged')->count(); return 0; } /** * 处理定时任务。 * Process measrecord cron. * * @access public * @return void */ public function processMeasrecordCron() { $this->loadModel('setting'); $closedFeatures = $this->setting->getItem('owner=system&module=common§ion=&key=closedFeatures'); $disabledFeatures = $this->setting->getItem('owner=system&module=common§ion=&key=disabledFeatures'); $disabledFeatures = $disabledFeatures . ',' . $closedFeatures; $hasProjectMeasrecord = strpos(",{$disabledFeatures},", ',projectMeasrecord,') === false; /* Determine whether the cron is enabled based on whether the feature is disabled. */ $cronStatus = 'normal'; if(!$hasProjectMeasrecord) $cronStatus = 'stop'; /* Update the status of the cron. */ $this->loadModel('cron'); $cron = $this->dao->select('id,status')->from(TABLE_CRON)->where('command')->like('%methodName=initCrontabQueue')->fetch(); if($cron && $cron->status != $cronStatus) $this->cron->changeStatus($cron->id, $cronStatus); $cron = $this->dao->select('id,status')->from(TABLE_CRON)->where('command')->like('%methodName=execCrontabQueue')->fetch(); if($cron && $cron->status != $cronStatus) $this->cron->changeStatus($cron->id, $cronStatus); } }