diff --git a/config/zentaopms.php b/config/zentaopms.php
index 04b1f89ca2..5f202f3f39 100644
--- a/config/zentaopms.php
+++ b/config/zentaopms.php
@@ -216,6 +216,7 @@ $config->openMethods[] = 'kanban.importexecution';
$config->openMethods[] = 'kanban.importbuild';
$config->openMethods[] = 'kanban.activatecard';
$config->openMethods[] = 'kanban.finishcard';
+$config->openMethods[] = 'kanban.deleteobjectcard';
/* Define the tables. */
define('TABLE_COMPANY', '`' . $config->db->prefix . 'company`');
diff --git a/module/bug/control.php b/module/bug/control.php
index 3844061cd9..b8dc031971 100644
--- a/module/bug/control.php
+++ b/module/bug/control.php
@@ -1864,6 +1864,16 @@ class bug extends control
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
+ if($this->app->tab == 'execution')
+ {
+ $execution = $this->loadModel('execution')->getByID($bug->execution);
+ if($execution->type == 'kanban')
+ {
+ $locateLink = $this->createLink("execution", "kanban", "executionID={$bug->execution}");
+ return print(js::locate($locateLink, 'parent.parent'));
+ }
+ }
+
$locateLink = $this->session->bugList ? $this->session->bugList : inlink('browse', "productID={$bug->product}");
return print(js::locate($locateLink, 'parent'));
}
diff --git a/module/bug/view/view.html.php b/module/bug/view/view.html.php
index 62f68d60af..45efbbbd1f 100644
--- a/module/bug/view/view.html.php
+++ b/module/bug/view/view.html.php
@@ -117,7 +117,7 @@
{
common::printIcon('bug', 'create', $copyParams, $bug, 'button', 'copy');
}
- common::printIcon('bug', 'delete', $params, $bug, 'button', 'trash', 'hiddenwin');
+ common::printIcon('bug', 'delete', $params, $bug, 'button', 'trash', 'hiddenwin', 'showinonlybody');
?>
diff --git a/module/execution/js/kanban.js b/module/execution/js/kanban.js
index 4a5d8f3740..7c6e707a2c 100644
--- a/module/execution/js/kanban.js
+++ b/module/execution/js/kanban.js
@@ -548,7 +548,7 @@ function renderBugItem(item, $item, col)
if(scaleSize <= 1)
{
var $actions = $item.find('.actions');
- if(!$actions.length && (priv.canEditBug || priv.canResolveBug || priv.canConfirmBug || priv.canCopyBug || priv.canToStoryBug))
+ if(!$actions.length && (priv.canEditBug || priv.canResolveBug || priv.canConfirmBug || priv.canCopyBug || priv.canToStoryBug || priv.canDeleteBug))
{
$actions = $([
'
',
@@ -969,6 +969,35 @@ function changeCardColType(cardID, fromColID, toColID, fromLaneID, toLaneID, car
modalTrigger.show();
}
}
+/**
+ * Delete a card.
+ *
+ * @param string objectType
+ * @param int objectID
+ * @param int regionID
+ * @access public
+ * @return void
+ */
+function deleteCard(objectType, objectID, regionID)
+{
+ var objectLang = objectType + 'Lang';
+ var result = confirm(window[objectLang].confirmDelete) ? true : false;
+
+ if(!result) return false;
+ if(!objectID) return false;
+
+ var url = createLink('kanban', 'deleteObjectCard', 'objectType=' + objectType + '&objectID=' + objectID + '®ionID=' + regionID);
+ return $.ajax(
+ {
+ method: 'post',
+ dataType: 'json',
+ url: url,
+ success: function(data)
+ {
+ updateRegion(regionID, data[regionID]);
+ }
+ });
+}
/**
* Close modal and update kanban data.
@@ -1070,6 +1099,7 @@ function createStoryMenu(options)
if(priv.canBatchCreateTask && showAction) items.push({label: executionLang.batchWBS, icon: 'pluses', url: createLink('task', 'batchCreate', 'executionID=' + execution.id + '&storyID=' + story.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canActivateStory && story.$col.type == 'closed') items.push({label: executionLang.activate, icon: 'magic', url: createLink('story', 'activate', 'storyID=' + story.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canUnlinkStory) items.push({label: executionLang.unlinkStory, icon: 'unlink', url: createLink('execution', 'unlinkStory', 'executionID=' + execution.id + '&storyID=' + story.id, '', 'false'), attrs: {target: 'hiddenwin'}});
+ if(priv.canDeleteStory) items.push({label: storyLang.delete, icon: 'trash', onClick: function(){deleteCard('story', story.id, story.$lane.region)}});
return items;
}
@@ -1090,6 +1120,7 @@ function createTaskMenu(options)
if(priv.canActivateTask && (task.$col.type == 'developed' || task.$col.type == 'canceled' || task.$col.type == 'closed')) items.push({label: executionLang.activate, icon: 'magic', url: createLink('task', 'activate', 'taskID=' + task.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canCreateTask) items.push({label: taskLang.copy, icon: 'copy', url: createLink('task', 'create', 'executionID=' + executionID + '&storyID=' + '0' + '&moduleID=' + '0' + '&taskID=' + task.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canCancelTask && (task.$col.type == 'wait' || task.$col.type == 'developing' || task.$col.type == 'pause')) items.push({label: taskLang.cancel, icon: 'cancel', url: createLink('task', 'cancel', 'taskID=' + task.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
+ if(priv.canDeleteTask) items.push({label: taskLang.delete, icon: 'trash', onClick: function(){deleteCard('task', task.id, task.$lane.region)}});
return items;
}
@@ -1109,6 +1140,7 @@ function createBugMenu(options)
if(priv.canCopyBug) items.push({label: bugLang.copy, icon: 'copy', url: createLink('bug', 'create', 'productID=' + productID + '&branch=' + '' + '&extras=bugID=' + bug.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canToStoryBug && (bug.$col.type != 'closed')) items.push({label: bugLang.toStory, icon: 'lightbulb', url: createLink('story', 'create', 'product=' + productID + '&branch=' + '0' + '&module=' + '0' + '&story=' + '0' + '&execution=' + '0' + '&bugID=' + bug.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(priv.canActivateBug && (bug.$col.type == 'fixed') || bug.$col.type == 'testing' || bug.$col.type == 'tested' || bug.$col.type == 'closed') items.push({label: bugLang.activate, icon: 'magic', url: createLink('bug', 'activate', 'bugID=' + bug.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
+ if(priv.canDeleteBug) items.push({label: bugLang.delete, icon: 'trash', onClick: function(){deleteCard('bug', bug.id, bug.$lane.region)}});
return items;
}
diff --git a/module/execution/view/kanban.html.php b/module/execution/view/kanban.html.php
index cc5d7bbb0d..b24108db0e 100644
--- a/module/execution/view/kanban.html.php
+++ b/module/execution/view/kanban.html.php
@@ -85,6 +85,7 @@ js::set('priv',
'canResolveBug' => common::hasPriv('bug', 'resolve'),
'canCopyBug' => common::hasPriv('bug', 'create'),
'canEditBug' => common::hasPriv('bug', 'edit'),
+ 'canDeleteBug' => common::hasPriv('bug', 'delete'),
'canActivateBug' => common::hasPriv('bug', 'activate'),
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
@@ -96,10 +97,12 @@ js::set('priv',
'canStartTask' => common::hasPriv('task', 'start'),
'canRestartTask' => common::hasPriv('task', 'restart'),
'canEditTask' => common::hasPriv('task', 'edit'),
+ 'canDeleteTask' => common::hasPriv('task', 'delete'),
'canRecordEstimateTask' => common::hasPriv('task', 'recordEstimate'),
'canToStoryBug' => common::hasPriv('story', 'create'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canEditStory' => common::hasPriv('story', 'edit'),
+ 'canDeleteStory' => common::hasPriv('story', 'delete'),
'canChangeStory' => common::hasPriv('story', 'change'),
'canUnlinkStory' => common::hasPriv('execution', 'unlinkStory'),
)
diff --git a/module/kanban/control.php b/module/kanban/control.php
index a8d23ab49b..55e64fb918 100644
--- a/module/kanban/control.php
+++ b/module/kanban/control.php
@@ -1380,6 +1380,28 @@ class kanban extends control
}
}
+ /**
+ * Delete a card.
+ *
+ * @param string $objectType story|task|bug
+ * @param int $objectID
+ * @param int $regionID
+ * @access public
+ * @return void
+ */
+ public function deleteObjectCard($objectType, $objectID, $regionID)
+ {
+ if(!($objectType == 'task' or $objectType == 'story' or $objectType == 'bug')) return false;
+ $table = 'TABLE_' . strtoupper($objectType);
+ $this->loadModel($objectType)->delete(constant($table), $objectID);
+ if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
+
+ $kanbanID = $this->kanban->getKanbanIDByregion($regionID);
+ $kanbanGroup = $this->kanban->getRDKanban($kanbanID);
+
+ return print(json_encode($kanbanGroup));
+ }
+
/**
* Set WIP.
*
diff --git a/module/kanban/model.php b/module/kanban/model.php
index b2fec43d2c..d11fc61feb 100644
--- a/module/kanban/model.php
+++ b/module/kanban/model.php
@@ -888,6 +888,18 @@ class kanbanModel extends model
->fetchPairs();
}
+ /**
+ * Get kanban id by region id.
+ *
+ * @param int $regionID
+ * @access public
+ * @return int
+ */
+ public function getKanbanIDByRegion($regionID)
+ {
+ return $this->dao->select('kanban')->from(TABLE_KANBANREGION)->where('id')->eq($regionID)->fetch('kanban');
+ }
+
/**
* Get kanban group by regions.
*
diff --git a/module/story/control.php b/module/story/control.php
index 691333d69a..36c3037388 100644
--- a/module/story/control.php
+++ b/module/story/control.php
@@ -1177,6 +1177,14 @@ class story extends control
$this->executeHooks($storyID);
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
+ if($this->app->tab == 'execution')
+ {
+ $execution = $this->loadModel('execution')->getByID(key($story->executions));
+ if($execution->type == 'kanban')
+ {
+ return print(js::reload('parent.parent'));
+ }
+ }
$locateLink = $this->session->storyList ? $this->session->storyList : $this->createLink('product', 'browse', "productID={$story->product}");
return print(js::locate($locateLink, 'parent'));
diff --git a/module/story/view/view.html.php b/module/story/view/view.html.php
index ab0cdd413f..608e3c5b45 100644
--- a/module/story/view/view.html.php
+++ b/module/story/view/view.html.php
@@ -244,7 +244,7 @@
echo "
";
common::printIcon('story', 'edit', "storyID=$story->id", $story);
common::printIcon('story', 'create', "productID=$story->product&branch=$story->branch&moduleID=$story->module&storyID=$story->id&executionID=0&bugID=0&planID=0&todoID=0&extra=&type=$story->type", $story, 'button', 'copy', '', '', '', "data-width='1050'");
- common::printIcon('story', 'delete', "storyID=$story->id", $story, 'button', 'trash', 'hiddenwin');
+ common::printIcon('story', 'delete', "storyID=$story->id", $story, 'button', 'trash', 'hiddenwin', 'showinonlybody',true);
?>
diff --git a/module/task/control.php b/module/task/control.php
index 3a077b1610..6aaa7d46bf 100644
--- a/module/task/control.php
+++ b/module/task/control.php
@@ -1583,6 +1583,14 @@ class task extends control
if($task->story) $this->loadModel('story')->setStage($task->story);
$this->executeHooks($taskID);
+ if($this->app->tab == 'execution')
+ {
+ $execution = $this->loadModel('execution')->getByID($task->execution);
+ if($execution->type == 'kanban')
+ {
+ return print(js::reload('parent.parent'));
+ }
+ }
$locateLink = $this->session->taskList ? $this->session->taskList : $this->createLink('execution', 'task', "executionID={$task->execution}");
return print(js::locate($locateLink, 'parent'));
diff --git a/module/task/view/view.html.php b/module/task/view/view.html.php
index 495e9739f3..b3b99dd751 100644
--- a/module/task/view/view.html.php
+++ b/module/task/view/view.html.php
@@ -187,7 +187,7 @@
if(!isonlybody()) echo "