Merge branch 'master' of https://git.zcorp.cc/easycorp/zentaopms
This commit is contained in:
@@ -4,11 +4,11 @@ namespace zin;
|
||||
class dtable extends wg
|
||||
{
|
||||
static $defineProps = array(
|
||||
'className?:string="shadow-sm rounded"', // 表格样式。
|
||||
'className?:string="shadow rounded"', // 表格样式。
|
||||
'id?:string', // ID。
|
||||
'customCols?: bool|array', // 是否支持自定义列。
|
||||
'cols?:array', // 表格列配置
|
||||
'date?:array', // 表格数据源
|
||||
'data?:array', // 表格数据源
|
||||
'module?:string', // 模块信息,主要是获取语言项
|
||||
);
|
||||
|
||||
@@ -56,6 +56,19 @@ class dtable extends wg
|
||||
|
||||
$tableData = $this->prop('data', array());
|
||||
$this->setProp('data', array_values($tableData));
|
||||
|
||||
/* Add dtable load info to pager links. */
|
||||
$pager = $this->prop('footPager');
|
||||
if(!empty($pager) && isset($pager['items']))
|
||||
{
|
||||
if(!isset($pager['btnProps'])) $pager['btnProps'] = array('data-load' => 'table', 'type' => 'ghost', 'size' => 'sm', 'data-id' => $this->prop('id'));
|
||||
foreach($pager['items'] as $index => $item)
|
||||
{
|
||||
if($item['type'] !== 'size-menu') continue;
|
||||
if(!isset($item['itemProps'])) $pager['items'][$index]['itemProps'] = array('data-load' => 'table', 'data-id' => $this->prop('id'));
|
||||
}
|
||||
$this->setProp('footPager', $pager);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getPageCSS(): string|false
|
||||
|
||||
@@ -12,7 +12,8 @@ class entityLabel extends wg
|
||||
'text: string', // 实体文本
|
||||
'reverse?: bool=false', // 编号与文本是否交换顺序
|
||||
'textClass?: string', // 文本样式类
|
||||
'idClass?: string' // 编号样式类
|
||||
'idClass?: string', // 编号样式类
|
||||
'href?: string', // 实体链接
|
||||
);
|
||||
|
||||
protected static $defineBlocks = array(
|
||||
@@ -51,14 +52,18 @@ class entityLabel extends wg
|
||||
$text = $this->prop('text');
|
||||
$level = $this->prop('level');
|
||||
$className = $this->prop('textClass');
|
||||
$href = $this->prop('href');
|
||||
|
||||
$titleClass = empty($level)
|
||||
? "article-content"
|
||||
: "article-h$level";
|
||||
|
||||
return div
|
||||
if(empty($href)) return div(setClass($titleClass, $className), $text);
|
||||
|
||||
return a
|
||||
(
|
||||
setClass($titleClass, $className),
|
||||
set::href($href),
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
+22
-20
@@ -5,7 +5,9 @@ require_once dirname(__DIR__) . DS . 'nav' . DS . 'v1.php';
|
||||
|
||||
class navbar extends wg
|
||||
{
|
||||
static $defineProps = 'items?:array';
|
||||
static $defineProps = array(
|
||||
'items: array'
|
||||
);
|
||||
|
||||
protected function getExecutionMoreItem($executionID)
|
||||
{
|
||||
@@ -32,8 +34,7 @@ class navbar extends wg
|
||||
$dropItems = array();
|
||||
foreach($executionPairs as $executionID => $executionName)
|
||||
{
|
||||
$dropItems[] = array
|
||||
(
|
||||
$dropItems[] = array(
|
||||
'url' => createLink('execution', 'task', "executionID=$executionID"),
|
||||
'text' => $executionName,
|
||||
'hint' => $executionName,
|
||||
@@ -45,8 +46,7 @@ class navbar extends wg
|
||||
|
||||
if(count($executionPairs) > 10)
|
||||
{
|
||||
$dropItems[] = array
|
||||
(
|
||||
$dropItems[] = array(
|
||||
'url' => createLink('project', 'execution', "status=all&projectID={$object->project}"),
|
||||
'text' => "$lang->preview $lang->more",
|
||||
'hint' => $lang->more,
|
||||
@@ -54,8 +54,7 @@ class navbar extends wg
|
||||
);
|
||||
}
|
||||
|
||||
return array
|
||||
(
|
||||
return array(
|
||||
'type' => 'dropdown',
|
||||
'items' => $dropItems,
|
||||
'text' => $lang->more,
|
||||
@@ -86,8 +85,7 @@ class navbar extends wg
|
||||
$dropItems = array();
|
||||
foreach($pipelineList as $pipeline)
|
||||
{
|
||||
$dropItems[] = array
|
||||
(
|
||||
$dropItems[] = array(
|
||||
'url' => $pipeline->url,
|
||||
'text' => "[{$pipeline->type}] {$pipeline->name}",
|
||||
'hint' => $pipeline->name,
|
||||
@@ -96,8 +94,7 @@ class navbar extends wg
|
||||
);
|
||||
}
|
||||
|
||||
return array
|
||||
(
|
||||
return array(
|
||||
'type' => 'dropdown',
|
||||
'items' => $dropItems,
|
||||
'text' => $lang->app->common,
|
||||
@@ -224,8 +221,7 @@ class navbar extends wg
|
||||
$label = $subLabel;
|
||||
}
|
||||
|
||||
$dropItems[] = array
|
||||
(
|
||||
$dropItems[] = array(
|
||||
'active' => $subActive,
|
||||
'data-id' => $dropMenuName,
|
||||
'url' => $subLink,
|
||||
@@ -235,8 +231,7 @@ class navbar extends wg
|
||||
}
|
||||
|
||||
if(empty($dropItems)) continue;
|
||||
$items[] = array
|
||||
(
|
||||
$items[] = array(
|
||||
'type' => 'dropdown',
|
||||
'items' => $dropItems,
|
||||
'class' => $class,
|
||||
@@ -250,8 +245,7 @@ class navbar extends wg
|
||||
}
|
||||
else
|
||||
{
|
||||
$items[] = array
|
||||
(
|
||||
$items[] = array(
|
||||
'class' => $class,
|
||||
'text' => $label,
|
||||
'url' => commonModel::createMenuLink($menuItem, $tab),
|
||||
@@ -264,8 +258,7 @@ class navbar extends wg
|
||||
}
|
||||
else
|
||||
{
|
||||
$items[] = array
|
||||
(
|
||||
$items[] = array(
|
||||
'class' => $class,
|
||||
'text' => $menuItem->text,
|
||||
'active' => $isActive,
|
||||
@@ -274,7 +267,7 @@ class navbar extends wg
|
||||
}
|
||||
|
||||
/* Set active menu to global data, make it accessible to other widgets */
|
||||
useData('activeMenu', $activeMenu);
|
||||
data('activeMenu', $activeMenu);
|
||||
|
||||
return $items;
|
||||
}
|
||||
@@ -292,6 +285,15 @@ class navbar extends wg
|
||||
set::id('navbar'),
|
||||
new nav
|
||||
(
|
||||
on::click(<<<'FUNC'
|
||||
const $target = $(e.target);
|
||||
if(!$target.closest('.nav-divider').length && $target.closest('.nav-item').length)
|
||||
{
|
||||
const $navbar = $target.closest('#navbar');
|
||||
$navbar.find('.active').removeClass('active');
|
||||
$target.closest('.nav-item').find('a').addClass('active');
|
||||
}
|
||||
FUNC),
|
||||
set::items($this->getItems()),
|
||||
$this->children()
|
||||
)
|
||||
|
||||
@@ -25,8 +25,8 @@ class popovers extends wg
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$trigger = $this->block('trigger')[0];
|
||||
$target = $this->block('target')[0];
|
||||
$trigger = $this->block('trigger')[0]->children()[0];
|
||||
$target = $this->block('target')[0]->children()[0];
|
||||
$trigger->setProp('data-zin-id', $trigger->gid);
|
||||
$trigger->setProp('data-target', "[data-zin-id='{$target->gid}']");
|
||||
$target->setProp('data-zin-id', $target->gid);
|
||||
|
||||
@@ -1,160 +1,12 @@
|
||||
.gg-chevron-down {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 100px
|
||||
}
|
||||
.program-menu {width: 200px; max-height: 100%; margin: -5px 0;}
|
||||
.program-menu .tree[data-zin-id] {border: 1px solid #E6EAF1; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); overflow-y: auto; width: 200px !important; background-color: #fff; padding-bottom: 20px;}
|
||||
.program-menu .tree-item.tree-heading {padding: 16px 10px; font-size: 12px; color: var(--color-slate-600)}
|
||||
|
||||
.gg-chevron-down::after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-bottom: 2px solid;
|
||||
border-right: 2px solid;
|
||||
transform: rotate(45deg);
|
||||
left: 3px;
|
||||
top: 3px;
|
||||
color: #313C52;
|
||||
}
|
||||
.program-menu .icon-container {width: 20px; height: 20px; display: flex; justify-content: center; align-items: center; border-radius: 50%; border: 1px solid #0066fa;}
|
||||
.program-menu .icon-container.down {background-color: inherit;}
|
||||
.program-menu .icon-container.up {background-color: #0066fa;}
|
||||
|
||||
.gg-chevron-up {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 100px
|
||||
}
|
||||
|
||||
.gg-chevron-up::after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-top: 2px solid;
|
||||
border-right: 2px solid;
|
||||
transform: rotate(-45deg);
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
color: #FCFDFE;
|
||||
}
|
||||
|
||||
.program-menu .menu-item > a:hover {
|
||||
background: #EFF5FF;
|
||||
color: #313C52;
|
||||
}
|
||||
|
||||
.program-menu .has-nested-menu > .menu.menu-nested {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.program-menu .menu {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.program-menu .menu-item.active {
|
||||
color: #2e7fff;
|
||||
}
|
||||
|
||||
.program-menu {
|
||||
width: 200px;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
margin: -5px 0;
|
||||
}
|
||||
|
||||
.program-menu > header {
|
||||
height: 40px;
|
||||
border: 1px solid #2e7fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding-left: 12px;
|
||||
flex: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.program-menu > header .icon-close {
|
||||
cursor: pointer;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.program-menu > .menu {
|
||||
border: 1px solid #E6EAF1;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
width: 200px !important;
|
||||
}
|
||||
|
||||
.program-menu > .menu.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.program-menu-subtitle {
|
||||
padding: 8px 8px 4px 8px;
|
||||
flex: none;
|
||||
z-index: 10;
|
||||
background-color: #fff;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.program-menu .title-container {
|
||||
display: flex;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.program-menu .title-container > span {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.program-menu .icon-container {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #0066fa;
|
||||
}
|
||||
|
||||
.program-menu .icon-container.down {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.program-menu .icon-container.up {
|
||||
background-color: #0066fa;
|
||||
}
|
||||
|
||||
.program-menu [data-toggle="dropdown"] .icon-container.up {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.program-menu [data-toggle="dropdown"] .icon-container.down {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.program-menu .with-dropdown-show[data-toggle="dropdown"] .icon-container.down {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.program-menu .with-dropdown-show[data-toggle="dropdown"] .icon-container.up {
|
||||
display: flex;
|
||||
}
|
||||
.program-menu[data-show="0"] .icon-container.up {display: none;}
|
||||
.program-menu[data-show="1"] .icon-container.up {display: flex;}
|
||||
.program-menu[data-show="0"] .icon-container.down {display: flex;}
|
||||
.program-menu[data-show="1"] .icon-container.down {display: none;}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
function toggleIcon() {
|
||||
const $pm = $(e.target).closest('.program-menu');
|
||||
const show = $pm.attr('data-show');
|
||||
$pm.attr('data-show', (++show) % 2);
|
||||
}
|
||||
@@ -6,11 +6,11 @@ class programMenu extends wg
|
||||
private $programs = array();
|
||||
|
||||
protected static $defineProps = array(
|
||||
'programs?:array',
|
||||
'activeClass?:string',
|
||||
'activeIcon?:string',
|
||||
'activeKey?:string',
|
||||
'closeLink?:string'
|
||||
'programs?: array',
|
||||
'activeClass?: string="active"',
|
||||
'activeIcon?: string="check"',
|
||||
'activeKey?: string',
|
||||
'closeLink?: string'
|
||||
);
|
||||
|
||||
public static function getPageCSS(): string|false
|
||||
@@ -18,6 +18,11 @@ class programMenu extends wg
|
||||
return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css');
|
||||
}
|
||||
|
||||
public static function getPageJS(): string|false
|
||||
{
|
||||
return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js');
|
||||
}
|
||||
|
||||
private function buildMenuTree($parent, $parentID)
|
||||
{
|
||||
$children = $this->getChildProgram($parentID);
|
||||
@@ -40,13 +45,13 @@ class programMenu extends wg
|
||||
private function getTitle($activeKey)
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if(empty($activeKey)) return $lang->program->all;
|
||||
|
||||
foreach($this->programs as $program)
|
||||
{
|
||||
if($program->id == $activeKey) return $program->name;
|
||||
}
|
||||
|
||||
return $lang->program->all;
|
||||
}
|
||||
|
||||
@@ -55,65 +60,71 @@ class programMenu extends wg
|
||||
return array_filter($this->programs, function($program) use($id) {return $program->parent == $id;});
|
||||
}
|
||||
|
||||
private function setMenuTreeProps()
|
||||
private function getTreeProps()
|
||||
{
|
||||
if(!empty($this->prop('programs'))) $this->programs = $this->prop('programs');
|
||||
$this->setProp('programs', null);
|
||||
$items = $this->buildMenuTree(array(), 0);
|
||||
array_unshift($items, array('type' => 'heading', 'text' => '筛选项目集'));
|
||||
$this->setProp('items', $items);
|
||||
$this->setProp('commonItemProps', array('item' => array('className' => 'not-hide-menu')));
|
||||
$this->setProp('isDropdownMenu', true);
|
||||
$this->setProp('_to', "[data-zin-id='$this->gid']");
|
||||
$this->setDefaultProps(array('activeClass' => 'active', 'activeIcon' => 'check'));
|
||||
return array('items' => $items);
|
||||
}
|
||||
|
||||
private function closeBtn()
|
||||
{
|
||||
$activeKey = $this->prop('activeKey');
|
||||
if(empty($activeKey)) return null;
|
||||
|
||||
return a
|
||||
(
|
||||
set('href', $this->prop('closeLink')),
|
||||
h::i
|
||||
(
|
||||
setClass('icon icon-close p-3 cursor-pointer'),
|
||||
setStyle('color', '#313C52'),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$this->setMenuTreeProps();
|
||||
|
||||
$activeKey = $this->prop('activeKey');
|
||||
$title = $this->getTitle($activeKey);
|
||||
$closeBtn = null;
|
||||
|
||||
if(!empty($activeKey))
|
||||
{
|
||||
$closeBtn = a
|
||||
(
|
||||
set('href', $this->prop('closeLink')),
|
||||
h::i
|
||||
(
|
||||
setClass('icon icon-close'),
|
||||
setStyle('color', '#313C52'),
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->programs = $this->prop('programs');
|
||||
$activeKey = $this->prop('activeKey');
|
||||
|
||||
return div
|
||||
(
|
||||
setClass('program-menu'),
|
||||
set('data-zin-id', $this->gid),
|
||||
h::header
|
||||
setClass('program-menu col shrink-0'),
|
||||
set('data-show', '0'),
|
||||
popovers
|
||||
(
|
||||
set('data-toggle', 'dropdown'),
|
||||
div
|
||||
set::placement('bottom-start'),
|
||||
to::trigger
|
||||
(
|
||||
setClass('title-container'),
|
||||
div
|
||||
button
|
||||
(
|
||||
setClass('icon-container down'),
|
||||
h::i(setClass('gg-chevron-down')),
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('icon-container up'),
|
||||
h::i(setClass('gg-chevron-up')),
|
||||
),
|
||||
span($title)
|
||||
setClass('h-10 border border-primary flex justify-between items-center cursor-pointer pl-3 rounded'),
|
||||
on::click('toggleIcon'),
|
||||
div
|
||||
(
|
||||
setClass('flex gap-x-2'),
|
||||
div
|
||||
(
|
||||
setClass('icon-container down'),
|
||||
icon('angle-down'),
|
||||
),
|
||||
div
|
||||
(
|
||||
setClass('icon-container up'),
|
||||
icon('angle-top', setClass('text-white')),
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('font-bold leading-5'),
|
||||
$this->getTitle($activeKey),
|
||||
)
|
||||
),
|
||||
$this->closeBtn(),
|
||||
)
|
||||
),
|
||||
$closeBtn
|
||||
),
|
||||
zui::menutree(inherit($this))
|
||||
to::target(zui::tree(set($this->getTreeProps()))),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ function usePager(array $userSetting = null, string $pagerName = 'pager'): array
|
||||
$setting['recPerPage'] = $pager->recPerPage;
|
||||
$setting['linkCreator'] = createLink($pager->moduleName, $pager->methodName, $params);
|
||||
$setting['items'] = array();
|
||||
$setting['btnProps'] = array('data-load' => 'table', 'type' => 'ghost', 'size' => 'sm');
|
||||
$setting['gap'] = 0;
|
||||
|
||||
if($pager->recTotal == 0)
|
||||
@@ -38,7 +37,7 @@ function usePager(array $userSetting = null, string $pagerName = 'pager'): array
|
||||
else
|
||||
{
|
||||
$setting['items'][] = array('type' => 'info', 'text' => $pager->lang->pager->totalCountAB);
|
||||
$setting['items'][] = array('type' => 'size-menu', 'text' => str_replace('<strong>', '', str_replace('</strong>', '', $pager->lang->pager->pageSize)), 'dropdown' => array('placement' => 'top'), 'itemProps' => array('data-load' => 'table'));
|
||||
$setting['items'][] = array('type' => 'size-menu', 'text' => str_replace('<strong>', '', str_replace('</strong>', '', $pager->lang->pager->pageSize)), 'dropdown' => array('placement' => 'top'));
|
||||
$setting['items'][] = array('type' => 'link', 'page' => 'first', 'hint' => $pager->lang->pager->firstPage, 'icon' => 'icon-first-page');
|
||||
$setting['items'][] = array('type' => 'link', 'page' => 'prev', 'hint' => $pager->lang->pager->previousPage, 'icon' => 'icon-angle-left');
|
||||
$setting['items'][] = array('type' => 'info', 'text' => '{page}/{pageTotal}');
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
.executionstatistic-block {background: #FCFDFE; height: 256px;}
|
||||
.executionstatistic-block .panel-body {padding: 0;}
|
||||
.executionstatistic-block.block-sm {height: auto;}
|
||||
|
||||
.executionstatistic-block .tab-content {width: 100%;}
|
||||
.executionstatistic-block .tab-pane {padding: 0;}
|
||||
.executionstatistic-block .bg-secondary-pale {background: #F3F6FA;}
|
||||
.executionstatistic-block .chart {margin: auto;}
|
||||
.executionstatistic-block .story-num {margin: auto;}
|
||||
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item {height:36px; line-height: 36px;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item:hover {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item.active {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item > a {color: #5E626D;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item > a:before {background: transparent;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item.active > a {color: #0B0F18;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item:hover > a {color: #0B0F18;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item:hover > a.link {display: flex!important;}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item.nav-prev {display: none}
|
||||
.executionstatistic-block.block-long .nav-tabs .nav-item.nav-next {display: none}
|
||||
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item.active {display: flex!important;}
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item > a {width: 100%;}
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item {display: none}
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item.nav-prev {display: flex}
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item.nav-prev > a {padding: 0;}
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item.nav-next {display: flex}
|
||||
.executionstatistic-block.block-sm .nav-tabs .nav-item.nav-next > a {padding: 0;}
|
||||
@@ -1,5 +1,4 @@
|
||||
.guide-block .panel-body {padding: 0}
|
||||
.guide-block .border-top {border-top: 1px solid #EEE}
|
||||
|
||||
.guide-block .tab-content {width: 100%;}
|
||||
.guide-block .tab-pane {padding: 0;}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.productstatistic-block {background: #FCFDFE; height: 256px;}
|
||||
.productstatistic-block .panel-body {padding: 0;}
|
||||
.productstatistic-block.block-sm {height: auto;}
|
||||
|
||||
.productstatistic-block .tab-content {width: 100%;}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.projectstatistic-block {background: #FCFDFE;}
|
||||
.projectstatistic-block .panel-body {padding: 0;}
|
||||
.projectstatistic-block .tab-content {width: 100%;}
|
||||
.projectstatistic-block .bg-secondary-pale {background: #F3F6FA;}
|
||||
.projectstatistic-block .nav-tabs .nav-item:hover {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
.qastatistic-block {background: #FCFDFE; height: 256px;}
|
||||
.qastatistic-block .panel-body {padding: 0;}
|
||||
.qastatistic-block.block-sm {height: auto;}
|
||||
|
||||
.qastatistic-block .tab-content {width: 100%;}
|
||||
.qastatistic-block .tab-pane {padding: 0;}
|
||||
.qastatistic-block .bg-secondary-pale {background: #F3F6FA;}
|
||||
.qastatistic-block .chart {margin: auto;}
|
||||
.qastatistic-block .story-num {margin: auto;}
|
||||
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item {height:36px; line-height: 36px;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item:hover {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item.active {border-left: 2px solid #2E7FFF; background-color: #FCFDFE;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item > a {color: #5E626D;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item > a:before {background: transparent;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item.active > a {color: #0B0F18;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item:hover > a {color: #0B0F18;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item:hover > a.link {display: flex!important;}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item.nav-prev {display: none}
|
||||
.qastatistic-block.block-long .nav-tabs .nav-item.nav-next {display: none}
|
||||
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item.active {display: flex!important;}
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item > a {width: 100%;}
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item {display: none}
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item.nav-prev {display: flex}
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item.nav-prev > a {padding: 0;}
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item.nav-next {display: flex}
|
||||
.qastatistic-block.block-sm .nav-tabs .nav-item.nav-next > a {padding: 0;}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The executionstatistic block view file of block module of ZenTaoPMS.
|
||||
* The execution statistic block view file of block 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 Yuting Wang <wangyuting@easycorp.ltd>
|
||||
@@ -11,13 +11,106 @@ declare(strict_types=1);
|
||||
|
||||
namespace zin;
|
||||
|
||||
/**
|
||||
* 获取区块左侧的执行列表.
|
||||
* Get execution tabs on the left side.
|
||||
*
|
||||
* @param array $executions
|
||||
* @param string $blockNavCode
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
function getExecutionTabs($executions, $blockNavCode): array
|
||||
{
|
||||
$navTabs = array();
|
||||
$selected = key($executions);
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
$navTabs[] = li
|
||||
(
|
||||
set('class', 'nav-item' . ($execution->id == $selected ? ' active' : '')),
|
||||
a
|
||||
(
|
||||
set('class', 'ellipsis'),
|
||||
set('data-toggle', 'tab'),
|
||||
set('href', "#tab3{$blockNavCode}Content{$execution->id}"),
|
||||
$execution->name
|
||||
|
||||
),
|
||||
a
|
||||
(
|
||||
set('class', 'link flex-1 text-right hidden'),
|
||||
set('href', helper::createLink('execution', 'task', "executionID={$execution->id}")),
|
||||
icon
|
||||
(
|
||||
set('class', 'rotate-90 text-primary'),
|
||||
'export'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return $navTabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取区块右侧显示的执行信息.
|
||||
* Get execution statistical information.
|
||||
*
|
||||
* @param object $executions
|
||||
* @param string $blockNavID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
function getExecutionInfo($executions, $blockNavID): array
|
||||
{
|
||||
global $lang;
|
||||
|
||||
$selected = key($executions);
|
||||
$tabItems = array();
|
||||
foreach($executions as $execution)
|
||||
{
|
||||
$tabItems[] = div
|
||||
(
|
||||
set('class', 'tab-pane' . ($execution->id == $selected ? ' active' : '')),
|
||||
set('id', "tab3{$blockNavID}Content{$execution->id}"),
|
||||
div()
|
||||
);
|
||||
}
|
||||
return $tabItems;
|
||||
}
|
||||
|
||||
$blockNavCode = 'nav-' . uniqid();
|
||||
panel
|
||||
(
|
||||
set('class', 'executionstatistic-block'),
|
||||
set('class', 'executionstatistic-block ' . ($longBlock ? 'block-long' : 'block-sm')),
|
||||
set('headingClass', 'border-b'),
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
span($block->title),
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
'执行统计',
|
||||
'正在开发中...'
|
||||
set('class', 'flex h-full of-hidden'),
|
||||
cell
|
||||
(
|
||||
set('width', '25%'),
|
||||
set('class', 'bg-secondary-pale of-y-auto of-x-hidden'),
|
||||
ul
|
||||
(
|
||||
set('class', 'nav nav-tabs nav-stacked'),
|
||||
getExecutionTabs($executions, $blockNavCode)
|
||||
),
|
||||
),
|
||||
cell
|
||||
(
|
||||
set('class', 'tab-content'),
|
||||
set('width', '75%'),
|
||||
getExecutionInfo($executions, $blockNavCode)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -100,25 +100,27 @@ $usedMode = zget($config->global, 'mode', 'light');
|
||||
jsVar('changeModeTips', sprintf($lang->custom->changeModeTips, $lang->custom->modeList[$usedMode == 'light' ? 'ALM' : 'light']));
|
||||
panel
|
||||
(
|
||||
set('class', 'guide-block of-hidden'),
|
||||
set('class', 'guide-block'),
|
||||
set('headingClass', 'border-b'),
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
$lang->block->guide,
|
||||
set('class', 'panel-title w-full justify-between'),
|
||||
$block->title,
|
||||
a(set('href', createLink('tutorial', 'start')), set('class', 'btn warning'), $lang->block->tutorial)
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
set('class', 'flex h-full border-top'),
|
||||
set('class', 'flex h-full of-hidden'),
|
||||
cell
|
||||
(
|
||||
set('width', '18%'),
|
||||
set('class', 'bg-secondary-pale of-hidden'),
|
||||
set('class', 'bg-secondary-pale of-y-auto'),
|
||||
ul
|
||||
(
|
||||
set('class', 'nav nav-tabs nav-stacked h-full of-y-auto of-x-hidden'),
|
||||
set('class', 'nav nav-tabs nav-stacked'),
|
||||
getGuideTabs($blockNavCode)
|
||||
),
|
||||
),
|
||||
|
||||
@@ -311,20 +311,29 @@ function getProductInfo(array $products, string $blockNavID, bool $longBlock): a
|
||||
}
|
||||
|
||||
$blockNavCode = 'nav-' . uniqid();
|
||||
div
|
||||
panel
|
||||
(
|
||||
on::click('.nav-prev,.nav-next', 'switchNav'),
|
||||
set('class', 'productstatistic-block of-hidden ' . ($longBlock ? 'block-long' : 'block-sm')),
|
||||
set('class', 'productstatistic-block ' . ($longBlock ? 'block-long' : 'block-sm')),
|
||||
set('headingClass', 'border-b'),
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
span($block->title),
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
set('class', "flex h-full " . ($longBlock ? '' : 'col')),
|
||||
set('class', "flex h-full of-hidden " . ($longBlock ? '' : 'col')),
|
||||
cell
|
||||
(
|
||||
set('width', '22%'),
|
||||
set('class', $longBlock ? 'bg-secondary-pale' : ''),
|
||||
set('width', '25%'),
|
||||
set('class', $longBlock ? 'bg-secondary-pale of-y-auto of-x-hidden' : ''),
|
||||
ul
|
||||
(
|
||||
set('class', 'nav nav-tabs ' . ($longBlock ? 'nav-stacked h-full of-y-auto of-x-hidden' : 'pt-4 px-4')),
|
||||
set('class', 'nav nav-tabs ' . ($longBlock ? 'nav-stacked' : 'pt-4 px-4')),
|
||||
getProductTabs($products, $blockNavCode, $longBlock)
|
||||
),
|
||||
),
|
||||
|
||||
@@ -345,16 +345,25 @@ function getWaterfallProjectInfo($project)
|
||||
}
|
||||
|
||||
$blockNavCode = 'nav-' . uniqid();
|
||||
div
|
||||
panel
|
||||
(
|
||||
set('class', 'projectstatistic-block'),
|
||||
set('class', 'projectstatistic-block ' . ($longBlock ? 'block-long' : 'block-sm')),
|
||||
set('headingClass', 'border-b'),
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
span($block->title),
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
set('class', 'flex'),
|
||||
set('class', 'flex h-full of-hidden'),
|
||||
cell
|
||||
(
|
||||
set('width', '25%'),
|
||||
set('class', 'of-hidden bg-secondary-pale'),
|
||||
set('class', 'bg-secondary-pale of-y-auto of-x-hidden'),
|
||||
ul
|
||||
(
|
||||
set('class', 'nav nav-tabs nav-stacked'),
|
||||
|
||||
@@ -89,7 +89,7 @@ function getProductInfo($products, $blockNavID): array
|
||||
div
|
||||
(
|
||||
set('class', 'mx-6 my-4 bg-primary aspect-square text-center align-middle'),
|
||||
span('需求交付率')
|
||||
span('BUG修复率')
|
||||
),
|
||||
div
|
||||
(
|
||||
@@ -266,16 +266,25 @@ function getProductInfo($products, $blockNavID): array
|
||||
}
|
||||
|
||||
$blockNavCode = 'nav-' . uniqid();
|
||||
div
|
||||
panel
|
||||
(
|
||||
set('class', 'productstatistic-block'),
|
||||
set('class', 'qastatistic-block ' . ($longBlock ? 'block-long' : 'block-sm')),
|
||||
set('headingClass', 'border-b'),
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
span($block->title),
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
set('class', 'flex'),
|
||||
set('class', 'flex h-full of-hidden'),
|
||||
cell
|
||||
(
|
||||
set('width', '25%'),
|
||||
set('class', 'of-hidden bg-secondary-pale'),
|
||||
set('class', 'bg-secondary-pale of-y-auto of-x-hidden'),
|
||||
ul
|
||||
(
|
||||
set('class', 'nav nav-tabs nav-stacked'),
|
||||
|
||||
@@ -17,13 +17,14 @@ foreach($projects as $projectID => $project)
|
||||
$viewLink = $this->createLink('project', 'index', "projectID=$project->id");
|
||||
$cards[] = cell
|
||||
(
|
||||
set('width', '33%'),
|
||||
set('class', 'border m-2 p-4'),
|
||||
set('width', '32%'),
|
||||
set('class', 'border p-4'),
|
||||
div
|
||||
(
|
||||
set('class', 'pb-2'),
|
||||
a
|
||||
(
|
||||
set('class', 'text-black'),
|
||||
set('href', $viewLink),
|
||||
$project->name
|
||||
)
|
||||
@@ -34,10 +35,11 @@ foreach($projects as $projectID => $project)
|
||||
div
|
||||
(
|
||||
set('class', 'py-1.5'),
|
||||
span('近期执行 : XXX'),
|
||||
span('近期执行 : '),
|
||||
a(set('href', ''), '执行名称'),
|
||||
label
|
||||
(
|
||||
set('class', 'warning-outline ml-4'),
|
||||
set('class', 'warning-pale circle ml-2'),
|
||||
'进行中'
|
||||
)
|
||||
),
|
||||
@@ -58,9 +60,18 @@ foreach($projects as $projectID => $project)
|
||||
panel
|
||||
(
|
||||
set('class', 'recentproject-block'),
|
||||
set('headingClass', 'border-b'),
|
||||
to::heading
|
||||
(
|
||||
div
|
||||
(
|
||||
set('class', 'panel-title'),
|
||||
$lang->block->titleList['recentproject']
|
||||
)
|
||||
),
|
||||
div
|
||||
(
|
||||
set('class', 'flex cards'),
|
||||
set('class', 'flex cards justify-between'),
|
||||
$cards
|
||||
)
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ $config->bug->list->customBatchCreateFields = 'project,execution,steps,type,pri,
|
||||
|
||||
$config->bug->custom = new stdclass();
|
||||
$config->bug->custom->createFields = $config->bug->list->customCreateFields;
|
||||
$config->bug->custom->batchCreateFields = 'project,execution,deadline,steps,type,severity,os,browser,%s';
|
||||
$config->bug->custom->batchCreateFields = 'project,execution,deadline,steps,type,pri,severity,os,browser,%s';
|
||||
$config->bug->custom->batchEditFields = 'type,severity,pri,assignedTo,deadline,status,resolvedBy,resolution';
|
||||
|
||||
$config->bug->excludeCheckFileds = ',severities,oses,browsers,lanes,regions,executions,projects,branches,';
|
||||
|
||||
@@ -17,6 +17,9 @@ formPanel
|
||||
(
|
||||
set::title($lang->bug->assignTo),
|
||||
set::headingClass('status-heading'),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->bug->assignTo),
|
||||
set::actionsClass('form-group no-label'),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
@@ -52,5 +55,6 @@ formPanel
|
||||
set::rows(6),
|
||||
),
|
||||
);
|
||||
history();
|
||||
|
||||
render('modalDialog');
|
||||
|
||||
@@ -149,7 +149,7 @@ $items[] = array
|
||||
'label' => $lang->bug->deadline,
|
||||
'hidden' => zget($visibleFields, 'deadline', true, false),
|
||||
'control' => 'date',
|
||||
'width' => '128px',
|
||||
'width' => '136px',
|
||||
'required' => isset($requiredFields['deadline']),
|
||||
'ditto' => true,
|
||||
);
|
||||
@@ -189,6 +189,7 @@ $items[] = array
|
||||
'value' => '',
|
||||
'width' => '80px',
|
||||
'required' => isset($requiredFields['pri']),
|
||||
'ditto' => true,
|
||||
);
|
||||
|
||||
/* Field of severity. */
|
||||
|
||||
@@ -23,14 +23,15 @@ featureBar
|
||||
);
|
||||
|
||||
$canBeChanged = common::canModify('product', $product);
|
||||
$canBatchEdit = $canBeChanged && common::hasPriv('bug', 'batchEdit');
|
||||
$canBatchConfirm = $canBeChanged && common::hasPriv('bug', 'batchConfirm');
|
||||
$canBatchActivate = $canBeChanged && common::hasPriv('bug', 'batchActivate');
|
||||
$canBatchChangeBranch = $canBeChanged && common::hasPriv('bug', 'batchChangeBranch');
|
||||
$canBatchChangeModule = $canBeChanged && common::hasPriv('bug', 'batchChangeModule');
|
||||
$canBatchResolve = $canBeChanged && common::hasPriv('bug', 'batchResolve');
|
||||
$canBatchAssignTo = $canBeChanged && common::hasPriv('bug', 'batchAssignTo');
|
||||
$canBatchEdit = $canBeChanged && hasPriv('bug', 'batchEdit');
|
||||
$canBatchConfirm = $canBeChanged && hasPriv('bug', 'batchConfirm');
|
||||
$canBatchActivate = $canBeChanged && hasPriv('bug', 'batchActivate');
|
||||
$canBatchChangeBranch = $canBeChanged && hasPriv('bug', 'batchChangeBranch');
|
||||
$canBatchChangeModule = $canBeChanged && hasPriv('bug', 'batchChangeModule');
|
||||
$canBatchResolve = $canBeChanged && hasPriv('bug', 'batchResolve');
|
||||
$canBatchAssignTo = $canBeChanged && hasPriv('bug', 'batchAssignTo');
|
||||
$canBatchClose = common::hasPriv('bug', 'batchClose');
|
||||
$canManageModule = hasPriv('tree', 'browse');
|
||||
$canBatchAction = $canBatchEdit || $canBatchConfirm || $canBatchClose || $canBatchActivate || $canBatchChangeBranch || $canBatchChangeModule || $canBatchResolve || $canBatchAssignTo;
|
||||
|
||||
if(!isonlybody())
|
||||
@@ -87,14 +88,16 @@ if(!isonlybody())
|
||||
);
|
||||
}
|
||||
|
||||
$closeLink = $browseType == 'bymodule' ? createLink('bug', 'browse', "productID={$product->id}&branch=$branch&browseType=$browseType¶m=0&orderBy=$orderBy&recTotal=0&recPerPage={$pager->recPerPage}") : 'javascript:removeCookieByKey("bugModule")';
|
||||
$closeLink = $browseType == 'bymodule' ? createLink('bug', 'browse', "productID={$product->id}&branch=$branch&browseType=$browseType¶m=0&orderBy=$orderBy&recTotal=0&recPerPage={$pager->recPerPage}") : 'javascript:removeCookieByKey("bugModule")';
|
||||
$settingLink = $canManageModule ? createLink('tree', 'browse', "productID={$product->id}&view=bug¤tModuleID=0&branch=0&from={$this->lang->navGroup->bug}") : '';
|
||||
sidebar
|
||||
(
|
||||
moduleMenu(set(array
|
||||
(
|
||||
'modules' => $moduleTree,
|
||||
'activeKey' => $currentModuleID,
|
||||
'closeLink' => $closeLink
|
||||
'modules' => $moduleTree,
|
||||
'activeKey' => $currentModuleID,
|
||||
'closeLink' => $closeLink,
|
||||
'settingLink' => $settingLink,
|
||||
)))
|
||||
);
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ formPanel
|
||||
(
|
||||
set::title($lang->bug->resolve),
|
||||
set::headingClass('status-heading'),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->bug->resolve),
|
||||
set::actionsClass('form-group no-label'),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
@@ -60,12 +63,12 @@ formPanel
|
||||
formRow
|
||||
(
|
||||
setClass('hidden'),
|
||||
set::id('duplicateBox'),
|
||||
set::id('duplicateBugBox'),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/3'),
|
||||
set::name('duplicate'),
|
||||
set::label($lang->bug->duplicate),
|
||||
set::name('duplicateBug'),
|
||||
set::label($lang->bug->duplicateBug),
|
||||
set::items(array()),
|
||||
set::placeholder($lang->bug->placeholder->duplicate),
|
||||
set::value(''),
|
||||
@@ -112,12 +115,12 @@ formPanel
|
||||
(
|
||||
set::id('newBuildBox'),
|
||||
set::label($lang->bug->resolvedBuild),
|
||||
set::required(true),
|
||||
setClass('hidden'),
|
||||
input
|
||||
(
|
||||
set::name('buildName'),
|
||||
set::value(''),
|
||||
set::required(true)
|
||||
),
|
||||
),
|
||||
$createBuild,
|
||||
|
||||
@@ -557,7 +557,7 @@ class build extends control
|
||||
$lastBuild = $this->build->getLast($executionID, $projectID);
|
||||
if($lastBuild)
|
||||
{
|
||||
echo "<div class='help-block'> " . $this->lang->build->last . ": <a class='code label label-badge label-light' id='lastBuildBtn'>" . $lastBuild->name . "</a></div>";
|
||||
echo "<div class='help-block'> " . $this->lang->build->last . ": <a class='code label light rounded-full' id='lastBuildBtn'>" . $lastBuild->name . "</a></div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -40,8 +40,8 @@ class caselib extends control
|
||||
$libID = $this->caselib->create($lib, $this->post->uid);
|
||||
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
$response = $this->caselibZen->responseAfterCreate($libID);
|
||||
return $this->send($response);
|
||||
if($this->viewType == 'json') return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $libID);
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->createLink('caselib', 'browse', "libID=$libID")));
|
||||
}
|
||||
|
||||
$this->caselibZen->setCreateMenu();
|
||||
|
||||
@@ -196,6 +196,7 @@ class caselibModel extends model
|
||||
{
|
||||
$libID = $this->dao->lastInsertID();
|
||||
$this->loadModel('file')->updateObjectID($uid, $libID, 'caselib');
|
||||
$this->loadModel('action')->create('caselib', $libID, 'opened');
|
||||
return $libID;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -68,6 +68,7 @@ toolbar
|
||||
setClass('btn secondary'),
|
||||
set::icon('plus'),
|
||||
set::url(helper::createLink('caselib', 'create')),
|
||||
set('data-toggle', 'modal'),
|
||||
$lang->caselib->create
|
||||
) : '',
|
||||
$canCreateCase && $canBatchCreateCase ? btngroup
|
||||
|
||||
@@ -14,23 +14,23 @@ declare(strict_types=1);
|
||||
namespace zin;
|
||||
$nameGroup = formGroup
|
||||
(
|
||||
set::name("name"),
|
||||
set::name('name'),
|
||||
set::width('1/2'),
|
||||
set::label($lang->caselib->name),
|
||||
set::required(true),
|
||||
set::control("text")
|
||||
set::control('text')
|
||||
);
|
||||
$descGroup = formGroup
|
||||
(
|
||||
set::name("desc"),
|
||||
set::name('desc'),
|
||||
set::label($lang->caselib->desc),
|
||||
set::control("editor")
|
||||
set::control('editor')
|
||||
);
|
||||
|
||||
$formItems = array();
|
||||
$formItems['name'] = $nameGroup;
|
||||
$formItems['desc'] = $descGroup;
|
||||
|
||||
/* TODO:printExtendFields */
|
||||
formPanel($formItems);
|
||||
|
||||
render();
|
||||
render(isAjaxRequest('modal') ? 'modalDialog' : 'page');
|
||||
|
||||
@@ -50,21 +50,4 @@ class caselibZen extends caselib
|
||||
|
||||
return $this->loadModel('file')->processImgURL($lib, $this->config->caselib->editor->create['id'], $uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建创建产品页面数据。
|
||||
* Build form fields for create.
|
||||
*
|
||||
* @param int $libID
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function responseAfterCreate(int $libID): array
|
||||
{
|
||||
$this->loadModel('action')->create('caselib', $libID, 'opened');
|
||||
|
||||
if($this->viewType == 'json') return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $libID);
|
||||
return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink('caselib', 'browse', "libID=$libID"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ class datatable extends control
|
||||
$module = zget($this->config->datatable->moduleAlias, "$module-$method", $module);
|
||||
$setting = '';
|
||||
if(isset($this->config->datatable->$target->cols)) $setting = $this->config->datatable->$target->cols;
|
||||
|
||||
if(empty($setting))
|
||||
{
|
||||
$cols = $this->datatable->getFieldList($module, $method);
|
||||
|
||||
@@ -92,6 +92,8 @@ class datatableModel extends model
|
||||
$datatableId = $module . ucfirst($method);
|
||||
|
||||
$module = zget($this->config->datatable->moduleAlias, "$module-$method", $module);
|
||||
|
||||
|
||||
if(!isset($this->config->$module)) $this->loadModel($module);
|
||||
if(isset($this->config->datatable->$datatableId->cols)) $setting = json_decode($this->config->datatable->$datatableId->cols, true);
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ $config->execution->actionList = array();
|
||||
$config->execution->actionList['start']['icon'] = 'start';
|
||||
$config->execution->actionList['start']['text'] = $lang->execution->start;
|
||||
$config->execution->actionList['start']['hint'] = $lang->execution->start;
|
||||
$config->execution->actionList['start']['url'] = helper::createLink('execution', 'start',"executionID={rawID}");
|
||||
$config->execution->actionList['start']['url'] = helper::createLink('execution', 'start',"executionID={rawID}", '', true);
|
||||
$config->execution->actionList['start']['data-toggle'] = 'modal';
|
||||
|
||||
$config->execution->actionList['createTask']['icon'] = 'plus';
|
||||
@@ -210,10 +210,11 @@ $config->execution->actionList['activate']['hint'] = $lang->execution->ac
|
||||
$config->execution->actionList['activate']['url'] = helper::createLink('execution', 'activate',"executionID={rawID}");
|
||||
$config->execution->actionList['activate']['data-toggle'] = 'modal';
|
||||
|
||||
$config->execution->actionList['delete']['icon'] = 'trash';
|
||||
$config->execution->actionList['delete']['text'] = $lang->execution->delete;
|
||||
$config->execution->actionList['delete']['hint'] = $lang->execution->delete;
|
||||
$config->execution->actionList['delete']['url'] = helper::createLink('execution', 'delete',"executionID={rawID}");
|
||||
$config->execution->actionList['delete']['icon'] = 'trash';
|
||||
$config->execution->actionList['delete']['class'] = 'ajax-submit';
|
||||
$config->execution->actionList['delete']['text'] = $lang->execution->delete;
|
||||
$config->execution->actionList['delete']['hint'] = $lang->execution->delete;
|
||||
$config->execution->actionList['delete']['url'] = helper::createLink('execution', 'delete',"executionID={rawID}");
|
||||
|
||||
$config->execution->team = new stdclass();
|
||||
$config->execution->team->actionList['unlink']['icon'] = 'unlink';
|
||||
|
||||
@@ -72,7 +72,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return object current object
|
||||
*/
|
||||
public function commonAction($executionID = 0, $extra = '')
|
||||
public function commonAction(int $executionID = 0, string $extra = '')
|
||||
{
|
||||
$this->loadModel('product');
|
||||
|
||||
@@ -89,7 +89,7 @@ class execution extends control
|
||||
$this->execution->setMenu($executionID, $buildID = 0, $extra);
|
||||
|
||||
/* Assign. */
|
||||
$this->view->hidden = !empty($project->hasProduct) ? "" : 'hide';
|
||||
$this->view->hidden = !empty($project->hasProduct) ? '' : 'hide';
|
||||
$this->view->executions = $this->executions;
|
||||
$this->view->execution = $execution;
|
||||
$this->view->childExecutions = $childExecutions;
|
||||
@@ -699,6 +699,7 @@ class execution extends control
|
||||
$this->app->loadLang('testcase');
|
||||
|
||||
/* Change for requirement story title. */
|
||||
$this->lang->story->linkStory = str_replace($this->lang->URCommon, $this->lang->SRCommon, $this->lang->story->linkStory);
|
||||
if($storyType == 'requirement')
|
||||
{
|
||||
$this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
|
||||
@@ -2197,7 +2198,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function start($executionID, $from = 'execution')
|
||||
public function start(int $executionID, string $from = 'execution')
|
||||
{
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
@@ -2207,7 +2208,7 @@ class execution extends control
|
||||
{
|
||||
$this->loadModel('action');
|
||||
$changes = $this->execution->start($executionID);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
if($this->post->comment != '' or !empty($changes))
|
||||
{
|
||||
@@ -2220,14 +2221,11 @@ class execution extends control
|
||||
|
||||
$this->loadModel('common')->syncPPEStatus($executionID);
|
||||
$this->executeHooks($executionID);
|
||||
if(isonlybody() and $from == 'kanban')
|
||||
{
|
||||
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('doing')"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
|
||||
$response['closeModal'] = true;
|
||||
$response['load'] = true;
|
||||
if($from == 'kanban') $response['callback'] = "changeStatus('doing')";
|
||||
return $this->sendSuccess($response);
|
||||
}
|
||||
|
||||
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->start;
|
||||
@@ -2285,7 +2283,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function suspend($executionID, $from = 'execution')
|
||||
public function suspend(int $executionID, string $from = 'execution')
|
||||
{
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
@@ -2296,7 +2294,7 @@ class execution extends control
|
||||
$this->loadModel('action');
|
||||
$this->execution->computeBurn($executionID);
|
||||
$changes = $this->execution->suspend($executionID);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
if($this->post->comment != '' or !empty($changes))
|
||||
{
|
||||
@@ -2308,14 +2306,11 @@ class execution extends control
|
||||
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'suspend');
|
||||
|
||||
$this->executeHooks($executionID);
|
||||
if(isonlybody() and $from == 'kanban')
|
||||
{
|
||||
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('suspended')"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
|
||||
$response['closeModal'] = true;
|
||||
$response['load'] = true;
|
||||
if($from == 'kanban') $response['callback'] = "changeStatus('suspended')";
|
||||
return $this->sendSuccess($response);
|
||||
}
|
||||
|
||||
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->suspend;
|
||||
@@ -2332,7 +2327,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function activate($executionID, $from = 'execution')
|
||||
public function activate(int $executionID, string $from = 'execution')
|
||||
{
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
@@ -2341,20 +2336,17 @@ class execution extends control
|
||||
if(!empty($_POST))
|
||||
{
|
||||
$this->execution->activate($executionID);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
$project = $this->loadModel('project')->getById($execution->project);
|
||||
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'activate');
|
||||
|
||||
$this->executeHooks($executionID);
|
||||
if(isonlybody() and $from == 'kanban')
|
||||
{
|
||||
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('doing')"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
|
||||
$response['closeModal'] = true;
|
||||
$response['load'] = true;
|
||||
if($from == 'kanban') $response['callback'] = "changeStatus('doing')";
|
||||
return $this->sendSuccess($response);
|
||||
}
|
||||
|
||||
$newBegin = date('Y-m-d');
|
||||
@@ -2378,7 +2370,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function close($executionID, $from = 'execution')
|
||||
public function close(int $executionID, string $from = 'execution')
|
||||
{
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
@@ -2388,20 +2380,17 @@ class execution extends control
|
||||
{
|
||||
$this->execution->computeBurn($executionID);
|
||||
$this->execution->close($executionID);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
$project = $this->loadModel('project')->getById($execution->project);
|
||||
if($project->model == 'waterfall' or $project->model == 'waterfallplus') $this->loadModel('programplan')->computeProgress($executionID, 'close');
|
||||
|
||||
$this->executeHooks($executionID);
|
||||
if(isonlybody() and $from == 'kanban')
|
||||
{
|
||||
return print(js::closeModal('parent.parent', '', "parent.parent.changeStatus('closed')"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
|
||||
$response['closeModal'] = true;
|
||||
$response['load'] = true;
|
||||
if($from == 'kanban') $response['callback'] = "changeStatus('closed')";
|
||||
return $this->sendSuccess($response);
|
||||
}
|
||||
|
||||
$this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->execution->close;
|
||||
@@ -3018,7 +3007,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function delete($executionID, $confirm = 'no')
|
||||
public function delete(int $executionID, string $confirm = 'no')
|
||||
{
|
||||
if($confirm == 'no')
|
||||
{
|
||||
@@ -3054,7 +3043,8 @@ class execution extends control
|
||||
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
|
||||
}
|
||||
|
||||
return print(js::confirm($tips . sprintf($this->lang->execution->confirmDelete, $this->executions[$executionID]), $this->createLink('execution', 'delete', "executionID=$executionID&confirm=yes")));
|
||||
$tips = $tips . sprintf($this->lang->execution->confirmDelete, $this->executions[$executionID]);
|
||||
return $this->send(array('callback' => "confirmDeleteExecution({$executionID}, \"{$tips}\")"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3073,7 +3063,7 @@ class execution extends control
|
||||
if($message) $this->lang->saveSuccess = $message;
|
||||
|
||||
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
|
||||
return print(js::reload('parent'));
|
||||
return $this->send(array('result' => 'success', 'load' => true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4133,7 +4123,7 @@ class execution extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function importPlanStories($executionID, $planID, $productID = 0, $fromMethod = 'story', $extra = '', $param = '')
|
||||
public function importPlanStories(int $executionID, int $planID, int $productID = 0, string $fromMethod = 'story', string $extra = '', string $param = '')
|
||||
{
|
||||
$planStories = $planProducts = array();
|
||||
$planStory = $this->loadModel('story')->getPlanStories($planID);
|
||||
@@ -4200,8 +4190,9 @@ class execution extends control
|
||||
|
||||
$haveDraft = sprintf($importPlanStoryTips, $count);
|
||||
if(!$execution->multiple or $moduleName == 'projectstory') $haveDraft = str_replace($this->lang->executionCommon, $this->lang->projectCommon, $haveDraft);
|
||||
if($count != 0) echo js::alert($haveDraft) . js::locate($this->createLink($moduleName, $fromMethod, $param));
|
||||
return print(js::locate(helper::createLink($moduleName, $fromMethod, $param)));
|
||||
if($count != 0) return $this->sendError($haveDraft);
|
||||
|
||||
return $this->sendSuccess(array('closeModal' => true, 'load' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.tooltip {width: 120px !important}
|
||||
@@ -0,0 +1,2 @@
|
||||
.panel-form > .panel-heading.status-heading {justify-content: flex-start; padding-bottom: 0px;}
|
||||
.panel-form > .panel-heading.status-heading > .panel-title {font-weight: 400; justify-content:flex-end; width: 5rem;}
|
||||
@@ -2,6 +2,7 @@ window.renderRowData = function($row, index, row)
|
||||
{
|
||||
if(row.type == 'stage')
|
||||
{
|
||||
/* If is stage, modify lifetime to attribute. */
|
||||
let $attribute = $row.find('.form-batch-input[data-name="lifetime"]').empty();
|
||||
let name = $attribute.attr('name');
|
||||
$attribute.attr('name', name.replace('lifetime', 'attribute'));
|
||||
@@ -12,3 +13,8 @@ window.renderRowData = function($row, index, row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$().ready(function()
|
||||
{
|
||||
new zui.Tooltip('#tooltipHover', {trigger: 'hover', placement: 'right', type: 'white', 'className': 'text-gray border border-light'});
|
||||
})
|
||||
|
||||
@@ -22,7 +22,16 @@ $(document).off('click','.batch-btn').on('click', '.batch-btn', function()
|
||||
postAndLoadPage($('#toTaskForm').attr('action'), formData);
|
||||
|
||||
return false;
|
||||
});
|
||||
}).on('click', '#linkStoryByPlan button[type="submit"]', function()
|
||||
{
|
||||
var planID = $('#plan').val();
|
||||
if(planID)
|
||||
{
|
||||
$.ajaxSubmit({url: $.createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID)});
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
|
||||
/**
|
||||
* 计算表格信息的统计。
|
||||
|
||||
@@ -141,6 +141,7 @@ $lang->execution->list = "{$lang->executionCommon} List";
|
||||
$lang->execution->allProject = 'All';
|
||||
$lang->execution->method = 'Management Method';
|
||||
$lang->execution->sameAsParent = "Same as parent";
|
||||
$lang->execution->selectStoryPlan = 'Select Plan';
|
||||
|
||||
/* Fields of zt_team. */
|
||||
$lang->execution->root = 'Root';
|
||||
|
||||
@@ -141,6 +141,7 @@ $lang->execution->list = 'List';
|
||||
$lang->execution->allProject = 'All';
|
||||
$lang->execution->method = 'Management Method';
|
||||
$lang->execution->sameAsParent = "Same as parent";
|
||||
$lang->execution->selectStoryPlan = 'Select Plan';
|
||||
|
||||
/* Fields of zt_team. */
|
||||
$lang->execution->root = 'Root';
|
||||
|
||||
@@ -141,6 +141,7 @@ $lang->execution->list = "{$lang->executionCommon} List";
|
||||
$lang->execution->allProject = 'Tous';
|
||||
$lang->execution->method = 'Management Method';
|
||||
$lang->execution->sameAsParent = "Same as parent";
|
||||
$lang->execution->selectStoryPlan = 'Select Plan';
|
||||
|
||||
/* Fields of zt_team. */
|
||||
$lang->execution->root = 'Root';
|
||||
|
||||
@@ -141,6 +141,7 @@ $lang->execution->list = '列表';
|
||||
$lang->execution->allProject = '全部' . $lang->projectCommon;
|
||||
$lang->execution->method = '管理方法';
|
||||
$lang->execution->sameAsParent = "同父阶段";
|
||||
$lang->execution->selectStoryPlan = '选择计划';
|
||||
|
||||
/* Fields of zt_team. */
|
||||
$lang->execution->root = '源ID';
|
||||
|
||||
+28
-40
@@ -1181,7 +1181,7 @@ class executionModel extends model
|
||||
/* When it has multiple errors, only the first one is prompted */
|
||||
if(dao::isError() and count(dao::$errors['realBegan']) > 1) dao::$errors['realBegan'] = dao::$errors['realBegan'][0];
|
||||
|
||||
if(!dao::isError()) return common::createChanges($oldExecution, $execution);
|
||||
return common::createChanges($oldExecution, $execution);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,45 +1262,35 @@ class executionModel extends model
|
||||
|
||||
$execution = fixer::input('post')
|
||||
->add('id', $executionID)
|
||||
->setDefault('realEnd', '')
|
||||
->setDefault('realEnd', null)
|
||||
->setDefault('status', 'doing')
|
||||
->setDefault('lastEditedBy', $this->app->user->account)
|
||||
->setDefault('lastEditedDate', $now)
|
||||
->setDefault('closedBy', '')
|
||||
->setDefault('closedDate', '')
|
||||
->setDefault('closedDate', null)
|
||||
->stripTags($this->config->execution->editor->activate['id'], $this->config->allowedTags)
|
||||
->remove('comment,readjustTime,readjustTask')
|
||||
->remove('comment,readjustTask')
|
||||
->get();
|
||||
|
||||
if(empty($oldExecution->totalConsumed) and helper::isZeroDate($oldExecution->realBegan)) $execution->status = 'wait';
|
||||
$begin = $execution->begin;
|
||||
$end = $execution->end;
|
||||
|
||||
if(!$this->post->readjustTime)
|
||||
if($begin > $end) dao::$errors['end'] = sprintf($this->lang->execution->errorLetterPlan, $end, $begin);
|
||||
|
||||
if($oldExecution->grade > 1)
|
||||
{
|
||||
unset($execution->begin);
|
||||
unset($execution->end);
|
||||
}
|
||||
|
||||
if($this->post->readjustTime)
|
||||
{
|
||||
$begin = $execution->begin;
|
||||
$end = $execution->end;
|
||||
|
||||
if($begin > $end) dao::$errors["message"][] = sprintf($this->lang->execution->errorLetterPlan, $end, $begin);
|
||||
|
||||
if($oldExecution->grade > 1)
|
||||
$parent = $this->dao->select('begin,end')->from(TABLE_PROJECT)->where('id')->eq($oldExecution->parent)->fetch();
|
||||
$parentBegin = $parent->begin;
|
||||
$parentEnd = $parent->end;
|
||||
if($begin < $parentBegin)
|
||||
{
|
||||
$parent = $this->dao->select('begin,end')->from(TABLE_PROJECT)->where('id')->eq($oldExecution->parent)->fetch();
|
||||
$parentBegin = $parent->begin;
|
||||
$parentEnd = $parent->end;
|
||||
if($begin < $parentBegin)
|
||||
{
|
||||
dao::$errors["message"][] = sprintf($this->lang->execution->errorLetterParent, $parentBegin);
|
||||
}
|
||||
dao::$errors['begin'] = sprintf($this->lang->execution->errorLetterParent, $parentBegin);
|
||||
}
|
||||
|
||||
if($end > $parentEnd)
|
||||
{
|
||||
dao::$errors["message"][] = sprintf($this->lang->execution->errorGreaterParent, $parentEnd);
|
||||
}
|
||||
if($end > $parentEnd)
|
||||
{
|
||||
dao::$errors['end'] = sprintf($this->lang->execution->errorGreaterParent, $parentEnd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1314,7 +1304,7 @@ class executionModel extends model
|
||||
->exec();
|
||||
|
||||
/* Readjust task. */
|
||||
if($this->post->readjustTime and $this->post->readjustTask)
|
||||
if($this->post->readjustTask)
|
||||
{
|
||||
$beginTimeStamp = strtotime($execution->begin);
|
||||
$tasks = $this->dao->select('id,estStarted,deadline,status')->from(TABLE_TASK)
|
||||
@@ -1855,7 +1845,7 @@ class executionModel extends model
|
||||
$this->session->set('executionForm', $query->form);
|
||||
}
|
||||
}
|
||||
if($this->session->executionQuery == false) $this->session->set('executionQuery', ' 1 = 1');
|
||||
if($this->session->executionQuery === false) $this->session->set('executionQuery', ' 1 = 1');
|
||||
|
||||
$executionQuery = $this->session->executionQuery;
|
||||
$allProject = "`project` = 'all'";
|
||||
@@ -1865,7 +1855,6 @@ class executionModel extends model
|
||||
}
|
||||
|
||||
/* Get involved executions. */
|
||||
$myExecutionIDList = array();
|
||||
if($browseType == 'involved')
|
||||
{
|
||||
$myExecutionIDList = $this->dao->select('root')->from(TABLE_TEAM)
|
||||
@@ -2011,7 +2000,6 @@ class executionModel extends model
|
||||
$executionProducts = array();
|
||||
if($project->hasProduct and ($project->stageBy == 'product'))
|
||||
{
|
||||
$executionList = array();
|
||||
$executionProducts = $this->dao->select('t1.project, t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
|
||||
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')
|
||||
->where('project')->in(array_keys($executions))
|
||||
@@ -2191,7 +2179,7 @@ class executionModel extends model
|
||||
$branches = $this->loadModel('branch')->getByProducts(array_keys($productBranchPairs));
|
||||
foreach($productBranchPairs as $product => $branch)
|
||||
{
|
||||
if($branch == 0 and isset($branches[$product])) $productBranchPairs[$product] = join(',', array_keys($branches[$product]));
|
||||
if($branch == 0 and isset($branches[$product])) $productBranchPairs[$product] = implode(',', array_keys($branches[$product]));
|
||||
}
|
||||
|
||||
return $productBranchPairs;
|
||||
@@ -2339,7 +2327,7 @@ class executionModel extends model
|
||||
->orderBy('root asc')
|
||||
->fetchPairs('root', 'root');
|
||||
|
||||
$_SESSION['limitedExecutions'] = join(',', $executions);
|
||||
$_SESSION['limitedExecutions'] = implode(',', $executions);
|
||||
return $_SESSION['limitedExecutions'];
|
||||
}
|
||||
|
||||
@@ -2428,7 +2416,7 @@ class executionModel extends model
|
||||
}
|
||||
else
|
||||
{
|
||||
if($this->session->taskQuery == false) $this->session->set('taskQuery', ' 1 = 1');
|
||||
if($this->session->taskQuery === false) $this->session->set('taskQuery', ' 1 = 1');
|
||||
}
|
||||
|
||||
if(strpos($this->session->taskQuery, "deleted =") === false) $this->session->set('taskQuery', $this->session->taskQuery . " AND deleted = '0'");
|
||||
@@ -2757,7 +2745,7 @@ class executionModel extends model
|
||||
}
|
||||
|
||||
$branches = isset($_POST['branch']) ? $_POST['branch'] : array();
|
||||
$plans = isset($_POST['plans']) ? $_POST['plans'] : array();;
|
||||
$plans = isset($_POST['plans']) ? $_POST['plans'] : array();
|
||||
|
||||
$existedProducts = array();
|
||||
foreach($products as $i => $productID)
|
||||
@@ -2825,7 +2813,7 @@ class executionModel extends model
|
||||
->andWhere('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->lt(1)
|
||||
->andWhere('t1.execution')->in(array_keys($executions))
|
||||
->andWhere("(t1.story = 0 OR (t2.branch in ('0','" . join("','", $branches) . "') and t2.product " . helper::dbIN(array_keys($branches)) . "))")
|
||||
->andWhere("(t1.story = 0 OR (t2.branch in ('0','" . implode("','", $branches) . "') and t2.product " . helper::dbIN(array_keys($branches)) . "))")
|
||||
->fetchGroup('execution', 'id');
|
||||
return $tasks;
|
||||
}
|
||||
@@ -3062,7 +3050,7 @@ class executionModel extends model
|
||||
}
|
||||
|
||||
$taskID = $this->dao->lastInsertID();
|
||||
if($task->story != false) $this->story->setStage($task->story);
|
||||
if($task->story !== false) $this->story->setStage($task->story);
|
||||
$actionID = $this->loadModel('action')->create('task', $taskID, 'Opened', '');
|
||||
$mails[$key] = new stdClass();
|
||||
$mails[$key]->taskID = $taskID;
|
||||
@@ -3242,7 +3230,7 @@ class executionModel extends model
|
||||
|
||||
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
|
||||
parse_str($extra, $output);
|
||||
foreach($stories as $key => $storyID)
|
||||
foreach($stories as $storyID)
|
||||
{
|
||||
$notAllowedStatus = $this->app->rawMethod == 'batchcreate' ? 'closed' : 'draft,reviewing,closed';
|
||||
if(strpos($notAllowedStatus, $storyList[$storyID]->status) !== false) continue;
|
||||
@@ -3676,7 +3664,7 @@ class executionModel extends model
|
||||
$oldJoin = $this->dao->select('`account`, `join`')->from(TABLE_TEAM)->where('root')->eq($projectID)->andWhere('type')->eq($projectType)->fetchPairs();
|
||||
|
||||
$accounts = array();
|
||||
foreach($members as $account => $member)
|
||||
foreach($members as $member)
|
||||
{
|
||||
if(isset($oldJoin[$member->account])) continue;
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The activate view file of execution 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package execution
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$space = common::checkNotCN() ? ' ' : '';
|
||||
formPanel
|
||||
(
|
||||
set::title($lang->execution->activate . $space . $lang->executionCommon),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->execution->activate . $space . $lang->executionCommon),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($execution->name),
|
||||
set::entityID($execution->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formRow
|
||||
(
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->execution->beginAndEnd),
|
||||
inputGroup
|
||||
(
|
||||
input
|
||||
(
|
||||
set::control('date'),
|
||||
set::name('begin'),
|
||||
set::value($newBegin),
|
||||
),
|
||||
$lang->execution->to,
|
||||
input
|
||||
(
|
||||
set::control('date'),
|
||||
set::name('end'),
|
||||
set::value($newEnd),
|
||||
),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
setClass('items-center'),
|
||||
checkbox(
|
||||
set::name('readjustTask'),
|
||||
set::text($lang->execution->readjustTask),
|
||||
set::value(1),
|
||||
set::rootClass('ml-4'),
|
||||
)
|
||||
),
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
@@ -12,6 +12,7 @@ namespace zin;
|
||||
|
||||
$setCode = (isset($config->setCode) and $config->setCode == 1);
|
||||
$showMethod = $app->tab == 'project' and isset($project) and ($project->model == 'agileplus' or $project->model == 'waterfallplus');
|
||||
$typeTip = $this->app->tab == 'execution' ? $lang->execution->waterfallTip . lcfirst($lang->execution->typeTip) : $lang->execution->typeTip;
|
||||
|
||||
jsVar('stageList', $lang->stage->typeList);
|
||||
|
||||
@@ -101,10 +102,24 @@ formBatchPanel
|
||||
formBatchItem
|
||||
(
|
||||
set::name('lifetime'),
|
||||
set::label($lang->execution->type),
|
||||
set::label(
|
||||
$lang->execution->type
|
||||
),
|
||||
set::control('select'),
|
||||
set::items($lang->execution->lifeTimeList),
|
||||
set::width('80px'),
|
||||
set::tipIcon('help'),
|
||||
set::tip($typeTip),
|
||||
set
|
||||
(
|
||||
'tipProps',
|
||||
array
|
||||
(
|
||||
'id' => 'tooltipHover',
|
||||
'data-toggle' => 'tooltip',
|
||||
'data-placement' => 'right',
|
||||
)
|
||||
),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The close view file of execution 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package execution
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$space = common::checkNotCN() ? ' ' : '';
|
||||
formPanel
|
||||
(
|
||||
set::title($lang->execution->close . $space . $lang->executionCommon),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->execution->close . $space . $lang->executionCommon),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($execution->name),
|
||||
set::entityID($execution->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->execution->realEnd),
|
||||
set::name('realEnd'),
|
||||
set::control('date'),
|
||||
set::value(!helper::isZeroDate($execution->realEnd) ? $execution->realEnd : helper::today()),
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The start view file of execution 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package execution
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$space = common::checkNotCN() ? ' ' : '';
|
||||
formPanel
|
||||
(
|
||||
set::title($lang->execution->start . $space . $lang->executionCommon),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->execution->start . $space . $lang->executionCommon),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($execution->name),
|
||||
set::entityID($execution->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->execution->realBegan),
|
||||
set::name('realBegan'),
|
||||
set::control('date'),
|
||||
set::value(!helper::isZeroDate($execution->realBegan) ? $execution->realBegan : helper::today()),
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
+153
-170
@@ -16,185 +16,130 @@ featureBar
|
||||
(
|
||||
set::current($type),
|
||||
set::link(createLink($app->rawModule, $app->rawMethod, "&executionID=$executionID&storyType=$storyType&orderBy=$orderBy&type={key}")),
|
||||
li(searchToggle(set::module('story')))
|
||||
li(searchToggle(set::module('executionStory')))
|
||||
);
|
||||
|
||||
/* Build create story button. */
|
||||
$fnBuildCreateStoryButton = function() use ($lang, $product, $storyType, $productID, $executionID)
|
||||
{
|
||||
if(!common::canModify('product', $product)) return null;
|
||||
|
||||
$createLink = createLink('story', 'create', "product=$productID&branch=0&moduleID=0&storyID=0&objectID=$executionID&bugID=0&planID=0&todoID=0&extra=&storyType=$storyType");
|
||||
$batchCreateLink = createLink('story', 'batchCreate', "productID=$productID&branch=0&moduleID=0&storyID=0&executionID=$executionID&plan=0&storyType=$storyType");
|
||||
|
||||
$createBtnLink = '';
|
||||
$createBtnTitle = '';
|
||||
if(hasPriv($storyType, 'create'))
|
||||
{
|
||||
$createBtnLink = $createLink;
|
||||
$createBtnTitle = $lang->story->create;
|
||||
}
|
||||
elseif(hasPriv($storyType, 'batchCreate'))
|
||||
{
|
||||
$createBtnLink = empty($productID) ? '' : $batchCreateLink;
|
||||
$createBtnTitle = $lang->story->batchCreate;
|
||||
}
|
||||
|
||||
/* Without privilege, don't render create button. */
|
||||
if(empty($createBtnLink)) return null;
|
||||
|
||||
if(!empty($productID) && hasPriv($storyType, 'batchCreate') && hasPriv($storyType, 'create'))
|
||||
{
|
||||
$items = array();
|
||||
|
||||
if(commonModel::isTutorialMode())
|
||||
{
|
||||
/* Tutorial create link. */
|
||||
$wizardParams = helper::safe64Encode("productID=$productID&branch=0&moduleID=0");
|
||||
$link = $this->createLink('tutorial', 'wizard', "module=story&method=create¶ms=$wizardParams");
|
||||
$items[] = array('text' => $lang->story->createCommon, 'url' => $link);
|
||||
}
|
||||
else
|
||||
{
|
||||
$items[] = array('text' => $lang->story->create, 'url' => $createLink);
|
||||
}
|
||||
|
||||
$items[] = array('text' => $lang->story->batchCreate, 'url' => $batchCreateLink);
|
||||
|
||||
return dropdown
|
||||
(
|
||||
icon('plus'),
|
||||
$createBtnTitle,
|
||||
span(setClass('caret')),
|
||||
setClass('btn secondary'),
|
||||
set::items($items),
|
||||
);
|
||||
}
|
||||
|
||||
return item(set(array
|
||||
jsVar('executionID', $executionID);
|
||||
$linkStoryByPlanTips = $multiBranch ? sprintf($lang->execution->linkBranchStoryByPlanTips, $lang->project->branch) : $lang->execution->linkNormalStoryByPlanTips;
|
||||
$linkStoryByPlanTips = $execution->multiple ? $linkStoryByPlanTips : str_replace($lang->execution->common, $lang->projectCommon, $linkStoryByPlanTips);
|
||||
modal
|
||||
(
|
||||
setID('linkStoryByPlan'),
|
||||
set::modalProps(array('title' => $lang->execution->linkStoryByPlan)),
|
||||
div
|
||||
(
|
||||
'text' => $createBtnTitle,
|
||||
'icon' => 'plus',
|
||||
'type' => 'dropdown',
|
||||
'class' => 'secondary',
|
||||
'url' => $createBtnLink
|
||||
)));
|
||||
};
|
||||
|
||||
/* Build link story button. */
|
||||
$fnBuildLinkStoryButton = function() use($lang, $product, $productID, $executionID)
|
||||
{
|
||||
if(!common::canModify('product', $product)) return null;
|
||||
|
||||
/* Tutorial mode. */
|
||||
if(commonModel::isTutorialMode())
|
||||
{
|
||||
$wizardParams = helper::safe64Encode("project={$executionID}");
|
||||
|
||||
return item(set(array
|
||||
setClass('flex-auto'),
|
||||
icon('info-sign', setClass('warning-pale rounded-full mr-1')),
|
||||
$linkStoryByPlanTips
|
||||
),
|
||||
form
|
||||
(
|
||||
setClass('text-center', 'py-4'),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->execution->linkStory),
|
||||
formGroup
|
||||
(
|
||||
'text' => $lang->project->linkStory,
|
||||
'url' => createLink('tutorial', 'wizard', "module=project&method=linkStory¶ms=$wizardParams")
|
||||
)));
|
||||
}
|
||||
|
||||
$buttonLink = '';
|
||||
$buttonTitle = '';
|
||||
$dataToggle = '';
|
||||
if(common::hasPriv('projectstory', 'importPlanStories'))
|
||||
{
|
||||
$buttonLink = empty($productID) ? '' : '#linkStoryByPlan';
|
||||
$buttonTitle = $lang->execution->linkStoryByPlan;
|
||||
$dataToggle = 'data-toggle="modal"';
|
||||
}
|
||||
if(common::hasPriv('projectstory', 'linkStory'))
|
||||
{
|
||||
$buttonLink = $this->createLink('projectstory', 'linkStory', "project=0");
|
||||
$buttonTitle = $lang->execution->linkStory;
|
||||
$dataToggle = '';
|
||||
}
|
||||
|
||||
if(empty($buttonLink)) return null;
|
||||
|
||||
if(!empty($productID) && common::hasPriv('projectstory', 'linkStory') && common::hasPriv('projectstory', 'importPlanStories'))
|
||||
{
|
||||
$items = array();
|
||||
$items[] = array('text' => $lang->execution->linkStory, 'url' => createLink('projectstory', 'linkStory', "project=0"));
|
||||
$items[] = array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => $dataToggle);
|
||||
|
||||
return dropdown
|
||||
(
|
||||
icon('link'),
|
||||
$buttonTitle,
|
||||
span(setClass('caret')),
|
||||
setClass('btn primary'),
|
||||
set::items($items),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
set::label($lang->execution->selectStoryPlan),
|
||||
set::required(true),
|
||||
select
|
||||
(
|
||||
set::name('plan'),
|
||||
set::items($allPlans)
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
/* Show tool bar. */
|
||||
$canModifyProduct = common::canModify('product', $product);
|
||||
$canCreate = $canModifyProduct && hasPriv('story', 'create');
|
||||
$canBatchCreate = $canModifyProduct && hasPriv('story', 'canBatchCreate');
|
||||
$createLink = createLink('story', 'create', "product={$productID}&branch=0&moduleID=0&storyID=0&objectID={$executionID}&bugID=0&planID=0&todoID=0&extra=&storyType={$storyType}");
|
||||
$batchCreateLink = createLink('story', 'batchCreate', "productID={$productID}&branch=0&moduleID=0&storyID=0&executionID={$executionID}&plan=0&storyType={$storyType}");
|
||||
|
||||
/* Tutorial create link. */
|
||||
if(commonModel::isTutorialMode())
|
||||
{
|
||||
$wizardParams = helper::safe64Encode("productID={$productID}&branch=0&moduleID=0");
|
||||
$createLink = $this->createLink('tutorial', 'wizard', "module=story&method=create¶ms={$wizardParams}");
|
||||
$canBatchCreate = false;
|
||||
}
|
||||
|
||||
$createItem = array('text' => $lang->story->create, 'url' => $createLink);
|
||||
$batchCreateItem = array('text' => $lang->story->batchCreate, 'url' => $batchCreateLink);
|
||||
|
||||
$canLinkStory = $canModifyProduct && hasPriv('story', 'linkStory');
|
||||
$canlinkPlanStory = $canModifyProduct && hasPriv('story', 'importPlanStories');
|
||||
$linkStoryUrl = createLink('story', 'linkStory', "project={$executionID}");
|
||||
|
||||
if(commonModel::isTutorialMode())
|
||||
{
|
||||
$wizardParams = helper::safe64Encode("project={$executionID}");
|
||||
$linkStoryUrl = createLink('tutorial', 'wizard', "module=project&method=linkStory¶ms=$wizardParams");
|
||||
$canlinkPlanStory = false;
|
||||
}
|
||||
|
||||
$linkItem = array('text' => $lang->story->linkStory, 'url' => $linkStoryUrl);
|
||||
$linkPlanItem = array('text' => $lang->execution->linkStoryByPlan, 'url' => '#linkStoryByPlan', 'data-toggle' => 'modal', 'data-size' => 'sm');
|
||||
|
||||
toolbar
|
||||
(
|
||||
item(set(array
|
||||
hasPriv('story', 'report') ? item(set(array
|
||||
(
|
||||
'text' => $lang->story->report->common,
|
||||
'icon' => 'common-report icon-bar-chart muted',
|
||||
'class' => 'ghost'
|
||||
))),
|
||||
item(set(array
|
||||
(
|
||||
'text' => $lang->story->export,
|
||||
'icon' => 'export',
|
||||
'text' => $lang->story->report->common,
|
||||
'icon' => 'bar-chart',
|
||||
'class' => 'ghost',
|
||||
'url' => createLink('story', 'export', "productID=$productID&orderBy=$orderBy&executionID=$executionID&browseType=$type&storyType=$storyType"),
|
||||
))),
|
||||
$fnBuildCreateStoryButton(),
|
||||
$fnBuildLinkStoryButton()
|
||||
'url' => createLink('story', 'report', "productID={$productID}&branchID=&storyType={$storyType}&browseType={$type}&moduleID={$param}&chartType=pie&projectID={$execution->id}"),
|
||||
))) : null,
|
||||
hasPriv('story', 'export') ? item(set(array
|
||||
(
|
||||
'text' => $lang->story->export,
|
||||
'icon' => 'export',
|
||||
'class' => 'ghost',
|
||||
'url' => createLink('story', 'export', "productID=$productID&orderBy=$orderBy&executionID=$executionID&browseType=$type&storyType=$storyType"),
|
||||
'data-toggle' => 'modal'
|
||||
))) : null,
|
||||
|
||||
$canCreate && $canBatchCreate ? btngroup
|
||||
(
|
||||
btn
|
||||
(
|
||||
setClass('btn primary'),
|
||||
set::icon('plus'),
|
||||
set::url($createLink),
|
||||
$lang->story->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,
|
||||
|
||||
$canLinkStory && $canlinkPlanStory ? btngroup
|
||||
(
|
||||
btn(
|
||||
setClass('btn primary'),
|
||||
set::icon('plus'),
|
||||
set::url($linkStoryUrl),
|
||||
$lang->story->linkStory
|
||||
),
|
||||
dropdown
|
||||
(
|
||||
btn(setClass('btn primary dropdown-toggle'),
|
||||
setStyle(array('padding' => '6px', 'border-radius' => '0 2px 2px 0'))),
|
||||
set::items(array_filter(array($linkItem, $linkPlanItem))),
|
||||
set::placement('bottom-end'),
|
||||
)
|
||||
) : null,
|
||||
$canLinkStory && !$canlinkPlanStory ? item(set($linkItem + array('class' => 'btn primary', 'icon' => 'plus'))) : null,
|
||||
$canlinkPlanStory && !$canLinkStory ? item(set($linkPlanItem + array('class' => 'btn primary', 'icon' => 'plus'))) : null,
|
||||
);
|
||||
|
||||
|
||||
/* DataTable columns. */
|
||||
$setting = $this->datatable->getSetting('story');
|
||||
$cols = array_values($setting);
|
||||
foreach($cols as $key => $col)
|
||||
{
|
||||
$col['name'] = $col['id'];
|
||||
if($col['id'] == 'title')
|
||||
{
|
||||
$col['link'] = sprintf($col['link'], createLink('execution', 'storyView', array('storyID' => '${row.id}', 'execution' => $executionID)));
|
||||
}
|
||||
|
||||
$cols[$key] = $col;
|
||||
}
|
||||
|
||||
|
||||
/* DataTable data. */
|
||||
$this->loadModel('story');
|
||||
|
||||
$data = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$story->taskCount = $storyTasks[$story->id];
|
||||
$story->actions = $this->story->buildActionButtonList($story, 'browse');
|
||||
$story->plan = isset($story->planTitle) ? $story->planTitle : $plans[$story->plan];
|
||||
|
||||
$data[] = $story;
|
||||
|
||||
if(!isset($story->children)) continue;
|
||||
|
||||
/* Children. */
|
||||
foreach($story->children as $key => $child)
|
||||
{
|
||||
$child->taskCount = $storyTasks[$child->id];
|
||||
$child->actions = $this->story->buildActionButtonList($child, 'browse');
|
||||
|
||||
$data[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
sidebar
|
||||
(
|
||||
moduleMenu(set(array(
|
||||
@@ -345,9 +290,9 @@ if($canBatchAction)
|
||||
if($canBatchClose)
|
||||
{
|
||||
$footToolbar['items'][] = array(
|
||||
'text' => $lang->close,
|
||||
'class' => 'btn batch-btn ajax-btn size-sm secondary',
|
||||
'url' => $this->createLink('story', 'batchClose', "productID=0&executionID={$execution->id}")
|
||||
'text' => $lang->close,
|
||||
'class' => 'btn batch-btn size-sm secondary',
|
||||
'data-url' => $this->createLink('story', 'batchClose', "productID=0&executionID={$execution->id}")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -393,6 +338,44 @@ if($canBatchAction)
|
||||
}
|
||||
}
|
||||
|
||||
/* DataTable columns. */
|
||||
$setting = $this->datatable->getSetting('execution');
|
||||
$cols = array();
|
||||
foreach($setting as $col)
|
||||
{
|
||||
if(!$execution->hasProduct and $col['id'] == 'branch') continue;
|
||||
if(!$execution->hasProduct and !$execution->multiple and $value['id'] == 'plan') continue;
|
||||
if(!$execution->hasProduct and !$execution->multiple and $storyType == 'requirement' and $value['id'] == 'stage') continue;
|
||||
|
||||
$col['name'] = $col['id'];
|
||||
if($col['id'] == 'title') $col['link'] = sprintf($col['link'], createLink('execution', 'storyView', array('storyID' => '{id}', 'execution' => $executionID)));
|
||||
|
||||
$cols[] = $col;
|
||||
}
|
||||
|
||||
|
||||
/* DataTable data. */
|
||||
$data = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$story->taskCount = $storyTasks[$story->id];
|
||||
$story->actions = $this->story->buildActionButtonList($story, 'browse');
|
||||
$story->plan = isset($story->planTitle) ? $story->planTitle : $plans[$story->plan];
|
||||
|
||||
$data[] = $story;
|
||||
|
||||
if(!isset($story->children)) continue;
|
||||
|
||||
/* Children. */
|
||||
foreach($story->children as $key => $child)
|
||||
{
|
||||
$child->taskCount = $storyTasks[$child->id];
|
||||
$child->actions = $this->story->buildActionButtonList($child, 'browse', $execution);
|
||||
|
||||
$data[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
jsVar('cases', $storyCases);
|
||||
jsVar('summary', $summary);
|
||||
jsVar('checkedSummary', str_replace('%storyCommon%', $lang->SRCommon, $lang->product->checkedSummary));
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The suspend view file of execution 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package execution
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$space = common::checkNotCN() ? ' ' : '';
|
||||
formPanel
|
||||
(
|
||||
set::title($lang->execution->suspend . $space . $lang->executionCommon),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->execution->suspend . $space . $lang->executionCommon),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($execution->name),
|
||||
set::entityID($execution->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
@@ -48,7 +48,7 @@ $config->my->todo->actionList['edit']['data-toggle'] = 'modal';
|
||||
$config->my->todo->actionList['delete']['icon'] = 'trash';
|
||||
$config->my->todo->actionList['delete']['text'] = $lang->todo->delete;
|
||||
$config->my->todo->actionList['delete']['hint'] = $lang->todo->delete;
|
||||
$config->my->todo->actionList['delete']['url'] = array('module' => 'todo', 'method' => 'delete', 'params' => 'todoID={id}&confirm=yes');
|
||||
$config->my->todo->actionList['delete']['url'] = array('module' => 'todo', 'method' => 'delete', 'params' => 'todoID={id}&confirm=no');
|
||||
|
||||
$config->my->todo->dtable = new stdclass();
|
||||
$config->my->todo->dtable->fieldList['id']['name'] = 'id';
|
||||
|
||||
@@ -38,6 +38,9 @@ onRenderPage(function(info)
|
||||
*/
|
||||
onClickBatchEdit = function(event)
|
||||
{
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
/* Get checked product ID list. */
|
||||
const dtable = zui.DTable.query(event.target);
|
||||
const checkedList = dtable.$.getChecks();
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
window.footerGenerator = function(summary) {
|
||||
return [{html: summary, className: "text-dark"}, "flex", "pager"];
|
||||
}
|
||||
$(document).on('click', '.batch-btn', function()
|
||||
{
|
||||
const $this = $(this);
|
||||
const dtable = zui.DTable.query($('#stories'));
|
||||
const checkedList = dtable.$.getChecks();
|
||||
if(!checkedList.length) return;
|
||||
|
||||
const postData = new FormData();
|
||||
checkedList.forEach((id) => postData.append('storyIdList[]', id));
|
||||
if($this.data('account')) postData.append('assignedTo', $this.data('account'));
|
||||
|
||||
if($this.data('page') == 'batch')
|
||||
{
|
||||
postAndLoadPage($this.data('formaction'), postData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$.ajaxSubmit({"url": $this.data('formaction'), "data": postData, "callback": loadCurrentPage()});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1289,7 +1289,7 @@ class productTao extends productModel
|
||||
$stmt = $this->dbh->query($this->tree->buildMenuQuery($productID, 'story', 0, $branch));
|
||||
while($module = $stmt->fetch())
|
||||
{
|
||||
$module->url = call_user_func_array(array($this->tree, $userFunc[1]), array('story', $module, $extra));
|
||||
$module->url = call_user_func_array(array($this->tree, $userFunc[1]), array('story', $module, 0, $extra));
|
||||
$moduleTree[] = $module;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ dtable
|
||||
(
|
||||
set::cols($cols),
|
||||
set::data($fnGenerateTableData($productStats)),
|
||||
set::checkable(true),
|
||||
set::checkable(common::hasPriv('product', 'batchEdit')),
|
||||
set::sortLink(createLink('product', 'all', "browseType={$browseType}&orderBy={name}_{sortType}&recTotal={$recTotal}&recPerPage={$recPerPage}")),
|
||||
set::footToolbar(array
|
||||
(
|
||||
|
||||
@@ -194,17 +194,97 @@ toolbar
|
||||
$fnBuildLinkStoryButton()
|
||||
);
|
||||
|
||||
$canBeChanged = common::canModify('product', $product);
|
||||
$canBatchEdit = ($canBeChanged and common::hasPriv($storyType, 'batchEdit'));
|
||||
$canBatchClose = (common::hasPriv($storyType, 'batchClose') and strtolower($browseType) != 'closedbyme' and strtolower($browseType) != 'closedstory');
|
||||
$canBatchReview = ($canBeChanged and common::hasPriv($storyType, 'batchReview'));
|
||||
$canBatchChangeStage = ($canBeChanged and common::hasPriv('story', 'batchChangeStage') and $storyType == 'story');
|
||||
$canBatchChangeBranch = ($canBeChanged and common::hasPriv($storyType, 'batchChangeBranch') and $this->session->currentProductType and $this->session->currentProductType != 'normal' and $productID);
|
||||
$canBatchChangeModule = ($canBeChanged and common::hasPriv($storyType, 'batchChangeModule'));
|
||||
$canBatchChangePlan = ($canBeChanged and common::hasPriv('story', 'batchChangePlan') and $storyType == 'story' and (!$isProjectStory or $projectHasProduct or ($isProjectStory and isset($project->model) and $project->model == 'scrum')));
|
||||
$canBatchAssignTo = ($canBeChanged and common::hasPriv($storyType, 'batchAssignTo'));
|
||||
$canBatchUnlink = ($canBeChanged and $projectHasProduct and common::hasPriv('projectstory', 'batchUnlinkStory'));
|
||||
$canBatchImportToLib = ($canBeChanged and $isProjectStory and isset($this->config->maxVersion) and common::hasPriv('story', 'batchImportToLib') and helper::hasFeature('storylib'));
|
||||
$canBatchAction = ($canBatchEdit or $canBatchClose or $canBatchReview or $canBatchChangeStage or $canBatchChangeModule or $canBatchChangePlan or $canBatchAssignTo or $canBatchUnlink or $canBatchImportToLib or $canBatchChangeBranch);
|
||||
$footToolbar = $canBatchAction ? array('items' => array
|
||||
(
|
||||
array('type' => 'btn-group', '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', 'class' => 'btn btn-caret size-sm secondary', 'url' => '#navActions', 'data-toggle' => 'dropdown', 'data-placement' => 'top-start'),
|
||||
)),
|
||||
!$canBatchUnlink ? null : array('text' => $lang->story->unlink, 'className' => 'secondary', 'id' => 'batchUnlinkStory'),
|
||||
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'),
|
||||
!$canBatchImportToLib ? null : array('text' => $lang->story->importToLib, 'className' => 'btn secondary', 'id' => 'importToLib', 'data-toggle' => 'modal', 'url' => '#batchImportToLib'),
|
||||
), 'btnProps' => array('size' => 'sm', 'btnType' => 'secondary')) : null;
|
||||
|
||||
unset($lang->story->reviewResultList[''], $lang->story->reviewResultList['revert']);
|
||||
unset($lang->story->reasonList[''], $lang->story->reasonList['subdivided'], $lang->story->reasonList['duplicate']);
|
||||
unset($plans[''], $lang->story->stageList[''], $users['']);
|
||||
|
||||
foreach($lang->story->reviewResultList as $key => $result) $reviewResultItems[$key] = array('text' => $result, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchReview', "result=$key"));
|
||||
foreach($lang->story->reasonList as $key => $reason) $reviewRejectItems[] = array('text' => $reason, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchReview', "result=reject&reason=$key"));
|
||||
foreach($branchTagOption as $branchID => $branchName) $branchItems[] = array('text' => $branchName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchChangeBranch', "branchID=$branchID"));
|
||||
foreach($modules as $moduleID => $moduleName) $moduleItems[] = array('text' => $moduleName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchChangeModule', "moduleID=$moduleID"));
|
||||
foreach($plans as $planID => $planName) $planItems[] = array('text' => $planName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchChangePlan', "planID=$planID"));
|
||||
foreach($lang->story->stageList as $key => $stageName)
|
||||
{
|
||||
if(!str_contains('|tested|verified|released|closed|', "|$key|")) continue;
|
||||
$stageItems[] = array('text' => $stageName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchChangeStage', "stage=$key"));
|
||||
}
|
||||
foreach($users as $account => $realname)
|
||||
{
|
||||
if($account == 'closed') continue;
|
||||
$assignItems[] = array('text' => $realname, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchAssignTo', "productID={$product->id}"), 'data-account' => $account);
|
||||
}
|
||||
|
||||
if(isset($reviewResultItems['reject'])) $reviewResultItems['reject'] = array('class' => 'not-hide-menu', 'text' => $lang->story->reviewResultList['reject'], 'items' => $reviewRejectItems);
|
||||
$reviewResultItems = array_values($reviewResultItems);
|
||||
|
||||
$navActionItems = array();
|
||||
if($canBatchClose) $navActionItems[] = array('text' => $lang->close, 'class' => 'batch-btn', 'data-page' => 'batch', 'data-formaction' => helper::createLink('story', 'batchClose', "productID={$product->id}"));
|
||||
if($canBatchReview) $navActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->review, 'items' => $reviewResultItems);
|
||||
if($canBatchChangeBranch && $product->type != 'normal') $navActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->product->branchName[$product->type], 'items' => $branchItems);
|
||||
if($canBatchChangeStage) $navActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->stageAB, 'items' => $stageItems);
|
||||
|
||||
menu
|
||||
(
|
||||
set::id('navActions'),
|
||||
set::class('menu dropdown-menu'),
|
||||
set::items($navActionItems),
|
||||
);
|
||||
menu
|
||||
(
|
||||
set::id('navModule'),
|
||||
set::class('dropdown-menu'),
|
||||
set::items($moduleItems)
|
||||
);
|
||||
menu
|
||||
(
|
||||
set::id('navPlan'),
|
||||
set::class('dropdown-menu'),
|
||||
set::items($planItems)
|
||||
);
|
||||
menu
|
||||
(
|
||||
set::id('navAssignedTo'),
|
||||
set::class('dropdown-menu'),
|
||||
set::items($assignItems)
|
||||
);
|
||||
|
||||
dtable
|
||||
(
|
||||
set::id('stories'),
|
||||
set::userMap($users),
|
||||
set::customCols(true),
|
||||
set::groupDivider(true),
|
||||
set::checkable($canBatchAction),
|
||||
set::cols($cols),
|
||||
set::data($data),
|
||||
set::className('shadow rounded'),
|
||||
set::footPager(usePager()),
|
||||
set::nested(true),
|
||||
set::footer(jsRaw("function(){return window.footerGenerator.call(this, '{$summary}');}"))
|
||||
set::footToolbar($footToolbar),
|
||||
set::footer(array('checkbox', 'toolbar', array('html' => $summary, 'className' => "text-dark"), 'flex', 'pager')),
|
||||
);
|
||||
|
||||
render();
|
||||
|
||||
@@ -33,7 +33,7 @@ $(document).on('click', '.batch-btn', function()
|
||||
{
|
||||
const $this = $(this);
|
||||
const type = $this.data('type');
|
||||
const dtable = zui.DTable.query($this);
|
||||
const dtable = zui.DTable.query($('#' + type + 'DTable'));
|
||||
const checkedList = dtable.$.getChecks();
|
||||
if(!checkedList.length) return;
|
||||
|
||||
|
||||
@@ -90,26 +90,26 @@ unset($lang->story->reviewResultList[''], $lang->story->reviewResultList['revert
|
||||
unset($lang->story->reasonList[''], $lang->story->reasonList['subdivided'], $lang->story->reasonList['duplicate']);
|
||||
unset($plans[''], $lang->story->stageList[''], $users['']);
|
||||
|
||||
foreach($lang->story->reviewResultList as $key => $result) $reviewResultItems[$key] = array('text' => $result, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchReview', "result=$key"));
|
||||
foreach($lang->story->reasonList as $key => $reason) $reviewRejectItems[] = array('text' => $reason, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchReview', "result=reject&reason=$key"));
|
||||
foreach($branchTagOption as $branchID => $branchName) $branchItems[] = array('text' => $branchName, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangeBranch', "branchID=$branchID"));
|
||||
foreach($modules as $moduleID => $moduleName) $moduleItems[] = array('text' => $moduleName, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangeModule', "moduleID=$moduleID"));
|
||||
foreach($plans as $planID => $planName) $planItems[] = array('text' => $planName, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangePlan', "planID=$planID&oldPlanID={$plan->id}"));
|
||||
foreach($lang->story->stageList as $key => $stageName) $stageItems[] = array('text' => $stageName, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangeStage', "stage=$key"));
|
||||
foreach($lang->story->reviewResultList as $key => $result) $reviewResultItems[$key] = array('text' => $result, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchReview', "result=$key"));
|
||||
foreach($lang->story->reasonList as $key => $reason) $reviewRejectItems[] = array('text' => $reason, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchReview', "result=reject&reason=$key"));
|
||||
foreach($branchTagOption as $branchID => $branchName) $branchItems[] = array('text' => $branchName, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangeBranch', "branchID=$branchID"));
|
||||
foreach($modules as $moduleID => $moduleName) $moduleItems[] = array('text' => $moduleName, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangeModule', "moduleID=$moduleID"));
|
||||
foreach($plans as $planID => $planName) $planItems[] = array('text' => $planName, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangePlan', "planID=$planID&oldPlanID={$plan->id}"));
|
||||
foreach($lang->story->stageList as $key => $stageName) $stageItems[] = array('text' => $stageName, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchChangeStage', "stage=$key"));
|
||||
foreach($users as $account => $realname)
|
||||
{
|
||||
if($account == 'closed') continue;
|
||||
$assignItems[] = array('text' => $realname, 'className' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchAssignTo', "productID=$plan->product"), 'data-account' => $account);
|
||||
$assignItems[] = array('text' => $realname, 'class' => 'batch-btn', 'data-type' => 'story', 'data-formaction' => $this->createLink('story', 'batchAssignTo', "productID=$plan->product"), 'data-account' => $account);
|
||||
}
|
||||
|
||||
if(isset($reviewResultItems['reject'])) $reviewResultItems['reject'] = array('class' => 'not-hide-menu', 'text' => $lang->story->reviewResultList['reject'], 'items' => $reviewRejectItems);
|
||||
$reviewResultItems = array_values($reviewResultItems);
|
||||
|
||||
$navStoryActionItems = array();
|
||||
if($canBatchCloseStory) $navStoryActionItems[] = array('text' => $lang->close, 'className' => 'batch-btn', 'data-type' => 'story', 'data-page' => 'batch', 'data-formaction' => helper::createLink('story', 'batchClose', "productID={$plan->product}"));
|
||||
if($canBatchEditStory) $navStoryActionItems[] = array('text' => $lang->edit, 'className' => 'batch-btn', 'data-type' => 'story', 'data-page' => 'batch', 'data-formaction' => helper::createLink('story', 'batchEdit', "productID=$plan->product&projectID=$projectID&branch=$branch"));
|
||||
if($canBatchCloseStory) $navStoryActionItems[] = array('text' => $lang->close, 'class' => 'batch-btn', 'data-type' => 'story', 'data-page' => 'batch', 'data-formaction' => helper::createLink('story', 'batchClose', "productID={$plan->product}"));
|
||||
if($canBatchEditStory) $navStoryActionItems[] = array('text' => $lang->edit, 'class' => 'batch-btn', 'data-type' => 'story', 'data-page' => 'batch', 'data-formaction' => helper::createLink('story', 'batchEdit', "productID=$plan->product&projectID=$projectID&branch=$branch"));
|
||||
if($canBatchReviewStory) $navStoryActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->review, 'items' => $reviewResultItems);
|
||||
if($canBatchChangeBranchStory && $product->type != 'normal') $navStoryActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->product->branchName[$this->session->currentProductType], 'items' => $branchItems);
|
||||
if($canBatchChangeBranchStory && $product->type != 'normal') $navStoryActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->product->branchName[$product->type], 'items' => $branchItems);
|
||||
if($canBatchChangeModuleStory) $navStoryActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->moduleAB, 'items' => $moduleItems);
|
||||
if($canBatchChangePlanStory) $navStoryActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->planAB, 'items' => $planItems);
|
||||
if($canBatchChangeStageStory) $navStoryActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->stageAB, 'items' => $stageItems);
|
||||
@@ -122,12 +122,11 @@ zui::menu
|
||||
set::items($navStoryActionItems)
|
||||
);
|
||||
|
||||
|
||||
$planItems = array();
|
||||
foreach($plans as $planID => $planName) $planItems[] = array('text' => $planName, 'className' => 'batch-btn', 'data-type' => 'bug', 'data-formaction' => $this->createLink('bug', 'batchChangePlan', "planID=$planID"));
|
||||
foreach($plans as $planID => $planName) $planItems[] = array('text' => $planName, 'class' => 'batch-btn', 'data-type' => 'bug', 'data-formaction' => $this->createLink('bug', 'batchChangePlan', "planID=$planID"));
|
||||
|
||||
$navBugActionItems = array();
|
||||
if($canBatchEditBug) $navBugActionItems[] = array('text' => $lang->edit, 'className' => 'batch-btn', 'data-type' => 'bug', 'data-page' => 'batch', 'data-formaction' => helper::createLink('bug', 'batchEdit', "productID=$plan->product&branch=$branch"));
|
||||
if($canBatchEditBug) $navBugActionItems[] = array('text' => $lang->edit, 'class' => 'batch-btn', 'data-type' => 'bug', 'data-page' => 'batch', 'data-formaction' => helper::createLink('bug', 'batchEdit', "productID=$plan->product&branch=$branch"));
|
||||
if($canBatchChangePlanBug) $navBugActionItems[] = array('class' => 'not-hide-menu', 'text' => $lang->story->planAB, 'items' => $planItems);
|
||||
zui::menu
|
||||
(
|
||||
|
||||
@@ -319,3 +319,134 @@ $config->program->projectView->dtable->fieldList['actions']['actionsMap'] = arra
|
||||
'project_whitelist' => array('icon' => 'icon-shield-check', 'hint' => $lang->project->whitelist),
|
||||
'project_delete' => array('icon' => 'icon-trash', 'hint' => $lang->project->delete)
|
||||
);
|
||||
|
||||
/* DataTable fields of browse View. */
|
||||
$config->program->browse = new stdClass();
|
||||
$config->program->browse->dtable = new stdClass();
|
||||
$config->program->browse->dtable->fieldList = array();
|
||||
|
||||
$config->program->browse->dtable->fieldList['name']['name'] = 'name';
|
||||
$config->program->browse->dtable->fieldList['name']['title'] = $lang->nameAB;
|
||||
$config->program->browse->dtable->fieldList['name']['width'] = 200;
|
||||
$config->program->browse->dtable->fieldList['name']['type'] = 'link';
|
||||
$config->program->browse->dtable->fieldList['name']['link'] = "RAWJS<function(info){const {row, col} = info; if(row.data.type == 'project') return {url:$.createLink('project', 'browse', 'projectID={id}')}; if(row.data.type == 'program') return {url:$.createLink('program', 'view', 'programID={id}')};}>RAWJS";
|
||||
$config->program->browse->dtable->fieldList['name']['flex'] = 1;
|
||||
$config->program->browse->dtable->fieldList['name']['nestedToggle'] = true;
|
||||
$config->program->browse->dtable->fieldList['name']['checkbox'] = true;
|
||||
$config->program->browse->dtable->fieldList['name']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['name']['iconRender'] = 'RAWJS<function(val,row){ if(row.data.type === \'program\') return \'icon-cards-view text-gray\'; if(row.data.type === \'productLine\') return \'icon-scrum text-gray\'; return \'\';}>RAWJS';
|
||||
$config->program->browse->dtable->fieldList['name']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['name']['group'] = 1;
|
||||
|
||||
$config->program->browse->dtable->fieldList['status']['name'] = 'status';
|
||||
$config->program->browse->dtable->fieldList['status']['title'] = $lang->program->status;
|
||||
$config->program->browse->dtable->fieldList['status']['minWidth'] = 60;
|
||||
$config->program->browse->dtable->fieldList['status']['type'] = 'status';
|
||||
$config->program->browse->dtable->fieldList['status']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['status']['statusMap'] = $lang->program->statusList;
|
||||
$config->program->browse->dtable->fieldList['status']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['status']['group'] = 2;
|
||||
|
||||
$config->program->browse->dtable->fieldList['PM']['name'] = 'PM';
|
||||
$config->program->browse->dtable->fieldList['PM']['title'] = $lang->program->PM;
|
||||
$config->program->browse->dtable->fieldList['PM']['minWidth'] = 80;
|
||||
$config->program->browse->dtable->fieldList['PM']['type'] = 'avatarBtn';
|
||||
$config->program->browse->dtable->fieldList['PM']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['PM']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['PM']['group'] = 3;
|
||||
|
||||
$config->program->browse->dtable->fieldList['budget']['name'] = 'budget';
|
||||
$config->program->browse->dtable->fieldList['budget']['title'] = $lang->program->budget;
|
||||
$config->program->browse->dtable->fieldList['budget']['width'] = 90;
|
||||
$config->program->browse->dtable->fieldList['budget']['type'] = 'format';
|
||||
$config->program->browse->dtable->fieldList['budget']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['budget']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['budget']['group'] = 4;
|
||||
|
||||
$config->program->browse->dtable->fieldList['invested']['name'] = 'invested';
|
||||
$config->program->browse->dtable->fieldList['invested']['title'] = $lang->program->invested;
|
||||
$config->program->browse->dtable->fieldList['invested']['minWidth'] = 70;
|
||||
$config->program->browse->dtable->fieldList['invested']['type'] = 'format';
|
||||
$config->program->browse->dtable->fieldList['invested']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['invested']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['invested']['group'] = 4;
|
||||
|
||||
$config->program->browse->dtable->fieldList['openedDate']['name'] = 'openedDate';
|
||||
$config->program->browse->dtable->fieldList['openedDate']['title'] = $lang->program->openedDate;
|
||||
$config->program->browse->dtable->fieldList['openedDate']['type'] = 'date';
|
||||
$config->program->browse->dtable->fieldList['openedDate']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['openedDate']['minWidth'] = 90;
|
||||
$config->program->browse->dtable->fieldList['openedDate']['group'] = 5;
|
||||
|
||||
$config->program->browse->dtable->fieldList['openedBy']['name'] = 'openedBy';
|
||||
$config->program->browse->dtable->fieldList['openedBy']['title'] = $lang->program->openedBy;
|
||||
$config->program->browse->dtable->fieldList['openedBy']['type'] = 'user';
|
||||
$config->program->browse->dtable->fieldList['openedBy']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['openedBy']['minWidth'] = 80;
|
||||
$config->program->browse->dtable->fieldList['openedBy']['group'] = 5;
|
||||
|
||||
$config->program->browse->dtable->fieldList['begin']['name'] = 'begin';
|
||||
$config->program->browse->dtable->fieldList['begin']['title'] = $lang->program->begin;
|
||||
$config->program->browse->dtable->fieldList['begin']['minWidth'] = 90;
|
||||
$config->program->browse->dtable->fieldList['begin']['type'] = 'date';
|
||||
$config->program->browse->dtable->fieldList['begin']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['begin']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['begin']['group'] = 6;
|
||||
|
||||
$config->program->browse->dtable->fieldList['end']['name'] = 'end';
|
||||
$config->program->browse->dtable->fieldList['end']['title'] = $lang->program->end;
|
||||
$config->program->browse->dtable->fieldList['end']['minWidth'] = 90;
|
||||
$config->program->browse->dtable->fieldList['end']['type'] = 'date';
|
||||
$config->program->browse->dtable->fieldList['end']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['end']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['end']['group'] = 6;
|
||||
|
||||
$config->program->browse->dtable->fieldList['realBegan']['name'] = 'realBegan';
|
||||
$config->program->browse->dtable->fieldList['realBegan']['title'] = $lang->program->realBeganAB;
|
||||
$config->program->browse->dtable->fieldList['realBegan']['minWidth'] = 90;
|
||||
$config->program->browse->dtable->fieldList['realBegan']['type'] = 'date';
|
||||
$config->program->browse->dtable->fieldList['realBegan']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['realBegan']['group'] = 7;
|
||||
|
||||
$config->program->browse->dtable->fieldList['realEnd']['name'] = 'realEnd';
|
||||
$config->program->browse->dtable->fieldList['realEnd']['title'] = $lang->program->realEndAB;
|
||||
$config->program->browse->dtable->fieldList['realEnd']['minWidth'] = 90;
|
||||
$config->program->browse->dtable->fieldList['realEnd']['type'] = 'date';
|
||||
$config->program->browse->dtable->fieldList['realEnd']['sortType'] = true;
|
||||
$config->program->browse->dtable->fieldList['realEnd']['group'] = 7;
|
||||
|
||||
$config->program->browse->dtable->fieldList['progress']['name'] = 'progress';
|
||||
$config->program->browse->dtable->fieldList['progress']['title'] = $lang->program->progressAB;
|
||||
$config->program->browse->dtable->fieldList['progress']['minWidth'] = 100;
|
||||
$config->program->browse->dtable->fieldList['progress']['type'] = 'progress';
|
||||
$config->program->browse->dtable->fieldList['progress']['show'] = true;
|
||||
$config->program->browse->dtable->fieldList['progress']['group'] = 8;
|
||||
|
||||
$config->program->browse->dtable->fieldList['actions']['name'] = 'actions';
|
||||
$config->program->browse->dtable->fieldList['actions']['title'] = $lang->actions;
|
||||
$config->program->browse->dtable->fieldList['actions']['width'] = 160;
|
||||
$config->program->browse->dtable->fieldList['actions']['type'] = 'actions';
|
||||
$config->program->browse->dtable->fieldList['actions']['fixed'] = 'right';
|
||||
$config->program->browse->dtable->fieldList['actions']['actionsMap'] = array
|
||||
(
|
||||
'program_start' => array('icon' => 'icon-start', 'hint' => $lang->program->start),
|
||||
'program_suspend' => array('icon' => 'icon-pause', 'hint' => $lang->program->suspend),
|
||||
'program_close' => array('icon' => 'icon-off', 'hint' => $lang->program->close),
|
||||
'program_activate' => array('icon' => 'icon-active', 'hint' => $lang->program->activate),
|
||||
'program_other' => array('caret' => true, 'hint' => $lang->other, 'type' => 'dropdown'),
|
||||
'program_edit' => array('icon' => 'icon-edit', 'hint' => $lang->program->edit),
|
||||
'program_create' => array('icon' => 'icon-split', 'hint' => $lang->program->create),
|
||||
'program_delete' => array('icon' => 'icon-trash', 'hint' => $lang->program->delete),
|
||||
'project_start' => array('icon' => 'icon-start', 'hint' => $lang->project->start),
|
||||
'project_suspend' => array('icon' => 'icon-pause', 'hint' => $lang->project->suspend),
|
||||
'project_close' => array('icon' => 'icon-off', 'hint' => $lang->project->close),
|
||||
'project_activate' => array('icon' => 'icon-active', 'hint' => $lang->project->activate),
|
||||
'project_other' => array('caret' => true, 'hint' => $lang->project->other, 'type' => 'dropdown'),
|
||||
'project_edit' => array('icon' => 'icon-edit', 'hint' => $lang->project->edit),
|
||||
'project_team' => array('icon' => 'icon-groups', 'hint' => $lang->project->manageMembers),
|
||||
'project_group' => array('icon' => 'icon-lock', 'hint' => $lang->project->group),
|
||||
'project_more' => array('icon' => 'icon-ellipsis-v', 'hint' => $lang->project->moreActions, 'type' => 'dropdown', 'caret' => false),
|
||||
'project_link' => array('icon' => 'icon-link', 'hint' => $lang->project->manageProducts),
|
||||
'project_whitelist' => array('icon' => 'icon-shield-check', 'hint' => $lang->project->whitelist),
|
||||
'project_delete' => array('icon' => 'icon-trash', 'hint' => $lang->project->delete)
|
||||
);
|
||||
|
||||
+12
-12
@@ -883,7 +883,7 @@ class program extends control
|
||||
/* Load module and set session. */
|
||||
$this->loadModel('product');
|
||||
$this->loadModel('user');
|
||||
$this->session->set('productView', $this->app->getURI(true), 'program');
|
||||
$this->session->set('productList', $this->app->getURI(true), 'program');
|
||||
|
||||
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
|
||||
|
||||
@@ -913,17 +913,17 @@ class program extends control
|
||||
$actionURL = $this->createLink('program', 'productview', "browseType=bySearch&orderBy=order_asc&queryID=myQueryID");
|
||||
$this->product->buildProductSearchForm($param, $actionURL);
|
||||
|
||||
$this->view->title = $this->lang->product->common;
|
||||
$this->view->recTotal = $pager->recTotal;
|
||||
$this->view->productStats = $productStats;
|
||||
$this->view->productStructure = $productStructure;
|
||||
$this->view->productLines = $productLines;
|
||||
$this->view->programLines = $programLines;
|
||||
$this->view->users = $this->user->getPairs('noletter');
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->browseType = $browseType;
|
||||
$this->view->pager = $pager;
|
||||
$this->view->showBatchEdit = $this->cookie->showProductBatchEdit;
|
||||
$this->view->title = $this->lang->product->common;
|
||||
$this->view->recTotal = $pager->recTotal;
|
||||
$this->view->productStats = $productStats;
|
||||
$this->view->productStructure = $productStructure;
|
||||
$this->view->productLines = $productLines;
|
||||
$this->view->programLines = $programLines;
|
||||
$this->view->users = $this->user->getPairs('noletter');
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->browseType = $browseType;
|
||||
$this->view->pager = $pager;
|
||||
$this->view->checkedEditProduct = !empty((int)$this->cookie->checkedEditProduct);
|
||||
|
||||
$this->render();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ window.footerGenerator = function()
|
||||
return [{children: summary, className: "text-dark"}, "flex", "pager"];
|
||||
}
|
||||
|
||||
window.renderReleaseCountCell = function(result, {col, row})
|
||||
window.renderCellProductView = function(result, {col, row})
|
||||
{
|
||||
if(col.name === 'createdDate')
|
||||
{
|
||||
@@ -47,3 +47,70 @@ window.iconRenderProductView = function(value, row)
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit data to product batch edit page by html form while click on the batch edit button.
|
||||
*
|
||||
* @param object event
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
onClickBatchEdit = function(event)
|
||||
{
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
/* Get checked product ID list. */
|
||||
const dtable = zui.DTable.query(event.target);
|
||||
const checkedList = dtable.$.getChecks();
|
||||
|
||||
if(checkedList.length === 0) return;
|
||||
|
||||
/* Create form. */
|
||||
const f = document.createElement("form");
|
||||
f.action = $(event.target).attr('href');
|
||||
f.method = "POST";
|
||||
f.target = "_self";
|
||||
|
||||
/* Create element to carry data. */
|
||||
checkedList.forEach(function(id)
|
||||
{
|
||||
if(id.includes('-')) return;
|
||||
|
||||
const item = document.createElement('input');
|
||||
item.name = 'productIDList[]';
|
||||
item.value = id;
|
||||
|
||||
f.appendChild(item);
|
||||
});
|
||||
|
||||
/* Append form to body. */
|
||||
document.body.appendChild(f);
|
||||
|
||||
f.submit();
|
||||
}
|
||||
|
||||
onClickCheckBatchEdit = function(event)
|
||||
{
|
||||
$.cookie.set('checkedEditProduct', 1);
|
||||
loadCurrentPage(["table/#dtable:type=json&data=props"]);
|
||||
}
|
||||
|
||||
window.footerSummary = function(checkedIdList)
|
||||
{
|
||||
if(!checkedIdList.length)
|
||||
{
|
||||
return {html: pageSummary, className: 'text-dark'};
|
||||
}
|
||||
|
||||
let totalProducts = 0;
|
||||
checkedIdList.forEach(function(id)
|
||||
{
|
||||
if(id.includes('-')) return;
|
||||
totalProducts++;
|
||||
});
|
||||
|
||||
var summary = checkedSummary.replace('%total%', totalProducts);
|
||||
|
||||
return {html: summary};
|
||||
}
|
||||
|
||||
@@ -223,3 +223,5 @@ $lang->program->project = 'Projects';
|
||||
$lang->program->totalBugs = 'Total Bugs';
|
||||
$lang->program->latestReleaseDate = 'Latest Release Date';
|
||||
$lang->program->latestRelease = 'Latest Release';
|
||||
$lang->program->manageLine = 'Verwalte Produktlinie';
|
||||
$lang->program->checkedProducts = '<strong>%total%</strong> products selected.';
|
||||
|
||||
@@ -223,3 +223,5 @@ $lang->program->project = 'Projects';
|
||||
$lang->program->totalBugs = 'Total Bugs';
|
||||
$lang->program->latestReleaseDate = 'Latest Release Date';
|
||||
$lang->program->latestRelease = 'Latest Release';
|
||||
$lang->program->manageLine = 'Manage Product Line';
|
||||
$lang->program->checkedProducts = '<strong>%total%</strong> products selected.';
|
||||
|
||||
@@ -223,3 +223,5 @@ $lang->program->project = 'Projects';
|
||||
$lang->program->totalBugs = 'Total Bugs';
|
||||
$lang->program->latestReleaseDate = 'Latest Release Date';
|
||||
$lang->program->latestRelease = 'Latest Release';
|
||||
$lang->program->manageLine = 'Gérer Ligne Produit';
|
||||
$lang->program->checkedProducts = '<strong>%total%</strong> products selected.';
|
||||
|
||||
@@ -223,3 +223,5 @@ $lang->program->project = '项目';
|
||||
$lang->program->totalBugs = 'Bug总数';
|
||||
$lang->program->latestReleaseDate = '最新发布时间';
|
||||
$lang->program->latestRelease = '最新发布';
|
||||
$lang->program->manageLine = '维护产品线';
|
||||
$lang->program->checkedProducts = '共选中 <strong>%total%</strong> 个产品。';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace zin;
|
||||
|
||||
$cols = array_values($config->program->projectView->dtable->fieldList);
|
||||
$cols = $this->loadModel('datatable')->getSetting('program');
|
||||
$data = array();
|
||||
foreach($programs as $program)
|
||||
{
|
||||
|
||||
@@ -12,10 +12,69 @@ declare(strict_types=1);
|
||||
|
||||
namespace zin;
|
||||
|
||||
/* Generate cols for the data table. */
|
||||
$fnGenerateCols = function()
|
||||
$canBatchEdit = common::hasPriv('product', 'batchEdit');
|
||||
$hasProduct = false;
|
||||
|
||||
/* Closure creating program buttons. */
|
||||
$fnGenerateCreateProgramBtns = function() use ($lang, $browseType)
|
||||
{
|
||||
return $this->loadModel('datatable') ->getSetting('program');
|
||||
$items = array();
|
||||
|
||||
hasPriv('program', 'create') ? $items[] = array
|
||||
(
|
||||
'text' => $lang->program->create,
|
||||
'icon' => 'plus',
|
||||
'url' => createLink('program', 'create')
|
||||
) : null;
|
||||
|
||||
hasPriv('product', 'create') ? $items[] = array
|
||||
(
|
||||
'text' => $lang->program->createProduct,
|
||||
'icon' => 'plus',
|
||||
'url' => createLink('product', 'create')
|
||||
) : null;
|
||||
|
||||
hasPriv('product', 'manageLine') ? $items[] = array
|
||||
(
|
||||
'text' => $lang->program->manageLine,
|
||||
'icon' => 'edit',
|
||||
'data-toggle' => 'modal',
|
||||
'data-url' => createLink('product', 'manageLine', $browseType)
|
||||
) : null;
|
||||
|
||||
return inputGroup
|
||||
(
|
||||
hasPriv('program', 'create') ? btn
|
||||
(
|
||||
setClass('btn primary'),
|
||||
set::icon('plus'),
|
||||
set::text($lang->program->create),
|
||||
set::url(createLink('program', 'create'))
|
||||
) : null,
|
||||
!empty($items) ? dropdown
|
||||
(
|
||||
setClass('btn primary'),
|
||||
span(setClass('caret')),
|
||||
set::items($items),
|
||||
) : null
|
||||
);
|
||||
};
|
||||
|
||||
/* Generate cols for the data table. */
|
||||
$fnGenerateCols = function() use ($canBatchEdit)
|
||||
{
|
||||
$cols = $this->loadModel('datatable') ->getSetting('program');
|
||||
|
||||
foreach($cols as $colName => &$setting)
|
||||
{
|
||||
if($colName == 'name')
|
||||
{
|
||||
$setting['checkbox'] = $canBatchEdit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $cols;
|
||||
};
|
||||
|
||||
/* Closure for generating program row data. */
|
||||
@@ -155,15 +214,16 @@ foreach($productStructure as $programID => $program)
|
||||
foreach($line['products'] as $productID => $product)
|
||||
{
|
||||
$productRow = $fnGenerateProductRowData($lineID, $product);
|
||||
if(!empty($productRow))
|
||||
{
|
||||
$totalProductExecutions += $productRow->totalExecutions;
|
||||
$totalProductBugs += $productRow->totalBugs;
|
||||
$totalProductActiveBugs += $productRow->totalActivatedBugs;
|
||||
$totalProductProjects += $productRow->totalProjects;
|
||||
|
||||
$data[] = $productRow;
|
||||
}
|
||||
$totalProductExecutions += $productRow->totalExecutions;
|
||||
$totalProductBugs += $productRow->totalBugs;
|
||||
$totalProductActiveBugs += $productRow->totalActivatedBugs;
|
||||
$totalProductProjects += $productRow->totalProjects;
|
||||
|
||||
$data[] = $productRow;
|
||||
|
||||
/* Set flag variable. */
|
||||
$hasProduct = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,22 +258,23 @@ foreach($productStructure as $programID => $program)
|
||||
}
|
||||
}
|
||||
|
||||
$summary = sprintf($lang->product->lineSummary, $linesCount, count($productStats));
|
||||
jsVar('summary', $summary);
|
||||
|
||||
/* Layout. */
|
||||
/* ZIN: layout. */
|
||||
|
||||
featureBar
|
||||
(
|
||||
set::current($browseType),
|
||||
set::linkParams("status={key}&orderBy=$orderBy"),
|
||||
(hasPriv('product', 'batchEdit') && $hasProduct === true) ? item
|
||||
(hasPriv('product', 'batchEdit') && $hasProduct === true) ? li(checkbox
|
||||
(
|
||||
on::click('onClickCheckBatchEdit'),
|
||||
set::type('checkbox'),
|
||||
set::text($lang->project->edit),
|
||||
set('data-id', 'checkbox-batchedit'),
|
||||
set('data-load', 'table'),
|
||||
set::checked($this->cookie->editProject)
|
||||
) : NULL,
|
||||
li(searchToggle())
|
||||
)) : NULL,
|
||||
li(searchToggle(set::open($browseType == 'bySearch'))),
|
||||
li(btn(setClass('ghost'), set::icon('unfold-all'), $lang->sort))
|
||||
);
|
||||
|
||||
toolbar
|
||||
@@ -231,18 +292,7 @@ toolbar
|
||||
'class'=> 'ghost',
|
||||
'url' => createLink('program', 'exportTable')
|
||||
))),
|
||||
item(set(array(
|
||||
'text' => $lang->program->createProduct,
|
||||
'icon' => 'plus',
|
||||
'class'=> 'btn secondary',
|
||||
'url' => createLink('program', 'exportTable')
|
||||
))),
|
||||
item(set(array(
|
||||
'text' => $lang->program->create,
|
||||
'icon' => 'plus',
|
||||
'class'=> 'btn primary',
|
||||
'url' => createLink('program', 'create')
|
||||
))),
|
||||
$fnGenerateCreateProgramBtns()
|
||||
);
|
||||
|
||||
dtable
|
||||
@@ -251,12 +301,28 @@ dtable
|
||||
set::data($data),
|
||||
set::userMap($users),
|
||||
set::customCols(true),
|
||||
set::checkable(true),
|
||||
set::checkable($canBatchEdit),
|
||||
set::nested(true),
|
||||
set::className('shadow rounded'),
|
||||
set::footPager(usePager()),
|
||||
set::onRenderCell(jsRaw('window.renderReleaseCountCell')),
|
||||
set::footer(jsRaw('window.footerGenerator()'))
|
||||
set::onRenderCell(jsRaw('window.renderCellProductView')),
|
||||
set::footToolbar(array
|
||||
(
|
||||
'type' => 'btn-group',
|
||||
'items' => array(
|
||||
$canBatchEdit ? array
|
||||
(
|
||||
'text' => $lang->edit,
|
||||
'btnType' => 'secondary',
|
||||
'url' => createLink('product', 'batchEdit'),
|
||||
'onClick' => jsRaw('onClickBatchEdit')
|
||||
) : null
|
||||
)
|
||||
)),
|
||||
set::checkInfo(jsRaw("function(checkedIDList){ return window.footerSummary(checkedIDList);}"))
|
||||
);
|
||||
|
||||
jsVar('pageSummary', sprintf($lang->product->lineSummary, $linesCount, count($productStats)));
|
||||
jsVar('checkedSummary', $lang->program->checkedProducts);
|
||||
|
||||
render();
|
||||
|
||||
+12
-10
@@ -314,23 +314,25 @@ $config->project->actionList['start']['icon'] = 'play';
|
||||
$config->project->actionList['start']['hint'] = $lang->project->start;
|
||||
$config->project->actionList['start']['url'] = array('module' => 'project', 'method' => 'start', 'params' => 'projectID={id}');
|
||||
$config->project->actionList['start']['data-toggle'] = 'modal';
|
||||
$config->project->actionList['start']['data-size'] = array('height' => '540px', 'width' => '800px');
|
||||
|
||||
$config->project->actionList['close']['icon'] = 'off';
|
||||
$config->project->actionList['close']['hint'] = $lang->project->close;
|
||||
$config->project->actionList['close']['url'] = helper::createLink('project', 'close', 'projectID={id}', '', true);
|
||||
$config->project->actionList['close']['icon'] = 'off';
|
||||
$config->project->actionList['close']['hint'] = $lang->project->close;
|
||||
$config->project->actionList['close']['url'] = helper::createLink('project', 'close', 'projectID={id}', '', true);
|
||||
$config->project->actionList['close']['data-toggle'] = 'modal';
|
||||
|
||||
$config->project->actionList['activate']['icon'] = 'magic';
|
||||
$config->project->actionList['activate']['hint'] = $lang->project->activate;
|
||||
$config->project->actionList['activate']['url'] = helper::createLink('project', 'activate', 'projectID={id}', '', true);
|
||||
$config->project->actionList['activate']['icon'] = 'magic';
|
||||
$config->project->actionList['activate']['hint'] = $lang->project->activate;
|
||||
$config->project->actionList['activate']['url'] = helper::createLink('project', 'activate', 'projectID={id}', '', true);
|
||||
$config->project->actionList['activate']['data-toggle'] = 'modal';
|
||||
|
||||
$config->project->actionList['edit']['icon'] = 'edit';
|
||||
$config->project->actionList['edit']['hint'] = $lang->project->edit;
|
||||
$config->project->actionList['edit']['url'] = array('module' => 'project', 'method' => 'edit', 'params' => 'projectID={id}');
|
||||
|
||||
$config->project->actionList['pause']['icon'] = 'pause';
|
||||
$config->project->actionList['pause']['hint'] = $lang->project->suspend;
|
||||
$config->project->actionList['pause']['url'] = helper::createLink('project', 'suspend', 'projectID={id}', '', true);
|
||||
$config->project->actionList['pause']['icon'] = 'pause';
|
||||
$config->project->actionList['pause']['hint'] = $lang->project->suspend;
|
||||
$config->project->actionList['pause']['url'] = helper::createLink('project', 'suspend', 'projectID={id}', '', true);
|
||||
$config->project->actionList['pause']['data-toggle'] = 'modal';
|
||||
|
||||
$config->project->actionList['group']['icon'] = 'group';
|
||||
$config->project->actionList['group']['hint'] = $lang->project->team;
|
||||
|
||||
+18
-22
@@ -541,7 +541,7 @@ class project extends control
|
||||
$this->loadModel('action');
|
||||
$this->loadModel('execution');
|
||||
|
||||
if($this->post->names)
|
||||
if($this->post->name)
|
||||
{
|
||||
$allChanges = $this->project->batchUpdate();
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
@@ -574,7 +574,7 @@ class project extends control
|
||||
}
|
||||
$unauthorizedPrograms = $this->program->getPairsByList($unauthorizedIDList);
|
||||
|
||||
$this->view->title = $this->lang->project->batchEdit;
|
||||
$this->view->title = $this->lang->project->batchEdit;
|
||||
|
||||
$this->view->projects = $projects;
|
||||
$this->view->programs = $programs;
|
||||
@@ -1555,9 +1555,7 @@ class project extends control
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
$this->projectZen->responseAfterStart($project, $changes, $this->post->comment);
|
||||
$response['closeModal'] = true;
|
||||
$response['load'] = true;
|
||||
return $this->sendSuccess($response);
|
||||
return $this->sendSuccess(array('closeModal' => true, 'load' => true));
|
||||
}
|
||||
|
||||
$this->projectZen->buildStartForm($project);
|
||||
@@ -1567,15 +1565,13 @@ class project extends control
|
||||
* 挂起一个项目
|
||||
* Suspend a project.
|
||||
*
|
||||
* @param string $projectID
|
||||
* @param int $projectID
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function suspend(string $projectID)
|
||||
public function suspend(int $projectID)
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
|
||||
/* Processing parameter passing while suspend project. */
|
||||
if(!empty($_POST))
|
||||
{
|
||||
@@ -1584,12 +1580,13 @@ class project extends control
|
||||
|
||||
/* Update the database status to suspended. */
|
||||
$changes = $this->project->suspend($projectID, $postData);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
/* Process the data returned after the suspended. */
|
||||
$comment = strip_tags($this->post->comment, $this->config->allowedTags);
|
||||
$this->projectZen->responseAfterSuspend($projectID, $changes, $comment);
|
||||
return print(js::reload('parent.parent'));
|
||||
return $this->sendSuccess(array('closeModal' => true, 'load' => true));
|
||||
}
|
||||
|
||||
$this->projectZen->buildSuspendForm($projectID);
|
||||
@@ -1599,15 +1596,13 @@ class project extends control
|
||||
* 关闭一个项目
|
||||
* Close a project.
|
||||
*
|
||||
* @param string $projectID
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close(string $projectID)
|
||||
public function close(int $projectID)
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
|
||||
/* Processing parameter passing while close project. */
|
||||
if(!empty($_POST))
|
||||
{
|
||||
@@ -1616,12 +1611,13 @@ class project extends control
|
||||
|
||||
/* Update the database status to closed. */
|
||||
$changes = $this->project->close($projectID, $postData);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
/* Process the data returned after the closed. */
|
||||
$comment = strip_tags($this->post->comment, $this->config->allowedTags);
|
||||
$this->projectZen->responseAfterClose($projectID, $changes, $comment);
|
||||
return print(js::reload('parent.parent'));
|
||||
|
||||
return $this->sendSuccess(array('closeModal' => true, 'load' => true));
|
||||
}
|
||||
|
||||
$this->projectZen->buildClosedForm($projectID);
|
||||
@@ -1631,14 +1627,13 @@ class project extends control
|
||||
* 激活项目并更新其状态
|
||||
* Activate a project.
|
||||
*
|
||||
* @param string $projectID
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function activate(string $projectID)
|
||||
public function activate(int $projectID)
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
$project = $this->project->getByID($projectID);
|
||||
|
||||
if(!empty($_POST))
|
||||
@@ -1647,10 +1642,11 @@ class project extends control
|
||||
$postData = $this->projectZen->prepareActivateExtras($projectID, $postData);
|
||||
|
||||
$changes = $this->project->activate($projectID, $postData);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
$this->projectZen->responseAfterActivate($projectID, $changes);
|
||||
return print(js::reload('parent.parent'));
|
||||
return $this->sendSuccess(array('closeModal' => true, 'load' => true));
|
||||
}
|
||||
|
||||
$this->projectZen->buildActivateForm($project);
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
.panel-form > .panel-heading.status-heading {justify-content: flex-start; padding-bottom: 0px;}
|
||||
.panel-form > .panel-heading.status-heading > .panel-title {font-weight: 400; justify-content:flex-end; width: 5rem;}
|
||||
.items-center {align-items: center !important;}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#modelList .model-block {flex: 1 1 30%; padding: 20px;}
|
||||
#modelList .model-block .border {border-radius: 4px;}
|
||||
|
||||
#modelList .model-block.more-model {min-height: 220px;}
|
||||
#modelList .model-block.more-model .border {line-height: 136px;}
|
||||
@@ -0,0 +1,9 @@
|
||||
$(document).on('click', '.model-item', function()
|
||||
{
|
||||
const link = $(this).data('url');
|
||||
if(!link) return false;
|
||||
|
||||
zui.Modal.hide();
|
||||
|
||||
loadPage(link);
|
||||
});
|
||||
@@ -26,3 +26,20 @@ $(document).on('click', '.batch-btn', function()
|
||||
postAndLoadPage(url, form);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 提示并删除执行。
|
||||
* Delete execution with tips.
|
||||
*
|
||||
* @param int executionID
|
||||
* @param string executionName
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
window.confirmDeleteExecution = function(executionID, confirmDeleteTip)
|
||||
{
|
||||
zui.Modal.confirm({message: confirmDeleteTip, icon:'icon-info-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) =>
|
||||
{
|
||||
if(res) $.ajaxSubmit({url: $.createLink('execution', 'delete', 'executionID=' + executionID + '&comfirm=yes')});
|
||||
});
|
||||
}
|
||||
|
||||
+15
-13
@@ -1427,28 +1427,28 @@ class projectModel extends model
|
||||
$projects = array();
|
||||
$allChanges = array();
|
||||
$data = fixer::input('post')->get();
|
||||
$oldProjects = $this->getByIdList($this->post->projectIdList);
|
||||
$oldProjects = $this->getByIdList($this->post->id);
|
||||
$extendFields = $this->getFlowExtendFields();
|
||||
foreach($data->projectIdList as $projectID)
|
||||
foreach($data->id as $projectID)
|
||||
{
|
||||
$projectID = (int)$projectID;
|
||||
$projectName = $data->names[$projectID];
|
||||
if(isset($data->codes)) $projectCode = $data->codes[$projectID];
|
||||
$projectName = $data->name[$projectID];
|
||||
if(isset($data->code)) $projectCode = $data->code[$projectID];
|
||||
|
||||
$projects[$projectID] = new stdClass();
|
||||
if(isset($data->parents[$projectID])) $projects[$projectID]->parent = $data->parents[$projectID];
|
||||
if(isset($data->parent[$projectID])) $projects[$projectID]->parent = $data->parent[$projectID];
|
||||
$projects[$projectID]->id = $projectID;
|
||||
$projects[$projectID]->name = $projectName;
|
||||
$projects[$projectID]->model = $oldProjects[$projectID]->model;
|
||||
$projects[$projectID]->PM = $data->PMs[$projectID];
|
||||
$projects[$projectID]->begin = $data->begins[$projectID];
|
||||
$projects[$projectID]->end = $data->ends[$projectID] == $this->lang->project->longTime ? LONG_TIME : $data->ends[$projectID];
|
||||
$projects[$projectID]->days = $data->ends[$projectID] == $this->lang->project->longTime ? 0 : $data->dayses[$projectID];
|
||||
$projects[$projectID]->acl = $data->acls[$projectID];
|
||||
$projects[$projectID]->PM = $data->PM[$projectID];
|
||||
$projects[$projectID]->begin = $data->begin[$projectID];
|
||||
$projects[$projectID]->end = $data->end[$projectID] == $this->lang->project->longTime ? LONG_TIME : $data->end[$projectID];
|
||||
$projects[$projectID]->acl = $data->acl[$projectID];
|
||||
$projects[$projectID]->lastEditedBy = $this->app->user->account;
|
||||
$projects[$projectID]->lastEditedDate = helper::now();
|
||||
|
||||
if(isset($data->codes)) $projects[$projectID]->code = $projectCode;
|
||||
if(isset($data->code)) $projects[$projectID]->code = $projectCode;
|
||||
if($data->end[$projectID] == $this->lang->project->longTime) $projects[$projectID]->days = 0;
|
||||
|
||||
foreach($extendFields as $extendField)
|
||||
{
|
||||
@@ -1458,6 +1458,7 @@ class projectModel extends model
|
||||
$projects[$projectID]->{$extendField->field} = htmlSpecialString($projects[$projectID]->{$extendField->field});
|
||||
}
|
||||
}
|
||||
|
||||
if(dao::isError()) return false;
|
||||
|
||||
$this->loadModel('execution');
|
||||
@@ -1580,11 +1581,12 @@ class projectModel extends model
|
||||
if(empty($oldProject->multiple) and $oldProject->model != 'waterfall') $this->loadModel('execution')->syncNoMultipleSprint($projectID);
|
||||
|
||||
/* Update start and end date of tasks in this project. */
|
||||
if($project->readjustTime and $project->readjustTask)
|
||||
if($project->readjustTask)
|
||||
{
|
||||
$tasks = $this->projectTao->fetchUndoneTasks((int)$projectID);
|
||||
$tasks = $this->projectTao->fetchUndoneTasks($projectID);
|
||||
$this->projectTao->updateTasksStartAndEndDate($tasks, $oldProject, $project);
|
||||
}
|
||||
|
||||
/* Activate the shadow product of the project. (only change product status) */
|
||||
if(!$oldProject->hasProduct)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The activate view file of project 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package project
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
formPanel
|
||||
(
|
||||
set::title($lang->project->activate),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->project->activate),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($project->name),
|
||||
set::entityID($project->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formRow
|
||||
(
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->execution->beginAndEnd),
|
||||
inputGroup
|
||||
(
|
||||
input
|
||||
(
|
||||
set::control('date'),
|
||||
set::name('begin'),
|
||||
set::value($newBegin),
|
||||
),
|
||||
$lang->execution->to,
|
||||
input
|
||||
(
|
||||
set::control('date'),
|
||||
set::name('end'),
|
||||
set::value($newEnd),
|
||||
),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
setClass('items-center'),
|
||||
checkbox(
|
||||
set::name('readjustTask'),
|
||||
set::text($lang->execution->readjustTask),
|
||||
set::value(1),
|
||||
set::rootClass('ml-4'),
|
||||
)
|
||||
),
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The batchedit view file of project 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 Sun Guangming<sunguangming@easycorp.ltd>
|
||||
* @package project
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
$setCode = (isset($config->setCode) and $config->setCode == 1);
|
||||
$aclList = (empty($globalDisableProgram) and $project->parent) ? $lang->program->subAcls : $lang->project->acls;
|
||||
|
||||
formBatchPanel
|
||||
(
|
||||
set::mode('edit'),
|
||||
set::data(array_values($projects)),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('id'),
|
||||
set::label($lang->idAB),
|
||||
set::control('hidden'),
|
||||
set::hidden(true),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('id'),
|
||||
set::label($lang->idAB),
|
||||
set::control('index'),
|
||||
set::width('38px'),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('parent'),
|
||||
set::label($lang->project->program),
|
||||
set::control('select'),
|
||||
set::items($programs),
|
||||
set::width('128px'),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('name'),
|
||||
set::label($lang->project->name),
|
||||
set::width('240px'),
|
||||
),
|
||||
$setCode ? formBatchItem
|
||||
(
|
||||
set::name('code'),
|
||||
set::label($lang->project->code),
|
||||
set::width('136px'),
|
||||
) : null,
|
||||
formBatchItem
|
||||
(
|
||||
set::name('PM'),
|
||||
set::label($lang->project->PM),
|
||||
set::control('select'),
|
||||
set::ditto(true),
|
||||
set::defaultDitto('off'),
|
||||
set::items($PMUsers),
|
||||
set::width('136px'),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('PO'),
|
||||
set::label($lang->project->PO),
|
||||
set::control('select'),
|
||||
set::ditto(true),
|
||||
set::defaultDitto('off'),
|
||||
set::items($poUsers),
|
||||
set::width('80px'),
|
||||
set::hidden(true),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('begin'),
|
||||
set::label($lang->project->begin),
|
||||
set::control('date'),
|
||||
set::width('76px'),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('end'),
|
||||
set::label($lang->project->end),
|
||||
set::control('date'),
|
||||
set::width('76px'),
|
||||
),
|
||||
formBatchItem
|
||||
(
|
||||
set::name('acl'),
|
||||
set::label($lang->project->acl),
|
||||
set::control('select'),
|
||||
set::items($aclList),
|
||||
set::width('128px'),
|
||||
),
|
||||
);
|
||||
|
||||
render();
|
||||
@@ -53,11 +53,11 @@ toolbar
|
||||
))),
|
||||
item(set(array
|
||||
(
|
||||
'icon' => 'plus',
|
||||
'text' => $lang->project->create,
|
||||
'class' => "primary create-project-btn",
|
||||
'url' => $this->createLink('project', 'createGuide', '', '', true),
|
||||
'data-modal' => 'modal'
|
||||
'icon' => 'plus',
|
||||
'text' => $lang->project->create,
|
||||
'class' => "primary create-project-btn",
|
||||
'url' => createLink('project', 'createGuide'),
|
||||
'data-toggle' => 'modal'
|
||||
)))
|
||||
);
|
||||
|
||||
|
||||
@@ -1,31 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The close view file of project 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package project
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
set::itemID($project->id);
|
||||
set::title($project->name);
|
||||
|
||||
form
|
||||
formPanel
|
||||
(
|
||||
set::url(createLink('project', 'close', ['projectID' => $project->id])),
|
||||
set::title($lang->project->close),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->project->close),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($project->name),
|
||||
set::entityID($project->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($app->loadLang('program')->program->realEnd),
|
||||
set::required(true),
|
||||
set::width('1/2'),
|
||||
set::label($lang->project->realEnd),
|
||||
set::name('realEnd'),
|
||||
set::control('date'),
|
||||
set::value(!helper::isZeroDate($project->realEnd) ? $project->realEnd : helper::today()),
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
set::name('comment'),
|
||||
set::control(['type' => 'textarea', 'rows' => 5]),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
)
|
||||
),
|
||||
set::submitBtnText($lang->project->close)
|
||||
);
|
||||
|
||||
h::hr(setClass('my-5'));
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The create guide view file of project 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 Yanyi Cao<caoyanyi@easycorp.ltd>
|
||||
* @package project
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
div
|
||||
(
|
||||
setClass('modal-header justify-center font-bold'),
|
||||
h3($lang->project->chooseProgramType)
|
||||
);
|
||||
|
||||
$hasWaterfall = helper::hasFeature('waterfall');
|
||||
$hasWaterfallPlus = helper::hasFeature('waterfallplus');
|
||||
|
||||
$createLink = createLink("project", "create", "model=%s&programID=$programID©ProjectID=0&extra=productID=$productID,branchID=$branchID");
|
||||
$itemList = array();
|
||||
foreach($lang->project->modelList as $model => $modelName)
|
||||
{
|
||||
if(empty($model)) continue;
|
||||
if(!$hasWaterfall && $model == 'waterfall') continue;
|
||||
if(!$hasWaterfallPlus && $model == 'waterfallplus') continue;
|
||||
|
||||
$titleKey = "{$model}Title";
|
||||
$itemList[] = center
|
||||
(
|
||||
setClass('model-block'),
|
||||
div
|
||||
(
|
||||
setClass('model-item modal-content items-center cursor-pointer'),
|
||||
set('data-url', sprintf($createLink, $model)),
|
||||
img
|
||||
(
|
||||
setClass('border'),
|
||||
set::src("theme/default/images/main/{$model}.png")
|
||||
),
|
||||
h3
|
||||
(
|
||||
setClass('mt-2 font-bold'),
|
||||
$lang->project->{$model}
|
||||
),
|
||||
p($lang->project->$titleKey)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
div
|
||||
(
|
||||
setID('modelList'),
|
||||
setClass('flex items-center flex-wrap'),
|
||||
$itemList,
|
||||
div
|
||||
(
|
||||
setClass('model-block more-model'),
|
||||
div
|
||||
(
|
||||
setClass('border text-gray text-center'),
|
||||
$lang->project->moreModelTitle
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
render('modalDialog');
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* The suspend view file of project 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 Shujie Tian<tianshujie@easycorp.ltd>
|
||||
* @package project
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
namespace zin;
|
||||
|
||||
formPanel
|
||||
(
|
||||
set::title($lang->project->suspend),
|
||||
set::headingClass('status-heading'),
|
||||
set::titleClass('form-label .form-grid'),
|
||||
set::shadow(false),
|
||||
set::actions(array('submit')),
|
||||
set::submitBtnText($lang->project->suspend),
|
||||
to::headingActions
|
||||
(
|
||||
entityLabel
|
||||
(
|
||||
setClass('my-3 gap-x-3'),
|
||||
set::level(1),
|
||||
set::text($project->name),
|
||||
set::entityID($project->id),
|
||||
set::reverse(true),
|
||||
)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::label($lang->comment),
|
||||
editor
|
||||
(
|
||||
set::name('comment'),
|
||||
set::rows('6'),
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
h::hr(set::class('mt-6'));
|
||||
|
||||
history();
|
||||
|
||||
/* ====== Render page ====== */
|
||||
render('modalDialog');
|
||||
@@ -9,7 +9,7 @@ $config->release->editor = new stdclass();
|
||||
$config->release->editor->create = array('id' => 'desc', 'tools' => 'simpleTools');
|
||||
$config->release->editor->edit = array('id' => 'desc', 'tools' => 'simpleTools');
|
||||
|
||||
global $lang;
|
||||
global $lang, $app;
|
||||
$config->release->actionList['linkStory']['icon'] = 'link';
|
||||
$config->release->actionList['linkStory']['hint'] = $lang->release->linkStory;
|
||||
$config->release->actionList['linkStory']['url'] = helper::createLink('release', 'view', 'releaseID={id}&type=story&link=true');
|
||||
|
||||
@@ -251,3 +251,17 @@ $config->story->dtable->fieldList['actions']['actionsMap']['processStoryChange']
|
||||
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['batchCreate']['icon'] = 'split';
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['batchCreate']['hint'] = $lang->story->subdivide;
|
||||
|
||||
$app->loadLang('task');
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['createTask']['icon'] = 'plus';
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['createTask']['hint'] = $lang->task->create;
|
||||
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['batchCreateTask']['icon'] = 'pluses';
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['batchCreateTask']['hint'] = $lang->task->batchCreate;
|
||||
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['storyEstimate']['icon'] = 'estimate';
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['storyEstimate']['hint'] = $lang->task->batchCreate;
|
||||
|
||||
$app->loadLang('execution');
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['unlink']['icon'] = 'unlink';
|
||||
$config->story->dtable->fieldList['actions']['actionsMap']['unlink']['hint'] = $lang->execution->unlinkStory;
|
||||
|
||||
@@ -396,7 +396,7 @@ class story extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchEdit($productID = 0, $executionID = 0, $branch = 0, $storyType = 'story', $from = '')
|
||||
public function batchEdit(int $productID = 0, int $executionID = 0, string $branch = '', string $storyType = 'story', string $from = '')
|
||||
{
|
||||
$this->story->replaceURLang($storyType);
|
||||
|
||||
|
||||
@@ -194,11 +194,13 @@ $config->task->effortTable->fieldList['actions']['list']['editEffort']['url']
|
||||
$config->task->effortTable->fieldList['actions']['list']['editEffort']['order'] = 5;
|
||||
$config->task->effortTable->fieldList['actions']['list']['editEffort']['show'] = 'clickable';
|
||||
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['icon'] = 'trash';
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['hint'] = $lang->task->deleteWorkhour;
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['url'] = helper::createLink('task', 'deleteWorkhour', 'taskID={id}');
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['order'] = 10;
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['show'] = 'clickable';
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['icon'] = 'trash';
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['hint'] = $lang->task->deleteWorkhour;
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['order'] = 10;
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['show'] = 'clickable';
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['url'] = array('module' => 'task', 'method' => 'deleteWorkhour', 'params' => 'taskID={id}');
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['class'] = 'ajax-submit btn ghost square size-sm';
|
||||
$config->task->effortTable->fieldList['actions']['list']['deleteWorkhour']['data-confirm'] = $lang->task->confirmDeleteEstimate;
|
||||
|
||||
$config->task->dtable->importTask = new stdclass();
|
||||
|
||||
|
||||
+11
-22
@@ -511,39 +511,28 @@ class task extends control
|
||||
* Delete estimate.
|
||||
*
|
||||
* @param int $estimateID
|
||||
* @param string $confirm
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function deleteWorkhour($estimateID, $confirm = 'no')
|
||||
public function deleteWorkhour($estimateID)
|
||||
{
|
||||
$estimate = $this->task->getEffortByID($estimateID);
|
||||
$taskID = $estimate->objectID;
|
||||
$task = $this->task->getById($taskID);
|
||||
if($confirm == 'no' and $task->consumed - $estimate->consumed != 0)
|
||||
{
|
||||
return print(js::confirm($this->lang->task->confirmDeleteEstimate, $this->createLink('task', 'deleteWorkhour', "estimateID=$estimateID&confirm=yes")));
|
||||
}
|
||||
elseif($confirm == 'no' and $task->consumed - $estimate->consumed == 0)
|
||||
{
|
||||
return print(js::confirm($this->lang->task->confirmDeleteLastEstimate, $this->createLink('task', 'deleteWorkhour', "estimateID=$estimateID&confirm=yes")));
|
||||
}
|
||||
else
|
||||
{
|
||||
$changes = $this->task->deleteWorkhour($estimateID);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
|
||||
$actionID = $this->loadModel('action')->create('task', $taskID, 'DeleteEstimate');
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
$changes = $this->task->deleteWorkhour($estimateID);
|
||||
if(dao::isError()) return print(js::error(dao::getError()));
|
||||
|
||||
if($task->consumed - $estimate->consumed == 0)
|
||||
{
|
||||
$this->action->create('task', $taskID, 'Adjusttasktowait');
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
$actionID = $this->loadModel('action')->create('task', $taskID, 'DeleteEstimate');
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
|
||||
return print(js::reload('parent'));
|
||||
if($task->consumed - $estimate->consumed == 0)
|
||||
{
|
||||
$this->action->create('task', $taskID, 'Adjusttasktowait');
|
||||
return print(js::reload('parent.parent'));
|
||||
}
|
||||
|
||||
return print(js::reload('parent'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1805,7 +1805,7 @@ class taskModel extends model
|
||||
*/
|
||||
public function deleteWorkhour($estimateID)
|
||||
{
|
||||
$estimate = $this->getEstimateByID($estimateID);
|
||||
$estimate = $this->getEffortByID($estimateID);
|
||||
$task = $this->getById($estimate->objectID);
|
||||
$now = helper::now();
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ formPanel
|
||||
set::id('story'),
|
||||
set::name('story'),
|
||||
set::value($task->story),
|
||||
set::items($stories),
|
||||
set::items(array_filter($stories)),
|
||||
on::change('setStoryRelated'),
|
||||
),
|
||||
$storyPreviewBtn,
|
||||
@@ -290,7 +290,7 @@ formPanel
|
||||
select
|
||||
(
|
||||
set::name('pri'),
|
||||
set::items($lang->task->priList),
|
||||
set::items(array_filter($lang->task->priList)),
|
||||
),
|
||||
$lang->task->estimate,
|
||||
inputControl
|
||||
|
||||
@@ -374,7 +374,7 @@ detailBody
|
||||
h::th
|
||||
(
|
||||
$lang->testcase->stepID,
|
||||
set::width('50px'),
|
||||
set::width('60px'),
|
||||
),
|
||||
h::th
|
||||
(
|
||||
|
||||
@@ -46,6 +46,7 @@ $config->todo->objectList['testtask'] = 'testtasks';
|
||||
|
||||
$config->todo->sessionUri = array();
|
||||
$config->todo->sessionUri['bugList'] = 'qa';
|
||||
$config->todo->sessionUri['todoList'] = 'my';
|
||||
$config->todo->sessionUri['taskList'] = 'execution';
|
||||
$config->todo->sessionUri['storyList'] = 'product';
|
||||
$config->todo->sessionUri['testtaskList'] = 'qa';
|
||||
|
||||
+21
-34
@@ -126,25 +126,17 @@ class todo extends control
|
||||
|
||||
/* Processing edit request data. */
|
||||
$todo = $this->todoZen->beforeEdit($todoID, $form);
|
||||
if(dao::isError())
|
||||
{
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => dao::getError()));
|
||||
return print(js::error(dao::getError()));
|
||||
}
|
||||
if(dao::isError()) return $this->send(array('status' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
/* update a todo. */
|
||||
$changes = $this->todo->update($todoID, $todo);
|
||||
if(dao::isError())
|
||||
{
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => dao::getError()));
|
||||
return print(js::error(dao::getError()));
|
||||
}
|
||||
if(dao::isError()) return $this->send(array('status' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
/* Handle data after edit todo. */
|
||||
$this->todoZen->afterEdit($todoID, $changes);
|
||||
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
return print(js::locate($this->session->todoList, 'parent.parent'));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
/* Judge a private todo or not, If private, die. */
|
||||
@@ -204,9 +196,8 @@ class todo extends control
|
||||
}
|
||||
|
||||
if(in_array($todo->type, array('bug', 'task', 'story'))) return $this->todoZen->printStartConfirm($todo);
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
|
||||
return print(js::reload('parent'));
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,9 +218,8 @@ class todo extends control
|
||||
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
}
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
|
||||
return print(js::reload('parent'));
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,7 +237,7 @@ class todo extends control
|
||||
if($todo->status == 'done')
|
||||
{
|
||||
$isClosed = $this->todo->close($todoID);
|
||||
if(!$isClosed) return print(js::error(dao::getError()));
|
||||
if(!$isClosed) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
}
|
||||
|
||||
/* Return json if run mode is API. */
|
||||
@@ -256,9 +246,8 @@ class todo extends control
|
||||
$this->send(array('status' => 'success'));
|
||||
}
|
||||
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
|
||||
return print(js::reload('parent'));
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,9 +267,10 @@ class todo extends control
|
||||
|
||||
$todo->id = $todoID;
|
||||
$isAssigned = $this->todoZen->doAssignTo($todo);
|
||||
if(!$isAssigned) return print(js::error(dao::getError()));
|
||||
if(!$isAssigned) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
|
||||
|
||||
return print(js::reload('parent.parent'));
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
$this->todoZen->buildAssignToView($todoID);
|
||||
@@ -332,13 +322,13 @@ class todo extends control
|
||||
*/
|
||||
public function delete(int $todoID, string $confirm = 'no')
|
||||
{
|
||||
if($confirm == 'no') return print(js::confirm($this->lang->todo->confirmDelete, $this->createLink('todo', 'delete', "todoID={$todoID}&confirm=yes")));
|
||||
if($confirm == 'no') return $this->send(array('result' => 'success', 'load' => array('confirm' => $this->lang->todo->confirmDelete, 'confirmed' => $this->createLink('todo', 'delete', "todoID={$todoID}&confirm=yes"), 'canceled' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo'))));
|
||||
|
||||
$this->todo->delete(TABLE_TODO, $todoID);
|
||||
|
||||
if(helper::isAjaxRequest())
|
||||
{
|
||||
$response = array('result' => 'success', 'message' => '');
|
||||
$response = array('result' => 'success', 'message' => '', 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo'));
|
||||
if(dao::isError())
|
||||
{
|
||||
$response['result'] = 'fail';
|
||||
@@ -348,10 +338,8 @@ class todo extends control
|
||||
}
|
||||
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success'));
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
|
||||
$browseLink = $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo');
|
||||
return print(js::locate($browseLink, 'parent'));
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,17 +371,16 @@ class todo extends control
|
||||
if($todo->type == 'task') $app = 'execution';
|
||||
if($todo->type == 'story') $app = 'product';
|
||||
|
||||
$cancelURL = $this->server->http_referer;
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success', 'message' => sprintf($this->lang->todo->$confirmNote, $todo->objectID), 'locate' => $confirmURL));
|
||||
return print(strpos($cancelURL, 'calendar') ? json_encode(array(sprintf($this->lang->todo->$confirmNote, $todo->objectID), $confirmURL)) : js::confirm(sprintf($this->lang->todo->$confirmNote, $todo->objectID), $confirmURL, $cancelURL, $okTarget, 'parent', $app));
|
||||
$cancelURL = $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo');
|
||||
return $this->send(array('result' => 'success', 'load' => array('confirm' => sprintf($this->lang->todo->{$confirmNote}, $todo->objectID), 'confirmed' => $confirmURL, 'canceled' => $cancelURL)));
|
||||
}
|
||||
|
||||
if(defined('RUN_MODE') && RUN_MODE == 'api')
|
||||
{
|
||||
return $this->send(array('status' => 'success'));
|
||||
}
|
||||
if(isonlybody()) return print(js::reload('parent.parent'));
|
||||
return print(js::reload('parent'));
|
||||
if(isonlybody()) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true));
|
||||
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,4 +91,4 @@ formPanel
|
||||
)
|
||||
);
|
||||
|
||||
render();
|
||||
render(isAjaxRequest('modal') ? 'modalDialog' : 'page');
|
||||
|
||||
@@ -35,8 +35,6 @@ $cycleTypeOptions = array(
|
||||
$days = array();
|
||||
for($day = 1; $day <= 31; $day ++) $days[$day] = $day . $lang->todo->day;
|
||||
|
||||
$isInModal = isAjaxRequest('modal');
|
||||
|
||||
formPanel
|
||||
(
|
||||
set::title(''),
|
||||
@@ -600,4 +598,4 @@ formPanel
|
||||
)
|
||||
);
|
||||
|
||||
render($isInModal ? 'modalDialog' : 'page');
|
||||
render(isAjaxRequest('modal') ? 'modalDialog' : 'page');
|
||||
|
||||
@@ -769,4 +769,4 @@ formPanel
|
||||
)
|
||||
);
|
||||
|
||||
render();
|
||||
render(isAjaxRequest('modal') ? 'modalDialog' : 'page');
|
||||
|
||||
+3
-3
@@ -278,7 +278,7 @@ class todoZen extends todo
|
||||
|
||||
if($todo->end < $todo->begin)
|
||||
{
|
||||
dao::$errors[] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
|
||||
dao::$errors['end'] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -587,9 +587,9 @@ class todoZen extends todo
|
||||
if($todo->type == 'bug') $app = 'qa';
|
||||
if($todo->type == 'task') $app = 'execution';
|
||||
if($todo->type == 'story') $app = 'product';
|
||||
$cancelURL = $this->server->http_referer;
|
||||
$cancelURL = $this->session->todoList ? $this->session->todoList : $this->createLink('my', 'todo');
|
||||
|
||||
return print(js::confirm(sprintf($this->lang->todo->$confirmNote, $todo->objectID), $confirmURL, $cancelURL, $okTarget, 'parent', $app));
|
||||
return $this->send(array('result' => 'success', 'load' => array('confirm' => sprintf($this->lang->todo->{$confirmNote}, $todo->objectID), 'confirmed' => $confirmURL, 'canceled' => $cancelURL)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+38
-11
@@ -162,7 +162,7 @@
|
||||
function updateTable(data)
|
||||
{
|
||||
const props = data.props;
|
||||
const $table = $('#' + props.id).parent();
|
||||
const $table = $('#' + props.id).closest('[data-zui-dtable]');
|
||||
if(!$table.length) return;
|
||||
const dtable = zui.DTable.get($table[0]);
|
||||
if(DEBUG) console.log('[APP] ', 'update table:', {data, props});
|
||||
@@ -293,13 +293,35 @@
|
||||
data = [{name: data.includes('Fatal error') ? 'fatal' : 'html', data: data}];
|
||||
}
|
||||
if(options.updateUrl !== false) currentAppUrl = url;
|
||||
data.forEach((item, idx) => item.selector = selectors[idx]);
|
||||
updatePerfInfo(options, 'renderBegin');
|
||||
renderPage(data, options);
|
||||
updatePerfInfo(options, 'renderEnd');
|
||||
$(document).trigger('pagerender.app');
|
||||
if(options.success) options.success(data);
|
||||
if(onFinish) onFinish(null, data);
|
||||
if(Array.isArray(data))
|
||||
{
|
||||
data.forEach((item, idx) => item.selector = selectors[idx]);
|
||||
updatePerfInfo(options, 'renderBegin');
|
||||
renderPage(data, options);
|
||||
updatePerfInfo(options, 'renderEnd');
|
||||
$(document).trigger('pagerender.app');
|
||||
if(options.success) options.success(data);
|
||||
if(onFinish) onFinish(null, data);
|
||||
}
|
||||
else if(data.load)
|
||||
{
|
||||
if(data.load === 'dtable') loadTable();
|
||||
else if(typeof data.load === 'string') loadPage(data.load);
|
||||
else if(data.load === true) loadCurrentPage();
|
||||
else if(typeof data.load === 'object')
|
||||
{
|
||||
if('confirm' in data.load)
|
||||
{
|
||||
const confirmed = confirm(data.load.confirm);
|
||||
if(confirmed) loadPage(data.load.confirmed);
|
||||
else loadPage(data.load.canceled);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadPage(data.load);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
error: (xhr, type, error) =>
|
||||
{
|
||||
@@ -463,8 +485,9 @@
|
||||
* @param {string} options.app
|
||||
* @param {string} options.load
|
||||
* @param {string} options.back
|
||||
* @param {Event} event
|
||||
*/
|
||||
function openUrl(url, options)
|
||||
function openUrl(url, options, event)
|
||||
{
|
||||
if(typeof url === 'object')
|
||||
{
|
||||
@@ -487,7 +510,11 @@
|
||||
if(options.loadId) {options.id = options.loadId; delete options.loadId;}
|
||||
if(load)
|
||||
{
|
||||
if(load === 'table') return loadTable(options.url, options.id, options);
|
||||
if(load === 'table')
|
||||
{
|
||||
if(!options.id && event) options.id = $(event.target).closest('.dtable').attr('id');
|
||||
return loadTable(options.url, options.id, options);
|
||||
}
|
||||
if(load !== 'APP' && typeof load === 'string') options.selector = load;
|
||||
delete options.load;
|
||||
}
|
||||
@@ -616,7 +643,7 @@
|
||||
const url = options.url || $link.attr('href');
|
||||
if(!url || url.startsWith('javascript:') || url.startsWith('#')) return;
|
||||
|
||||
openUrl(url, options);
|
||||
openUrl(url, options, e);
|
||||
e.preventDefault();
|
||||
}).on('locate.zt', (_e, data) =>
|
||||
{
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/home/tintin/code/zui/dist/zentao/zui.zentao.css
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
/home/tintin/code/zui/dist/zentao/zui.zentao.js
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
/home/tintin/code/zui/dist/zentao/zui.zentao.umd.cjs
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user