app->loadLang('workflow'); $this->app->loadConfig('workflow'); if(!empty($this->config->workflow->buildin)) { foreach($this->config->workflow->buildin->modules as $appModules) { if(isset($appModules->$flowModule)) { $buildin = true; break; } } } if(!$buildin) return $searchConfig; $groupID = $this->loadModel('workflowgroup')->getGroupIDBySession(); $fields = $this->loadModel('workflowfield')->getList($flowModule, 'searchOrder, `order`, id', $groupID); $maxCount = $this->config->maxCount; $this->config->maxCount = 0; $fieldValues = array(); $formName = $module . 'Form'; if($this->session->$formName) { foreach($this->session->$formName as $formField) { $field = zget($formField, 'field', ''); $value = zget($formField, 'value', ''); if(empty($field)) continue; if($value) $fieldValues[$field][$value] = $value; } } foreach($fields as $field) { if($field->canSearch == 0 || $field->buildin) continue; if(in_array($field->control, $this->config->workflowfield->optionControls)) { $field->options = $this->workflowfield->getFieldOptions($field, true, zget($fieldValues, $field->field, ''), '', $this->config->flowLimit); } $searchConfig['fields'][$field->field] = $field->name; $searchConfig['params'][$field->field] = $this->loadModel('flow', 'sys')->processSearchParams($field->control, $field->options); } $this->config->maxCount = $maxCount; return $searchConfig; } /** * 初始化搜索表单,并且保存到 session。 * Init the search session for the first time search. * * @param string $module * @param array $fields * @param array $fieldParams * @access public * @return array */ public function initSession($module, $fields, $fieldParams) { $formSessionName = $module . 'Form'; $queryForm = array(); for($i = 1; $i <= $this->config->search->groupItems * 2; $i ++) { $currentField = key($fields); $currentParams = zget($fieldParams, $currentField, array()); $operator = zget($currentParams, 'operator', '='); $queryForm[] = array('field' => $currentField, 'andOr' => 'and', 'operator' => $operator, 'value' => ''); if(!next($fields)) reset($fields); } $queryForm[] = array('groupAndOr' => 'and'); $this->session->set($formSessionName, $queryForm); return $queryForm; } /** * Init the old search session for the first time search. * * @param string $module * @param array $fields * @param array $fieldParams * @access public * @return void */ public function initOldSession($module, $fields, $fieldParams) { $formSessionName = $module . 'Form'; if(isset($_SESSION[$formSessionName]) and $_SESSION[$formSessionName] != false) return; for($i = 1; $i <= $this->config->search->groupItems * 2; $i ++) { /* Var names. */ $fieldName = "field$i"; $andOrName = "andOr$i"; $operatorName = "operator$i"; $valueName = "value$i"; $currentField = key($fields); $operator = isset($fieldParams[$currentField]['operator']) ? $fieldParams[$currentField]['operator'] : '='; $queryForm[$fieldName] = key($fields); $queryForm[$andOrName] = 'and'; $queryForm[$operatorName] = $operator; $queryForm[$valueName] = ''; if(!next($fields)) reset($fields); } $queryForm['groupAndOr'] = 'and'; $this->session->set($formSessionName, $queryForm); } /** * 处理查询表单的相关数据。 * Process query form datas. * * @param array $fieldParams * @param string $field * @param string $andOrName * @param string $operatorName * @param string $valueName * @access public * @return array */ public function processQueryFormDatas($fieldParams, $field, $andOrName, $operatorName, $valueName) { /* 设置分组之间的逻辑关系。*/ /* Set and or. */ $andOr = strtoupper($this->post->$andOrName); if($andOr != 'AND' && $andOr != 'OR') $andOr = 'AND'; /* 设置操作符。*/ /* Set operator. */ $operator = $this->post->$operatorName; if(!isset($this->lang->search->operators[$operator])) $operator = '='; /* 设置字段的值。*/ /* Skip empty values. */ $value = $this->post->$valueName; if($value == 'ZERO') $this->post->set($valueName, 0); // ZERO is special, stands to 0. if(isset($fieldParams[$field]) && $fieldParams[$field]['control'] == 'select' && $value === 'null') $this->post->set($valueName, ''); // Null is special, stands to empty if control is select. $value = addcslashes(trim((string)$this->post->{$valueName}), '%'); return array($andOr, $operator, $value); } /** * 根据字段,操作符和值设置查询条件。 * Set condition by field, operator and value. * * @param string $field * @param string $operator * @param string $value * @param string $control * @access protected * @return string */ protected function setCondition($field, $operator, $value, $control = '') { /* 替换特殊字符。 */ if(is_string($value)) $value = htmlspecialchars($value, ENT_QUOTES); $condition = ''; if($operator == 'include') { if($field == 'module') { $allModules = $this->loadModel('tree')->getAllChildId($value); if($allModules) $condition = helper::dbIN($allModules); } else { $condition = $control == 'select' ? " LIKE CONCAT('%,', '{$value}', ',%')" : ' LIKE ' . $this->dbh->quote("%$value%"); } } elseif($operator == "notinclude") { if($field == 'module') { $allModules = $this->loadModel('tree')->getAllChildId($value); if($allModules) { if(count($allModules) > 1) $condition = " NOT " . helper::dbIN($allModules); else $condition = " !" . helper::dbIN($allModules); } } else { $condition = $control == 'select' ? " NOT LIKE CONCAT('%,', '{$value}', ',%')" : ' NOT LIKE ' . $this->dbh->quote("%$value%"); } } elseif($operator == 'belong') { if($field == 'module') { $allModules = $this->loadModel('tree')->getAllChildId((int)$value); if($allModules) $condition = helper::dbIN($allModules); } elseif($field == 'dept') { $allDepts = $this->loadModel('dept')->getAllChildId((int)$value); $condition = helper::dbIN($allDepts); } elseif($field == 'scene') { $allScenes = $value === '0' ? array() : ($value === '' ? array(0) : $this->loadModel('testcase')->getAllChildId((int)$value)); if(count($allScenes)) $condition = helper::dbIN($allScenes); } else { $condition = ' = ' . $this->dbh->quote($value) . ' '; } } else { if($operator == 'between' and !isset($this->config->search->dynamic[$value])) $operator = '='; $condition = $operator . ' ' . $this->dbh->quote($value) . ' '; if($operator == '=' and $field == 'id' and preg_match('/^[0-9]+(,[0-9]*)+$/', $value) and !preg_match('/[\x7f-\xff]+/', $value)) { $values = array_filter(explode(',', trim($this->dbh->quote($value), "'"))); foreach($values as $value) $value = "'" . $value . "'"; $value = implode(',', $values); $operator = 'IN'; $condition = $operator . ' (' . $value . ') '; } } return $condition; } /** * 设置 where 查询条件。 * Set where condition. * * @param string $where * @param string $field * @param string $operator * @param string $value * @param string $andOr * @param string $control * @access public * @return string */ public function setWhere($where, $field, $operator, $value, $andOr, $control = '') { $condition = $this->setCondition($field, $operator, $value, $control); if($operator == '=' && preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value)) { $condition = '`' . $field . "` >= '$value' AND `" . $field . "` <= '$value 23:59:59'"; $where .= " $andOr ($condition)"; } elseif($operator == '!=' && preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value)) { $condition = '`' . $field . "` < '$value' OR `" . $field . "` > '$value 23:59:59'"; $where .= " $andOr ($condition)"; } elseif($operator == '<=' and preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value)) { $where .= " $andOr " . '`' . $field . "` <= '$value 23:59:59'"; } elseif($operator == '>' and preg_match('/^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/', $value)) { $where .= " $andOr " . '`' . $field . "` > '$value 23:59:59'"; } elseif(in_array($operator, array('include', 'notinclude')) && $control == 'select') { $where .= " $andOr CONCAT(',', `{$field}`, ',') {$condition}"; } elseif($condition) { $where .= " $andOr " . '`' . $field . '` ' . $condition; } return $where; } /** * 替换日期和用户变量。 * Replace dynamic account and date. * * @param string $query * @access public * @return string */ public function replaceDynamic($query) { $this->app->loadClass('date'); $lastWeek = date::getLastWeek(); $thisWeek = date::getThisWeek(); $lastMonth = date::getLastMonth(); $thisMonth = date::getThisMonth(); $yesterday = date::yesterday(); $today = date(DT_DATE1); if(strpos($query, '$') !== false) { $query = str_replace('$@me', $this->app->user->account, $query); $query = str_replace("'\$lastMonth'", "'" . $lastMonth['begin'] . "' and '" . $lastMonth['end'] . "'", $query); $query = str_replace("'\$thisMonth'", "'" . $thisMonth['begin'] . "' and '" . $thisMonth['end'] . "'", $query); $query = str_replace("'\$lastWeek'", "'" . $lastWeek['begin'] . "' and '" . $lastWeek['end'] . "'", $query); $query = str_replace("'\$thisWeek'", "'" . $thisWeek['begin'] . "' and '" . $thisWeek['end'] . "'", $query); $query = str_replace("'\$yesterday'", "'" . $yesterday . ' 00:00:00' . "' and '" . $yesterday . ' 23:59:59' . "'", $query); $query = str_replace("'\$today'", "'" . $today . ' 00:00:00' . "' and '" . $today . ' 23:59:59' . "'", $query); } return $query; } /** * 如果搜索框的选项是users,products, executions, 获取相对应的列表。 * Get user, product and execution value of the param. * * @param string $module * @param array $fields * @param array $params * @access protected * @return array */ protected function getParamValues($module, $fields, $params) { $hasProduct = false; $hasExecution = false; $hasUser = false; foreach($fields as $fieldName) { if(empty($params[$fieldName])) continue; if($params[$fieldName]['values'] == 'products') $hasProduct = true; if($params[$fieldName]['values'] == 'users') $hasUser = true; if($params[$fieldName]['values'] == 'executions') $hasExecution = true; } /* 将用户的值追加到获取到的用户列表。*/ $appendUsers = array(); $formSessionName = $module . 'Form'; if(isset($_SESSION[$formSessionName])) { for($i = 1; $i <= $this->config->search->groupItems; $i ++) { if(!isset($_SESSION[$formSessionName][$i - 1])) continue; $fieldName = $_SESSION[$formSessionName][$i - 1]['field']; if(isset($params[$fieldName]) and $params[$fieldName]['values'] == 'users') { if($_SESSION[$formSessionName][$i - 1]['value']) $appendUsers[] = $_SESSION[$formSessionName][$i - 1]['value']; } } } $users = $products = $executions = array(); if($hasUser) { $users = $this->loadModel('user')->getPairs('realname|noclosed', $appendUsers); $users['$@me'] = $this->lang->search->me; } if($hasProduct) $products = $this->loadModel('product')->getPairs('', $this->session->project); if($hasExecution) $executions = $this->loadModel('execution')->getPairs($this->session->project); return array($users, $products, $executions); } /** * 获取 sql 语句的参数。 * Get list sql params. * * @param string $keywords * @access protected * @return array */ protected function getSqlParams($keywords) { $spliter = $this->app->loadClass('spliter'); $words = explode(' ', $this->unify($keywords, ' ')); $against = ''; $againstCond = array(); foreach($words as $word) { /* 将 utf-8 字符串拆分为单词,为每个单词计算 unicode. */ $splitedWords = $spliter->utf8Split($word); $trimmedWord = trim($splitedWords['words']); $against .= '"' . $trimmedWord . '" '; if(in_array($this->config->db->driver, $this->config->pgsqlDriverList)) { $splitWords = preg_split('/\s+/', trim($trimmedWord)); $quotedWords = array_map(function($word) { return "''" . $word . "''"; }, $splitWords); $againstCond[] = implode(' <-> ', $quotedWords); } else { $againstCond[] = '(+"' . $trimmedWord . '")'; } if(is_numeric($word) && strpos($word, '.') === false && strlen($word) == 5) $againstCond .= "(-\" $word \") "; } if(in_array($this->config->db->driver, $this->config->pgsqlDriverList)) { $againstCond = implode(' | ', $againstCond); } else { $againstCond = implode(' ', $againstCond); } $likeCondition = trim($keywords) ? 'OR title LIKE ' . $this->dbh->quote("%{$keywords}%") . ' OR content LIKE ' . $this->dbh->quote("%{$keywords}%") : ''; $words = str_replace('"', '', $against); $words = str_pad($words, 5, '_'); return array($words, $againstCond, $likeCondition); } /** * 获取允许的模块。 * Get allowed objects. * * @param string|array $type * @access protected * @return array */ protected function getAllowedObjects($type) { $allowedObjects = array(); if($type != 'all') { foreach($type as $module) $allowedObjects[] = $module; return $allowedObjects; } if($this->config->systemMode == 'light') unset($this->config->search->fields->program); foreach($this->config->search->fields as $objectType => $fields) { $module = $objectType; $method = 'view'; if($module == 'case') $module = 'testcase'; if($module == 'feedback') $method = 'adminView'; if(common::hasPriv($module, $method)) $allowedObjects[] = $objectType; if($module == 'deploystep' && common::haspriv('deploy', 'viewstep')) $allowedobjects[] = $objectType; } return $allowedObjects; } /** * 检查产品的权限。 * Check product prividge. * * @param array $results * @param array $objectIdList * @param string $products * @access private * @return array */ private function checkProductPriv($results, $objectIdList, $products) { $shadowProducts = $this->dao->select('id')->from(TABLE_PRODUCT)->where('shadow')->eq(1)->fetchPairs('id'); foreach($objectIdList as $productID => $recordID) { if(strpos(",$products,", ",$productID,") === false) unset($results[$recordID]); if(in_array($productID, $shadowProducts)) unset($results[$recordID]); } return $results; } /** * 检查项目集的权限。 * Check program priviledge. * * @param array $results * @param array $objectIdList * @access private * @return array */ private function checkProgramPriv($results, $objectIdList) { $programs = $this->app->user->view->programs; foreach($objectIdList as $programID => $recordID) { if(strpos(",$programs,", ",$programID,") === false) unset($results[$recordID]); } return $results; } /** * 检查项目的权限。 * Check project priviledge. * * @param array $results * @param array $objectIdList * @access private * @return array */ private function checkProjectPriv($results, $objectIdList) { $projects = $this->app->user->view->projects; foreach($objectIdList as $projectID => $recordID) { if(strpos(",$projects,", ",$projectID,") === false) unset($results[$recordID]); } return $results; } /** * 检查执行的权限。 * Check execution priviledge. * * @param array $results * @param array $objectIdList * @param string $executions * @access private * @return array */ private function checkExecutionPriv($results, $objectIdList, $executions) { foreach($objectIdList as $executionID => $recordID) { if(strpos(",$executions,", ",$executionID,") === false) unset($results[$recordID]); } return $results; } /** * 检查文档的权限。 * Check doc priviledge. * * @param array $results * @param array $objectIdList * @param string $table * @access private * @return array */ private function checkDocPriv($results, $objectIdList, $table) { $this->loadModel('doc'); $objectDocs = $this->dao->select('*')->from($table)->where('id')->in(array_keys($objectIdList))->andWhere('deleted')->eq('0')->fetchAll('id'); $privLibs = array(); foreach($objectIdList as $docID => $recordID) { if(!isset($objectDocs[$docID]) || !$this->doc->checkPrivDoc($objectDocs[$docID])) { unset($results[$recordID]); continue; } $objectDoc = $objectDocs[$docID]; $privLibs[$objectDoc->lib] = $objectDoc->lib; } $libs = $this->doc->getLibs('all'); $objectDocLibs = $this->dao->select('id')->from(TABLE_DOCLIB)->where('id')->in($privLibs)->andWhere('id')->in(array_keys($libs))->andWhere('deleted')->eq('0')->fetchPairs(); foreach($objectDocs as $docID => $doc) { if(!isset($objectDocLibs[$doc->lib])) { $recordID = $objectIdList[$docID]; unset($results[$recordID]); } } return $results; } /** * 检查待办的权限。 * Check todo priviledge. * * @param array $results * @param array $objectIdList * @param string $table * @access private * @return array */ private function checkTodoPriv($results, $objectIdList, $table) { $objectTodos = $this->dao->select('id')->from($table)->where('id')->in(array_keys($objectIdList))->andWhere("private")->eq(1)->andWhere('account')->ne($this->app->user->account)->fetchPairs(); foreach($objectTodos as $todoID) { if(isset($objectIdList[$todoID])) { $recordID = $objectIdList[$todoID]; unset($results[$recordID]); } } return $results; } /** * 检查套件的权限。 * Check testsuite Priviledge. * * @param array $results * @param array $objectIdList * @param string $table * @access private * @return array */ private function checkTestsuitePriv($results, $objectIdList, $table) { $objectSuites = $this->dao->select('id')->from($table)->where('id')->in(array_keys($objectIdList))->andWhere('type')->eq('private')->andWhere('deleted')->eq('0')->fetchPairs(); foreach($objectSuites as $suiteID) { if(isset($objectIdList[$suiteID])) { $recordID = $objectIdList[$suiteID]; unset($results[$recordID]); } } return $results; } /** * 检查反馈和工单的权限。 * Check feedback and ticket priviledge. * * @param string $objectType * @param array $results * @param array $objectIdList * @param string $table * @access private * @return array */ private function checkFeedbackAndTicketPriv($objectType, $results, $objectIdList, $table) { $grantProducts = $this->loadModel('feedback')->getGrantProducts(); $objects = $this->dao->select('*')->from($table)->where('id')->in(array_keys($objectIdList))->fetchAll('id'); foreach($objects as $objectID => $object) { if($objectType == 'feedback' && $object->openedBy == $this->app->user->account) continue; if(isset($grantProducts[$object->product])) continue; if(isset($objectIdList[$objectID])) { $recordID = $objectIdList[$objectID]; unset($results[$recordID]); } } return $results; } /** * 检查搜索到的模块的权限。 * Check the priviledge of the object. * * @param string $objectType * @param string $table * @param array $results * @param array $objectIdList * @param string $products * @param string $executions * @access private * @return array */ private function checkObjectPriv($objectType, $table, $results, $objectIdList, $products, $executions) { if($objectType == 'product') return $this->checkProductPriv($results, $objectIdList, $products); if($objectType == 'program') return $this->checkProgramPriv($results, $objectIdList); if($objectType == 'project') return $this->checkProjectPriv($results, $objectIdList); if($objectType == 'execution') return $this->checkExecutionPriv($results, $objectIdList, $executions); if($objectType == 'doc') return $this->checkDocPriv($results, $objectIdList, $table); if($objectType == 'todo') return $this->checkTodoPriv($results, $objectIdList, $table); if($objectType == 'testsuite') return $this->checkTestsuitePriv($results, $objectIdList, $table); if(strpos(',feedback,ticket,', ",$objectType,") !== false) return $this->checkFeedbackAndTicketPriv($objectType, $results, $objectIdList, $table); return $results; } /** * 检查各个模块所属的产品或者执行的权限。 * Check related object priviledge. * * @param string $objectType * @param string $table * @param array $results * @param array $objectIdList * @param string $products * @param string $executions * @access private * @return array */ private function checkRelatedObjectPriv($objectType, $table, $results, $objectIdList, $products, $executions) { $objectProducts = array(); $objectExecutions = array(); if(strpos(',bug,case,testcase,productplan,release,story,testtask,', ",$objectType,") !== false) { $objectProducts = $this->dao->select('id, product')->from($table)->where('id')->in(array_keys($objectIdList))->fetchGroup('product', 'id'); } elseif(strpos(',build,task,testreport,', ",$objectType,") !== false) { $objectExecutions = $this->dao->select('id, execution')->from($table)->where('id')->in(array_keys($objectIdList))->fetchGroup('execution', 'id'); } elseif($objectType == 'effort') { $efforts = $this->dao->select('id, product, execution')->from($table)->where('id')->in(array_keys($objectIdList))->fetchAll(); foreach($efforts as $effort) { $objectExecutions[$effort->execution][$effort->id] = $effort; $effortProducts = explode(',', trim($effort->product, ',')); foreach($effortProducts as $effortProduct) $objectProducts[$effortProduct][$effort->id] = $effort; } } foreach($objectProducts as $productID => $idList) { if(empty($productID)) continue; if(strpos(",$products,", ",$productID,") === false) { foreach($idList as $object) { $recordID = $objectIdList[$object->id]; unset($results[$recordID]); } } } foreach($objectExecutions as $executionID => $idList) { if(empty($executionID)) continue; if(strpos(",$executions,", ",$executionID,") === false) { foreach($idList as $object) { $recordID = $objectIdList[$object->id]; unset($results[$recordID]); } } } return $results; } /** * 将搜索到的结果分页。 * Set results in pages. * * @param array $results * @param object $pager * @access protected * @return array */ protected function setResultsInPage($results, $pager) { $pager->setRecTotal(count($results)); $pager->setPageTotal(); $pager->setPageID($pager->pageID); $results = array_chunk($results, $pager->recPerPage, true); return $results[$pager->pageID - 1]; } /** * 获取搜索到的结果对应的内容。 * Get object list. * * @param array $idListGroup * @access protected * @return array */ protected function getObjectList($idListGroup) { $objectList = array(); foreach($idListGroup as $module => $idList) { if(!isset($this->config->objectTables[$module])) continue; $fields = ''; if($module == 'issue') $fields = ($this->config->edition == 'max' || $this->config->edition == 'ipd') ? 'id,project,owner,lib' : 'id,project,owner'; if($module == 'project') $fields = 'id,model'; if($module == 'execution') $fields = 'id,type,project'; if(in_array($module, array('story', 'requirement', 'epic'))) $fields = ($this->config->edition == 'max' || $this->config->edition == 'ipd') ? 'id,type,lib' : 'id,type'; if(($module == 'risk' || $module == 'opportunity') && ($this->config->edition == 'max' || $this->config->edition == 'ipd')) $fields = 'id,lib'; if($module == 'doc' && ($this->config->edition == 'max' || $this->config->edition == 'ipd')) $fields = 'id,assetLib,assetLibType'; if(empty($fields)) continue; $table = $this->config->objectTables[$module]; $objectList[$module] = $this->dao->select($fields)->from($table)->where('id')->in($idList)->filterTpl('skip')->fetchAll('id'); } return $objectList; } /** * 检查查询到的结果的权限。 * Check product and project priv. * * @param array $results * @param array $objectPairs * @access protected * @return array */ protected function checkPriv($results, $objectPairs = array()) { if($this->app->user->admin) return $results; $products = $this->app->user->view->products; $executions = $this->app->user->view->sprints; if(empty($objectPairs)) foreach($results as $record) $objectPairs[$record->objectType][$record->objectID] = $record->id; foreach($objectPairs as $objectType => $objectIdList) { if(!isset($this->config->objectTables[$objectType])) continue; $table = $this->config->objectTables[$objectType]; $results = $this->checkObjectPriv($objectType, $table, $results, $objectIdList, $products, $executions); $results = $this->checkRelatedObjectPriv($objectType, $table, $results, $objectIdList, $products, $executions); } return $results; } /** * 处理搜索结果。 * Process results. * * @param array $results * @param array $objectList * @param string $words * @access protected * @return array */ protected function processResults($results, $objectList, $words) { foreach($results as $record) { $record->title = str_replace(' ', '', $this->decode($this->markKeywords($record->title, $words))); $record->title = str_replace('_', '', $record->title); $record->summary = str_replace(' ', '', $this->getSummary($record->content, $words)); $record->summary = str_replace('_', '', $record->summary); $record = $this->processRecord($record, $objectList); } return $results; } /** * 将 unicode 转换为对应的字。 * Transfer unicode to words. * * @param string $string * @access private * @return string */ private function decode($string) { static $dict; if(empty($dict)) { $dict = $this->dao->select("concat(`key`, ' ') AS `key`, value")->from(TABLE_SEARCHDICT)->fetchPairs(); $dict['|'] = ''; } if(strpos($string, ' ') === false) return zget($dict, $string . ' '); return trim(str_replace(array_keys($dict), array_values($dict), $string . ' ')); } /** * 获取结果显示的摘要。 * Get summary of results. * * @param string $content * @param string $words * @access private * @return string */ private function getSummary($content, $words) { $length = $this->config->search->summaryLength; if(strlen($content) <= $length) return $this->decode($this->markKeywords($content, $words)); $content = $this->markKeywords($content, $words); preg_match_all("/\.*\<\/span\>/U", $content, $matches); if(empty($matches[0])) return $this->decode($this->markKeywords(substr($content, 0, $length), $words)); $matches = $matches[0]; $score = 0; $needle = ''; foreach($matches as $matched) { if(strlen($matched) > $score) { $content = str_replace($needle, strip_tags($needle), $content); $needle = $matched; $score = strlen($matched); } } $content = str_replace('decode($summary); } /** * 在文中标记关键词。 * Mark keywords in content. * * @param string $content * @param string $keywords * @access private * @return string */ private function markKeywords($content, $keywords) { $words = explode(' ', trim($keywords, ' ')); $leftMark = '|0000'; $rightMark = '0000|'; $markedWords = array(); foreach($words as $key => $word) { if(preg_match('/^\|[0-9]+\|$/', $word)) { $words[$key] = trim($word, '|'); } elseif(is_numeric($word)) { $words[$key] = $word . ' '; } else { $words[$key] = strlen($word) == 5 ? str_replace('_', '', $word) : $word; } $markedWords[] = $leftMark . $this->decode($word) . $rightMark; } $content = str_replace($words, $markedWords, $content . ' '); $content = str_replace(array($leftMark, $rightMark), array("", " "), $content); $content = str_replace(" ", '', $content); $content = str_replace("", '', $content); return $content; } /** * 处理搜索的记录。 * Process record. * * @param object $record * @param array $objectList * @access private * @return object */ private function processRecord($record, $objectList) { $module = $record->objectType == 'case' ? 'testcase' : $record->objectType; $method = 'view'; if($module == 'deploystep') { $module = 'deploy'; $method = 'viewstep'; } $linkProjectModules = ',task,bug,testcase,build,release,testtask,testsuite,testreport,trainplan,'; if(strpos($linkProjectModules, ",$module,") !== false && !isset($this->config->objectTables[$record->objectType])) return $record; if($module == 'issue') return $this->processIssueRecord($record, $objectList); if($module == 'project') return $this->processProjectRecord($record, $objectList); if($module == 'execution') return $this->processExecutionRecord($record, $objectList); if($module == 'task') return $this->processTaskRecord($record); if(in_array($module, array('story', 'requirement', 'epic'))) return $this->processStoryRecord($record, $module, $objectList); if(($module == 'risk' || $module == 'opportunity') && ($this->config->edition == 'max' || $this->config->edition == 'ipd')) return $this->processRiskRecord($record, $module, $objectList); if($module == 'doc' && ($this->config->edition == 'max' || $this->config->edition == 'ipd')) return $this->processDocRecord($record, $objectList); $record->url = helper::createLink($module, $method, "id={$record->objectID}"); return $record; } /** * 处理搜索到的问题记录的链接和类型。 * Process issue record url and extra type. * * @param object $record * @param array $objectList * @access private * @return object */ private function processIssueRecord($record, $objectList) { $issue = $objectList['issue'][$record->objectID]; $module = !empty($issue->lib) ? 'assetlib' : 'issue'; $method = !empty($issue->lib) ? 'issueView' : 'view'; $record->url = helper::createLink($module, $method, "id={$record->objectID}", '', false, $issue->project); $record->extraType = empty($issue->owner) ? 'commonIssue' : 'stakeholderIssue'; return $record; } /** * 处理搜索到的项目的链接。 * processProjectRecord * * @param object $record * @param array $objectList * @access private * @return object */ private function processProjectRecord($record, $objectList) { $projectModel = $objectList['project'][$record->objectID]->model; $method = $projectModel == 'kanban' ? 'index' : 'view'; $record->url = helper::createLink('project', $method, "id={$record->objectID}"); return $record; } /** * 处理执行记录的链接和类型。 * Process execution record url and extra type. * * @param object $record * @param array $objectList * @access private * @return object */ private function processExecutionRecord($record, $objectList) { $execution = $objectList['execution'][$record->objectID]; $method = $execution->type == 'kanban' ? 'kanban' : 'view'; $record->url = helper::createLink('execution', $method, "id={$record->objectID}"); $record->extraType = empty($execution->type) ? '' : $execution->type; return $record; } /** * 处理执行记录的链接和类型。 * Process task record url and extra type. * * @param object $record * @access private * @return object */ private function processTaskRecord($record) { $task = $this->dao->findById($record->objectID)->from(TABLE_TASK)->fetch(); $project = $this->dao->findById($task->project)->from(TABLE_PROJECT)->fetch(); $record->url = helper::createLink('task', 'view', "id={$record->objectID}"); if(!$project->multiple) $record->dataApp = 'project'; return $record; } /** * 处理需求记录的链接。 * Process story record url. * * @param object $record * @param string $module * @param array $objectList * @access private * @return object */ private function processStoryRecord($record, $module, $objectList) { $story = zget($objectList[$module], $record->objectID, null); if(empty($story)) { $record->url = ''; return $record; } $module = 'story'; $method = 'storyView'; if(!empty($story->lib)) { $module = 'assetlib'; $method = 'storyView'; } $record->url = helper::createLink($module, $method, "id={$record->objectID}", '', false, 0, true); if($this->config->vision == 'lite') $record->url = helper::createLink('projectstory', $method, "storyID={$record->objectID}", '', false, 0, true); $record->extraType = zget($story, 'type', ''); return $record; } /** * 处理文档记录的链接。 * Process doc record url. * * @param object $record * @param array $objectList * @access private * @return object */ private function processDocRecord($record, $objectList) { $doc = $objectList['doc'][$record->objectID]; $module = 'doc'; $method = 'view'; if(!empty($doc->assetLib)) { $module = 'assetlib'; $method = $doc->assetLibType == 'practice' ? 'practiceView' : 'componentView'; } $record->url = helper::createLink($module, $method, "id={$record->objectID}", '', false, 0, true); return $record; } /** * 处理风险记录的链接。 * Process risk record url. * * @param object $record * @param string $module * @param array $objectList * @access private * @return object */ private function processRiskRecord($record, $module, $objectList) { $object = $objectList[$module][$record->objectID]; $method = 'view'; if(!empty($object->lib)) { $method = $module == 'risk' ? 'riskView' : 'opportunityView'; $module = 'assetlib'; } $record->url = helper::createLink($module, $method, "id={$record->objectID}", '', false, 0, true); return $record; } /** * Process data list for saving index. * 处理要保存的索引数据。 * * @param string $module * @param object $field * @param array $dataList * @access protected * @return array */ protected function processDataList($module, $field, $dataList) { if($module == 'case') $caseStep = $this->dao->select('*')->from(TABLE_CASESTEP)->where('`case`')->in(array_keys($dataList))->fetchGroup('case', 'id'); $actions = $this->dao->select('*')->from(TABLE_ACTION)->where('objectType')->eq($module)->andWhere('objectID')->in(array_keys($dataList))->orderBy('date asc')->fetchGroup('objectID', 'id'); $files = $this->dao->select('id,objectID,title,extension')->from(TABLE_FILE)->where('objectType')->eq($module)->andWhere('objectID')->in(array_keys($dataList))->orderBy('id asc')->fetchGroup('objectID', 'id'); foreach($dataList as $id => $data) { $data->comment = ''; if(isset($actions[$id])) { foreach($actions[$id] as $action) { if($action->action == 'opened') $data->{$field->addedDate} = $action->date; $data->{$field->editedDate} = $action->date; if(!empty($action->comment)) $data->comment .= $action->comment . "\n"; } } if(isset($files[$id])) { foreach($files[$id] as $file) { if(!empty($file->title)) $data->comment .= $file->title . '.' . $file->extension . "\n"; } } if($module == 'case') { $data->desc = ''; $data->expect = ''; if(isset($caseStep[$id])) { foreach($caseStep[$id] as $step) { if($step->version != $data->version) continue; $data->desc .= $step->desc . "\n"; $data->expect .= $step->expect . "\n"; } } } } return $dataList; } /** * 将特殊符号替换成统一的符号。 * Unified processing of search keywords. * * @param string $string * @param string $to * @access private * @return string */ private static function unify($string, $to = ',') { $labels = array('_', '、', ' ', '-', '\n', '?', '@', '&', '%', '~', '`', '+', '*', '/', '\\', '。', ','); $string = str_replace($labels, $to, $string); return preg_replace("/[{$to}]+/", $to, trim($string, $to)); } /** * 将文档的内容追加到文档的索引备注中。 * Append document content to the document. * * @param object $object * @access protected * @return object */ protected function appendFiles($object) { $docFiles = $this->dao->select('files')->from(TABLE_DOCCONTENT)->where('doc')->eq($object->id)->orderBy('version')->limit(1)->fetch('files'); if(empty($docFiles)) return $object; if(!isset($object->comment)) $object->comment = ''; $allDocFiles = $this->loadModel('file')->getByObject('doc', $object->id); foreach($allDocFiles as $file) { if(strpos(",$docFiles,", ",{$file->id},") === false) continue; if(strpos('docx|doc', $file->extension) !== false) { $convertedFile = $this->file->convertOffice($file, 'txt'); if($convertedFile) $object->comment .= substr(file_get_contents($convertedFile), 0, $this->config->search->maxFileSize); } if($file->extension == 'txt' && file_exists($file->realPath)) $object->comment .= substr(file_get_contents($file->realPath), 0, $this->config->search->maxFileSize); } $object->comment = htmlspecialchars($object->comment); return $object; } }