* adjust for parent-child story.
This commit is contained in:
@@ -23,6 +23,7 @@ common:
|
||||
cp -fr framework zentaopms/
|
||||
cp -fr lib zentaopms/
|
||||
cp -fr module zentaopms/
|
||||
cp -fr sdk zentaopms/
|
||||
cp -fr www zentaopms && rm -fr zentaopms/www/data/ && mkdir -p zentaopms/www/data/upload
|
||||
mkdir zentaopms/tmp
|
||||
mkdir zentaopms/tmp/cache/
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `zt_story` ADD `parent` mediumint NOT NULL DEFAULT '0' AFTER `id`;
|
||||
@@ -133,12 +133,19 @@ $lang->action->desc->linked2bug = '$date 由 <strong>$actor</strong> 关联
|
||||
/* 用来描述和父子任务相关的操作历史记录。*/
|
||||
$lang->action->desc->createchildren = '$date, 由 <strong>$actor</strong> 创建子任务 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->linkchildtask = '$date, 由 <strong>$actor</strong> 关联子任务 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->linkchildtask = '$date, 由 <strong>$actor</strong> 关联子任务 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->unlinkchildrentask = '$date, 由 <strong>$actor</strong> 移除子任务 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->linkparenttask = '$date, 由 <strong>$actor</strong> 关联到父任务 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->unlinkparenttask = '$date, 由 <strong>$actor</strong> 从父任务<strong>$extra</strong>取消关联。' . "\n";
|
||||
$lang->action->desc->deletechildrentask = '$date, 由 <strong>$actor</strong> 删除子任务<strong>$extra</strong>。' . "\n";
|
||||
|
||||
/* 用来描述和父子需求相关的操作历史记录。*/
|
||||
$lang->action->desc->createchildrenstory = '$date, 由 <strong>$actor</strong> 创建子需求 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->linkchildstory = '$date, 由 <strong>$actor</strong> 关联子需求 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->unlinkchildrenstory = '$date, 由 <strong>$actor</strong> 移除子需求 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->linkparentstory = '$date, 由 <strong>$actor</strong> 关联到父需求 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->unlinkparentstory = '$date, 由 <strong>$actor</strong> 从父需求<strong>$extra</strong>取消关联。' . "\n";
|
||||
$lang->action->desc->deletechildrenstory = '$date, 由 <strong>$actor</strong> 删除子需求<strong>$extra</strong>。' . "\n";
|
||||
|
||||
/* 关联用例和移除用例时的历史操作记录。*/
|
||||
$lang->action->desc->linkrelatedcase = '$date, 由 <strong>$actor</strong> 关联相关用例 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->unlinkrelatedcase = '$date, 由 <strong>$actor</strong> 移除相关用例 <strong>$extra</strong>。' . "\n";
|
||||
@@ -211,6 +218,12 @@ $lang->action->label->batchcreate = "批量创建任务";
|
||||
$lang->action->label->createchildren = "创建子任务";
|
||||
$lang->action->label->managed = "维护";
|
||||
$lang->action->label->deletechildrentask = "删除子任务";
|
||||
$lang->action->label->createchildrenstory = "创建子需求";
|
||||
$lang->action->label->linkchildstory = "关联子需求";
|
||||
$lang->action->label->unlinkchildrenstory = "取消关联子需求";
|
||||
$lang->action->label->linkparentstory = "关联到父需求";
|
||||
$lang->action->label->unlinkparentstory = "从父需求取消关联";
|
||||
$lang->action->label->deletechildrenstory = "删除子需求";
|
||||
|
||||
/* 动态信息按照对象分组 */
|
||||
$lang->action->dynamicAction = new stdclass();
|
||||
@@ -286,6 +299,11 @@ $lang->action->dynamicAction->task['unlinkparenttask'] = '从父任务取消
|
||||
$lang->action->dynamicAction->task['deletechildrentask'] = '删除子任务';
|
||||
$lang->action->dynamicAction->task['linkparenttask'] = '关联到父任务';
|
||||
$lang->action->dynamicAction->task['linkchildtask'] = '关联子任务';
|
||||
$lang->action->dynamicAction->task['createchildrenstory'] = '创建子需求';
|
||||
$lang->action->dynamicAction->task['unlinkparentstory'] = '从父需求取消关联';
|
||||
$lang->action->dynamicAction->task['deletechildrenstory'] = '删除子需求';
|
||||
$lang->action->dynamicAction->task['linkparentstory'] = '关联到父需求';
|
||||
$lang->action->dynamicAction->task['linkchildstory'] = '关联子需求';
|
||||
$lang->action->dynamicAction->task['undeleted'] = '还原任务';
|
||||
$lang->action->dynamicAction->task['hidden'] = '隐藏任务';
|
||||
$lang->action->dynamicAction->task['svncommited'] = 'SVN提交';
|
||||
|
||||
@@ -319,6 +319,11 @@ class actionModel extends model
|
||||
$name = $this->dao->select('name')->from(TABLE_TASK)->where('id')->eq($action->extra)->fetch('name');
|
||||
if($name) $action->extra = common::hasPriv('task', 'view') ? html::a(helper::createLink('task', 'view', "taskID=$action->extra"), "#$action->extra " . $name) : "#$action->extra " . $name;
|
||||
}
|
||||
elseif($actionName == 'linkchildstory' or $actionName == 'unlinkchildrenstory' or $actionName == 'linkparentstory' or $actionName == 'unlinkparentstory' or $actionName == 'deletechildrenstory')
|
||||
{
|
||||
$name = $this->dao->select('title')->from(TABLE_STORY)->where('id')->eq($action->extra)->fetch('title');
|
||||
if($name) $action->extra = common::hasPriv('story', 'view') ? html::a(helper::createLink('story', 'view', "storyID=$action->extra"), "#$action->extra " . $name) : "#$action->extra " . $name;
|
||||
}
|
||||
elseif($actionName == 'buildopened')
|
||||
{
|
||||
$name = $this->dao->select('name')->from(TABLE_BUILD)->where('id')->eq($action->objectID)->fetch('name');
|
||||
|
||||
@@ -153,7 +153,14 @@ class product extends control
|
||||
|
||||
/* Get related tasks, bugs, cases count of each story. */
|
||||
$storyIdList = array();
|
||||
foreach($stories as $story) $storyIdList[$story->id] = $story->id;
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$storyIdList[$story->id] = $story->id;
|
||||
if(!empty($story->children))
|
||||
{
|
||||
foreach($story->children as $child) $storyIdList[$child->id] = $child->id;
|
||||
}
|
||||
}
|
||||
$storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList);
|
||||
$storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList);
|
||||
$storyCases = $this->loadModel('testcase')->getStoryCaseCounts($storyIdList);
|
||||
|
||||
@@ -15,3 +15,15 @@
|
||||
.popoverStage .icon-caret-down{position:absolute;}
|
||||
a.removeModule{color:#ddd}
|
||||
a.removeModule:hover{color:red}
|
||||
|
||||
.table-children {border-left: 2px solid #cbd0db; border-right: 2px solid #cbd0db;}
|
||||
.table tbody > tr.table-children.table-child-top {border-top: 2px solid #cbd0db;}
|
||||
.table tbody > tr.table-children.table-child-bottom {border-bottom: 2px solid #cbd0db;}
|
||||
.table td.has-child > a:not(.story-toggle) {max-width: 90%; max-width: calc(100% - 30px); display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
|
||||
.table td.has-child > .story-toggle {color: #838a9d; position: relative; top: 1px;}
|
||||
.table td.has-child > .story-toggle:hover {color: #006af1; cursor: pointer;}
|
||||
.table td.has-child > .story-toggle > .icon {font-size: 16px; display: inline-block; transition: transform .2s; -ms-transform:rotate(-90deg); -moz-transform:rotate(-90deg); -o-transform:rotate(-90deg); -webkit-transform:rotate(-90deg); transform: rotate(-90deg);}
|
||||
.table td.has-child > .story-toggle > .icon:before {text-align: left;}
|
||||
.table td.has-child > .story-toggle.collapsed > .icon {-ms-transform:rotate(90deg); -moz-transform:rotate(90deg); -o-transform:rotate(90deg); -webkit-transform:rotate(90deg); transform: rotate(90deg);}
|
||||
.main-table tbody > tr.table-children > td:first-child::before {width: 3px;}
|
||||
@-moz-document url-prefix() {.main-table tbody > tr.table-children > td:first-child::before {width: 4px;}}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
$(function()
|
||||
{
|
||||
if($('#storyList thead th.c-title').width() < 150) $('#storyList thead th.c-title').width(150);
|
||||
$(document).on('click', '.story-toggle', function(e)
|
||||
{
|
||||
var $toggle = $(this);
|
||||
var id = $(this).data('id');
|
||||
var isCollapsed = $toggle.toggleClass('collapsed').hasClass('collapsed');
|
||||
$toggle.closest('[data-ride="table"]').find('tr.parent-' + id).toggle(!isCollapsed);
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Fix state dropdown menu position
|
||||
$('.c-stage > .dropdown').each(function()
|
||||
|
||||
@@ -198,6 +198,17 @@
|
||||
<tr data-id='<?php echo $story->id?>' data-estimate='<?php echo $story->estimate?>' data-cases='<?php echo zget($storyCases, $story->id, 0);?>'>
|
||||
<?php foreach($setting as $key => $value) $this->story->printCell($value, $story, $users, $branches, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases, $useDatatable ? 'datatable' : 'table');?>
|
||||
</tr>
|
||||
<?php if(!empty($story->children)):?>
|
||||
<?php $i = 0;?>
|
||||
<?php foreach($story->children as $key => $child):?>
|
||||
<?php $class = $i == 0 ? ' table-child-top' : '';?>
|
||||
<?php $class .= ($i + 1 == count($story->children)) ? ' table-child-bottom' : '';?>
|
||||
<tr class='table-children<?php echo $class;?> parent-<?php echo $story->id;?>' data-id='<?php echo $child->id?>' data-status='<?php echo $child->status?>' data-estimate='<?php echo $child->estimate?>'>
|
||||
<?php foreach($setting as $key => $value) $this->story->printCell($value, $child, $users, $branches, $storyStages, $modulePairs, $storyTasks, $storyBugs, $storyCases, $useDatatable ? 'datatable' : 'table');?>
|
||||
</tr>
|
||||
<?php $i ++;?>
|
||||
<?php endforeach;?>
|
||||
<?php endif;?>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -398,7 +398,7 @@ class productplan extends control
|
||||
}
|
||||
else
|
||||
{
|
||||
$allStories = $this->story->getProductStories($this->view->product->id, $plan->branch ? "0,{$plan->branch}" : 0, $moduleID = '0', $status = 'draft,active,changed');
|
||||
$allStories = $this->story->getProductStories($this->view->product->id, $plan->branch ? "0,{$plan->branch}" : 0, $moduleID = '0', $status = 'draft,active,changed', null, $hasParent = false);
|
||||
}
|
||||
|
||||
$this->view->allStories = $allStories;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<tbody class='text-center'>
|
||||
<?php $unlinkedCount = 0;?>
|
||||
<?php foreach($allStories as $story):?>
|
||||
<?php if($story->parent < 0) continue;?>
|
||||
<?php if(isset($planStories[$story->id])) continue;?>
|
||||
<tr>
|
||||
<td class='c-id text-left'>
|
||||
|
||||
@@ -2008,7 +2008,11 @@ class project extends control
|
||||
}
|
||||
else
|
||||
{
|
||||
$allStories = $this->story->getProductStories(array_keys($products), $branches, $moduleID = '0', $status = 'active');
|
||||
$allStories = $this->story->getProductStories(array_keys($products), $branches, $moduleID = '0', $status = 'active', $pager = null, $hasParent = false);
|
||||
}
|
||||
foreach($allStories as $id => $story)
|
||||
{
|
||||
if(isset($prjStories[$story->id])) unset($allStories[$id]);
|
||||
}
|
||||
$prjStories = $this->story->getProjectStoryPairs($projectID);
|
||||
|
||||
|
||||
@@ -194,5 +194,5 @@ $config->story->datatable->fieldList['caseCount']['name'] = $lang->story->ca
|
||||
|
||||
$config->story->datatable->fieldList['actions']['title'] = 'actions';
|
||||
$config->story->datatable->fieldList['actions']['fixed'] = 'right';
|
||||
$config->story->datatable->fieldList['actions']['width'] = '150';
|
||||
$config->story->datatable->fieldList['actions']['width'] = '180';
|
||||
$config->story->datatable->fieldList['actions']['required'] = 'yes';
|
||||
|
||||
@@ -307,7 +307,7 @@ class story extends control
|
||||
if($storyID)
|
||||
{
|
||||
if(empty($mails)) die(js::closeModal('parent.parent', 'this'));
|
||||
$actionID = $this->story->subdivide($storyID, $mails);
|
||||
$this->story->subdivide($storyID, $stories);
|
||||
|
||||
if(dao::isError()) die(js::error(dao::getError()));
|
||||
|
||||
@@ -474,6 +474,7 @@ class story extends control
|
||||
$this->view->title = $this->lang->story->edit . "STORY" . $this->lang->colon . $this->view->story->title;
|
||||
$this->view->position[] = $this->lang->story->edit;
|
||||
$this->view->story = $story;
|
||||
$this->view->stories = $this->story->getParentStoryPairs($story->product, $story->parent);
|
||||
$this->view->users = $this->user->getPairs('pofirst|nodeleted', "$story->assignedTo,$story->openedBy,$story->closedBy");
|
||||
$this->view->product = $product;
|
||||
$this->view->branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getPairs($story->product);
|
||||
@@ -766,6 +767,9 @@ class story extends control
|
||||
*/
|
||||
public function delete($storyID, $confirm = 'no')
|
||||
{
|
||||
$story = $this->story->getById($storyID);
|
||||
if($story->parent < 0) die(js::alert($this->lang->story->cannotDeleteParent));
|
||||
|
||||
if($confirm == 'no')
|
||||
{
|
||||
echo js::confirm($this->lang->story->confirmDelete, $this->createLink('story', 'delete', "story=$storyID&confirm=yes"), '');
|
||||
@@ -774,6 +778,11 @@ class story extends control
|
||||
else
|
||||
{
|
||||
$this->story->delete(TABLE_STORY, $storyID);
|
||||
if($story->parent > 0)
|
||||
{
|
||||
$this->story->updateParentStatus($story->id);
|
||||
$this->loadModel('action')->create('story', $task->parent, 'deleteChildrenStory', '', $storyID);
|
||||
}
|
||||
|
||||
$this->executeHooks($storyID);
|
||||
|
||||
@@ -1510,6 +1519,32 @@ class story extends control
|
||||
while($row = $stmt->fetch()) $stories[$row->id] = $row;
|
||||
}
|
||||
|
||||
if($stories)
|
||||
{
|
||||
$children = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
if($story->parent > 0 and isset($stories[$story->parent]))
|
||||
{
|
||||
$children[$story->parent][$story->id] = $story;
|
||||
unset($stories[$story->id]);
|
||||
}
|
||||
}
|
||||
if(!empty($children))
|
||||
{
|
||||
$position = 0;
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$position ++;
|
||||
if(isset($children[$story->id]))
|
||||
{
|
||||
array_splice($stories, $position, 0, $children[$story->id]);
|
||||
$position += count($children[$story->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get users, products and projects. */
|
||||
$users = $this->loadModel('user')->getPairs('noletter');
|
||||
$products = $this->loadModel('product')->getPairs('nocode');
|
||||
@@ -1666,6 +1701,8 @@ class story extends control
|
||||
}
|
||||
$story->reviewedBy = rtrim($story->reviewedBy, ',');
|
||||
|
||||
/* Set child story title. */
|
||||
if($story->parent > 0 && strpos($story->title, htmlentities('>')) !== 0) $story->title = '>' . $story->title;
|
||||
}
|
||||
|
||||
if($projectID)
|
||||
|
||||
@@ -60,6 +60,7 @@ $lang->story->viewAll = '查看全部';
|
||||
|
||||
$lang->story->common = $lang->storyCommon;
|
||||
$lang->story->id = '编号';
|
||||
$lang->story->parent = '父需求';
|
||||
$lang->story->product = "所属{$lang->productCommon}";
|
||||
$lang->story->branch = "分支/平台";
|
||||
$lang->story->module = '所属模块';
|
||||
@@ -100,6 +101,8 @@ $lang->story->version = '版本号';
|
||||
$lang->story->plan = '所属计划';
|
||||
$lang->story->planAB = '计划';
|
||||
$lang->story->comment = '备注';
|
||||
$lang->story->children = "子{$lang->storyCommon}";
|
||||
$lang->story->childrenAB = "子";
|
||||
$lang->story->linkStories = "相关{$lang->storyCommon}";
|
||||
$lang->story->childStories = "细分{$lang->storyCommon}";
|
||||
$lang->story->duplicateStory = "重复{$lang->storyCommon}ID";
|
||||
@@ -216,6 +219,7 @@ $lang->story->mustChooseResult = '必须选择评审结果';
|
||||
$lang->story->mustChoosePreVersion = '必须选择回溯的版本';
|
||||
$lang->story->noStory = "暂时没有{$lang->storyCommon}。";
|
||||
$lang->story->ignoreChangeStage = "{$lang->storyCommon} %s 为草稿状态,没有修改其阶段。";
|
||||
$lang->story->cannotDeleteParent = "不能删除父{$lang->storyCommon}";
|
||||
|
||||
$lang->story->form = new stdclass();
|
||||
$lang->story->form->area = "该{$lang->storyCommon}所属范围";
|
||||
|
||||
+262
-29
@@ -33,6 +33,9 @@ class storyModel extends model
|
||||
$story->spec = isset($spec->spec) ? $spec->spec : '';
|
||||
$story->verify = isset($spec->verify) ? $spec->verify : '';
|
||||
|
||||
/* Check parent story. */
|
||||
if($story->parent > 0) $story->parentName = $this->dao->findById($story->parent)->from(TABLE_STORY)->fetch('title');
|
||||
|
||||
$story = $this->loadModel('file')->replaceImgURL($story, 'spec,verify');
|
||||
if($setImgSize) $story->spec = $this->file->setImgSize($story->spec);
|
||||
if($setImgSize) $story->verify = $this->file->setImgSize($story->verify);
|
||||
@@ -62,6 +65,10 @@ class storyModel extends model
|
||||
if($story->childStories) $extraStories = array_merge($extraStories, explode(',', $story->childStories));
|
||||
$extraStories = array_unique($extraStories);
|
||||
if(!empty($extraStories)) $story->extraStories = $this->dao->select('id,title')->from(TABLE_STORY)->where('id')->in($extraStories)->fetchPairs();
|
||||
|
||||
$story->children = array();
|
||||
if($story->parent == '-1') $story->children = $this->dao->select('*')->from(TABLE_STORY)->where('parent')->eq($storyID)->andWhere('deleted')->eq(0)->fetchAll('id');
|
||||
|
||||
return $story;
|
||||
}
|
||||
|
||||
@@ -574,11 +581,179 @@ class storyModel extends model
|
||||
$this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadModel('action');
|
||||
$changed = $story->parent != $oldStory->parent;
|
||||
if($oldStory->parent > 0)
|
||||
{
|
||||
$oldParentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($oldStory->parent)->fetch();
|
||||
$this->updateParentStatus($storyID, $oldStory->parent, !$changed);
|
||||
|
||||
if($changed)
|
||||
{
|
||||
$oldChildren = $this->dao->select('id')->from(TABLE_STORY)->where('parent')->eq($oldStory->parent)->andWhere('deleted')->eq(0)->fetchPairs('id', 'id');
|
||||
if(empty($oldChildren)) $this->dao->update(TABLE_STORY)->set('parent')->eq(0)->where('id')->eq($oldStory->parent)->exec();
|
||||
$this->dao->update(TABLE_STORY)->set('childStories')->eq(join(',', $oldChildren))->set('lastEditedBy')->eq($this->app->user->account)->set('lastEditedDate')->eq(helper::now())->where('id')->eq($oldStory->parent)->exec();
|
||||
$this->action->create('story', $storyID, 'unlinkParentStory', '', $oldStory->parent, '', false);
|
||||
|
||||
$actionID = $this->action->create('story', $oldStory->parent, 'unLinkChildrenStory', '', $storyID, '', false);
|
||||
|
||||
$newParentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($oldStory->parent)->fetch();
|
||||
$changes = common::createChanges($oldParentStory, $newParentStory);
|
||||
if(!empty($changes)) $this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
}
|
||||
|
||||
if($story->parent > 0)
|
||||
{
|
||||
$parentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($story->parent)->fetch();
|
||||
$this->dao->update(TABLE_STORY)->set('parent')->eq(-1)->where('id')->eq($story->parent)->exec();
|
||||
$this->updateParentStatus($storyID, $story->parent, !$changed);
|
||||
|
||||
if($changed)
|
||||
{
|
||||
$children = $this->dao->select('id')->from(TABLE_STORY)->where('parent')->eq($story->parent)->andWhere('deleted')->eq(0)->fetchPairs('id', 'id');
|
||||
$this->dao->update(TABLE_STORY)
|
||||
->set('parent')->eq('-1')
|
||||
->set('childStories')->eq(join(',', $children))
|
||||
->set('lastEditedBy')->eq($this->app->user->account)
|
||||
->set('lastEditedDate')->eq(helper::now())
|
||||
->where('id')->eq($story->parent)
|
||||
->exec();
|
||||
|
||||
$this->action->create('story', $storyID, 'linkParentStory', '', $story->parent, '', false);
|
||||
$actionID = $this->action->create('story', $story->parent, 'linkChildStory', '', $storyID, '', false);
|
||||
|
||||
$newParentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($story->parent)->fetch();
|
||||
$changes = common::createChanges($parentStory, $newParentStory);
|
||||
if(!empty($changes)) $this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($story->closedReason) and $story->closedReason == 'done') $this->loadModel('score')->create('story', 'close');
|
||||
|
||||
unset($oldStory->parent);
|
||||
unset($story->parent);
|
||||
return common::createChanges($oldStory, $story);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update parent status.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param int $parentID
|
||||
* @param bool $createAction
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function updateParentStatus($storyID, $parentID = 0, $createAction = true)
|
||||
{
|
||||
$childStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($storyID)->fetch();
|
||||
if(empty($parentID)) $parentID = $childStory->parent;
|
||||
if($parentID <= 0) return true;
|
||||
|
||||
$oldParentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($parentID)->andWhere('deleted')->eq(0)->fetch();
|
||||
if(empty($oldParentStory)) return $this->dao->update(TABLE_STORY)->set('parent')->eq('0')->where('id')->eq($storyID)->exec();
|
||||
if($oldParentStory->parent != '-1') $this->dao->update(TABLE_STORY)->set('parent')->eq('-1')->where('id')->eq($parentID)->exec();
|
||||
$this->computeEstimate($parentID);
|
||||
|
||||
$childrenStatus = $this->dao->select('id,status')->from(TABLE_STORY)->where('parent')->eq($parentID)->andWhere('deleted')->eq(0)->fetchPairs('status', 'status');
|
||||
if(empty($childrenStatus)) return $this->dao->update(TABLE_STORY)->set('parent')->eq('0')->where('id')->eq($parentID)->exec();
|
||||
|
||||
$status = $oldParentStory->status;
|
||||
if(count($childrenStatus) == 1 and $oldParentStory->status != 'changed')
|
||||
{
|
||||
$status = current($childrenStatus);
|
||||
if($status == 'draft' or $status == 'changed') $status = 'active';
|
||||
}
|
||||
elseif(count($childrenStatus) != 1 and $oldParentStory->status == 'closed')
|
||||
{
|
||||
$status = 'active';
|
||||
}
|
||||
|
||||
if($status and $oldParentStory->status != $status)
|
||||
{
|
||||
$now = helper::now();
|
||||
$story = new stdclass();
|
||||
$story->status = $status;
|
||||
$story->stage = 'wait';
|
||||
if($status == 'active')
|
||||
{
|
||||
$story->assignedTo = $oldParentStory->openedBy;
|
||||
$story->assignedDate = $now;
|
||||
$story->closedBy = '';
|
||||
$story->closedReason = '';
|
||||
$story->closedDate = '0000-00-00';
|
||||
$story->reviewedBy = '';
|
||||
$story->reviewedDate = '0000-00-00';
|
||||
}
|
||||
|
||||
if($status == 'closed')
|
||||
{
|
||||
$story->assignedTo = 'closed';
|
||||
$story->assignedDate = $now;
|
||||
$story->closedBy = $this->app->user->account;
|
||||
$story->closedDate = $now;
|
||||
$story->closedReason = 'done';
|
||||
$story->closedReason = 'done';
|
||||
}
|
||||
|
||||
$story->lastEditedBy = $this->app->user->account;
|
||||
$story->lastEditedDate = $now;
|
||||
$story->parent = '-1';
|
||||
$this->dao->update(TABLE_STORY)->data($story)->where('id')->eq($parentID)->exec();
|
||||
if(!dao::isError())
|
||||
{
|
||||
if(!$createAction) return $story;
|
||||
|
||||
$newParentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($parentID)->fetch();
|
||||
$changes = common::createChanges($oldParentStory, $newParentStory);
|
||||
$action = '';
|
||||
if($status == 'active') $action = 'Activated';
|
||||
if($status == 'closed') $action = 'Closed';
|
||||
if($action)
|
||||
{
|
||||
$actionID = $this->loadModel('action')->create('story', $parentID, $action, '', '', '', false);
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!dao::isError())
|
||||
{
|
||||
$newParentStory = $this->dao->select('*')->from(TABLE_STORY)->where('id')->eq($parentID)->fetch();
|
||||
$changes = common::createChanges($oldParentStory, $newParentStory);
|
||||
if($changes)
|
||||
{
|
||||
$actionID = $this->loadModel('action')->create('story', $parentID, 'Edited', '', '', '', false);
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute parent story estimate.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function computeEstimate($storyID)
|
||||
{
|
||||
if(!$storyID) return true;
|
||||
|
||||
$stories = $this->dao->select('`id`,`estimate`,status')->from(TABLE_STORY)->where('parent')->eq($storyID)->andWhere('deleted')->eq(0)->fetchAll('id');
|
||||
if(empty($stories)) return true;
|
||||
|
||||
$estimate = 0;
|
||||
foreach($stories as $story) $estimate += $story->estimate;
|
||||
$this->dao->update(TABLE_STORY)->set('estimate')->eq($estimate)->autoCheck()->where('id')->eq($storyID)->exec();
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch update stories.
|
||||
*
|
||||
@@ -810,40 +985,36 @@ class storyModel extends model
|
||||
* @param int $storyID
|
||||
* @param array $stories
|
||||
* @access public
|
||||
* @return int
|
||||
* @return void
|
||||
*/
|
||||
public function subdivide($storyID, $stories)
|
||||
{
|
||||
$now = helper::now();
|
||||
$oldStory = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
|
||||
|
||||
/* Set parent to child story. */
|
||||
$this->dao->update(TABLE_STORY)->set('parent')->eq($storyID)->where('id')->in($stories)->exec();
|
||||
$this->computeEstimate($storyID);
|
||||
|
||||
/* Set childStories. */
|
||||
$childStories = '';
|
||||
foreach($stories as $story) $childStories .= $story->storyID . ',';
|
||||
$childStories = trim($childStories, ',');
|
||||
$childStories = join(',', $stories);
|
||||
|
||||
$newStory = new stdClass();
|
||||
$newStory->parent = '-1';
|
||||
$newStory->plan = 0;
|
||||
$newStory->lastEditedBy = $this->app->user->account;
|
||||
$newStory->lastEditedDate = $now;
|
||||
$newStory->closedDate = $now;
|
||||
$newStory->closedBy = $this->app->user->account;
|
||||
$newStory->assignedTo = 'closed';
|
||||
$newStory->assignedDate = $now;
|
||||
$newStory->status = 'closed';
|
||||
$newStory->closedReason = 'subdivided';
|
||||
$newStory->childStories = $childStories;
|
||||
$newStory->childStories = trim($oldStory->childStories . ',' . $childStories, ',');
|
||||
|
||||
/* Subdivide story and close it. */
|
||||
$this->dao->update(TABLE_STORY)->data($newStory)
|
||||
->autoCheck()
|
||||
->batchCheck($this->config->story->close->requiredFields, 'notempty')
|
||||
->where('id')->eq($storyID)->exec();
|
||||
$changes = common::createChanges($oldStory, $newStory);
|
||||
$actionID = $this->action->create('story', $storyID, 'Closed', '', 'Subdivided');
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
/* Subdivide story. */
|
||||
$this->dao->update(TABLE_STORY)->data($newStory)->autoCheck()->where('id')->eq($storyID)->exec();
|
||||
|
||||
return $actionID;
|
||||
$changes = common::createChanges($oldStory, $newStory);
|
||||
if($changes)
|
||||
{
|
||||
$actionID = $this->action->create('story', $storyID, 'createChildrenStory', '', $childStories);
|
||||
$this->action->logHistory($actionID, $changes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,6 +1047,10 @@ class storyModel extends model
|
||||
->batchCheck($this->config->story->close->requiredFields, 'notempty')
|
||||
->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty')
|
||||
->where('id')->eq($storyID)->exec();
|
||||
|
||||
/* Update parent story status. */
|
||||
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent);
|
||||
|
||||
if(!dao::isError()) $this->loadModel('score')->create('story', 'close', $storyID);
|
||||
return common::createChanges($oldStory, $story);
|
||||
}
|
||||
@@ -934,6 +1109,9 @@ class storyModel extends model
|
||||
|
||||
if(!dao::isError())
|
||||
{
|
||||
/* Update parent story status. */
|
||||
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent);
|
||||
|
||||
$allChanges[$storyID] = common::createChanges($oldStory, $story);
|
||||
}
|
||||
else
|
||||
@@ -1164,6 +1342,9 @@ class storyModel extends model
|
||||
$this->dao->update(TABLE_STORY)->data($story)->autoCheck()->where('id')->eq($storyID)->exec();
|
||||
$this->setStage($storyID);
|
||||
|
||||
/* Update parent story status. */
|
||||
if($oldStory->parent > 0) $this->updateParentStatus($storyID, $oldStory->parent);
|
||||
|
||||
return common::createChanges($oldStory, $story);
|
||||
}
|
||||
|
||||
@@ -1392,11 +1573,12 @@ class storyModel extends model
|
||||
* @param string $type requirement|story
|
||||
* @param string $orderBy
|
||||
* @param object $pager
|
||||
* @param bool $hasParent
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getProductStories($productID = 0, $branch = 0, $moduleIdList = 0, $status = 'all', $type = 'story', $orderBy = 'id_desc', $pager = null)
|
||||
public function getProductStories($productID = 0, $branch = 0, $moduleIdList = 0, $status = 'all', $type = 'story', $orderBy = 'id_desc', $pager = null, $hasParent = true)
|
||||
{
|
||||
if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getStories();
|
||||
|
||||
@@ -1408,12 +1590,13 @@ class storyModel extends model
|
||||
}
|
||||
$stories = $this->dao->select('*')->from(TABLE_STORY)
|
||||
->where('product')->in($productID)
|
||||
->beginIF(!$hasParent)->andWhere("parent")->ge(0)->fi()
|
||||
->beginIF($branch)->andWhere("branch")->in($branch)->fi()
|
||||
->beginIF(!empty($moduleIdList))->andWhere('module')->in($moduleIdList)->fi()
|
||||
->beginIF($status and $status != 'all')->andWhere('status')->in($status)->fi()
|
||||
->andWhere('deleted')->eq(0)
|
||||
->andWhere('type')->eq($type)
|
||||
->orderBy($orderBy)->page($pager)->fetchAll();
|
||||
->orderBy($orderBy)->page($pager)->fetchAll('id');
|
||||
return $this->mergePlanTitle($productID, $stories, $branch);
|
||||
}
|
||||
|
||||
@@ -1571,7 +1754,7 @@ class storyModel extends model
|
||||
->beginIF($operator == 'include')->andWhere($fieldName)->like("%$fieldValue%")->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll();
|
||||
->fetchAll('id');
|
||||
return $this->mergePlanTitle($productID, $stories, $branch);
|
||||
}
|
||||
|
||||
@@ -1877,6 +2060,18 @@ class storyModel extends model
|
||||
->fetchAll();
|
||||
}
|
||||
|
||||
public function getParentStoryPairs($productID, $append = '')
|
||||
{
|
||||
$stories = $this->dao->select('id, title')->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('parent')->le(0)
|
||||
->andWhere('status')->notin('closed,draft')
|
||||
->andWhere('product')->eq($productID)
|
||||
->beginIF($append)->orWhere('id')->in($append)->fi()
|
||||
->fetchPairs();
|
||||
return array(0 => '') + $stories ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stories of a user.
|
||||
*
|
||||
@@ -1903,7 +2098,7 @@ class storyModel extends model
|
||||
->fi()
|
||||
->orderBy($orderBy)
|
||||
->page($pager)
|
||||
->fetchAll();
|
||||
->fetchAll('id');
|
||||
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false);
|
||||
$productIdList = array();
|
||||
@@ -1984,7 +2179,7 @@ class storyModel extends model
|
||||
*/
|
||||
public function getZeroCase($productID, $orderBy = 'id_desc')
|
||||
{
|
||||
$allStories = $this->getProductStories($productID, 0, 0, 'all', 'story', $orderBy);
|
||||
$allStories = $this->getProductStories($productID, 0, 0, 'all', 'story', $orderBy, null, $hasParent = false);
|
||||
$casedStories = $this->dao->select('DISTINCT story')->from(TABLE_CASE)->where('product')->eq($productID)->andWhere('story')->ne(0)->andWhere('deleted')->eq(0)->fetchAll('story');
|
||||
|
||||
foreach($allStories as $key => $story)
|
||||
@@ -2413,11 +2608,15 @@ class storyModel extends model
|
||||
{
|
||||
$action = strtolower($action);
|
||||
|
||||
if($story->parent < 0 and $action != 'edit' and $action != 'batchcreate') return false;
|
||||
|
||||
if($action == 'change') return $story->status != 'closed';
|
||||
if($action == 'review') return $story->status == 'draft' or $story->status == 'changed';
|
||||
if($action == 'close') return $story->status != 'closed';
|
||||
if($action == 'activate') return $story->status == 'closed';
|
||||
if($action == 'assignto') return $story->status != 'closed';
|
||||
if($action == 'batchcreate' and $story->parent > 0) return false;
|
||||
if($action == 'batchcreate' and ($story->status == 'closed' or $story->status == 'draft' or $story->stage != 'wait')) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2446,8 +2645,31 @@ class storyModel extends model
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchPairs('id', 'title');
|
||||
|
||||
$parents = array();
|
||||
$tmpStories = array();
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$tmpStories[$story->id] = $story;
|
||||
if($story->parent > 0) $parents[$story->parent] = $story->parent;
|
||||
}
|
||||
$parents = $this->dao->select('*')->from(TABLE_STORY)->where('id')->in($parents)->fetchAll('id');
|
||||
|
||||
foreach($stories as $storyID => $story)
|
||||
{
|
||||
if($story->parent > 0)
|
||||
{
|
||||
if(isset($stories[$story->parent]))
|
||||
{
|
||||
$stories[$story->parent]->children[$story->id] = $story;
|
||||
unset($stories[$storyID]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent = $parents[$story->parent];
|
||||
$story->parentName = $story->title;
|
||||
}
|
||||
}
|
||||
|
||||
$story->planTitle = '';
|
||||
$storyPlans = explode(',', trim($story->plan, ','));
|
||||
foreach($storyPlans as $planID) $story->planTitle .= zget($plans, $planID, '') . ' ';
|
||||
@@ -2484,14 +2706,21 @@ class storyModel extends model
|
||||
$class = "c-{$id}";
|
||||
$title = '';
|
||||
|
||||
if ($id == 'assignedTo')
|
||||
if($id == 'assignedTo')
|
||||
{
|
||||
$title = zget($users, $story->assignedTo, $story->assignedTo);
|
||||
if($story->assignedTo == $account) $class .= ' red';
|
||||
}
|
||||
else if($id == 'openedBy') $title = zget($users, $story->openedBy, $story->openedBy);
|
||||
else if($id == 'title') $title = $story->title;
|
||||
else if($id == 'plan')
|
||||
elseif($id == 'openedBy')
|
||||
{
|
||||
$title = zget($users, $story->openedBy, $story->openedBy);
|
||||
}
|
||||
elseif($id == 'title')
|
||||
{
|
||||
$title = $story->title;
|
||||
if(!empty($story->children)) $class .= ' has-child';
|
||||
}
|
||||
elseif($id == 'plan')
|
||||
{
|
||||
$title = $story->planTitle;
|
||||
$class .= ' text-ellipsis';
|
||||
@@ -2524,9 +2753,12 @@ class storyModel extends model
|
||||
echo "</span>";
|
||||
break;
|
||||
case 'title':
|
||||
if($story->parent > 0 and isset($story->parentName)) $story->title = "{$story->parentName} / {$story->title}";
|
||||
if($story->branch and isset($branches[$story->branch])) echo "<span class='label label-outline label-badge'>{$branches[$story->branch]}</span> ";
|
||||
if($story->module and isset($modulePairs[$story->module])) echo "<span class='label label-gray label-badge'>{$modulePairs[$story->module]}</span> ";
|
||||
if($story->parent > 0) echo '<span class="label label-badge label-light" title="' . $this->lang->story->children . '">' . $this->lang->story->childrenAB . '</span> ';
|
||||
echo $canView ? html::a($storyLink, $story->title, '', "style='color: $story->color'") : "<span style='color: $story->color'>{$story->title}</span>";
|
||||
if(!empty($story->children)) echo '<a class="story-toggle" data-id="' . $story->id . '"><i class="icon icon-angle-double-right"></i></a>';
|
||||
break;
|
||||
case 'plan':
|
||||
echo $story->planTitle;
|
||||
@@ -2634,6 +2866,7 @@ class storyModel extends model
|
||||
common::printIcon('story', 'close', $vars, $story, 'list', '', '', 'iframe', true);
|
||||
common::printIcon('story', 'edit', $vars, $story, 'list');
|
||||
if($this->config->global->flow != 'onlyStory') common::printIcon('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap');
|
||||
common::printIcon('story', 'batchCreate', "productID=$story->product&branch=$story->branch&module=0&storyID=$story->id", $story, 'list', 'treemap-alt', '', '', '', '', $this->lang->story->subdivide);
|
||||
break;
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<?php include './header.html.php';?>
|
||||
<div id="mainContent" class="main-content">
|
||||
<div class="main-header">
|
||||
<h2><?php echo $lang->story->batchCreate;?></h2>
|
||||
<h2><?php echo $storyID ? $this->lang->story->subdivide : $this->lang->story->batchCreate;?></h2>
|
||||
<div class="pull-right btn-toolbar">
|
||||
<?php if(common::hasPriv('file', 'uploadImages')) echo html::a($this->createLink('file', 'uploadImages', 'module=story¶ms=' . helper::safe64Encode("productID=$productID&branch=$branch&moduleID=$moduleID")), $lang->uploadImages, '', "data-toggle='modal' data-type='iframe' class='btn btn-primary' data-width='70%'")?>
|
||||
<button type='button' data-toggle='modal' data-target="#importLinesModal" class="btn btn-primary"><?php echo $lang->pasteText;?></button>
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<div class='detail'>
|
||||
<div class='detail-title'><?php echo $lang->story->legendBasicInfo;?></div>
|
||||
<table class='table table-form'>
|
||||
<?php if($story->parent <= 0):?>
|
||||
<tr>
|
||||
<th class='thWidth'><?php echo $lang->story->product;?></th>
|
||||
<td>
|
||||
@@ -76,8 +77,9 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->module;?></th>
|
||||
<th class='thWidth'><?php echo $lang->story->module;?></th>
|
||||
<td>
|
||||
<div class='input-group' id='moduleIdBox'>
|
||||
<?php
|
||||
@@ -94,6 +96,12 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if($story->parent >= 0):?>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->parent;?></th>
|
||||
<td><?php echo html::select('parent', $stories, $story->parent, "class='form-control chosen'");?></td>
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->plan;?></th>
|
||||
<td>
|
||||
@@ -153,7 +161,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->estimate;?></th>
|
||||
<td><?php echo html::input('estimate', $story->estimate, "class='form-control'");?></td>
|
||||
<td><?php echo $story->parent >= 0 ? html::input('estimate', $story->estimate, "class='form-control'") : $story->estimate;?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo $lang->story->keywords;?></th>
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
<?php endif;?>
|
||||
<div class="page-title">
|
||||
<span class="label label-id"><?php echo $story->id?></span>
|
||||
<span class="text" title='<?php echo $story->title;?>' style='color: <?php echo $story->color;?>'><?php echo $story->title;?></span>
|
||||
<span class="text" title='<?php echo $story->title;?>' style='color: <?php echo $story->color;?>'>
|
||||
<?php if($story->parent > 0) echo '<span class="label label-badge label-primary no-margin">' . $this->lang->story->childrenAB . '</span>';?>
|
||||
<?php if($story->parent > 0) echo isset($story->parentName) ? html::a(inlink('view', "storyID={$story->parent}"), $story->parentName) . ' / ' : '';?><?php echo $story->title;?>
|
||||
</span>
|
||||
<?php if($story->version > 1):?>
|
||||
<small class='dropdown'>
|
||||
<a href='#' data-toggle='dropdown' class='text-muted'><?php echo '#' . $version;?> <span class='caret'></span></a>
|
||||
@@ -61,6 +64,55 @@
|
||||
</div>
|
||||
<?php echo $this->fetch('file', 'printFiles', array('files' => $story->files, 'fieldset' => 'true'));?>
|
||||
<?php $actionFormLink = $this->createLink('action', 'comment', "objectType=story&objectID=$story->id");?>
|
||||
<?php if(!empty($story->children)):?>
|
||||
<div class='detail'>
|
||||
<div class='detail-title'><?php echo $this->lang->story->children;?></div>
|
||||
<div class='detail-content article-content'>
|
||||
<table class='table table-hover table-fixed'>
|
||||
<thead>
|
||||
<tr class='text-center'>
|
||||
<th class='w-50px'> <?php echo $lang->story->id;?></th>
|
||||
<th class='w-40px'> <?php echo $lang->priAB;?></th>
|
||||
<th> <?php echo $lang->story->title;?></th>
|
||||
<th class='w-100px'><?php echo $lang->story->assignedTo;?></th>
|
||||
<th class='w-80px'> <?php echo $lang->story->estimate;?></th>
|
||||
<th class='w-80px'> <?php echo $lang->story->status;?></th>
|
||||
<th class='w-200px'><?php echo $lang->actions;?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($story->children as $child):?>
|
||||
<tr class='text-center'>
|
||||
<td><?php echo $child->id;?></td>
|
||||
<td>
|
||||
<?php
|
||||
echo "<span class='pri-" . $child->pri . "'>";
|
||||
echo $child->pri == '0' ? '' : zget($this->lang->story->priList, $child->pri, $child->pri);
|
||||
echo "</span>";
|
||||
?>
|
||||
</td>
|
||||
<td class='text-left' title='<?php echo $child->title;?>'><a class="iframe" data-width="90%" href="<?php echo $this->createLink('story', 'view', "storyID=$child->id", '', true); ?>"><?php echo $child->title;?></a></td>
|
||||
<td><?php echo zget($users, $child->assignedTo);?></td>
|
||||
<td><?php echo $child->estimate;?></td>
|
||||
<td><?php echo $this->processStatus('story', $child);?></td>
|
||||
<td class='c-actions'>
|
||||
<?php
|
||||
common::printIcon('story', 'change', "storyID=$child->id", $child, 'list');
|
||||
common::printIcon('story', 'review', "storyID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
|
||||
common::printIcon('story', 'assignTo', "storyID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
|
||||
common::printIcon('story', 'close', "storyID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
|
||||
common::printIcon('story', 'activate', "storyID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
|
||||
common::printIcon('story', 'edit', "storyID=$child->id", $child, 'list');
|
||||
common::printIcon('story', 'createCase', "productID=$child->product&branch=$child->branch&module=0&from=¶m=0&story={$child->id}", $child, 'list', 'sitemap');
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach;?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php $this->printExtendFields($story, 'div', "position=left&inForm=0&inCell=1");?>
|
||||
<div class="cell"><?php include '../../common/view/action.html.php';?></div>
|
||||
@@ -72,18 +124,18 @@
|
||||
<?php
|
||||
common::printIcon('story', 'change', "storyID=$story->id", $story, 'button', '', '', 'showinonlybody');
|
||||
common::printIcon('story', 'review', "storyID=$story->id", $story, 'button', '', '', 'showinonlybody');
|
||||
if($story->status != 'closed' and !isonlybody())
|
||||
if($story->status != 'closed' and $story->status != 'draft' and $story->parent <= 0 and !isonlybody())
|
||||
{
|
||||
$misc = "class='btn' data-toggle='modal' data-type='iframe' data-width='95%'";
|
||||
$link = $this->createLink('story', 'batchCreate', "productID=$story->product&branch=$story->branch&moduleID=$story->module&storyID=$story->id", '', true);
|
||||
if(common::hasPriv('story', 'batchCreate')) echo html::a($link, "<i class='icon icon-sitemap'></i> " . $lang->story->subdivide, '', $misc);
|
||||
if(common::hasPriv('story', 'batchCreate')) echo html::a($link, "<i class='icon icon-treemap-alt'></i> " . $lang->story->subdivide, '', $misc);
|
||||
}
|
||||
|
||||
common::printIcon('story', 'assignTo', "storyID=$story->id", $story, 'button', '', '', 'iframe showinonlybody', true);
|
||||
common::printIcon('story', 'close', "storyID=$story->id", $story, 'button', '', '', 'iframe showinonlybody', true);
|
||||
common::printIcon('story', 'activate', "storyID=$story->id", $story, 'button', '', '', 'iframe showinonlybody', true);
|
||||
|
||||
if($config->global->flow != 'onlyStory' and !isonlybody() and (common::hasPriv('testcase', 'create') or common::hasPriv('testcase', 'batchCreate')))
|
||||
if($config->global->flow != 'onlyStory' and !isonlybody() and $story->parent >= 0 and (common::hasPriv('testcase', 'create') or common::hasPriv('testcase', 'batchCreate')))
|
||||
{
|
||||
$this->app->loadLang('testcase');
|
||||
echo "<div class='btn-group dropup'>";
|
||||
|
||||
@@ -1393,7 +1393,7 @@ class task extends control
|
||||
$children = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if(!empty($task->parent) and isset($tasks[$task->parent]))
|
||||
if($task->parent > 0 and isset($tasks[$task->parent]))
|
||||
{
|
||||
$children[$task->parent][$task->id] = $task;
|
||||
unset($tasks[$task->id]);
|
||||
|
||||
Reference in New Issue
Block a user