taskkanban.html.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?php
  2. /**
  3. * The taskkanban 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 Yidong Wangy <yidong@easycorp.ltd>
  7. * @package execution
  8. * @link https://www.zentao.net
  9. */
  10. namespace zin;
  11. $this->app->loadLang('risk');
  12. $laneCount = 0;
  13. $lanePairs = array();
  14. $colPairs = array();
  15. $groupCols = array(); // 卡片可以移动到的同一group下的列。
  16. foreach($kanbanGroup 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['canDrop'] = jsRaw('window.canDrop');
  23. $group['onDrop'] = jsRaw('window.onDrop');
  24. $group['minColWidth'] = $execution->fluidBoard == '0' ? $execution->colWidth : $execution->minColWidth;
  25. $group['maxColWidth'] = $execution->fluidBoard == '0' ? $execution->colWidth : $execution->maxColWidth;
  26. $group['colProps'] = array('actions' => jsRaw('window.getColActions'));
  27. $group['itemProps'] = array('actions' => jsRaw('window.getItemActions'));
  28. foreach($group['data']['lanes'] as $lane) $lanePairs[$lane['id']] = $lane['type'];
  29. foreach($group['data']['cols'] as $colIndex => $col)
  30. {
  31. $colPairs[$col['id']] = $col['type'];
  32. if(isset($col['parent']) && $col['parent'] != '-1') $groupCols[$groupID][$col['id']] = $col['title'];
  33. }
  34. if($execution->displayCards > 0)
  35. {
  36. $group['minLaneHeight'] = $execution->displayCards * 70;
  37. $group['maxLaneHeight'] = $execution->displayCards * 70;
  38. }
  39. $kanbanGroup[$index] = $group;
  40. }
  41. if(!empty($kanbanGroup)) $laneCount += count($kanbanGroup);
  42. $checkObject = new stdclass();
  43. $checkObject->execution = $executionID;
  44. $canModifyExecution = common::canModify('execution', $execution);
  45. $canCreateTask = $canModifyExecution && !$isLimited && common::hasPriv('task', 'create', $checkObject);
  46. $canBatchCreateTask = $canModifyExecution && !$isLimited && common::hasPriv('task', 'batchCreate', $checkObject);
  47. $canCreateRisk = $canModifyExecution && common::hasPriv('risk', 'create', $checkObject) && in_array($config->edition, array('max', 'ipd'));
  48. $canBatchCreateRisk = $canModifyExecution && common::hasPriv('risk', 'batchCreate', $checkObject) && in_array($config->edition, array('max', 'ipd'));
  49. $canCreateBug = ($canModifyExecution && $productID && common::hasPriv('bug', 'create'));
  50. $canBatchCreateBug = ($canModifyExecution && $productID && common::hasPriv('bug', 'batchCreate'));
  51. $canImportBug = ($canModifyExecution && $productID && !$isLimited && common::hasPriv('execution', 'importBug'));
  52. $canCreateStory = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('story', 'create'));
  53. $canBatchCreateStory = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('story', 'batchCreate'));
  54. $canLinkStory = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('execution', 'linkStory') && !empty($execution->hasProduct));
  55. $canLinkStoryByPlan = (!$hasFrozenStories && $canModifyExecution && $productID && common::hasPriv('execution', 'importplanstories') && !$hiddenPlan && !empty($execution->hasProduct));
  56. $hasStoryButton = !$hasFrozenStories && ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlan);
  57. $hasTaskButton = ($canCreateTask or $canBatchCreateTask or $canImportBug);
  58. $hasBugButton = ($canCreateBug or $canBatchCreateBug);
  59. $canStartExecution = true;
  60. if($config->edition == 'ipd')
  61. {
  62. $executionStatus = $this->execution->checkStageStatus($execution->id, 'start');
  63. $canStartExecution = empty($executionStatus['disabled']);
  64. }
  65. jsVar('laneCount', $laneCount);
  66. jsVar('cardLang', $lang->kanbancard);
  67. jsVar('groupCols', $groupCols);
  68. jsVar('lanePairs', $lanePairs);
  69. jsVar('colPairs', $colPairs);
  70. jsVar('vision', $config->vision);
  71. jsVar('colorList', $config->kanban->cardColorList);
  72. jsVar('ERURColumn', array_keys($lang->kanban->ERURColumn));
  73. jsVar('executionID', $executionID);
  74. jsVar('productID', $productID);
  75. jsVar('kanbanGroup', $kanbanGroup);
  76. jsVar('kanbanList', array_keys($kanbanGroup));
  77. jsVar('browseType', $browseType);
  78. jsVar('groupBy', $groupBy);
  79. jsVar('productNum', $productNum);
  80. jsVar('childrenAB', $lang->task->childrenAB);
  81. jsVar('parentAB', $lang->task->parentAB);
  82. jsVar('priv', array(
  83. 'canEditName' => common::hasPriv('kanban', 'setColumn'),
  84. 'canSetWIP' => common::hasPriv('kanban', 'setWIP'),
  85. 'canSetLane' => common::hasPriv('kanban', 'setLane'),
  86. 'canSortCards' => common::hasPriv('kanban', 'cardsSort'),
  87. 'canCreateTask' => $canCreateTask,
  88. 'canBatchCreateTask' => $canBatchCreateTask,
  89. 'canCreateRisk' => $canCreateRisk,
  90. 'canBatchCreateRisk' => $canBatchCreateRisk,
  91. 'canImportBug' => $canImportBug,
  92. 'canCreateBug' => $canCreateBug,
  93. 'canBatchCreateBug' => $canBatchCreateBug,
  94. 'canCreateStory' => $canCreateStory,
  95. 'canBatchCreateStory' => $canBatchCreateStory,
  96. 'canLinkStory' => $canLinkStory,
  97. 'canLinkStoryByPlan' => $canLinkStoryByPlan,
  98. 'canAssignTask' => $canModifyExecution && common::hasPriv('task', 'assignto'),
  99. 'canAssignStory' => $canModifyExecution && common::hasPriv('story', 'assignto'),
  100. 'canFinishTask' => $canModifyExecution && common::hasPriv('task', 'finish') && $canStartExecution,
  101. 'canPauseTask' => $canModifyExecution && common::hasPriv('task', 'pause'),
  102. 'canCancelTask' => $canModifyExecution && common::hasPriv('task', 'cancel'),
  103. 'canCloseTask' => $canModifyExecution && common::hasPriv('task', 'close'),
  104. 'canActivateTask' => $canModifyExecution && common::hasPriv('task', 'activate'),
  105. 'canStartTask' => $canModifyExecution && common::hasPriv('task', 'start') && $canStartExecution,
  106. 'canAssignBug' => $canModifyExecution && common::hasPriv('bug', 'assignto'),
  107. 'canConfirmBug' => $canModifyExecution && common::hasPriv('bug', 'confirmBug'),
  108. 'canActivateBug' => $canModifyExecution && common::hasPriv('bug', 'activate'),
  109. 'canResolveBug' => $canModifyExecution && common::hasPriv('bug', 'resolve'),
  110. 'canCloseBug' => $canModifyExecution && common::hasPriv('bug', 'close'),
  111. 'canCloseStory' => $canModifyExecution && common::hasPriv('story', 'close'),
  112. 'canTrackRisk' => $canModifyExecution && common::hasPriv('risk', 'track'),
  113. 'canAssignRisk' => $canModifyExecution && common::hasPriv('risk', 'assignto'),
  114. 'canCloseRisk' => $canModifyExecution && common::hasPriv('risk', 'close'),
  115. 'canHangupRisk' => $canModifyExecution && common::hasPriv('risk', 'hangup'),
  116. 'canCancelRisk' => $canModifyExecution && common::hasPriv('risk', 'cancel'),
  117. 'canActivateRisk' => $canModifyExecution && common::hasPriv('risk', 'activate')
  118. )
  119. );
  120. jsVar('executionLang', $lang->execution);
  121. jsVar('storyLang', $lang->story);
  122. jsVar('taskLang', $lang->task);
  123. jsVar('riskLang', $lang->risk);
  124. jsVar('bugLang', $lang->bug);
  125. jsVar('editName', $lang->execution->editName);
  126. jsVar('setWIP', $lang->execution->setWIP);
  127. jsVar('sortColumn', $lang->execution->sortColumn);
  128. jsVar('kanbanLang', $lang->kanban);
  129. jsVar('deadlineLang', $lang->task->deadlineAB);
  130. jsVar('estStartedLang', $lang->task->estStarted);
  131. jsVar('noAssigned', $lang->task->noAssigned);
  132. jsVar('userList', $userList);
  133. jsVar('entertime', time());
  134. jsVar('fluidBoard', $execution->fluidBoard);
  135. jsVar('displayCards', $execution->displayCards);
  136. jsVar('needLinkProducts', $lang->execution->needLinkProducts);
  137. jsVar('hourUnit', $config->hourUnit);
  138. jsVar('orderBy', $storyOrder);
  139. jsVar('minColWidth', $execution->fluidBoard == '0' ? $execution->colWidth : $execution->minColWidth);
  140. jsVar('maxColWidth', $execution->fluidBoard == '0' ? $execution->colWidth : $execution->maxColWidth);
  141. jsVar('teamWords', $lang->execution->teamWords);
  142. jsVar('canImportBug', $features['qa']);
  143. jsVar('canChangeObject', common::canModify('execution', $execution));
  144. $canCreateObject = ($canCreateTask or $canBatchCreateTask or $canCreateRisk or $canBatchCreateRisk or $canImportBug or $canCreateBug or $canBatchCreateBug or $canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlan);
  145. if($execution->type != 'stage' || !in_array($execution->attribute, array('mix', 'request', 'design')))
  146. {
  147. unset($lang->kanban->type['epic'], $lang->kanban->type['requirement']);
  148. }
  149. else
  150. {
  151. if(strpos($project->storyType, 'epic') === false) unset($lang->kanban->type['epic']);
  152. if(strpos($project->storyType, 'requirement') === false) unset($lang->kanban->type['requirement']);
  153. }
  154. featureBar
  155. (
  156. inputControl
  157. (
  158. setClass('c-type'),
  159. picker
  160. (
  161. set::width('200'),
  162. set::name('type'),
  163. set::items($lang->kanban->type),
  164. set::value($browseType),
  165. set::required(true),
  166. set::onchange('changeBrowseType()')
  167. )
  168. ),
  169. $browseType != 'all' ? inputControl
  170. (
  171. setClass('c-group ml-5'),
  172. picker
  173. (
  174. set::width('200'),
  175. set::name('group'),
  176. set::items($lang->kanban->group->$browseType),
  177. set::value($groupBy),
  178. set::required(true),
  179. set::onchange('changeGroupBy()')
  180. )
  181. ) : null,
  182. in_array($browseType, array('all', 'task')) ? checkbox
  183. (
  184. set::rootClass('ml-4 mr-4'),
  185. set::name('showParent'),
  186. set::checked($this->cookie->showParent ? 'checked' : ''),
  187. set::onchange('changeShowParent()'),
  188. set::text($lang->task->showParent)
  189. ) : null
  190. );
  191. toolbar
  192. (
  193. inputGroup
  194. (
  195. set::style(array('display' => 'none')),
  196. setID('taskKanbanSearch'),
  197. inputControl
  198. (
  199. setID('searchBox'),
  200. setClass('search-box'),
  201. input
  202. (
  203. setID('taskKanbanSearchInput'),
  204. set::name('taskKanbanSearchInput'),
  205. set::placeholder($lang->execution->pleaseInput)
  206. )
  207. )
  208. ),
  209. btn(setClass('querybox-toggle'), set::type('link'), set::onclick('toggleSearchBox()'), set::icon('search'), $lang->searchAB),
  210. common::hasPriv('task', 'export') ? btn(set::type('link'), set::url(createLink('task', 'export', "execution=$executionID&orderBy=$orderBy&type=unclosed")), set::icon('export'), set('data-toggle', 'modal'), $lang->export) : null,
  211. $canBeChanged ? dropdown
  212. (
  213. setID('importAction'),
  214. set::arrow(true),
  215. set::caret(false),
  216. btn(set::type('link'), set::icon('import'), $lang->import),
  217. set::items(array
  218. (
  219. common::hasPriv('execution', 'importTask') && $execution->multiple ? array('text' => $lang->execution->importTask, 'url' => createLink('execution', 'importTask', "execution=$execution->id")) : null,
  220. ($features['qa'] && !$isLimited && common::hasPriv('execution', 'importBug')) ? array('text' => $lang->execution->importBug, 'url' => createLink('execution', 'importBug', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null
  221. ))
  222. ) : null,
  223. dropdown
  224. (
  225. setClass('kanbanSetting'),
  226. btn(set::type('link'), setClass('btn-icon'), icon('ellipsis-v')),
  227. set::caret(false),
  228. set::items(array
  229. (
  230. common::hasPriv('execution', 'setKanban') ? array('text' => $lang->execution->setKanban, 'url' => createLink('execution', 'setKanban', "executionID=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'sm') : null,
  231. common::hasPriv('execution', 'printKanban') ? array('text' => $lang->execution->printKanban, 'url' => createLink('execution', 'printKanban', "executionID=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'sm', 'id' => 'printKanban') : null,
  232. array('text' => $lang->execution->fullScreen, 'url' => "javascript:$(\"#kanbanList\").fullscreen();")
  233. ))
  234. ),
  235. $canCreateObject ? dropdown
  236. (
  237. setID('createDropdown'),
  238. btn(setClass('primary'),set::icon('plus'), $lang->create),
  239. set::items(array
  240. (
  241. !$hasFrozenStories && $features['story'] && common::hasPriv('story', 'create') ? ($hasStoryButton && $canCreateStory && !empty($productID) ? array('text' => $lang->execution->createStory, 'url' => createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : array('text' => $lang->story->create, 'data-on' => 'click', 'data-do' => "zui.Modal.alert('" . $lang->execution->needLinkProducts . "')")) : null,
  242. ($features['story'] && $hasStoryButton && $canBatchCreateStory) ? array('text' => $lang->execution->batchCreateStory, 'url' => createLink('story', 'batchCreate', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null,
  243. ($features['story'] && $hasStoryButton && $canLinkStory) ? array('text' => $lang->execution->linkStory, 'url' => createLink('execution', 'linkStory', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null,
  244. ($features['story'] && $hasStoryButton && $canLinkStoryByPlan) ? array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm') : null,
  245. ($features['story'] && $hasStoryButton && $features['qa']) ? array('class' => 'divider menu-divider') : null,
  246. $features['qa'] && common::hasPriv('bug', 'create') ? ($canCreateBug && !empty($productID) ? array('text' => $lang->bug->create, 'url' => createLink('bug', 'create', "productID=$productID&branch=0&extra=executionID=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : array('text' => $lang->bug->create, 'data-on' => 'click', 'data-do' => "zui.Modal.alert('" . $lang->execution->needLinkProducts . "')" )) : null,
  247. ($features['qa'] && $canBatchCreateBug) ? array('text' => $lang->bug->batchCreate, 'url' => ($productNum > 1 ? '#batchCreateBug' : createLink('bug', 'batchCreate', "productID=$productID&branch=0&executionID=$execution->id")), 'data-toggle' => 'modal', 'data-size' => $productNum > 1 ? null : 'lg') : null,
  248. ($features['qa'] && $canImportBug) ? array('text' => $lang->execution->importBug, 'url' => createLink('execution', 'importBug', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null,
  249. ($features['qa'] && ($canCreateTask || $canBatchCreateTask)) ? array('text' => '', 'class' => 'divider menu-divider') : null,
  250. ($canCreateTask) ? array('text' => $lang->task->create, 'url' => createLink('task', 'create', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null,
  251. ($canBatchCreateTask) ? array('text' => $lang->execution->batchCreateTask, 'url' => createLink('task', 'batchCreate', "execution=$execution->id"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null,
  252. ($canCreateRisk || $canBatchCreateRisk) ? array('class' => 'divider menu-divider') : null,
  253. ($canCreateRisk) ? array('text' => $lang->risk->create, 'url' => createLink('risk', 'create', "execution=$execution->id&from=execution"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null,
  254. ($canBatchCreateRisk) ? array('text' => $lang->risk->batchCreateRisk, 'url' => createLink('risk', 'batchCreate', "execution=$execution->id&from=execution"), 'data-toggle' => 'modal', 'data-size' => 'lg') : null
  255. ))
  256. ) : null
  257. );
  258. div
  259. (
  260. set::id('kanbanList'),
  261. setClass('bg-white'),
  262. zui::kanbanList
  263. (
  264. set('$replace', false),
  265. set::key('kanban'),
  266. set::items($kanbanGroup),
  267. set::height('calc(100vh - 120px)'),
  268. set::links($links),
  269. set::selectable(true),
  270. set::showLinkOnSelected(true)
  271. )
  272. );
  273. $linkStoryByPlanTips = $lang->execution->linkNormalStoryByPlanTips;
  274. $linkStoryByPlanTips = $execution->multiple ? $linkStoryByPlanTips : str_replace($lang->execution->common, $lang->projectCommon, $linkStoryByPlanTips);
  275. modal
  276. (
  277. setID('linkStoryByPlan'),
  278. setData('size', '500px'),
  279. set::modalProps(array('title' => $lang->execution->linkStoryByPlan)),
  280. div
  281. (
  282. setClass('flex-auto'),
  283. icon('info-sign', setClass('warning-pale rounded-full mr-1')),
  284. $linkStoryByPlanTips
  285. ),
  286. form
  287. (
  288. setClass('text-center', 'py-4'),
  289. set::actions(array('submit')),
  290. set::submitBtnText($lang->execution->linkStory),
  291. formGroup
  292. (
  293. set::label($lang->execution->selectStoryPlan),
  294. set::required(true),
  295. setClass('text-left'),
  296. picker
  297. (
  298. set::name('plan'),
  299. set::required(true),
  300. set::items($allPlans)
  301. )
  302. )
  303. )
  304. );
  305. modal
  306. (
  307. setID('batchCreateBug'),
  308. set::title($lang->bug->product),
  309. setData('size', '500px'),
  310. inputGroup
  311. (
  312. setClass('mt-3'),
  313. picker
  314. (
  315. set::width(300),
  316. setID('product'),
  317. set::name('productName'),
  318. set::items($productNames),
  319. set::required(true),
  320. set::onchange('changeBugProduct()')
  321. ),
  322. span
  323. (
  324. setClass('input-group-btn ml-2'),
  325. btn
  326. (
  327. setClass('primary'),
  328. setID('batchCreateBugButton'),
  329. set::url(createLink('bug', 'batchCreate', 'productID=' . key($productNames) . '&branch=&executionID=' . $executionID)),
  330. set('data-toggle', 'modal'),
  331. set('data-dismiss', 'modal'),
  332. set('data-size', 'lg'),
  333. $lang->bug->batchCreate
  334. )
  335. )
  336. )
  337. );
  338. render();