diff --git a/config/zentaopms.php b/config/zentaopms.php index 66c35c27c1..5bc18f074a 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -666,3 +666,4 @@ $config->excludeDropmenuList = array('program-browse', 'program-productview', ' $config->hasSwitcherModules = array('design'); $config->hasSwitcherMethods = array('project-bug', 'project-testcase', 'execution-bug', 'execution-testcase', 'testtask-cases'); $config->excludeSwitcherList = array(); +$config->hasMainNavBar = array(); diff --git a/extension/lite/execution/ext/css/header/lite.ui.css b/extension/lite/execution/ext/css/header/lite.ui.css new file mode 100644 index 0000000000..8f570cc51d --- /dev/null +++ b/extension/lite/execution/ext/css/header/lite.ui.css @@ -0,0 +1,2 @@ +#mainNavbar .main-navbar-right {position: absolute; right: 2.5rem; top: 0.5rem;} +#mainNavbar .main-navbar-right .btn {padding: 0.5rem;} diff --git a/extension/lite/execution/ext/model/lite.php b/extension/lite/execution/ext/model/lite.php index 33eccd0e6b..76a1a55905 100644 --- a/extension/lite/execution/ext/model/lite.php +++ b/extension/lite/execution/ext/model/lite.php @@ -13,11 +13,7 @@ public function setMenu(int $executionID, int $buildID = 0, string $extra = '') $kanbanList = $this->getList($execution->project, 'kanban', 'all'); $currentKanban = zget($kanbanList, $execution->id, ''); - if(empty($currentKanban)) - { - echo(js::alert($this->lang->execution->accessDenied)); - die(js::locate(helper::createLink('project', 'execution', "status=all&projectID={$execution->project}"))); - } + if(empty($currentKanban)) $this->accessDenied(); $lowerModule = strtolower($this->app->rawModule); $lowerMethod = strtolower($this->app->rawMethod); diff --git a/extension/lite/execution/ext/ui/grouptask.html.php b/extension/lite/execution/ext/ui/grouptask.html.php new file mode 100644 index 0000000000..1383f9de01 --- /dev/null +++ b/extension/lite/execution/ext/ui/grouptask.html.php @@ -0,0 +1,493 @@ + + * @package execution + * @link https://www.zentao.net + */ +namespace zin; + +include 'header.html.php'; + +$items = array(); +if(isset($lang->execution->groupFilter[$groupBy])) +{ + foreach($lang->execution->groupFilter[$groupBy] as $filterKey => $name) + { + $items[] = li + ( + setClass('nav-item'), + a + ( + ($filterKey == $filter) ? set('class', 'active') : null, + span(setClass('text'), $name), + ($filterKey == $filter) ? span(setClass('label size-sm rounded-full white'), $allCount) : null, + set::href(createLink('execution', 'grouptask', "executionID={$executionID}&groupBy={$groupBy}&filter={$filterKey}")) + ) + ); + } +} +else +{ + $items[] = li + ( + setClass('nav-item'), + a + ( + set('class', 'active'), + span(setClass('text'), $lang->all), + span(setClass('label size-sm rounded-full white'), $allCount), + set::href(createLink('execution', 'grouptask', "executionID={$executionID}&groupBy={$groupBy}")) + ) + ); +} + +featureBar +( + li + ( + setClass('nav-item feature-actions'), + a + ( + setClass('btn ghost group-collapse-all'), + span(setClass('text'), $lang->execution->treeLevel['root']), + icon('fold-all'), + ) + ), + li + ( + setClass('nav-item hidden feature-actions'), + a + ( + setClass('btn ghost group-expand-all'), + span(setClass('text'), $lang->execution->treeLevel['all']), + icon('unfold-all'), + ) + ), + $items +); + +$canCreate = hasPriv('task', 'create'); +$canImportTask = hasPriv('task', 'importTask'); +if(common::canModify('execution', $execution)) +{ + $createLink = $this->createLink('task', 'create', "executionID={$execution->id}"); + if(commonModel::isTutorialMode()) + { + $wizardParams = helper::safe64Encode("executionID={$execution->id}"); + $taskCreateLink = $this->createLink('tutorial', 'wizard', "module=task&method=create¶ms=$wizardParams"); + } + + $createItem = array('text' => $lang->task->create, 'url' => $createLink); + + if($canImportTask && $execution->multiple) $importTaskItem = array('text' => $lang->execution->importTask, 'url' => $this->createLink('execution', 'importTask', "execution={$execution->id}")); +} + +toolbar +( + hasPriv('task', 'report') ? item(set(array + ( + 'text' => $lang->task->report->common, + 'icon' => 'bar-chart', + 'class' => 'ghost', + 'url' => createLink('task', 'report', "execution={$execution->id}&browseType={$browseType}") + ))) : null, + hasPriv('task', 'export') ? item(set(array + ( + 'text' => $lang->export, + 'icon' => 'export', + 'class' => 'ghost export', + 'url' => createLink('task', 'export', "execution={$execution->id}&orderBy={$orderBy}&type={$browseType}"), + 'data-toggle' => 'modal' + ))) : null, + !empty($importTaskItem) ? dropdown( + btn( + setClass('ghost btn square btn-default'), + set::icon('import'), + set::text($lang->import), + ), + set::items(array_filter(array($importTaskItem))), + set::placement('bottom-end'), + ) : null, + $canCreate ? item(set($createItem + array('class' => 'btn primary', 'icon' => 'plus'))) : null, +); + +$groupList = array(); +foreach($lang->execution->groups as $key => $value) +{ + $link = createLink('execution', 'grouptask', "executionID={$executionID}&groupBy={$key}"); + $groupList[] = array( + 'text' => $value, + 'url' => $link, + ); +} + +$thead = function() use($lang, $groupList, $groupBy, $allCount) +{ + return h::tr + ( + $allCount ? setClass('divider') : null, + h::th + ( + setClass('c-side text-left has-btn group-menu'), + dropdown + ( + btn + ( + setClass('ghost btn square btn-default'), + $lang->execution->groups[$groupBy] + ), + set::items($groupList) + ) + ), + h::th + ( + setClass('c-id'), + $lang->task->id + ), + h::th + ( + setClass('c-pri'), + $lang->priAB + ), + h::th + ( + setClass('c-name'), + $lang->task->name + ), + h::th + ( + setClass('c-status'), + $lang->task->status + ), + h::th + ( + setClass('c-user'), + $lang->task->assignedTo + ), + h::th + ( + setClass('c-user'), + $lang->task->finishedBy + ), + h::th + ( + setClass('c-hours'), + $lang->task->estimateAB + ), + h::th + ( + setClass('c-hours'), + $lang->task->consumedAB + ), + h::th + ( + setClass('c-hours'), + $lang->task->leftAB + ), + h::th + ( + setClass('c-progress'), + $lang->task->progressAB + ), + h::th + ( + setClass('c-type'), + $lang->typeAB + ), + h::th + ( + setClass('c-date'), + $lang->task->deadline + ), + h::th + ( + setClass('c-actions'), + $lang->actions + ) + ); +}; + +$tbody = function() use($tasks, $lang, $groupBy, $users, $groupByList, $execution) +{ + global $app; + + $tbody = array(); + $groupIndex = 1; + foreach($tasks as $groupKey => $groupTasks) + { + $groupWait = 0; + $groupDone = 0; + $groupDoing = 0; + $groupClosed = 0; + $groupEstimate = 0.0; + $groupConsumed = 0.0; + $groupLeft = 0.0; + + $groupName = $groupKey; + if($groupBy == 'story') $groupName = empty($groupName) ? $lang->task->noStory : zget($groupByList, $groupKey); + if($groupBy == 'assignedTo' and $groupName == '') $groupName = $this->lang->task->noAssigned; + + $groupSum = 0; + foreach($groupTasks as $taskKey => $task) + { + if($groupBy == 'story') + { + if($task->parent >= 0) + { + $groupEstimate += $task->estimate; + $groupConsumed += $task->consumed; + if($task->status != 'cancel' && $task->status != 'closed') $groupLeft += $task->left; + } + } + else + { + if($task->parent >= 0) + { + $groupEstimate += $task->estimate; + $groupConsumed += $task->consumed; + if($groupBy == 'status' || ($task->status != 'cancel' && $task->status != 'closed')) $groupLeft += $task->left; + } + } + + if($task->status == 'wait') $groupWait++; + if($task->status == 'doing') $groupDoing++; + if($task->status == 'done') $groupDone++; + if($task->status == 'closed') $groupClosed++; + } + + $groupSum = count($groupTasks); + + $i = 0; + foreach($groupTasks as $task) + { + $assignedToStyle = $task->assignedTo == $app->user->account ? "style='color:red'" : ''; + $taskLink = $this->createLink('task','view',"taskID=$task->id"); + + $tbody[] = h::tr + ( + set(array('data-id' => $groupIndex)), + ($groupIndex > 1 and $i == 0) ? setClass('divider-top') : null, + $i == 0 ? h::td + ( + $groupSum > 4 ? setClass('c-side text-left group-toggle text-top c-side-lg') : setClass('c-side text-left group-toggle text-top'), + set::rowspan($groupSum), + div + ( + setClass('group-header'), + a + ( + setClass('groupBtn'), + set::href('###'), + icon('caret-down'), + $groupName, + ), + div + ( + setClass('groupSummary small'), + ($groupBy == 'assignedTo' and isset($members[$task->assignedTo])) ? html(sprintf($lang->execution->memberHoursAB, zget($users, $task->assignedTo), $members[$task->assignedTo]->totalHours)) : null, + html(sprintf($lang->execution->groupSummaryAB, $groupSum, $groupWait, $groupDoing, $groupEstimate . $lang->execution->workHourUnit, $groupConsumed . $lang->execution->workHourUnit, $groupLeft . $lang->execution->workHourUnit)) + ), + ), + ) : null, + h::td(sprintf('%03d', (string)$task->id)), + h::td + ( + span + ( + setClass("pri-{$task->pri}"), + $task->pri + ) + ), + h::td + ( + !empty($task->team) ? span(setClass('label gray-pale rounded-xl'), $lang->task->multipleAB) : null, + $task->parent > 0 ? span(setClass('label gray-pale rounded-xl'), $lang->task->childrenAB) : null, + (isset($task->children) && $task->children == true) ? span(setClass('label gray-pale rounded-xl'), $lang->task->parentAB) : null, + a(set::href(createLink('task', 'view', "task=$task->id")), $task->name) + ), + h::td + ( + setClass('text-center'), + span + ( + setClass("status-{$task->status}"), + $lang->task->statusList[$task->status] + ) + ), + h::td + ( + setClass('text-center'), + span + ( + set(array('style' => $assignedToStyle)), + $task->assignedToRealName + ) + ), + h::td + ( + setClass('text-center'), + zget($users, $task->finishedBy) + ), + h::td + ( + setClass('text-right'), + $task->estimate . $lang->execution->workHourUnit + ), + h::td + ( + setClass('text-right'), + $task->consumed . $lang->execution->workHourUnit + ), + h::td + ( + setClass('text-right'), + $task->left . $lang->execution->workHourUnit + ), + h::td + ( + setClass('text-right'), + $task->progress . '%' + ), + h::td + ( + setClass('text-center'), + zget($lang->task->typeList, $task->type) + ), + h::td + ( + setClass('text-center'), + isset($task->delay) ? setClass('delayed') : null, + (substr((string)$task->deadline, 0, 4) > 0) ? substr((string)$task->deadline, 5, 6) : null + ), + common::canModify('execution', $execution) ? h::td + ( + setClass('text-center'), + common::hasPriv('task', 'assignTo') ? btn + ( + set + ( + array + ( + 'url' => createLink('task', 'assignTo', "executionID=$task->execution&taskID=$task->id"), + 'data-toggle' => 'modal', + 'class' => 'btn ghost toolbar-item text-primary square size-sm', + 'icon' => 'hand-right' + ) + ) + ) : null, + common::hasPriv('task', 'edit') ? btn + ( + set + ( + array + ( + 'url' => createLink('task', 'edit', "taskID=$task->id"), + 'class' => 'btn ghost toolbar-item text-primary square size-sm', + 'icon' => 'edit' + ) + ) + ) : null, + common::hasPriv('task', 'edit') ? btn + ( + set + ( + array + ( + 'url' => createLink('task', 'delete', "taskID=$task->id"), + 'data-confirm' => $lang->task->confirmDelete, + 'class' => 'btn ghost toolbar-item text-primary square size-sm ajax-submit', + 'icon' => 'trash' + ) + ) + ) : null, + ) + : h::td() + ); + + $i ++; + } + + if($i != 0) + { + $tbody[] = h::tr + ( + $groupIndex > 1 ? setClass('hidden group-toggle group-summary divider-top') : setClass('hidden group-summary group-toggle'), + set(array('data-id' => $groupIndex)), + h::td + ( + setClass('c-side'), + div + ( + setClass('summary-header'), + a + ( + setClass('summaryBtn'), + set::href('###'), + icon('caret-right'), + $groupName, + ) + ), + ), + h::td + ( + set::colspan(13), + div + ( + setClass('table-row segments-list'), + ($groupBy == 'assignedTo' and isset($members[$task->assignedTo])) ? html(sprintf($lang->execution->memberHours, zget($users, $task->assignedTo), $members[$task->assignedTo]->totalHours)) : null, + ($groupBy == 'assignedTo' and $task->assignedTo and !isset($members[$task->assignedTo])) ? html(sprintf($lang->execution->memberHours, zget($users, $task->assignedTo), '0.0')) : null, + ($groupBy == 'assignedTo' and empty($task->assignedTo)) ? div + ( + setClass('table-col'), + div + ( + setClass('segments'), + div + ( + setClass('segment'), + div + ( + setClass('segment-title'), + $groupName + ) + ) + ) + ) : null, + html(sprintf($lang->execution->countSummary, $groupSum, $groupDoing, $groupWait)), + html(sprintf($lang->execution->timeSummary, $groupEstimate . $lang->execution->workHourUnit, $groupConsumed . $lang->execution->workHourUnit, $groupLeft . $lang->execution->workHourUnit)), + ) + ) + ); + } + + $groupIndex ++; + } + + return $tbody; +}; + +if($tasks) +{ + div + ( + set::id('tasksTable'), + h::table + ( + setClass('table condensed'), + h::thead($thead()), + h::tbody($tbody()), + ) + ); +} +else +{ + div + ( + setClass('canvas text-center py-8'), + $lang->task->noTask + ); +} diff --git a/extension/lite/execution/ext/ui/grouptask.lite.html.hook.php b/extension/lite/execution/ext/ui/grouptask.lite.html.hook.php deleted file mode 100644 index 3395f45a7d..0000000000 --- a/extension/lite/execution/ext/ui/grouptask.lite.html.hook.php +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/extension/lite/execution/ext/ui/header.html.php b/extension/lite/execution/ext/ui/header.html.php new file mode 100644 index 0000000000..4a3ce65e8f --- /dev/null +++ b/extension/lite/execution/ext/ui/header.html.php @@ -0,0 +1,53 @@ + + * @package execution + * @link https://www.zentao.net + */ +namespace zin; + +h::css(file_get_contents('../css/header/lite.ui.css')); + +$currentModule = $app->rawModule; +$currentMethod = $app->rawMethod; +$config->hasMainNavBar = array("{$currentModule}-{$currentMethod}"); +$dropdownItems = array(); +foreach($lang->execution->menu as $menuKey => $menu) +{ + $active = $menuKey == $currentMethod; + $dropdownItems[] = array('icon' => $lang->execution->icons[$menuKey], 'text' => $menu['name'], 'url' => createLink($menu['module'], $menu['method'], $menu['vars']), 'active' => $active); +} + +mainNavbar +( + to::left + ( + zui::dropmenu + ( + setID("execution-menu"), + set('_id', 'switcher'), + set(array('text' => $execution->name, 'fetcher' => createLink('execution', 'ajaxGetExecutionSwitcherMenu', "projectID={$execution->project}&executionID={$execution->id}&method={$currentMethod}"), 'defaultValue' => $execution->id)), + ) + ), + to::right + ( + dropdown + ( + btn( + setClass('ghost btn square btn-default absolute right-0'), + set::icon($lang->execution->icons[$currentMethod]), + span + ( + setClass('pl-1'), + $lang->execution->menu->{$currentMethod}['name'] + ) + ), + set::placement('bottom-end'), + set::items($dropdownItems) + ) + ) +); diff --git a/extension/lite/execution/ext/ui/task.html.php b/extension/lite/execution/ext/ui/task.html.php new file mode 100644 index 0000000000..a31fa809f9 --- /dev/null +++ b/extension/lite/execution/ext/ui/task.html.php @@ -0,0 +1,185 @@ + + * @package execution + * @link http://www.zentao.net + */ + +namespace zin; + +include 'header.html.php'; + +/* zin: Define the set::module('task') feature bar on main menu. */ +featureBar +( + set::current($browseType), + set::linkParams("executionID={$execution->id}&status={key}¶m={$param}&orderBy={$orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"), + li(searchToggle(set::module('task'), set::open($browseType == 'bysearch'))) +); + +/* zin: Define the toolbar on main menu. */ +$canCreate = hasPriv('task', 'create'); +$canBatchCreate = hasPriv('task', 'batchCreate'); +$canImportTask = hasPriv('task', 'importTask'); + +$this->loadModel('task'); +$importItems = array(); +if(common::canModify('execution', $execution)) +{ + $params = isset($moduleID) ? "&storyID=0&moduleID=$moduleID" : ""; + $batchCreateLink = $this->createLink('task', 'batchCreate', "executionID={$execution->id}{$params}"); + $createLink = $this->createLink('task', 'create', "executionID={$execution->id}{$params}"); + if(commonModel::isTutorialMode()) + { + $wizardParams = helper::safe64Encode("executionID={$execution->id}{$params}"); + $taskCreateLink = $this->createLink('tutorial', 'wizard', "module=task&method=create¶ms=$wizardParams"); + } + + $createItem = array('text' => $lang->task->create, 'url' => $createLink); + $batchCreateItem = array('text' => $lang->task->batchCreate, 'url' => $batchCreateLink); + + if($canImportTask && $execution->multiple) $importItems[] = array('text' => $lang->execution->importTask, 'url' => $this->createLink('execution', 'importTask', "execution={$execution->id}")); +} + +$cols = $this->loadModel('datatable')->getSetting('execution'); +$tableData = initTableData($tasks, $cols, $this->task); + +toolbar +( + hasPriv('task', 'report') ? item(set(array + ( + 'icon' => 'bar-chart', + 'class' => 'ghost', + 'url' => createLink('task', 'report', "execution={$execution->id}&browseType={$browseType}") + ))) : null, + hasPriv('task', 'export') ? item(set(array + ( + 'icon' => 'export', + 'class' => 'ghost export', + 'url' => createLink('task', 'export', "execution={$execution->id}&orderBy={$orderBy}&type={$browseType}"), + 'data-toggle' => 'modal' + ))) : null, + $importItems ? dropdown( + btn( + setClass('ghost btn square btn-default'), + set::icon('import') + ), + set::items($importItems), + set::placement('bottom-end'), + ) : null, + $canCreate && $canBatchCreate ? btngroup + ( + btn(setClass('btn primary'), set::icon('plus'), set::url($createLink), $lang->task->create), + dropdown + ( + btn(setClass('btn primary dropdown-toggle'), + setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))), + set::items(array_filter(array($createItem, $batchCreateItem))), + set::placement('bottom-end'), + ) + ) : null, + $canCreate && !$canBatchCreate ? item(set($createItem + array('class' => 'btn primary', 'icon' => 'plus'))) : null, + $canBatchCreate && !$canCreate ? item(set($batchCreateItem + array('class' => 'btn primary', 'icon' => 'plus'))) : null, +); + +/* zin: Define the sidebar in main content. */ +sidebar +( + moduleMenu + ( + set::modules($moduleTree), + set::activeKey($moduleID), + set::settingLink(createLink('tree', 'browsetask', "rootID=$execution->id&productID=0")), + set::closeLink(createLink('execution', 'task')) + ) +); + +$firstTask = reset($tasks); +$canBatchEdit = common::hasPriv('firstTask', 'batchEdit', !empty($firstTask) ? $firstTask : null); +$canBatchClose = common::hasPriv('firstTask', 'batchClose', !empty($firstTask) ? $firstTask : null) && strtolower($browseType) != 'closed'; +$canBatchCancel = common::hasPriv('firstTask', 'batchCancel', !empty($firstTask) ? $firstTask : null) && strtolower($browseType) != 'cancel'; +$canBatchAssignTo = common::hasPriv('firstTask', 'batchAssignTo', !empty($firstTask) ? $firstTask : null); +$canBatchChangeModule = common::hasPriv('firstTask', 'batchChangeModule', !empty($firstTask) ? $firstTask : null); +$canBatchAction = in_array(true, array($canBatchEdit, $canBatchClose, $canBatchCancel, $canBatchChangeModule, $canBatchAssignTo)); + +$footToolbar = array(); +if($canBatchAction) +{ + if($canBatchClose || $canBatchCancel) + { + $batchItems = array + ( + array('text' => $lang->close, 'innerClass' => 'batch-btn ajax-btn not-open-url' . ($canBatchClose ? '' : 'hidden'), 'data-url' => createLink('task', 'batchClose')), + array('text' => $lang->task->cancel, 'innerClass' => 'batch-btn ajax-btn not-open-url' . ($canBatchCancel ? '' : 'hidden'), 'data-url' => createLink('task', 'batchCancel')), + ); + } + + if($canBatchChangeModule) + { + $moduleItems = array(); + foreach($modules as $moduleID => $module) + { + $moduleItems[] = array('text' => $module, 'innerClass' => 'batch-btn ajax-btn', 'data-url' => createLink('task', 'batchChangeModule', "moduleID=$moduleID")); + } + } + + if($canBatchAssignTo) + { + $assignedToItems = array(); + foreach ($memberPairs as $account => $name) + { + $assignedToItems[] = array('text' => $name, 'innerClass' => 'batch-btn ajax-btn', 'data-url' => createLink('task', 'batchAssignTo', "executionID={$execution->id}&assignedTo={$account}")); + } + } + + if($canBatchClose || $canBatchCancel || $canBatchEdit) + { + $editClass = $canBatchEdit ? 'batch-btn' : 'disabled'; + $footToolbar['items'][] = array( + 'type' => 'btn-group', + 'items' => array( + array('text' => $lang->edit, 'className' => "btn size-sm {$editClass}", 'btnType' => 'secondary', 'data-url' => createLink('task', 'batchEdit', "executionID={$execution->id}")), + array('caret' => 'up', 'className' => 'btn btn-caret size-sm not-open-url', 'btnType' => 'secondary', 'items' => $batchItems, 'data-placement' => 'top-start'), + ) + ); + } + + if($canBatchChangeModule) $footToolbar['items'][] = array('caret' => 'up', 'text' => $lang->task->moduleAB, 'className' => 'btn btn-caret size-sm', 'btnType' => 'secondary', 'items' => $moduleItems, 'type' => 'dropdown', 'data-placement' => 'top-start', 'data-menu' => array('searchBox' => true)); + if($canBatchAssignTo) $footToolbar['items'][] = array('caret' => 'up', 'text' => $lang->task->assignedTo, 'className' => 'btn btn-caret size-sm', 'btnType' => 'secondary', 'items' => $assignedToItems,'type' => 'dropdown'); +} + +jsVar('+pageSummary', $lang->execution->pageSummary); +jsVar('checkedSummary', $lang->execution->checkedSummary); +jsVar('multipleAB', $lang->task->multipleAB); +jsVar('childrenAB', $lang->task->childrenAB); +jsVar('todayLabel', $lang->today); +jsVar('yesterdayLabel', $lang->yesterday); + +dtable +( + set::groupDivider(true), + set::userMap($memberPairs), + set::cols($cols), + set::data($tableData), + set::checkable($canBatchAction), + set::orderBy($orderBy), + set::sortLink(createLink('execution', 'task', "executionID={$execution->id}&status={$status}¶m={$param}&orderBy={name}_{sortType}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}")), + set::onRenderCell(jsRaw('window.renderCell')), + set::modules($modulePairs), + set::footToolbar($footToolbar), + set::footPager(usePager(array + ( + 'recPerPage' => $pager->recPerPage, + 'recTotal' => $pager->recTotal, + 'linkCreator' => helper::createLink('execution', 'task', "executionID={$execution->id}&status={$status}¶m={$param}&orderBy=$orderBy&recTotal={$pager->recTotal}&recPerPage={recPerPage}&page={page}") + ))), + set::checkInfo(jsRaw('function(checkedIDList){return window.setStatistics(this, checkedIDList);}')), + set::customCols(true) +); + +render(); diff --git a/extension/lite/execution/ext/ui/task.lite.html.hook.php b/extension/lite/execution/ext/ui/task.lite.html.hook.php deleted file mode 100644 index 3395f45a7d..0000000000 --- a/extension/lite/execution/ext/ui/task.lite.html.hook.php +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/extension/lite/execution/ext/ui/tree.html.php b/extension/lite/execution/ext/ui/tree.html.php new file mode 100644 index 0000000000..ff54c140d1 --- /dev/null +++ b/extension/lite/execution/ext/ui/tree.html.php @@ -0,0 +1,115 @@ + + * @package execution + * @link https://www.zentao.net + */ +namespace zin; + +include 'header.html.php'; + +/* zin: Define the feature bar on main menu. */ +featureBar +( + set::current('all'), + set::linkParams("executionID={$execution->id}&status={key}"), + checkbox + ( + set::rootClass('ml-2'), + set::name('showStory'), + set::text($lang->execution->treeLevel['story']), + on::change('changeDisplay') + ), +); + +/* zin: Define the toolbar on main menu. */ +if(!isset($browseType)) $browseType = 'all'; +if(!isset($orderBy)) $orderBy = ''; + +$canImportTask = hasPriv('task', 'importTask'); +if(common::canModify('execution', $execution)) +{ + if($canImportTask && $execution->multiple) $importTaskItem = array('text' => $lang->execution->importTask, 'url' => $this->createLink('execution', 'importTask', "execution={$execution->id}")); +} +toolbar +( + hasPriv('task', 'report') ? item(set(array + ( + 'icon' => 'bar-chart', + 'text' => $lang->task->reportChart, + 'class' => 'ghost', + 'url' => createLink('task', 'report', "execution={$executionID}&browseType={$browseType}"), + ))) : null, + !empty($importTaskItem) ? dropdown( + btn( + setClass('ghost btn square btn-default'), + set::icon('import'), + set::text($lang->import) + ), + set::items(array_filter(array($importTaskItem))), + set::placement('bottom-end'), + ) : null, + hasPriv('task', 'export') ? item(set(array + ( + 'icon' => 'export', + 'text' => $lang->export, + 'class' => 'ghost', + 'url' => createLink('task', 'export', "execution={$executionID}&orderBy={$orderBy}&type=tree"), + 'data-toggle' => 'modal' + ))) : null, + hasPriv('task', 'create') ? item(set(array + ( + 'icon' => 'plus', + 'text' => $lang->task->create, + 'class' => 'primary create-execution-btn', + 'url' => createLink('task', 'create', "execution={$executionID}"), + ))) : null +); + +$noData = null; +if(empty($tree)) +{ + + $noData = div( + setClass('table-empty-tip h-60 flex items-center justify-center'), + span + ( + setClass('text-gray'), + $lang->task->noTask + ), + common::hasPriv('task', 'create', $checkObject) ? btn + ( + set::text($lang->task->create), + set::icon('plus'), + set::url(createLink('task', 'create', "execution={$executionID}" . (isset($moduleID) ? "&storyID=&moduleID={$moduleID}" : ''))), + setClass('primary ml-2'), + ) : null + ); +} + +div +( + setClass('flex flex-nowrap'), + panel + ( + setClass('flex-auto'), + $noData ? $noData : tree + ( + set::id('taskTree'), + set::items($tree), + set::canSplit(false), + set::hover(true), + set::defaultNestedShow(true), + set::onClickItem(jsRaw('loadObject')) + ) + ), + div + ( + setID('detailBlock'), + setClass('w-96 ml-4 hidden'), + ) +); diff --git a/extension/lite/execution/ext/ui/tree.lite.html.hook.php b/extension/lite/execution/ext/ui/tree.lite.html.hook.php deleted file mode 100644 index 3395f45a7d..0000000000 --- a/extension/lite/execution/ext/ui/tree.lite.html.hook.php +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/lib/zin/wg/mainnavbar/v1.php b/lib/zin/wg/mainnavbar/v1.php index 81bf3275ad..581e0aa8a5 100644 --- a/lib/zin/wg/mainnavbar/v1.php +++ b/lib/zin/wg/mainnavbar/v1.php @@ -99,7 +99,12 @@ class mainNavbar extends nav */ protected function build(): wg { - if(!$this->prop('items')) return wg(); + global $app, $config; + + $moduleName = $app->rawModule; + $methodName = $app->rawMethod; + + if(!$this->prop('items') && !in_array("$moduleName-$methodName", $config->hasMainNavBar)) return wg(); $leftBlock = $this->block('left'); $rightBlock = $this->block('right'); diff --git a/module/execution/control.php b/module/execution/control.php index 95a9c44d08..0dff9448c1 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -182,6 +182,7 @@ class execution extends control $this->view->modulePairs = $this->tree->getModulePairs($executionID, 'task', $showModule); $this->view->moduleTree = $this->tree->getTaskTreeMenu($executionID, $productID, 0, array('treeModel', 'createTaskLink'), $showModule); $this->view->memberPairs = $memberPairs; + $this->view->execution = $execution; $this->display(); } @@ -3122,4 +3123,23 @@ class execution extends control $this->view->executionID = $executionID; $this->display(); } + + /** + * Ajax get execution drop menu. + * 通过Ajax获取执行下拉菜单。 + * + * @param int $projectID + * @param int $executionID + * @param string $method + * @access public + * @return void + */ + public function ajaxGetExecutionSwitcherMenu(int $projectID, int $executionID, string $method = '') + { + $this->view->link = helper::createLink('execution', $method, 'executionID={id}'); + $this->view->executionID = $executionID; + $this->view->executions = $this->execution->getList($projectID, 'kanban'); + + $this->display(); + } } diff --git a/module/execution/ui/ajaxgetexecutionswitchermenu.html.php b/module/execution/ui/ajaxgetexecutionswitchermenu.html.php new file mode 100644 index 0000000000..bdcb5e8ace --- /dev/null +++ b/module/execution/ui/ajaxgetexecutionswitchermenu.html.php @@ -0,0 +1,56 @@ + + * @package execution + * @link https://www.zentao.net + */ +namespace zin; + +/** + * 定义每个分组下的选项数据列表。 + * Define the grouped data list. + */ + +$data = array('normal' => array(), 'closed' => array()); + +foreach($executions as $execution) +{ + $item = array(); + $item['id'] = $execution->id; + $item['text'] = $execution->name; + $item['active'] = $executionID == $execution->id; + $item['type'] = 'execution'; + $item['icon'] = 'kanban'; + $item['keys'] = zget(common::convert2Pinyin(array($execution->name)), $execution->name, ''); + + if($execution->status == 'closed') + { + $data['closed'][] = $item; + } + else + { + $data['normal'][] = $item; + } +} + +/** + * 定义每个分组名称信息,包括可展开的已关闭分组。 + * Define every group name, include expanded group. + */ +$tabs = array(); +$tabs[] = array('name' => 'closed', 'text' => $lang->execution->closedExecution); +$tabs[] = array('name' => 'normal', 'text' => ''); + +$json = array(); +$json['data'] = $data; +$json['tabs'] = $tabs; +$json['searchHint'] = $lang->searchAB; +$json['expandName'] = 'closed'; +$json['itemType'] = 'execution'; +$json['link'] = array('execution' => sprintf($link, '{id}')); + +renderJson($json);