kanban.html.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <?php
  2. /**
  3. * The kanban view file of execution module of ZenTaoPMS.
  4. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  5. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  6. * @author Sun Guangming <sunguangming@easycorp.ltd>
  7. * @package kanban
  8. * @link https://www.zentao.net
  9. */
  10. namespace zin;
  11. $canModifyExecution = common::canModify('execution', $execution);
  12. $laneCount = 0;
  13. $links = array();
  14. foreach($kanbanList as $current => $region)
  15. {
  16. foreach($region['items'] as $index => $group)
  17. {
  18. $groupID = $group['id'];
  19. $group['getLane'] = jsRaw('window.getLane');
  20. $group['getCol'] = jsRaw('window.getCol');
  21. $group['getItem'] = jsRaw('window.getItem');
  22. $group['minColWidth'] = $execution->fluidBoard == '0' ? $execution->colWidth : $execution->minColWidth;
  23. $group['maxColWidth'] = $execution->fluidBoard == '0' ? $execution->colWidth : $execution->maxColWidth;
  24. $group['colProps'] = array('actions' => jsRaw('window.getColActions'));
  25. $group['laneProps'] = array('actions' => jsRaw('window.getLaneActions'));
  26. $group['itemProps'] = array('actions' => jsRaw('window.getItemActions'));
  27. if($execution->displayCards > 0)
  28. {
  29. $group['minLaneHeight'] = $execution->displayCards * 70;
  30. $group['maxLaneHeight'] = $execution->displayCards * 70;
  31. }
  32. if($canModifyExecution)
  33. {
  34. $group['canDrop'] = jsRaw('window.canDrop');
  35. $group['onDrop'] = jsRaw('window.onDrop');
  36. }
  37. $kanbanList[$current]['items'][$index] = $group;
  38. }
  39. $laneCount += isset($region['laneCount']) ? $region['laneCount'] : 0;
  40. $links = isset($region['links']) ? $region['links'] : array();
  41. }
  42. $operationMenu = array();
  43. if($this->execution->isClickable($execution, 'start')) $operationMenu[] = array('text' => $lang->execution->start, 'url' => inlink('start', "id=$execution->id"), 'data-toggle' => 'modal', 'icon' => 'start');
  44. if($this->execution->isClickable($execution, 'putoff')) $operationMenu[] = array('text' => $lang->execution->putoff, 'url' => inlink('putoff', "id=$execution->id"), 'data-toggle' => 'modal', 'icon' => 'calendar');
  45. if($this->execution->isClickable($execution, 'suspend')) $operationMenu[] = array('text' => $lang->execution->suspend, 'url' => inlink('suspend', "id=$execution->id"), 'data-toggle' => 'modal', 'icon'=> 'pause');
  46. if($this->execution->isClickable($execution, 'close')) $operationMenu[] = array('text' => $lang->execution->close, 'url' => inlink('close', "id=$execution->id"), 'data-toggle' => 'modal', 'icon' => 'off');
  47. if($this->execution->isClickable($execution, 'activate')) $operationMenu[] = array('text' => $lang->execution->activate, 'url' => inlink('activate', "id=$execution->id"), 'data-toggle' => 'modal', 'icon' => 'magic');
  48. if($this->execution->isClickable($execution, 'delete')) $operationMenu[] = array('text' => $lang->delete, 'url' => inlink('delete', "id=$execution->id&confirm=no"), 'innerClass' => 'ajax-submit', 'icon' => 'trash');
  49. $canCreateTask = $canModifyExecution && !$isLimited && common::hasPriv('task', 'create');
  50. $canBatchCreateTask = $canModifyExecution && !$isLimited && common::hasPriv('task', 'batchCreate');
  51. $canImportTask = $canModifyExecution && !$isLimited && common::hasPriv('execution', 'importTask') && $execution->multiple && $this->config->vision != 'lite';
  52. $canCreateBug = $features['qa'] && $canModifyExecution && common::hasPriv('bug', 'create') && $productID && $this->config->vision != 'lite';
  53. $canBatchCreateBug = $features['qa'] && $canModifyExecution && common::hasPriv('bug', 'batchCreate') && $execution->multiple && $productID && $this->config->vision != 'lite';
  54. $canImportBug = $features['qa'] && $canModifyExecution && !$isLimited && common::hasPriv('execution', 'importBug') && $execution->multiple && $productID && $this->config->vision != 'lite';
  55. $hasBugButton = $canCreateBug || $canBatchCreateBug;
  56. $canCreateStory = $features['story'] && $canModifyExecution && common::hasPriv('story', 'create') && common::canModify('execution', $execution) && $productID && $this->config->vision != 'lite';
  57. $canBatchCreateStory = $features['story'] && $canModifyExecution && common::hasPriv('story', 'batchCreate') && common::canModify('execution', $execution) && $productID && $this->config->vision != 'lite';
  58. $canLinkStory = $features['story'] && $canModifyExecution && common::hasPriv('execution', 'linkStory') && !empty($execution->hasProduct) && common::canModify('execution', $execution) && $productID && $this->config->vision != 'lite';
  59. $canLinkStoryByPlan = $features['story'] && $canModifyExecution && common::hasPriv('execution', 'importplanstories') && !empty($project->hasProduct) && common::canModify('execution', $execution) && $productID && $this->config->vision != 'lite';
  60. $hasStoryButton = $features['story'] && ($canCreateStory || $canBatchCreateStory || $canLinkStory || $canLinkStoryByPlan);
  61. $hasTaskButton = $canCreateTask || $canBatchCreateTask || $canImportBug;
  62. $createMenu = array();
  63. $modal = $productID ? 'modal' : false;
  64. if($canCreateStory) $createMenu[] = array('text' => $lang->story->create, 'url' => $productID ? helper::createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id") : 'javascript:;', 'data-toggle' => $modal, 'data-size' => 'lg', 'data-on' => 'click', 'data-call' => 'checkProducts');
  65. if($canBatchCreateStory) $createMenu[] = array('text' => $lang->story->batchCreate, 'url' => $productID ? (count($productNames) > 1 ? '#batchCreateStory' : helper::createLink('story', 'batchCreate', "productID=$productID&branch=$branchID&moduleID=0&story=0&execution=$execution->id")) : 'javascript:;', 'data-toggle' => $modal, 'data-size' => 'lg', 'data-on' => 'click', 'data-call' => 'checkProducts');
  66. if($canLinkStory) $createMenu[] = array('text' => $lang->execution->linkStory, 'url' => $productID ? helper::createLink('execution', 'linkStory', "execution=$execution->id") : 'javascript:;', 'data-toggle' => $modal, 'data-size' => 'lg', 'data-on' => 'click', 'data-call' => 'checkProducts', 'class' => 'linkStory-btn');
  67. if($canLinkStoryByPlan) $createMenu[] = array('text' => $lang->execution->linkStoryByPlan, 'url' => $productID ? "#linkStoryByPlan" : 'javascript:;', 'data-toggle' => $modal, 'data-size' => 'sm', 'data-on' => 'click', 'data-call' => 'checkProducts');
  68. if($hasStoryButton && $hasTaskButton) $createMenu[] = array('type' => 'divider');
  69. if($canCreateBug) $createMenu[] = array('text' => $lang->bug->create, 'url' => helper::createLink('bug', 'create', "productID=$productID&branch=0&extra=executionID=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg', 'class' => 'bug-create-btn');
  70. if($canBatchCreateBug)
  71. {
  72. if(count($productNames) > 1)
  73. {
  74. $createMenu[] = array('text' => $lang->bug->batchCreate, 'url' => '#batchCreateBug', 'data-toggle' => 'modal');
  75. }
  76. else
  77. {
  78. $createMenu[] = array('text' => $lang->bug->batchCreate, 'url' => helper::createLink('bug', 'batchCreate', "productID=$productID&branch=$branchID&executionID=$execution->id&extra="), 'data-toggle' => 'modal', 'data-size' => 'lg');
  79. }
  80. }
  81. if(($hasStoryButton or $hasBugButton) and $hasTaskButton) $createMenu[] = array('type' => 'divider');
  82. if($canCreateTask) $createMenu[] = array('text' => $lang->task->create, 'url' => helper::createLink('task', 'create', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg');
  83. if($canImportBug) $createMenu[] = array('text' => $lang->execution->importBug, 'url' => helper::createLink('execution', 'importBug', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg');
  84. if($canImportTask) $createMenu[] = array('text' => $lang->execution->importTask, 'url' => helper::createLink('execution', 'importTask', "execution=$execution->id"));
  85. if($canBatchCreateTask) $createMenu[] = array('text' => $lang->execution->batchCreateTask, 'url' => helper::createLink('task', 'batchCreate', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg');
  86. jsVar('taskToOpen', $taskToOpen);
  87. jsVar('laneCount', $laneCount);
  88. jsVar('kanbanLang', $lang->kanban);
  89. jsVar('storyLang', $lang->story);
  90. jsVar('executionLang', $lang->execution);
  91. jsVar('laneLang', $lang->kanbanlane);
  92. jsVar('cardLang', $lang->kanbancard);
  93. jsVar('ERURColumn', array_keys($lang->kanban->ERURColumn));
  94. jsVar('bugLang', $lang->bug);
  95. jsVar('taskLang', $lang->task);
  96. jsVar('executionID', $execution->id);
  97. jsVar('productID', $productID);
  98. jsVar('productCount', count($productNames));
  99. jsVar('vision', $config->vision);
  100. jsVar('groupBy', $groupBy);
  101. jsVar('browseType', $browseType);
  102. jsVar('orderBy', $orderBy);
  103. jsVar('isLimited', $isLimited);
  104. jsVar('childrenAB', $lang->task->childrenAB);
  105. jsVar('parentAB', $lang->task->parentAB);
  106. jsVar('minColWidth', $execution->fluidBoard == '0' ? $execution->colWidth : $execution->minColWidth);
  107. jsVar('maxColWidth', $execution->fluidBoard == '0' ? $execution->colWidth : $execution->maxColWidth);
  108. jsVar('priv',
  109. array(
  110. 'canCreateTask' => $canCreateTask,
  111. 'canBatchCreateTask' => $canBatchCreateTask,
  112. 'canImportBug' => $canImportBug,
  113. 'canCreateBug' => $canCreateBug,
  114. 'canBatchCreateBug' => $canBatchCreateBug,
  115. 'canCreateStory' => $canCreateStory,
  116. 'canBatchCreateStory' => $canBatchCreateStory,
  117. 'canLinkStory' => $canLinkStory,
  118. 'canLinkStoryByPlan' => $canLinkStoryByPlan,
  119. 'canViewBug' => common::hasPriv('bug', 'view'),
  120. 'canAssignBug' => common::hasPriv('bug', 'assignto') && $canModifyExecution,
  121. 'canConfirmBug' => common::hasPriv('bug', 'confirm') && $canModifyExecution,
  122. 'canResolveBug' => common::hasPriv('bug', 'resolve') && $canModifyExecution,
  123. 'canCopyBug' => common::hasPriv('bug', 'create') && $canModifyExecution,
  124. 'canEditBug' => common::hasPriv('bug', 'edit') && $canModifyExecution,
  125. 'canDeleteBug' => common::hasPriv('bug', 'delete') && $canModifyExecution,
  126. 'canActivateBug' => common::hasPriv('bug', 'activate') && $canModifyExecution,
  127. 'canViewTask' => common::hasPriv('task', 'view'),
  128. 'canAssignTask' => common::hasPriv('task', 'assignto') && $canModifyExecution,
  129. 'canFinishTask' => common::hasPriv('task', 'finish') && $canModifyExecution,
  130. 'canPauseTask' => common::hasPriv('task', 'pause') && $canModifyExecution,
  131. 'canCancelTask' => common::hasPriv('task', 'cancel') && $canModifyExecution,
  132. 'canCloseTask' => common::hasPriv('task', 'close'),
  133. 'canActivateTask' => common::hasPriv('task', 'activate') && $canModifyExecution,
  134. 'canActivateStory' => common::hasPriv('story', 'activate') && $canModifyExecution,
  135. 'canStartTask' => common::hasPriv('task', 'start') && $canModifyExecution,
  136. 'canRestartTask' => common::hasPriv('task', 'restart') && $canModifyExecution,
  137. 'canEditTask' => common::hasPriv('task', 'edit') && $canModifyExecution,
  138. 'canDeleteTask' => common::hasPriv('task', 'delete') && $canModifyExecution,
  139. 'canRecordWorkhourTask' => common::hasPriv('task', 'recordWorkhour') && $canModifyExecution,
  140. 'canToStoryBug' => common::hasPriv('story', 'create') && $canModifyExecution,
  141. 'canAssignStory' => common::hasPriv('story', 'assignto') && $canModifyExecution,
  142. 'canEditStory' => common::hasPriv('story', 'edit') && $canModifyExecution,
  143. 'canDeleteStory' => common::hasPriv('story', 'delete') && $canModifyExecution,
  144. 'canChangeStory' => common::hasPriv('story', 'change') && $canModifyExecution,
  145. 'canCloseStory' => common::hasPriv('story', 'close'),
  146. 'canUnlinkStory' => (common::hasPriv('execution', 'unlinkStory') && !empty($execution->hasProduct)) && $canModifyExecution,
  147. 'canViewStory' => common::hasPriv('execution', 'storyView')
  148. )
  149. );
  150. if(!$features['story']) unset($lang->kanban->type['story']);
  151. if(!$features['qa']) unset($lang->kanban->type['bug']);
  152. unset($lang->kanban->type['epic'], $lang->kanban->type['requirement'], $lang->kanban->type['risk']);
  153. $executionItems = array();
  154. foreach($executionList as $childExecution) $executionItems[] = array('text' => $childExecution->name, 'url' => createLink('execution', 'kanban', "kanbanID={$childExecution->id}"));
  155. featureBar
  156. (
  157. $this->config->vision == 'lite' ? dropdown(btn(setClass('dropdown-btn'), $execution->name), set::items($executionItems)) : null,
  158. (($features['story'] or $features['qa']) && $this->config->vision != 'lite') ? inputControl
  159. (
  160. setClass('c-type'),
  161. picker(set::width('200'), set::name('type'), set::items($lang->kanban->type), set::value($browseType), set::required(true), set::onchange('changeBrowseType()'))
  162. ) : null,
  163. $browseType != 'all' && $this->config->vision != 'lite' ? inputControl
  164. (
  165. setClass('c-group ml-5'),
  166. picker(set::width('200'), set::name('group'), set::items($lang->kanban->group->$browseType), set::value($groupBy), set::required(true), set::onchange('changeGroupBy()'))
  167. ) : null,
  168. in_array($browseType, array('all', 'task')) ? checkbox
  169. (
  170. set::rootClass('ml-2 mr-4 mt-1'),
  171. set::name('showParent'),
  172. set::checked($this->cookie->showParent ? 'checked' : ''),
  173. set::onchange('changeShowParent()'),
  174. set::text($lang->task->showParent)
  175. ) : null
  176. );
  177. $editModule = $execution->multiple ? 'execution' : 'project';
  178. $editParams = $execution->multiple ? "executionID={$execution->id}" : "projectID={$execution->project}";
  179. toolbar
  180. (
  181. inputGroup
  182. (
  183. set::style(array('display' => 'none')),
  184. setID('kanbanSearch'),
  185. inputControl
  186. (
  187. setID('searchBox'),
  188. setClass('search-box'),
  189. input
  190. (
  191. setID('kanbanSearchInput'),
  192. set::name('kanbanSearchInput'),
  193. set::placeholder($lang->execution->pleaseInput)
  194. )
  195. )
  196. ),
  197. btn(setClass('querybox-toggle ghost btn-default'), set::onclick('toggleSearchBox()'), set::icon('search'), $lang->searchAB),
  198. btnGroup
  199. (
  200. btn
  201. (
  202. set
  203. (
  204. array
  205. (
  206. 'class' => 'btn ghost btn-default',
  207. 'url' => 'javascript:toggleFullScreen();',
  208. 'icon' => 'fullscreen'
  209. )
  210. ),
  211. $lang->kanban->fullScreen
  212. ),
  213. common::hasPriv('execution', 'setKanban') ? btn
  214. (
  215. set
  216. (
  217. array
  218. (
  219. 'class' => 'btn ghost btn-default',
  220. 'url' => inlink('setKanban', "id=$execution->id"),
  221. 'icon' => 'cog-outline',
  222. 'data-toggle' => 'modal'
  223. )
  224. ),
  225. $lang->settings
  226. ) : null,
  227. common::hasPriv($editModule, 'edit') ? btn
  228. (
  229. set
  230. (
  231. array
  232. (
  233. 'class' => 'btn ghost btn-default',
  234. 'url' => createLink($editModule, 'edit', $editParams),
  235. 'icon' => 'edit',
  236. 'data-toggle' => 'modal',
  237. 'data-size' => 'lg'
  238. )
  239. ),
  240. $lang->edit
  241. ) : null
  242. ),
  243. $operationMenu ? dropdown
  244. (
  245. btn(setClass('ghost btn square btn-default'), set::icon('ellipsis-v')),
  246. set::caret(false),
  247. set::items($operationMenu)
  248. ) : null,
  249. $createMenu ? dropdown
  250. (
  251. btn(setClass('primary btn btn-default create-btn'), set::icon('plus'), $lang->create),
  252. set::items($createMenu)
  253. ) : null
  254. );
  255. div
  256. (
  257. set::id('kanbanList'),
  258. zui::kanbanList
  259. (
  260. set('$replace', false),
  261. set::key('kanban'),
  262. set::items($kanbanList),
  263. set::height('calc(100vh - 120px)'),
  264. set::links($links),
  265. set::selectable(true),
  266. set::showLinkOnSelected(true)
  267. )
  268. );
  269. $linkStoryByPlanTips = $lang->execution->linkNormalStoryByPlanTips;
  270. $linkStoryByPlanTips = $execution->multiple ? $linkStoryByPlanTips : str_replace($lang->execution->common, $lang->projectCommon, $linkStoryByPlanTips);
  271. modal
  272. (
  273. setID('linkStoryByPlan'),
  274. setData('size', '500px'),
  275. set::modalProps(array('title' => $lang->execution->linkStoryByPlan)),
  276. div
  277. (
  278. setClass('flex-auto'),
  279. icon('info-sign', setClass('warning-pale rounded-full mr-1')),
  280. $linkStoryByPlanTips
  281. ),
  282. form
  283. (
  284. setClass('text-center', 'py-4'),
  285. set::actions(array('submit')),
  286. set::submitBtnText($lang->execution->linkStory),
  287. formGroup
  288. (
  289. set::label($lang->execution->selectStoryPlan),
  290. set::required(true),
  291. setClass('text-left'),
  292. picker
  293. (
  294. set::name('plan'),
  295. set::required(true),
  296. set::items($allPlans)
  297. )
  298. )
  299. )
  300. );
  301. modal
  302. (
  303. setID('batchCreateStory'),
  304. to::header
  305. (
  306. h4($lang->bug->product)
  307. ),
  308. setData('size', '500px'),
  309. inputGroup
  310. (
  311. setClass('mt-3'),
  312. picker
  313. (
  314. set::width(300),
  315. set::name('productName'),
  316. set::items($productNames),
  317. set::required(true),
  318. set::onchange('changeStoryProduct()')
  319. ),
  320. span
  321. (
  322. setClass('input-group-btn ml-2'),
  323. btn
  324. (
  325. setClass('primary'),
  326. setID('batchCreateStoryButton'),
  327. set::url(createLink('story', 'batchCreate', 'productID=' . key($productNames) . '&branch=0&moduleID=0&storyID=0&executionID=' . $executionID)),
  328. set('data-toggle', 'modal'),
  329. set('data-dismiss', 'modal'),
  330. set('data-size', 'lg'),
  331. $lang->story->batchCreate
  332. )
  333. )
  334. )
  335. );
  336. modal
  337. (
  338. setID('batchCreateBug'),
  339. set::title($lang->bug->product),
  340. setData('size', '500px'),
  341. inputGroup
  342. (
  343. setClass('mt-3'),
  344. picker
  345. (
  346. set::width(300),
  347. set::name('productName'),
  348. set::items($productNames),
  349. set::required(true),
  350. set::onchange('changeBugProduct()')
  351. ),
  352. span
  353. (
  354. setClass('input-group-btn ml-2'),
  355. btn
  356. (
  357. setClass('primary'),
  358. setID('batchCreateBugButton'),
  359. set::url(createLink('bug', 'batchCreate', 'productID=' . key($productNames) . '&branch=&executionID=' . $executionID)),
  360. set('data-toggle', 'modal'),
  361. set('data-dismiss', 'modal'),
  362. set('data-size', 'lg'),
  363. $lang->bug->batchCreate
  364. )
  365. )
  366. )
  367. );