diff --git a/module/backup/control.php b/module/backup/control.php index 650d157317..96c2dbbeee 100644 --- a/module/backup/control.php +++ b/module/backup/control.php @@ -113,11 +113,11 @@ class backup extends control } /* Delete expired backup. */ - $sqlFiles = glob("{$this->backupPath}*.sql.php"); - if(!empty($sqlFiles)) + $backupFiles = glob("{$this->backupPath}*.php"); + if(!empty($backupFiles)) { $time = time(); - foreach($sqlFiles as $file) + foreach($backupFiles as $file) { if($time - filemtime($file) > $this->config->backup->holdDays * 24 * 3600) unlink($file); } diff --git a/module/branch/control.php b/module/branch/control.php index 1e105daac2..177ddf17a4 100644 --- a/module/branch/control.php +++ b/module/branch/control.php @@ -51,10 +51,10 @@ class branch extends control public function ajaxGetBranches($productID) { $product = $this->loadModel('product')->getById($productID); - if($product->type == 'normal') die(); + if(empty($product) or $product->type == 'normal') die(); $branches = $this->branch->getPairs($productID); - die(html::select('branch[]', $branches, '', "class='form-control'")); + die(html::select('branch', $branches, '', "class='form-control'")); } } diff --git a/module/branch/model.php b/module/branch/model.php index 08d6c74f04..f2d2901baf 100644 --- a/module/branch/model.php +++ b/module/branch/model.php @@ -37,5 +37,19 @@ class branchModel extends model return dao::isError(); } + + public function getByProducts($products, $params = '') + { + $branches = $this->dao->select('*')->from(TABLE_BRANCH)->where('product')->in($products)->andWhere('deleted')->eq(0)->fetchAll(); + + $branchGroups = array(); + foreach($branches as $branch) + { + if(!isset($branchGroups[$branch->product]) and strpos($params, 'noempty') === false) $branchGroups[$branch->product][0] = $this->lang->branch->all; + $branchGroups[$branch->product][$branch->id] = $branch->name; + } + + return $branchGroups; + } } diff --git a/module/project/control.php b/module/project/control.php index 3279a5d7e1..cdcaf998db 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -900,10 +900,11 @@ class project extends control $name = ''; $code = ''; $team = ''; - $products = ''; + $products = array(); $whitelist = ''; $acl = 'open'; + $productIDList = array(); if($copyProjectID) { $copyProject = $this->dao->select('*')->from(TABLE_PROJECT)->where('id')->eq($copyProjectID)->fetch(); @@ -912,7 +913,8 @@ class project extends control $team = $copyProject->team; $acl = $copyProject->acl; $whitelist = $copyProject->whitelist; - $products = join(',', array_keys($this->project->getProducts($copyProjectID))); + $products = $this->project->getProducts($copyProjectID); + foreach($products as $product) $productIDList[$product->id] = $product->id; } if(!empty($_POST)) @@ -931,7 +933,7 @@ class project extends control $this->view->position[] = $this->view->title; $this->view->projects = array('' => '') + $this->projects; $this->view->groups = $this->loadModel('group')->getPairs(); - $this->view->allProducts = $this->loadModel('product')->getPairs('noclosed|nocode'); + $this->view->allProducts = array(0 => '') + $this->loadModel('product')->getPairs('noclosed|nocode'); $this->view->name = $name; $this->view->code = $code; $this->view->team = $team; @@ -939,6 +941,7 @@ class project extends control $this->view->whitelist = $whitelist; $this->view->acl = $acl ; $this->view->copyProjectID = $copyProjectID; + $this->view->branchGroups = $this->loadModel('branch')->getByProducts($productIDList); $this->display(); } @@ -986,10 +989,14 @@ class project extends control $position[] = html::a($browseProjectLink, $project->name); $position[] = $this->lang->project->edit; - $allProducts = $this->loadModel('product')->getPairs('noclosed|nocode'); + $allProducts = array(0 => '') + $this->loadModel('product')->getPairs('noclosed|nocode'); $linkedProducts = $this->project->getProducts($project->id); - $allProducts += $linkedProducts; - $linkedProducts = join(',', array_keys($linkedProducts)); + $productIDList = array(); + foreach($linkedProducts as $product) + { + if(!isset($allProducts[$product->id])) $allProducts[$product->id] = $product->name; + $productIDList[$product->id] = $product->id; + } $this->view->title = $title; $this->view->position = $position; @@ -1002,6 +1009,7 @@ class project extends control $this->view->groups = $this->loadModel('group')->getPairs(); $this->view->allProducts = $allProducts; $this->view->linkedProducts = $linkedProducts; + $this->view->branchGroups = $this->loadModel('branch')->getByProducts($productIDList); $this->display(); } @@ -1343,14 +1351,21 @@ class project extends control $allProducts = $this->product->getPairs('noclosed|nocode'); $linkedProducts = $this->project->getProducts($project->id); // Merge allProducts and linkedProducts for closed product. - $allProducts += $linkedProducts; - $linkedProducts = join(',', array_keys($linkedProducts)); + $products = array(); + foreach($linkedProducts as $product) + { + if(!isset($allProducts[$product->id])) $allProducts[$product->id] = $product->name; + if(!isset($products[$product->id])) $products[$product->id] = $product; + $products[$product->id]->branches[$product->branch] = $product->branch; + } + $linkedProducts = $products; /* Assign. */ $this->view->title = $title; $this->view->position = $position; $this->view->allProducts = $allProducts; $this->view->linkedProducts = $linkedProducts; + $this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($allProducts)); $this->display(); } diff --git a/module/project/css/manageproducts.css b/module/project/css/manageproducts.css index dd728adb6b..bdf0ad390b 100644 --- a/module/project/css/manageproducts.css +++ b/module/project/css/manageproducts.css @@ -1,7 +1,6 @@ #productsBox {padding: 20px;} -#productsBox > label {padding: 8px 15px; border:1px solid #ddd; min-width: 31%} -#productsBox > label > input {margin-left: 0; margin-right: 5px;} -#productsBox > label:hover {background: #f1f1f1} -#productsBox > label.checked, #productsBox > label.checked:hover {background: #E5FFE6; border: 1px solid #229F24;} -.radio-inline+.radio-inline, .checkbox-inline+.checkbox-inline {margin-left: 0;} -.checkbox-inline {margin-right: 10px; margin-bottom: 10px;} +#productsBox > .col-sm-4 {padding: 8px 15px; border:1px solid #ddd; min-height:48px; margin-bottom:5px; width:32%; margin-right:8px;} +#productsBox > .col-sm-4 label {display:block} +#productsBox > .col-sm-4:hover {background: #f1f1f1} +#productsBox > .col-sm-4.checked, #productsBox > .col-sm-4.checked:hover {background: #E5FFE6; border: 1px solid #229F24;} +.col-sm-4+.col-sm-4 {margin-left: 0;} diff --git a/module/project/js/common.js b/module/project/js/common.js index 81e42134c1..3ab3f88f01 100644 --- a/module/project/js/common.js +++ b/module/project/js/common.js @@ -109,6 +109,31 @@ function computeEndDate(delta) computeWorkDays(); } +function loadBranches(product) +{ + if($('#productsBox .input-group:last select:first').val() != 0) + { + var length = $('#productsBox .input-group').size(); + $('#productsBox .row').append('
' + $('#productsBox .col-sm-3:last').html() + '
'); + if($('#productsBox .input-group:last select').size() >= 2)$('#productsBox .input-group:last select:last').remove(); + $('#productsBox .input-group:last .chosen-container').remove(); + $('#productsBox .input-group:last select:first').attr('name', 'products[' + length + ']').attr('id', 'products' + length); + $('#productsBox .input-group:last .chosen').chosen(defaultChosenOptions); + } + + var $inputgroup = $(product).closest('.input-group'); + if($inputgroup.find('select').size() >= 2)$inputgroup.find('select:last').remove(); + var index = $inputgroup.find('select:first').attr('id').replace('products' , ''); + $.get(createLink('branch', 'ajaxGetBranches', "productID=" + $(product).val()), function(data) + { + if(data) + { + $inputgroup.append(data); + $inputgroup.find('select:last').attr('name', 'branch[' + index + ']').attr('id', 'branch' + index).css('width', '80px'); + } + }) +} + /* Auto compute the work days. */ $(function() { diff --git a/module/project/js/manageproducts.js b/module/project/js/manageproducts.js index d199b1dd09..a84915100b 100644 --- a/module/project/js/manageproducts.js +++ b/module/project/js/manageproducts.js @@ -3,12 +3,12 @@ $(function() $('#productsBox input:checkbox').each(function() { var cb = $(this); - if(cb.attr('checked')) cb.closest('label').addClass('checked'); + if(cb.prop('checked')) cb.closest('.col-sm-4').addClass('checked'); }); $('#productsBox input:checkbox').change(function() { var cb = $(this); - cb.closest('label').toggleClass('checked', cb.attr('checked')); + cb.closest('.col-sm-4').toggleClass('checked', cb.prop('checked')); }); }) diff --git a/module/project/model.php b/module/project/model.php index fb7d5ed343..53788eacca 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -219,7 +219,7 @@ class projectModel extends model ->setDefault('team', substr($this->post->name,0, 30)) ->join('whitelist', ',') ->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags) - ->remove('products, workDays, delta') + ->remove('products, workDays, delta, branch') ->get(); $this->dao->insert(TABLE_PROJECT)->data($project) ->autoCheck($skipFields = 'begin,end') @@ -292,7 +292,7 @@ class projectModel extends model ->setDefault('team', $this->post->name) ->join('whitelist', ',') ->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags) - ->remove('products') + ->remove('products,branch') ->get(); $this->dao->update(TABLE_PROJECT)->data($project) ->autoCheck($skipFields = 'begin,end') @@ -790,11 +790,11 @@ class projectModel extends model */ public function getProducts($projectID) { - return $this->dao->select('t2.id, t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1') + return $this->dao->select('t2.id, t2.name, t2.type, t1.branch')->from(TABLE_PROJECTPRODUCT)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2') ->on('t1.product = t2.id') ->where('t1.project')->eq((int)$projectID) - ->fetchPairs(); + ->fetchAll(); } /** @@ -833,13 +833,36 @@ class projectModel extends model { $this->dao->delete()->from(TABLE_PROJECTPRODUCT)->where('project')->eq((int)$projectID)->exec(); if(!isset($_POST['products'])) return; - $products = array_unique($_POST['products']); - foreach($products as $productID) + $products = $_POST['products']; + $branches = $_POST['branch']; + + $existedProducts = array(); + foreach($products as $i => $productID) { + if(empty($productID)) continue; + $data = new stdclass(); $data->project = $projectID; $data->product = $productID; - $this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec(); + + /* When manageProducts. */ + if(isset($branches[$i]) and is_array($branches[$i])) + { + foreach($branches[$i] as $branch) + { + $data->branch = $branch; + if(isset($existedProducts[$data->product][$data->branch])) continue; + $existedProducts[$data->product][$data->branch] = true; + $this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec(); + } + } + else + { + $data->branch = isset($branches[$i]) ? $branches[$i] : 0; + if(isset($existedProducts[$data->product][$data->branch])) continue; + $existedProducts[$data->product][$data->branch] = true; + $this->dao->insert(TABLE_PROJECTPRODUCT)->data($data)->exec(); + } } } diff --git a/module/project/view/create.html.php b/module/project/view/create.html.php index 989c49e937..fd44981fcf 100644 --- a/module/project/view/create.html.php +++ b/module/project/view/create.html.php @@ -89,7 +89,27 @@ project->manageProducts;?> - project->linkProduct}' multiple");?> + +
+ + +
+
+ id, "class='form-control chosen' onchange='loadBranches(this)'");?> + + type != 'normal') echo html::select("branch[$i]", $branchGroups[$product->id], $product->branch, "class='form-control' style='width:80px'")?> +
+
+ + +
+
+ + +
+
+
+ project->desc;?> diff --git a/module/project/view/edit.html.php b/module/project/view/edit.html.php index 0e4c23021b..71eb51ed97 100644 --- a/module/project/view/edit.html.php +++ b/module/project/view/edit.html.php @@ -102,7 +102,27 @@ project->manageProducts;?> - project->linkProduct}' multiple");?> + +
+ + +
+
+ id, "class='form-control chosen' onchange='loadBranches(this)'");?> + + type != 'normal') echo html::select("branch[$i]", $branchGroups[$product->id], $product->branch, "class='form-control' style='width:80px'")?> +
+
+ + +
+
+ + +
+
+
+ project->desc;?> diff --git a/module/project/view/manageproducts.html.php b/module/project/view/manageproducts.html.php index fa7c58854c..5516eb5821 100644 --- a/module/project/view/manageproducts.html.php +++ b/module/project/view/manageproducts.html.php @@ -18,7 +18,26 @@
-
+
+ $productName):?> + +
+ "?> + +
"; + echo html::select("branch[$productID][]", $branchGroups[$productID], $checked ? join(',', $linkedProducts[$productID]->branches) : '', "class='from-control chosen' multiple"); + echo '
'; + } + ?> +
+ + +
diff --git a/module/tree/model.php b/module/tree/model.php index 665a562d87..4675b2f0c3 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -139,6 +139,7 @@ class treeModel extends model /* If createdVersion <= 4.1, go to getOptionMenu(). */ $createdVersion = $this->dao->select('openedVersion')->from(TABLE_PROJECT)->where('id')->eq($rootID)->fetch('openedVersion'); $products = $this->loadModel('product')->getProductsByProject($rootID); + $branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products)); if(!$createdVersion or version_compare($createdVersion, '4.1', '<=') or !$products) return $this->getOptionMenu($rootID, 'task', $startModule); @@ -185,7 +186,9 @@ class treeModel extends model { $parentModules = explode(',', trim($module->path, ',')); if($type == 'product' and isset($noProductModules[$parentModules[0]])) continue; - $this->buildTreeArray($treeMenu, $modules, $module, $productNum > 1 ? "/$rootModule/" : '/'); + $rootName = $rootModule; + if($type == 'product' and $module->branch and isset($branchGroups[$id][$module->branch])) $rootName .= '/' . $branchGroups[$id][$module->branch]; + $this->buildTreeArray($treeMenu, $modules, $module, $productNum > 1 ? "/$rootName/" : '/'); } ksort($treeMenu); @@ -316,7 +319,8 @@ class treeModel extends model $createdVersion = $this->dao->select('openedVersion')->from(TABLE_PROJECT) ->where('id')->eq($rootID) ->fetch('openedVersion'); - $products = $this->loadModel('product')->getProductsByProject($rootID); + $products = $this->loadModel('product')->getProductsByProject($rootID); + $productGroups = $this->dao->select('product,branch')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($rootID)->fetchGroup('product', 'branch'); if(!$createdVersion or version_compare($createdVersion, '4.1', '<=') or !$products) { $extra['tip'] = false; @@ -356,7 +360,7 @@ class treeModel extends model /* tree menu. */ $treeMenu = array(); - $query = $this->dao->select('*')->from(TABLE_MODULE)->where("((root = $rootID and type = 'task' and parent != 0) OR (root = $id and type = 'story'))") + $query = $this->dao->select('*')->from(TABLE_MODULE)->where("((root = $rootID and type = 'task' and parent != 0) OR (root = $id and type = 'story' and branch in(0," . (isset($productGroups[$id]) ? join(',', array_keys($productGroups[$id])) : 0) . ")))") ->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi() ->orderBy('grade desc, type, `order`') ->get(); @@ -436,6 +440,7 @@ class treeModel extends model { /* if not manage, ignore unused modules. */ if(!isset($projectModules[$module->id])) continue; + if(!isset($projectModules[$module->id])) continue; $this->buildTree($treeMenu, $module, 'task', $userFunc, $extra); } @@ -518,11 +523,18 @@ class treeModel extends model } else { - $products = $this->dao->select('product')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchPairs(); - $paths = $this->dao->select('id')->from(TABLE_MODULE) - ->where('root')->in($products) + $productGroups = $this->dao->select('product,branch')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchGroup('product', 'branch'); + $modules = $this->dao->select('id,root,branch')->from(TABLE_MODULE) + ->where('root')->in(array_keys($productGroups)) ->andWhere('type')->eq('story') - ->fetchPairs(); + ->fetchAll(); + + $paths = array(); + foreach($modules as $module) + { + if(empty($module->branch)) $paths[$module->id] = $module->id; + if(isset($productGroups[$module->root][$module->branch])) $paths[$module->id] = $module->id; + } } /* Add task paths of this project.*/ diff --git a/module/tree/view/browsetask.html.php b/module/tree/view/browsetask.html.php index 6825e7e9a1..97049baf4e 100644 --- a/module/tree/view/browsetask.html.php +++ b/module/tree/view/browsetask.html.php @@ -60,9 +60,9 @@ $maxOrder = 0; if($newModule and !$productID) { - foreach($products as $id => $product) + foreach($products as $product) { - echo '' . html::input("products[id$id]", $product, 'class=form-control disabled="true"') . ''; + echo '' . html::input("products[id$product->id]", $product->name, 'class=form-control disabled="true"') . ''; } } foreach($sons as $sonModule)