* Adjust for batch unlink story page.

This commit is contained in:
wangyidong
2023-07-11 15:14:07 +08:00
parent fda5bae1ad
commit 9c2adf7a54
15 changed files with 130 additions and 65 deletions
+6 -20
View File
@@ -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")));
}
/**
+2 -2
View File
@@ -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();
+5 -1
View File
@@ -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
+1 -1
View File
@@ -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;
+24 -6
View File
@@ -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);
});
});
+4 -4
View File
@@ -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'),
+1 -1
View File
@@ -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')
+15 -22
View File
@@ -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');
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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);
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace zin;
function getAffectedTabs($story, $users)
$getAffectedTabs = function($story, $users)
{
global $lang, $config;
$affectedProjects = array();
+1 -1
View File
@@ -131,7 +131,7 @@ $formItems['file'] = formGroup
set::name('files[]'),
)
);
$formItems['affected'] = getAffectedTabs($story, $users);
$formItems['affected'] = $getAffectedTabs($story, $users);
$formActions = formRow
(
+1 -1
View File
@@ -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));