diff --git a/module/project/model.php b/module/project/model.php index 2bc7f863e9..174ffa69c0 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -1945,34 +1945,113 @@ class projectModel extends model } /** - * Format task list for tree view - * @param array $tasks + * Fill tasks in tree. + * @param object $tree + * @param int $projectID + * @access public + * @return object + */ + public function fillTasksInTree($node, $projectID) + { + $node = (object)$node; + static $storyGroups, $taskGroups; + if(empty($storyGroups)) + { + $stories = $this->dao->select('t2.*, t1.version as taskVersion')->from(TABLE_PROJECTSTORY)->alias('t1') + ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') + ->where('t1.project')->eq((int)$projectID) + ->andWhere('t2.deleted')->eq(0) + ->fetchAll(); + $storyGroups = array(); + foreach($stories as $story) $storyGroups[$story->product][$story->module][$story->id] = $story; + } + if(empty($taskGroups)) + { + $tasks = $this->dao->select('*')->from(TABLE_TASK) + ->where('project')->eq((int)$projectID) + ->andWhere('deleted')->eq(0) + ->fetchAll(); + $taskGroups = array(); + foreach($tasks as $task) $taskGroups[$task->module][$task->story][$task->id] = $task; + } + + if(!empty($node->children)) foreach($node->children as $i => $child) $node->children[$i] = $this->fillTasksInTree($child, $projectID); + + if(!isset($node->id))$node->id = 0; + if($node->type == 'story' or $node->type == 'product') + { + $stories = $storyGroups[$node->root][$node->id]; + foreach($stories as $story) + { + $storyItem = new stdclass(); + $storyItem->type = 'story'; + $storyItem->id = 'story' . $story->id; + $storyItem->title = $story->title; + $storyItem->color = $story->color; + $storyItem->pri = $story->pri; + $storyItem->storyId = $story->id; + $storyItem->url = helper::createLink('story', 'view', "storyID=$story->id&version=$story->version&from=project¶m=$projectID"); + $storyItem->taskCreateUrl = helper::createLink('task', 'batchCreate', "projectID={$projectID}&story={$story->id}"); + + $storyTasks = $taskGroups[$node->id][$story->id]; + if(!empty($storyTasks)) + { + $taskItems = $this->formatTasksForTree($storyTasks, $story); + $storyItem->tasksCount = count($taskItems); + $storyItem->children = array(); + $storyItem->children[] = array('id' => 'tasks' . $story->id, 'tasks' => $taskItems, 'type' => 'tasks', 'actions' => false); + } + + $node->children[] = $storyItem; + } + } + elseif($node->type == 'task') + { + $tasks = $taskGroups[$node->id][0]; + if(!empty($tasks)) + { + $taskItems = $this->formatTasksForTree($tasks); + $node->children[] = array('id' => 'tasks', 'tasks' => $taskItems, 'type' => 'tasks', 'actions' => false); + } + + } + + $node->type = 'module'; + $node->actions = false; + return $node; + } + + /** + * Format tasks for tree. + * + * @param array $tasks + * @param object $story * @access public * @return array */ - public function formatTasksForTree($tasks) + public function formatTasksForTree($tasks, $story = '') { static $users; if(empty($users)) $users = $this->loadModel('user')->getPairs('noletter'); $taskItems = array(); - foreach ($tasks as $task) + foreach($tasks => $task) { $taskItem = new stdclass(); $taskItem->type = 'task'; $taskItem->id = $task->id; $taskItem->title = $task->name; $taskItem->color = $task->color; - $taskItem->pri = (int) $task->pri; + $taskItem->pri = (int)$task->pri; $taskItem->status = $task->status; $taskItem->estimate = $task->estimate; $taskItem->consumed = $task->consumed; $taskItem->left = $task->left; $taskItem->assignedTo = $users[$task->assignedTo]; $taskItem->url = helper::createLink('task', 'view', "task=$task->id"); - $taskItem->storyChanged = $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion; + $taskItem->storyChanged = $story and $story->status == 'active' and $story->version > $story->taskVersion; - $buttons = ''; + $buttons = ''; $buttons .= common::buildIconButton('task', 'assignTo', "projectID=$task->project&taskID=$task->id", $task, 'list', '', '', 'iframe', true); $buttons .= common::buildIconButton('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); @@ -1985,72 +2064,13 @@ class projectModel extends model $buttons .= common::buildIconButton('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); $buttons .= common::buildIconButton('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true); $buttons .= common::buildIconButton('task', 'edit', "taskID=$task->id", '', 'list'); - $taskItem->buttons = $buttons; $taskItems[] = $taskItem; } + return $taskItems; } - /** - * Build product task tree item - * @param object $tree - * @access public - * @return object - */ - public function buildProductTaskTree($node, $projectID) - { - $this->loadModel('story'); - $this->loadModel('task'); - - static $users; - if(empty($users)) $users = $this->loadModel('user')->getPairs('noletter'); - - if($node->children) - { - foreach ($node->children as $child) - { - $child = $this->buildProductTaskTree($child, $projectID); - } - } - - if($node->type === 'product') - { - $node->actions = false; - // TODO: get product root storys and appent to children - } - else if($node->type === 'story') - { - $node->type = 'module'; - $node->actions = false; - $stories = $this->story->getProjectStories($projectID, 'pri_asc,id_desc', 'byModule', $node->id); - foreach ($stories as $story) - { - $storyItem = new stdclass(); - $storyItem->type = 'story'; - $storyItem->id = 'story' . $story->id; - $storyItem->title = $story->title; - $storyItem->color = $story->color; - $storyItem->pri = $story->pri; - $storyItem->storyId = $story->id; - $storyItem->url = helper::createLink('story', 'view', "storyID=$story->id&version=$story->version&from=project¶m=$projectID"); - $storyItem->taskCreateUrl = helper::createLink('task', 'batchCreate', "projectID={$projectID}&story={$story->id}"); - - $storyTasks = $this->task->getStoryTasks($story->id, $projectID); - if(!empty($storyTasks)) - { - $taskItems = $this->formatTasksForTree($storyTasks); - $storyItem->tasksCount = count($taskItems); - $storyItem->children = array(); - $storyItem->children[] = array('id' => 'tasks' . $story->id, 'tasks' => $taskItems, 'type' => 'tasks', 'actions' => false); - } - - $node->children[] = $storyItem; - } - } - return $node; - } - /** * Get projects tree data * @param int $projectID @@ -2059,32 +2079,9 @@ class projectModel extends model */ public function getProjectTree($projectID) { - $tree = array(); - $products = $this->getProducts($projectID); - foreach ($products as $product) - { - $productItem = new stdclass(); - $productItem->type = 'product'; - $productItem->id = 'product' . $product->id; - $productItem->title = $product->name; - $productItem->children = $this->loadModel('tree')->getFullTree($product->id, 'story'); - - $tree[] = $this->buildProductTaskTree($productItem, $projectID); - } - $zeroStoryTasks = $this->loadModel('task')->getStoryTasks(0, $projectID); - if(count($zeroStoryTasks)) - { - $zeroStoryTasks = $this->formatTasksForTree($zeroStoryTasks); - $unlinkStoryItem = new stdclass(); - $unlinkStoryItem->type = 'unlinkStory'; - $unlinkStoryItem->id = 'unlinkStory'; - $unlinkStoryItem->tasksCount = count($zeroStoryTasks); - $unlinkStoryItem->title = $this->lang->project->unlinkStoryTasks; - $unlinkStoryItem->children = array(); - $unlinkStoryItem->actions = false; - $unlinkStoryItem->children[] = array('id' => 'tasks0', 'tasks' => $zeroStoryTasks, 'type' => 'tasks', 'actions' => false); - $tree[] = $unlinkStoryItem; - } - return $tree; + $fullTrees = $this->loadModel('tree')->getFullTaskTree($projectID, 0, false); + array_unshift($fullTrees, array('id' => 0, 'name' => '/', 'type' => 'task', 'actions' => false, 'root' => $projectID)); + foreach($fullTrees as $i => $tree) $fullTrees[$i] = $this->fillTasksInTree($tree, $projectID); + return $fullTrees; } } diff --git a/module/tree/control.php b/module/tree/control.php index f27b55b2cc..3df4ff714b 100644 --- a/module/tree/control.php +++ b/module/tree/control.php @@ -128,20 +128,11 @@ class tree extends control $project = $this->loadModel('project')->getById($rootID); $this->view->root = $project; - $products = $this->project->getProducts($rootID); - $this->view->products = $products; - $this->lang->set('menugroup.tree', 'project'); $this->project->setMenu($this->project->getPairs(), $rootID); $this->lang->tree->menu = $this->lang->project->menu; $this->lang->tree->menuOrder = $this->lang->project->menuOrder; - $projects = $this->project->getPairs(); - unset($projects[$rootID]); - $currentProject = key($projects); - $parentModules = $this->tree->getParents($currentModuleID); - $newModule = (version_compare($project->openedVersion, '4.1', '>') and $products) ? true : false; - $title = $project->name . $this->lang->colon . $this->lang->tree->manageProject; $position[] = html::a($this->createLink('project', 'task', "projectID=$rootID"), $project->name); $position[] = $this->lang->tree->manageProject; @@ -150,12 +141,8 @@ class tree extends control $this->view->position = $position; $this->view->rootID = $rootID; $this->view->productID = $productID; - $this->view->allProject = $projects; - $this->view->newModule = $newModule; - $this->view->currentProject = $currentProject; - $this->view->projectModules = $this->tree->getTaskOptionMenu($currentProject, $productID); $this->view->currentModuleID = $currentModuleID; - $this->view->tree = $this->tree->getFullTaskTree($rootID, $productID, 0, $newModule); + $this->view->tree = $this->tree->getFullTaskTree($rootID, $productID); $this->display(); } diff --git a/module/tree/model.php b/module/tree/model.php index 5ba200d484..d47ca51061 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -456,32 +456,52 @@ class treeModel extends model * @access public * @return object */ - public function getFullTaskTree($projectID, $productID, $moduleID = 0, $newModule = false) + public function getFullTaskTree($rootID, $productID = 0, $manage = true) { - $tree = array_values($this->getTaskSons($projectID, $productID, $moduleID)); - if(!$moduleID) + $extra = array('projectID' => $rootID, 'productID' => $productID, 'tip' => true); + + /* If createdVersion <= 4.1, go to getTreeMenu(). */ + $products = $this->loadModel('product')->getProductsByProject($rootID); + $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products)); + + if(!$this->isMergeModule($rootID, 'task') or !$products) { - $products = $this->loadModel('project')->getProducts($projectID); - if($newModule and $products) - { - $productModulesTree = array(); - foreach ($products as $product) - { - $productModulesTree[] = array('type' => 'product', 'name' => $product->name, 'actions' => false, 'id' => 'product' . $product->id, 'children' => $this->getFullTrees($product->id, 'story')); - } - $tree = array_merge($productModulesTree, $tree); - } + $extra['tip'] = false; + $stmt = $this->dbh->query($this->buildMenuQuery($rootID, 'task')); + return $this->getFullTree($stmt, 'task'); } - if($tree) + + /* if not manage, only get linked modules and ignore others. */ + $projectModules = $manage ? array() : $this->getTaskTreeModules($rootID, true); + + /* Get module according to product. */ + $fullTrees = array(); + foreach($products as $id => $product) { - foreach ($tree as $node) + $productInfo = $this->product->getById($id); + /* tree menu. */ + $productTree = ''; + $branchTrees = ''; + if(empty($branchGroups[$id])) $branchGroups[$id]['0'] = ''; + foreach($branchGroups[$id] as $branch => $branchName) { - if(is_array($node))$node = (object)$node; - $children = $this->getFullTaskTree($projectID, $productID, $node->id); - if(!empty($children)) $node->children = $children; + $query = $this->dao->select('*')->from(TABLE_MODULE)->where("((root = $rootID and type = 'task' and parent != 0) OR (root = $id and type = 'story' and branch ='$branch'))") + ->orderBy('grade desc, type, `order`') + ->get(); + $stmt = $this->dbh->query($query); + if($branch == 0)$productTree = $this->getFullTree($stmt, 'task', $projectModules); + if($branch != 0)$branchTrees[] = array('name' => $branchName, 'root' => $id, 'type' => 'branch', 'actions' => false, 'children' => $this->getFullTree($stmt, 'task', $projectModules)); } + $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); } - return $tree; + + /* Get project module. */ + $query = $this->dao->select('*')->from(TABLE_MODULE)->where("root = $rootID and type = 'task'")->orderBy('grade desc, type, `order`')->get(); + $stmt = $this->dbh->query($query); + $taskTrees = $this->getFullTree($stmt, 'task', $projectModules); + foreach($taskTrees as $taskModule) $fullTrees[] = $taskModule; + return $fullTrees; } /** @@ -1464,14 +1484,19 @@ class treeModel extends model $fullTrees = array(); if(isset($branches[0])) { - $fullTrees = $this->getFullTree($rootID, $viewType, 0, $currentModuleID); + $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $viewType, $currentModuleID, 'null')); + $fullTrees = $this->getFullTree($stmt, $viewType); unset($branches[0]); } if($branches) { $branchTrees = array(); - foreach($branches as $branchID => $branch) $branchTrees[] = array('name' => $branch, 'type' => 'branch', 'actions' => false, 'children' => $this->getFullTree($rootID, $viewType, $branchID, $currentModuleID)); - $fullTrees[] = array('name' => $this->lang->product->branchName[$product->type], 'type' => 'branch', 'actions' => false, 'children' => $branchTrees); + foreach($branches as $branchID => $branch) + { + $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $viewType, $currentModuleID, $branchID)); + $branchTrees[] = array('name' => $branch, 'root' => $rootID, 'type' => 'branch', 'actions' => false, 'children' => $this->getFullTree($stmt, $viewType)); + } + $fullTrees[] = array('name' => $this->lang->product->branchName[$product->type], 'root' => $rootID, 'type' => 'branch', 'actions' => false, 'children' => $branchTrees); } return $fullTrees; } @@ -1485,17 +1510,32 @@ class treeModel extends model * @access public * @return array */ - public function getFullTree($rootID, $viewType, $branch = 0, $currentModuleID = 0) + public function getFullTree($stmt, $viewType, $keepModules = array()) { - $tree = array_values($this->getSons($rootID, $currentModuleID, $viewType, $branch)); - if($tree) + $parent = array(); + while($module = $stmt->fetch()) { - foreach($tree as $node) + /* Ignore useless module for task. */ + if($keepModules and !isset($keepModules[$module->id])) continue; + if(isset($parent[$module->id])) { - $children = $this->getFullTree($rootID, $viewType, $branch, $node->id); - if($children) $node->children = $children; + $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))->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; + $tree[] = $children; } } - return $tree; + return $tree; } }