diff --git a/module/kanban/model.php b/module/kanban/model.php index d29ea2588f..ee6580f9be 100644 --- a/module/kanban/model.php +++ b/module/kanban/model.php @@ -537,7 +537,7 @@ class kanbanModel extends model $branches = array(); $colorIndex = 0; $laneOrder = 1; - $cardActions = array('view'); + $cardActions = array('view', 'createExecution', 'linkStory', 'linkBug', 'edit', 'start', 'finish', 'close', 'activate'); if($product->type == 'normal') { @@ -573,7 +573,12 @@ class kanbanModel extends model $plan->actions = array(); foreach($cardActions as $action) { - if(common::hasPriv('productplan', $action)) $plan->actions[] = $action; + if($action == 'createExecution') + { + if(common::hasPriv('execution', 'create')) $plan->actions[] = $action; + continue; + } + if($this->productplan->isClickable($plan, $action)) $plan->actions[] = $action; } $planList[$plan->status][] = $plan; } diff --git a/module/productplan/control.php b/module/productplan/control.php index 035d61cbea..56e9bd455a 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -252,8 +252,12 @@ class productplan extends control $viewType = $this->cookie->viewType ? $this->cookie->viewType : 'list'; $this->commonAction($productID, $branch); - $product = $this->product->getById($productID); - $productName = empty($product) ? '' : $product->name; + $product = $this->product->getById($productID); + $productName = empty($product) ? '' : $product->name; + $branchList = $this->branch->getList($productID, 0, 'all'); + $branchStatusList = array(); + foreach($branchList as $productBranch) $branchStatusList[$productBranch->id] = $productBranch->status; + if($viewType == 'kanban') { $branches = array(); @@ -285,17 +289,18 @@ class productplan extends control $this->view->kanbanData = $this->loadModel('kanban')->getPlanKanban($product, $branchID, $planGroup); } - $this->view->title = $productName . $this->lang->colon . $this->lang->productplan->browse; - $this->view->position[] = $this->lang->productplan->browse; - $this->view->productID = $productID; - $this->view->branch = $branch; - $this->view->browseType = $browseType; - $this->view->viewType = $viewType; - $this->view->orderBy = $orderBy; - $this->view->plans = $this->productplan->getList($productID, $branch, $browseType, $pager, $sort); - $this->view->pager = $pager; - $this->view->projects = $this->product->getProjectPairsByProduct($productID, $branch); - $this->view->statusList = $this->lang->productplan->featureBar['browse']; + $this->view->title = $productName . $this->lang->colon . $this->lang->productplan->browse; + $this->view->position[] = $this->lang->productplan->browse; + $this->view->productID = $productID; + $this->view->branch = $branch; + $this->view->branchStatusList = $branchStatusList; + $this->view->browseType = $browseType; + $this->view->viewType = $viewType; + $this->view->orderBy = $orderBy; + $this->view->plans = $this->productplan->getList($productID, $branch, $browseType, $pager, $sort); + $this->view->pager = $pager; + $this->view->projects = $this->product->getProjectPairsByProduct($productID, $branch); + $this->view->statusList = $this->lang->productplan->featureBar['browse']; $this->display(); } diff --git a/module/productplan/js/browse.js b/module/productplan/js/browse.js index 4e8afc1570..a114e35ecb 100644 --- a/module/productplan/js/browse.js +++ b/module/productplan/js/browse.js @@ -75,9 +75,94 @@ $(function() { $.zui.ContextMenu.hide(); }); + + /* Init contextmenu */ + $('#kanban').on('click', '[data-contextmenu]', function(event) + { + var $trigger = $(this); + var menuType = $trigger.data('contextmenu'); + var menuCreator = window.menuCreators[menuType]; + if(!menuCreator) return; + + var options = $.extend({event: event, $trigger: $trigger}, $trigger.data()); + var items = menuCreator(options); + if(!items || !items.length) return; + + $.zui.ContextMenu.show(items, items.$options || {event: event}); + }); } }); +/* Define menu creators */ +window.menuCreators = +{ + card: createCardMenu +}; + +/** + * Create card menu. + * + * @param object $options + * @access public + * @return array + */ +function createCardMenu(options) +{ + var card = options.$trigger.closest('.kanban-item').data('item'); + var privs = card.actions; + if(!privs.length) return []; + + var items = []; + if(privs.includes('createExecution')) + { + var className = ''; + var executionLink = systemMode == 'new' ? '#projects' : createLink('execution', 'create', "projectID=0&executionID=0©ExecutionID=0&plan=" + card.id + "&confirm=no&productID=" + productID); + var today = new Date(); + var end = $.zui.createDate(card.end); + if(end.toLocaleDateString() < today.toLocaleDateString() && (card.status == 'wait' || card.status == 'doing')) + { + className = 'disabled'; + } + else if(product.type != 'normal') + { + var branchStatus = branchStatusList[card.branch]; + if(branchStatus == 'closed') className = 'disabled'; + } + + if(systemMode == 'new') + { + items.push({label: productplanLang.createExecution, icon: 'plus', url: executionLink, className: className, attrs: {'data-toggle': 'modal', 'onclick': 'getPlanID(this,' + card.branch + ')', 'data-id': card.id}}); + } + else + { + items.push({label: productplanLang.createExecution, icon: 'plus', url: executionLink, className: className}); + } + } + + if(privs.includes('linkStory')) items.push({label: productplanLang.linkStory, icon: 'link', url: createLink('productplan', 'view', "planID=" + card.id + "&type=story&orderBy=id_desc&link=true")}); + if(privs.includes('linkBug')) items.push({label: productplanLang.linkBug, icon: 'bug', url: createLink('productplan', 'view', "planID=" + card.id + "&type=bug&orderBy=id_desc&link=true")}); + if(privs.includes('edit')) items.push({label: productplanLang.edit, icon: 'edit', url: createLink('productplan', 'edit', "planID=" + card.id)}); + if(privs.includes('start')) + { + if(card.begin == '2030-01-01' || card.end == '2030-01-01') + { + items.push({label: productplanLang.start, icon: 'start', url: createLink('productplan', 'start', "planID=" + card.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-id': card.id, 'data-width': '550px'}}); + } + else + { + items.push({label: productplanLang.start, icon: 'start', url: createLink('productplan', 'start', "planID=" + card.id), attrs: {'target': 'hiddenwin'}}); + } + } + + if(privs.includes('finish')) items.push({label: productplanLang.finish, icon: 'checked', url: createLink('productplan', 'finish', "planID=" + card.id), attrs: {'target': 'hiddenwin'}}); + if(privs.includes('close')) items.push({label: productplanLang.close, icon: 'off', url: createLink('productplan', 'close', "planID=" + card.id), attrs: {'target': 'hiddenwin'}}); + if(privs.includes('activate')) items.push({label: productplanLang.activate, icon: 'magic', url: createLink('productplan', 'activate', "planID=" + card.id), attrs: {'target': 'hiddenwin'}}); + + var bounds = options.$trigger[0].getBoundingClientRect(); + items.$options = {x: bounds.right, y: bounds.top}; + return items; +} + $(document).on('click', 'td.content .more', function(e) { var $toggle = $(this); @@ -168,7 +253,7 @@ function changeCardColType(card, fromColType, toColType, kanbanID) function renderKanbanItem(item, $item) { var privs = item.actions; - var printMoreBtn = privs.includes('view'); + var printMoreBtn = (privs.includes('createExecution') || privs.includes('linkStory') || privs.includes('linkBug') || privs.includes('edit') || privs.includes('start') || privs.includes('finish') || privs.includes('close') || privs.includes('activate')); /* Output header information. */ var $header = $item.children('.header'); diff --git a/module/productplan/view/browse.html.php b/module/productplan/view/browse.html.php index d29e415c8f..c206d4b389 100644 --- a/module/productplan/view/browse.html.php +++ b/module/productplan/view/browse.html.php @@ -18,6 +18,9 @@ productplan->enterProjectList);?> productplan->projectNotEmpty)?> + +systemMode);?> + +