Merge branch 'master' into 'sprint/181_zhengrunyu_fixbug'

# Conflicts:
#   module/productplan/model.php
This commit is contained in:
孙广明
2021-12-30 08:48:11 +00:00
28 changed files with 485 additions and 145 deletions
+4
View File
@@ -707,6 +707,10 @@ class actionModel extends model
{
$desc = $this->lang->$objectType->action->rejectreviewed;
}
elseif($action->objectType == 'productplan' and in_array($action->action, array('startedbychild','finishedbychild','closedbychild','activatedbychild')))
{
$desc = $this->lang->$objectType->action->changebychild;
}
elseif(isset($this->config->maxVersion) and strpos($this->config->action->assetType, $action->objectType) !== false and $action->action == 'approved')
{
$desc = empty($this->lang->action->approve->{$action->extra}) ? '' : $this->lang->action->approve->{$action->extra};
+1 -1
View File
@@ -49,7 +49,7 @@ $sessionString .= session_name() . '=' . session_id();
if($canImportToPracticeLib or $canImportToComponentLib)
{
echo "<div class='btn-group' id='more'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
echo html::a('javascript:;', "<i class='icon icon-diamond'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
echo "<ul class='dropdown-menu pull-right'>";
if($canImportToPracticeLib) echo '<li>' . html::a('#importToPracticeLib', $lang->doc->importToPracticeLib, '', 'data-toggle="modal"') . '</li>';
if($canImportToComponentLib) echo '<li>' . html::a('#importToComponentLib', $lang->doc->importToComponentLib, '', 'data-toggle="modal"') . '</li>';
+7 -2
View File
@@ -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', 'delete');
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;
}
+1
View File
@@ -1078,6 +1078,7 @@ class my extends control
$this->session->set('opportunityLibList', $uri, 'assetlib');
$this->session->set('practiceLibList', $uri, 'assetlib');
$this->session->set('componentLibList', $uri, 'assetlib');
$this->session->set('opportunityList', $uri, 'project');
/* Set the pager. */
$this->app->loadClass('pager', $static = true);
+1
View File
@@ -10,5 +10,6 @@ $config->productplan->start->requiredFields = 'begin,end';
$config->productplan->editor = new stdclass();
$config->productplan->editor->create = array('id' => 'desc', 'tools' => 'simpleTools');
$config->productplan->editor->edit = array('id' => 'desc', 'tools' => 'simpleTools');
$config->productplan->editor->start = array('id' => 'desc', 'tools' => 'simpleTools');
$config->productplan->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26', '#FFC20E', '#00A78E', '#7FBB00', '#424BAC', '#C0E9FF', '#EC2761');
+20 -29
View File
@@ -68,7 +68,7 @@ class productplan extends control
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $planID));
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => 'parent.refreshPlan()'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('productplan', 'browse', "productID=$productID&branch=$branchID")));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('productplan', 'browse', "productID=$productID&branch=$branchID&browseType=wait")));
}
$this->commonAction($productID, $branchID);
@@ -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();
}
@@ -403,9 +408,6 @@ class productplan extends control
$changes = $this->productplan->updateStatus($planID, 'doing');
if(dao::isError()) die(js::error(dao::getError()));
$actionID = $this->loadModel('action')->create('productplan', $planID, 'started');
$this->action->logHistory($actionID, $changes);
die(js::reload('parent'));
}
}
@@ -416,11 +418,9 @@ class productplan extends control
$changes = $this->productplan->updateStatus($planID, 'doing');
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$actionID = $this->loadModel('action')->create('productplan', $planID, 'started');
$this->action->logHistory($actionID, $changes);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess,'locate' => 'parent'));
}
$this->view->plan = $plan;
$this->display();
}
@@ -445,9 +445,6 @@ class productplan extends control
$changes = $this->productplan->updateStatus($planID, 'done');
if(dao::isError()) die(js::error(dao::getError()));
$actionID = $this->loadModel('action')->create('productplan', $planID, 'finished');
$this->action->logHistory($actionID, $changes);
die(js::reload('parent'));
}
}
@@ -471,9 +468,6 @@ class productplan extends control
$changes = $this->productplan->updateStatus($planID, 'closed');
if(dao::isError()) die(js::error(dao::getError()));
$actionID = $this->loadModel('action')->create('productplan', $planID, 'closed');
$this->action->logHistory($actionID, $changes);
die(js::reload('parent'));
}
}
@@ -497,9 +491,6 @@ class productplan extends control
$changes = $this->productplan->updateStatus($planID, 'doing');
if(dao::isError()) die(js::error(dao::getError()));
$actionID = $this->loadModel('action')->create('productplan', $planID, 'activated');
$this->action->logHistory($actionID, $changes);
die(js::reload('parent'));
}
}
+7 -5
View File
@@ -17,16 +17,18 @@ td.c-branch {overflow: hidden; text-align: left !important; text-overflow: ellip
.c-title {width: 160px;}
.c-branch {width: 100px;}
.c-story {width: 80px;}
.c-execution {width: 120px;}
.c-story, .c-status {width: 80px;}
.c-execution {width: 50px !important;}
.c-bug, .c-hour {width: 60px;}
.plan-name {position: relative; display: flex; align-items: center;}
.plan-name > span {flex: none;}
.c-title.has-child > .plan-name.has-suffix > a, .c-title.has-child > .plan-name.has-prefix > a {overflow:hidden; max-width: calc(100% - 70px);}
.plan-name.has-suffix > a, .plan-name.has-prefix > a {overflow:hidden; max-width: calc(100% - 46px);}
#productplanList .plan-name > .label.label-danger {margin-left:3px; }
#productplanList .c-title.has-child > .plan-name.expired > a {overflow:hidden; max-width: calc(100% - 62px);}
#productplanList .c-title.has-child > .plan-name > a {overflow:hidden; max-width: calc(100% - 16px);}
#productplanList .c-title > .plan-name.expired > a {overflow:hidden; max-width: calc(100% - 46px);}
#productplanList .plan-name.expired > .label.label-danger {margin-left:3px; }
#productplanList .plan-name > .label.label-light {margin-right:3px; }
.switchButton {background: #fff !important;}
.panel-actions {position: relative; padding: 0 0; padding-top: 1px;}
+177 -7
View File
@@ -75,9 +75,105 @@ $(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});
});
}
/* Hide contextmenu when page scroll */
$(window).on('scroll', function()
{
$.zui.ContextMenu.hide();
});
});
/* 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&copyExecutionID=0&plan=" + card.id + "&confirm=no&productID=" + productID);
var today = new Date();
var end = $.zui.createDate(card.end);
if(end.toLocaleDateString() < today.toLocaleDateString())
{
className = 'disabled';
}
else if(card.status == 'done' || card.status == 'closed')
{
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'}});
if(privs.includes('delete')) items.push({label: productplanLang.delete, icon: 'trash', url: createLink('productplan', 'delete', "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);
@@ -107,8 +203,7 @@ function findDropColumns($element, $root)
var $col = $element.closest('.kanban-col');
var col = $col.data();
var lane = $col.closest('.kanban-lane').data();
var kanbanID = $root.data('id');
var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules[kanbanID] : null;
var kanbanRules = window.kanbanDropRules ? window.kanbanDropRules : null;
if(!kanbanRules) return $root.find('.kanban-lane[data-id="' + lane.id + '"] .kanban-lane-col:not([data-type="project"],[data-type="' + col.type + '"])');
@@ -160,6 +255,50 @@ function handleFinishDrop(event)
function changeCardColType(card, fromColType, toColType, kanbanID)
{
if(typeof card == 'undefined') return false;
var objectID = card.id;
var privs = card.actions;
var showIframe = false;
var link = '';
if(toColType == 'doing')
{
if(fromColType == 'wait' && privs.includes('start'))
{
var link = createLink('productplan', 'start', 'planID=' + objectID, '', true);
showIframe = true;
}
else if((fromColType == 'done' || fromColType == 'closed') && privs.includes('activate'))
{
var link = createLink('productplan', 'activate', 'planID=' + objectID, '', true);
showIframe = false;
}
}
else if(toColType == 'done')
{
if(fromColType == 'doing')
{
var link = createLink('productplan', 'finish', 'planID=' + objectID, '', true);
showIframe = false;
}
}
else if(toColType == 'closed')
{
if(fromColType == 'done')
{
var link = createLink('productplan', 'close', 'planID=' + objectID);
showIframe = false;
}
}
if(showIframe)
{
var modalTrigger = new $.zui.ModalTrigger({type: 'iframe', width: '50%', url: link});
modalTrigger.show();
}
else if(!showIframe && link)
{
hiddenwin.location.href = link;
}
}
/**
@@ -167,22 +306,30 @@ function changeCardColType(card, fromColType, toColType, kanbanID)
*/
function renderKanbanItem(item, $item)
{
var privs = item.actions;
var $titleBox = $item.children('.titleBox');
var privs = item.actions;
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') || privs.includes('delete'));
/* Output header information. */
var $header = $item.children('.header');
if(!$header.length) $header = $(
[
'<div class="header">',
'</div>'
].join('')).appendTo($item);
var $titleBox = $item.children('.titleBox');
if(!$titleBox.length) $titleBox = $(
[
'<div class="titleBox">',
'</div>'
].join('')).appendTo($item);
].join('')).appendTo($header);
/* Print plan name. */
var $title = $titleBox.children('.title');
if(!$title.length)
{
if(privs.includes('view')) $title = $('<a class="title"></a>').appendTo($titleBox).attr('href', createLink('productplan', 'view', 'cardID=' + item.id));
if(!privs.includes('view')) $title = $('<p class="title"></p>').appendTo($titleBox);
if(!privs.includes('view')) $title = $('<a class="title"></a>').appendTo($titleBox);
}
$title.text(item.title).attr('title', item.title);
@@ -190,7 +337,7 @@ function renderKanbanItem(item, $item)
var today = new Date();
var begin = $.zui.createDate(item.begin);
var end = $.zui.createDate(item.end);
if(end.toLocaleDateString() < today.toLocaleDateString())
if(end.toLocaleDateString() < today.toLocaleDateString() && (item.status == 'wait' || item.status == 'doing'))
{
$expired = $titleBox.children('.expired');
if(!$expired.length)
@@ -199,6 +346,18 @@ function renderKanbanItem(item, $item)
}
}
if(printMoreBtn)
{
$(
[
'<div class="actions" title="' + productplanLang.more + '">',
'<a data-contextmenu="card" data-id="' + item.id + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($header);
}
/* Output plan date information. */
var $dateBox = $item.children('.dateBox');
if(!$dateBox.length) $dateBox = $(
@@ -263,3 +422,14 @@ function getPlanID(obj, branch)
}
});
}
if(!window.kanbanDropRules)
{
window.kanbanDropRules =
{
wait: ['doing'],
doing: ['done'],
done: ['doing', 'closed'],
closed: ['doing']
}
}
+11 -1
View File
@@ -18,12 +18,13 @@ $lang->productplan->delete = "Delete Plan";
$lang->productplan->start = "Start Plan";
$lang->productplan->finish = "Finish Plan";
$lang->productplan->close = "Close Plan";
$lang->productplan->activate = "Activate Plan";
$lang->productplan->startAB = "Start";
$lang->productplan->finishAB = "Finish";
$lang->productplan->closeAB = "Close";
$lang->productplan->activateAB = "Activate";
$lang->productplan->view = "Plan Detail";
$lang->productplan->activate = "Activate Plan";
$lang->productplan->view = "View";
$lang->productplan->bugSummary = "Total <strong>%s</strong> Bugs on this page.";
$lang->productplan->basicInfo = 'Basic Info';
$lang->productplan->batchEdit = 'Batch Edit';
@@ -31,6 +32,7 @@ $lang->productplan->project = 'Project';
$lang->productplan->plan = 'Plan';
$lang->productplan->allAB = 'All';
$lang->productplan->to = 'To';
$lang->productplan->more = 'More';
$lang->productplan->batchUnlink = "Batch Unlink";
$lang->productplan->unlinkAB = "Unlink";
@@ -96,6 +98,11 @@ $lang->productplan->statusList['doing'] = 'Doing';
$lang->productplan->statusList['done'] = 'Done';
$lang->productplan->statusList['closed'] = 'Closed';
$lang->productplan->parentActionList['startedbychild'] = '<strong>starting </strong>the productplan sets the plan status as <strong>Doing</strong>.';
$lang->productplan->parentActionList['finishedbychild'] = '<strong>finishing </strong>all productplan sets the plan status as <strong>Done</strong>.';
$lang->productplan->parentActionList['closedbychild'] = '<strong>closing </strong>all productplan sets the plan status as <strong>Closed</strong>.';
$lang->productplan->parentActionList['activatedbychild'] = '<strong>activating </strong>the productplan sets the plan status as <strong>Doing</strong>.';
$lang->productplan->endList[7] = '1 Week';
$lang->productplan->endList[14] = '2 Weeks';
$lang->productplan->endList[31] = '1 Month';
@@ -120,3 +127,6 @@ $lang->productplan->featureBar['browse']['wait'] = 'Waiting';
$lang->productplan->featureBar['browse']['doing'] = 'Doing';
$lang->productplan->featureBar['browse']['done'] = 'Done';
$lang->productplan->featureBar['browse']['closed'] = 'Closed';
$lang->productplan->action = new stdclass();
$lang->productplan->action->changebychild = array('main' => '$date, $extra', 'extra' => 'parentActionList');
+9
View File
@@ -31,6 +31,7 @@ $lang->productplan->project = '项目';
$lang->productplan->plan = '计划';
$lang->productplan->allAB = '所有';
$lang->productplan->to = '至';
$lang->productplan->more = '更多操作';
$lang->productplan->batchUnlink = "批量移除";
$lang->productplan->unlinkAB = "移除";
@@ -96,6 +97,11 @@ $lang->productplan->statusList['doing'] = '进行中';
$lang->productplan->statusList['done'] = '已完成';
$lang->productplan->statusList['closed'] = '已关闭';
$lang->productplan->parentActionList['startedbychild'] = '系统判断由于子计划 <strong>开始</strong> ,将计划状态置为 <strong>进行中</strong> 。';
$lang->productplan->parentActionList['finishedbychild'] = '系统判断由于子计划 <strong>全部完成</strong> ,将计划状态置为 <strong>已完成</strong> 。';
$lang->productplan->parentActionList['closedbychild'] = '系统判断由于子计划 <strong>全部关闭</strong> ,将计划状态置为 <strong>已关闭</strong> 。';
$lang->productplan->parentActionList['activatedbychild'] = '系统判断由于子计划 <strong>激活</strong> ,将计划状态置为 <strong>进行中</strong> 。';
$lang->productplan->endList[7] = '一星期';
$lang->productplan->endList[14] = '两星期';
$lang->productplan->endList[31] = '一个月';
@@ -120,3 +126,6 @@ $lang->productplan->featureBar['browse']['wait'] = '未开始';
$lang->productplan->featureBar['browse']['doing'] = '进行中';
$lang->productplan->featureBar['browse']['done'] = '已完成';
$lang->productplan->featureBar['browse']['closed'] = '已关闭';
$lang->productplan->action = new stdclass();
$lang->productplan->action->changebychild = array('main' => '$date, $extra', 'extra' => 'parentActionList');
+63 -12
View File
@@ -520,14 +520,23 @@ class productplanModel extends model
{
$planID = (int)$planID;
$oldPlan = $this->getByID($planID);
if($oldPlan->parent > 0) $parentPlan = $this->getByID($oldPlan->parent);
if($oldPlan->parent > 0)
{
$parentPlan = $this->getByID($oldPlan->parent);
$parentChange = false;
}
if($status == 'doing' and !empty($_POST))
{
$plan = fixer::input('post')->add('status', $status)->get();
$plan = fixer::input('post')
->add('status', $status)
->stripTags($this->config->productplan->editor->start['id'], $this->config->allowedTags)
->remove('uid')
->get();
$this->checkDate4Plan($oldPlan, $plan->begin, $plan->end);
if(dao::isError()) return false;
$plan = $this->loadModel('file')->processImgURL($plan, $this->config->productplan->editor->start['id'], $this->post->uid);
$this->dao->update(TABLE_PRODUCTPLAN)
->data($plan)
@@ -535,8 +544,15 @@ class productplanModel extends model
->batchCheck($this->config->productplan->start->requiredFields, 'notempty')
->checkIF(!empty($_POST['begin']) && !empty($_POST['end']), 'end', 'ge', $plan->begin)
->where('id')->eq($planID)
->beginIF(isset($parentPlan))->orWhere('id')->eq($oldPlan->parent)->fi()
->exec();
$this->file->updateObjectID($this->post->uid, $planID, 'productplan');
if(isset($parentPlan) and $parentPlan->status != 'doing')
{
$this->dao->update(TABLE_PRODUCTPLAN)->set('status')->eq($status)->where('id')->eq($oldPlan->parent)->exec();
$parentChange = true;
}
}
elseif($status == 'doing' and isset($parentPlan) and $parentPlan->status != 'doing')
{
@@ -545,6 +561,7 @@ class productplanModel extends model
->where('id')->eq($planID)
->orWhere('id')->eq($oldPlan->parent)
->exec();
if($parentPlan->status != 'doing') $parentChange = true;
}
elseif($status == 'done' and isset($parentPlan))
{
@@ -558,6 +575,7 @@ class productplanModel extends model
break;
}
}
if($parentDone) $parentChange = true;
$this->dao->update(TABLE_PRODUCTPLAN)
->set('`status`')->eq($status)
->where('id')->eq($planID)
@@ -576,6 +594,7 @@ class productplanModel extends model
break;
}
}
if($parentClosed) $parentChange = true;
$this->dao->update(TABLE_PRODUCTPLAN)
->set('`status`')->eq($status)
->where('id')->eq($planID)
@@ -593,7 +612,39 @@ class productplanModel extends model
if(dao::isError()) return false;
if(!isset($plan)) $plan = $this->getByID($planID);
if(!dao::isError())return common::createChanges($oldPlan, $plan);
if(!dao::isError())
{
$method = $this->app->rawMethod;
if($method == 'start')
{
$action = 'started';
if(isset($parentChange) and $parentChange) $parentAction = 'startedbychild';
}
elseif($method == 'finish')
{
$action = 'finished';
if(isset($parentChange) and $parentChange) $parentAction = 'finishedbychild';
}
elseif($method == 'close')
{
$action = 'closed';
if(isset($parentChange) and $parentChange) $parentAction = 'closedbychild';
}
elseif($method == 'activate')
{
$action = 'activated';
if(isset($parentChange) and $parentChange) $parentAction = 'activatedbychild';
}
if(isset($action))
{
$changes = common::createChanges($oldPlan, $plan);
$actionID = $this->loadModel('action')->create('productplan', $planID, $action);
$this->action->loghistory($actionID, $changes);
if(isset($parentChange) and $parentChange) $actionID = $this->action->create('productplan', $oldPlan->parent, $parentAction, '', $parentAction);
}
}
}
/**
@@ -612,7 +663,7 @@ class productplanModel extends model
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($config);
$plans = array();
$extendFields = $this->getFlowExtendFields();
foreach($data->id as $planID)
@@ -689,17 +740,17 @@ class productplanModel extends model
$maxEnd = $end;
foreach($childPlans as $childPlan)
{
if($childPlan->begin < $minBegin) $minBegin = $childPlan->begin;
if($childPlan->end > $maxEnd) $maxEnd = $childPlan->end;
if($childPlan->begin < $minBegin and $minBegin != '2030-01-01') $minBegin = $childPlan->begin;
if($childPlan->end > $maxEnd and $maxEnd != '2030-01-01') $maxEnd = $childPlan->end;
}
if($minBegin < $begin) dao::$errors['begin'] = sprintf($this->lang->beginGreaterChild, $minBegin);
if($maxEnd > $end) dao::$errors['end'] = sprintf($this->lang->endLetterChild, $maxEnd);
if($minBegin < $begin and $begin != '2030-01-01') dao::$errors['begin'] = sprintf($this->lang->beginGreaterChild, $minBegin);
if($maxEnd > $end and $end != '2030-01-01') dao::$errors['end'] = sprintf($this->lang->endLetterChild, $maxEnd);
}
elseif($plan->parent > 0)
{
$parentPlan = $this->getByID($plan->parent);
if($begin < $parentPlan->begin) dao::$errors['begin'] = sprintf($this->lang->productplan->beginLetterParent, $parentPlan->begin);
if($end > $parentPlan->end) dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $parentPlan->end);
if($begin < $parentPlan->begin and $parentPlan->begin != '2030-01-01') dao::$errors['begin'] = sprintf($this->lang->productplan->beginLetterParent, $parentPlan->begin);
if($end > $parentPlan->end and $parentPlan->end != '2030-01-01') dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $parentPlan->end);
}
}
@@ -929,7 +980,7 @@ class productplanModel extends model
if($plan->status != 'done' or $plan->parent < 0) return false;
break;
case 'activate' :
if($plan->status != 'closed' or $plan->parent < 0) return false;
if($plan->status == 'wait' or $plan == 'doing' or $plan->parent < 0) return false;
break;
}
+31
View File
@@ -18,6 +18,9 @@
<?php js::set('enterProjectList', $lang->productplan->enterProjectList);?>
<?php js::set('projectNotEmpty', $lang->productplan->projectNotEmpty)?>
<?php js::set('viewType', $viewType);?>
<?php js::set('product', $product);?>
<?php js::set('systemMode', $config->systemMode);?>
<?php js::set('branchStatusList', $branchStatusList);?>
<?php
if($viewType == 'kanban')
{
@@ -28,4 +31,32 @@ else
include 'browsebylist.html.php';
}
?>
<div class="modal fade" id="projects">
<div class="modal-dialog mw-500px">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"><?php echo $lang->productplan->selectProjects;?></h4>
</div>
<div class="modal-body">
<table class='table table-form'>
<tr>
<th><?php echo $lang->productplan->project?></th>
<td><?php echo html::select('project', $projects, '', "class='form-control chosen'");?></td>
</tr>
<tr class='tips hidden'>
<th></th>
<td><span class='text-red'><?php echo $lang->productplan->noLinkedProject;?></span></td>
</tr>
<tr>
<td colspan='2' class='text-center'>
<?php echo html::hidden('planID', '');?>
<?php echo html::commonButton($lang->productplan->nextStep, "id='createExecutionButton'", 'btn btn-primary btn-wide');?>
<?php echo html::commonButton($lang->cancel, "data-dismiss='modal'", 'btn btn-default btn-wide');?>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
@@ -18,13 +18,19 @@
#branchBox {width: 120px;}
#branch_chosen .icon-delay {padding-right: 10px; font-size: 15px;}
#kanbanContainer {padding-bottom: 0; margin-bottom: 0;}
.kanban-item:hover > .header > .actions {opacity: 1;}
.kanban-card {height: 80px !important;}
.kanban-card .titleBox {width: 100%; display: flex;}
.kanban-card .titleBox > span {flex: none;}
.kanban-card .header .titleBox {display: flex; width: 90%; float: left;}
.kanban-card .header .titleBox > span {flex: none;}
.kanban-card .header .actions {float: right; opacity: 0;}
.kanban-card .header .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-card:hover > .header > .actions {opacity: 1;}
.kanban-card .title {white-space: nowrap; overflow: hidden;}
.kanban-card .expired {margin-left: 2px;}
.kanban-card .dateBox {padding-top: 5px;}
.kanban-card .dateBox {padding-top: 22px;}
.kanban-card .desc {overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #838a9d; padding-top: 5px;}
.kanban-card .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.dropdown-menu > li > .disabled {pointer-events: none; color: #838a9d;}
</style>
<?php js::set('kanbanData', $kanbanData);?>
<?php js::set('productplanLang', $lang->productplan);?>
+30 -42
View File
@@ -57,7 +57,7 @@
</th>
<th class='c-title'><?php common::printOrderLink('title', $orderBy, $vars, $lang->productplan->title);?></th>
<?php if($browseType == 'all'):?>
<th><?php common::printOrderLink('status', $orderBy, $vars, $lang->productplan->status);?></th>
<th class='c-status'><?php common::printOrderLink('status', $orderBy, $vars, $lang->productplan->status);?></th>
<?php endif;?>
<?php if($this->session->currentProductType != 'normal'):?>
<th class='c-branch'><?php common::printOrderLink('branch',$orderBy, $vars, $lang->productplan->branch);?></th>
@@ -78,6 +78,7 @@
</thead>
<tbody>
<?php $this->loadModel('file');?>
<?php $today = date('Y-m-d');?>
<?php foreach($plans as $plan):?>
<?php
$canBeChanged = common::canBeChanged('plan', $plan);
@@ -110,14 +111,22 @@
<?php echo sprintf('%03d', $plan->id);?>
<?php endif;?>
</td>
<td class='c-title text-left<?php if($plan->parent == '-1') echo ' has-child';?>' title="<?php echo $plan->title?>">
<td class='c-title text-left<?php if(isset($plan->children)) echo ' has-child';?>' title="<?php echo $plan->title?>">
<?php
$suffix = '';
if($plan->end < helper::now()) $suffix = "<span class='label label-danger label-badge'>{$this->lang->productplan->expired}</span>";
if($plan->parent == '-1') $suffix .= '<a class="task-toggle" data-id="' . $plan->id . '"><i class="icon icon-angle-double-right"></i></a>';
if(!empty($suffix)) echo '<div class="plan-name has-prefix has-suffix">';
$class = '';
$expired = '';
if($plan->end < $today and in_array($plan->status, array('wait', 'doing')))
{
$class .= ' expired';
$expired = "<span class='label label-danger label-badge'>{$this->lang->productplan->expired}</span>";
}
echo "<div class='plan-name has-prefix {$class}'>";
if($plan->parent > 0 and !isset($plans[$plan->parent])) echo "<span class='label label-badge label-light' title='{$this->lang->productplan->children}'>{$this->lang->productplan->childrenAB}</span>";
echo html::a(inlink('view', "id=$plan->id"), $plan->title);
if(!empty($suffix)) echo $suffix . '</div>';
if(!empty($expired)) echo $expired;
if(isset($plan->children)) echo '<a class="task-toggle" data-id="' . $plan->id . '"><i class="icon icon-angle-double-right"></i></a>';
echo '</div>';
?>
</td>
<?php if($browseType == 'all'):?>
@@ -147,12 +156,12 @@
if($plan->begin == '2030-01-01' or $plan->end == '2030-01-01')
{
$class = 'iframe';
$attr = "data-toggle='modal' data-id='{$plan->id}' data-width='550px'";
$attr = "data-id='{$plan->id}' data-width='550px'";
$isOnlyBody = true;
}
common::printIcon('productplan', 'start', "planID=$plan->id", $plan, 'list', 'play', '', $class, $isOnlyBody, $attr, $lang->productplan->start);
common::printIcon('productplan', 'finish', "planID=$plan->id", $plan, 'list', 'checked', '', $class, false, $attr, $lang->productplan->finish);
common::printIcon('productplan', 'close', "planID=$plan->id", $plan, 'list', 'off', '', $class, false, $attr, $lang->productplan->close);
common::printIcon('productplan', 'start', "planID=$plan->id", $plan, 'list', 'play', '', $class, $isOnlyBody);
common::printIcon('productplan', 'finish', "planID=$plan->id", $plan, 'list', 'checked', '', $class, false, $attr);
common::printIcon('productplan', 'close', "planID=$plan->id", $plan, 'list', 'off', '', $class, false, $attr);
}
$attr = $plan->expired ? "disabled='disabled'" : '';
@@ -163,9 +172,10 @@
if($product->type != 'normal')
{
$branchStatus = $this->branch->getByID($plan->branch, 0, 'status');
$branchStatus = isset($branchStatusList[$plan->branch]) ? $branchStatusList[$plan->branch] : '';
if($branchStatus == 'closed') $disabled = 'disabled';
}
if(in_array($plan->status, array('done', 'closed'))) $disabled = 'disabled';
if($config->systemMode == 'new')
{
@@ -181,8 +191,14 @@
common::printIcon('productplan', 'edit', "planID=$plan->id", $plan, 'list');
if(common::hasPriv('productplan', 'create', $plan))
{
if($plan->parent > '0') echo "<button type='button' class='disabled btn'><i class='disabled icon-split' title='{$this->lang->productplan->children}'></i></button> ";
if($plan->parent <= '0') echo html::a($this->createLink('productplan', 'create', "product=$productID&branch=$branch&parent={$plan->id}"), "<i class='icon-split'></i>", '', "class='btn' title='{$this->lang->productplan->children}'");
if($plan->parent > 0 or in_array($plan->status, array('done', 'closed')))
{
echo "<button type='button' class='disabled btn'><i class='disabled icon-split' title='{$this->lang->productplan->children}'></i></button> ";
}
else
{
echo html::a($this->createLink('productplan', 'create', "product=$productID&branch=$branch&parent={$plan->id}"), "<i class='icon-split'></i>", '', "class='btn' title='{$this->lang->productplan->children}'");
}
}
if(common::hasPriv('productplan', 'delete', $plan))
@@ -214,31 +230,3 @@
</form>
<?php endif;?>
</div>
<div class="modal fade" id="projects">
<div class="modal-dialog mw-500px">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title"><?php echo $lang->productplan->selectProjects;?></h4>
</div>
<div class="modal-body">
<table class='table table-form'>
<tr>
<th><?php echo $lang->productplan->project?></th>
<td><?php echo html::select('project', $projects, '', "class='form-control chosen'");?></td>
</tr>
<tr class='tips hidden'>
<th></th>
<td><span class='text-red'><?php echo $lang->productplan->noLinkedProject;?></span></td>
</tr>
<tr>
<td colspan='2' class='text-center'>
<?php echo html::hidden('planID', '');?>
<?php echo html::commonButton($lang->productplan->nextStep, "id='createExecutionButton'", 'btn btn-primary btn-wide');?>
<?php echo html::commonButton($lang->cancel, "data-dismiss='modal'", 'btn btn-default btn-wide');?>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
+6 -5
View File
@@ -10,11 +10,8 @@
* @link https://www.zentao.net
*/
?>
<style>
.table-form {margin-top: 90px;}
.form-actions {height:160px;}
</style>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
@@ -31,7 +28,11 @@
<td colspan='2'><?php echo html::input('end', $plan->end != '2030-01-01' ? formatTime($plan->end) : '', 'class="form-control form-date"');?></td>
</tr>
<tr>
<td colspan='4' class='text-center form-actions'>
<th><?php echo $lang->productplan->desc;?></th>
<td colspan='4'><?php echo html::textarea('desc', $plan->desc, 'class="form-control"');?></td>
</tr>
<tr>
<td colspan='5' class='text-center form-actions'>
<?php echo html::submitButton($lang->productplan->start);?>
<?php echo html::commonButton($lang->cancel, "data-dismiss='modal'", 'btn btn-wide');?>
</td>
+3 -2
View File
@@ -59,11 +59,12 @@
common::printLink('productplan', 'finish', "planID=$plan->id", "<i class='icon-checked'></i>{$lang->productplan->finishAB}", '', "class='btn btn-link {$class}' target='hiddenwin' title='{$lang->productplan->finish}'", '', false, $plan);
$class = $plan->status == 'done' ? '' : 'disabled';
common::printLink('productplan', 'close', "planID=$plan->id", "<i class='icon-off'></i>{$lang->productplan->closeAB}", '', "class='btn btn-link {$class}' target='hiddenwin' title='{$lang->productplan->close}'", '', false, $plan);
$class = $plan->status == 'closed' ? '' : 'disabled';
$class = in_array($plan->status, array('closed', 'done')) ? '' : 'disabled';
common::printLink('productplan', 'activate', "planID=$plan->id", "<i class='icon-magic'></i>{$lang->productplan->activateAB}", '', "class='btn btn-link {$class}' target='hiddenwin' title='{$lang->productplan->activate}'", '', false, $plan);
}
if(common::hasPriv('productplan', 'create', $plan) and $plan->parent <= '0') echo html::a($this->createLink('productplan', 'create', "product={$plan->product}&branch={$plan->branch}&parent={$plan->id}"), "<i class='icon-split'></i> " . $this->lang->productplan->children , '', "class='btn btn-link' title='{$this->lang->productplan->children}'");
$class = in_array($plan->status, array('done', 'closed')) ? 'disabled' : '';
if(common::hasPriv('productplan', 'create', $plan) and $plan->parent <= 0) echo html::a($this->createLink('productplan', 'create', "product={$plan->product}&branch={$plan->branch}&parent={$plan->id}"), "<i class='icon-split'></i> " . $this->lang->productplan->children , '', "class='btn btn-link {$class}' title='{$this->lang->productplan->children}'");
if(common::hasPriv('productplan', 'edit', $plan)) echo html::a($this->createLink('productplan', 'edit', "planID=$plan->id"), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}'");
if(common::hasPriv('productplan', 'delete', $plan) and $plan->parent >= 0) echo html::a($this->createLink('productplan', 'delete', "planID=$plan->id"), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin'");
}
+1
View File
@@ -145,6 +145,7 @@ class programplan extends control
$this->view->plans = $this->programplan->getStage($planID ? $planID : $projectID, $this->productID, 'parent');
$this->view->planID = $planID;
$this->view->type = 'lists';
$this->view->PMUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst', $project->PM);
$this->display();
}
+2
View File
@@ -5,6 +5,8 @@ function addItem(obj)
var newItem = $('#names' + i).closest('tr');
newItem.find('.form-date').datepicker();
$("#output" + i).chosen();
$("#PM_i__chosen").remove();
$("#PM" + i).chosen();
$("#output_i__chosen").remove();
i ++;
}
+1
View File
@@ -21,6 +21,7 @@ $lang->programplan->createSubPlan = 'Create Sub Stage';
$lang->programplan->parent = 'Parent Stage';
$lang->programplan->emptyParent = 'N/A';
$lang->programplan->name = 'Stage Name';
$lang->programplan->PM = 'Stage Manager';
$lang->programplan->subStageName = 'Sub Stage Name';
$lang->programplan->percent = 'Workload Ratio';
$lang->programplan->percentAB = 'Ratio';
+1
View File
@@ -21,6 +21,7 @@ $lang->programplan->createSubPlan = '创建二级阶段';
$lang->programplan->parent = '父阶段';
$lang->programplan->emptyParent = '无';
$lang->programplan->name = '阶段名称';
$lang->programplan->PM = '阶段负责人';
$lang->programplan->subStageName = '子阶段名称';
$lang->programplan->percent = '工作量占比';
$lang->programplan->percentAB = '工作量占比';
+1 -2
View File
@@ -409,8 +409,6 @@ class programplanModel extends model
$parentACL = $parentStage->acl;
}
$attributes = array_values($attributes);
$milestone = array_values($milestone);
$datas = array();
foreach($names as $key => $name)
{
@@ -431,6 +429,7 @@ class programplanModel extends model
$plan->realEnd = empty($realEnd[$key]) ? '0000-00-00' : $realEnd[$key];
$plan->output = empty($output[$key]) ? '' : implode(',', $output[$key]);
$plan->acl = empty($parentID) ? $acl[$key] : $parentACL;
$plan->PM = empty($PM[$key]) ? '' : $PM[$key];
$datas[] = $plan;
}
+35 -30
View File
@@ -45,6 +45,7 @@
<thead>
<tr class='text-center'>
<th class='required'><?php echo $name;?></th>
<th class='w-110px'><?php echo $lang->programplan->PM;?></th>
<th class='w-110px'>
<?php echo $lang->programplan->percent;?>
<?php if($planID):?>
@@ -69,20 +70,21 @@
<?php if(empty($plans) and $planID == 0):?>
<?php foreach($stages as $stage):?>
<tr>
<td><input type='text' name='names[]' id='names<?php echo $i;?>' value='<?php echo $stage->name;?>' class='form-control' /></td>
<td><input type='text' name='names[<?php echo $i;?>]' id='names<?php echo $i;?>' value='<?php echo $stage->name;?>' class='form-control' /></td>
<td><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control chosen'");?></td>
<td>
<div class='input-group'>
<input type='text' name='percents[]' id='percent<?php echo $i;?>' value='<?php echo $stage->percent;?>' class='form-control'/>
<input type='text' name='percents[<?php echo $i;?>]' id='percent<?php echo $i;?>' value='<?php echo $stage->percent;?>' class='form-control'/>
<span class='input-group-addon'>%</span>
</div>
</td>
<td class='<?php echo $hideAttribute?>'><?php echo html::select("attributes[$i]", $lang->stage->typeList, $stage->type, "class='form-control'");?></td>
<td><?php echo html::select("acl[]", $lang->project->aclList, 'open', "class='form-control' $class");?></td>
<td><?php echo html::select("acl[$i]", $lang->project->aclList, 'open', "class='form-control' $class");?></td>
<td><?php echo html::radio("milestone[$i]", $lang->programplan->milestoneList, 0);?></td>
<td><input type='text' name='begin[]' id='begin<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='end[]' id='end<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[]' id='realBegan<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[]' id='realEnd<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='begin[<?php echo $i;?>]' id='begin<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='end[<?php echo $i;?>]' id='end<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[<?php echo $i;?>]' id='realBegan<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[<?php echo $i;?>]' id='realEnd<?php echo $i;?>' value='' class='form-control form-date' /></td>
<?php if(isset($this->config->qcVersion)):?>
<td><?php echo html::select("output[$i][]", $documentList, '', "class='form-control chosen' multiple");?></td>
<?php endif;?>
@@ -98,22 +100,23 @@
<?php if(!empty($plans)):?>
<?php foreach($plans as $plan):?>
<?php $disabled = $plan->setMilestone ? '' : "disabled='disabled'"?>
<?php echo html::hidden('planIDList[]', $plan->id);?>
<?php echo html::hidden("planIDList[$i]", $plan->id);?>
<tr>
<td><input type='text' name="names[]" id='names<?php echo $i;?>' value='<?php echo $plan->name;?>' class='form-control' /></td>
<td><input type='text' name="names[<?php echo $i;?>]" id='names<?php echo $i;?>' value='<?php echo $plan->name;?>' class='form-control' /></td>
<td><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control chosen'");?></td>
<td>
<div class='input-group'>
<input type='text' name='percents[]' id='percent<?php echo $i;?>' value='<?php echo $plan->percent;?>' class='form-control' />
<input type='text' name='percents[<?php echo $i;?>]' id='percent<?php echo $i;?>' value='<?php echo $plan->percent;?>' class='form-control' />
<span class='input-group-addon'>%</span>
</div>
</td>
<td class='<?php echo $hideAttribute?>'><?php echo html::select("attributes[$i]", $lang->stage->typeList, $plan->attribute, "class='form-control'");?></td>
<td><?php echo html::select("acl[]", $lang->project->aclList, $plan->acl, "class='form-control' $class");?></td>
<td><?php echo html::select("acl[$i]", $lang->project->aclList, $plan->acl, "class='form-control' $class");?></td>
<td><?php echo html::radio("milestone[$i]", $lang->programplan->milestoneList, $plan->milestone, $disabled);?></td>
<td><input type='text' name='begin[] ' id='begin<?php echo $i;?>' value='<?php echo $plan->begin;?>' class='form-control form-date' /></td>
<td><input type='text' name='end[]' id='end<?php echo $i;?>' value='<?php echo $plan->end;?>' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[] ' id='realBegan<?php echo $i;?>' value='<?php echo $plan->realBegan;?>' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[]' id='realEnd<?php echo $i;?>' value='<?php echo $plan->realEnd;?>' class='form-control form-date' /></td>
<td><input type='text' name='begin[<?php echo $i;?>] ' id='begin<?php echo $i;?>' value='<?php echo $plan->begin;?>' class='form-control form-date' /></td>
<td><input type='text' name='end[<?php echo $i;?>]' id='end<?php echo $i;?>' value='<?php echo $plan->end;?>' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[<?php echo $i;?>] ' id='realBegan<?php echo $i;?>' value='<?php echo $plan->realBegan;?>' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[<?php echo $i;?>]' id='realEnd<?php echo $i;?>' value='<?php echo $plan->realEnd;?>' class='form-control form-date' /></td>
<?php if(isset($this->config->qcVersion)):?>
<?php $option = empty($plan->output) ? 0 : explode(',', $plan->output);?>
<td><?php echo html::select("output[$i][]", $documentList, $option, "class='form-control chosen' multiple");?></td>
@@ -128,20 +131,21 @@
<?php for($j = 0; $j < 5; $j ++):?>
<tr class='addedItem'>
<td><input type='text' name='names[]' id='names<?php echo $i;?>' value='' class='form-control' /></td>
<td><input type='text' name='names[<?php echo $i;?>]' id='names<?php echo $i;?>' value='' class='form-control' /></td>
<td><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control chosen'");?></td>
<td>
<div class='input-group'>
<input type='text' name='percents[]' id='percent<?php echo $i;?>' value='' class='form-control' />
<input type='text' name='percents[<?php echo $i;?>]' id='percent<?php echo $i;?>' value='' class='form-control' />
<span class='input-group-addon'>%</span>
</div>
</td>
<td class='<?php echo $hideAttribute?>'><?php echo html::select("attributes[$i]", $lang->stage->typeList, '', "class='form-control'");?></td>
<td><?php echo html::select("acl[]", $lang->project->aclList, 'open', "class='form-control' $class");?></td>
<td><?php echo html::select("acl[$i]", $lang->project->aclList, 'open', "class='form-control' $class");?></td>
<td><?php echo html::radio("milestone[$i]", $lang->programplan->milestoneList, 0);?></td>
<td><input type='text' name='begin[] ' id='begin<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='end[]' id='end<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[] ' id='realBegan<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[]' id='realEnd<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='begin[<?php echo $i;?>] ' id='begin<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='end[<?php echo $i;?>]' id='end<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[<?php echo $i;?>] ' id='realBegan<?php echo $i;?>' value='' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[<?php echo $i;?>]' id='realEnd<?php echo $i;?>' value='' class='form-control form-date' /></td>
<?php if(isset($this->config->qcVersion)):?>
<td><?php echo html::select("output[$i][]", $documentList, '', "class='form-control chosen' multiple");?></td>
<?php endif;?>
@@ -167,21 +171,22 @@
<?php $i = '%i%';?>
<table class='hidden'>
<tr id='addItem' class='hidden'>
<td><input type='text' name='names[]' id='names<?php echo $i;?>' class='form-control' /></td>
<?php echo html::hidden('planIDList[]', 0);?>
<td><input type='text' name='<?php echo "names[$i]";?>' id='names<?php echo $i;?>' class='form-control' /></td>
<td><?php echo html::select("PM[$i]", $PMUsers, '', "class='form-control chosen' id='PM$i'");?></td>
<?php echo html::hidden("planIDList[$i]", 0);?>
<td>
<div class='input-group'>
<input type='text' name='percents[]' id='percent<?php echo $i;?>' class='form-control' />
<input type='text' name='<?php echo "percents[$i]";?>' id='percent<?php echo $i;?>' class='form-control' />
<span class='input-group-addon'>%</span>
</div>
</td>
<td class='<?php echo $hideAttribute?>'><?php echo html::select("attributes[$i]", $lang->stage->typeList, '', "class='form-control'");?></td>
<td><?php echo html::select("acl[]", $lang->project->aclList, 'open', "class='form-control' $class");?></td>
<td><?php echo html::select("acl[$i]", $lang->project->aclList, 'open', "class='form-control' $class");?></td>
<td><?php echo html::radio("milestone[$i]", $lang->programplan->milestoneList, 0);?></td>
<td><input type='text' name='begin[] ' id='begin<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='end[]' id='end<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='realBegan[] ' id='realBegan<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='realEnd[]' id='realEnd<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='<?php echo "begin[$i]";?>' id='begin<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='<?php echo "end[$i]";?>' id='end<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='<?php echo "realBegan[$i]";?>' id='realBegan<?php echo $i;?>' class='form-control form-date' /></td>
<td><input type='text' name='<?php echo "realEnd[$i]";?>' id='realEnd<?php echo $i;?>' class='form-control form-date' /></td>
<?php if(isset($this->config->qcVersion)):?>
<td><?php echo html::select("output[$i][]", $documentList, '', "class='form-control chosen ' multiple");?></td>
<?php endif;?>
+4
View File
@@ -129,3 +129,7 @@ $config->project->removePriv['project'] = array('index', 'browse', 'kanban', '
$config->project->removePriv['bug'] = array('batchChangePlan');
$config->project->removePriv['doc'] = array('catalog', 'index');
$config->project->removePriv['auditplan'] = array('delete');
$config->project->checkList = new stdclass();
$config->project->checkList->scrum = array('bug', 'execution', 'meeting', 'release', 'build', 'doc', 'testtask', 'risk', 'issue', 'case');
$config->project->checkList->waterfall = array('execution', 'design', 'doc', 'bug', 'case', 'build', 'release', 'testtask', 'review', 'build', 'researchplan', 'issue', 'risk', 'opportunity', 'auditplan', 'gapanalysis', 'meeting');
+5
View File
@@ -593,6 +593,9 @@ class project extends control
}
}
$canChangeModel = $this->project->checkCanChangeModel($projectID, $project->model);
$disableModel = $canChangeModel == true ? '' : 'disabled';
$this->view->title = $this->lang->project->edit;
$this->view->position[] = $this->lang->project->edit;
@@ -617,6 +620,8 @@ class project extends control
$this->view->parentProgram = $this->program->getByID($project->parent);
$this->view->availableBudget = $this->program->getBudgetLeft($parentProject) + (float)$project->budget;
$this->view->budgetUnitList = $this->project->getBudgetUnitList();
$this->view->model = $project->model;
$this->view->disableModel = $disableModel;
$this->display();
}
+2 -2
View File
@@ -259,8 +259,8 @@ $lang->project->membersUnit = '%s men';
$lang->project->lastIteration = "Recent {$lang->executionCommon}";
$lang->project->ongoingStage = 'Ongoing stage';
$lang->project->scrum = 'Scrum';
$lang->project->waterfall = 'CMMI';
$lang->project->waterfallTitle = 'CMMI';
$lang->project->waterfall = 'Waterfall';
$lang->project->waterfallTitle = 'Waterfall project management';
$lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.';
$lang->project->confirmDelete = "Do you want to delete [%s]?";
$lang->project->emptyPM = 'No manager';
+48
View File
@@ -2086,4 +2086,52 @@ class projectModel extends model
if(!$this->common->isOpenMethod($moduleName, $methodName) and !commonModel::hasPriv($moduleName, $methodName)) $this->common->deny($moduleName, $methodName, false);
common::setMenuVars('project', $objectID);
}
/**
* Check if the project model can be changed.
*
* @param int $projectID
* @param string $model
* @access public
* @return bool
*/
public function checkCanChangeModel($projectID, $model)
{
$checkList = $this->config->project->checkList->$model;
foreach($checkList as $module)
{
if($module == '') continue;
$type = '';
$table = constant('TABLE_'. strtoupper($module));
$object = new stdclass();
if($table == TABLE_EXECUTION)
{
$type = $model == 'scrum' ? 'sprint' : 'stage';
}
$object = $this->getDataByProject($table, $projectID, $type);
if(!empty($object)) return false;
}
return true;
}
/**
* Get the objects under the project.
*
* @param constant $table
* @param int $projectID
* @param string $type
* @access public
* @return object
*/
public function getDataByProject($table, $projectID, $type = '')
{
$result = $this->dao->select('id')->from($table)
->where('project')->eq($projectID)
->beginIF(!empty($type))->andWhere('type')->eq($type)->fi()
->fetch();
return $result;
}
}
+4 -1
View File
@@ -34,6 +34,10 @@
</div>
<form class='form-indicator main-form form-ajax' method='post' target='hiddenwin' id='dataform'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->project->model;?></th>
<td><?php echo html::select('model', $lang->project->modelList, $model, "class='form-control chosen' required $disableModel");?></td>
</tr>
<tr>
<th class='w-120px'><?php echo $lang->program->parent;?></th>
<?php
@@ -183,7 +187,6 @@
<tr>
<td colspan='4' class='text-center form-actions'>
<?php
echo html::hidden('model', $project->model);
echo html::submitButton();
if(!isonlybody()) echo html::backButton();
?>
+1 -1
View File
@@ -193,7 +193,7 @@
</tr>
<tr>
<th><?php echo $lang->execution->totalEstimate;?></th>
<td><?php echo (float)$workhour->totalEstimate . $lang->execution->workHour;?></td>
<td><?php echo (float)$workhour->totalEstimate . $lang->execution->workHour;?></td>
<th><?php echo $lang->execution->totalDays;?></th>
<td><?php echo $project->days;?></td>
</tr>