* @package tree * @version $Id: model.php 5149 2013-07-16 01:47:01Z zhujinyonging@gmail.com $ * @link http://www.zentao.net */ ?> dao->findById((int)$moduleID)->from(TABLE_MODULE)->fetch(); } /** * Get all module pairs with path. * * @access public * @return object */ public function getAllModulePairs($type = 'task') { $modules = $this->dao->select('*')->from(TABLE_MODULE) ->where('type')->eq('story') ->beginIF($type == 'task')->orWhere('type')->eq('task')->fi() ->beginIF($type == 'bug')->orWhere('type')->eq('bug')->fi() ->beginIF($type == 'case')->orWhere('type')->eq('case')->fi() ->andWhere('deleted')->eq('0') ->orderBy('grade asc') ->fetchAll(); $pairs = array(); $pairs[0] = '/'; foreach($modules as $module) { if($module->grade == 1) { $pairs[$module->id] = '/' . $module->name; continue; } $moduleName = '/' . $module->name; $pairs[$module->id] = isset($pairs[$module->parent]) ? $pairs[$module->parent] . $moduleName : $moduleName; } return $pairs; } /** * Build the sql query. * * @param int $rootID * @param string $type * @param int $startModule * @param string|int $branch * @param string $param * @param int $grade * @access public * @return void */ public function buildMenuQuery($rootID, $type, $startModule = 0, $branch = 'all', $param = 'nodeleted', $grade = 0) { /* Set the start module. */ $startModulePath = ''; if($startModule > 0) { $startModule = $this->getById($startModule); if($startModule) $startModulePath = $startModule->path . '%'; } /* If feedback module is merge add story module.*/ $syncConfig = $this->getSyncConfig($type); if(($type == 'feedback' or $type == 'ticket') and strpos($param, 'noproduct') === false and isset($syncConfig[$rootID])) $type = 'story,' . $type; if($this->isMergeModule($rootID, $type)) { return $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->beginIF($type == 'task')->andWhere('type')->eq('task')->fi() ->beginIF($type != 'task')->andWhere('type')->in("story,$type")->fi() ->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi() ->beginIF($branch !== 'all' and $branch !== '' and $branch !== false and strpos($param, 'noMainBranch') === false) ->andWhere("(branch")->eq(0) ->orWhere('branch')->eq($branch) ->markRight(1) ->fi() ->beginIF($branch !== 'all' and $branch !== '' and $branch !== false and strpos($param, 'noMainBranch') !== false) ->andWhere('branch')->eq($branch) ->fi() ->beginIF(strpos($param, 'nodeleted') !== false)->andWhere('deleted')->eq(0)->fi() ->orderBy('grade desc, `order`, type desc') ->get(); } /* $createdVersion < 4.1 or $type == 'story'. */ return $this->dao->select('*')->from(TABLE_MODULE) ->where('1=1') ->beginIF($type != 'feedback' or !empty($rootID))->andwhere('root')->eq((int)$rootID)->fi() ->andWhere('type')->in($type) ->beginIF($grade)->andWhere('grade')->le($grade)->fi() ->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi() ->beginIF($branch !== 'all' and $branch !== '' and $branch !== false and strpos($param, 'noMainBranch') === false) ->andWhere('(branch')->eq(0) ->orWhere('branch')->eq($branch) ->markRight(1) ->fi() ->beginIF($branch !== 'all' and $branch !== '' and $branch !== false and strpos($param, 'noMainBranch') !== false) ->andWhere('branch')->eq($branch) ->fi() ->beginIF(strpos($param, 'nodeleted') !== false)->andWhere('deleted')->eq(0)->fi() ->orderBy('grade desc, `order`') ->get(); } /** * Create an option menu in html. * * @param int $rootID * @param string $type * @param int $startModule * @param int|array $branch * @param string $param * @param string $grade * @access public * @return void */ public function getOptionMenu($rootID, $type = 'story', $startModule = 0, $branch = 0, $param = 'nodeleted', $grade = 'all') { if(empty($branch)) $branch = 0; if(defined('TUTORIAL')) { $modulePairs = $this->loadModel('tutorial')->getModulePairs(); if(!is_array($branch)) return $modulePairs; $modules = array(); foreach($branch as $branchID) $modules[$branchID] = $modulePairs; return $modules; } /* If type of $branch is array, get modules of these branches. */ if(is_array($branch)) { $modules = array(); foreach($branch as $branchID) $modules[$branchID] = $this->getOptionMenu($rootID, $type, $startModule, $branchID, $param); return $modules; } if($type == 'line') $rootID = 0; $branches = array($branch => ''); if($branch != 'all' and strpos('story|bug|case', $type) !== false) { $product = $this->loadModel('product')->getById($rootID); if($product and $product->type != 'normal') { $branchPairs = $this->loadModel('branch')->getPairs($rootID, 'all'); foreach(explode(',', $branch) as $branchID) $branches += array($branchID => $branchPairs[$branchID]); } elseif($product and $product->type == 'normal') { $branches = array(0 => ''); } } /* If feedback or ticket module is merge add story module.*/ $syncConfig = $this->getSyncConfig($type); $treeMenu = array(); foreach($branches as $branchID => $branch) { $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $type, $startModule, $branchID, $param)); $modules = array(); while($module = $stmt->fetch()) { /* If is feedback or ticket filter story module by grade.*/ if(($type == 'feedback' or $type == 'ticket') and $module->type == 'story') { if(isset($syncConfig[$module->root]) and $module->grade > $syncConfig[$module->root]) continue; } if($grade != 'all' and $module->grade > $grade) continue; $modules[$module->id] = $module; } foreach($modules as $module) { $branchName = (isset($product) and $product->type != 'normal' and $module->branch === BRANCH_MAIN) ? $this->lang->branch->main : $branch; $this->buildTreeArray($treeMenu, $modules, $module, (empty($branchName)) ? '/' : "/$branchName/"); } } ksort($treeMenu); $topMenu = @array_shift($treeMenu); $topMenu = explode("\n", trim((string)$topMenu)); $lastMenu[] = '/'; foreach($topMenu as $menu) { if(!strpos($menu, '|')) continue; list($label, $moduleID) = explode('|', $menu); $lastMenu[$moduleID] = $label; } return $lastMenu; } /** * Get module pairs. * * @param int $rootID * @param string $viewType * @param string $showModule * @access public * @return array */ public function getModulePairs($rootID, $viewType = 'story', $showModule = 'end', $extra = '') { if($viewType == 'task') { $products = array_keys($this->loadModel('product')->getProductPairsByProject($rootID)); if(!$this->isMergeModule($rootID, $viewType) or !$products) { $modules = $this->dao->select('id,name,path,short')->from(TABLE_MODULE)->where('root')->eq($rootID)->andWhere('type')->in($viewType)->andWhere('deleted')->eq(0)->fetchAll('id'); } else { $modules = $this->dao->select('id,name,path,short')->from(TABLE_MODULE) ->where("((root = '" . (int)$rootID . "' and type = 'task')") ->orWhere('(root')->in($products)->andWhere('type')->eq('story') ->markRight(2) ->andWhere('deleted')->eq(0) ->fetchAll('id'); } } else { /* When case with libIdList then append lib modules. */ $modules = array(); if($this->isMergeModule($rootID, $viewType) or !$rootID) $viewType .= ',story'; $modules += $this->dao->select('id,name,path,short')->from(TABLE_MODULE) ->where('type')->in($viewType) ->beginIF($rootID)->andWhere('root')->eq($rootID)->fi() ->andWhere('deleted')->eq(0) ->fetchAll('id'); } $modulePairs = array(); foreach($modules as $moduleID => $module) { list($baseModule) = explode(',', trim($module->path, ',')); if($showModule == 'base' and isset($modules[$baseModule])) $module = $modules[$baseModule]; $modulePairs[$moduleID] = $module->short ? $module->short : $module->name; } return $modulePairs; } /** * Create an option menu of task in html. * * @param int $rootID * @param int $startModule * @access public * @return void */ public function getTaskOptionMenu($rootID, $productID = 0, $startModule = 0, $extra = '') { /* If createdVersion <= 4.1, go to getOptionMenu(). */ $products = $this->loadModel('product')->getProductPairsByProject($rootID); $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), 'noclosed'); if(!$this->isMergeModule($rootID, 'task') or !$products) return $this->getOptionMenu($rootID, 'task', $startModule); /* createdVersion > 4.1. */ $startModulePath = ''; if($startModule > 0) { $startModule = $this->getById($startModule); if($startModule) { $startModulePath = $startModule->path . '%'; $modulePaths = explode(",", $startModulePath); $rootModule = $this->getById($modulePaths[0]); $productID = $rootModule->root; } } $treeMenu = array(); $lastMenu[] = '/'; $executionModules = $this->getTaskTreeModules($rootID, true); $noProductModules = $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->andWhere('type')->eq('task') ->andWhere('parent')->eq(0) ->andWhere('deleted')->eq(0) ->orderBy('grade desc, branch, `order`, type') ->fetchPairs('id', 'name'); /* Fix for not in product modules. */ $productNum = count($products); foreach(array('product' => $products, 'noProduct' => $noProductModules) as $type => $rootModules) { foreach($rootModules as $id => $rootModule) { $activeBranch = isset($branchGroups[$id]) ? array_keys($branchGroups[$id]) : array(); if($type == 'product') { $modules = $this->dao->select('*')->from(TABLE_MODULE)->where("((root = '" . (int)$rootID . "' and type = 'task' and parent != 0) OR (root = $id and type = 'story'))") ->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi() ->andWhere('branch')->in($activeBranch)->fi() ->andWhere('deleted')->eq(0) ->orderBy('grade desc, branch, `order`, type') ->fetchAll('id'); } else { $modules = $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->andWhere('type')->eq('task') ->andWhere('path')->like("%,$id,%") ->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi() ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->fetchAll('id'); } foreach($modules as $module) { $parentModules = explode(',', trim($module->path, ',')); if($type == 'product' and isset($noProductModules[$parentModules[0]])) continue; /* Fix bug #2007. */ if($type == 'product' and $module->type == 'task' and !isset($modules[$parentModules[0]])) continue; $rootName = ($productNum > 1 and $type == 'product') ? "/$rootModule/" : '/'; if($type == 'product' and $module->branch and isset($branchGroups[$id][$module->branch])) $rootName .= $branchGroups[$id][$module->branch] . '/'; $this->buildTreeArray($treeMenu, $modules, $module, $rootName); } ksort($treeMenu); $topMenu = @array_shift($treeMenu); $topMenu = explode("\n", trim((string)$topMenu)); foreach($topMenu as $menu) { if(!strpos($menu, '|')) continue; list($label, $moduleID) = explode('|', $menu); if(isset($executionModules[$moduleID]) or strpos($extra, 'allModule') !== false) $lastMenu[$moduleID] = $label; } foreach($topMenu as $moduleID => $moduleName) { if(!isset($executionModules[$moduleID])) unset($treeMenu[$moduleID]); } } } return $lastMenu; } /** * Build tree array. * * @param $&treeMenu * @param array $modules * @param object $module * @param string $moduleName * @access public * @return void */ public function buildTreeArray(& $treeMenu, $modules, $module, $moduleName = '/') { $parentModules = explode(',', $module->path); foreach($parentModules as $parentModuleID) { if(empty($parentModuleID)) continue; if(empty($modules[$parentModuleID])) continue; $moduleName .= $modules[$parentModuleID]->name . '/'; } $moduleName = rtrim($moduleName, '/'); $moduleName .= "|$module->id\n"; if(isset($treeMenu[$module->id]) and !empty($treeMenu[$module->id])) { if(isset($treeMenu[$module->parent])) { $treeMenu[$module->parent] .= $moduleName; } else { $treeMenu[$module->parent] = $moduleName;; } $treeMenu[$module->parent] .= $treeMenu[$module->id]; } else { if(isset($treeMenu[$module->parent]) and !empty($treeMenu[$module->parent])) { $treeMenu[$module->parent] .= $moduleName; } else { $treeMenu[$module->parent] = $moduleName; } } } /** * Get the tree menu in html. * * @param int $rootID * @param string $type * @param int $startModule * @param string $userFunc the function used to create link * @param string $extra extra params * @param string $branch product branch * @param string $extraParams extra params * @access public * @return string */ public function getTreeMenu($rootID, $type = 'root', $startModule = 0, $userFunc = '', $extra = '', $branch = 0, $extraParams = '') { if($type == 'line') $rootID = 0; $this->loadModel('branch'); $projectID = zget($extra, 'projectID', 0); $branches = array($branch => ''); $executionModules = array(); if($branch and empty($projectID)) { $branchName = $this->branch->getById($branch); $branches = array($branch => $branchName); $extra = $userFunc[1] == 'createTestTaskLink' ? $extra : array('rootID' => $rootID, 'branch' => $branch); } $manage = $userFunc[1] == 'createManageLink' ? true : false; $product = $this->loadModel('product')->getById($rootID); $onlyGetLinked = ($projectID and $this->config->vision != 'lite'); if(strpos('story|bug|case', $type) !== false and $branch === 'all' and empty($projectID)) { if($product->type != 'normal') $branches = array(BRANCH_MAIN => $this->lang->branch->main) + $this->loadModel('branch')->getPairs($rootID, 'noempty'); } elseif(strpos(',case,bug,', ",$type,") !== false and $this->app->tab == 'execution') { if($product->type != 'normal' and $projectID) $branches += $this->branch->getPairs($product->id, 'noempty', $projectID); if($onlyGetLinked) $executionModules = $this->getTaskTreeModules($projectID, true, $type, array('branchID' => $branch)); } elseif(($type == 'story' and $this->app->rawModule == 'projectstory') or (strpos(',case,bug,', ",$type,") !== false and $this->app->tab == 'project')) { if($product->type != 'normal' and $projectID) $branches += $this->branch->getPairs($product->id, 'noempty', $projectID); if($onlyGetLinked) $executionModules = $this->getTaskTreeModules($projectID, true, $type, $type == 'story' ? array() : array('branchID' => $branch)); } /* Add for task #1945. check the module has case or no. */ if($type == 'case' and !empty($extra)) $this->loadModel('testtask'); $lastMenu = ''; $treeMenu = array(); $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $type, $startModule, $branch)); while($module = $stmt->fetch()) { if(!$onlyGetLinked) { $this->buildTree($treeMenu, $module, $type, $userFunc, $extra, $branch); } elseif(isset($executionModules[$module->id]) || !empty($product->shadow)) { $this->buildTree($treeMenu, $module, $type, $userFunc, $extra, $branch); } } ksort($treeMenu); $lastMenu .= array_shift($treeMenu); if($lastMenu) $lastMenu = "\n"; return $lastMenu; } /** * Get the tree menu of task in html. * * @param int $rootID * @param int $productID * @param int $startModule * @param string $userFunc * @param string $extra * @access public * @return void */ public function getTaskTreeMenu($rootID, $productID = 0, $startModule = 0, $userFunc = '', $extra = '') { $extra = array('executionID' => $rootID, 'productID' => $productID, 'tip' => true, 'extra' => $extra); /* If createdVersion <= 4.1, go to getTreeMenu(). */ $products = $this->loadModel('product')->getProductPairsByProject($rootID); $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products)); if(!$this->isMergeModule($rootID, 'task') or !$products) { $extra['tip'] = false; return $this->getTreeMenu($rootID, 'task', $startModule, $userFunc, $extra); } /* createdVersion > 4.1. */ $menu = "'; return $menu; } /** * Get full task tree * @param integer $executionID, common value is execution id * @param integer $productID * @access public * @return array */ public function getTaskStructure($rootID, $productID = 0) { $extra = array('executionID' => $rootID, 'productID' => $productID, 'tip' => true); /* If createdVersion <= 4.1, go to getTreeMenu(). */ $products = $this->loadModel('product')->getProductPairsByProject($rootID); $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products)); if(!$this->isMergeModule($rootID, 'task') or !$products) { $extra['tip'] = false; $stmt = $this->dbh->query($this->buildMenuQuery($rootID, 'task', $startModule = 0)); if(empty($products)) $this->config->execution->task->allModule = 1; return $this->getDataStructure($stmt, 'task'); } /* only get linked modules and ignore others. */ $executionModules = $this->getTaskTreeModules($rootID, true); /* Get module according to product. */ $fullTrees = array(); foreach($products as $id => $product) { $productInfo = $this->product->getById($id); /* tree menu. */ $productTree = array(); $branchTrees = array(); if(empty($branchGroups[$id])) $branchGroups[$id]['0'] = ''; foreach($branchGroups[$id] as $branch => $branchName) { $query = $this->dao->select('*')->from(TABLE_MODULE)->where("((root = '" . (int)$rootID . "' and type = 'task' and parent != 0) OR (root = $id and type = 'story' and branch ='$branch'))") ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); $stmt = $this->dbh->query($query); if($branch == 0) $productTree = $this->getDataStructure($stmt, 'task', $executionModules); if($branch != 0) { $children = $this->getDataStructure($stmt, 'task', $executionModules); if($children) $branchTrees[] = array('name' => $branchName, 'root' => $id, 'type' => 'branch', 'actions' => false, 'children' => $children); } } if($branchTrees) $productTree[] = array('name' => $this->lang->product->branchName[$productInfo->type], 'root' => $id, 'type' => 'branch', 'actions' => false, 'children' => $branchTrees); $fullTrees[] = array('name' => $productInfo->name, 'root' => $id, 'type' => 'product', 'actions' => false, 'children' => $productTree); } /* Get execution module. */ $query = $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->andWhere('type')->eq('task') ->andWhere('deleted')->eq(0) ->orderBy('grade desc, `order`, type') ->get(); $stmt = $this->dbh->query($query); $taskTrees = $this->getDataStructure($stmt, 'task', $executionModules); foreach($taskTrees as $taskModule) $fullTrees[] = $taskModule; return $fullTrees; } /** * Get tree structure. * * @param int $rootID * @param string $type * @access public * @return array */ public function getTreeStructure($rootID, $type) { $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $type, $startModule = 0)); return $this->getDataStructure($stmt, $type); } /** * Get the tree menu of bug in html. * * @param int $rootID * @param int $productID * @param int $startModule * @param string $userFunc * @param string $extra * @access public * @return void */ public function getBugTreeMenu($rootID, $productID = 0, $startModule = 0, $userFunc = '', $extra = '') { $extra += array('executionID' => $rootID, 'projectID' => $rootID, 'productID' => $productID, 'tip' => true); $tab = $this->app->tab; /* If createdVersion <= 4.1, go to getTreeMenu(). */ $products = $tab == 'execution' ? $this->loadModel('product')->getProducts($rootID) : $this->loadModel('product')->getProductPairsByProject($rootID); $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products)); /* createdVersion > 4.1. */ $menu = "'; return $menu; } /** * Get the tree menu of case in html. * * @param int $rootID * @param int $productID * @param int $startModule * @param string $userFunc * @param string $extra * @access public * @return void */ public function getCaseTreeMenu($rootID, $productID = 0, $startModule = 0, $userFunc = '', $extra = '') { $extra = array('projectID' => $rootID, 'executionID' => $rootID, 'productID' => $productID, 'tip' => true, 'extra' => $extra); /* If createdVersion <= 4.1, go to getTreeMenu(). */ $products = $this->app->tab != 'execution' ? $this->loadModel('product')->getProductPairsByProject($rootID) : $this->loadModel('product')->getProducts($rootID); $branchGroups = $this->dao->select('t1.product as product,branch,t3.name')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') ->leftJoin(TABLE_BRANCH)->alias('t3')->on('t1.branch=t3.id') ->where('t1.project')->eq($rootID) ->andWhere('t2.type')->ne('normal') ->andWhere('t1.product')->in(array_keys($products)) ->andWhere('t2.deleted')->eq('0') ->fetchGroup('product', 'branch'); $this->app->loadLang('branch'); foreach($branchGroups as $productID => $branches) { $branchGroups[$productID][0] = $this->lang->branch->main; foreach($branches as $branchID => $branchInfo) { $branchGroups[$productID][$branchID] = $branchID == BRANCH_MAIN ? $this->lang->branch->main : $branchInfo->name; } } /* createdVersion > 4.1. */ $menu = "'; return $menu; } /** * Get project story tree menu. * * @param int $rootID * @param int $startModule * @param string $userFunc * @access public * @return string */ public function getProjectStoryTreeMenu($rootID, $startModule = 0, $userFunc = '') { $this->app->loadLang('branch'); if($this->app->rawModule == 'projectstory') $extra['projectID'] = $rootID; if($this->app->rawModule == 'execution') $extra['executionID'] = $rootID; $menu = "'; return $menu; } /** * Build tree. * * @param & $&treeMenu * @param object $module * @param string $type * @param string $userFunc * @param array $extra * @param int $branch * @access public * @return void */ public function buildTree(& $treeMenu, $module, $type, $userFunc, $extra, $branch = 'all') { /* Add for task #1945. check the module has case or no. */ if((isset($extra['rootID']) and isset($extra['branch']) and $branch === 'null') or ($type == 'case' and is_numeric($extra))) { static $objects = array(); if(empty($objects)) { if(is_array($extra)) { $table = $this->config->objectTables[$type]; $objects = $this->dao->select('module')->from($table)->where('product')->eq((int)$extra['rootID'])->andWhere('branch')->eq($extra['branch'])->fetchAll('module'); } else { $objects = $this->dao->select('t1.*,t2.module')->from(TABLE_TESTRUN)->alias('t1') ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') ->where('t1.task')->eq((int)$extra) ->fetchAll('module'); } } static $modules = array(); if(empty($modules)) { $typeCondition = "type='story'"; if($type != 'story') $typeCondition .= " or type='{$type}'"; $modules = $this->dao->select('id,path')->from(TABLE_MODULE)->where('root')->eq($module->root)->andWhere("({$typeCondition})")->fetchPairs('id', 'path'); } $childModules = array(); foreach($modules as $moduleID => $modulePath) { if(strpos($modulePath, $module->path) === 0) $childModules[$moduleID] = $moduleID; } $hasObjects = false; foreach($childModules as $moduleID) { if(isset($objects[$moduleID])) { $hasObjects = true; break; } } if(!$hasObjects) return; } if(is_array($extra)) $extra['branchID'] = $branch; if(empty($extra)) { $extra = array(); $extra['branchID'] = $branch; } $linkHtml = call_user_func($userFunc, $type, $module, $extra); if(isset($treeMenu[$module->id]) and !empty($treeMenu[$module->id])) { if(!isset($treeMenu[$module->parent])) $treeMenu[$module->parent] = ''; $treeMenu[$module->parent] .= "
  • $linkHtml"; $treeMenu[$module->parent] .= "\n"; } else { if(!isset($treeMenu[$module->parent])) $treeMenu[$module->parent] = ""; $treeMenu[$module->parent] .= "
  • $linkHtml\n"; } $treeMenu[$module->parent] .= "
  • \n"; } /** * Get execution modules. * * @param int $executionID * @param bool $parent * @param string $linkObject * @param array $extra * @access public * @return array */ public function getTaskTreeModules($executionID, $parent = false, $linkObject = 'story', $extra = array()) { $executionModules = array(); $field = $parent ? 'path' : 'id'; if($linkObject == 'story') { $table1 = TABLE_PROJECTSTORY; $table2 = TABLE_STORY; } if($linkObject == 'case') { $table1 = TABLE_PROJECTCASE; $table2 = TABLE_CASE; } if($linkObject) { $branch = zget($extra, 'branchID', 0); /* Get object paths of this execution. */ if(strpos(',story,case,', ",$linkObject,") !== false) { $paths = $this->dao->select('DISTINCT t3.' . $field)->from($table1)->alias('t1') ->leftJoin($table2)->alias('t2')->on('t1.' . $linkObject . ' = t2.id') ->leftJoin(TABLE_MODULE)->alias('t3')->on('t2.module = t3.id') ->leftJoin(TABLE_PROJECT)->alias('t4')->on('t1.project = t4.id') ->where('(t1.project')->eq($executionID) ->orWhere('t4.project')->eq($executionID)->markRight(1) ->andWhere('t3.deleted')->eq(0) ->andWhere('t2.deleted')->eq(0) ->beginIF(isset($extra['branchID']) and $branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi() ->fetchPairs(); } elseif($linkObject == 'bug' and strpos(',project,execution,', ",{$this->app->tab},") !== false) { $paths = $this->dao->select('DISTINCT t2.' . $field)->from(TABLE_BUG)->alias('t1') ->leftJoin(TABLE_MODULE)->alias('t2')->on('t1.module = t2.id') ->where('t1.deleted')->eq(0) ->andWhere('t2.deleted')->eq(0) ->beginIF(isset($extra['branchID']) and $branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi() ->andWhere("t1.{$this->app->tab}")->eq($executionID) ->fetchPairs(); } else { return array(); } } else { $productGroups = $this->dao->select('product,branch')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($executionID)->fetchGroup('product', 'branch'); $modules = $this->dao->select('id,root,branch')->from(TABLE_MODULE) ->where('root')->in(array_keys($productGroups)) ->andWhere('type')->eq('story') ->andWhere('deleted')->eq(0) ->fetchAll(); $paths = array(); foreach($modules as $module) { if(empty($module->branch)) $paths[$module->id] = $module->id; if(isset($productGroups[$module->root][0]) or isset($productGroups[$module->root][$module->branch])) $paths[$module->id] = $module->id; } } if(strpos(',case,bug,', ",$linkObject,") === false) { /* Add task paths of this execution.*/ $paths += $this->dao->select($field)->from(TABLE_MODULE) ->where('root')->eq($executionID) ->andWhere('type')->eq('task') ->andWhere('deleted')->eq(0) ->fetchPairs(); /* Add task paths of this execution for has existed. */ $paths += $this->dao->select('DISTINCT t1.' . $field)->from(TABLE_MODULE)->alias('t1') ->leftJoin(TABLE_TASK)->alias('t2')->on('t1.id=t2.module') ->where('t2.module')->ne(0) ->andWhere('t2.execution')->eq($executionID) ->andWhere('t2.deleted')->eq(0) ->andWhere('t1.type')->eq('story') ->andWhere('t1.deleted')->eq(0) ->fetchPairs(); } /* Get all modules from paths. */ foreach($paths as $path) { $modules = explode(',', $path); foreach($modules as $module) $executionModules[$module] = $module; } return $executionModules; } /** * Create link of a story. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createStoryLink($type, $module, $extra = array()) { if(isset($extra['projectID']) and !empty($extra['projectID'])) { $productID = zget($extra, 'productID', 0); $projectID = $extra['projectID']; return html::a(helper::createLink('projectstory', 'story', "projectID=$projectID&productID=$productID&branch=&browseType=byModule¶m={$module->id}&storyType=story"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } elseif(isset($extra['executionID']) and !empty($extra['executionID'])) { $executionID = $extra['executionID']; return html::a(helper::createLink('execution', 'story', "executionID=$executionID&storyType=story&orderBy=order_desc&type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } else { return html::a(helper::createLink('product', 'browse', "root={$module->root}&branch={$extra['branchID']}&type=byModule¶m={$module->id}&storyType=story"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } } /** * Create link of requirement for waterfall. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createRequirementLink($type, $module, $extra = array()) { if(isset($extra['projectID']) and !empty($extra['projectID'])) { $productID = zget($extra, 'productID', 0); $projectID = $extra['projectID']; return html::a(helper::createLink('projectstory', 'story', "projectID=$projectID&productID=$productID&branch=&browseType=byModule¶m={$module->id}&storyType=requirement"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } elseif(isset($extra['executionID']) and !empty($extra['executionID'])) { $executionID = $extra['executionID']; return html::a(helper::createLink('execution', 'story', "executionID=$executionID&storyType=requirement&orderBy=order_desc&type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } else { return html::a(helper::createLink('product', 'browse', "root={$module->root}&branch={$extra['branchID']}&type=byModule¶m={$module->id}&storyType=requirement"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } } /** * Create link of a product line. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createLineLink($type, $module, $extra) { $productID = $extra['productID']; $status = $extra['status']; return html::a(helper::createLink('product', 'all', "productID={$productID}&line={$module->id}&status={$status}"), $module->name, '_self', "id='module{$module->id}'"); } /** * Create link of a task. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createTaskLink($type, $module, $extra) { $executionID = $extra['executionID']; $productID = $extra['productID']; return html::a(helper::createLink('execution', 'task', "executionID={$executionID}&type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of project story. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createProjectStoryLink($type, $module, $extra) { $productID = $extra['productID']; return html::a(helper::createLink('projectstory', 'story', "projectID={$extra['executionID']}&productID=$productID&branch=&browseType=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of a doc. * * @param object $module * @access public * @return string */ public function createDocLink($type, $module, $extra = '') { return html::a(helper::createLink('doc', 'browse', "libID={$module->root}&browseType=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create the manage link of a module. * * @param object $module * @access public * @return string */ public function createManageLink($type, $module, $extra) { $branchID = $extra['branchID']; $tip = strpos('bug,case', $type) === false ? '' : ' [' . strtoupper(substr($type, 0, 1)) . ']'; static $users; if(empty($users)) $users = $this->loadModel('user')->getPairs('noletter'); $linkHtml = $module->name; $linkHtml .= $module->type != 'story' ? $tip : ''; if($type == 'bug' and $module->owner) $linkHtml .= '[' . $users[$module->owner] . ']'; if($type != 'story' and $module->type == 'story') { if(common::hasPriv('tree', 'edit') and $type == 'bug') $linkHtml .= ' ' . html::a(helper::createLink('tree', 'edit', "module={$module->id}&type=$type&branch=$branchID"), $this->lang->tree->edit, '', 'data-toggle="modal" data-type="ajax"'); if(common::hasPriv('tree', 'browse')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'browse', "root={$module->root}&type=$type&module={$module->id}&branch=$branchID"), $this->lang->tree->child); } else { if(common::hasPriv('tree', 'edit')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'edit', "module={$module->id}&type=$type&branch=$branchID"), $this->lang->tree->edit, '', 'data-toggle="modal" data-type="ajax" data-width="500"'); if(common::hasPriv('tree', 'browse') and strpos($this->config->tree->noBrowse, ",$module->type,") === false) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'browse', "root={$module->root}&type=$type&module={$module->id}&branch=$branchID"), $this->lang->tree->child); if(common::hasPriv('tree', 'delete')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'delete', "root={$module->root}&module={$module->id}"), $this->lang->delete, 'hiddenwin'); if(common::hasPriv('tree', 'updateorder')) $linkHtml .= ' ' . html::input("orders[$module->id]", $module->order, 'class="text-center w-40px inline"'); } return $linkHtml; } /** * Create the task manage link of a module. * * @param int $productID * @param int $module * @access public * @return void */ public function createTaskManageLink($type, $module, $extra) { $executionID = $extra['executionID']; $productID = $extra['productID']; $tip = $extra['tip']; $linkHtml = $module->name; $linkHtml .= ($tip and $module->type != 'story') ? ' [T]' : ''; if($module->type == 'story') { if(common::hasPriv('tree', 'browseTask')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'browsetask', "rootID=$executionID&productID=$productID&module={$module->id}"), $this->lang->tree->child); } else { if(common::hasPriv('tree', 'edit')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'edit', "module={$module->id}&type=task"), $this->lang->tree->edit, '', 'data-toggle="modal" data-type="ajax"'); if(common::hasPriv('tree', 'browseTask')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'browsetask', "rootID=$executionID&productID=$productID&module={$module->id}"), $this->lang->tree->child); if(common::hasPriv('tree', 'delete')) $linkHtml .= ' ' . html::a(helper::createLink('tree', 'delete', "root={$module->root}&module={$module->id}"), $this->lang->delete, 'hiddenwin'); if(common::hasPriv('tree', 'updateorder')) $linkHtml .= ' ' . html::input("orders[$module->id]", $module->order, 'style="width:30px;text-align:center"'); } return $linkHtml; } /** * Create link of a bug. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createBugLink($type, $module, $extra = array()) { $moduleName = strpos(',project,execution,', ",{$this->app->tab},") !== false ? $this->app->tab : 'bug'; $methodName = strpos(',project,execution,', ",{$this->app->tab},") !== false ? 'bug' : 'browse'; $param = "root={$module->root}&branch=&type=byModule¶m={$module->id}"; $extra['type'] = (isset($extra['type']) and $extra['type'] != 'bysearch') ? $extra['type'] : 'all'; if($this->app->tab == 'execution') $param = "execuitonID={$extra['projectID']}&productID={$module->root}&branch={$extra['branchID']}&orderBy={$extra['orderBy']}&build={$extra['build']}&type={$extra['type']}¶m={$module->id}"; if($this->app->tab == 'project') $param = "projectID={$extra['projectID']}&productID={$module->root}&branch={$extra['branchID']}&orderBy={$extra['orderBy']}&build={$extra['build']}&type={$extra['type']}¶m={$module->id}"; return html::a(helper::createLink($moduleName, $methodName, $param), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of a test case. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createCaseLink($type, $module, $extra = array()) { $moduleName = strpos(',project,execution,', ",{$this->app->tab},") !== false ? $this->app->tab : 'testcase'; $methodName = strpos(',project,execution,', ",{$this->app->tab},") !== false ? 'testcase' : 'browse'; $param = $this->app->tab == 'project' ? "projectID={$this->session->project}&" : ""; $param = $this->app->tab == 'execution' ? "executionID={$extra['projectID']}&" : $param; return html::a(helper::createLink($moduleName, $methodName, $param . "root={$module->root}&branch={$extra['branchID']}&type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' data-app='{$this->app->tab}' title='{$module->name}'"); } /** * Create link of a test task. * * @param object $module * @access public * @return string */ public function createTestTaskLink($type, $module, $extra) { return html::a(helper::createLink('testtask', 'cases', "taskID=$extra&type=byModule&module={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create case lib link * * @param string $type * @param string $module * @access public * @return string */ public function createCaseLibLink($type, $module) { return html::a(helper::createLink('caselib', 'browse', "root={$module->root}&type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create branch link * * @param string $type * @param int $rootID * @param int $branchID * @param string $branch * @access public * @return string */ public function createBranchLink($type, $rootID, $branchID, $branch) { if($type == 'story') return html::a(helper::createLink('product', 'browse', "productID={$rootID}&branch=$branchID"), $branch, '_self', "id='branch{$branchID}'"); if($type == 'bug') return html::a(helper::createLink('bug', 'browse', "productID={$rootID}&branch=$branchID"), $branch, '_self', "id='branch{$branchID}'"); if($type == 'case') return html::a(helper::createLink('testcase', 'browse', "productID={$rootID}&branch=$branchID"), $branch, '_self', "id='branch{$branchID}'"); } /** * Create link of feedback. * * @param string $type * @param object $module * @access public * @return string */ public function createFeedbackLink($type, $module) { return html::a(helper::createLink('feedback', $this->app->methodName, "type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of ticket. * * @param string $type * @param object $module * @access public * @return string */ public function createTicketLink($type, $module) { return html::a(helper::createLink('ticket', $this->app->methodName, "type=byModule¶m={$module->id}"), $module->name, '_self', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of trainskill. * * @param string $type * @param object $module * @param string $extra * @access public * @return string */ public function createTrainSkillLink($type, $module, $extra = '') { return html::a(helper::createLink('trainskill', 'browse', "type=byModule¶m={$module->id}"), $module->name, '', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of traincourse. * * @param string $type * @param object $module * @param string $extra * @access public * @return string */ public function createTrainCourseLink($type, $module, $extra = '') { return html::a(helper::createLink('traincourse', 'browse', "type=byModule¶m={$module->id}"), $module->name, '', "id='module{$module->id}' title='{$module->name}'"); } /** * Create link of trainpost. * * @param string $type * @param object $module * @param string $extra * @access public * @return string */ public function createTrainPostLink($type, $module, $extra = '') { return html::a(helper::createLink('trainpost', 'browse', "type=byModule¶m={$module->id}"), $module->name, '', "id='module{$module->id}' title='{$module->name}'"); } /** * Create dashboard link. * * @param string $type * @param object $module * @param string $extra * @access public * @return string */ public function createDashboardLink($type, $module, $extra = '') { return html::a(helper::createLink('dashboard', 'browse', "type=bymodule¶m={$module->id}"), $module->name, '', "id='module{$module->id}' title='{$module->name}'"); } /** * Create report link. * * @param string $type * @param object $module * @param array $extra * @access public * @return string */ public function createReportLink($type, $module, $extra) { $dimension = zget($extra, 'dimension', 0); return html::a(helper::createLink('report', 'browsereport', "dimension={$dimension}&module={$module->id}"), $module->name, '', "id='module{$module->id}' title='{$module->name}'"); } /** * Get sons of a module. * * @param int $rootID * @param int $moduleID * @param string $type * @param int $branch * @access public * @return array */ public function getSons($rootID, $moduleID, $type = 'root', $branch = 0) { $syncConfig = $this->getSyncConfig($type); if($type == 'line') $rootID = 0; if(($type == 'feedback' or $type == 'ticket') and isset($syncConfig[$rootID])) $type = "$type,story"; /* if createVersion <= 4.1 or type == 'story', only get modules of its type. */ if(!$this->isMergeModule($rootID, $type) or $type == 'story') { return $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->andWhere('parent')->eq((int)$moduleID) ->andWhere('type')->in($type) ->beginIF($branch !== 'all') ->andWhere("(branch")->eq(0) ->orWhere("branch")->eq((int)$branch) ->markRight(1) ->fi() ->andWhere('deleted')->eq(0) ->orderBy('`order`') ->fetchAll(); } /* else get modules of its type and story type. */ if(strpos('task|case|bug', $type) !== false) $type = "$type,story"; return $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->andWhere('parent')->eq((int)$moduleID) ->andWhere('type')->in($type) ->andWhere() ->markLeft(1) ->where("branch")->eq(0) ->beginIF($branch != 0)->orWhere("branch")->eq((int)$branch)->fi() ->markRight(1) ->andWhere('deleted')->eq(0) ->orderBy('`order`, type desc') ->fetchAll(); } /** * Get sons of a task module. * * @param int $rootID * @param int $productID * @param int $moduleID * @access public * @return void */ public function getTaskSons($rootID, $productID, $moduleID) { if($moduleID) { return $this->dao->select('*')->from(TABLE_MODULE) ->where('root')->eq((int)$rootID) ->andWhere('parent')->eq((int)$moduleID) ->andWhere('type')->in("task,story") ->andWhere('deleted')->eq(0) ->orderBy('`order`, type') ->fetchAll(); } else { return $this->dao->select('*')->from(TABLE_MODULE) ->where('parent')->eq(0) ->andWhere('deleted')->eq(0) ->andWhere("(root = '" . (int)$rootID . "' and type = 'task'") ->orWhere("root = '" . (int)$productID . "' and type = 'story')") ->orderBy('`order`, type') ->fetchAll(); } } /** * Get id list of a module's childs. * * @param int $moduleID * @access public * @return array */ public function getAllChildId($moduleID) { if($moduleID == 0) return array(); $module = $this->getById((int)$moduleID); if(empty($module)) return array(); return $this->dao->select('id')->from(TABLE_MODULE)->where("CONCAT(',', path, ',')")->like("%$module->path%")->andWhere('deleted')->eq(0)->fetchPairs(); } /** * Get project module. * * @param int $projectID * @param int $productID * @access public * @return void */ public function getProjectModule($projectID, $productID = 0) { $modules = array(); $rootModules = $this->dao->select('path')->from(TABLE_MODULE) ->where('root')->eq($productID) ->andWhere('type')->eq('story') ->andWhere('parent')->eq(0) ->andWhere('deleted')->eq(0) ->fetchAll(); foreach($rootModules as $module) { $modules += $this->dao->select('id')->from(TABLE_MODULE) ->where('path')->like($module->path . '%') ->andWhere('deleted')->eq(0) ->fetchPairs(); } return $modules; } /** * Get parents of a module. * * @param int $moduleID * @access public * @return array */ public function getParents($moduleID) { if($moduleID == 0) return array(); $path = $this->dao->select('path')->from(TABLE_MODULE)->where('id')->eq((int)$moduleID)->fetch('path'); $path = trim($path, ','); if(!$path) return array(); return $this->dao->select('*')->from(TABLE_MODULE)->where('id')->in($path)->andWhere('deleted')->eq(0)->orderBy('grade')->fetchAll(); } /** * Get product by moduleID. * * @param int $moduleID * @access public * @return void */ public function getProduct($moduleID) { if($moduleID == 0) return ''; $path = $this->dao->select('path')->from(TABLE_MODULE)->where('id')->eq((int)$moduleID)->fetch('path'); $paths = explode(',', trim($path, ',')); if(!$path) return ''; $moduleID = $paths[0]; $module = $this->dao->select('*')->from(TABLE_MODULE)->where('id')->eq($moduleID)->fetch(); if($module->type != 'story' or !$module->root) return ''; return $this->dao->select('name')->from(TABLE_PRODUCT)->where('id')->eq($module->root)->fetch(); } /** * Get the module that its type == 'story'. * * @param int $moduleID * @access public * @return void */ public function getStoryModule($moduleID) { $module = $this->dao->select('id,type,parent')->from(TABLE_MODULE)->where('id')->eq((int)$moduleID)->fetch(); if(empty($module)) return 0; while(!empty($module) and $module->id and $module->type != 'story') { $module = $this->dao->select('id,type,parent')->from(TABLE_MODULE)->where('id')->eq($module->parent)->fetch(); } return empty($module) ? 0 : $module->id; } /** * Get modules name. * * @param array $moduleIdList * @param bool $allPath * @param bool $branchPath * @access public * @return array */ public function getModulesName($moduleIdList, $allPath = true, $branchPath = false) { if(!$allPath) return $this->dao->select('id, name')->from(TABLE_MODULE)->where('id')->in($moduleIdList)->andWhere('deleted')->eq(0)->fetchPairs('id', 'name'); $modules = $this->dao->select('id, name, path, branch')->from(TABLE_MODULE)->where('id')->in($moduleIdList)->andWhere('deleted')->eq(0)->fetchAll('path'); $allModules = $this->dao->select('id, name')->from(TABLE_MODULE)->where('id')->in(join(array_keys($modules)))->andWhere('deleted')->eq(0)->fetchPairs('id', 'name'); $branchIDList = array(); $modulePairs = array(); foreach($modules as $module) { $paths = explode(',', trim($module->path, ',')); $moduleName = ''; foreach($paths as $path) $moduleName .= '/' . $allModules[$path]; $modulePairs[$module->id] = $moduleName; if($module->branch) $branchIDList[$module->branch] = $module->branch; } if(!$branchPath) return $modulePairs; $branchs = $this->dao->select('id, name')->from(TABLE_BRANCH)->where('id')->in($branchIDList)->andWhere('deleted')->eq(0)->fetchALL('id'); foreach($modules as $module) { if(isset($modulePairs[$module->id])) { $branchName = isset($branchs[$module->branch]) ? '/' . $branchs[$module->branch]->name : ''; $modulePairs[$module->id] = $branchName . $modulePairs[$module->id]; } } return $modulePairs; } /** * Update modules' order. * * @param array $orders * @access public * @return void */ public function updateOrder($orders) { asort($orders); $orderInfo = $this->dao->select('*')->from(TABLE_MODULE)->where('id')->in(array_keys($orders))->andWhere('deleted')->eq(0)->fetchAll('id'); $newOrders = array(); foreach($orders as $moduleID => $order) { $parent = $orderInfo[$moduleID]->parent; $grade = $orderInfo[$moduleID]->grade; $branch = $orderInfo[$moduleID]->branch; if(!isset($newOrders[$parent][$grade][$branch])) { $newOrders[$parent][$grade][$branch] = 1; } else { $newOrders[$parent][$grade][$branch] ++; } $newOrder = $newOrders[$parent][$grade][$branch] * 10; $this->dao->update(TABLE_MODULE)->set('`order`')->eq($newOrder)->where('id')->eq((int)$moduleID)->limit(1)->exec(); } } /** * Manage childs of a module. * * @param int $rootID * @param string $type * @access public * @return array */ public function manageChild($rootID, $type) { if($type == 'line') $rootID = 0; $data = fixer::input('post')->get(); $childs = $data->modules; $parentModuleID = $data->parentModuleID; foreach($childs as $moduleID => $moduleName) { if(preg_match('/(^\s+$)/', $moduleName)) helper::end(js::alert($this->lang->tree->shouldNotBlank)); } $module = new stdClass(); $module->root = $rootID; $module->type = $type; $module->parent = $parentModuleID; $repeatName = $this->checkUnique($module, $childs); if($repeatName) helper::end(js::alert(sprintf($this->lang->tree->repeatName, $repeatName))); $parentModule = $this->getByID($parentModuleID); $branches = isset($data->branch) ? $data->branch : array(); $orders = isset($data->order) ? $data->order : array(); $shorts = $data->shorts; if($parentModule) { $grade = $parentModule->grade + 1; $parentPath = $parentModule->path; } else { $grade = 1; $parentPath = ','; } $i = 1; $oldModules = $this->getOptionMenu($rootID, 'story', 0, 'all'); $createIdList = array(); $editIdList = array(); foreach($childs as $moduleID => $moduleName) { if(empty($moduleName)) continue; /* The new modules. */ if(is_numeric($moduleID)) { if(isset($orders[$moduleID]) and !empty($orders[$moduleID])) { $order = $orders[$moduleID]; } else { $order = $this->post->maxOrder + $i * 10; $i ++; } $module = new stdClass(); $module->root = $rootID; $module->name = strip_tags(trim($moduleName)); $module->parent = $parentModuleID; $module->branch = isset($branches[$moduleID]) ? $branches[$moduleID] : 0; $module->short = $shorts[$moduleID]; $module->grade = $grade; $module->type = $type; $module->order = $order; $this->dao->insert(TABLE_MODULE)->data($module)->exec(); $moduleID = $this->dao->lastInsertID(); $createIdList[] = $moduleID; $childPath = $parentPath . "$moduleID,"; $this->dao->update(TABLE_MODULE)->set('path')->eq($childPath)->where('id')->eq($moduleID)->limit(1)->exec(); } else { $short = $shorts[$moduleID]; $order = $orders[$moduleID]; $moduleID = str_replace('id', '', $moduleID); $oldModule = $this->getByID($moduleID); $data = new stdClass(); $data->name = strip_tags(trim($moduleName)); $data->short = $short; $data->order = $order; $this->setModuleLang(); $this->dao->update(TABLE_MODULE)->data($data)->autoCheck()->where('id')->eq($moduleID)->limit(1)->exec(); $newModule = $this->getByID($moduleID); if(common::createChanges($oldModule, $newModule)) { $editIdList[] = $moduleID; $moduleChanges[$moduleID] = common::createChanges($oldModule, $newModule); } } } if($type == 'story' or strpos($this->config->tree->groupTypes, ",$type,") !== false) { $objectType = $type == 'story' ? 'module' : 'chartgroup'; $this->loadModel('action'); if(!empty($createIdList)) $actionID = $this->action->create($objectType, $rootID, 'created', '', implode(',', $createIdList)); if(!empty($editIdList)) { $changes = array(); $newModules = $this->getOptionMenu($rootID, 'story', 0, 'all'); foreach($moduleChanges as $moduleID => $moduleChange) { foreach($moduleChange as $change) { if($change['field'] == 'name') { $change['old'] = zget($oldModules, $moduleID); $change['new'] = zget($newModules, $moduleID); $change['diff'] = ''; } $changes[] = $change; } } $actionID = $this->action->create($objectType, $rootID, 'edited', '', implode(',', $editIdList)); if(!empty($changes)) $this->action->logHistory($actionID, $changes); } } return $createIdList; } /** * Update a module. * * @param int $moduleID * @access public * @return void */ public function update($moduleID) { $module = fixer::input('post')->get(); $self = $this->getById($moduleID); $changes = common::createChanges($self, $module); if(!isset($_POST['branch'])) $module->branch = $self->branch; $repeatName = $this->checkUnique($self, array("id{$self->id}" => $module->name), array("id{$self->id}" => $module->branch)); if($repeatName) helper::end(js::alert(sprintf($this->lang->tree->repeatName, $repeatName))); $modules = $self->type == 'story' ? $this->getOptionMenu($self->root, 'story', 0, 'all') : ''; $parent = $this->getById($this->post->parent); $childs = $this->getAllChildId($moduleID); $module->name = strip_tags(trim($module->name)); $module->grade = $parent ? $parent->grade + 1 : 1; $module->path = $parent ? $parent->path . $moduleID . ',' : ',' . $moduleID . ','; $this->dao->update(TABLE_MODULE)->data($module)->autoCheck()->check('name', 'notempty')->where('id')->eq($moduleID)->exec(); $this->dao->update(TABLE_MODULE)->set('grade = grade + 1')->where('id')->in($childs)->andWhere('id')->ne($moduleID)->exec(); $this->dao->update(TABLE_MODULE)->set('owner')->eq($this->post->owner)->where('id')->in($childs)->andWhere('owner')->eq('')->exec(); $this->dao->update(TABLE_MODULE)->set('owner')->eq($this->post->owner)->where('id')->in($childs)->andWhere('owner')->eq($self->owner)->exec(); if($self->type == 'story' or strpos($this->config->tree->groupTypes, ",$self->type,") !== false) { $objectType = $self->type == 'story' ? 'module' : 'chartgroup'; $rootID = isset($module->root) ? $module->root : $self->root; $newModules = $this->getOptionMenu($rootID, 'story', 0, 'all'); foreach($changes as $id => $change) { if($change['field'] == 'name') { $changes[$id]['old'] = zget($modules, $moduleID); $changes[$id]['new'] = zget($newModules, $moduleID); $changes[$id]['diff'] = ''; break; } } $actionID = $this->loadModel('action')->create($objectType, $self->root, 'edited', '', $moduleID); if(!empty($changes)) $this->action->logHistory($actionID, $changes); if(isset($module->root) and $module->root != $self->root) { $actionID = $this->action->create($objectType, $rootID, 'edited', '', $moduleID); if(!empty($changes)) $this->action->logHistory($actionID, $changes); } } if(isset($module->root) and $module->root != $self->root) { $this->dao->update(TABLE_MODULE)->set('root')->eq($module->root)->where('id')->in($childs)->exec(); $this->dao->update(TABLE_MODULE)->set('branch')->eq($module->branch)->where('id')->in($childs)->exec(); if($self->type == 'doc') { $this->dao->update(TABLE_DOC)->set('`lib`')->eq($module->root) ->where('module')->eq($moduleID) ->orWhere('module')->in($childs) ->exec(); } } $this->fixModulePath(isset($module->root) ? $module->root : $self->root, $self->type); if(isset($module->root) and $module->root != $self->root) $this->changeRoot($moduleID, $self->root, $module->root, $self->type); } /** * Change root. * * @param int $moduleID * @param int $oldRoot * @param int $newRoot * @param string $type * @access public * @return void */ public function changeRoot($moduleID, $oldRoot, $newRoot, $type) { /* Get all children id list. */ $childIdList = $this->dao->select('id')->from(TABLE_MODULE)->where('path')->like("%,$moduleID,%")->andWhere('deleted')->eq(0)->fetchPairs('id', 'id'); /* Update product field for stories, bugs, cases under this module. */ $this->dao->update(TABLE_STORY)->set('product')->eq($newRoot)->where('module')->in($childIdList)->exec(); $this->dao->update(TABLE_BUG)->set('product')->eq($newRoot)->where('module')->in($childIdList)->exec(); $this->dao->update(TABLE_CASE)->set('product')->eq($newRoot)->where('module')->in($childIdList)->exec(); if($type != 'story') return; /* If the type if story, check it's releated projects. */ $projectStories = $this->dao->select('DISTINCT t1.id,t1.version,t2.project') ->from(TABLE_STORY)->alias('t1') ->leftJoin(TABLE_PROJECTSTORY)->alias('t2')->on('t1.id=t2.story') ->where('t1.module')->in($childIdList) ->andWhere('t2.product')->eq($oldRoot) ->fetchAll('id'); $executions = array(); foreach($projectStories as $story) { $this->dao->update(TABLE_PROJECTSTORY) ->set('product')->eq($newRoot) ->where('project')->eq($story->project) ->andWhere('story')->eq($story->id) ->andWhere('version')->eq($story->version) ->exec(); $executions[$story->project] = $story->project; } if($executions) { $projectProduct = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)->where('project')->in($executions)->fetchGroup('project', 'product'); $linkedProduct = $this->dao->select('DISTINCT project,product')->from(TABLE_PROJECTSTORY)->where('project')->in($executions)->fetchGroup('project', 'product'); foreach($executions as $executionID) { if(!isset($projectProduct[$executionID]) or !in_array($newRoot, array_keys($projectProduct[$executionID]))) $this->dao->insert(TABLE_PROJECTPRODUCT)->set('project')->eq($executionID)->set('product')->eq($newRoot)->exec(); if(isset($linkedProduct[$executionID]) and !in_array($oldRoot, array_keys($linkedProduct[$executionID]))) { $this->dao->delete()->from(TABLE_PROJECTPRODUCT)->where('project')->eq($project)->andWhere('product')->eq($oldRoot)->exec(); $this->dao->update(TABLE_BUILD)->set('product')->eq($newRoot)->where('product')->eq($oldRoot)->andWhere('execution')->eq($executionID)->exec(); } } } } /** * Delete a module. * * @param int $moduleID * @param null $null compatible with that of model::delete() * @access public * @return void */ public function delete($moduleID, $null = null) { $module = $this->getById($moduleID); if(empty($module)) return false; $childs = $this->getAllChildId($moduleID); $childs[$moduleID] = $moduleID; $objectType = (!empty($module->type) and strpos($this->config->tree->groupTypes, ",$module->type,") !== false) ? 'chartgroup' : 'module'; /* Mark deletion when delete a module. */ $this->dao->update(TABLE_MODULE)->set('deleted')->eq(1)->where('id')->in($childs)->exec(); foreach($childs as $childID) { $this->loadModel('action')->create($objectType, $childID, 'deleted', '', $extra = ACTIONMODEL::CAN_UNDELETED); } $this->fixModulePath($module->root, $module->type); $cookieName = ''; switch ($module->type) { case 'line': $this->dao->update(TABLE_PRODUCT)->set('line')->eq('0')->where('line')->eq($moduleID)->exec(); break; case 'task': $this->dao->update(TABLE_TASK)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $cookieName = 'moduleBrowseParam'; break; case 'bug': $this->dao->update(TABLE_BUG)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $cookieName = 'bugModule'; break; case 'case': $this->dao->update(TABLE_CASE)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $cookieName = 'caseModule'; break; case 'story': $this->dao->update(TABLE_STORY)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $this->dao->update(TABLE_TASK)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $this->dao->update(TABLE_BUG)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $this->dao->update(TABLE_CASE)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $this->dao->update(TABLE_FEEDBACK)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $this->dao->update(TABLE_TICKET)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $cookieName = 'storyModule'; break; case 'chart': $this->dao->update(TABLE_CHART)->set('`group`')->eq($module->parent)->where('`group`')->in($childs)->exec(); break; case 'report': $this->dao->update(TABLE_REPORT)->set('`module`')->eq($module->parent)->where('`module`')->in($childs)->exec(); break; case 'dataview': $this->dao->update(TABLE_DATAVIEW)->set('`group`')->eq($module->parent)->where('`group`')->in($childs)->exec(); break; case 'feedback': $this->dao->update(TABLE_FEEDBACK)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $cookieName = 'feedbackModule'; break; case 'ticket': $this->dao->update(TABLE_TICKET)->set('module')->eq($module->parent)->where('module')->in($childs)->exec(); $cookieName = 'ticketModule'; break; } if(strpos($this->session->{$module->type . 'List'}, 'param=' . $moduleID)) $this->session->set($module->type . 'List', str_replace('param=' . $moduleID, 'param=0', $this->session->{$module->type . 'List'})); if($cookieName) setcookie($cookieName, 0, time() - 3600, $this->config->webRoot, '', $this->config->cookieSecure, false); return true; } /** * Fix the path, grade fields according to the id and parent fields. * * @param string $root * @param string $type * @access public * @return void */ public function fixModulePath($root, $type) { /* Get all modules grouped by parent. */ if($type == 'bug' or $type == 'case') $type = 'story,' . $type; $groupModules = $this->dao->select('id, parent, branch')->from(TABLE_MODULE)->where('root')->eq($root)->andWhere('type')->in($type)->andWhere('deleted')->eq(0)->fetchGroup('parent', 'id'); $modules = array(); /* Cycle the groupModules until it has no item any more. */ while(count($groupModules) > 0) { $oldCounts = count($groupModules); // Record the counts before processing. foreach($groupModules as $parentModuleID => $childModules) { /* If the parentModule doesn't exsit in the modules, skip it. If exists, compute it's child modules. */ if(!isset($modules[$parentModuleID]) and $parentModuleID != 0) continue; if($parentModuleID == 0) { $parentModule = new stdclass(); $parentModule->grade = 0; $parentModule->path = ','; } else { $parentModule = $modules[$parentModuleID]; } /* Compute it's child modules. */ foreach($childModules as $childModuleID => $childModule) { $childModule->grade = $parentModule->grade + 1; $childModule->path = $parentModule->path . $childModule->id . ','; if(isset($parentModule->branch))$childModule->branch = $parentModule->branch; $modules[$childModuleID] = $childModule; // Save child module to modules, thus the child of child can compute it's grade and path. } unset($groupModules[$parentModuleID]); // Remove it from the groupModules. } if(count($groupModules) == $oldCounts) break; // If after processing, no module processed, break the cycle. } /* Save modules to database. */ foreach($modules as $module) $this->dao->update(TABLE_MODULE)->data($module)->where('id')->eq($module->id)->limit(1)->exec(); } /** * Check unique module name. * * @param int $rootID * @param string $viewType * @param int $parentModuleID * @param array $modules * @param array $branches * @access public * @return bool */ public function checkUnique($module, $modules = array(), $branches = array()) { if(empty($branches)) $branches = $this->post->branch; if(empty($branches) and isset($module->branch)) $branches = array($module->branch); if(empty($branches)) $branches = array(0); if(empty($modules) and isset($module->name)) $modules = array($module->name); $branches = array_unique($branches); $rootID = $module->root; $viewType = $module->type; $parentModuleID = $module->parent; if($this->isMergeModule($rootID, $viewType) and $viewType != 'task') $viewType .= ',story'; $existsModules = $this->dao->select('id,branch,name')->from(TABLE_MODULE)->where('root')->eq($rootID)->andWhere('type')->in($viewType)->andWhere('parent')->eq($parentModuleID)->andWhere('branch')->in($branches)->andWhere('deleted')->eq(0)->fetchAll(); $checkedModules = ','; $repeatName = ''; foreach($modules as $id => $name) { $existed = false; if(strpos($id, 'id') === 0) { $existed = true; $moduleID = substr($id, 2); } if(strpos($checkedModules, ",$name,") !== false) { $repeatName = $name; break; } foreach($existsModules as $existsModule) { if($name == $existsModule->name and (!$existed or $moduleID != $existsModule->id) and (!isset($branches[$id]) or $branches[$id] == $existsModule->branch)) { $repeatName = $name; break 2; } } $checkedModules .= "$name,"; } if($repeatName) return $repeatName; return false; } /** * Check merge module version. * * @param int $rootID * @param string $viewType * @access public * @return bool */ public function isMergeModule($rootID, $viewType) { if($viewType == 'bug' or $viewType == 'case' or $viewType == 'task') { /* Get createdVersion. */ $table = $viewType == 'task' ? TABLE_PROJECT : TABLE_PRODUCT; $versionField = $viewType == 'task' ? 'openedVersion' : 'createdVersion'; $createdVersion = $this->dao->select($versionField)->from($table)->where('id')->eq($rootID)->fetch($versionField); if($createdVersion) { if(is_numeric($createdVersion[0]) and version_compare($createdVersion, '4.1', '<=')) return false; return true; } } return false; } /** * Get full trees. * * @param int $rootID * @param string $viewType * @param string|int $branchID * @param int $currentModuleID * @access public * @return array */ public function getProductStructure($rootID, $viewType, $branchID = 'all', $currentModuleID = 0) { if($viewType == 'line') $rootID = 0; $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $viewType, $currentModuleID, $branchID)); $trees = $this->getDataStructure($stmt, $viewType); return $trees; } /** * Get full modules tree * @param object $stmt * @param string $viewType * @param array $keepModules * @access public * @return array */ public function getDataStructure($stmt, $viewType, $keepModules = array()) { $parent = array(); /* If feedback or ticket module is merge add story module.*/ $syncConfig = $this->getSyncConfig($viewType); while($module = $stmt->fetch()) { /* If is feedback or ticket filter story module by grade.*/ if(($viewType == 'feedback' or $viewType == 'ticket') and $module->type == 'story') { if(isset($syncConfig[$module->root]) and $module->grade > $syncConfig[$module->root]) continue; } /* Ignore useless module for task. */ $allModule = (isset($this->config->execution->task->allModule) and ($this->config->execution->task->allModule == 1)); if($keepModules and !isset($keepModules[$module->id]) and !$allModule) continue; if($viewType == 'task' and empty($keepModules) and !$allModule) continue; if(isset($parent[$module->id])) { $module->children = $parent[$module->id]->children; unset($parent[$module->id]); } if(!isset($parent[$module->parent])) $parent[$module->parent] = new stdclass(); $parent[$module->parent]->children[] = $module; } if($viewType == 'task') $parentTypePairs = $this->dao->select('*')->from(TABLE_MODULE)->where('id')->in(array_keys($parent))->andWhere('deleted')->eq(0)->fetchPairs('id', 'type'); $tree = array(); foreach($parent as $module) { foreach($module->children as $children) { if($viewType == 'task' and isset($parentTypePairs[$children->parent]) and $parentTypePairs[$children->parent] != 'task') continue; if($children->parent != 0) continue; // Filter project children modules. $tree[] = $children; } } return $tree; } /** * Get all doc structure. * * @access public * @return array */ public function getDocStructure() { $stmt = $this->dbh->query($this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('doc')->andWhere('deleted')->eq(0)->orderBy('`grade`_desc, `order`')->get()); $parent = array(); while($module = $stmt->fetch()) { if(!isset($parent[$module->root])) $parent[$module->root] = array(); if(isset($parent[$module->root][$module->id])) { $module->children = $parent[$module->root][$module->id]->children; unset($parent[$module->root][$module->id]); } if(!isset($parent[$module->root][$module->parent])) $parent[$module->root][$module->parent] = new stdclass(); $parent[$module->root][$module->parent]->children[] = $module; } $tree = array(); foreach($parent as $root => $modules) { foreach($modules as $module) { foreach($module->children as $children) { if($children->parent != 0 && !empty($tree[$root])) { foreach($tree[$root] as $firstChildren) { if($firstChildren->id == $children->parent) $firstChildren->children[] = $children; } } $tree[$root][] = $children; } } } return $tree; } /** * Get syncProduct module config. * * @param string $type feedback|ticket * @access public * @return array */ public function getSyncConfig($type = '') { /* If feedback or ticket module is merge add story module.*/ $syncConfig = json_decode($this->config->global->syncProduct, true); $syncConfig = isset($syncConfig[$type]) ? $syncConfig[$type] : array(); return $syncConfig; } /** * Load module language. * * @access public * @return void */ public function setModuleLang() { $this->lang->module = new stdclass(); $this->lang->module->name = $this->lang->tree->wordName; $this->lang->module->short = $this->lang->tree->short; } }