* @package product * @version $Id: model.php 5118 2013-07-12 07:41:41Z chencongzhi520@gmail.com $ * @link http://www.zentao.net */ ?> app->getModuleName(); $currentMethod = $this->app->getMethodName(); /* init currentModule and currentMethod for report and story. */ if($currentModule == 'story') { $storyMethods = ",track,create,batchcreate,batchclose,"; if(strpos($storyMethods, "," . $currentMethod . ",") === false) $currentModule = 'product'; if($currentMethod == 'view' || $currentMethod == 'change' || $currentMethod == 'review') $currentMethod = 'browse'; } if($currentMethod == 'report') $currentMethod = 'browse'; $selectHtml = $this->select($products, $product->id, $currentModule, $currentMethod, $extra, $branch, $module, $moduleType); $pageNav = ''; $isMobile = $this->app->viewType == 'mhtml'; if($isMobile) { $pageNav = html::a(helper::createLink('product', 'index'), $this->lang->product->index) . $this->lang->colon; $pageNav .= $selectHtml; } else { $pageNav = $selectHtml; } return $pageNav; } /** * Create the select code of products. * * @param array $products * @param int $productID * @param string $currentModule * @param string $currentMethod * @param string $extra * * @access public * @return string */ public function select($products, $productID, $currentModule, $currentMethod, $extra = '', $branch = 0, $module = 0, $moduleType = '') { $isProject = ($this->app->openApp == 'project' and strpos('bug,testcase,testtask', $this->app->rawMethod) !== false and isset($products[0])) ? true : false; $this->app->loadLang('product'); if(!$isProject and !$productID) { unset($this->lang->product->menu->settings['subMenu']->branch); return; } $isMobile = $this->app->viewType == 'mhtml'; setCookie("lastProduct", $productID, $this->config->cookieLife, $this->config->webRoot, '', false, true); if($productID) $currentProduct = $this->getById($productID); if($isProject) $extra = $this->session->project; if($isProject and !$productID) { $currentProduct = new stdclass(); $currentProduct->name = $products[$productID]; $currentProduct->type = 'normal'; } $this->session->set('currentProductType', $currentProduct->type); $output = ''; if(!empty($products)) { $dropMenuLink = helper::createLink($isProject ? 'bug' : 'product', 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra"); $output = "
"; if($isMobile) $output = "{$currentProduct->name} "; if($currentProduct->type == 'normal') unset($this->lang->product->menu->settings['subMenu']->branch); if($currentProduct->type != 'normal' && $currentModule != 'programplan') { $this->lang->product->branch = $this->lang->product->branchName[$currentProduct->type]; $this->lang->product->menu->settings['subMenu']->branch = str_replace('@branch@', $this->lang->product->branch, $this->lang->product->menu->settings['subMenu']->branch); $branches = $this->loadModel('branch')->getPairs($productID); $branchName = isset($branches[$branch]) ? $branches[$branch] : $branches[0]; if(!$isMobile) { $dropMenuLink = helper::createLink('branch', 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra"); $output .= "
"; } else { $output .= "{$branchName} "; } } if(!$isMobile) $output .= '
'; } return $output; } /** * Save the product id user last visited to session. * * @param int $productID * @param array $products * @access public * @return int */ public function saveState($productID, $products) { if($productID > 0) $this->session->set('product', (int)$productID); if($productID == 0 and $this->cookie->lastProduct) $this->session->set('product', (int)$this->cookie->lastProduct); if($productID == 0 and $this->session->product == '') $this->session->set('product', key($products)); if(!isset($products[$this->session->product])) { $this->session->set('product', key($products)); if($productID && strpos(",{$this->app->user->view->products},", ",{$this->session->product},") === false) $this->accessDenied(); } if($this->cookie->preProductID != $productID) { $this->cookie->set('preBranch', 0); setcookie('preBranch', 0, $this->config->cookieLife, $this->config->webRoot, '', false, true); } return $this->session->product; } /** * Check privilege. * * @param int $product * @access public * @return bool */ public function checkPriv($productID) { if(empty($productID)) return false; /* Is admin? */ if($this->app->user->admin) return true; return (strpos(",{$this->app->user->view->products},", ",{$productID},") !== false); } /** * Show accessDenied response. * * @access private * @return void */ public function accessDenied() { echo(js::alert($this->lang->product->accessDenied)); if(!$this->server->http_referer) die(js::locate(helper::createLink('product', 'index'))); $loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login"; if(strpos($this->server->http_referer, $loginLink) !== false) die(js::locate(helper::createLink('product', 'index'))); die(js::locate('back')); } /** * Get product by id. * * @param int $productID * @access public * @return object */ public function getById($productID) { if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProduct(); $product = $this->dao->findById($productID)->from(TABLE_PRODUCT)->fetch(); if(!$product) return false; return $this->loadModel('file')->replaceImgURL($product, 'desc'); } /** * Get by idList. * * @param array $productIDList * @access public * @return array */ public function getByIdList($productIDList) { return $this->dao->select('*')->from(TABLE_PRODUCT)->where('id')->in($productIDList)->fetchAll('id'); } /** * Get products. * * @param int $programID * @param string $status * @param int $limit * @param int $line * @param string $orderBy * @access public * @return array */ public function getList($programID = 0, $status = 'all', $limit = 0, $line = 0) { return $this->dao->select('t1.id as id,t1.*')->from(TABLE_PRODUCT)->alias('t1') ->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id') ->where('t1.deleted')->eq(0) ->beginIF($programID)->andWhere('t1.program')->eq($programID)->fi() ->beginIF($line > 0)->andWhere('t1.line')->eq($line)->fi() ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->products)->fi() ->beginIF($status == 'noclosed')->andWhere('t1.status')->ne('closed')->fi() ->beginIF($status != 'all' and $status != 'noclosed' and $status != 'involved')->andWhere('t1.status')->in($status)->fi() ->beginIF($status == 'involved') ->andWhere('t1.PO', true)->eq($this->app->user->account) ->orWhere('t1.QD')->eq($this->app->user->account) ->orWhere('t1.RD')->eq($this->app->user->account) ->orWhere('t1.createdBy')->eq($this->app->user->account) ->markRight(1) ->fi() ->orderBy('t2.order_asc, t1.line_desc, t1.order_asc') ->beginIF($limit > 0)->limit($limit)->fi() ->fetchAll('id'); } /** * Get product pairs. * * @param string $mode * @param string $programID * @return array */ public function getPairs($mode = '', $programID = 0) { if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getProductPairs(); $orderBy = !empty($this->config->product->orderBy) ? $this->config->product->orderBy : 'isClosed'; $products = $this->dao->select('*, IF(INSTR(" closed", status) < 2, 0, 1) AS isClosed') ->from(TABLE_PRODUCT) ->where('deleted')->eq(0) ->beginIF($programID)->andWhere('program')->eq($programID)->fi() ->beginIF(strpos($mode, 'noclosed') !== false)->andWhere('status')->ne('closed')->fi() ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi() ->orderBy($orderBy) ->fetchPairs('id', 'name'); return $products; } /** * Get product pairs by project. * * @param int $projectID * @param int $status all|noclosed * @access public * @return array */ public function getProductPairsByProject($projectID = 0, $status = 'all') { $products = empty($projectID) ? $this->getList() : $this->getProducts($projectID, $status); $pairs = array(); if(!empty($products)) { foreach($products as $product) $pairs[$product->id] = $product->name; } return $pairs; } /** * Get products by project. * * @param int $projectID * @param int $status all|noclosed * @param string $orderBy * @access public * @return array */ public function getProducts($projectID = 0, $status = 'all', $orderBy = '') { return $this->dao->select('t1.branch, t1.plan, t2.*') ->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2') ->on('t1.product = t2.id') ->where('t2.deleted')->eq(0) ->beginIF(!empty($projectID))->andWhere('t1.project')->eq($projectID)->fi() ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->products)->fi() ->beginIF(strpos($status, 'noclosed') !== false)->andWhere('status')->ne('closed')->fi() ->orderBy($orderBy . 't2.order asc') ->fetchAll('id'); } /** * Get product id by project. * * @param int $projectID * @param bool $isFirst * @access public * @return array */ public function getProductIDByProject($projectID, $isFirst = true) { $products = $this->dao->select('product')->from(TABLE_PROJECTPRODUCT) ->where('product')->in($this->app->user->view->products) ->beginIF($projectID)->andWhere('project')->eq($projectID)->fi() ->fetchPairs(); if($isFirst === false) return $products; return empty($products) ? 0 : current($products); } /** * Get grouped products. * * @access public * @return void */ public function getStatusGroups() { $products = $this->dao->select('id, name, status')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetchGroup('status'); } /** * Get ordered products. * * @param string $status * @param int $num * @access public * @return array */ public function getOrderedProducts($status, $num = 0, $projectID = 0) { $products = array(); if($projectID) { $pairs = $this->getProducts($projectID); $products = $this->getByIdList(array_keys($pairs)); } else { $products = $this->getList('', $status, $num); } if(empty($products)) return $products; $lines = $this->getLinePairs(); $productList = array(); foreach($lines as $id => $name) { foreach($products as $key => $product) { if($product->line == $id) { $product->name = $name . '/' . $product->name; $productList[] = $product; unset($products[$key]); } } } $productList = array_merge($productList, $products); $products = $mineProducts = $otherProducts = $closedProducts = array(); foreach($productList as $product) { if(!$this->app->user->admin and !$this->checkPriv($product->id)) continue; if($product->status == 'normal' and $product->PO == $this->app->user->account) { $mineProducts[$product->id] = $product; } elseif($product->status == 'normal' and $product->PO != $this->app->user->account) { $otherProducts[$product->id] = $product; } elseif($product->status == 'closed') { $closedProducts[$product->id] = $product; } } $products = $mineProducts + $otherProducts + $closedProducts; if(empty($num)) return $products; return array_slice($products, 0, $num, true); } /* * Get product switcher. * * @param int $productID * @param string $extra * @param int $branch * @access public * @return void */ public function getSwitcher($productID = 0, $extra = '', $branch = 0) { $currentModule = $this->app->moduleName; $currentMethod = $this->app->methodName; /* init currentModule and currentMethod for report and story. */ if($currentModule == 'story') { $storyMethods = ",track,create,batchcreate,batchclose,zerocase,"; if(strpos($storyMethods, "," . $currentMethod . ",") === false) $currentModule = 'product'; if($currentMethod == 'view' || $currentMethod == 'change' || $currentMethod == 'review') $currentMethod = 'browse'; } if($currentMethod == 'report') $currentMethod = 'browse'; $this->app->loadLang('project'); $currentProductName = $this->lang->product->common; if($productID) { $currentProduct = $this->getById($productID); $currentProductName = $currentProduct->name; $this->session->set('currentProductType', $currentProduct->type); } $fromModule = $this->lang->navGroup->qa == 'qa' ? 'qa' : ''; $dropMenuLink = helper::createLink($this->app->openApp == 'qa' ? 'product' : $this->app->openApp, 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra&from=$fromModule"); $output = "
"; if(isset($currentProduct->type) && $currentProduct->type != 'normal') { $this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$currentProduct->type]); $branches = $this->loadModel('branch')->getPairs($productID); $branch = empty($branch) ? (int)$this->cookie->preBranch : $branch; $branchName = isset($branches[$branch]) ? $branches[$branch] : $branches[0]; $dropMenuLink = helper::createLink('branch', 'ajaxGetDropMenu', "objectID=$productID&module=$currentModule&method=$currentMethod&extra=$extra"); $output .= "
"; } return $output; } /** * Create a product. * * @access public * @return int */ public function create() { $product = fixer::input('post') ->setDefault('status', 'normal') ->setDefault('line', 0) ->setDefault('createdBy', $this->app->user->account) ->setDefault('createdDate', helper::now()) ->setDefault('createdVersion', $this->config->version) ->setIF($this->post->acl == 'open', 'whitelist', '') ->stripTags($this->config->product->editor->create['id'], $this->config->allowedTags) ->join('whitelist', ',') ->remove('uid') ->get(); $product = $this->loadModel('file')->processImgURL($product, $this->config->product->editor->create['id'], $this->post->uid); $this->dao->insert(TABLE_PRODUCT)->data($product)->autoCheck() ->batchCheck($this->config->product->create->requiredFields, 'notempty') ->checkIF(!empty($product->name), 'name', 'unique') ->checkIF(!empty($product->name), 'code', 'unique') ->exec(); if(!dao::isError()) { $productID = $this->dao->lastInsertID(); $this->file->updateObjectID($this->post->uid, $productID, 'product'); $this->dao->update(TABLE_PRODUCT)->set('`order`')->eq($productID * 5)->where('id')->eq($productID)->exec(); $whitelist = explode(',', $product->whitelist); $this->loadModel('personnel')->updateWhitelist($whitelist, 'product', $productID); if($product->acl != 'open') $this->loadModel('user')->updateUserView($productID, 'product'); /* Create doc lib. */ $this->app->loadLang('doc'); $lib = new stdclass(); $lib->product = $productID; $lib->name = $this->lang->doclib->main['product']; $lib->type = 'product'; $lib->main = '1'; $lib->acl = 'default'; $this->dao->insert(TABLE_DOCLIB)->data($lib)->exec(); return $productID; } } /** * Update a product. * * @param int $productID * @access public * @return array */ public function update($productID) { $productID = (int)$productID; $oldProduct = $this->dao->findById($productID)->from(TABLE_PRODUCT)->fetch(); if($oldProduct->bind) $this->config->product->edit->requiredFields = 'name'; $product = fixer::input('post') ->setIF($this->post->acl == 'open', 'whitelist', '') ->setDefault('line', 0) ->join('whitelist', ',') ->stripTags($this->config->product->editor->edit['id'], $this->config->allowedTags) ->remove('uid,changeProjects') ->get(); if($this->config->systemMode == 'new') { if($product->program != $oldProduct->program) { /* Link the projects stories under this product. */ $unmodifiableProjects = $this->dao->select('t1.*')->from(TABLE_PROJECTSTORY)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq($productID) ->andWhere('t2.type')->eq('project') ->andWhere('t2.deleted')->eq('0') ->fetchPairs('project', 'product'); if(!empty($unmodifiableProjects)) { dao::$errors[] = $this->lang->product->changeProgramError; return false; } } } $product = $this->loadModel('file')->processImgURL($product, $this->config->product->editor->edit['id'], $this->post->uid); $this->dao->update(TABLE_PRODUCT)->data($product)->autoCheck() ->batchCheck($this->config->product->edit->requiredFields, 'notempty') ->check('name', 'unique', "id != $productID and deleted = '0'") ->check('code', 'unique', "id != $productID and deleted = '0'") ->where('id')->eq($productID) ->exec(); if(!dao::isError()) { $this->file->updateObjectID($this->post->uid, $productID, 'product'); $whitelist = explode(',', $product->whitelist); $this->loadModel('personnel')->updateWhitelist($whitelist, 'product', $productID); if($product->acl != 'open') $this->loadModel('user')->updateUserView($productID, 'product'); return common::createChanges($oldProduct, $product); } } /** * Batch update products. * * @access public * @return array */ public function batchUpdate() { $products = array(); $allChanges = array(); $data = fixer::input('post')->get(); $oldProducts = $this->getByIdList($this->post->productIDList); $nameList = array(); foreach($data->productIDList as $productID) { $productName = $data->names[$productID]; $productID = (int)$productID; $products[$productID] = new stdClass(); $products[$productID]->name = $productName; $products[$productID]->PO = $data->POs[$productID]; $products[$productID]->QD = $data->QDs[$productID]; $products[$productID]->RD = $data->RDs[$productID]; $products[$productID]->type = $data->types[$productID]; $products[$productID]->status = $data->statuses[$productID]; $products[$productID]->desc = strip_tags($this->post->descs[$productID], $this->config->allowedTags); $products[$productID]->acl = $data->acls[$productID]; $products[$productID]->order = $data->orders[$productID]; /* Check unique name for edited products. */ if(isset($nameList[$productName])) dao::$errors['name'][] = 'product#' . $productID . sprintf($this->lang->error->unique, $this->lang->product->name, $productName); $nameList[$productName] = $productName; } if(dao::isError()) die(js::error(dao::getError())); foreach($products as $productID => $product) { $oldProduct = $oldProducts[$productID]; $this->dao->update(TABLE_PRODUCT) ->data($product) ->autoCheck() ->batchCheck($this->config->product->edit->requiredFields , 'notempty') ->check('name', 'unique', "id NOT " . helper::dbIN($data->productIDList) . " and deleted='0'") ->where('id')->eq($productID) ->exec(); if(dao::isError()) die(js::error('product#' . $productID . dao::getError(true))); /* When acl is open, white list set empty. When acl is private,update user view. */ if($product->acl == 'open') $this->loadModel('personnel')->updateWhitelist('', 'product', $productID); if($product->acl != 'open') $this->loadModel('user')->updateUserView($productID, 'product'); $allChanges[$productID] = common::createChanges($oldProduct, $product); } return $allChanges; } /** * Close product. * * @param int $productID. * @access public * @return void */ public function close($productID) { $oldProduct = $this->getById($productID); $now = helper::now(); $product= fixer::input('post') ->setDefault('status', 'closed') ->remove('comment')->get(); $this->dao->update(TABLE_PRODUCT)->data($product) ->autoCheck() ->where('id')->eq((int)$productID) ->exec(); if(!dao::isError()) return common::createChanges($oldProduct, $product); } /** * Manage line. * * @access public * @return void */ public function manageLine() { $data = fixer::input('post')->get(); $line = new stdClass(); $line->type = 'line'; $line->parent = 0; $line->grade = 1; $maxOrder = $this->dao->select("max(`order`) as maxOrder")->from(TABLE_MODULE)->where('type')->eq('line')->fetch('maxOrder'); $maxOrder = $maxOrder ? $maxOrder : 0; foreach($data->modules as $id => $name) { if(!$name) continue; $line->name = strip_tags(trim($name)); $line->root = $data->programs[$id]; if(is_numeric($id)) { $maxOrder += 10; $line->order = $maxOrder; $this->dao->insert(TABLE_MODULE)->data($line)->exec(); $lineID = $this->dao->lastInsertID(); $path = ",$lineID,"; $this->dao->update(TABLE_MODULE)->set('path')->eq($path)->where('id')->eq($lineID)->exec(); } else { $lineID = str_replace('id', '', $id); $this->dao->update(TABLE_MODULE)->data($line)->where('id')->eq($lineID)->exec(); } } } /** * Get stories. * * @param int $productID * @param int $branch * @param string $browseType * @param int $queryID * @param int $moduleID * @param string $type requirement|story * @param string $sort * @param object $pager * @access public * @return array */ public function getStories($productID, $branch, $browseType, $queryID, $moduleID, $type = 'story', $sort, $pager) { if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getStories(); $this->loadModel('story'); /* Set modules and browse type. */ $modules = $moduleID ? $this->loadModel('tree')->getAllChildID($moduleID) : '0'; $browseType = $browseType == 'bybranch' ? 'bymodule' : $browseType; $browseType = ($browseType == 'bymodule' and $this->session->storyBrowseType and $this->session->storyBrowseType != 'bysearch') ? $this->session->storyBrowseType : $browseType; /* Get stories by browseType. */ $stories = array(); if($browseType == 'unclosed') { $unclosedStatus = $this->lang->story->statusList; unset($unclosedStatus['closed']); $stories = $this->story->getProductStories($productID, $branch, $modules, array_keys($unclosedStatus), $type, $sort, true, '', $pager); } if($browseType == 'unplan') $stories = $this->story->getByPlan($productID, $queryID, $modules, '', $type, $sort, $pager); if($browseType == 'allstory') $stories = $this->story->getProductStories($productID, $branch, $modules, 'all', $type, $sort, true, '', $pager); if($browseType == 'bymodule') $stories = $this->story->getProductStories($productID, $branch, $modules, 'all', $type, $sort, true, '', $pager); if($browseType == 'bysearch') $stories = $this->story->getBySearch($productID, $branch, $queryID, $sort, '', $type, '', $pager); if($browseType == 'assignedtome') $stories = $this->story->getByAssignedTo($productID, $branch, $modules, $this->app->user->account, $type, $sort, $pager); if($browseType == 'openedbyme') $stories = $this->story->getByOpenedBy($productID, $branch, $modules, $this->app->user->account, $type, $sort, $pager); if($browseType == 'reviewedbyme') $stories = $this->story->getByReviewedBy($productID, $branch, $modules, $this->app->user->account, $type, $sort, $pager); if($browseType == 'closedbyme') $stories = $this->story->getByClosedBy($productID, $branch, $modules, $this->app->user->account, $type, $sort, $pager); if($browseType == 'draftstory') $stories = $this->story->getByStatus($productID, $branch, $modules, 'draft', $type, $sort, $pager); if($browseType == 'activestory') $stories = $this->story->getByStatus($productID, $branch, $modules, 'active', $type, $sort, $pager); if($browseType == 'changedstory') $stories = $this->story->getByStatus($productID, $branch, $modules, 'changed', $type, $sort, $pager); if($browseType == 'willclose') $stories = $this->story->get2BeClosed($productID, $branch, $modules, $type, $sort, $pager); if($browseType == 'closedstory') $stories = $this->story->getByStatus($productID, $branch, $modules, 'closed', $type, $sort, $pager); return $stories; } /** * Batch get story stage. * * @param array $stories. * @access public * @return array */ public function batchGetStoryStage($stories) { /* Set story id list. */ $storyIdList = array(); foreach($stories as $story) $storyIdList[$story->id] = $story->id; return $this->loadModel('story')->batchGetStoryStage($storyIdList); } /** * Build search form. * * @param int $productID * @param array $products * @param int $queryID * @param int $actionURL * @access public * @return void */ public function buildSearchForm($productID, $products, $queryID, $actionURL) { $this->config->product->search['actionURL'] = $actionURL; $this->config->product->search['queryID'] = $queryID; $this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productID); $product = ($this->app->openApp == 'project' and empty($productID)) ? $products : array($productID => $products[$productID]); $this->config->product->search['params']['product']['values'] = $product + array('all' => $this->lang->product->allProduct); /* Get module of all products.*/ $module = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'story', $startModuleID = 0); if(!$productID) { $module = array(); foreach($products as $id => $product) $module += $this->loadModel('tree')->getOptionMenu($id, $viewType = 'story', $startModuleID = 0); } $this->config->product->search['params']['module']['values'] = $module; if($this->session->currentProductType == 'normal') { unset($this->config->product->search['fields']['branch']); unset($this->config->product->search['params']['branch']); } else { $this->config->product->search['fields']['branch'] = $this->lang->product->branch; $this->config->product->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty') + array('all' => $this->lang->branch->all); } $this->loadModel('search')->setSearchParams($this->config->product->search); } /** * Get project pairs by product. * * @param int $productID * @param int $branch * @access public * @return array */ public function getProjectPairsByProduct($productID, $branch = 0) { return $this->dao->select('t2.id,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq($productID) ->andWhere('t2.type')->eq('project') ->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi() ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->andWhere('t2.deleted')->eq('0') ->fetchPairs(); } /** * Get project list by product. * * @param int $productID * @param string $browseType * @param int $branch * @param int $involved * @access public * @return array */ public function getProjectListByProduct($productID, $browseType = 'all', $branch = 0, $involved = 0) { $projectList = $this->dao->select('t2.*')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq($productID) ->beginIF($this->config->systemMode == 'new')->andWhere('t2.type')->eq('project')->fi() ->beginIF($browseType != 'all')->andWhere('t2.status')->eq($browseType)->fi() ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() ->beginIF($this->cookie->involved or $involved) ->andWhere('t2.openedBy', true)->eq($this->app->user->account) ->orWhere('t2.PM')->eq($this->app->user->account) ->markRight(1) ->fi() ->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi() ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->andWhere('t2.deleted')->eq('0') ->fetchAll('id'); /* Determine how to display the name of the program. */ $programList = $this->loadModel('program')->getParentPairs('', 'noclosed'); foreach($projectList as $id => $project) $projectList[$id]->programName = $programList[$project->parent]; return $projectList; } /** * Get project stats by product. * * @param int $productID * @param string $browseType * @param int $branch * @param int $involved * @access public * @return array */ public function getProjectStatsByProduct($productID, $browseType = 'all', $branch = 0, $involved = 0) { $projects = $this->getProjectListByProduct($productID, $browseType, $branch, $involved); if(empty($projects)) return array(); $projectKeys = array_keys($projects); $stats = array(); $hours = array(); $emptyHour = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'progress' => 0); /* Get all tasks and compute totalEstimate, totalConsumed, totalLeft, progress according to them. */ $tasks = $this->dao->select('id, project, estimate, consumed, `left`, status, closedReason') ->from(TABLE_TASK) ->where('project')->in($projectKeys) ->andWhere('parent')->lt(1) ->andWhere('deleted')->eq(0) ->fetchGroup('project', 'id'); /* Compute totalEstimate, totalConsumed, totalLeft. */ foreach($tasks as $projectID => $projectTasks) { $hour = (object)$emptyHour; foreach($projectTasks as $task) { if($task->status != 'cancel') { $hour->totalEstimate += $task->estimate; $hour->totalConsumed += $task->consumed; } if($task->status != 'cancel' and $task->status != 'closed') $hour->totalLeft += $task->left; } $hours[$projectID] = $hour; } /* Compute totalReal and progress. */ foreach($hours as $hour) { $hour->totalEstimate = round($hour->totalEstimate, 1) ; $hour->totalConsumed = round($hour->totalConsumed, 1); $hour->totalLeft = round($hour->totalLeft, 1); $hour->totalReal = $hour->totalConsumed + $hour->totalLeft; $hour->progress = $hour->totalReal ? round($hour->totalConsumed / $hour->totalReal, 2) * 100 : 0; } /* Get the number of project teams. */ $teams = $this->dao->select('root,count(*) as teams')->from(TABLE_TEAM) ->where('root')->in($projectKeys) ->andWhere('type')->eq('project') ->groupBy('root') ->fetchAll('root'); /* Process projects. */ foreach($projects as $key => $project) { if($project->end == '0000-00-00') $project->end = ''; /* Judge whether the project is delayed. */ if($project->status != 'done' and $project->status != 'closed' and $project->status != 'suspended') { $delay = helper::diffDate(helper::today(), $project->end); if($delay > 0) $project->delay = $delay; } /* Process the hours. */ $project->hours = isset($hours[$project->id]) ? $hours[$project->id] : (object)$emptyHour; $project->teamCount = isset($teams[$project->id]) ? $teams[$project->id]->teams : 0; $stats[] = $project; } return $stats; } /** * Get executions by product and project. * * @param int $productID * @param int $branch * @param string $orderBy * @param int $projectID * @access public * @return array */ public function getExecutionPairsByProduct($productID, $branch = 0, $orderBy = 'id_asc', $projectID = 0) { if(empty($productID)) return array(); if(empty($projectID)) return $this->getAllExecutionPairsByProduct($productID, $branch); $project = $this->loadModel('project')->getByID($projectID); $orderBy = $project->model == 'waterfall' ? 'begin_asc,id_asc' : 'begin_desc,id_desc'; $executions = $this->dao->select('t2.id,t2.name,t2.grade,t2.parent')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq($productID) ->andWhere('t2.project')->eq($projectID) ->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi() ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->sprints)->fi() ->andWhere('t2.deleted')->eq('0') ->orderBy($orderBy) ->fetchAll('id'); /* The waterfall project needs to show the hierarchy and remove the parent stage. */ $executionList = array('0' => ''); if($project->model == 'waterfall') { foreach($executions as $id => $execution) { if($execution->grade == 2 and isset($executions[$execution->parent])) { $execution->name = $executions[$execution->parent]->name . '/' . $execution->name; $executions[$execution->parent]->children[$id] = $execution->name; unset($executions[$id]); } } foreach($executions as $execution) { if(isset($execution->children)) { $executionList = $executionList + $execution->children; continue; } $executionList[$execution->id] = $execution->name; } } else { foreach($executions as $execution) $executionList[$execution->id] = $execution->name; } return $executionList; } /** * Get execution pairs by product. * * @param int $productID * @param int $branch * @access public * @return array */ public function getAllExecutionPairsByProduct($productID, $branch = 0) { if(empty($productID)) return array(); $executions = $this->dao->select('t2.id,t2.project,t2.name,t2.grade,t2.parent')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq($productID) ->andWhere('t2.type')->in('stage,sprint') ->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi() ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->sprints)->fi() ->andWhere('t2.deleted')->eq('0') ->fetchAll('id'); $projectIdList = array(); foreach($executions as $id => $execution) $projectIdList[$execution->project] = $execution->project; $executionPairs = array(); $projectPairs = $this->loadModel('project')->getPairsByIdList($projectIdList); foreach($executions as $id => $execution) { if($execution->grade == 2 && isset($executions[$execution->parent])) { $execution->name = $projectPairs[$execution->project] . '/' . $executions[$execution->parent]->name . '/' . $execution->name; $executions[$execution->parent]->children[$id] = $execution->name; unset($executions[$id]); } } foreach($executions as $execution) { if(isset($execution->children)) { $executionPairs = $executionPairs + $execution->children; continue; } if($this->config->systemMode == 'new') $executionPairs[$execution->id] = $projectPairs[$execution->project] . '/' . $execution->name; if($this->config->systemMode == 'classic') $executionPairs[$execution->id] = $execution->name; } return $executionPairs; } /** * Get roadmap of a proejct. * * @param int $productID * @param int $branch * @param int $count * @access public * @return array */ public function getRoadmap($productID, $branch = 0, $count = 0) { $plans = $this->loadModel('productplan')->getList($productID, $branch); $releases = $this->loadModel('release')->getList($productID, $branch); $roadmap = array(); $total = 0; $parents = array(); $orderedPlans = array(); foreach($plans as $planID => $plan) { if($plan->parent == '-1') { $parents[$planID] = $plan->title; unset($plans[$planID]); continue; } if((!helper::isZeroDate($plan->end) and strtotime($plan->end) - time() <= 0) or $plan->end == '2030-01-01') continue; $orderedPlans[$plan->end][] = $plan; } krsort($orderedPlans); foreach($orderedPlans as $plans) { krsort($plans); foreach($plans as $plan) { if($plan->parent > 0 and isset($parents[$plan->parent])) $plan->title = $parents[$plan->parent] . ' / ' . $plan->title; $year = substr($plan->end, 0, 4); $roadmap[$year][$plan->branch][] = $plan; $total++; if($count > 0 and $total >= $count) return $this->processRoadmap($roadmap); } } $orderedReleases = array(); foreach($releases as $release) $orderedReleases[$release->date][] = $release; krsort($orderedReleases); foreach($orderedReleases as $releases) { krsort($releases); foreach($releases as $release) { $year = substr($release->date, 0, 4); $roadmap[$year][$release->branch][] = $release; $total++; if($count > 0 and $total >= $count) return $this->processRoadmap($roadmap); } } if($count > 0) return $this->processRoadmap($roadmap); $groupRoadmap = array(); foreach($roadmap as $year => $branchRoadmaps) { foreach($branchRoadmaps as $branch => $roadmaps) { $totalData = count($roadmaps); $rows = ceil($totalData / 8); $maxPerRow = ceil($totalData / $rows); $groupRoadmap[$year][$branch] = array_chunk($roadmaps, $maxPerRow); foreach($groupRoadmap[$year][$branch] as $row => $rowRoadmaps) krsort($groupRoadmap[$year][$branch][$row]); } } /* Get last 5 roadmap. */ $lastKeys = array_slice(array_keys($groupRoadmap), 0, 5); $lastRoadmap = array(); $lastRoadmap['total'] = 0; foreach($lastKeys as $key) { if($key == '2030') { $lastRoadmap[$this->lang->productplan->future] = $groupRoadmap[$key]; } else { $lastRoadmap[$key] = $groupRoadmap[$key]; } foreach($groupRoadmap[$key] as $branchRoadmaps) $lastRoadmap['total'] += (count($branchRoadmaps, 1) - count($branchRoadmaps)); } return $lastRoadmap; } /** * Process roadmap. * * @param array $roadmap * @access public * @return array */ public function processRoadmap($roadmapGroups) { $newRoadmap = array(); foreach($roadmapGroups as $year => $branchRoadmaps) { foreach($branchRoadmaps as $branch => $roadmaps) { foreach($roadmaps as $roadmap) $newRoadmap[] = $roadmap; } } krsort($newRoadmap); return $newRoadmap; } /** * Get team members of a product from projects. * * @param object $product * @access public * @return array */ public function getTeamMemberPairs($product) { $members[$product->PO] = $product->PO; $members[$product->QD] = $product->QD; $members[$product->RD] = $product->RD; $members[$product->createdBy] = $product->createdBy; /* Set projects and teams as static thus we can only query sql one times. */ static $projects, $teams; if(empty($projects)) { $projects = $this->dao->select('t1.project, t1.product')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t2.deleted')->eq(0) ->fetchGroup('product', 'project'); } if(empty($teams)) { $teams = $this->dao->select('t1.root, t1.account')->from(TABLE_TEAM)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.root = t2.id') ->where('t2.deleted')->eq(0) ->andWhere('t1.type')->eq('project') ->fetchGroup('root', 'account'); } if(!isset($projects[$product->id])) return $members; $productProjects = $projects[$product->id]; $projectTeams = array(); foreach(array_keys($productProjects) as $projectID) $projectTeams = array_merge($projectTeams, array_keys($teams[$projectID])); return array_flip(array_merge($members, $projectTeams)); } /** * Get product stat by id * * @param int $productID * @param string $storyType * @access public * @return object|bool */ public function getStatByID($productID, $storyType = 'story') { if(!$this->checkPriv($productID)) return false; $product = $this->getById($productID); if(empty($product)) return false; $stories = $this->dao->select('product, status, count(status) AS count')->from(TABLE_STORY) ->where('deleted')->eq(0) ->andWhere('type')->eq($storyType) ->andWhere('product')->eq($productID) ->groupBy('product, status') ->fetchAll('status'); /* Padding the stories to sure all status have records. */ foreach(array_keys($this->lang->story->statusList) as $status) { $stories[$status] = isset($stories[$status]) ? $stories[$status]->count : 0; } $plans = $this->dao->select('count(*) AS count')->from(TABLE_PRODUCTPLAN)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->andWhere('end')->gt(helper::now())->fetch(); $builds = $this->dao->select('count(*) AS count')->from(TABLE_BUILD)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch(); $cases = $this->dao->select('count(*) AS count')->from(TABLE_CASE)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch(); $bugs = $this->dao->select('count(*) AS count')->from(TABLE_BUG)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch(); $docs = $this->dao->select('count(*) AS count')->from(TABLE_DOC)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch(); $releases = $this->dao->select('count(*) AS count')->from(TABLE_RELEASE)->where('deleted')->eq(0)->andWhere('product')->eq($productID)->fetch(); $projects = $this->dao->select('count("t1.*") AS count')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t2.deleted')->eq(0) ->andWhere('t1.product')->eq($productID) ->fetch(); $product->stories = $stories; $product->plans = $plans ? $plans->count : 0; $product->releases = $releases ? $releases->count : 0; $product->builds = $builds ? $builds->count : 0; $product->cases = $cases ? $cases->count : 0; $product->projects = $projects ? $projects->count : 0; $product->bugs = $bugs ? $bugs->count : 0; $product->docs = $docs ? $docs->count : 0; return $product; } /** * Get product stats. * * @param string $orderBy * @param object $pager * @param string $status * @param int $line * @param string $storyType requirement|story * @param int $programID * @access public * @return array */ public function getStats($orderBy = 'order_desc', $pager = null, $status = 'noclosed', $line = 0, $storyType = 'story', $programID = 0) { $this->loadModel('report'); $this->loadModel('story'); $this->loadModel('bug'); $products = $this->getList($programID, $status, $limit = 0, $line); if(empty($products)) return array(); $productKeys = array_keys($products); if($orderBy == 'program_asc') { $products = $this->dao->select('t1.id as id, t1.*')->from(TABLE_PRODUCT)->alias('t1') ->leftJoin(TABLE_PROGRAM)->alias('t2')->on('t1.program = t2.id') ->where('t1.id')->in($productKeys) ->orderBy('t2.order_asc, t1.line_desc, t1.order_desc') ->page($pager) ->fetchAll('id'); } else { $products = $this->dao->select('*')->from(TABLE_PRODUCT) ->where('id')->in($productKeys) ->orderBy($orderBy) ->page($pager) ->fetchAll('id'); } $linePairs = $this->getLinePairs(); foreach($products as $product) $product->lineName = zget($linePairs, $product->line, ''); $stories = $this->dao->select('product, status, type, count(status) AS count') ->from(TABLE_STORY) ->where('deleted')->eq(0) ->andWhere('type')->eq('story') ->andWhere('product')->in($productKeys) ->groupBy('product, status') ->fetchGroup('product', 'status'); $requirements = $this->dao->select('product, status, type, count(status) AS count') ->from(TABLE_STORY) ->where('deleted')->eq(0) ->andWhere('type')->eq('requirement') ->andWhere('product')->in($productKeys) ->groupBy('product, status') ->fetchGroup('product', 'status'); /* Padding the stories to sure all products have records. */ $emptyStory = array_keys($this->lang->story->statusList); foreach($productKeys as $productID) { if(!isset($stories[$productID])) $stories[$productID] = $emptyStory; if(!isset($requirements[$productID])) $requirements[$productID] = $emptyStory; } /* Padding the stories to sure all status have records. */ foreach($stories as $key => $story) { foreach(array_keys($this->lang->story->statusList) as $status) { $story[$status] = isset($story[$status]) ? $story[$status]->count : 0; } $stories[$key] = $story; } foreach($requirements as $key => $requirement) { foreach(array_keys($this->lang->story->statusList) as $status) { $requirement[$status] = isset($requirement[$status]) ? $requirement[$status]->count : 0; } $requirements[$key] = $requirement; } if($storyType == 'requirement') $stories = $requirements; $plans = $this->dao->select('product, count(*) AS count') ->from(TABLE_PRODUCTPLAN) ->where('deleted')->eq(0) ->andWhere('product')->in($productKeys) ->andWhere('end')->gt(helper::now()) ->groupBy('product') ->fetchPairs(); $releases = $this->dao->select('product, count(*) AS count') ->from(TABLE_RELEASE) ->where('deleted')->eq(0) ->andWhere('product')->in($productKeys) ->groupBy('product') ->fetchPairs(); $bugs = $this->dao->select('product,count(*) AS conut') ->from(TABLE_BUG) ->where('deleted')->eq(0) ->andWhere('product')->in($productKeys) ->groupBy('product') ->fetchPairs(); $unResolved = $this->dao->select('product,count(*) AS count') ->from(TABLE_BUG) ->where('deleted')->eq(0) ->andwhere('status')->eq('active') ->andWhere('product')->in($productKeys) ->groupBy('product') ->fetchPairs(); $closedBugs = $this->dao->select('product,count(*) AS count') ->from(TABLE_BUG) ->where('deleted')->eq(0) ->andwhere('status')->eq('closed') ->andWhere('product')->in($productKeys) ->groupBy('product') ->fetchPairs(); $assignToNull = $this->dao->select('product,count(*) AS count') ->from(TABLE_BUG) ->where('deleted')->eq(0) ->andwhere('assignedTo')->eq('') ->andWhere('product')->in($productKeys) ->groupBy('product') ->fetchPairs(); if(empty($programID)) { $programKeys = array(0=>0); foreach($products as $product) $programKeys[] = $product->program; $programs = $this->dao->select('id,name')->from(TABLE_PROGRAM) ->where('id')->in(array_unique($programKeys)) ->andWhere('deleted')->eq('0') ->fetchPairs(); foreach($products as $product) $product->programName = isset($programs[$product->program]) ? $programs[$product->program] : ''; } $stats = array(); foreach($products as $key => $product) { $product->stories = $stories[$product->id]; $product->requirements = $requirements[$product->id]; $product->plans = isset($plans[$product->id]) ? $plans[$product->id] : 0; $product->releases = isset($releases[$product->id]) ? $releases[$product->id] : 0; $product->bugs = isset($bugs[$product->id]) ? $bugs[$product->id] : 0; $product->unResolved = isset($unResolved[$product->id]) ? $unResolved[$product->id] : 0; $product->closedBugs = isset($closedBugs[$product->id]) ? $closedBugs[$product->id] : 0; $product->assignToNull = isset($assignToNull[$product->id]) ? $assignToNull[$product->id] : 0; $stats[] = $product; } return $stats; } /** * Get product line pairs. * * @param int $programID * @access public * @return void */ public function getLinePairs($programID = 0) { return $this->dao->select('id,name')->from(TABLE_MODULE) ->where('type')->eq('line') ->beginIF($programID)->andWhere('root')->eq($programID)->fi() ->andWhere('deleted')->eq(0) ->fetchPairs(); } /** * Get the summary of product's stories. * * @param array $stories * @param string $storyType story|requirement * @access public * @return string. */ public function summary($stories, $storyType = 'story') { $totalEstimate = 0.0; $storyIdList = array(); $rateCount = 0; $allCount = 0; foreach($stories as $key => $story) { if(!empty($story->type) && $story->type != $storyType) continue; $totalEstimate += $story->estimate; /* When the status is not closed or closedReason is done or postponed then add cases rate..*/ if( $story->status != 'closed' or ($story->status == 'closed' and ($story->closedReason == 'done' or $story->closedReason == 'postponed')) ) { $storyIdList[] = $story->id; $rateCount ++; } $allCount ++; if(!empty($story->children)) { foreach($story->children as $child) { if($child->type != $storyType) continue; if( $child->status != 'closed' or ($child->status == 'closed' and ($child->closedReason == 'done' or $child->closedReason == 'postponed')) ) { $storyIdList[] = $child->id; $rateCount ++; } $allCount ++; } } } $cases = $this->dao->select('story')->from(TABLE_CASE)->where('story')->in($storyIdList)->andWhere('deleted')->eq(0)->fetchAll('story'); $rate = count($stories) == 0 || $rateCount == 0 ? 0 : round(count($cases) / $rateCount, 2); $storyCommon = $this->lang->SRCommon; if($storyType == 'requirement') $storyCommon = $this->lang->URCommon; if($storyType == 'story') $storyCommon = $this->lang->SRCommon; return sprintf($this->lang->product->storySummary, $allCount, $storyCommon, $totalEstimate, $rate * 100 . "%"); } /** * Judge an action is clickable or not. * * @param object $product * @param string $action * @access public * @return void */ public static function isClickable($product, $action) { $action = strtolower($action); if($action == 'close') return $product->status != 'closed'; return true; } /** * Create the link from module,method,extra,branch. * * @param string $module * @param string $method * @param string $extra * @param bool $branch * @access public * @return void */ public function getProductLink($module, $method, $extra, $branch = false) { $link = ''; if(strpos('programplan,product,roadmap,bug,testcase,testtask,story,qa,testsuite,testreport,build,projectrelease,projectstory', ',' . $module . ',') !== false) { if($module == 'product' && $method == 'project') { $link = helper::createLink($module, $method, "status=all&productID=%s" . ($branch ? "&branch=%s" : '')); } elseif($module == 'product' && ($method == 'doc' or $method == 'view')) { $link = helper::createLink($module, $method, "productID=%s"); } elseif($module == 'product' && $method == 'dynamic') { $link = helper::createLink($module, $method, "productID=%s&type=$extra"); } elseif($module == 'product' && $method == 'create') { $link = helper::createLink($module, 'browse', "productID=%s&type=$extra"); } elseif($module == 'qa' && $method == 'index') { $link = helper::createLink('bug', 'browse', "productID=%s" . ($branch ? "&branch=%s" : '')); } elseif($module == 'product' && ($method == 'browse' or $method == 'index' or $method == 'all')) { $link = helper::createLink($module, 'browse', "productID=%s" . ($branch ? "&branch=%s" : '&branch=0') . "&browseType=¶m=0&$extra"); } elseif($module == 'programplan') { $extra = $extra ? $extra : 'gantt'; $link = helper::createLink($module, 'browse', "projectID=%s&productID=%s&type=$extra" . ($branch ? "&branch=%s" : '')); } elseif($module == 'story' && $method == 'report') { $link = helper::createLink($module, 'report', "productID=%s" . ($branch ? "&branch=%s" : '&branch=0') . "&extra=$extra"); } elseif($module == 'bug' && $method == 'view') { $link = helper::createLink('bug', 'browse', "productID=%s" . ($branch ? "&branch=%s" : '&branch=0') . "&extra=$extra"); } else { $link = helper::createLink($module, $method, "productID=%s" . ($branch ? "&branch=%s" : '')); } } else if($module == 'productplan' || $module == 'release') { if($method != 'browse' && $method != 'create') $method = 'browse'; $link = helper::createLink($module, $method, "productID=%s" . ($branch ? "&branch=%s" : '')); } else if($module == 'tree') { $link = helper::createLink($module, $method, "productID=%s&type=$extra¤tModuleID=0" . ($branch ? "&branch=%s" : '')); } else if($module == 'branch') { $link = helper::createLink($module, $method, "productID=%s"); } else if($module == 'doc') { $link = helper::createLink('doc', 'objectLibs', "type=product&objectID=%s&from=product"); } elseif($module == 'design') { return helper::createLink('design', 'browse', "productID=%s"); } elseif($module == 'project') { return helper::createLink('project', $method, "projectID=$extra&productID=%s"); } return $link; } /** * Setting parameters for link. * * @param string $module * @param string $link * @param int $projectID * @param int $productID * @access public * @return void */ public function setParamsForLink($module, $link, $projectID, $productID) { $linkHtml = strpos('programplan', $module) !== false ? sprintf($link, $projectID, $productID) : sprintf($link, $productID); return $linkHtml; } /** * get the latest project of the product. * * @param int $productID * @access public * @return object */ public function getLatestProject($productID) { return $this->dao->select('t2.id, t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq((int)$productID) ->andWhere('t2.status')->ne('closed') ->andWhere('t2.deleted')->eq('0') ->orderBy('t2.begin desc') ->limit(1) ->fetch(); } /** * Change the projects set of the program. * * @param int $productID * @param array $singleLinkProjects * @param array $multipleLinkProjects * @access public * @return void */ public function updateProjects($productID, $singleLinkProjects = array(), $multipleLinkProjects = array()) { $programID = $_POST['program']; foreach($singleLinkProjects as $projectID => $projectName) { if($projectName) { $this->dao->update(TABLE_PROJECT) ->set('parent')->eq($programID) ->set('path')->eq(',' . $programID . ',' . $projectID . ',') ->where('id')->eq($projectID) ->exec(); } } foreach($multipleLinkProjects as $projectID => $projectName) { if(strpos($_POST['changeProjects'], ',' . $projectID . ',') !== false) { $this->dao->delete()->from(TABLE_PROJECTPRODUCT) ->where('project')->eq($projectID) ->andWhere('product')->ne($productID) ->exec(); $this->dao->update(TABLE_PROJECT) ->set('parent')->eq($programID) ->set('path')->eq(',' . $programID . ',' . $projectID . ',') ->where('id')->eq($projectID) ->exec(); $this->loadModel('action')->create('project', $projectID, 'Managed', '', $productID); } else { $this->dao->delete()->from(TABLE_PROJECTPRODUCT) ->where('project')->eq($projectID) ->andWhere('product')->eq($productID) ->exec(); $newProducts = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchPairs('product','product'); $this->loadModel('action')->create('project', $projectID, 'Managed', '', join(',', $newProducts)); } } } /** * * Set menu. * * @param int $productID * @param int $branch * @param int $module * @param string $moduleType * @param string $extra * * @access public * @return void */ public function setMenu($productID, $branch = 0, $module = 0, $moduleType = '', $extra = '') { $product = $this->getByID($productID); if(!$product) return; $this->lang->switcherMenu = $this->getSwitcher($productID, $extra, $branch); $params = array('branch' => $branch); common::setMenuVars('product', $productID, $params); if($product->type == 'normal') { unset($this->lang->product->menu->settings['subMenu']->branch); } else { $branchLink = $this->lang->product->menu->settings['subMenu']->branch['link']; $this->lang->product->menu->settings['subMenu']->branch['link'] = str_replace('@branch@', $this->lang->product->branchName[$product->type], $branchLink); } } }