diff --git a/config/zentaopms.php b/config/zentaopms.php index 949a5a8146..148afc917c 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -59,6 +59,7 @@ define('TABLE_GROUPPRIV', '`' . $config->db->prefix . 'grouppriv`'); define('TABLE_USERGROUP', '`' . $config->db->prefix . 'usergroup`'); define('TABLE_USERQUERY', '`' . $config->db->prefix . 'userquery`'); define('TABLE_USERCONTACT', '`' . $config->db->prefix . 'usercontact`'); +define('TABLE_USERVIEW', '`' . $config->db->prefix . 'userview`'); define('TABLE_BUG', '`' . $config->db->prefix . 'bug`'); define('TABLE_CASE', '`' . $config->db->prefix . 'case`'); diff --git a/db/update10.3.1.sql b/db/update10.3.1.sql new file mode 100644 index 0000000000..cf99b234bf --- /dev/null +++ b/db/update10.3.1.sql @@ -0,0 +1,6 @@ +CREATE TABLE `zt_userview` ( + `account` char(30) NOT NULL, + `products` text NOT NULL, + `projects` text NOT NULL, + UNIQUE KEY `account` (`account`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/module/action/model.php b/module/action/model.php index 70d9c20111..679ebcb02e 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -574,12 +574,12 @@ class actionModel extends model /* Build has priv condition. */ $condition = 1; - if($productID == 'all') $products = $this->loadModel('product')->getPairs(); - if($projectID == 'all') $projects = $this->loadModel('project')->getPairs(); + if($productID == 'all') $products = $this->app->user->view->products; + if($projectID == 'all') $projects = $this->app->user->view->projects; if($productID == 'all' or $projectID == 'all') { - $projectCondition = $projectID == 'all' ? "project " . helper::dbIN(array_keys($projects)) : ''; - $productCondition = $productID == 'all' ? "INSTR('," . join(',', array_keys($products)) . ",', product) > 0" : ''; + $projectCondition = $projectID == 'all' ? "project " . helper::dbIN($projects) : ''; + $productCondition = $productID == 'all' ? "INSTR('," . $products . ",', product) > 0" : ''; if(is_numeric($productID)) $productCondition = "product like'%,$productID,%' or product='$productID'"; if(is_numeric($projectID)) $projectCondition = "project='$projectID'"; diff --git a/module/block/control.php b/module/block/control.php index b8665325e9..0600dd4317 100644 --- a/module/block/control.php +++ b/module/block/control.php @@ -595,14 +595,13 @@ class block extends control $this->session->set('testtaskList', $this->server->http_referer); if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) die(); $this->app->loadLang('testtask'); - $products = $this->loadModel('product')->getPairs(); $this->view->testtasks = $this->dao->select('t1.*,t2.name as productName,t3.name as buildName,t4.name as projectName')->from(TABLE_TESTTASK)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') ->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id') ->leftJoin(TABLE_PROJECT)->alias('t4')->on('t1.project=t4.id') ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t5')->on('t1.project=t5.project') ->where('t1.deleted')->eq('0') - ->andWhere('t1.product')->in(array_keys($products)) + ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi() ->andWhere('t1.product = t5.product') ->beginIF($this->params->type != 'all')->andWhere('t1.status')->eq($this->params->type)->fi() ->orderBy('t1.id desc') @@ -638,11 +637,10 @@ class block extends control { $this->session->set('productPlanList', $this->server->http_referer); $this->app->loadLang('productplan'); - $products = $this->loadModel('product')->getPairs(); $this->view->plans = $this->dao->select('t1.*,t2.name as productName')->from(TABLE_PRODUCTPLAN)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') ->where('t1.deleted')->eq('0') - ->andWhere('t1.product')->in(array_keys($products)) + ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi() ->orderBy('t1.begin desc') ->beginIF($this->viewType != 'json')->limit((int)$this->params->num)->fi() ->fetchAll(); @@ -658,12 +656,11 @@ class block extends control { $this->session->set('releaseList', $this->server->http_referer); $this->app->loadLang('release'); - $products = $this->loadModel('product')->getPairs(); $this->view->releases = $this->dao->select('t1.*,t2.name as productName,t3.name as buildName')->from(TABLE_RELEASE)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') ->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id') ->where('t1.deleted')->eq('0') - ->andWhere('t1.product')->in(array_keys($products)) + ->beginIF(!$this->app->user->admin)->andWhere('t1.product')->in($this->app->user->view->products)->fi() ->orderBy('t1.id desc') ->beginIF($this->viewType != 'json')->limit((int)$this->params->num)->fi() ->fetchAll(); @@ -679,11 +676,10 @@ class block extends control { $this->session->set('buildList', $this->server->http_referer); $this->app->loadLang('build'); - $projects = $this->loadModel('project')->getPairs(); $this->view->builds = $this->dao->select('t1.*, t2.name as productName')->from(TABLE_BUILD)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') ->where('t1.deleted')->eq('0') - ->andWhere('t1.project')->in(array_keys($projects)) + ->beginIF(!$this->app->user->admin)->andWhere('t1.project')->in($this->app->user->view->projects)->fi() ->orderBy('t1.id desc') ->beginIF($this->viewType != 'json')->limit((int)$this->params->num)->fi() ->fetchAll(); diff --git a/module/bug/control.php b/module/bug/control.php index 4a60ae6d61..88d6030491 100644 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -523,7 +523,7 @@ class bug extends control $bug = $this->bug->getById($bugID, true); if(!$bug) die(js::error($this->lang->notFound) . js::locate('back')); - if($bug->project and !$this->loadModel('project')->checkPriv($this->project->getByID($bug->project))) + if($bug->project and !$this->loadModel('project')->checkPriv($bug->project)) { echo(js::alert($this->lang->project->accessDenied)); $loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login"; diff --git a/module/bug/model.php b/module/bug/model.php index 7e7cf40bce..371b1cdfd8 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -2292,7 +2292,7 @@ class bugModel extends model if(strpos($bugQuery, '`product` =') === false) $bugQuery .= ' AND `product` = ' . $productID; if(strpos($bugQuery, $allProduct) !== false) { - $products = array_keys($this->loadModel('product')->getPrivProducts()); + $products = $this->app->user->view->products; $bugQuery = str_replace($allProduct, '1', $bugQuery); $bugQuery = $bugQuery . ' AND `product` ' . helper::dbIN($products); } diff --git a/module/common/model.php b/module/common/model.php index 344ea98648..2f9935a576 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -86,6 +86,7 @@ class commonModel extends model { if($this->session->user) { + $this->session->user->view = $this->loadModel('user')->grantUserView(); $this->app->user = $this->session->user; } elseif($this->app->company->guest or PHP_SAPI == 'cli') @@ -98,6 +99,7 @@ class commonModel extends model $user->admin = false; $user->rights = $this->loadModel('user')->authorize('guest'); $user->groups = array('group'); + $user->view = $this->user->grantUserView($user->account, $user->rights['acls']); $this->session->set('user', $user); $this->app->user = $this->session->user; } @@ -1390,7 +1392,7 @@ EOD; if($module == 'task' and !empty($object->project))$objectID = $object->project; $limitedProjects = !empty($_SESSION['limitedProjects']) ? $_SESSION['limitedProjects'] : ''; - if(strpos(",{$limitedProjects},", ",$objectID,") !== false) $limitedProject = true; + if($objectID and strpos(",{$limitedProjects},", ",$objectID,") !== false) $limitedProject = true; } if(empty($app->user->rights['rights']['my']['limited']) && !$limitedProject) return true; diff --git a/module/doc/control.php b/module/doc/control.php index 8ceb4f11ee..0697958b9a 100644 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -690,15 +690,13 @@ class doc extends control if($type == 'product') { $productID = $objectID; - $lib = $this->product->getById($objectID); - if(!$this->product->checkPriv($lib)) $this->accessDenied(); + if(!$this->product->checkPriv($objectID)) $this->accessDenied(); } if($type == 'project') { $projectID = $objectID; - $lib = $this->project->getById($objectID); - if(!$this->project->checkPriv($lib)) $this->accessDenied(); + if(!$this->project->checkPriv($objectID)) $this->accessDenied(); } $this->doc->setMenu($type, $libID = 0, $moduleID = 0, $productID, $projectID, $crumb); diff --git a/module/doc/model.php b/module/doc/model.php index dda419e267..b920a7b740 100644 --- a/module/doc/model.php +++ b/module/doc/model.php @@ -887,20 +887,8 @@ class docModel extends model if(isset($extraDocLibs[$object->id])) return true; } - if($object->project) - { - static $projects; - if(empty($projects)) $projects = $this->loadModel('project')->getPairs(); - return isset($projects[$object->project]); - } - - if($object->product) - { - static $products; - if(empty($products)) $products = $this->loadModel('product')->getPairs(); - return isset($products[$object->product]); - } - + if($object->project) return strpos(",{$this->app->user->view->projects},", ",{$object->project},") !== false; + if($object->product) return strpos(",{$this->app->user->view->products},", ",{$object->product},") !== false; return false; } diff --git a/module/group/model.php b/module/group/model.php index 3d33a35c9c..fd888049b1 100644 --- a/module/group/model.php +++ b/module/group/model.php @@ -306,17 +306,37 @@ class groupModel extends model */ public function updateUser($groupID) { + $userGroups = $this->dao->select('account')->from(TABLE_USERGROUP)->where('`group`')->eq($groupID)->fetchPairs('account', 'account'); + /* Delete old. */ $this->dao->delete()->from(TABLE_USERGROUP)->where('`group`')->eq($groupID)->exec(); /* Insert new. */ - if($this->post->members == false) return; - foreach($this->post->members as $account) + if($this->post->members) { - $data = new stdclass(); - $data->account = $account; - $data->group = $groupID; - $this->dao->insert(TABLE_USERGROUP)->data($data)->exec(); + foreach($this->post->members as $account) + { + $data = new stdclass(); + $data->account = $account; + $data->group = $groupID; + $this->dao->insert(TABLE_USERGROUP)->data($data)->exec(); + + if(isset($userGroups[$account])) + { + unset($userGroups[$account]); + } + else + { + $userGroups[$account] = $account; + } + } + } + + /* Adjust user view. */ + if($userGroups) + { + $this->loadModel('user'); + foreach($userGroups as $account) $this->user->computeUserView($account, true); } } diff --git a/module/product/model.php b/module/product/model.php index 99f9e4df40..29e98534b7 100644 --- a/module/product/model.php +++ b/module/product/model.php @@ -30,7 +30,7 @@ class productModel extends model public function setMenu($products, $productID, $branch = 0, $module = 0, $moduleType = '', $extra = '') { /* Has access privilege?. */ - if($products and !isset($products[$productID]) and !$this->checkPriv($this->getById($productID))) + if($products and !isset($products[$productID]) and !$this->checkPriv($productID)) { echo(js::alert($this->lang->product->accessDenied)); $loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login"; @@ -214,21 +214,11 @@ class productModel extends model * @access public * @return bool */ - public function checkPriv($product) + public function checkPriv($productID) { /* Is admin? */ - $account = ',' . $this->app->user->account . ','; if($this->app->user->admin) return true; - - $acls = $this->app->user->rights['acls']; - if(!empty($acls['products']) and !in_array($product->id, $acls['products'])) return false; - - /* Product is open, return true. */ - if($product->acl == 'open') return true; - - /* Get team members. */ - $privProducts = $this->getPrivProducts(); - return isset($privProducts[$product->id]) ? true : false; + return (strpos(",{$this->app->user->view->products},", ",{$productID},") !== false); } /** @@ -273,6 +263,7 @@ class productModel extends model return $this->dao->select('*')->from(TABLE_PRODUCT) ->where('deleted')->eq(0) ->beginIF($line > 0)->andWhere('line')->eq($line)->fi() + ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->products)->fi() ->beginIF($status == 'noclosed')->andWhere('status')->ne('closed')->fi() ->beginIF($status != 'all' and $status != 'noclosed' and $status != 'involved')->andWhere('status')->in($status)->fi() ->beginIF($status == 'involved') @@ -302,14 +293,10 @@ class productModel extends model ->from(TABLE_PRODUCT) ->where('deleted')->eq(0) ->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) - ->fetchAll(); - $pairs = array(); - foreach($products as $product) - { - if($this->checkPriv($product)) $pairs[$product->id] = $product->name; - } - return $pairs; + ->fetchPairs('id', 'name'); + return $products; } /** @@ -381,6 +368,7 @@ class productModel extends model $lib->main = '1'; $lib->acl = $product->acl == 'open' ? 'open' : 'private'; $this->dao->insert(TABLE_DOCLIB)->data($lib)->exec(); + $this->loadModel('user')->updateUserView($productID, 'product'); return $productID; } @@ -415,6 +403,7 @@ class productModel extends model if($product->acl != $oldProduct->acl) $this->dao->update(TABLE_DOCLIB)->set('acl')->eq($product->acl == 'open' ? 'open' : 'private')->where('product')->eq($productID)->exec(); $this->file->updateObjectID($this->post->uid, $productID, 'product'); + if($product->acl != $oldProduct->acl or $product->whitelist != $oldProduct->whitelist) $this->loadModel('user')->updateUserView($productID, 'product'); return common::createChanges($oldProduct, $product); } } @@ -594,13 +583,12 @@ class productModel extends model */ public function getProjectPairs($productID, $branch = 0, $param = 'all') { - $projectList = array_keys($this->loadModel('project')->getPairs()); $projects = array(); $datas = $this->dao->select('t2.id, t2.name, t2.deleted')->from(TABLE_PROJECTPRODUCT) ->alias('t1')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq((int)$productID) ->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi() - ->andWhere('t2.id')->in($projectList) + ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->orderBy('t1.project desc') ->fetchAll(); @@ -726,8 +714,8 @@ class productModel extends model */ public function getStatByID($productID) { + if(!$this->checkPriv($productID)) return false; $product = $this->getById($productID); - if(!$product || !$this->checkPriv($product)) return false; $stories = $this->dao->select('product, status, count(status) AS count')->from(TABLE_STORY)->where('deleted')->eq(0)->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) @@ -776,10 +764,6 @@ class productModel extends model $this->loadModel('bug'); $products = $this->getList($status, $limit = 0, $line); - foreach($products as $productID => $product) - { - if(!$this->checkPriv($product)) unset($products[$productID]); - } $products = $this->dao->select('*')->from(TABLE_PRODUCT) ->where('id')->in(array_keys($products)) ->orderBy($orderBy) @@ -862,93 +846,6 @@ class productModel extends model return $stats; } - /** - * Get priv products. - * - * @access public - * @return array - */ - public function getPrivProducts() - { - $account = ',' . $this->app->user->account . ','; - static $products; - if($products === null) - { - $groups = ''; - if(isset($this->app->user->groups)) - { - foreach($this->app->user->groups as $group) $groups .= ",$group,"; - } - - $allProducts = $this->dao->select('*')->from(TABLE_PRODUCT)->where('deleted')->eq(0)->fetchAll('id'); - $productProjects = $this->dao->select('t1.product,t1.project')->from(TABLE_PROJECTPRODUCT)->alias('t1') - ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id') - ->where('t1.product')->in(array_keys($allProducts)) - ->andWhere('t2.deleted')->eq('0') - ->fetchGroup('product', 'project'); - - $linkedProjects = array(); - foreach($productProjects as $product => $projects) - { - foreach($projects as $projectID => $productProject) $linkedProjects[$projectID] = $projectID; - } - - $teams = $this->dao->select('root, account')->from(TABLE_TEAM)->where('root')->in($linkedProjects)->andWhere('type')->eq('project')->fetchGroup('root', 'account'); - - $products = array(); - $account = $this->app->user->account; - foreach($allProducts as $id => $product) - { - if($this->app->user->admin) - { - $products[$id] = $id; - } - else - { - if($product->PO == $account OR $product->QD == $account OR $product->RD == $account OR $product->createdBy == $account) - { - $products[$id] = $id; - continue; - } - if($product->acl == 'open') - { - $products[$id] = $id; - continue; - } - - $hasPriv = false; - if($product->acl == 'custom') - { - foreach(explode(',', $product->whitelist) as $whitelist) - { - if(empty($whitelist)) continue; - if(strpos($groups, ",$whitelist,") !== false) - { - $products[$id] = $id; - $hasPriv = true; - break; - } - } - } - if($hasPriv) continue; - - if(!empty($productProjects[$id])) - { - foreach($productProjects[$id] as $projectID => $productProject) - { - if(isset($teams[$projectID][$account])) - { - $products[$id] = $id; - break; - } - } - } - } - } - } - return $products; - } - /** * Get the summary of product's stories. * diff --git a/module/productplan/control.php b/module/productplan/control.php index 22bd866bf7..78c889cc6c 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -463,8 +463,7 @@ class productplan extends control */ public function linkBug($planID = 0, $browseType = '', $param = 0, $orderBy = 'id_desc') { - $projects = $this->loadModel('project')->getPairs(); - $projects[0] = ''; + $projects = $this->app->user->view->projects . ',0'; if(!empty($_POST['bugs'])) { @@ -514,9 +513,7 @@ class productplan extends control } else { - $projects = $this->loadModel('project')->getPairs(); - $projects[0] = ''; - $allBugs = $this->bug->getActiveBugs($this->view->product->id, $plan->branch, array_keys($projects)); + $allBugs = $this->bug->getActiveBugs($this->view->product->id, $plan->branch, $projects); } $this->view->allBugs = $allBugs; diff --git a/module/project/control.php b/module/project/control.php index a14b43c898..6a244b31ab 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -221,7 +221,6 @@ class project extends control $this->view->modules = $this->tree->getTaskOptionMenu($projectID); $this->view->moduleID = $moduleID; $this->view->moduleTree = $this->tree->getTaskTreeMenu($projectID, $productID, $startModuleID = 0, array('treeModel', 'createTaskLink')); - $this->view->projectTree = $this->project->tree(); $this->view->memberPairs = $memberPairs; $this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), 'noempty'); $this->view->setShowModule = true; diff --git a/module/project/model.php b/module/project/model.php index bf6c9d5d49..6699fe28ca 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -23,40 +23,11 @@ class projectModel extends model * @access public * @return bool */ - public function checkPriv($project) + public function checkPriv($projectID) { /* If is admin, return true. */ if($this->app->user->admin) return true; - - $acls = $this->app->user->rights['acls']; - if(!empty($acls['projects']) and !in_array($project->id, $acls['projects'])) return false; - - /* If project is open, return true. */ - if($project->acl == 'open') return true; - - /* Get all teams of all projects and group by projects, save it as static. */ - static $teams; - if(empty($teams)) $teams = $this->dao->select('root, account')->from(TABLE_TEAM)->where('type')->eq('project')->fetchGroup('root', 'account'); - $currentTeam = isset($teams[$project->id]) ? $teams[$project->id] : array(); - - /* If project is private, only members can access. */ - if($project->acl == 'private') - { - return isset($currentTeam[$this->app->user->account]); - } - - /* Project's acl is custom, check the groups. */ - if($project->acl == 'custom') - { - if(isset($currentTeam[$this->app->user->account])) return true; - $userGroups = $this->app->user->groups; - $projectGroups = explode(',', $project->whitelist); - foreach($userGroups as $groupID) - { - if(in_array($groupID, $projectGroups)) return true; - } - return false; - } + return (strpos(",{$this->app->user->view->projects},", ",{$projectID},") !== false); } /** @@ -84,7 +55,7 @@ class projectModel extends model unset($this->lang->project->subMenu->qa->testtask); } - if($projects and !isset($projects[$projectID]) and !$this->checkPriv($project)) + if($projects and !isset($projects[$projectID]) and !$this->checkPriv($projectID)) { echo(js::alert($this->lang->project->accessDenied)); $loginLink = $this->config->requestType == 'GET' ? "?{$this->config->moduleVar}=user&{$this->config->methodVar}=login" : "user{$this->config->requestFix}login"; @@ -375,6 +346,17 @@ class projectModel extends model $lib->main = '1'; $lib->acl = $project->acl == 'open' ? 'open' : 'private'; $this->dao->insert(TABLE_DOCLIB)->data($lib)->exec(); + + $this->loadModel('user')->updateUserView($projectID, 'project'); + if(isset($_POST['products'])) + { + foreach($this->post->products as $productID) + { + if(empty($productID)) continue; + $this->user->updateUserView($productID, 'product'); + } + } + if(!dao::isError()) $this->loadModel('score')->create('project', 'create', $projectID); return $projectID; } @@ -437,6 +419,7 @@ class projectModel extends model if($project->acl != $oldProject->acl) $this->dao->update(TABLE_DOCLIB)->set('acl')->eq($project->acl == 'open' ? 'open' : 'private')->where('project')->eq($projectID)->exec(); $this->file->updateObjectID($this->post->uid, $projectID, 'project'); + if($project->acl != $oldProject->acl or $project->whitelist != $oldProject->whitelist) $this->loadModel('user')->updateUserView($projectID, 'project'); return common::createChanges($oldProject, $project); } } @@ -694,18 +677,19 @@ class projectModel extends model $projects = $this->dao->select('*, IF(INSTR(" done,closed", status) < 2, 0, 1) AS isDone')->from(TABLE_PROJECT) ->where('iscat')->eq(0) ->beginIF(strpos($mode, 'withdelete') === false)->andWhere('deleted')->eq(0)->fi() + ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() ->orderBy($orderBy) ->fetchAll(); $pairs = array(); foreach($projects as $project) { if(strpos($mode, 'noclosed') !== false and ($project->status == 'done' or $project->status == 'closed')) continue; - if($this->checkPriv($project)) $pairs[$project->id] = $project->name; + $pairs[$project->id] = $project->name; } if(strpos($mode, 'empty') !== false) $pairs[0] = ''; /* If the pairs is empty, to make sure there's an project in the pairs. */ - if(empty($pairs) and isset($projects[0]) and $this->checkPriv($projects[0])) + if(empty($pairs) and isset($projects[0])) { $firstProject = $projects[0]; $pairs[$firstProject->id] = $firstProject->name; @@ -750,6 +734,7 @@ class projectModel extends model ->beginIF($branch)->andWhere('t1.branch')->eq($branch)->fi() ->beginIF($status == 'isdoing')->andWhere('t2.status')->ne('done')->andWhere('t2.status')->ne('suspended')->andWhere('t2.status')->ne('closed')->fi() ->beginIF($status != 'all' and $status != 'isdoing' and $status != 'undone')->andWhere('status')->in($status)->fi() + ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->orderBy('order_desc') ->beginIF($limit)->limit($limit)->fi() ->fetchAll('id'); @@ -760,6 +745,7 @@ class projectModel extends model ->beginIF($status == 'undone')->andWhere('status')->ne('done')->andWhere('status')->ne('closed')->fi() ->beginIF($status == 'isdoing')->andWhere('status')->ne('done')->andWhere('status')->ne('suspended')->andWhere('status')->ne('closed')->fi() ->beginIF($status != 'all' and $status != 'isdoing' and $status != 'undone')->andWhere('status')->in($status)->fi() + ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() ->andWhere('deleted')->eq(0) ->orderBy('order_desc') ->beginIF($limit)->limit($limit)->fi() @@ -788,6 +774,7 @@ class projectModel extends model ->andWhere('t2.deleted')->eq(0) ->andWhere('t2.iscat')->eq(0) ->beginIF($branch)->andWhere('t1.branch')->eq($branch)->fi() + ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->andWhere('t2.openedBy', true)->eq($this->app->user->account) ->orWhere('t3.account')->eq($this->app->user->account) ->markRight(1) @@ -801,6 +788,7 @@ class projectModel extends model return $this->dao->select('t1.*, IF(INSTR(" done,closed", t1.status) < 2, 0, 1) AS isDone')->from(TABLE_PROJECT)->alias('t1') ->leftJoin(TABLE_TEAM)->alias('t2')->on('t2.root=t1.id') ->where('t1.iscat')->eq(0) + ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->projects)->fi() ->andWhere('t1.openedBy', true)->eq($this->app->user->account) ->orWhere('t2.account')->eq($this->app->user->account) ->markRight(1) @@ -823,22 +811,17 @@ class projectModel extends model $list = $this->dao->select('t1.id, t1.name,t1.status, t2.product')->from(TABLE_PROJECT)->alias('t1') ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')->on('t1.id = t2.project') ->where('t1.deleted')->eq(0) + ->beginIF(!$this->app->user->admin)->andWhere('t1.id')->in($this->app->user->view->projects)->fi() ->fetchGroup('product'); $noProducts = array(); - $projects = $this->getList(); - foreach($list as $id => $product) { foreach($product as $ID => $project) { - if(!$this->checkPriv($projects[$project->id])) - { - unset($list[$id][$ID]); - } if(!$project->product) { - if($this->checkPriv($projects[$project->id])) $noProducts[] = $project; + if(strpos(",{$this->app->user->view->projects},", ",{$project->id},") !== false) $noProducts[] = $project; unset($list[$id][$ID]); } } @@ -864,10 +847,6 @@ class projectModel extends model { /* Init vars. */ $projects = $this->getList($status, 0, $productID, $branch); - foreach($projects as $projectID => $project) - { - if(!$this->checkPriv($project)) unset($projects[$projectID]); - } $projects = $this->dao->select('*')->from(TABLE_PROJECT) ->where('id')->in(array_keys($projects)) ->orderBy($orderBy) @@ -1218,16 +1197,14 @@ class projectModel extends model { $projects = $this->dao->select('*')->from(TABLE_PROJECT) ->where('id')->in($projectIds) + ->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->projects)->fi() ->andWhere('deleted')->eq(0) ->orderBy('id desc') ->fetchAll('id'); $pairs = array(); $now = date('Y-m-d'); - foreach($projects as $id => $project) - { - if($this->checkPriv($project)) $pairs[$id] = ucfirst(substr($project->code, 0, 1)) . ':' . $project->name; - } + foreach($projects as $id => $project) $pairs[$id] = ucfirst(substr($project->code, 0, 1)) . ':' . $project->name; return $pairs; } @@ -1240,9 +1217,15 @@ class projectModel extends model */ public function updateProducts($projectID) { + $this->loadModel('user'); $oldProjectProducts = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)->where('project')->eq((int)$projectID)->fetchGroup('product', 'branch'); $this->dao->delete()->from(TABLE_PROJECTPRODUCT)->where('project')->eq((int)$projectID)->exec(); - if(!isset($_POST['products'])) return; + if(!isset($_POST['products'])) + { + foreach($oldProjectProducts as $productID => $branches) $this->user->updateUserView($productID, 'product'); + return true; + } + $products = $_POST['products']; $branches = isset($_POST['branch']) ? $_POST['branch'] : array(); $plans = isset($_POST['plans']) ? $_POST['plans'] : array();; @@ -1269,6 +1252,14 @@ class projectModel extends model $this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec(); $existedProducts[$productID] = true; } + + $oldProductKeys = array_keys($oldProjectProducts); + $needUpdate = array_merge(array_diff($oldProductKeys, $products), array_diff($products, $oldProductKeys)); + foreach($needUpdate as $productID) + { + if(empty($productID)) continue; + $this->user->updateUserView($productID, 'product'); + } } /** @@ -1792,6 +1783,14 @@ class projectModel extends model $this->dao->insert(TABLE_TEAM)->data($member)->exec(); } } + $this->loadModel('user')->updateUserView($projectID, 'project', $accounts); + + $products = $this->getProducts($projectID, false); + foreach($products as $productID => $productName) + { + if(empty($productID)) continue; + $this->user->updateUserView($productID, 'product', $accounts); + } } /** @@ -1805,6 +1804,14 @@ class projectModel extends model public function unlinkMember($projectID, $account) { $this->dao->delete()->from(TABLE_TEAM)->where('root')->eq((int)$projectID)->andWhere('type')->eq('project')->andWhere('account')->eq($account)->exec(); + + $this->loadModel('user')->updateUserView($projectID, 'project', array($account)); + $products = $this->getProducts($projectID, false); + foreach($products as $productID => $productName) + { + if(empty($productID)) continue; + $this->user->updateUserView($productID, 'product', array($account)); + } } /** diff --git a/module/release/control.php b/module/release/control.php index 3eca4cac6f..cbb6414373 100644 --- a/module/release/control.php +++ b/module/release/control.php @@ -155,9 +155,9 @@ class release extends control $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'leftBugs'); $this->commonAction($release->product); - $products = $this->product->getPairs(); + $product = $this->product->getById($release->product); - $this->view->title = "RELEASE #$release->id $release->name/" . $products[$release->product]; + $this->view->title = "RELEASE #$release->id $release->name/" . $product->name; $this->view->position[] = $this->lang->release->view; $this->view->release = $release; $this->view->stories = $stories; diff --git a/module/testcase/model.php b/module/testcase/model.php index d3f9a51924..a3497902d1 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -511,7 +511,7 @@ class testcaseModel extends model $caseQuery = '(' . $this->session->testcaseQuery; if(strpos($this->session->testcaseQuery, $allProduct) !== false) { - $products = array_keys($this->loadModel('product')->getPrivProducts()); + $products = $this->app->user->view->products; $caseQuery = str_replace($allProduct, '1', $caseQuery); $caseQuery = $caseQuery . ' AND `product` ' . helper::dbIN($products); $queryProductID = 'all'; diff --git a/module/testtask/control.php b/module/testtask/control.php index dc3504760a..aa32e08e7f 100644 --- a/module/testtask/control.php +++ b/module/testtask/control.php @@ -138,15 +138,13 @@ class testtask extends control /* Create testtask from testtask of test.*/ if($projectID == 0) { - $projectList = array_keys($this->loadModel('project')->getPairs()); - $params = 'nodeleted'; $projects = array(); $datas = $this->dao->select('t2.id, t2.name, t2.deleted')->from(TABLE_PROJECTPRODUCT) ->alias('t1')->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id') ->where('t1.product')->eq((int)$productID) ->beginIF('0')->andWhere('t1.branch')->in('0')->fi() - ->andWhere('t2.id')->in($projectList) + ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi() ->andWhere('t2.type')->ne('ops') ->orderBy('t1.project desc') ->fetchAll(); diff --git a/module/testtask/model.php b/module/testtask/model.php index 8dfff46215..1224346a67 100644 --- a/module/testtask/model.php +++ b/module/testtask/model.php @@ -157,7 +157,7 @@ class testtaskModel extends model */ public function getProductTasks($productID, $branch = 0, $orderBy = 'id_desc', $pager = null, $scopeAndStatus = array(), $beginTime = 0, $endTime = 0) { - $products = $scopeAndStatus[0] == 'all' ? $this->loadModel('product')->getPairs() : array(); + $products = $scopeAndStatus[0] == 'all' ? $this->app->user->view->products : array(); if($this->config->global->flow == 'onlyTest') { return $this->dao->select("t1.*, t2.name AS productName,t4.name AS buildName, t4.branch AS branch") @@ -166,7 +166,7 @@ class testtaskModel extends model ->leftJoin(TABLE_BUILD)->alias('t4')->on('t1.build = t4.id') ->where('t1.deleted')->eq(0) ->beginIF($scopeAndStatus[0] == 'local')->andWhere('t1.product')->eq((int)$productID)->fi() - ->beginIF($scopeAndStatus[0] == 'all')->andWhere('t1.product')->in(array_keys($products))->fi() + ->beginIF($scopeAndStatus[0] == 'all')->andWhere('t1.product')->in($products)->fi() ->beginIF($scopeAndStatus[1] == 'totalStatus')->andWhere('t1.status')->in(('blocked,doing,wait,done'))->fi() ->beginIF($scopeAndStatus[1] != 'totalStatus')->andWhere('t1.status')->eq($scopeAndStatus[1])->fi() ->beginIF($branch)->andWhere("t4.branch = '$branch'")->fi() @@ -176,19 +176,17 @@ class testtaskModel extends model } else { - $projects = $this->loadModel('project')->getPairs(); return $this->dao->select("t1.*, t2.name AS productName, t3.name AS projectName, t4.name AS buildName, if(t4.name != '', t4.branch, t5.branch) AS branch") ->from(TABLE_TESTTASK)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id') ->leftJoin(TABLE_PROJECT)->alias('t3')->on('t1.project = t3.id') ->leftJoin(TABLE_BUILD)->alias('t4')->on('t1.build = t4.id') - ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t5')->on('t1.project = t5.project') + ->leftJoin(TABLE_PROJECTPRODUCT)->alias('t5')->on('t1.project = t5.project and t1.product = t5.product') ->where('t1.deleted')->eq(0) - ->andWhere('t5.product = t1.product') - ->andWhere('t3.id')->in(array_keys($projects)) + ->andWhere('t3.id')->in($this->app->user->view->projects) ->beginIF($scopeAndStatus[0] == 'local')->andWhere('t1.product')->eq((int)$productID)->fi() - ->beginIF($scopeAndStatus[0] == 'all')->andWhere('t1.product')->in(array_keys($products))->fi() + ->beginIF($scopeAndStatus[0] == 'all')->andWhere('t1.product')->in($products)->fi() ->beginIF($scopeAndStatus[1] == 'totalStatus')->andWhere('t1.status')->in('blocked,doing,wait,done')->fi() ->beginIF($scopeAndStatus[1] != 'totalStatus')->andWhere('t1.status')->eq($scopeAndStatus[1])->fi() ->beginIF($branch)->andWhere("if(t4.branch, t4.branch, t5.branch) = '$branch'")->fi() @@ -837,9 +835,8 @@ class testtaskModel extends model $caseQuery = $this->session->testtaskQuery; if(strpos($this->session->testtaskQuery, $allProduct) !== false) { - $products = array_keys($this->loadModel('product')->getPrivProducts()); $caseQuery = str_replace($allProduct, '1', $this->session->testtaskQuery); - $caseQuery = $caseQuery . ' AND `product` ' . helper::dbIN(array_keys($products)); + $caseQuery = $caseQuery . ' AND `product` ' . helper::dbIN($this->app->user->view->products); $queryProductID = 'all'; } diff --git a/module/user/control.php b/module/user/control.php index 30645546ae..2cb748acc7 100644 --- a/module/user/control.php +++ b/module/user/control.php @@ -710,6 +710,7 @@ class user extends control /* Authorize him and save to session. */ $user->rights = $this->user->authorize($user->account); $user->groups = $this->user->getGroups($user->account); + $user->view = $this->user->grantUserView($account, $user->rights['acls']); $this->session->set('user', $user); $this->app->user = $this->session->user; $this->loadModel('action')->create('user', $user->id, 'login'); diff --git a/module/user/model.php b/module/user/model.php index 61ff04c705..5c18327228 100644 --- a/module/user/model.php +++ b/module/user/model.php @@ -248,6 +248,7 @@ class userModel extends model $this->dao->insert(TABLE_USERGROUP)->data($data)->exec(); } + $this->computeUserView($user->account); $this->loadModel('action')->create('user', $userID, 'Created'); $this->loadModel('mail'); if($this->config->mail->mta == 'sendcloud' and !empty($user->email)) $this->mail->syncSendCloud('sync', $user->email, $user->realname); @@ -345,6 +346,7 @@ class userModel extends model } else { + $this->computeUserView($user->account); if($this->config->mail->mta == 'sendcloud' and !empty($user->email)) $this->mail->syncSendCloud('sync', $user->email, $user->realname); } } @@ -397,6 +399,7 @@ class userModel extends model if($this->post->account != $oldUser->account) { $this->dao->update(TABLE_USERGROUP)->set('account')->eq($this->post->account)->where('account')->eq($oldUser->account)->exec(); + $this->dao->update(TABLE_USERVIEW)->set('account')->eq($this->post->account)->where('account')->eq($oldUser->account)->exec(); if(strpos($this->app->company->admins, ',' . $oldUser->account . ',') !== false) { $admins = str_replace(',' . $oldUser->account . ',', ',' . $this->post->account . ',', $this->app->company->admins); @@ -415,6 +418,7 @@ class userModel extends model $data->group = $groupID; $this->dao->replace(TABLE_USERGROUP)->data($data)->exec(); } + $this->computeUserView($this->post->account, true); } if(!empty($user->password) and $user->account == $this->app->user->account) $this->app->user->password = $user->password; if(!dao::isError()) @@ -520,6 +524,7 @@ class userModel extends model { $oldAccount = $oldUser->account; $this->dao->update(TABLE_USERGROUP)->set('account')->eq($user['account'])->where('account')->eq($oldAccount)->exec(); + $this->dao->update(TABLE_USERVIEW)->set('account')->eq($user['account'])->where('account')->eq($oldAccount)->exec(); if(strpos($this->app->company->admins, ',' . $oldAccount . ',') !== false) { $admins = str_replace(',' . $oldAccount . ',', ',' . $user['account'] . ',', $this->app->company->admins); @@ -1231,4 +1236,239 @@ class userModel extends model } return true; } + + /** + * Compute user view. + * + * @param string $account + * @param bool $force + * @access public + * @return object + */ + public function computeUserView($account = '', $force = false) + { + if(empty($account)) $account = $this->session->user->account; + if(empty($account)) return array(); + + $userView = $this->dao->select('*')->from(TABLE_USERVIEW)->where('account')->eq($account)->fetch(); + if(empty($userView) or $force) + { + $isAdmin = strpos($this->app->company->admins, ',' . $account . ',') !== false; + $groups = $this->dao->select('`group`')->from(TABLE_USERGROUP)->where('account')->eq($account)->fetchPairs('group', 'group'); + $groups = ',' . join(',', $groups) . ','; + + static $allProducts, $allProjects, $projectProducts, $teams; + if($allProducts === null) $allProducts = $this->dao->select('id,PO,QD,RD,createdBy,acl,whitelist')->from(TABLE_PRODUCT)->fetchAll('id'); + if($allProjects === null) $allProjects = $this->dao->select('id,PO,PM,QD,RD,acl,whitelist')->from(TABLE_PROJECT)->fetchAll('id'); + if($projectProducts === null) + { + $stmt = $this->dao->select('project,product')->from(TABLE_PROJECTPRODUCT)->query(); + while($projectProduct = $stmt->fetch()) + { + $projectProducts[$projectProduct->product][$projectProduct->project] = $projectProduct->project; + } + } + if($teams === null) + { + $stmt = $this->dao->select('root,account')->from(TABLE_TEAM)->where('type')->eq('project')->query(); + while($team = $stmt->fetch()) $teams[$team->root][$team->account] = $team->account; + } + + $userView = new stdclass(); + $userView->account = $account; + $userView->products = array(); + $userView->projects = array(); + if($isAdmin) + { + $userView->products = join(',', array_keys($allProducts)); + $userView->projects = join(',', array_keys($allProjects)); + } + else + { + $products = array(); + foreach($allProducts as $id => $product) + { + if($this->checkProductPriv($product, $account, $groups, zget($projectProducts, $id, array()), $teams)) $products[$id] = $id; + } + $userView->products = join(',', $products); + + $projects = array(); + foreach($allProjects as $id => $project) + { + $projectTeams = isset($teams[$id]) ? $teams[$id] : array(); + if($this->checkProjectPriv($project, $account, $groups, $projectTeams)) $projects[$id] = $id; + } + $userView->projects = join(',', $projects); + } + $this->dao->replace(TABLE_USERVIEW)->data($userView)->exec(); + } + return $userView; + } + + /** + * Grant user view. + * + * @param string $account + * @param array $acls + * @access public + * @return object + */ + public function grantUserView($account = '', $acls = array()) + { + if(empty($account)) $account = $this->session->user->account; + if(empty($account)) return array(); + if(empty($acls) and isset($this->app->user->rights['acls'])) $acls = $this->app->user->rights['acls']; + $userView = $this->dao->select('*')->from(TABLE_USERVIEW)->where('account')->eq($account)->fetch(); + + if(empty($userView)) $userView = $this->computeUserView($account); + if(!empty($acls['products'])) + { + $grantProducts = ''; + foreach($acls['products'] as $productID) + { + if(strpos(",{$userView->products},", ",{$productID},") !== false) $grantProducts .= ",{$productID}"; + } + $userView->products = $grantProducts; + } + if(!empty($acls['projects'])) + { + $grantProjects = ''; + foreach($acls['projects'] as $projectID) + { + if(strpos(",{$userView->projects},", ",{$projectID},") !== false) $grantProjects .= ",{$projectID}"; + } + $userView->projects = $grantProjects; + } + + return $userView; + } + + /** + * Update user view. + * + * @param int $objectID + * @param string $objectType + * @param array $users + * @access public + * @return void + */ + public function updateUserView($objectID, $objectType, $users = array()) + { + $table = ''; + if($objectType == 'product') $table = TABLE_PRODUCT; + if($objectType == 'project') $table = TABLE_PROJECT; + if(empty($table)) return false; + + $object = $this->dao->select('*')->from($table)->where('id')->eq($objectID)->fetch(); + $allGroups = $this->dao->select('account,`group`')->from(TABLE_USERGROUP)->fetchAll(); + $userGroups = array(); + foreach($allGroups as $group) + { + if(!isset($userGroups[$group->account])) $userGroups[$group->account] = ''; + $userGroups[$group->account] .= "{$group->group},"; + } + + $linkedProjects = array(); + if($objectType == 'product') + { + $stmt = $this->dao->select('project,product')->from(TABLE_PROJECTPRODUCT)->where('product')->eq($objectID)->query(); + while($projectProduct = $stmt->fetch()) $linkedProjects[$projectProduct->project] = $projectProduct->project; + } + + $teams = array(); + $stmt = $this->dao->select('root,account')->from(TABLE_TEAM)->where('type')->eq('project') + ->beginIF($objectType == 'product')->andWhere('root')->in($linkedProjects)->fi() + ->beginIF($objectType == 'project')->andWhere('root')->eq($objectID)->fi() + ->query(); + while($team = $stmt->fetch()) $teams[$team->root][$team->account] = $team->account; + + $field = $objectType == 'product' ? 'products' : 'projects'; + $stmt = $this->dao->select("account,{$field}")->from(TABLE_USERVIEW) + ->beginIF($users)->where('account')->in($users)->fi() + ->query(); + while($userView = $stmt->fetch()) + { + $account = $userView->account; + if($objectType == 'product') + { + $hasPriv = $this->checkProductPriv($object, $account, zget($userGroups, $account, ''), $linkedProjects, $teams); + if($hasPriv and strpos(",{$userView->products},", ",{$objectID},") === false) $userView->products .= ",{$objectID}"; + if(!$hasPriv and strpos(",{$userView->products},", ",{$objectID},") !== false) $userView->products = trim(str_replace(",{$objectID},", ',', ",{$userView->products},"), ','); + $this->dao->update(TABLE_USERVIEW)->set('products')->eq($userView->products)->where('account')->eq($account)->exec(); + } + elseif($objectType == 'project') + { + $hasPriv = $this->checkProjectPriv($object, $account, zget($userGroups, $account, ''), zget($teams, $objectID)); + if($hasPriv and strpos(",{$userView->projects},", ",{$objectID},") === false) $userView->projects .= ",{$objectID}"; + if(!$hasPriv and strpos(",{$userView->projects},", ",{$objectID},") !== false) $userView->projects = trim(str_replace(",{$objectID},", ',', ",{$userView->projects},"), ','); + $this->dao->update(TABLE_USERVIEW)->set('projects')->eq($userView->projects)->where('account')->eq($account)->exec(); + } + } + } + + /** + * Check product priv. + * + * @param object $product + * @param string $account + * @param string $groups + * @param array $linkedProjects + * @param array $teams + * @access public + * @return bool + */ + public function checkProductPriv($product, $account, $groups, $linkedProjects, $teams) + { + if(strpos($this->app->company->admins, ',' . $account . ',') !== false) return true; + if($product->PO == $account OR $product->QD == $account OR $product->RD == $account OR $product->createdBy == $account) return true; + if($product->acl == 'open') return true; + + if($product->acl == 'custom') + { + foreach(explode(',', $product->whitelist) as $whitelist) + { + if(empty($whitelist)) continue; + if(strpos(",{$groups},", ",$whitelist,") !== false) return true; + } + } + + if(!empty($linkedProjects)) + { + foreach($linkedProjects as $projectID) + { + if(isset($teams[$projectID][$account])) return true; + } + } + + return false; + } + + /** + * Check project priv. + * + * @param object $project + * @param string $account + * @param string $groups + * @param array $teams + * @access public + * @return bool + */ + public function checkProjectPriv($project, $account, $groups, $teams) + { + if(strpos($this->app->company->admins, ',' . $account . ',') !== false) return true; + if($project->PO == $account OR $project->QD == $account OR $project->RD == $account OR $project->PM == $account) return true; + if($project->acl == 'open') return true; + if(isset($teams[$account])) return true; + + if($project->acl == 'custom') + { + foreach(explode(',', $project->whitelist) as $whitelist) + { + if(empty($whitelist)) continue; + if(strpos(",{$groups},", ",$whitelist,") !== false) return true; + } + } + + return false; + } }