* Adjust for batch unlink story page.
This commit is contained in:
@@ -3402,6 +3402,8 @@ class execution extends control
|
||||
{
|
||||
$execution = $this->execution->getByID($executionID);
|
||||
$this->execution->unlinkStory($executionID, $storyID, $laneID, $columnID);
|
||||
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
if($execution->type == 'kanban')
|
||||
{
|
||||
/* Fix bug #29171. */
|
||||
@@ -3410,22 +3412,6 @@ class execution extends control
|
||||
if(empty($executionStories)) $this->execution->unlinkStory($execution->parent, $storyID, $laneID, $columnID);
|
||||
}
|
||||
|
||||
/* if kanban then reload and if ajax request then send result. */
|
||||
if(helper::isAjaxRequest())
|
||||
{
|
||||
if(dao::isError())
|
||||
{
|
||||
$response['result'] = 'fail';
|
||||
$response['message'] = dao::getError();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response['result'] = 'success';
|
||||
$response['message'] = '';
|
||||
}
|
||||
return $this->send($response);
|
||||
}
|
||||
|
||||
$execLaneType = $this->session->execLaneType ? $this->session->execLaneType : 'all';
|
||||
$execGroupBy = $this->session->execGroupBy ? $this->session->execGroupBy : 'default';
|
||||
if($this->app->tab == 'execution' and $execution->type == 'kanban')
|
||||
@@ -3433,7 +3419,7 @@ class execution extends control
|
||||
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
|
||||
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
|
||||
$kanbanData = json_encode($kanbanData);
|
||||
return print(js::closeModal('parent', '', "parent.updateKanban($kanbanData)"));
|
||||
return $this->send(array('result' => 'success', 'closeModal' => true, 'callback' => "updateKanban($kanbanData)"));
|
||||
}
|
||||
elseif($from == 'taskkanban')
|
||||
{
|
||||
@@ -3442,10 +3428,10 @@ class execution extends control
|
||||
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
|
||||
$kanbanData = $kanbanData[$kanbanType];
|
||||
$kanbanData = json_encode($kanbanData);
|
||||
return print(js::closeModal('parent', '', "parent.updateKanban(\"story\", $kanbanData)"));
|
||||
return $this->send(array('result' => 'success', 'closeModal' => true, 'callback' => "updateKanban(\"story\", $kanbanData)"));
|
||||
}
|
||||
|
||||
return print(js::reload('parent'));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3474,7 +3460,7 @@ class execution extends control
|
||||
}
|
||||
}
|
||||
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchOther');
|
||||
return print(js::locate($this->createLink('execution', 'story', "executionID=$executionID")));
|
||||
return $this->send(array('result' => 'success', 'load' => $this->createLink('execution', 'story', "executionID=$executionID")));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3369,7 +3369,7 @@ class executionModel extends model
|
||||
{
|
||||
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('parent')->eq($executionID)->fetchAll('id');
|
||||
$executionStories = $this->dao->select('project,story')->from(TABLE_PROJECTSTORY)->where('story')->eq($storyID)->andWhere('project')->in(array_keys($executions))->fetchAll();
|
||||
if(!empty($executionStories)) return print(js::alert($this->lang->execution->notAllowedUnlinkStory));
|
||||
if(!empty($executionStories)) return dao::$errors[] = $this->lang->execution->notAllowedUnlinkStory;
|
||||
}
|
||||
$this->dao->delete()->from(TABLE_PROJECTSTORY)->where('project')->eq($executionID)->andWhere('story')->eq($storyID)->limit(1)->exec();
|
||||
|
||||
@@ -3389,7 +3389,7 @@ class executionModel extends model
|
||||
->andWhere('`column`')->eq($columnID)
|
||||
->andWhere('lane')->eq($laneID)
|
||||
->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$order = 1;
|
||||
$stories = $this->dao->select('*')->from(TABLE_PROJECTSTORY)->where('project')->eq($executionID)->orderBy('order')->fetchAll();
|
||||
|
||||
@@ -289,7 +289,11 @@ if($('.dtable .dtable-header .has-checkbox').length > 0)
|
||||
{
|
||||
const dtable = zui.DTable.query($('.dtable .dtable-header .has-checkbox').closest('.dtable')[0]);
|
||||
const checkedList = dtable.$.getChecks();
|
||||
if(checkedList.length) $('#exportType').val('selected');
|
||||
if(checkedList.length)
|
||||
{
|
||||
$('#exportType').val('selected');
|
||||
$.cookie.set('checkedItem', checkedList.join(','));
|
||||
}
|
||||
}
|
||||
|
||||
JAVASCRIPT
|
||||
|
||||
@@ -142,7 +142,7 @@ class product extends control
|
||||
/* Generate data. */
|
||||
$productID = $this->app->tab != 'project' ? $this->product->saveState($productID, $this->products) : $productID;
|
||||
$product = $this->productZen->getBrowseProduct($productID);
|
||||
$project = $this->loadModel('project')->getByID($projectID);
|
||||
$project = $projectID ? $this->loadModel('project')->getByID($projectID) : null;
|
||||
$branchID = $this->productZen->getBranchID($product, $branch);
|
||||
$orderBy = $orderBy ? $orderBy : $cookieOrderBy;
|
||||
$branch = $branchID ? $branchID : $branch;
|
||||
|
||||
@@ -19,13 +19,31 @@ $(document).on('click', '.batch-btn', function()
|
||||
}
|
||||
});
|
||||
|
||||
/* Put the checked stories to cookie for exporting. */
|
||||
$(document).on('click', '.has-checkbox', function()
|
||||
$(document).on('click', '#batchUnlinkStory', function(e)
|
||||
{
|
||||
const $this = $(this);
|
||||
const dtable = zui.DTable.query($this);
|
||||
const $this = $(this);
|
||||
const dtable = zui.DTable.query($('#stories'));
|
||||
const checkedList = dtable.$.getChecks();
|
||||
if(!checkedList.length) return;
|
||||
|
||||
$.cookie.set('checkedItem', checkedList);
|
||||
})
|
||||
let batchUnlinkStoryURL = $.createLink('projectstory', 'batchUnlinkStory', 'projectID=' + projectID + '&stories=' + encodeURIComponent(checkedList.join(',')));
|
||||
$.get(batchUnlinkStoryURL, function(data)
|
||||
{
|
||||
var jsonData = {};
|
||||
if(typeof data == 'string' && data.indexOf('{') == 0) jsonData = JSON.parse(data);
|
||||
|
||||
setTimeout(function()
|
||||
{
|
||||
if(typeof jsonData.result != 'undefined')
|
||||
{
|
||||
zui.Modal.hide('#' + $('.modal.show.in').attr('id'));
|
||||
return loadCurrentPage();
|
||||
}
|
||||
|
||||
$('.modal.show.in .modal-dialog').html(data);
|
||||
$('.modal.show.in .modal-dialog .modal-dialog .modal-header .modal-header').unwrap();
|
||||
$('.modal.show.in .modal-dialog .modal-dialog').unwrap();
|
||||
$('.modal.show.in .modal-dialog .modal-header').attr('class', '').attr('class', 'modal-header');
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace zin;
|
||||
|
||||
jsVar('projectID', $projectID);
|
||||
|
||||
$isProjectStory = $this->app->rawModule == 'projectstory';
|
||||
$projectHasProduct = $isProjectStory && !empty($project->hasProduct);
|
||||
$projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
|
||||
@@ -22,8 +24,6 @@ $storyProductIds = array();
|
||||
foreach($stories as $story) $storyProductIds[$story->product] = $story->product;
|
||||
$storyProductID = count($storyProductIds) > 1 ? 0 : $productID;
|
||||
|
||||
dropmenu();
|
||||
|
||||
/* Generate sidebar to display module tree menu. */
|
||||
$fnGenerateSideBar = function() use ($moduleTree, $moduleID, $productID, $branchID)
|
||||
{
|
||||
@@ -167,7 +167,7 @@ $cols = array_values($setting);
|
||||
$this->loadModel('story');
|
||||
|
||||
$data = array();
|
||||
$options = array('storyTasks' => $storyTasks, 'storyBugs' => $storyBugs, 'storyCases' => $storyCases, 'modules' => $modules, 'plans' => (isset($plans) ? $plans : array()), 'users' => $users);
|
||||
$options = array('storyTasks' => $storyTasks, 'storyBugs' => $storyBugs, 'storyCases' => $storyCases, 'modules' => $modules, 'plans' => (isset($plans) ? $plans : array()), 'users' => $users, 'execution' => $project);
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$options['branches'] = zget($branchOptions, $story->product, array());
|
||||
@@ -199,7 +199,7 @@ $footToolbar = $canBatchAction ? array('items' => array
|
||||
array('text' => $lang->edit, 'className' => 'secondary batch-btn', 'disabled' => ($canBatchEdit ? '': 'disabled'), 'data-page' => 'batch', 'data-formaction' => $this->createLink('story', 'batchEdit', "productID=$storyProductID&projectID=$projectID&branch=$branch&storyType=$storyType")),
|
||||
array('caret' => 'up', 'className' => 'size-sm secondary', 'url' => '#navActions', 'data-toggle' => 'dropdown', 'data-placement' => 'top-start'),
|
||||
)),
|
||||
!$canBatchUnlink ? null : array('text' => $lang->story->unlink, 'className' => 'secondary', 'id' => 'batchUnlinkStory'),
|
||||
!$canBatchUnlink ? null : array('text' => $lang->story->unlink, 'className' => 'secondary', 'id' => 'batchUnlinkStory', 'data-toggle' => 'modal'),
|
||||
array('caret' => 'up', 'text' => $lang->story->moduleAB, 'className' => $canBatchChangeModule ? '' : 'hidden', 'url' => '#navModule', 'data-toggle' => 'dropdown', 'data-placement' => 'top-start'),
|
||||
array('caret' => 'up', 'text' => $lang->story->planAB, 'className' => $canBatchChangePlan ? '' : 'hidden', 'url' => '#navPlan', 'data-toggle' => 'dropdown', 'data-placement' => 'top-start'),
|
||||
array('caret' => 'up', 'text' => $lang->story->assignedTo, 'className' => ($canBatchAssignTo ? '' : 'hidden'), 'url' => '#navAssignedTo', 'data-toggle' => 'dropdown', 'data-placement' => 'top-start'),
|
||||
|
||||
@@ -1081,7 +1081,7 @@ class productZen extends product
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function buildSearchFormForBrowse(object|bool $project, int $projectID, int &$productID, string $branch, int $param, string $storyType, string $browseType, bool $isProjectStory): void
|
||||
protected function buildSearchFormForBrowse(object|null $project, int $projectID, int &$productID, string $branch, int $param, string $storyType, string $browseType, bool $isProjectStory): void
|
||||
{
|
||||
/* Change for requirement story title. */
|
||||
if($storyType == 'requirement')
|
||||
|
||||
@@ -140,35 +140,28 @@ class projectStory extends control
|
||||
* @param int $projectID
|
||||
* @param string $storyIdList
|
||||
* @access public
|
||||
* @return string
|
||||
* @return void
|
||||
*/
|
||||
public function batchUnlinkStory($projectID, $storyIdList = '')
|
||||
public function batchUnlinkStory(int $projectID, string $storyIdList = '')
|
||||
{
|
||||
$storyIdList = empty($storyIdList) ? array() : array_filter(explode(',', $storyIdList));
|
||||
$executionStories = $this->projectstory->getExecutionStories($projectID, $storyIdList);
|
||||
$html = '';
|
||||
|
||||
foreach($executionStories as $story)
|
||||
{
|
||||
$storyLink = $this->createLink('story', 'view', "storyID={$story->id}");
|
||||
$executionLink = $this->createLink('execution', 'story', "executionID={$story->executionID}");
|
||||
$html .=<<<ETO
|
||||
<tr>
|
||||
<td class='c-name w-500px'><a href="$storyLink" title={$story->title} style='color:#5988e2'>{$story->title}</a></td>
|
||||
<td class='c-name w-200px'><a href="$executionLink" title={$story->execution} style='color:#32579c'>{$story->execution}</a></td>
|
||||
</tr>
|
||||
ETO;
|
||||
}
|
||||
$storyIdList = empty($storyIdList) ? array() : array_filter(explode(',', $storyIdList));
|
||||
if(empty($storyIdList)) $this->send(array('result' => 'success', 'load' => true, 'closeModal' => true));
|
||||
|
||||
$this->loadModel('execution');
|
||||
$executionStories = $this->projectstory->getExecutionStories($projectID, $storyIdList);
|
||||
$errors = array();
|
||||
foreach($storyIdList as $storyID)
|
||||
{
|
||||
if(isset($executionStories[$storyID])) continue;
|
||||
$this->execution->unlinkStory($projectID, $storyID);
|
||||
}
|
||||
|
||||
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchOther');
|
||||
echo $html;
|
||||
$this->execution->unlinkStory($projectID, $storyID);
|
||||
if(dao::isError()) $errors[$storyID] = dao::getError();
|
||||
}
|
||||
if(empty($errors)) $this->loadModel('score')->create('ajax', 'batchOther');
|
||||
if(empty($executionStories)) $this->send(array('result' => 'success', 'load' => true, 'closeModal' => true));
|
||||
|
||||
$this->view->executionStories = $executionStories;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +173,7 @@ ETO;
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function importPlanStories($projectID, $planID, $productID = 0)
|
||||
public function importPlanStories($projectID = 0, $planID = 0, $productID = 0)
|
||||
{
|
||||
echo $this->fetch('execution', 'importPlanStories', "projectID=$projectID&planID=$planID&productID=$productID");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The batchUnlinkStory view file of projectstory module of ZenTaoPMS.
|
||||
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
||||
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Wang Yidong <yidong@easycorp.ltd>
|
||||
* @package projectstory
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
modalHeader(set::titleClass('text-danger font-bold'), set::title($lang->projectstory->batchUnlinkTip));
|
||||
|
||||
$tableTR = array();
|
||||
foreach($executionStories as $story)
|
||||
{
|
||||
$storyLink = helper::createLink('story', 'view', "storyID={$story->id}");
|
||||
$executionLink = helper::createLink('execution', 'story', "executionID={$story->executionID}");
|
||||
$tableTR[] = h::tr
|
||||
(
|
||||
h::td(a(set::href($storyLink), set::title($story->title), set::style(array('color' => '#5988e2')), $story->title)),
|
||||
h::td(a(set::href($executionLink), set::title($story->execution), set::style(array('color' => '#32579c')), $story->execution)),
|
||||
);
|
||||
}
|
||||
|
||||
h::table
|
||||
(
|
||||
set::class('table'),
|
||||
h::tr
|
||||
(
|
||||
h::th($lang->story->title),
|
||||
h::th($lang->story->link . $lang->execution->common),
|
||||
),
|
||||
$tableTR,
|
||||
h::tr
|
||||
(
|
||||
setClass('text-center'),
|
||||
h::td(set::colspan(2), setStyle(array('text-align' => 'center')), btn(setClass('primary'), on::mousedown('loadCurrentPage'), set('data-dismiss', 'modal'), $lang->projectstory->confirm))
|
||||
)
|
||||
);
|
||||
|
||||
render('modalDialog');
|
||||
@@ -1111,7 +1111,7 @@ class story extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchClose($productID = 0, $executionID = 0, $storyType = 'story', $from = '')
|
||||
public function batchClose(int $productID = 0, int $executionID = 0, string $storyType = 'story', string $from = '')
|
||||
{
|
||||
if(!$this->post->storyIdList) return $this->send(array('result' => 'success', 'load' => $this->session->storyList));
|
||||
$storyIdList = $this->post->storyIdList;
|
||||
|
||||
@@ -5194,7 +5194,7 @@ class storyModel extends model
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function buildActionButtonList(object $story, $type = 'view', object $execution = null, $storyType = 'story'): array
|
||||
public function buildActionButtonList(object $story, $type = 'view', object|null $execution = null, $storyType = 'story'): array
|
||||
{
|
||||
$menu = '';
|
||||
$params = "storyID=$story->id";
|
||||
|
||||
+24
-3
@@ -1570,11 +1570,32 @@ class storyTao extends storyModel
|
||||
|
||||
if($execution->hasProduct)
|
||||
{
|
||||
$unlinkModule = $execution->type == 'project' ? 'projectstory' : 'execution';
|
||||
$unlinkModule = 'execution';
|
||||
$canUnlinkStory = common::hasPriv($unlinkModule, 'unlinkStory');
|
||||
$unlinkStoryLink = helper::createLink($unlinkModule, 'unlinkStory', "projectID={$execution->id}&$params&confirm=yes");
|
||||
$unlinkStoryTip = $unlinkModule == 'projectstory' ? $this->lang->execution->confirmUnlinkExecutionStory : $this->lang->execution->confirmUnlinkStory;
|
||||
$actions[] = array('name' => 'unlink', 'className' => 'ajax-submit', 'data-confirm' => $unlinkStoryTip, 'url' => $canUnlinkStory ? $unlinkStoryLink : null, 'disabled' => !$canUnlinkStory);
|
||||
$unlinkStoryTip = $this->lang->execution->confirmUnlinkStory;
|
||||
$unlinkTitle = '';
|
||||
$disabled = !$canUnlinkStory;
|
||||
|
||||
if($execution->type == 'project')
|
||||
{
|
||||
$unlinkModule = 'projectstory';
|
||||
$unlinkStoryTip = $this->lang->execution->confirmUnlinkExecutionStory;
|
||||
|
||||
static $executionStories = array();
|
||||
if(!isset($executionStories[$execution->id]))
|
||||
{
|
||||
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)->where('parent')->eq($execution->id)->andWhere('type')->ne('project')->fetchAll('id');
|
||||
$executionStories[$execution->id] = $this->dao->select('project,story')->from(TABLE_PROJECTSTORY)->where('project')->in(array_keys($executions))->fetchPairs('story', 'story');
|
||||
}
|
||||
if(isset($executionStories[$execution->id][$story->id]))
|
||||
{
|
||||
$disabled = true;
|
||||
$unlinkTitle = $this->lang->execution->notAllowedUnlinkStory;
|
||||
}
|
||||
}
|
||||
|
||||
$actions[] = array('name' => 'unlink', 'className' => 'ajax-submit', 'data-confirm' => $unlinkStoryTip, 'url' => $canUnlinkStory ? $unlinkStoryLink : null, 'disabled' => $disabled, 'title' => $unlinkTitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
declare(strict_types=1);
|
||||
namespace zin;
|
||||
|
||||
function getAffectedTabs($story, $users)
|
||||
$getAffectedTabs = function($story, $users)
|
||||
{
|
||||
global $lang, $config;
|
||||
$affectedProjects = array();
|
||||
|
||||
@@ -131,7 +131,7 @@ $formItems['file'] = formGroup
|
||||
set::name('files[]'),
|
||||
)
|
||||
);
|
||||
$formItems['affected'] = getAffectedTabs($story, $users);
|
||||
$formItems['affected'] = $getAffectedTabs($story, $users);
|
||||
|
||||
$formActions = formRow
|
||||
(
|
||||
|
||||
@@ -39,7 +39,7 @@ foreach($fields as $field => $attr)
|
||||
)
|
||||
);
|
||||
}
|
||||
$formItems['affected'] = getAffectedTabs($story, $users);
|
||||
$formItems['affected'] = $getAffectedTabs($story, $users);
|
||||
|
||||
$formItems['result']->add(on::change('switchShow(e.target);'));
|
||||
$formItems['assignedTo']->add(set::id('assignedToBox'))->add(set::hidden(!$isLastOne));
|
||||
|
||||
Reference in New Issue
Block a user