Merge branch 'master' into zenops_46

This commit is contained in:
曹延义
2022-09-30 15:07:37 +08:00
81 changed files with 798 additions and 318 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ class releasesEntry extends entry
if(empty($productID)) return $this->sendError(400, 'Need product id.');
$control = $this->loadController('release', 'browse');
$control->browse($productID, $this->param('branch', 0), $this->param('status', 'all'), $this->param('order', 't1.date_desc'));
$control->browse($productID, $this->param('branch', 0), $this->param('status', 'all'), $this->param('order', 't1.date_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
/* Response */
$data = $this->getData();
+5
View File
@@ -97,6 +97,11 @@ $config->domainPostfix .= "|international|house|coffee|florist|rich|ceo|camp|edu
$config->CSPs = array();
$config->CSPs[] = "form-action 'self';connect-src 'self'";
/* Config for kanban col setting */
$config->colWidth = 264;
$config->minColWidth = 180;
$config->maxColWidth = 384;
/* 系统框架配置。Framework settings. */
$config->framework = new stdclass();
$config->framework->autoConnectDB = true; // 是否自动连接数据库。 Whether auto connect database or not.
+6
View File
@@ -0,0 +1,6 @@
ALTER TABLE `zt_kanban` ADD `colWidth` smallint(4) NOT NULL DEFAULT '264' AFTER `fluidBoard`;
ALTER TABLE `zt_kanban` ADD `minColWidth` smallint(4) NOT NULL DEFAULT '180' AFTER `colWidth`;
ALTER TABLE `zt_kanban` ADD `maxColWidth` smallint(4) NOT NULL DEFAULT '384' AFTER `minColWidth`;
ALTER TABLE `zt_project` ADD `colWidth` smallint(4) NOT NULL DEFAULT '264' AFTER `fluidBoard`;
ALTER TABLE `zt_project` ADD `minColWidth` smallint(4) NOT NULL DEFAULT '180' AFTER `colWidth`;
ALTER TABLE `zt_project` ADD `maxColWidth` smallint(4) NOT NULL DEFAULT '384' AFTER `minColWidth`;
+6
View File
@@ -835,6 +835,9 @@ CREATE TABLE IF NOT EXISTS `zt_kanban` (
`displayCards` smallint(6) NOT NULL default '0',
`showWIP` enum('0','1') NOT NULL DEFAULT '1',
`fluidBoard` enum('0','1') NOT NULL DEFAULT '0',
`colWidth` tinyint(3) NOT NULL DEFAULT '0',
`minColWidth` tinyint(3) NOT NULL DEFAULT '0',
`maxColWidth` tinyint(3) NOT NULL DEFAULT '0',
`object` varchar(255) NOT NULL,
`alignment` varchar(10) NOT NULL default 'center',
`createdBy` char(30) NOT NULL,
@@ -1193,6 +1196,9 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
`displayCards` smallint(6) NOT NULL default '0',
`fluidBoard` enum('0','1') NOT NULL DEFAULT '0',
`colWidth` tinyint(3) NOT NULL DEFAULT '0',
`minColWidth` tinyint(3) NOT NULL DEFAULT '0',
`maxColWidth` tinyint(3) NOT NULL DEFAULT '0',
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `parent` (`parent`),
@@ -21,7 +21,6 @@
<?php js::set('teamMemberError', $lang->task->error->teamMember);?>
<?php js::set('estimateNotEmpty', sprintf($lang->error->gt, $lang->task->estimate, '0'))?>
<?php if(!empty($storyID)):?>
<style> .title-group.required > .required:after {right: 110px;}</style>
<?php endif;?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
+10 -2
View File
@@ -996,7 +996,14 @@ class baseRouter
if($writable)
{
$ztSessionHandler = new ztSessionHandler($_GET['tid']);
session_set_save_handler($ztSessionHandler, true);
session_set_save_handler(
array($ztSessionHandler, "open"),
array($ztSessionHandler, "close"),
array($ztSessionHandler, "read"),
array($ztSessionHandler, "write"),
array($ztSessionHandler, "destroy"),
array($ztSessionHandler, "gc")
);
}
}
@@ -3140,7 +3147,7 @@ class EndResponseException extends \Exception
*
* @package framework
*/
class ztSessionHandler implements SessionHandlerInterface
class ztSessionHandler
{
public $sessSavePath;
public $tagID;
@@ -3160,6 +3167,7 @@ class ztSessionHandler implements SessionHandlerInterface
{
$this->tagID = $tagID;
ini_set('session.save_handler', 'files');
register_shutdown_function('session_write_close');
}
/**
+1 -1
View File
@@ -586,7 +586,7 @@ class actionModel extends model
if($history->field == 'git') $history->diff = str_replace('+', '%2B', $history->diff);
}
}
elseif($actionName == 'linkstory' or $actionName == 'unlinkstory')
elseif(strpos('linkstory,unlinkstory,createchildrenstory', $actionName) !== false)
{
$extra = '';
foreach(explode(',', $action->extra) as $id) $extra .= common::hasPriv('story', 'view') ? html::a(helper::createLink('story', 'view', "storyID=$id"), "#$id ") . ', ' : "#$id, ";
+2
View File
@@ -1159,6 +1159,7 @@ class bug extends control
$branch = $product->type == 'branch' ? ($bug->branch > 0 ? $bug->branch . ',0' : '0') : '';
$productBugs = $this->bug->getProductBugPairs($productID, $branch);
unset($productBugs[$bugID]);
$this->view->bug = $bug;
$this->view->productID = $productID;
@@ -1815,6 +1816,7 @@ class bug extends control
$product = $this->loadModel('product')->getById($productID);
$branch = $product->type == 'branch' ? ($bug->branch > 0 ? $bug->branch . ',0' : '0') : '';
$productBugs = $this->bug->getProductBugPairs($productID, $branch);
unset($productBugs[$bugID]);
$this->bug->checkBugExecutionPriv($bug);
$this->qa->setMenu($this->products, $productID, $bug->branch);
+5 -1
View File
@@ -143,7 +143,11 @@
<tr>
<td class='pd-0'><?php echo html::select("resolutions[$bugID]", $resolutionList, $bug->resolution, "class='form-control' onchange=setDuplicate(this.value,$bugID)");?></td>
<td class='pd-0 w-p50' id='<?php echo 'duplicateBugBox' . $bugID;?>' <?php if($bug->resolution != 'duplicate') echo "style='display:none'";?>>
<?php echo html::select("duplicateBugs[$bugID]", $productBugList[$bug->product][$bug->branch], $bug->duplicateBug, "class='form-control' placeholder='{$lang->bug->duplicateTip}'");?>
<?php
$productBugs = $productBugList[$bug->product][$bug->branch];
if(isset($productBugs[$bug->id])) unset($productBugs[$bug->id]);
?>
<?php echo html::select("duplicateBugs[$bugID]", $productBugs, $bug->duplicateBug, "class='form-control' placeholder='{$lang->bug->duplicateTip}'");?>
</td>
</tr>
</table>
-1
View File
@@ -67,7 +67,6 @@ class datatable extends control
$this->app->loadConfig('testtask');
$this->config->testcase->datatable->defaultField = $this->config->testtask->datatable->defaultField;
$this->config->testcase->datatable->fieldList['actions']['width'] = '100';
$this->config->testcase->datatable->fieldList['status']['title'] = $this->lang->testcase->executionStatus;
$this->config->testcase->datatable->fieldList['status']['width'] = '90';
}
if($module == 'testcase')
+1 -1
View File
@@ -3183,7 +3183,7 @@ class execution extends control
{
$this->project->setMenu($object->id);
}
elseif($object->type == 'sprint' or $object->type == 'stage')
elseif($object->type == 'sprint' or $object->type == 'stage' or $object->type == 'kanban')
{
$this->execution->setMenu($object->id);
}
+1
View File
@@ -121,3 +121,4 @@
#kanban .kanban-affixed .kanban-header-col .title.action-dbc {pointer-events: none;}
#kanban .kanban-affixed .kanban-header-col .title.action-dbc .storyColumn .text {color: #fff;}
#kanban .kanban-affixed .kanban-header-col .title.action-dbc .storyColumn .caret {display: none;}
.kanban-card a[disabled] {color: #313c52;}
+1
View File
@@ -73,3 +73,4 @@
#kanbans .kanban-affixed .kanban-header-col .title.action-dbc {pointer-events: none;}
#kanbans .kanban-affixed .kanban-header-col .title.action-dbc .storyColumn .text {color: #fff;}
#kanbans .kanban-affixed .kanban-header-col .title.action-dbc .storyColumn .caret {display: none;}
.kanban-card a[disabled] {color: #313c52;}
+2 -2
View File
@@ -1450,8 +1450,8 @@ function initKanban($kanban)
data: groupBy == 'default' ? region.groups : kanbanData[groupBy],
maxColHeight: 'auto',
calcColHeight: calcColHeight,
minColWidth: 240,
maxColWidth: 240,
minColWidth: typeof window.minColWidth === 'number' ? window.minColWidth: defaultMinColWidth,
maxColWidth: typeof window.maxColWidth === 'number' ? window.maxColWidth: defaultMaxColWidth,
cardHeight: getCardHeight(),
fluidBoardWidth: fluidBoard,
displayCards: typeof window.displayCards === 'number' ? window.displayCards : 2,
+1
View File
@@ -2,4 +2,5 @@ $(function()
{
var heightType = $("[name='heightType']:checked").val();
setCardCount(heightType);
handleKanbanWidthAttr();
});
+2 -2
View File
@@ -1232,8 +1232,8 @@ $(function()
var commonOptions =
{
maxColHeight: 'auto',
minColWidth: 240,
maxColWidth: 240,
minColWidth: typeof window.minColWidth === 'number' ? window.minColWidth: defaultMinColWidth,
maxColWidth: typeof window.maxColWidth === 'number' ? window.maxColWidth: defaultMaxColWidth,
cardHeight: getCardHeight(),
showCount: true,
showZeroCount: true,
+13 -2
View File
@@ -1130,7 +1130,18 @@ class executionModel extends model
if(isset($_POST['heightType']) and $this->post->heightType == 'custom' and !$this->loadModel('kanban')->checkDisplayCards($execution->displayCards)) return;
$this->dao->update(TABLE_EXECUTION)->data($execution)->where('id')->eq((int)$executionID)->exec();
$this->app->loadLang('kanban');
$this->lang->project->colWidth = $this->lang->kanban->colWidth;
$this->lang->project->minColWidth = $this->lang->kanban->minColWidth;
$this->lang->project->maxColWidth = $this->lang->kanban->maxColWidth;
$this->dao->update(TABLE_EXECUTION)->data($execution)
->autoCheck()
->batchCheck($this->config->kanban->edit->requiredFields, 'notempty')
->checkIF(!$execution->fluidBoard, 'colWidth', 'gt', 0)
->batchCheckIF($execution->fluidBoard, 'minColWidth,maxColWidth', 'gt', 0)
->checkIF($execution->minColWidth and $execution->maxColWidth and $execution->fluidBoard, 'maxColWidth', 'gt', $execution->minColWidth)
->where('id')->eq((int)$executionID)
->exec();
}
/**
@@ -4865,7 +4876,7 @@ class executionModel extends model
case 'name':
$label = $execution->type == 'stage' ? 'label-warning' : 'label-info';
$executionLink = $execution->projectModel == 'kanban' ? html::a(helper::createLink('execution', 'kanban', 'executionID=' . $execution->id), $execution->name, '', "class='text-ellipsis'") : html::a(helper::createLink('execution', 'task', 'execution=' . $execution->id), $execution->name, '', "class='text-ellipsis'");
if(!$onlyChildStage) echo "<span class='project-type-label label label-outline $label'>{$this->lang->execution->typeList[$execution->type]}</span>";
if($this->config->systemMode != 'classic' and !$onlyChildStage) echo "<span class='project-type-label label label-outline $label'>{$this->lang->execution->typeList[$execution->type]}</span>";
if($onlyChildStage) echo "<span class='label label-badge label-light label-children'>{$this->lang->programplan->childrenAB}</span> ";
echo !empty($execution->children) ? "<span class='text-ellipsis'>$execution->name</span>" : $executionLink;
if(isset($execution->delay)) echo "<span class='label label-danger label-badge'>{$this->lang->execution->delayed}</span> ";
+4
View File
@@ -179,6 +179,9 @@
<td id="plansBox" colspan="2">
<div class='row'>
<?php $i = 0;?>
<?php if(empty($linkedProducts)):?>
<div class="col-sm-4" id="plan0"><?php echo html::select("plans[][][]", $productPlans, '', "class='form-control chosen' multiple");?></div>
<?php else:?>
<?php foreach($linkedProducts as $product):?>
<?php foreach($linkedBranches[$product->id] as $branchID => $branch):?>
<?php $plans = isset($productPlans[$product->id][$branchID]) ? $productPlans[$product->id][$branchID] : array();?>
@@ -186,6 +189,7 @@
<?php $i++;?>
<?php endforeach;?>
<?php endforeach;?>
<?php endif;?>
</div>
</td>
</tr>
+4
View File
@@ -47,6 +47,8 @@ js::set('entertime', time());
js::set('displayCards', $execution->displayCards);
js::set('productNum', $productNum);
js::set('fluidBoard', $execution->fluidBoard);
js::set('minColWidth', $execution->fluidBoard == '0' ? $execution->colWidth : $execution->minColWidth);
js::set('maxColWidth',$execution->fluidBoard == '0' ? $execution->colWidth : $execution->maxColWidth);
js::set('colorListLang', $lang->kanbancard->colorList);
js::set('colorList', $this->config->kanban->cardColorList);
js::set('projectID', $projectID);
@@ -56,6 +58,8 @@ js::set('executionID', $execution->id);
js::set('needLinkProducts', $lang->execution->needLinkProducts);
js::set('lastUpdateData', '');
js::set('rdSearchValue', '');
js::set('defaultMinColWidth', $this->config->minColWidth);
js::set('defaultMaxColWidth', $this->config->maxColWidth);
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
$canCreateRegion = (common::hasPriv('kanban', 'createRegion') and $groupBy == 'default');
+12 -1
View File
@@ -22,7 +22,18 @@
<table class='table table-form'>
<tr>
<th><?php echo $lang->kanban->columnWidth;?></th>
<td colspan='2'><?php echo nl2br(html::radio('fluidBoard', $lang->kanbancolumn->fluidBoardList, $execution->fluidBoard));?></td>
<td colspan='2'>
<div class="width-radio-row">
<?php echo html::radio('fluidBoard', array(0 => $lang->kanbancolumn->fluidBoardList['0']), "class='inline-block'", $execution->fluidBoard);?>
<?php echo html::input('colWidth', !empty($execution->colWidth) ? $execution->colWidth : $this->config->colWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->colWidth}' autocomplete='off'");?>px
</div>
<div class="width-radio-row mt10">
<?php echo html::radio('fluidBoard', array(1 => $lang->kanbancolumn->fluidBoardList['1']), "class='inline-block'", $execution->fluidBoard);?>
<?php echo html::input('minColWidth', !empty($execution->minColWidth) ? $execution->minColWidth: $this->config->minColWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->minColWidth}' autocomplete='off'");?>px
<span class="input-divider">~</span>
<?php echo html::input('maxColWidth', !empty($execution->maxColWidth) ? $execution->maxColWidth: $this->config->maxColWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->maxColWidth}' autocomplete='off'");?>px
</div>
</td>
</tr>
<?php if($laneCount > 1):?>
<tr>
+5 -1
View File
@@ -71,7 +71,7 @@
</div>
<?php
$width = common::checkNotCN() ? '600px' : '470px';
$width = common::checkNotCN() ? '600px' : '560px';
echo "<div class='btn-group menu-actions'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
echo "<ul class='dropdown-menu pull-right'>";
@@ -223,8 +223,12 @@ js::set('priv',
<?php js::set('userList', $userList);?>
<?php js::set('entertime', time());?>
<?php js::set('fluidBoard', $execution->fluidBoard);?>
<?php js::set('minColWidth', $execution->fluidBoard == '0' ? $execution->colWidth : $execution->minColWidth);?>
<?php js::set('maxColWidth',$execution->fluidBoard == '0' ? $execution->colWidth : $execution->maxColWidth);?>
<?php js::set('displayCards', $execution->displayCards);?>
<?php js::set('needLinkProducts', $lang->execution->needLinkProducts);?>
<?php js::set('hourUnit', $config->hourUnit);?>
<?php js::set('orderBy', $storyOrder);?>
<?php js::set('defaultMinColWidth', $this->config->minColWidth);?>
<?php js::set('defaultMaxColWidth', $this->config->maxColWidth);?>
<?php include '../../common/view/footer.html.php';?>
+10 -10
View File
@@ -13,7 +13,7 @@
</div>
</div>
<div class="btn-toolbar pull-left">
<?php echo html::a(inlink('testcase', "executionID=$executionID&productID=$productID&branchID=$branchID&type=$type&moduleID=$moduleID&orderBy=$orderBy"), "<span class='text'>{$lang->execution->featureBar['all']['all']}</span>", '', "class='btn btn-link btn-active-text'");?>
<?php echo html::a(inlink('testcase', "executionID=$executionID&productID=$productID&branchID=$branchID&type=$type&moduleID=$moduleID&orderBy=$orderBy"), "<span class='text'>{$lang->execution->featureBar['all']['all']}</span> <span class='label label-light label-badge'>{$pager->recTotal}</span>", '', "class='btn btn-link btn-active-text'");?>
</div>
<div class="btn-toolbar pull-right">
<?php if(common::canModify('execution', $execution)) echo html::a(helper::createLink('testcase', 'create', "productID=$productID&branch=0&moduleID=0&from=execution&param=$execution->id", '', '', '', true), "<i class='icon icon-plus'></i> " . $lang->testcase->create, '', "class='btn btn-primary' data-app='{$this->app->tab}'");?>
@@ -49,14 +49,14 @@
<thead>
<tr>
<th class='w-id'> <?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?></th>
<th class='c-pri' title=<?php echo $lang->execution->pri;?>> <?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
<th> <?php common::printOrderLink('title', $orderBy, $vars, $lang->testcase->title);?></th>
<th class='c-type'> <?php common::printOrderLink('type', $orderBy, $vars, $lang->typeAB);?></th>
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
<th class='c-pri'> <?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
<th class='c-type'> <?php common::printOrderLink('type', $orderBy, $vars, $lang->testcase->type);?></th>
<th class='c-status'><?php common::printOrderLink('status', $orderBy, $vars, $lang->statusAB);?></th>
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->testcase->openedByAB);?></th>
<th class='c-user'> <?php common::printOrderLink('lastRunner', $orderBy, $vars, $lang->testtask->lastRunAccount);?></th>
<th class='c-date'> <?php common::printOrderLink('lastRunDate', $orderBy, $vars, $lang->testtask->lastRunTime);?></th>
<th class='c-result'><?php common::printOrderLink('lastRunResult', $orderBy, $vars, $lang->testtask->lastRunResult);?></th>
<th class='c-status'><?php common::printOrderLink('status', $orderBy, $vars, $lang->statusAB);?></th>
<th class='c-actions-6 text-center'><?php echo $lang->actions;?></th>
</tr>
</thead>
@@ -70,15 +70,11 @@
<td class="c-id">
<?php echo sprintf('%03d', $case->id); ?>
</td>
<td><span class='label-pri <?php echo 'label-pri-' . $case->pri?>' title='<?php echo zget($lang->testcase->priList, $case->pri, $case->pri);?>'><?php echo zget($lang->testcase->priList, $case->pri, $case->pri)?></span></td>
<?php $params = "testcaseID=$caseID&version=$case->version";?>
<?php if($type == 'assigntome') $params .= "&from=testtask&taskID=$case->task";?>
<td class='c-title text-left' title="<?php echo $case->title?>"><?php echo html::a($this->createLink('testcase', 'view', $params, '', '', $case->project), $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'");?></td>
<td><span class='label-pri <?php echo 'label-pri-' . $case->pri?>' title='<?php echo zget($lang->testcase->priList, $case->pri, $case->pri);?>'><?php echo zget($lang->testcase->priList, $case->pri, $case->pri)?></span></td>
<td><?php echo zget($lang->testcase->typeList, $case->type);?></td>
<td><?php echo zget($users, $case->openedBy);?></td>
<td><?php echo zget($users, $case->lastRunner);?></td>
<td><?php if(!helper::isZeroDate($case->lastRunDate)) echo substr($case->lastRunDate, 5, 11);?></td>
<td class='<?php echo $case->lastRunResult;?>'><?php if($case->lastRunResult) echo $lang->testcase->resultList[$case->lastRunResult];?></td>
<td>
<?php
if($case->needconfirm)
@@ -95,6 +91,10 @@
}
?>
</td>
<td><?php echo zget($users, $case->openedBy);?></td>
<td><?php echo zget($users, $case->lastRunner);?></td>
<td><?php if(!helper::isZeroDate($case->lastRunDate)) echo substr($case->lastRunDate, 5, 11);?></td>
<td class='<?php echo $case->lastRunResult;?>'><?php if($case->lastRunResult) echo $lang->testcase->resultList[$case->lastRunResult];?></td>
<td class='c-actions'>
<?php
$case->browseType = $type;
+1
View File
@@ -49,6 +49,7 @@ $(function()
{
setCopyKanban($(this).data('id')); $('#copyKanbanModal').modal('hide');
});
handleKanbanWidthAttr();
})
/**
+1
View File
@@ -39,6 +39,7 @@ $(function()
return false;
}
})
handleKanbanWidthAttr();
})
/**
+3 -2
View File
@@ -1399,8 +1399,8 @@ function initKanban($kanban)
maxColHeight: 510,
calcColHeight: calcColHeight,
fluidBoardWidth: fluidBoard,
minColWidth: 285,
maxColWidth: 285,
minColWidth: typeof window.minColWidth === 'number' ? window.minColWidth: defaultMinColWidth,
maxColWidth: typeof window.maxColWidth === 'number' ? window.maxColWidth: defaultMaxColWidth,
cardHeight: cardHeight,
displayCards: typeof window.displayCards === 'number' ? window.displayCards : 2,
createColumnText: kanbanLang.createColumn,
@@ -1559,6 +1559,7 @@ $(function()
distance = 0;
radiusWidth = 10;
initRegionTabs();
$('.leftBtn').click(function()
{
+5 -2
View File
@@ -89,6 +89,9 @@ $lang->kanban->allProducts = 'All Products';
$lang->kanban->mine = 'Mine';
$lang->kanban->alignment = 'Alignment';
$lang->kanban->allUsers = 'Alle Benutzer laden';
$lang->kanban->colWidth = 'Column Width';
$lang->kanban->minColWidth = 'Min Column Width';
$lang->kanban->maxColWidth = 'Max Column Width';
/* Fields. */
$lang->kanban->space = 'Space';
@@ -351,8 +354,8 @@ $lang->kanbanlane->modeList['independent'] = 'Independent Kanban column';
$lang->kanbanlane->heightTypeList['auto'] = "Adaptive <span class='tip'>(Adaptive to card height and lane name height)</span>";
$lang->kanbanlane->heightTypeList['custom'] = "Custom <span class='tip'>(Customize lane height based on number of cards)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "Fixed<span class='tip'>(Keep all columns the same width)</span>";
$lang->kanbancolumn->fluidBoardList['1'] = "Auto Width<span class='tip'>(Adapts to lane width)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "Fixed<i class='radio-text-divider'></i>Width";
$lang->kanbancolumn->fluidBoardList['1'] = "Auto Width<i class='radio-text-divider'></i>Column width range";
$lang->kanbanlane->error = new stdclass();
$lang->kanbanlane->error->mustBeInt = 'No. of cards must be a positive integer from 3 to 32767.';
+5 -2
View File
@@ -89,6 +89,9 @@ $lang->kanban->allProducts = 'All Products';
$lang->kanban->mine = 'Mine';
$lang->kanban->alignment = 'Alignment';
$lang->kanban->allUsers = 'Load All Users';
$lang->kanban->colWidth = 'Column Width';
$lang->kanban->minColWidth = 'Min Column Width';
$lang->kanban->maxColWidth = 'Max Column Width';
/* Fields. */
$lang->kanban->space = 'Space';
@@ -351,8 +354,8 @@ $lang->kanbanlane->modeList['independent'] = 'Independent Kanban column';
$lang->kanbanlane->heightTypeList['auto'] = "Adaptive <span class='tip'>(Adaptive to card height and lane name height)</span>";
$lang->kanbanlane->heightTypeList['custom'] = "Custom <span class='tip'>(Customize lane height based on number of cards)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "Fixed<span class='tip'>(Keep all columns the same width)</span>";
$lang->kanbancolumn->fluidBoardList['1'] = "Auto Width<span class='tip'>(Adapts to lane width)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "Fixed<i class='radio-text-divider'></i>Width";
$lang->kanbancolumn->fluidBoardList['1'] = "Auto Width<i class='radio-text-divider'></i>Column width range";
$lang->kanbanlane->error = new stdclass();
$lang->kanbanlane->error->mustBeInt = 'No. of cards must be a positive integer from 3 to 32767.';
+5 -2
View File
@@ -89,6 +89,9 @@ $lang->kanban->allProducts = 'All Products';
$lang->kanban->mine = 'Mine';
$lang->kanban->alignment = 'Alignment';
$lang->kanban->allUsers = 'Charger Tous';
$lang->kanban->colWidth = 'Column Width';
$lang->kanban->minColWidth = 'Min Column Width';
$lang->kanban->maxColWidth = 'Max Column Width';
/* Fields. */
$lang->kanban->space = 'Space';
@@ -351,8 +354,8 @@ $lang->kanbanlane->modeList['independent'] = 'Independent Kanban column';
$lang->kanbanlane->heightTypeList['auto'] = "Adaptive <span class='tip'>(Adaptive to card height and lane name height)</span>";
$lang->kanbanlane->heightTypeList['custom'] = "Custom <span class='tip'>(Customize lane height based on number of cards)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "Fixed<span class='tip'>(Largeur standard pour toutes les colonnes)</span>";
$lang->kanbancolumn->fluidBoardList['1'] = "Auto Width<span class='tip'>(Adapter selon la largeur de voie)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "Fixed<i class='radio-text-divider'></i>Width";
$lang->kanbancolumn->fluidBoardList['1'] = "Auto Width<i class='radio-text-divider'></i>Column width range";
$lang->kanbanlane->error = new stdclass();
$lang->kanbanlane->error->mustBeInt = 'Nb de cartes doit être un entier positif compris entre 3 et 32767.';
+5 -2
View File
@@ -89,6 +89,9 @@ $lang->kanban->allProducts = '所有产品';
$lang->kanban->mine = '我负责';
$lang->kanban->alignment = '列标题对齐方式';
$lang->kanban->allUsers = '加载所有用户';
$lang->kanban->colWidth = '列宽';
$lang->kanban->minColWidth = '最小列宽';
$lang->kanban->maxColWidth = '最大列宽';
/* Fields. */
$lang->kanban->space = '所属空间';
@@ -351,8 +354,8 @@ $lang->kanbanlane->modeList['independent'] = '采用独立的看板列';
$lang->kanbanlane->heightTypeList['auto'] = "自适应<span class='tip'>(根据卡片高度和泳道名称高度自适应)</span>";
$lang->kanbanlane->heightTypeList['custom'] = "自定义<span class='tip'>(根据卡片数量自定义泳道高度)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "固定列宽<span class='tip'>(所有列保持一致的宽度)</span>";
$lang->kanbancolumn->fluidBoardList['1'] = "自适应列宽<span class='tip'>(根据泳道宽度自适应)</span>";
$lang->kanbancolumn->fluidBoardList['0'] = "固定列宽<i class='radio-text-divider'></i>宽度";
$lang->kanbancolumn->fluidBoardList['1'] = "自适应列宽<i class='radio-text-divider'></i>列宽范围";
$lang->kanbanlane->error = new stdclass();
$lang->kanbanlane->error->mustBeInt = '卡片数量必须是 3~32767 的正整数。';
+9 -10
View File
@@ -2237,6 +2237,9 @@ class kanbanModel extends model
$this->dao->insert(TABLE_KANBAN)->data($kanban)
->autoCheck()
->batchCheck($this->config->kanban->create->requiredFields, 'notempty')
->checkIF(!$kanban->fluidBoard, 'colWidth', 'gt', 0)
->batchCheckIF($kanban->fluidBoard, 'minColWidth,maxColWidth', 'gt', 0)
->checkIF($kanban->minColWidth and $kanban->maxColWidth and $kanban->fluidBoard, 'maxColWidth', 'gt', $kanban->minColWidth)
->check('name', 'unique', "space = {$kanban->space}")
->exec();
@@ -2336,24 +2339,20 @@ class kanbanModel extends model
->get();
if($this->post->import == 'on') $kanban->object = implode(',', $this->post->importObjectList);
if(isset($_POST['heightType']) and $this->post->heightType == 'custom')
{
if(!$this->checkDisplayCards($kanban->displayCards)) return;
}
if(isset($_POST['heightType']) and $this->post->heightType == 'custom' and !$this->checkDisplayCards($kanban->displayCards)) return;
$this->dao->update(TABLE_KANBAN)->data($kanban)
->autoCheck()
->batchCheck($this->config->kanban->edit->requiredFields, 'notempty')
->checkIF(!$kanban->fluidBoard, 'colWidth', 'gt', 0)
->batchCheckIF($kanban->fluidBoard, 'minColWidth,maxColWidth', 'gt', 0)
->checkIF($kanban->minColWidth and $kanban->maxColWidth and $kanban->fluidBoard, 'maxColWidth', 'gt', $kanban->minColWidth)
->where('id')->eq($kanbanID)
->exec();
if(!dao::isError())
{
return common::createChanges($oldKanban, $kanban);
}
if(dao::isError()) return false;
return false;
return common::createChanges($oldKanban, $kanban);
}
/**
+12 -1
View File
@@ -66,7 +66,18 @@
<?php endif;?>
<tr>
<th class='columnWidth'><?php echo $lang->kanban->columnWidth;?></th>
<td colspan='2'><?php echo nl2br(html::radio('fluidBoard', $lang->kanbancolumn->fluidBoardList, isset($copyKanban->fluidBoard) ? $copyKanban->fluidBoard : 0));?></td>
<td colspan='2'>
<div class="width-radio-row">
<?php echo html::radio('fluidBoard', array(0 => $lang->kanbancolumn->fluidBoardList['0']), isset($copyKanban->fluidBoard) ? $copyKanban->fluidBoard : 0);?>
<?php echo html::input('colWidth', !empty($copyKanban->colWidth) ? $copyKanban->colWidth : $this->config->colWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->colWidth}' autocomplete='off'");?>px
</div>
<div class="width-radio-row mt10">
<?php echo html::radio('fluidBoard', array(1 => $lang->kanbancolumn->fluidBoardList['1']), isset($copyKanban->fluidBoard) ? $copyKanban->fluidBoard : 0);?>
<?php echo html::input('minColWidth', !empty($copyKanban->minColWidth) ? $copyKanban->minColWidth: $this->config->minColWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->minColWidth}' autocomplete='off'");?>px
<span class="input-divider">~</span>
<?php echo html::input('maxColWidth', !empty($copyKanban->maxColWidth) ? $copyKanban->maxColWidth: $this->config->maxColWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->maxColWidth}' autocomplete='off'");?>px
</div>
</td>
</tr>
<tr>
<th rowspan='2'><?php echo $lang->kanban->import?></th>
+12 -1
View File
@@ -27,7 +27,18 @@
</tr>
<tr>
<th class='columnWidth'><?php echo $lang->kanban->columnWidth;?></th>
<td colspan='2'><?php echo nl2br(html::radio('fluidBoard', $lang->kanbancolumn->fluidBoardList, $kanban->fluidBoard));?></td>
<td colspan='2'>
<div class="width-radio-row">
<?php echo html::radio('fluidBoard', array(0 => $lang->kanbancolumn->fluidBoardList['0']), $kanban->fluidBoard);?>
<?php echo html::input('colWidth', !empty($kanban->colWidth) ? $kanban->colWidth : $this->config->colWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->colWidth}' autocomplete='off'");?>px
</div>
<div class="width-radio-row mt10">
<?php echo html::radio('fluidBoard', array(1 => $lang->kanbancolumn->fluidBoardList['1']), $kanban->fluidBoard);?>
<?php echo html::input('minColWidth', !empty($kanban->minColWidth) ? $kanban->minColWidth : $this->config->minColWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->minColWidth}' autocomplete='off'");?>px
<span class="input-divider">~</span>
<?php echo html::input('maxColWidth', !empty($kanban->maxColWidth) ? $kanban->maxColWidth : $this->config->maxColWidth, "class='form-control inline-block setting-input' placeholder='{$this->config->maxColWidth}' autocomplete='off'");?>px
</div>
</td>
</tr>
<?php if($laneCount > 1):?>
<tr>
+4
View File
@@ -41,8 +41,12 @@ js::set('colorListLang', $lang->kanbancard->colorList);
js::set('colorList', $this->config->kanban->cardColorList);
js::set('displayCards', $kanban->displayCards);
js::set('fluidBoard', $kanban->fluidBoard);
js::set('minColWidth', $kanban->fluidBoard == '0' ? $kanban->colWidth : $kanban->minColWidth);
js::set('maxColWidth',$kanban->fluidBoard == '0' ? $kanban->colWidth : $kanban->maxColWidth);
js::set('mode', $config->systemMode);
js::set('alignment', $kanban->alignment);
js::set('defaultMinColWidth', $this->config->minColWidth);
js::set('defaultMaxColWidth', $this->config->maxColWidth);
js::set('priv', array('canAssignCard' => common::hasPriv('kanban', 'assigncard')));
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
-1
View File
@@ -1517,7 +1517,6 @@ EOF;
$this->session->set('designList', $uri, 'project');
$this->session->set('productPlanList', $uri, 'product');
$this->session->set('releaseList', $uri, 'product');
$this->session->set('programList', $uri, 'program');
$this->session->set('projectList', $uri, 'project');
$this->session->set('executionList', $uri, 'execution');
$this->session->set('taskList', $uri, 'execution');
+11 -11
View File
@@ -36,13 +36,13 @@
<tr>
<th class='c-id'><?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?></th>
<th><?php common::printOrderLink('name', $orderBy, $vars, $lang->project->name);?></th>
<th class='c-user'><?php common::printOrderLink('PM', $orderBy, $vars, $lang->project->PM);?></th>
<?php if($status == 'openedbyme'):?>
<th class='c-status'><?php common::printOrderLink('status', $orderBy, $vars, $lang->project->status);?></th>
<?php endif;?>
<th class='text-right c-budget'><?php common::printOrderLink('budget', $orderBy, $vars, $lang->project->budget);?></th>
<th class='c-date c-begin'><?php common::printOrderLink('begin', $orderBy, $vars, $lang->project->begin);?></th>
<th class='c-date c-end'><?php common::printOrderLink('end', $orderBy, $vars, $lang->project->end);?></th>
<th class='text-right c-budget'><?php common::printOrderLink('budget', $orderBy, $vars, $lang->project->budget);?></th>
<th class='c-user'><?php common::printOrderLink('PM', $orderBy, $vars, $lang->project->PM);?></th>
<th class='text-center c-actions-6'><?php echo $lang->actions;?></th>
</tr>
</thead>
@@ -53,21 +53,21 @@
<td class='c-name text-left' title='<?php echo $project->name?>'>
<?php echo html::a($this->createLink('project', 'index', "projectID=$project->id", '', '', $project->id), $project->name, '', "data-group='project' title='{$project->name} ({$this->lang->project->{$project->model}})'");?>
</td>
<?php if($status == 'openedbyme'):?>
<td class='c-status'><span class="status-project status-<?php echo $project->status?>"><?php echo zget($lang->project->statusList, $project->status, '');?></span></td>
<?php endif;?>
<td class='text-left'><?php echo $project->begin;?></td>
<td class='text-left'><?php echo $project->end == LONG_TIME ? $this->lang->project->longTime : $project->end;?></td>
<?php $projectBudget = in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$project->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$project->budget, 2);?>
<td class='text-right c-budget'><?php echo $project->budget != 0 ? zget($lang->project->currencySymbol, $project->budgetUnit) . ' ' . $projectBudget : $lang->project->future;?></td>
<td class='c-manager'>
<?php if(!empty($project->PM)):?>
<?php $userName = zget($users, $project->PM);?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$project->PM], 'account' => $project->PM, 'name' => $userName), "avatar-circle avatar-top avatar-{$project->PM}"); ?>
<?php $userID = isset($PMList[$project->PM]) ? $PMList[$project->PM]->id : '';?>
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$project->PM], 'account' => $project->PM, 'name' => $userName), "avatar-circle avatar-{$project->PM}"); ?>
<?php $userID = isset($PMList[$project->PM]) ? $PMList[$project->PM]->id : '';?>
<?php echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), $userName, '', "title='{$userName}' data-toggle='modal' data-type='iframe' data-width='600'");?>
<?php endif;?>
</td>
<?php if($status == 'openedbyme'):?>
<td class='c-status'><span class="status-project status-<?php echo $project->status?>"><?php echo zget($lang->project->statusList, $project->status, '');?></span></td>
<?php endif;?>
<?php $projectBudget = in_array($this->app->getClientLang(), array('zh-cn','zh-tw')) ? round((float)$project->budget / 10000, 2) . $this->lang->project->tenThousand : round((float)$project->budget, 2);?>
<td class='text-right c-budget'><?php echo $project->budget != 0 ? zget($lang->project->currencySymbol, $project->budgetUnit) . ' ' . $projectBudget : $lang->project->future;?></td>
<td class='text-left'><?php echo $project->begin;?></td>
<td class='text-left'><?php echo $project->end == LONG_TIME ? $this->lang->project->longTime : $project->end;?></td>
<td class='c-actions'>
<?php if($project->status == 'wait' || $project->status == 'suspended') common::printIcon('project', 'start', "projectID=$project->id", $project, 'list', 'play', '', 'iframe', true);?>
<?php if($project->status == 'doing') common::printIcon('project', 'close', "projectID=$project->id", $project, 'list', 'off', '', 'iframe', true);?>
+5 -5
View File
@@ -55,17 +55,17 @@
<?php endif;?>
<?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?>
</th>
<th class='c-pri' title=<?php echo $lang->testcase->pri;?>> <?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
<th> <?php common::printOrderLink('title', $orderBy, $vars, $lang->testcase->title);?></th>
<th class='c-pri'> <?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
<?php if($type == 'assigntome'):?>
<th class='c-task'> <?php common::printOrderLink('task', $orderBy, $vars, $lang->testtask->common);?></th>
<?php endif;?>
<th class='c-type'> <?php common::printOrderLink('type', $orderBy, $vars, $lang->typeAB);?></th>
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
<th class='c-status'> <?php echo $lang->statusAB;?></th>
<th class='c-user'> <?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->testcase->openedByAB);?></th>
<th class='c-user'> <?php common::printOrderLink('lastRunner', $orderBy, $vars, $lang->testtask->lastRunAccount);?></th>
<th class='c-full-date'><?php common::printOrderLink('lastRunDate', $orderBy, $vars, $lang->testtask->lastRunTime);?></th>
<th class='c-result'> <?php common::printOrderLink('lastRunResult', $orderBy, $vars, $lang->testtask->lastRunResult);?></th>
<th class='c-status'> <?php echo $lang->statusAB;?></th>
<th class='c-actions-5 text-center'> <?php echo $lang->actions;?></th>
</tr>
</thead>
@@ -86,19 +86,19 @@
<?php endif;?>
<?php echo sprintf('%03d', $case->id); ?>
</td>
<td><span class='label-pri <?php echo 'label-pri-' . $case->pri?>' title='<?php echo zget($lang->testcase->priList, $case->pri, $case->pri);?>'><?php echo zget($lang->testcase->priList, $case->pri, $case->pri)?></span></td>
<?php $params = "testcaseID=$caseID&version=$case->version";?>
<?php if($type == 'assigntome') $params .= "&from=testtask&taskID=$case->task";?>
<td class='c-name'><?php echo html::a($this->createLink('testcase', 'view', $params), $case->title, null, "style='color: $case->color' title='{$case->title}'");?></td>
<td><span class='label-pri <?php echo 'label-pri-' . $case->pri?>' title='<?php echo zget($lang->testcase->priList, $case->pri, $case->pri);?>'><?php echo zget($lang->testcase->priList, $case->pri, $case->pri)?></span></td>
<?php if($type == 'assigntome'):?>
<td class='c-name' title='<?php echo $case->taskName;?>'><?php echo $case->taskName;?></td>
<?php endif;?>
<td><?php echo zget($lang->testcase->typeList, $case->type);?></td>
<td class='<?php if(isset($run)) echo $run->status;?>'><?php echo $this->processStatus('testcase', $case);?></td>
<td><?php echo zget($users, $case->openedBy);?></td>
<td><?php echo zget($users, $case->lastRunner);?></td>
<td><?php echo helper::isZeroDate($case->lastRunDate) ? '' : substr($case->lastRunDate, 5, 11);?></td>
<td class='<?php echo $case->lastRunResult;?>'><?php if($case->lastRunResult) echo $lang->testcase->resultList[$case->lastRunResult];?></td>
<td class='<?php if(isset($run)) echo $run->status;?>'><?php echo $this->processStatus('testcase', $case);?></td>
<td class='c-actions'>
<?php
if($canBeChanged)
+1 -1
View File
@@ -11,7 +11,7 @@ $config->product->custom->batchEditFields = 'line,PO,QD,RD';
if($config->systemMode == 'new') $config->product->custom->batchEditFields .= ',program';
$config->product->list = new stdclass();
$config->product->list->exportFields = 'id,program,line,name,activeRequirements,changedRequirements,draftRequirements,closedRequirements,requireCompleteRate,activeStories,changedStories,draftStories,closedStories,storyCompleteRate,plans,releases,bugs,unResolvedBugs,assignToNullBugs,closedBugs,bugFixedRate';
$config->product->list->exportFields = 'id,program,line,name,manager,draftStories,activeStories,changedStories,reviewingStories,closedStories,storyCompleteRate,bugs,unResolvedBugs,assignToNullBugs,bugFixedRate,plans,releases';
$config->product->showBranchMethod = 'browse,project';
+6 -14
View File
@@ -1440,7 +1440,6 @@ class product extends control
$productConfig = $this->config->product;
/* Create field lists. */
if(!$this->config->URAndSR) $productConfig->list->exportFields = str_replace('activeRequirements,changedRequirements,draftRequirements,closedRequirements,requireCompleteRate,', '', $productConfig->list->exportFields);
$fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $productConfig->list->exportFields);
foreach($fields as $key => $fieldName)
{
@@ -1451,28 +1450,21 @@ class product extends control
$lastProgram = $lastLine = '';
$lines = $this->product->getLinePairs();
$users = $this->user->getPairs('noletter');
$productStats = $this->product->getStats('program_desc,line_desc,' . $orderBy, null, $status);
foreach($productStats as $i => $product)
{
$product->line = zget($lines, $product->line, '');
if($this->config->URAndSR)
{
$product->activeRequirements = (int) $product->requirements['active'];
$product->changedRequirements = (int) $product->requirements['changing'];
$product->draftRequirements = (int) $product->requirements['draft'];
$product->closedRequirements = (int) $product->requirements['closed'];
$product->totalRequirements = $product->activeRequirements + $product->changedRequirements + $product->draftRequirements + $product->closedRequirements;
$product->requireCompleteRate = ($product->totalRequirements == 0 ? 0 : round($product->closedRequirements / $product->totalRequirements, 3) * 100) . '%';
}
$product->line = zget($lines, $product->line, '');
$product->manager = zget($users, $product->PO, '');
$product->draftStories = (int)$product->stories['draft'];
$product->activeStories = (int)$product->stories['active'];
$product->changedStories = (int)$product->stories['changing'];
$product->draftStories = (int)$product->stories['draft'];
$product->reviewingStories = (int)$product->stories['reviewing'];
$product->closedStories = (int)$product->stories['closed'];
$product->totalStories = $product->activeStories + $product->changedStories + $product->draftStories + $product->closedStories;
$product->totalStories = $product->activeStories + $product->changedStories + $product->draftStories + $product->closedStories + $product->reviewingStories;
$product->storyCompleteRate = ($product->totalStories == 0 ? 0 : round($product->closedStories / $product->totalStories, 3) * 100) . '%';
$product->unResolvedBugs = (int)$product->unResolved;
$product->assignToNullBugs = (int)$product->assignToNull;
$product->closedBugs = (int)$product->closedBugs;
$product->bugFixedRate = (($product->unResolved + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unResolved + $product->fixedBugs), 3) * 100) . '%';
$product->program = $product->programName;
+124 -94
View File
@@ -1,4 +1,121 @@
$("#" + browseType + "Tab").addClass('btn-active-text');
/**
* Set batch edit checkbox.
*
* @access public
* @return void
*/
function setCheckbox()
{
$('#productListForm .c-checkbox, #productListForm .check-all').hide();
$('.c-name').css('border-left', 'none');
$(":checkbox[name^='productIDList']").prop('checked', false);
$('.check-all, .program-checkbox, .row-product').removeClass('checked');
if($.cookie('showProductBatchEdit') == 1)
{
$('#productListForm .c-checkbox, #productListForm .check-all').show();
$('.c-name').css('border-left', '1px solid #ddd');
}
else
{
$('.table-actions').hide();
$('#productsCount').show();
}
}
/**
* Update prarent checkbox.
*
* @param object $parent
* @access public
* @return void
*/
function updatePrarentCheckbox($parent)
{
var $row = $parent.closest('tr');
var $checkbox = $row.find('.program-checkbox');
var rowID = $row.data('id');
var $subRows = $('#productTableList').children('.row-product[data-nest-path^="' + rowID + ',"],.row-product[data-nest-path*=",' + rowID + ',"]');
var allCount = $subRows.length;
var selectedCount = $subRows.find('input:checkbox:checked').length;
var isAllChecked = allCount > 0 && allCount === selectedCount;
$checkbox.toggleClass('checked', isAllChecked)
.toggleClass('indeterminate', selectedCount > 0 && selectedCount < allCount);
$row.toggleClass('checked', isAllChecked);
}
/**
* Update all checkbox.
*
* @access public
* @return void
*/
function updateCheckboxes()
{
$('#productTableList').children('.row-program,.row-line').each(function()
{
updatePrarentCheckbox($(this))
});
}
/**
* Add a statistics prompt statement after the Edit button.
*
* @access public
* @return void
*/
function addStatistic()
{
var checkedLength = $(":checkbox[name^='productIDList']:checked").length;
if(checkedLength > 0)
{
var summary = checkedProducts.replace('%s', checkedLength);
if(cilentLang == "en" && checkedLength < 2) summary = summary.replace('products', 'product');
var statistic = "<div id='productsSummary' class='statistic'>" + summary + "</div>";
$('#productsCount').hide();
$('#productsSummary').remove();
$('#editBtn').after(statistic);
$('.table-actions').show();
}
else
{
$('.table-actions').hide();
$('#productsCount').show();
$('#productsSummary').addClass('hidden');
}
}
/**
* Anti shake operation for jquery.
*
* @param fn $fn
* @param delay $delay
* @access public
* @return void
*/
function debounce(fn, delay)
{
var timer = null;
return function()
{
if(timer) clearTimeout(timer);
timer = setTimeout(fn, delay)
}
}
/**
* Update statistics.
*
* @access public
* @return void
*/
function updateStatistic()
{
debounce(addStatistic(), 200)
}
$(function()
{
$('input[name^="showEdit"]').click(function()
@@ -43,106 +160,19 @@ $(function()
}
});
/**
* Set batch edit checkbox.
*
* @access public
* @return void
*/
function setCheckbox()
$('.main-table').on('click', 'tr', function(e)
{
$('#productListForm .c-checkbox, #productListForm .check-all').hide();
$('.c-name').css('border-left', 'none');
if($.cookie('showProductBatchEdit') == 1)
{
$('#productListForm .c-checkbox, #productListForm .check-all').show();
$('.c-name').css('border-left', '1px solid #ddd');
}
}
if($.cookie('showProductBatchEdit') == 1) updateStatistic();
});
/* Update parent checkbox */
function updatePrarentCheckbox($parent)
$('#productTableList').on('click', 'tr', function(e)
{
var $row = $parent.closest('tr');
var $checkbox = $row.find('.program-checkbox');
var rowID = $row.data('id');
var $subRows = $('#productTableList').children('.row-product[data-nest-path^="' + rowID + ',"],.row-product[data-nest-path*=",' + rowID + ',"]');
var allCount = $subRows.length;
var selectedCount = $subRows.find('input:checkbox:checked').length;
var isAllChecked = allCount > 0 && allCount === selectedCount;
$checkbox.toggleClass('checked', isAllChecked)
.toggleClass('indeterminate', selectedCount > 0 && selectedCount < allCount);
$row.toggleClass('checked', isAllChecked);
}
/* Update checkboxes */
function updateCheckboxes()
{
$('#productTableList').children('.row-program,.row-line').each(function()
{
updatePrarentCheckbox($(this))
});
}
/**
* Add a statistics prompt statement after the Edit button.
*
* @access public
* @return void
*/
function addStatistic()
{
var checkedLength = $(":checkbox[name^='productIDList']:checked").length;
if(checkedLength > 0)
{
var summary = checkedProducts.replace('%s', checkedLength);
if(cilentLang == "en" && checkedLength < 2) summary = summary.replace('products', 'product');
var statistic = "<div id='productsSummary' class='statistic'>" + summary + "</div>";
$('#productsCount').hide();
$('#productsSummary').remove();
$('#editBtn').after(statistic);
$('.table-actions').show();
}
else
{
$('.table-actions').hide();
$('#productsCount').show();
$('#productsSummary').addClass('hidden');
}
}
/**
* Anti shake operation for jquery.
*
* @param fn $fn
* @param delay $delay
* @access public
* @return void
*/
function debounce(fn, delay)
{
var timer = null;
return function()
{
if(timer) clearTimeout(timer);
timer = setTimeout(fn, delay)
}
}
/**
* Update statistics.
*
* @access public
* @return void
*/
function updateStatistic()
{
debounce(addStatistic(), 200)
}
if($.cookie('showProductBatchEdit') != 1) e.stopPropagation();
});
$('#productTableList').on('click', '.row-program,.row-line', function(e)
{
if($.cookie('showProductBatchEdit') != 1) return;
if($(e.target).closest('.table-nest-toggle,a').length) return;
var $row = $(this);
+2
View File
@@ -72,6 +72,8 @@ $lang->product->changedStories = 'Geänderte [S]';
$lang->product->changedStoriesTitle = 'Changed Stories';
$lang->product->draftStories = 'Entwurf [S]';
$lang->product->draftStoriesTitle = 'Draft Stories';
$lang->product->reviewingStories = "Reviewing [S]";
$lang->product->reviewingStoriesTitle = "Reviewing Stories";
$lang->product->closedStories = 'Geschlossene [S]';
$lang->product->closedStoriesTitle = 'Closed Stories';
$lang->product->storyCompleteRate = "{$lang->SRCommon} Completion rate";
+2
View File
@@ -72,6 +72,8 @@ $lang->product->changedStories = 'Changed [S]';
$lang->product->changedStoriesTitle = 'Changed Stories';
$lang->product->draftStories = 'Draft [S]';
$lang->product->draftStoriesTitle = 'Draft Stories';
$lang->product->reviewingStories = "Reviewing [S]";
$lang->product->reviewingStoriesTitle = "Reviewing Stories";
$lang->product->closedStories = 'Closed [S]';
$lang->product->closedStoriesTitle = 'Closed Stories';
$lang->product->storyCompleteRate = "{$lang->SRCommon} Completion rate";
+2
View File
@@ -72,6 +72,8 @@ $lang->product->changedStories = 'Changées [S]';
$lang->product->changedStoriesTitle = 'Stories Modifiées';
$lang->product->draftStories = 'Brouillon [S]';
$lang->product->draftStoriesTitle = 'Stories en Analyse';
$lang->product->reviewingStories = "Reviewing [S]";
$lang->product->reviewingStoriesTitle = "Reviewing Stories";
$lang->product->closedStories = 'Fermées [S]';
$lang->product->closedStoriesTitle = 'Stories Fermées';
$lang->product->storyCompleteRate = "{$lang->SRCommon} Completion rate";
+2
View File
@@ -72,6 +72,8 @@ $lang->product->changedStories = "已变更{$lang->SRCommon}";
$lang->product->changedStoriesTitle = "已变更{$lang->SRCommon}";
$lang->product->draftStories = "草稿{$lang->SRCommon}";
$lang->product->draftStoriesTitle = "草稿{$lang->SRCommon}";
$lang->product->reviewingStories = "评审中{$lang->SRCommon}";
$lang->product->reviewingStoriesTitle = "评审中{$lang->SRCommon}";
$lang->product->closedStories = "已关闭{$lang->SRCommon}";
$lang->product->closedStoriesTitle = "已关闭{$lang->SRCommon}";
$lang->product->storyCompleteRate = "{$lang->SRCommon}完成率";
+2 -2
View File
@@ -73,11 +73,11 @@
<?php
if($config->systemMode == 'new')
{
echo html::a($this->createLink('project', 'index', 'project=' . $project->id), $project->name);
echo html::a($this->createLink('project', 'index', 'project=' . $project->id), $project->name, '', "title='{$project->name} ({$lang->project->{$project->model}})'");
}
else
{
echo html::a($this->createLink('execution', 'task', 'project=' . $project->id), $project->name);
echo html::a($this->createLink('execution', 'task', 'project=' . $project->id), $project->name, '', "title='{$project->name}'");
}
?>
</td>
+6 -1
View File
@@ -38,4 +38,9 @@ td.c-branch {overflow: hidden; text-align: left !important; text-overflow: ellip
#productplanList .c-actions {width: 265px;}
#productplanList .c-actions .btn+.btn {margin-left: -1px;}
#productplanList .c-actions .btn {display: block; float: left;}
#productplanList td.c-actions .dividing-line {width: 1px; height: 16px; display: inline-block; vertical-align: middle; background: #F4F5F7; margin: 6px 4px 0 0; float: left;}
#productplanList td.c-actions .dividing-line {width: 1px; height: 16px; display: inline-block; vertical-align: middle; background: #F4F5F7; margin: 6px 0 0 0; float: left;}
.popover.right {left: 40px; top: -5px; white-space: nowrap;}
.popover.right .popover-content {padding: 5px 20px 0px 0px;}
.execution-tip {list-style: none;}
.popover.right .arrow {margin-top: -30px;}
+2
View File
@@ -31,3 +31,5 @@ ol, ul {padding-left: 20px;}
#mainMenu .page-title {display: flex;}
#mainMenu .btn-toolbar .page-title .label {top: 9px; margin-right: 10px; height: 16px;}
#mainMenu .btn-toolbar .page-title .text {max-width: 434px; overflow: hidden; white-space:nowrap; margin-right: 6px;}
#actionsBox a:last-of-type + .divider {display: none;}
#actionsBox div.divider:first-child {display: none;}
+23 -2
View File
@@ -57,8 +57,8 @@ $(function()
$('#kanban').kanban(
{
data: kanbanData,
minColWidth: 290,
maxColWidth: 290,
minColWidth: typeof window.minColWidth === 'number' ? window.minColWidth: defaultMinColWidth,
maxColWidth: typeof window.maxColWidth === 'number' ? window.maxColWidth: defaultMaxColWidth,
maxColHeight: 460,
minColHeight: 190,
cardHeight: 80,
@@ -99,6 +99,27 @@ $(function()
{
$.zui.ContextMenu.hide();
});
$('.execution-popover').on('click', function(e)
{
e.stopPropagation();
var showPopover = $(this).next().css('display') == 'block';
$('.popover.right').hide();
if(!showPopover) $(this).next().show();
});
$('.execution-link').on('click', function()
{
$('.popover.right').hide();
});
/* Hide popover tip. */
$(document).on('mousedown', function(e)
{
var $target = $(e.target);
var $toggle = $target.closest('.popover, .project-popover');
if(!$toggle.length) $('.popover.right').hide();
});
});
/* Define menu creators. */
+2
View File
@@ -73,6 +73,8 @@ $lang->productplan->cannotDeleteParent = 'Cannot delete parent plan';
$lang->productplan->selectProjects = "Please select the project";
$lang->productplan->projectNotEmpty = 'Project cannot be empty.';
$lang->productplan->nextStep = "Next step";
$lang->productplan->summary = "Total: <strong>%s</strong>, Parents: <strong>%s</strong>, Children: <strong>%s</strong>.";
$lang->productplan->checkedSummary = "Seleted: <strong>%total%</strong>, Parents: <strong>%parent%</strong>, Children: <strong>%child%</strong>.";
$lang->productplan->id = 'ID';
$lang->productplan->product = $lang->productCommon;
+2
View File
@@ -73,6 +73,8 @@ $lang->productplan->cannotDeleteParent = 'Cannot delete parent plan';
$lang->productplan->selectProjects = "Please select the project";
$lang->productplan->projectNotEmpty = 'Project cannot be empty.';
$lang->productplan->nextStep = "Next step";
$lang->productplan->summary = "Total: <strong>%s</strong>, Parents: <strong>%s</strong>, Children: <strong>%s</strong>.";
$lang->productplan->checkedSummary = "Seleted: <strong>%total%</strong>, Parents: <strong>%parent%</strong>, Children: <strong>%child%</strong>.";
$lang->productplan->id = 'ID';
$lang->productplan->product = $lang->productCommon;
+2
View File
@@ -73,6 +73,8 @@ $lang->productplan->cannotDeleteParent = 'Impossible de supprimer le plan parent
$lang->productplan->selectProjects = "Please select the project";
$lang->productplan->projectNotEmpty = 'Project cannot be empty.';
$lang->productplan->nextStep = "Next step";
$lang->productplan->summary = "Total: <strong>%s</strong>, Parents: <strong>%s</strong>, Children: <strong>%s</strong>.";
$lang->productplan->checkedSummary = "Seleted: <strong>%total%</strong>, Parents: <strong>%parent%</strong>, Children: <strong>%child%</strong>.";
$lang->productplan->id = 'ID';
$lang->productplan->product = $lang->productCommon;
+2
View File
@@ -73,6 +73,8 @@ $lang->productplan->cannotDeleteParent = "不能删除父计划";
$lang->productplan->selectProjects = "请选择所属项目";
$lang->productplan->projectNotEmpty = '所属项目不能为空。';
$lang->productplan->nextStep = "下一步";
$lang->productplan->summary = "本页共 <strong>%s</strong> 个计划,父计划 <strong>%s</strong>,子计划 <strong>%s</strong>。";
$lang->productplan->checkedSummary = "共选中 <strong>%total%</strong> 个计划,父计划 <strong>%parent%</strong>,子计划 <strong>%child%</strong>。";
$lang->productplan->id = '编号';
$lang->productplan->product = $lang->productCommon;
+18 -19
View File
@@ -122,14 +122,14 @@ class productplanModel extends model
foreach($planIdList as $planID)
{
$planProjects[$planID] = $this->dao->select('t1.*,t2.type')->from(TABLE_PROJECTPRODUCT)->alias('t1')
$planProjects[$planID] = $this->dao->select('t1.project,t2.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
->where('t1.product')->eq($product)
->andWhere('t2.deleted')->eq(0)
->andWhere('t1.plan')->like(",$planID,")
->andWhere('t2.type')->in('sprint,stage,kanban')
->orderBy('project_desc')
->fetch('project');
->fetchAll('project');
}
$storyCountInTable = $this->dao->select('plan,count(story) as count')->from(TABLE_PLANSTORY)->where('plan')->in($planIdList)->groupBy('plan')->fetchPairs('plan', 'count');
@@ -159,12 +159,11 @@ class productplanModel extends model
->andWhere('deleted')->eq(0)
->fetchPairs('id', 'estimate');
}
$plan->stories = count($storyPairs);
$plan->bugs = isset($bugs[$plan->id]) ? count($bugs[$plan->id]) : 0;
$plan->hour = array_sum($storyPairs);
$plan->project = zget($planProjects, $plan->id, '');
$plan->projectID = $plan->project;
$plan->expired = $plan->end < $date ? true : false;
$plan->stories = count($storyPairs);
$plan->bugs = isset($bugs[$plan->id]) ? count($bugs[$plan->id]) : 0;
$plan->hour = array_sum($storyPairs);
$plan->projects = zget($planProjects, $plan->id, '');
$plan->expired = $plan->end < $date ? true : false;
/* Sync linked stories. */
if(!isset($storyCountInTable[$plan->id]) or $storyCountInTable[$plan->id] != $plan->stories)
@@ -1134,8 +1133,8 @@ class productplanModel extends model
$canFinish = common::hasPriv('productplan', 'finish');
$canClose = common::hasPriv('productplan', 'close');
$canCreateExec = common::hasPriv('execution', 'create');
$canLinkStory = common::hasPriv('productplan', 'linkStory');
$canLinkBug = common::hasPriv('productplan', 'linkBug');
$canLinkStory = common::hasPriv('productplan', 'linkStory', $plan);
$canLinkBug = common::hasPriv('productplan', 'linkBug', $plan);
$canEdit = common::hasPriv('productplan', 'edit');
$canCreateChild = common::hasPriv('productplan', 'create');
$canDelete = common::hasPriv('productplan', 'delete');
@@ -1179,30 +1178,30 @@ class productplanModel extends model
$menu .= html::a($executionLink, '<i class="icon-plus"></i>', '', "class='btn' title='{$this->lang->productplan->createExecution}'");
}
}
else
elseif($canCreateExec)
{
$menu .= "<button type='button' class='btn disabled'><i class='icon-plus' title='{$this->lang->productplan->createExecution}'></i></button>";
}
if(($canStart or $canFinsh or $canClose or $canCreateExec) and ($canLinkStory or $canLinkBug or $canEdit or $canCreateChild or $canDelete))
if($type == 'browse' and ($canStart or $canFinish or $canClose or $canCreateExec) and ($canLinkStory or $canLinkBug or $canEdit or $canCreateChild or $canDelete))
{
$menu .= "<div class='dividing-line'></div>";
}
if(common::hasPriv('productplan', 'linkStory', $plan) and $plan->parent >= 0)
if($canLinkStory and $plan->parent >= 0)
{
$menu .= $this->buildMenu('productplan', 'view', "{$params}&type=story&orderBy=id_desc&link=true", $plan, $type, 'link', '', '', '', '', $this->lang->productplan->linkStory);
}
else
elseif($canLinkStory)
{
$menu .= "<button type='button' class='disabled btn'><i class='icon-link' title='{$this->lang->productplan->linkStory}'></i></button>";
}
if(common::hasPriv('productplan', 'linkBug', $plan) and $plan->parent >= 0)
if($canLinkBug and $plan->parent >= 0)
{
$menu .= $this->buildMenu('productplan', 'view', "{$params}&type=bug&orderBy=id_desc&link=true", $plan, $type, 'bug', '', '', '', '', $this->lang->productplan->linkBug);
}
else
elseif($canLinkBug)
{
$menu .= "<button type='button' class='disabled btn'><i class='icon-bug' title='{$this->lang->productplan->linkBug}'></i></button>";
}
@@ -1212,7 +1211,7 @@ class productplanModel extends model
$menu .= $this->buildMenu('productplan', 'create', "product={$plan->product}&branch={$plan->branch}&parent={$plan->id}", $plan, $type, 'split', '', '', '', '', $this->lang->productplan->children);
if(($canLinkStory or $canLinkBug or $canEdit or $canCreateChild) and $canDelete)
if($type == 'browse' and ($canLinkStory or $canLinkBug or $canEdit or $canCreateChild) and $canDelete)
{
$menu .= "<div class='dividing-line'></div>";
}
@@ -1227,8 +1226,8 @@ class productplanModel extends model
$editClickable = $this->buildMenu('productplan', 'edit', $params, $plan, $type, '', '', '', '', '', '', false);
$deleteClickable = $this->buildMenu('productplan', 'delete', $params, $plan, $type, '', '', '', '', '', '', false);
if(common::hasPriv('productplan', 'edit') and $editClickable) $menu .= html::a(helper::createLink('productplan', 'edit', $params), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}'");
if(common::hasPriv('productplan', 'delete') and $deleteClickable) $menu .= html::a(helper::createLink('productplan', 'delete', $params), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin'");
if($canEdit and $editClickable) $menu .= html::a(helper::createLink('productplan', 'edit', $params), "<i class='icon-common-edit icon-edit'></i> " . $this->lang->edit, '', "class='btn btn-link' title='{$this->lang->edit}'");
if($canDelete and $deleteClickable) $menu .= html::a(helper::createLink('productplan', 'delete', $params), "<i class='icon-common-delete icon-trash'></i> " . $this->lang->delete, '', "class='btn btn-link' title='{$this->lang->delete}' target='hiddenwin'");
}
return $menu;
+2
View File
@@ -21,6 +21,8 @@
<?php js::set('product', $product);?>
<?php js::set('systemMode', $config->systemMode);?>
<?php js::set('branchStatusList', $branchStatusList);?>
<?php js::set('defaultMinColWidth', $this->config->minColWidth);?>
<?php js::set('defaultMaxColWidth', $this->config->maxColWidth);?>
<?php
if($viewType == 'kanban')
{
+58 -3
View File
@@ -33,6 +33,8 @@
</div>
<div class="cell<?php if($browseType == 'bySearch') echo ' show';?>" id="queryBox" data-module='productplan'></div>
<div id="mainContent">
<?php $totalParent = 0;?>
<?php $totalChild = 0;?>
<?php if(empty($plans)):?>
<div class="table-empty-tip">
<p>
@@ -43,7 +45,7 @@
</p>
</div>
<?php else:?>
<form class='main-table table-productplan' data-ride='table' method='post' id='productplanForm' action='<?php echo inlink('batchEdit', "productID=$product->id&branch=$branch")?>'>
<form class='main-table table-productplan' method='post' id='productplanForm' action='<?php echo inlink('batchEdit', "productID=$product->id&branch=$branch")?>'>
<table class='table has-sort-head' id="productplanList">
<thead>
<?php $vars = "productID=$productID&branch=$branch&browseType=$browseType&queryID=$queryID&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?>
@@ -89,10 +91,13 @@
{
$parent = $plan->id;
$children = isset($plan->children) ? $plan->children : 0;
$totalParent ++;
}
if($plan->parent == 0) $parent = 0;
if(!empty($parent) and $plan->parent > 0 and $plan->parent != $parent) $parent = 0;
if($plan->parent <= 0) $i = 0;
if($plan->parent > 0) $totalChild ++;
$class = '';
if(!empty($parent) and $plan->parent == $parent)
@@ -103,7 +108,7 @@
$i++;
}
?>
<tr class='<?php echo $class;?>'>
<tr class='<?php echo $class;?>' data-parent="<?php echo $plan->parent;?>">
<td class='cell-id'>
<?php if(common::hasPriv('productplan', 'batchEdit') or common::hasPriv('productplan', 'batchChangeStatus')):?>
<?php echo html::checkbox('planIDList', array($plan->id => ''), '', $attribute) . html::a(helper::createLink('productplan', 'view', "planID=$plan->id"), sprintf('%03d', $plan->id));?>
@@ -140,7 +145,31 @@
<td class='text-center'><?php echo $plan->stories;?></td>
<td class='text-center'><?php echo $plan->bugs;?></td>
<td class='text-center'><?php echo $plan->hour;?></td>
<td class='text-center'><?php if(!empty($plan->projectID)) echo html::a(helper::createLink('execution', 'task', 'projectID=' . $plan->projectID), '<i class="icon-search"></i>');?></td>
<td class='text-center'>
<?php
if(!empty($plan->projects))
{
if(count($plan->projects) === 1)
{
$executionID = key($plan->projects);
echo html::a(helper::createLink('execution', 'task', "executionID=$executionID"), '<i class="icon-run text-primary"></i>', '', "title='{$plan->projects[$executionID]->name}'");
}
else
{
$executionHtml = '<div class="popover right">';
$executionHtml .= '<div class="arrow"></div>';
$executionHtml .= '<div class="popover-content">';
$executionHtml .= '<ul class="execution-tip">';
foreach($plan->projects as $executionID => $execution) $executionHtml .= '<li>' . html::a(helper::createLink('execution', 'task', "executionID=$executionID"), $execution->name, '', "class='execution-link' title='{$execution->name}'") . '</li>';
$executionHtml .= '</ul>';
$executionHtml .= '</div>';
$executionHtml .= '</div>';
echo "<i class='icon-run execution-popover text-primary'></i>";
echo $executionHtml;
}
}
?>
</td>
<td class='text-left content'>
<?php $desc = trim(strip_tags(str_replace(array('</p>', '<br />', '<br>', '<br/>'), "\n", str_replace(array("\n", "\r"), '', $plan->desc)), '<img>'));?>
<div title='<?php echo $desc;?>'><?php echo nl2br($desc);?></div>
@@ -179,8 +208,34 @@
</div>
<?php endif;?>
</div>
<div class="table-statistic"><?php echo sprintf($lang->productplan->summary, count($plans), $totalParent, $totalChild);?></div>
<?php $pager->show('right', 'pagerjs');?>
</div>
</form>
<?php endif;?>
</div>
<script>
$(function()
{
var pageSummary = '<?php echo sprintf($lang->productplan->summary, count($plans), $totalParent, $totalChild);?>';
var checkedSummary = '<?php echo $lang->productplan->checkedSummary?>';
$('#productplanForm').table(
{
replaceId: 'productplanList',
statisticCreator: function(table)
{
var $table = table.getTable();
var $checkedRows = $table.find('tbody>tr.checked');
var checkedTotal = $checkedRows.length;
var checkedParent = $checkedRows.filter("[data-parent=-1]").length;
var checkedNormal = $checkedRows.filter("[data-parent=0]").length;
var checkedChild = checkedTotal - checkedParent - checkedNormal;
var summary = checkedSummary.replace('%total%', checkedTotal)
.replace('%parent%', checkedParent)
.replace('%child%', checkedChild);
return checkedTotal ? summary : pageSummary;
}
});
});
</script>
+2 -1
View File
@@ -196,7 +196,8 @@ class program extends control
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action')->create('program', $programID, 'opened');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $programID, 'locate' => $this->session->programList));
$locateLink = $this->session->programList ? $this->session->programList : $this->createLink('program', 'browse');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $programID, 'locate' => $locateLink));
}
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
+3
View File
@@ -106,6 +106,9 @@ function showEditCheckbox(show)
}
else
{
$('#programForm').removeClass('has-row-checked');
$('#projectsSummary').addClass('hidden');
$('#programSummary').removeClass('hidden');
$('#programForm').find('.editCheckbox').remove();
if($('#programForm .pager').length == 0) $('.table-footer').hide();
$('#programForm').removeAttr('action');
+15 -2
View File
@@ -9,7 +9,7 @@ $(function()
$(":checkbox[name^='productIDList']").on('click', function()
{
updateStatistic()
updateStatistic();
});
$(".check-all").on('click', function()
@@ -22,7 +22,17 @@ $(function()
{
$(":checkbox[name^='productIDList']").prop('checked', true);
}
updateStatistic()
updateStatistic();
});
$('.main-table').on('click', 'tr', function(e)
{
if($.cookie('showProductBatchEdit') == 1) updateStatistic();
});
$('#productListForm .table').on('click', '.sortable tr', function(e)
{
if($.cookie('showProductBatchEdit') != 1) e.stopPropagation();
});
});
@@ -37,12 +47,15 @@ function setCheckbox()
{
$('#productListForm .checkbox-primary, #productListForm .check-all').hide();
$('#productListForm .product-id').addClass('hidden');
$(":checkbox[name^='productIDList']").prop('checked', false);
$('.check-all, .sortable tr').removeClass('checked');
if($.cookie('showProductBatchEdit') == 1)
{
$('#productListForm .checkbox-primary, #productListForm .check-all').show();
}
else
{
$('.table-actions').hide();
$('#productListForm .product-id').removeClass('hidden');
}
}
+86 -1
View File
@@ -6,6 +6,34 @@ $(function()
setCheckbox();
});
setCheckbox();
$(":checkbox[name^='projectIdList']").on('click', function()
{
updateStatistic()
});
$(".check-all").on('click', function()
{
if($(":checkbox[name^='projectIdList']:not(:checked)").length == 0)
{
$(":checkbox[name^='projectIdList']").prop('checked', false);
}
else
{
$(":checkbox[name^='projectIdList']").prop('checked', true);
}
updateStatistic()
});
$('.main-table').on('click', 'tr', function(e)
{
if($.cookie('showProjectBatchEdit') == 1) updateStatistic();
})
$('#projectForm .table').on('click', '.sortable tr', function(e)
{
if($.cookie('showProjectBatchEdit') != 1) e.stopPropagation();
})
});
/**
@@ -17,5 +45,62 @@ $(function()
function setCheckbox()
{
$('#projectsForm .checkbox-primary').hide();
if($.cookie('showProjectBatchEdit') == 1) $('#projectsForm .checkbox-primary').show();
$(":checkbox[name^='projectIdList']").prop('checked', false);
$('.check-all, .sortable tr').removeClass('checked');
if($.cookie('showProjectBatchEdit') == 1)
{
$('#projectsForm .checkbox-primary').show();
}
else
{
$('.table-actions').hide();
}
}
/**
* Add a statistics prompt statement after the Edit button.
*
* @access public
* @return void
*/
function addStatistic()
{
var checkedLength = $(":checkbox[name^='projectIdList']:checked").length;
if(checkedLength > 0)
{
$('.table-actions').show();
}
else
{
$('.table-actions').hide();
}
}
/**
* Anti shake operation for jquery.
*
* @param fn $fn
* @param delay $delay
* @access public
* @return void
*/
function debounce(fn, delay)
{
var timer = null;
return function()
{
if(timer) clearTimeout(timer);
timer = setTimeout(fn, delay)
}
}
/**
* Update statistics.
*
* @access public
* @return void
*/
function updateStatistic()
{
debounce(addStatistic(), 200)
}
+4 -5
View File
@@ -52,7 +52,7 @@
</th>
<th rowspan="2"><?php common::printOrderLink('name', $orderBy, $vars, $lang->product->name);?></th>
<th class='c-PO' rowspan="2"><?php common::printOrderLink('PO', $orderBy, $vars, $lang->product->manager);?></th>
<th class='c-story' colspan="4"><?php echo $lang->story->story;?></th>
<th class='c-story' colspan="5"><?php echo $lang->story->story;?></th>
<th class='c-bug' colspan="2"><?php echo $lang->bug->common;?></th>
<th class='c-plan' rowspan="2"><?php echo $lang->product->plan;?></th>
<th class='c-release' rowspan="2"><?php echo $lang->product->release;?></th>
@@ -62,6 +62,7 @@
<th style="border-left: 1px solid #ddd;"><?php echo $lang->story->draft;?></th>
<th><?php echo $lang->story->activate;?></th>
<th><?php echo $lang->story->change;?></th>
<th><?php echo $lang->story->statusList['reviewing'];?></th>
<th><?php echo $lang->story->completeRate;?></th>
<th style="border-left: 1px solid #ddd;"><?php echo $lang->bug->activate;?></th>
<th><?php echo $lang->bug->fixedRate;?></th>
@@ -69,10 +70,7 @@
</thead>
<tbody class="sortable" id="productTableList">
<?php foreach($products as $product):?>
<?php
$totalStories = $product->stories['active'] + $product->stories['closed'] + $product->stories['draft'] + $product->stories['changing'];
$totalRequirements = $product->requirements['active'] + $product->requirements['closed'] + $product->requirements['draft'] + $product->requirements['changing'];
?>
<?php $totalStories = $product->stories['active'] + $product->stories['closed'] + $product->stories['draft'] + $product->stories['changing'] + $product->stories['reviewing'];?>
<tr class="text-center" data-id='<?php echo $product->id ?>' data-order='<?php echo $product->code;?>'>
<td class='c-id text-left'>
<?php if($canBatchEdit):?>
@@ -96,6 +94,7 @@
<td><?php echo $product->stories['draft'];?></td>
<td><?php echo $product->stories['active'];?></td>
<td><?php echo $product->stories['changing'];?></td>
<td><?php echo $product->stories['reviewing'];?></td>
<td><?php echo $totalStories == 0 ? 0 : round($product->stories['closed'] / $totalStories, 3) * 100;?>%</td>
<td><?php echo $product->unResolved;?></td>
<td><?php echo ($product->unResolved + $product->fixedBugs) == 0 ? 0 : round($product->fixedBugs / ($product->unResolved + $product->fixedBugs), 3) * 100;?>%</td>
+1 -1
View File
@@ -63,7 +63,7 @@ class project extends control
$project->PM = zget($users, $project->PM);
$project->status = $this->processStatus('project', $project);
$project->model = zget($projectLang->modelList, $project->model);
$project->budget = $project->budget . zget($projectLang->unitList, $project->budgetUnit);
$project->budget = $project->budget != 0 ? $project->budget . zget($projectLang->unitList, $project->budgetUnit) : $this->lang->project->future;
$project->parent = $project->parentName;
$linkedProducts = $this->product->getProducts($project->id, 'all', '', false);
+86 -1
View File
@@ -22,6 +22,34 @@ $(function()
setCheckbox();
if(!useDatatable) resetNameWidth();
$(":checkbox[name^='projectIdList']").on('click', function()
{
updateStatistic();
});
$(".check-all").on('click', function()
{
if($(":checkbox[name^='projectIdList']:not(:checked)").length == 0)
{
$(":checkbox[name^='projectIdList']").prop('checked', false);
}
else
{
$(":checkbox[name^='projectIdList']").prop('checked', true);
}
updateStatistic();
});
$('.main-table').on('click', 'tr', function(e)
{
if($.cookie('showProjectBatchEdit') == 1) updateStatistic();
});
$('#projectForm .table').on('click', '.sortable tr', function(e)
{
if($.cookie('showProjectBatchEdit') != 1) e.stopPropagation();
});
});
/**
@@ -33,7 +61,16 @@ $(function()
function setCheckbox()
{
$('#projectForm .checkbox-primary').hide();
if($.cookie('showProjectBatchEdit') == 1) $('#projectForm .checkbox-primary').show();
$('.check-all, .sortable tr').removeClass('checked');
$(":checkbox[name^='projectIdList']").prop('checked', false);
if($.cookie('showProjectBatchEdit') == 1)
{
$('#projectForm .checkbox-primary').show();
}
else
{
$('.table-actions').hide();
}
}
function resetNameWidth()
@@ -61,3 +98,51 @@ function changeProgram(programID)
}
$(".tree #program" + programID).parent('li').addClass('active');
/**
* Add a statistics prompt statement after the Edit button.
*
* @access public
* @return void
*/
function addStatistic()
{
var checkedLength = $(":checkbox[name^='projectIdList']:checked").length;
if(checkedLength > 0)
{
$('.table-actions').show();
}
else
{
$('.table-actions').hide();
}
}
/**
* Anti shake operation for jquery.
*
* @param fn $fn
* @param delay $delay
* @access public
* @return void
*/
function debounce(fn, delay)
{
var timer = null;
return function()
{
if(timer) clearTimeout(timer);
timer = setTimeout(fn, delay)
}
}
/**
* Update statistics.
*
* @access public
* @return void
*/
function updateStatistic()
{
debounce(addStatistic(), 200)
}
@@ -18,8 +18,6 @@
.project-name > span,
.project-name > span {flex: none;}
.project-name > a {display: inline-block; max-width: calc(100% - 50px);}
.project-name.has-prefix > a,
.project-name.has-suffix > a {max-width: calc(100% - 100px);}
.project-name.has-prefix > a {padding-left: 5px;}
.project-name.has-suffix > a {padding-right: 5px;}
</style>
+4 -4
View File
@@ -421,7 +421,7 @@ class story extends control
$this->view->mailto = $mailto;
$this->view->blockID = $blockID;
$this->view->URS = $storyType == 'story' ? $this->story->getRequirements($productID) : '';
$this->view->needReview = ($this->app->user->account == $product->PO || $objectID > 0 || $this->config->story->needReview == 0) ? "checked='checked'" : "";
$this->view->needReview = ($this->app->user->account == $product->PO or $objectID > 0 or $this->config->story->needReview == 0 or !$this->story->checkForceReview()) ? "checked='checked'" : "";
$this->view->type = $storyType;
$this->display();
@@ -687,7 +687,7 @@ class story extends control
$this->view->branch = $branch;
$this->view->branches = $branches;
/* When the user is product owner or add story in project or not set review, the default is not to review. */
$this->view->needReview = ($this->app->user->account == $product->PO || $executionID > 0 || $this->config->story->needReview == 0) ? 0 : 1;
$this->view->needReview = ($this->app->user->account == $product->PO or $executionID > 0 or $this->config->story->needReview == 0 or !$this->story->checkForceReview()) ? 0 : 1;
$this->view->forceReview = $this->story->checkForceReview();
$this->view->executionID = $executionID;
@@ -1150,7 +1150,7 @@ class story extends control
$this->view->title = $this->lang->story->change . "STORY" . $this->lang->colon . $this->view->story->title;
$this->view->users = $this->user->getPairs('pofirst|nodeleted|noclosed', $this->view->story->assignedTo);
$this->view->position[] = $this->lang->story->change;
$this->view->needReview = (($this->app->user->account == $this->view->product->PO or $this->config->story->needReview == 0) and empty($reviewer)) ? "checked='checked'" : "";
$this->view->needReview = (($this->app->user->account == $this->view->product->PO or $this->config->story->needReview == 0 or !$this->story->checkForceReview()) and empty($reviewer)) ? "checked='checked'" : "";
$this->view->reviewer = implode(',', array_keys($reviewer));
$this->view->productReviewers = $this->user->getPairs('noclosed|nodeleted', $reviewer, 0, $productReviewers);
@@ -1574,7 +1574,7 @@ class story extends control
$this->view->actions = $this->action->getList('story', $storyID);
$this->view->reviewers = $this->user->getPairs('noclosed|nodeleted', '', 0, $reviewers);
$this->view->users = $this->user->getPairs('noclosed|noletter');
$this->view->needReview = (($this->app->user->account == $product->PO || $this->config->story->needReview == 0) and empty($story->reviewer)) ? "checked='checked'" : "";
$this->view->needReview = (($this->app->user->account == $product->PO or $this->config->story->needReview == 0 or !$this->story->checkForceReview()) and empty($story->reviewer)) ? "checked='checked'" : "";
$this->display();
}
+12 -1
View File
@@ -1197,7 +1197,18 @@ class task extends control
if(isonlybody()) $this->session->set('estimateList', $uri . (strpos($uri, '?') === false ? '?' : '&') . 'onlybody=yes', 'execution');
$task = $this->task->getById($taskID);
if(!empty($task->team) and $task->mode == 'linear' and !$orderBy) $orderBy = 'order_asc';
if(!empty($task->team) and $task->mode == 'linear')
{
if(empty($orderBy))
{
$orderBy = 'order_asc,id';
}
else
{
/* The id sort with order or date style. */
$orderBy .= preg_replace('/(order_|date_)/', ',id_', $orderBy);
}
}
if(!$orderBy) $orderBy = 'date_asc';
$this->view->title = $this->lang->task->record;
+3 -2
View File
@@ -392,11 +392,11 @@ function addItem(obj)
}
if($nextTr.find('.deadlineBox').length == 0)
{
$nextTr.find('.deadlineInput').after('<span class="input-group-addon deadlineBox"><input type="checkbox" name="deadlineDitto[]" id="deadlineDitto" checked/> ' + ditto + '</span>');
$nextTr.find('.deadlineInput').after('<span class="input-group-addon deadlineBox"><input type="checkbox" name="deadlineDitto[' + index + ']" id="deadlineDitto" checked/> ' + ditto + '</span>');
}
if($nextTr.find('.estStartedBox').length == 0)
{
$nextTr.find('.startInput').after('<span class="input-group-addon estStartedBox"><input type="checkbox" name="estStartedDitto[] id="estStartedDitto" checked/> ' + ditto + '</span>');
$nextTr.find('.startInput').after('<span class="input-group-addon estStartedBox"><input type="checkbox" name="estStartedDitto[' + index + '] id="estStartedDitto" checked/> ' + ditto + '</span>');
}
if($nextTr.find('.deadlineBox').is(':hidden'))
@@ -409,6 +409,7 @@ function addItem(obj)
$nextTr.find('.estStartedBox').show();
$nextTr.find(".estStartedBox input[type='checkBox']").attr('checked', true);
}
index ++;
}
/**
+25 -16
View File
@@ -29,7 +29,11 @@ class taskModel extends model
}
$executionID = (int)$executionID;
$estStarted = '0000-00-00';
$deadline = '0000-00-00';
$assignedTo = '';
$taskIdList = array();
$taskDatas = array();
$taskFiles = array();
$requiredFields = "," . $this->config->task->create->requiredFields . ",";
@@ -38,26 +42,31 @@ class taskModel extends model
foreach($this->post->testStory as $i => $storyID)
{
if(empty($storyID)) continue;
if($this->post->testEstStarted[$i] > $this->post->testDeadline[$i])
{
dao::$errors[] = "ID: $storyID {$this->lang->task->error->deadlineSmall}";
return false;
}
}
/* Check required fields when create test task. */
foreach($this->post->testStory as $i => $storyID)
{
if(empty($storyID)) continue;
$estStarted = (!isset($this->post->testEstStarted[$i]) or (isset($this->post->estStartedDitto[$i]) and $this->post->estStartedDitto[$i] == 'on')) ? $estStarted : $this->post->testEstStarted[$i];
$deadline = (!isset($this->post->testDeadline[$i]) or (isset($this->post->deadlineDitto[$i]) and $this->post->deadlineDitto[$i] == 'on')) ? $deadline : $this->post->testDeadline[$i];
$assignedTo = (!isset($this->post->testAssignedTo[$i]) or $this->post->testAssignedTo[$i] == 'ditto') ? $assignedTo : $this->post->testAssignedTo[$i];
if($estStarted > $deadline)
{
dao::$errors[] = "ID: $storyID {$this->lang->task->error->deadlineSmall}";
return false;
}
$task = new stdclass();
$task->pri = $this->post->testPri[$i];
$task->estStarted = $this->post->testEstStarted[$i];
$task->deadline = $this->post->testDeadline[$i];
$task->assignedTo = $this->post->testAssignedTo[$i];
$task->estStarted = $estStarted;
$task->deadline = $deadline;
$task->assignedTo = $assignedTo;
$task->estimate = $this->post->testEstimate[$i];
$task->left = $this->post->testEstimate[$i];
/* Check requiredFields */
$this->dao->insert(TABLE_TASK)->data($task)->batchCheck($requiredFields, 'notempty');
if(dao::isError())
{
@@ -67,6 +76,7 @@ class taskModel extends model
return false;
}
}
$taskDatas[$i] = $task;
}
$requiredFields = str_replace(",estimate,", ',', "$requiredFields");
@@ -187,10 +197,6 @@ class taskModel extends model
$this->loadModel('action');
if($this->post->testStory)
{
$assignedTo = '';
$testEstStarted = '0000-00-00';
$testDeadline = '0000-00-00';
foreach($this->post->testStory as $storyID)
{
if($storyID) $testStoryIdList[$storyID] = $storyID;
@@ -200,9 +206,9 @@ class taskModel extends model
{
if(!isset($testStories[$storyID])) continue;
$assignedTo = (!isset($this->post->testAssignedTo[$i]) or $this->post->testAssignedTo[$i] == 'ditto') ? $assignedTo : $this->post->testAssignedTo[$i];
$testEstStarted = (!isset($this->post->testEstStarted[$i]) or (isset($this->post->estStartedDitto[$i]) and $this->post->estStartedDitto[$i] == 'on')) ? $testEstStarted : $this->post->testEstStarted[$i];
$testDeadline = (!isset($this->post->testDeadline[$i]) or (isset($this->post->deadlineDitto[$i]) and $this->post->deadlineDitto[$i] == 'on')) ? $testDeadline : $this->post->testDeadline[$i];
$assignedTo = $taskDatas[$i]->assignedTo;
$testEstStarted = $taskDatas[$i]->estStarted;
$testDeadline = $taskDatas[$i]->deadline;
$task->parent = $taskID;
$task->story = $storyID;
@@ -2905,8 +2911,11 @@ class taskModel extends model
public function updateEstimate($estimateID)
{
$oldEstimate = $this->getEstimateById($estimateID);
$estimate = fixer::input('post')->cleanINT('consumed,left')->get();
$today = helper::today();
$estimate = fixer::input('post')
->setIF(is_numeric($this->post->consumed), 'consumed', (float)$this->post->consumed)
->setIF(is_numeric($this->post->left), 'left', (float)$this->post->left)
->get();
if(helper::isZeroDate($estimate->date)) return dao::$errors[] = $this->lang->task->error->dateEmpty;
if($estimate->date > $today) return dao::$errors[] = $this->lang->task->error->date;
+9 -8
View File
@@ -136,25 +136,25 @@ foreach(explode(',', $config->task->create->requiredFields) as $field)
<?php foreach($testStories as $storyID => $storyTitle):?>
<?php if($i > 0) $members['ditto'] = $lang->task->ditto;?>
<tr>
<td><?php echo html::select("testStory[]", array($storyID => $storyTitle), $storyID, "class='form-control chosen'");?></td>
<td><?php echo html::select("testPri[]", $lang->task->priList, $task->pri, "class='form-control chosen'");?></td>
<td><?php echo html::select("testStory[$i]", array($storyID => $storyTitle), $storyID, "class='form-control chosen'");?></td>
<td><?php echo html::select("testPri[$i]", $lang->task->priList, $task->pri, "class='form-control chosen'");?></td>
<td>
<div class='input-group'>
<?php
echo html::input("testEstStarted[]", $task->estStarted, "class='startInput form-control form-date' onchange='hiddenDitto(this)' placeholder='{$lang->task->estStarted}'");
if($i != 0) echo "<span class='input-group-addon estStartedBox'><input type='checkbox' name='estStartedDitto[]' id='estStartedDitto' " . ($i > 0 ? "checked" : '') . " /> {$lang->task->ditto}</span>";
echo html::input("testEstStarted[$i]", $task->estStarted, "class='startInput form-control form-date' onchange='hiddenDitto(this)' placeholder='{$lang->task->estStarted}'");
if($i != 0) echo "<span class='input-group-addon estStartedBox'><input type='checkbox' name='estStartedDitto[$i]' id='estStartedDitto' " . ($i > 0 ? "checked" : '') . " /> {$lang->task->ditto}</span>";
?>
</div>
<td>
<div class='input-group'>
<?php
echo html::input("testDeadline[]", $task->deadline, "class='deadlineInput form-control form-date' onchange='hiddenDitto(this)' placeholder='{$lang->task->deadline}'");
if($i != 0) echo "<span class='input-group-addon deadlineBox'><input type='checkbox' name='deadlineDitto[]' id='deadlineDitto' " . ($i > 0 ? "checked" : '') . " /> {$lang->task->ditto}</span>";
echo html::input("testDeadline[$i]", $task->deadline, "class='deadlineInput form-control form-date' onchange='hiddenDitto(this)' placeholder='{$lang->task->deadline}'");
if($i != 0) echo "<span class='input-group-addon deadlineBox'><input type='checkbox' name='deadlineDitto[$i]' id='deadlineDitto' " . ($i > 0 ? "checked" : '') . " /> {$lang->task->ditto}</span>";
?>
</div>
</td>
<td><?php echo html::select("testAssignedTo[]", $members, $i == 0 ? $task->assignedTo : 'ditto', "class='form-control chosen'");?></td>
<td><?php echo html::input("testEstimate[]", '', "class='form-control'");?></td>
<td><?php echo html::select("testAssignedTo[$i]", $members, $i == 0 ? $task->assignedTo : 'ditto', "class='form-control chosen'");?></td>
<td><?php echo html::input("testEstimate[$i]", '', "class='form-control'");?></td>
<td class='text-center'>
<div class="btn-group">
<button type="button" class="btn btn-sm" tabindex="-1" onclick='addItem(this)'><i class="icon icon-plus"></i></button>
@@ -165,6 +165,7 @@ foreach(explode(',', $config->task->create->requiredFields) as $field)
<?php $i++;?>
<?php if($i > 30) break;?>
<?php endforeach;?>
<?php js::set('index', $i);?>
<?php unset($members['ditto']);?>
</tbody>
</table>
+17 -15
View File
@@ -13,12 +13,15 @@ $this->app->loadLang('execution');
$teamOrders = array();
foreach($task->team as $team) $teamOrders[$team->order] = $team->account;
$myOrders = array();
$allEfforts = array();
$recorders = array();
$index = 0;
$allOrders = array();
$efforts = array_values($efforts);
$index = 0;
$efforts = array_values($efforts);
$recorders = array();
$allOrders = array();
$allEfforts = array();
$myOrders = array();
$myLastID = array();
$myEfforts = array();
$myLastOrder = 0;
foreach($efforts as $key => $effort)
{
$prevEffort = $key > 0 ? $efforts[$key - 1] : null;
@@ -27,12 +30,15 @@ foreach($efforts as $key => $effort)
$allEfforts[$order][] = $effort;
$recorders[$order][$account] = $account;
$allOrders[$order] = $effort->order + 1;
if($app->user->account == $account)
{
if(!isset($myOrders[$order])) $myOrders[$order] = 0;
$myOrders[$order] += 1;
if($allOrders[$myLastOrder] != $effort->order + 1) $myLastOrder = $order;
$myOrders[$myLastOrder] = isset($myOrders[$myLastOrder]) ? ++$myOrders[$myLastOrder] : 1;
$myLastID[$myLastOrder] = isset($myLastID[$myLastOrder]) ? ($myLastID[$myLastOrder] < $effort->id ? $effort->id : $myLastID[$myLastOrder]) : $effort->id;
$myEfforts[$myLastOrder][] = $effort;
}
$allOrders[$order] = $effort->order + 1;
}
?>
<div id='linearefforts'>
@@ -65,8 +71,7 @@ foreach($efforts as $key => $effort)
<tbody>
<?php foreach($myOrders as $order => $count):?>
<?php $showOrder = false;?>
<?php $index = 1;?>
<?php foreach($allEfforts[$order] as $effort):?>
<?php foreach($myEfforts[$order] as $effort):?>
<?php if($effort->account != $this->app->user->account) continue;?>
<tr class="text-center">
<?php if(!$showOrder):?>
@@ -83,12 +88,11 @@ foreach($efforts as $key => $effort)
$canOperateEffort = $this->task->canOperateEffort($task, $effort);
common::printIcon($this->config->edition == 'open' ? 'task' : 'effort', $this->config->edition == 'open' ? 'editEstimate' : 'edit', "effortID=$effort->id", '', 'list', 'edit', '', 'showinonlybody', true, $canOperateEffort ? '' : 'disabled');
$deleteDisable = false;
if(!$canOperateEffort or ($index == $count and $effort->left == 0)) $deleteDisable = true;
if(!$canOperateEffort or ($myLastID[$order] == $effort->id and $effort->left == 0)) $deleteDisable = true;
common::printIcon($this->config->edition == 'open' ? 'task' : 'effort', $this->config->edition == 'open' ? 'deleteEstimate' : 'delete', "effortID=$effort->id", '', 'list', 'trash', 'hiddenwin', 'showinonlybody', false, $deleteDisable ? 'disabled' : '');
?>
</td>
</tr>
<?php $index ++;?>
<?php endforeach;?>
<?php endforeach;?>
</tbody>
@@ -111,7 +115,6 @@ foreach($efforts as $key => $effort)
<tbody>
<?php foreach($recorders as $order => $accounts):?>
<?php $showOrder = false;?>
<?php $index = 1;?>
<?php $count = count($allEfforts[$order]);?>
<?php foreach($allEfforts[$order] as $effort):?>
<tr class="text-center">
@@ -125,7 +128,6 @@ foreach($efforts as $key => $effort)
<td title="<?php echo $effort->consumed . ' ' . $lang->execution->workHour;?>"><?php echo $effort->consumed . ' ' . $lang->execution->workHourUnit;?></td>
<td title="<?php echo $effort->left . ' ' . $lang->execution->workHour;?>"><?php echo $effort->left . ' ' . $lang->execution->workHourUnit;?></td>
</tr>
<?php $index ++;?>
<?php endforeach;?>
<?php endforeach;?>
</tbody>
+40 -41
View File
@@ -79,7 +79,7 @@ $config->testcase->search['params']['lastEditedDate'] = array('operator' => '=',
global $app;
$config->testcase->datatable = new stdclass();
$config->testcase->datatable->defaultField = array('id', 'pri', 'title', 'type', 'openedBy', 'lastRunner', 'lastRunDate', 'lastRunResult', 'status', 'bugs', 'results', 'stepNumber', 'actions');
$config->testcase->datatable->defaultField = array('id', 'title', 'pri', 'openedBy', 'lastRunner', 'lastRunDate', 'lastRunResult', 'actions');
$config->testcase->datatable->fieldList['id']['title'] = 'idAB';
$config->testcase->datatable->fieldList['id']['fixed'] = 'left';
@@ -94,12 +94,6 @@ $config->testcase->datatable->fieldList['module']['title'] = 'module';
$config->testcase->datatable->fieldList['module']['control'] = 'select';
$config->testcase->datatable->fieldList['module']['dataSource'] = array('module' => 'tree', 'method' => 'getOptionMenu', 'params' => '$productID&case');
$config->testcase->datatable->fieldList['pri']['title'] = 'priAB';
$config->testcase->datatable->fieldList['pri']['fixed'] = 'left';
$config->testcase->datatable->fieldList['pri']['width'] = '40';
$config->testcase->datatable->fieldList['pri']['required'] = 'no';
$config->testcase->datatable->fieldList['pri']['name'] = $lang->testcase->pri;
$config->testcase->datatable->fieldList['title']['title'] = 'title';
$config->testcase->datatable->fieldList['title']['fixed'] = 'left';
$config->testcase->datatable->fieldList['title']['width'] = 'auto';
@@ -111,11 +105,44 @@ $config->testcase->datatable->fieldList['branch']['width'] = '100';
$config->testcase->datatable->fieldList['branch']['required'] = 'no';
$config->testcase->datatable->fieldList['branch']['dataSource'] = array('module' => 'port', 'method' => 'getRelatedObjects', 'params' => 'testcase&branch&id,name');
$config->testcase->datatable->fieldList['pri']['title'] = 'priAB';
$config->testcase->datatable->fieldList['pri']['fixed'] = 'left';
$config->testcase->datatable->fieldList['pri']['width'] = '40';
$config->testcase->datatable->fieldList['pri']['required'] = 'no';
$config->testcase->datatable->fieldList['pri']['name'] = $lang->testcase->pri;
$config->testcase->datatable->fieldList['type']['title'] = 'type';
$config->testcase->datatable->fieldList['type']['fixed'] = 'no';
$config->testcase->datatable->fieldList['type']['width'] = '80';
$config->testcase->datatable->fieldList['type']['required'] = 'no';
$config->testcase->datatable->fieldList['assignedTo']['title'] = 'assignedTo';
$config->testcase->datatable->fieldList['assignedTo']['fixed'] = 'no';
$config->testcase->datatable->fieldList['assignedTo']['width'] = '90';
$config->testcase->datatable->fieldList['assignedTo']['required'] = 'no';
$config->testcase->datatable->fieldList['status']['title'] = 'statusAB';
if($app->rawMethod == 'cases')
{
$config->testcase->datatable->fieldList['status']['width'] = '90';
}
else
{
$config->testcase->datatable->fieldList['status']['width'] = '70';
}
$config->testcase->datatable->fieldList['status']['fixed'] = 'no';
$config->testcase->datatable->fieldList['status']['required'] = 'no';
$config->testcase->datatable->fieldList['openedBy']['title'] = 'openedByAB';
$config->testcase->datatable->fieldList['openedBy']['fixed'] = 'no';
$config->testcase->datatable->fieldList['openedBy']['width'] = '80';
$config->testcase->datatable->fieldList['openedBy']['required'] = 'no';
$config->testcase->datatable->fieldList['openedDate']['title'] = 'openedDate';
$config->testcase->datatable->fieldList['openedDate']['fixed'] = 'no';
$config->testcase->datatable->fieldList['openedDate']['width'] = '90';
$config->testcase->datatable->fieldList['openedDate']['required'] = 'no';
$config->testcase->datatable->fieldList['stage']['title'] = 'stage';
$config->testcase->datatable->fieldList['stage']['fixed'] = 'no';
$config->testcase->datatable->fieldList['stage']['width'] = '110';
@@ -132,20 +159,12 @@ $config->testcase->datatable->fieldList['keywords']['fixed'] = 'no';
$config->testcase->datatable->fieldList['keywords']['width'] = '100';
$config->testcase->datatable->fieldList['keywords']['required'] = 'no';
$config->testcase->datatable->fieldList['assignedTo']['title'] = 'assignedTo';
$config->testcase->datatable->fieldList['assignedTo']['fixed'] = 'no';
$config->testcase->datatable->fieldList['assignedTo']['width'] = '90';
$config->testcase->datatable->fieldList['assignedTo']['required'] = 'no';
$config->testcase->datatable->fieldList['openedBy']['title'] = 'openedByAB';
$config->testcase->datatable->fieldList['openedBy']['fixed'] = 'no';
$config->testcase->datatable->fieldList['openedBy']['width'] = '80';
$config->testcase->datatable->fieldList['openedBy']['required'] = 'no';
$config->testcase->datatable->fieldList['openedDate']['title'] = 'openedDate';
$config->testcase->datatable->fieldList['openedDate']['fixed'] = 'no';
$config->testcase->datatable->fieldList['openedDate']['width'] = '90';
$config->testcase->datatable->fieldList['openedDate']['required'] = 'no';
$config->testcase->datatable->fieldList['story']['title'] = 'story';
$config->testcase->datatable->fieldList['story']['fixed'] = 'no';
$config->testcase->datatable->fieldList['story']['width'] = '90';
$config->testcase->datatable->fieldList['story']['required'] = 'no';
$config->testcase->datatable->fieldList['story']['control'] = 'select';
$config->testcase->datatable->fieldList['story']['dataSource'] = array('module' => 'story', 'method' => 'getProductStoryPairs', 'params' => '$productID&$branch');
$config->testcase->datatable->fieldList['reviewedBy']['title'] = 'reviewedBy';
$config->testcase->datatable->fieldList['reviewedBy']['fixed'] = 'no';
@@ -172,19 +191,6 @@ $config->testcase->datatable->fieldList['lastRunResult']['fixed'] = 'no';
$config->testcase->datatable->fieldList['lastRunResult']['width'] = '70';
$config->testcase->datatable->fieldList['lastRunResult']['required'] = 'no';
if($app->rawMethod == 'cases')
{
$config->testcase->datatable->fieldList['status']['title'] = 'executionStatus';
$config->testcase->datatable->fieldList['status']['width'] = '90';
}
else
{
$config->testcase->datatable->fieldList['status']['title'] = 'statusAB';
$config->testcase->datatable->fieldList['status']['width'] = '70';
}
$config->testcase->datatable->fieldList['status']['fixed'] = 'no';
$config->testcase->datatable->fieldList['status']['required'] = 'no';
$config->testcase->datatable->fieldList['lastEditedBy']['title'] = 'lastEditedBy';
$config->testcase->datatable->fieldList['lastEditedBy']['fixed'] = 'no';
$config->testcase->datatable->fieldList['lastEditedBy']['width'] = '92';
@@ -200,13 +206,6 @@ $config->testcase->datatable->fieldList['version']['fixed'] = 'no';
$config->testcase->datatable->fieldList['version']['width'] = '60';
$config->testcase->datatable->fieldList['version']['required'] = 'no';
$config->testcase->datatable->fieldList['story']['title'] = 'story';
$config->testcase->datatable->fieldList['story']['fixed'] = 'no';
$config->testcase->datatable->fieldList['story']['width'] = '90';
$config->testcase->datatable->fieldList['story']['required'] = 'no';
$config->testcase->datatable->fieldList['story']['control'] = 'select';
$config->testcase->datatable->fieldList['story']['dataSource'] = array('module' => 'story', 'method' => 'getProductStoryPairs', 'params' => '$productID&$branch');
$config->testcase->datatable->fieldList['bugs']['title'] = 'B';
$config->testcase->datatable->fieldList['bugs']['fixed'] = 'no';
$config->testcase->datatable->fieldList['bugs']['width'] = '32';
+2
View File
@@ -31,9 +31,11 @@ $lang->testcase->precondition = 'Voraussetzungen';
$lang->testcase->pri = 'Priorität';
$lang->testcase->type = 'Type';
$lang->testcase->status = 'Status';
$lang->testcase->statusAB = 'Status';
$lang->testcase->subStatus = 'Sub Status';
$lang->testcase->steps = 'Schritte';
$lang->testcase->openedBy = 'Ersteller';
$lang->testcase->openedByAB = 'Reporter';
$lang->testcase->openedDate = 'Erstellt am';
$lang->testcase->lastEditedBy = 'Bearbeitet von';
$lang->testcase->result = 'Ergebnis';
+2
View File
@@ -31,9 +31,11 @@ $lang->testcase->precondition = 'Prerequisite';
$lang->testcase->pri = 'Priority';
$lang->testcase->type = 'Type';
$lang->testcase->status = 'Status';
$lang->testcase->statusAB = 'Status';
$lang->testcase->subStatus = 'Sub Status';
$lang->testcase->steps = 'Steps';
$lang->testcase->openedBy = 'CreatedBy';
$lang->testcase->openedByAB = 'Reporter';
$lang->testcase->openedDate = 'CreatedDate';
$lang->testcase->lastEditedBy = 'EditedBy';
$lang->testcase->result = 'Result';
+2
View File
@@ -31,9 +31,11 @@ $lang->testcase->precondition = 'Prérequis';
$lang->testcase->pri = 'Priorité';
$lang->testcase->type = 'Type';
$lang->testcase->status = 'Statut';
$lang->testcase->statusAB = 'Statut';
$lang->testcase->subStatus = 'Sous-statut';
$lang->testcase->steps = 'Etape';
$lang->testcase->openedBy = 'Créé par';
$lang->testcase->openedByAB = 'Détecteur';
$lang->testcase->openedDate = 'Créé le';
$lang->testcase->lastEditedBy = 'Modifié par';
$lang->testcase->result = 'Résultat';
+2
View File
@@ -31,9 +31,11 @@ $lang->testcase->precondition = '前置条件';
$lang->testcase->pri = '优先级';
$lang->testcase->type = '用例类型';
$lang->testcase->status = '用例状态';
$lang->testcase->statusAB = '状态';
$lang->testcase->subStatus = '子状态';
$lang->testcase->steps = '用例步骤';
$lang->testcase->openedBy = '由谁创建';
$lang->testcase->openedByAB = '创建者';
$lang->testcase->openedDate = '创建日期';
$lang->testcase->lastEditedBy = '最后修改者';
$lang->testcase->result = '测试结果';
-2
View File
@@ -61,7 +61,6 @@ class testcaseModel extends model
->setIF($this->config->systemMode == 'new' and $this->app->tab == 'project', 'project', $this->session->project)
->setIF($this->app->tab == 'execution', 'execution', $this->session->execution)
->setIF($this->post->story != false, 'storyVersion', $this->loadModel('story')->getVersion((int)$this->post->story))
->stripTags($this->config->testcase->editor->create['id'], $this->config->allowedTags)
->remove('steps,expects,files,labels,stepType,forceNotReview')
->setDefault('story', 0)
->cleanInt('story,product,branch,module')
@@ -78,7 +77,6 @@ class testcaseModel extends model
/* Value of story may be showmore. */
$case->story = (int)$case->story;
$case = $this->loadModel('file')->processImgURL($case, $this->config->testcase->editor->create['id'], $this->post->uid);
$this->dao->insert(TABLE_CASE)->data($case)->autoCheck()->batchCheck($this->config->testcase->create->requiredFields, 'notempty')->checkFlow()->exec();
if(!$this->dao->isError())
{
+1 -1
View File
@@ -19,7 +19,7 @@ $config->testtask->editor->activate = array('id' => 'comment', 'tools' =
$config->testtask->editor->importunitresult = array('id' => 'desc', 'tools' => 'simpleTools');
$config->testtask->datatable = new stdclass();
$config->testtask->datatable->defaultField = array('id', 'pri', 'title', 'type', 'assignedTo', 'lastRunner', 'lastRunDate', 'lastRunResult', 'status', 'bugs', 'results', 'stepNumber','actions');
$config->testtask->datatable->defaultField = array('id', 'title', 'pri', 'assignedTo', 'openedBy', 'lastRunner', 'lastRunDate', 'lastRunResult', 'actions');
$config->testtask->unitResultRules = new stdclass();
$config->testtask->unitResultRules->common = array('path' => array('testsuite/testcase', 'testcase'), 'name' => array('classname', 'name'), 'failure' => 'failure', 'skipped' => 'skipped', 'suite' => 'name', 'aliasSuite' => array('classname'));
+36
View File
@@ -1329,3 +1329,39 @@ function refreshBudgetUnit(data)
$('#future').prop('checked', false);
}
}
/**
* Handle radio logic of Kanban column width setting.
*
* @access public
* @return void
*/
function handleKanbanWidthAttr ()
{
$('#colWidth, #minColWidth, #maxColWidth').attr('onkeyup', 'value=value.match(/^\\d+$/) ? value : ""');
$('#colWidth, #minColWidth, #maxColWidth').attr('maxlength', '3');
var fluidBoard = $("#mainContent input[name='fluidBoard'][checked='checked']").val() || 0;
var addAttrEle = fluidBoard == 0 ? '#colWidth' : '#minColWidth, #maxColWidth';
$(addAttrEle).closest('.width-radio-row').addClass('required');
$('#colWidth').attr('disabled',fluidBoard == 1);
$('#minColWidth, #maxColWidth').attr('disabled',fluidBoard == 0);
$(document).on('change', "#mainContent input[name='fluidBoard']", function(e)
{
$('#colWidth').attr('disabled', e.target.value == 1);
$('#minColWidth, #maxColWidth').attr('disabled', e.target.value == 0);
if(e.target.value == 0 && $('#minColWidthLabel, #maxColWidthLabel'))
{
$('#colWidth').closest('.width-radio-row').addClass('required');
$('#minColWidth, #maxColWidth').closest('.width-radio-row').removeClass('required');
$('#minColWidthLabel, #maxColWidthLabel').remove();
$('#minColWidth, #maxColWidth').removeClass('has-error');
}
else if(e.target.value == 1 && $('#colWidthLabel'))
{
$('#minColWidth, #maxColWidth').closest('.width-radio-row').addClass('required');
$('#colWidth').closest('.width-radio-row').removeClass('required');
$('#colWidthLabel').remove();
$('#colWidth').removeClass('has-error');
}
})
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+8 -4
View File
@@ -118,10 +118,14 @@ body.xxc-embed.m-productplan-view,body.xxc-embed.m-bug-browse,body.xxc-embed.m-p
#downloadMobile .dropdown-menu .mobile-version {text-align: center;}
#downloadMobile .dropdown-menu .mobile-version span {color: #0c64eb; font-size: 12px; font-weight: 700;}
/* #mainMenu > .btn-toolBar .btn-link .label-light,
#mainMenu > .btn-toolbar .btn-link .label-light {color: #203362; background: #fff;}
#mainMenu > .btn-toolBar .btn-active-text,
#mainMenu > .btn-toolbar .btn-active-text {background: #E6EAF1;} */
.radio-text-divider {width: 1px; height: 13px; display: inline-block; margin: -2px 9px 0; background: #D9D9D9; vertical-align: middle;}
.input-divider {margin: 0 4px;}
.setting-input {width: 64px; height: 20px; margin: 0 4px; text-align: center;}
.setting-input.disabled,.setting-input[disabled="disabled"] {color: #C4C4C4;}
.width-radio-row {display: flex;}
#dataform .width-radio-row.required:after {top: 1px; left: 360px;}
.mt10 {margin-top: 10px;}
#programTableList > tr:hover {background: #E6F0FF; opacity: 0.8;}
#programTableList > tr[data-type="project"] > .c-name:hover > a {color: #1E6AEB !important;}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long