Merge branch 'master' into 'sprint/191_zhouxin_54352'

# Conflicts:
#   module/datatable/config.php
This commit is contained in:
孙广明
2022-05-19 03:18:24 +00:00
52 changed files with 528 additions and 158 deletions
+3 -1
View File
@@ -58,7 +58,9 @@ class productEntry extends Entry
$product->bugStatistic = $this->loadModel('bug')->getStatistic($productID);
break;
case 'moduleoptionmenu':
$product->moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($productID, 'story', 0, '0');
$modules = $this->loadModel('tree')->getOptionMenu($productID, $this->param('moduleType', 'story'));
$product->moduleOptionMenu = array();
foreach($modules as $id => $name) $product->moduleOptionMenu[] = array('id' => $id, 'name' => $name);
break;
case 'parentstories':
$product->parentstories= $this->loadModel('story')->getParentStoryPairs($productID);
+3 -3
View File
@@ -661,11 +661,11 @@ class baseValidater
global $config;
if(empty($config->framework->filterXSS)) return $var;
if(stripos($var, '<script') !== false)
if(stripos($var, '&lt;script') !== false or stripos($var, '<script') !== false)
{
$var = (string)$var;
$evils = array('appendchild(', 'createElement(', 'xss.re', 'onfocus', 'onclick', 'innerHTML', 'replaceChild(', 'html(', 'append(', 'appendTo(', 'prepend(', 'prependTo(', 'after(', 'insertBefore', 'before(', 'replaceWith(');
$replaces = array('a p p e n d c h i l d (', 'c r e a t e E l e m e n t (', 'x s s . r e', 'o n f o c u s', 'o n c l i c k', 'i n n e r H T M L', 'r e p l a c e C h i l d (', 'h t m l (', 'a p p e n d (', 'a p p e n d T o (', 'p r e p e n d (', 'p r e p e n d T o (', 'a f t e r (', 'i n s e r t B e f o r e (', 'b e f o r e (', 'r e p l a c e W i t h (');
$evils = array('appendchild(', 'createElement(', 'xss.re', 'onfocus', 'onclick', 'innerHTML', 'replaceChild(', 'html(', 'append(', 'appendTo(', 'prepend(', 'prependTo(', 'after(', 'insertBefore', 'before(', 'replaceWith(', 'alert(', 'confirm(');
$replaces = array('a p p e n d c h i l d (', 'c r e a t e E l e m e n t (', 'x s s . r e', 'o n f o c u s', 'o n c l i c k', 'i n n e r H T M L', 'r e p l a c e C h i l d (', 'h t m l (', 'a p p e n d (', 'a p p e n d T o (', 'p r e p e n d (', 'p r e p e n d T o (', 'a f t e r (', 'i n s e r t B e f o r e (', 'b e f o r e (', 'r e p l a c e W i t h (', 'a l e r t (', 'c o n f i r m (');
$var = str_ireplace($evils, $replaces, $var);
}
+1 -1
View File
@@ -73,7 +73,7 @@ class actionModel extends model
if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $objectType);
/* Call the message notification function. */
$this->loadModel('message')->send($objectType, $objectID, $actionType, $actionID, $actor);
$this->loadModel('message')->send(strtolower($objectType), $objectID, $actionType, $actionID, $actor);
/* Add index for global search. */
$this->saveIndex($objectType, $objectID, $actionType);
+1
View File
@@ -418,6 +418,7 @@ $lang->bug->featureBar['browse']['unclosed'] = $lang->bug->unclosed;
$lang->bug->featureBar['browse']['openedbyme'] = $lang->bug->openedByMe;
$lang->bug->featureBar['browse']['assigntome'] = $lang->bug->assignToMe;
$lang->bug->featureBar['browse']['resolvedbyme'] = $lang->bug->resolvedByMe;
$lang->bug->featureBar['browse']['assignedbyme'] = $lang->bug->assignedByMe;
$lang->bug->featureBar['browse']['unresolved'] = $lang->bug->unResolved;
$lang->bug->featureBar['browse']['more'] = $lang->more;
+1
View File
@@ -418,6 +418,7 @@ $lang->bug->featureBar['browse']['unclosed'] = $lang->bug->unclosed;
$lang->bug->featureBar['browse']['openedbyme'] = $lang->bug->openedByMe;
$lang->bug->featureBar['browse']['assigntome'] = $lang->bug->assignToMe;
$lang->bug->featureBar['browse']['resolvedbyme'] = $lang->bug->resolvedByMe;
$lang->bug->featureBar['browse']['assignedbyme'] = $lang->bug->assignedByMe;
$lang->bug->featureBar['browse']['toclosed'] = $lang->bug->toClosed;
$lang->bug->featureBar['browse']['unresolved'] = $lang->bug->unResolved;
$lang->bug->featureBar['browse']['more'] = $lang->more;
+31
View File
@@ -396,6 +396,7 @@ class bugModel extends model
elseif($browseType == 'needconfirm') $bugs = $this->getByNeedconfirm($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
elseif($browseType == 'bysearch') $bugs = $this->getBySearch($productIDList, $branch, $queryID, $sort, '', $pager, $projectID);
elseif($browseType == 'overduebugs') $bugs = $this->getOverdueBugs($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
elseif($browseType == 'assignedbyme') $bugs = $this->getByAssignedbyme($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID);
return $this->checkDelayedBugs($bugs);
}
@@ -2646,6 +2647,36 @@ class bugModel extends model
->fetchAll();
}
/**
* Get by assigned by me.
* @param array $productIDList
* @param int|string $branch
* @param array $modules
* @param array $executions
* @param string $sort
* @param object $pager
* @param int $projectID
*
* @access public
* @return array
*/
public function getByAssignedbyme($productIDList, $branch, $modules, $executions, $sort, $pager, $projectID)
{
$actionIDList = $this->dao->select('objectID')->from(TABLE_ACTION)->where('objectType')->eq('bug')->andWhere('action')->eq('assigned')->andWhere('actor')->eq($this->app->user->account)->fetchPairs('objectID', 'objectID');
return $this->dao->select('*')->from(TABLE_BUG)
->where('product')->in($productIDList)
->beginIF($branch !== 'all')->andWhere('branch')->in($branch)->fi()
->beginIF($modules)->andWhere('module')->in($modules)->fi()
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
->andWhere('execution')->in(array_keys($executions))
->andWhere('deleted')->eq(0)
->andWhere('status')->ne('closed')
->andWhere('id')->in($actionIDList)
->orderBy($sort)
->page($pager)
->fetchAll();
}
/**
* Get by Sonarqube id.
*
+22 -2
View File
@@ -71,7 +71,18 @@ class custom extends control
if(($module == 'story' or $module == 'testcase') and $field == 'review')
{
$this->app->loadConfig($module);
$this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted');
$this->loadModel('user');
if($module == 'story')
{
$this->view->depts = $this->loadModel('dept')->getDeptPairs();
$this->view->forceReviewAll = zget($this->config->$module, 'forceReviewAll', 'false');
$this->view->forceReview = zget($this->config->$module, 'forceReview', '');
$this->view->forceReviewRoles = zget($this->config->$module, 'forceReviewRoles', '');
$this->view->forceReviewDepts = zget($this->config->$module, 'forceReviewDepts', '');
}
$this->view->users = $module == 'story' ? $this->user->getCanCreateStoryUsers() : $this->user->getPairs('noclosed|nodeleted');
$this->view->needReview = zget($this->config->$module, 'needReview', 1);
$this->view->forceReview = zget($this->config->$module, 'forceReview', '');
$this->view->forceNotReview = zget($this->config->$module, 'forceNotReview', '');
@@ -112,7 +123,16 @@ class custom extends control
}
elseif($module == 'story' and $field == 'review')
{
$data = fixer::input('post')->join('forceReview', ',')->get();
$data = fixer::input('post')
->setDefault('forceReviewAll', 0)
->setDefault('forceReviewDepts', '')
->join('forceReview', ',')
->join('forceReviewRoles', ',')
->join('forceReviewDepts', ',')
->join('forceReviewAll', ',')
->setIF(isset($this->post->forceReviewAll), 'forceReviewAll', 1)
->get();
$this->loadModel('setting')->setItems("system.$module@{$this->config->vision}", $data);
}
elseif($module == 'story' and $field == 'reviewRules')
+6
View File
@@ -50,6 +50,10 @@ $lang->custom->oneUnit = "One {$lang->hourCommon}";
$lang->custom->convertRelationTitle = "Please firstly set the conversion factor from {$lang->hourCommon} to %s";
$lang->custom->superReviewers = "Super Reviewer";
$lang->custom->kanban = "Kanban";
$lang->custom->allUsers = 'All Users';
$lang->custom->account = 'Users';
$lang->custom->role = 'Role';
$lang->custom->dept = 'Dept';
if($config->systemMode == 'new') $lang->custom->execution = 'Execution';
if($config->systemMode == 'classic' || !$config->systemMode) $lang->custom->execution = $lang->executionCommon;
@@ -181,6 +185,8 @@ $lang->custom->notice->readOnlyOfKanban = "If Change Forbidden, any change on
$lang->custom->notice->URSREmpty = 'Custom requirement name can not be empty!';
$lang->custom->notice->confirmDelete = 'Are you sure you want to delete it?';
$lang->custom->notice->confirmReviewCase = 'Set the case in Wait to Normal?';
$lang->custom->notice->storyReviewTip = 'After selecting by individual, position, and department, take the union of these three filters. ';
$lang->custom->notice->selectAllTip = 'After selecting all people, the reviewers will be emptied and grayed out while hiding their positions and departments.';
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ has Product Home. Do you want to go to Product Home?";
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ has Project Home. Do you want to go to Project Home?";
+6
View File
@@ -50,6 +50,10 @@ $lang->custom->oneUnit = "一个{$lang->hourCommon}";
$lang->custom->convertRelationTitle = "请先设置{$lang->hourCommon}转换为%s的换算系数";
$lang->custom->superReviewers = "超级评审人";
$lang->custom->kanban = "看板";
$lang->custom->allUsers = '所有人员';
$lang->custom->account = '人员';
$lang->custom->role = '职位';
$lang->custom->dept = '部门';
if($config->systemMode == 'new') $lang->custom->execution = '执行';
if($config->systemMode == 'classic' || !$config->systemMode) $lang->custom->execution = $lang->executionCommon;
@@ -181,6 +185,8 @@ $lang->custom->notice->readOnlyOfKanban = "禁止修改后,已关闭{$lang-
$lang->custom->notice->URSREmpty = '自定义需求名称不能为空!';
$lang->custom->notice->confirmDelete = '您确定要删除吗?';
$lang->custom->notice->confirmReviewCase = '是否将待评审的用例修改为正常状态?';
$lang->custom->notice->storyReviewTip = '按人员、职位、部门勾选后,取所有人员的并集。';
$lang->custom->notice->selectAllTip = '勾选所有人员后,会清空并置灰评审人员,同时隐藏职位、部门。';
$lang->custom->notice->indexPage['product'] = "从8.2版本起增加了产品主页视图,是否默认进入产品主页?";
$lang->custom->notice->indexPage['project'] = "从8.2版本起增加了项目主页视图,是否默认进入项目主页?";
+74 -5
View File
@@ -36,6 +36,18 @@ EOT;
<style>
.checkbox-primary {width: 170px; margin: 0 10px 10px 0; display: inline-block;}
</style>
<?php if($module == 'story' and $field == 'review'):?>
<style>
.reviewBox > th {width: 95px !important;}
.reviewBox > td {width: 500px !important;}
.checkbox-primary {margin-bottom: 0px; width: 82px !important;}
.storyReviewTip {padding-left: 95px;}
<?php if($app->getClientLang() != 'zh-cn' and $app->getClientLang() != 'zh-tw'):?>
.reviewBox > th {width: 160px !important;}
.storyReviewTip {padding-left: 160px;}
<?php endif;?>
</style>
<?php endif;?>
<div id='mainContent' class='main-row'>
<div class='side-col' id='sidebar'>
<div class='cell'>
@@ -88,18 +100,39 @@ EOT;
</tr>
</table>
<?php elseif(($module == 'story' or $module == 'testcase') and $field == 'review'):?>
<table class='table table-form mw-800px'>
<tr>
<table class='table table-form'>
<tr class='reviewBox'>
<th class='thWidth'><?php echo $lang->custom->storyReview;?></th>
<td><?php echo html::radio('needReview', $lang->custom->reviewList, $needReview);?></td>
<td></td>
</tr>
<tr <?php if($needReview and $module == 'testcase') echo "class='hidden'"?>>
<?php if($module == 'story'):?>
<tr>
<?php $space = ($app->getClientLang() != 'zh-cn' and $app->getClientLang() != 'zh-tw') ? ' ': '';?>
<td colspan='3'><div class='storyReviewTip'><?php echo sprintf($lang->custom->notice->forceReview, $lang->$module->common) . $lang->custom->notice->storyReviewTip;?></td>
</tr>
<tr id='userBox'>
<th><?php echo $lang->custom->forceReview . $space . $lang->custom->account;?></th>
<td><?php echo html::select('forceReview[]', $users, $forceReview, "class='form-control chosen' multiple");?></td>
<td>
<?php echo html::checkbox('forceReviewAll', array('1' => $lang->custom->allUsers), $forceReviewAll);?>
<icon class='icon icon-help' data-toggle='popover' data-trigger='focus hover' data-placement='right' data-tip-class='text-muted popover-sm' data-content="<?php echo $lang->custom->notice->selectAllTip;?>"></icon>
</td>
</tr>
<tr id='roleBox'>
<th><?php echo $lang->custom->forceReview . $space . $lang->custom->role;?></th>
<td><?php echo html::select('forceReviewRoles[]', $lang->user->roleList, $forceReviewRoles, "class='form-control chosen' multiple");?></td>
</tr>
<tr id='deptBox'>
<th><?php echo $lang->custom->forceReview . $space . $lang->custom->dept;?></th>
<td><?php echo html::select('forceReviewDepts[]', $depts, $forceReviewDepts, "class='form-control chosen' multiple");?></td>
</tr>
<?php endif;?>
<?php if($module == 'testcase'):?>
<tr <?php if($needReview) echo "class='hidden'"?>>
<th><?php echo $lang->custom->forceReview;?></th>
<td><?php echo html::select('forceReview[]', $users, $forceReview, "class='form-control chosen' multiple");?></td>
<td style='width:300px'><?php printf($lang->custom->notice->forceReview, $lang->$module->common);?></td>
</tr>
<?php if($module == 'testcase'):?>
<tr <?php if(!$needReview) echo "class='hidden'"?>>
<th><?php echo $lang->custom->forceNotReview;?></th>
<td><?php echo html::select('forceNotReview[]', $users, $forceNotReview, "class='form-control chosen' multiple");?></td>
@@ -231,6 +264,42 @@ EOT;
</form>
</div>
</div>
<?php if($module == 'story' and $field == 'review'):?>
<script>
$(function()
{
$('[data-toggle="popover"]').popover();
toggleBox($("input[name^='forceReviewAll']").prop('checked'));
$("input[name^='forceReviewAll']").click(function()
{
toggleBox($(this).prop('checked'));
});
/**
* Toggle box.
*
* @param bool $checked
* @access public
* @return void
*/
function toggleBox(checked)
{
$('#roleBox').toggleClass('hidden', checked);
$('#deptBox').toggleClass('hidden', checked);
if(checked)
{
$('#forceReview').val('').attr('disabled', 'disabled').trigger('chosen:updated');
}
else
{
$('#forceReview').removeAttr('disabled', 'disabled').trigger('chosen:updated');
}
}
})
</script>
<?php endif;?>
<?php if($module == 'testcase' and $field == 'review'):?>
<script>
$(function()
+1
View File
@@ -3,4 +3,5 @@ $config->datatable->moduleAlias['product-browse'] = 'story';
$config->datatable->moduleAlias['execution-task'] = 'task';
$config->datatable->moduleAlias['testtask-cases'] = 'testcase';
$config->datatable->moduleAlias['program-project'] = 'project';
$config->datatable->moduleAlias['project-bug'] = 'bug';
$config->datatable->moduleAlias['execution-bug'] = 'bug';
+1 -1
View File
@@ -348,7 +348,7 @@ if(!window.kanbanDropRules)
'wait': ['wait', 'developing', 'developed', 'canceled'],
'developing': ['developing', 'developed', 'pause', 'canceled'],
'developed': ['developed', 'developing', 'closed'],
'pause': ['pause', 'developing', 'developed', 'canceled'],
'pause': ['pause', 'developing', 'canceled'],
'canceled': ['canceled', 'developing', 'closed'],
'closed': ['closed', 'developing'],
}
+2
View File
@@ -260,6 +260,7 @@ $lang->execution->RDKanban = 'Research & Development Kanban';
$lang->execution->allTasks = 'All';
$lang->execution->assignedToMe = 'My';
$lang->execution->myInvolved = 'Involved';
$lang->execution->assignedByMe = 'AssignedByMe';
$lang->execution->statusSelects[''] = 'More';
$lang->execution->statusSelects['wait'] = 'Waiting';
@@ -443,6 +444,7 @@ $lang->execution->featureBar['task']['all'] = $lang->execution->allTask
$lang->execution->featureBar['task']['unclosed'] = $lang->execution->unclosed;
$lang->execution->featureBar['task']['assignedtome'] = $lang->execution->assignedToMe;
$lang->execution->featureBar['task']['myinvolved'] = $lang->execution->myInvolved;
$lang->execution->featureBar['task']['assignedbyme'] = $lang->execution->assignedByMe;
$lang->execution->featureBar['task']['delayed'] = 'Delayed';
$lang->execution->featureBar['task']['needconfirm'] = 'Changed';
$lang->execution->featureBar['task']['status'] = $lang->execution->statusSelects[''];
+2
View File
@@ -260,6 +260,7 @@ $lang->execution->RDKanban = '研发看板';
$lang->execution->allTasks = '所有';
$lang->execution->assignedToMe = '指派给我';
$lang->execution->myInvolved = '由我参与';
$lang->execution->assignedByMe = '由我指派';
$lang->execution->statusSelects[''] = '更多';
$lang->execution->statusSelects['wait'] = '未开始';
@@ -443,6 +444,7 @@ $lang->execution->featureBar['task']['all'] = $lang->execution->allTask
$lang->execution->featureBar['task']['unclosed'] = $lang->execution->unclosed;
$lang->execution->featureBar['task']['assignedtome'] = $lang->execution->assignedToMe;
$lang->execution->featureBar['task']['myinvolved'] = $lang->execution->myInvolved;
$lang->execution->featureBar['task']['assignedbyme'] = $lang->execution->assignedByMe;
$lang->execution->featureBar['task']['delayed'] = '已延期';
$lang->execution->featureBar['task']['needconfirm'] = "{$lang->SRCommon}变更";
$lang->execution->featureBar['task']['status'] = $lang->execution->statusSelects[''];
+1 -1
View File
@@ -25,7 +25,7 @@
</div>
<form class='load-indicator main-form form-ajax' method='post' target='hiddenwin' id='dataform'>
<table class='table table-form'>
<?php if($config->systemMode == 'new'):?>
<?php if($config->systemMode == 'new' and isset($project) and $project->model == 'scrum'):?>
<tr>
<th class='w-120px'><?php echo $lang->execution->projectName;?></th>
<td><?php echo html::select('project', $allProjects, $execution->project, "class='form-control chosen' onchange='changeProject(this.value)' required");?></td><td></td>
-2
View File
@@ -136,5 +136,3 @@
@-moz-document url-prefix(){ .region .kanban-lane-items{scrollbar-width:thin; padding:5px 0px 5px 5px !important;} .region .kanban-card .info .user{right: -6px !important;}}
#archivedCards .table-empty-tip {border-style: hidden;}
#archivedColumns .table-empty-tip {border-style: hidden;}
.dragging>.kanban-card{background-color:#fff !important;opacity:.2}
+1 -1
View File
@@ -2944,7 +2944,7 @@ class kanbanModel extends model
$actions .= "<div class='btn-group'>";
$actions .= "<a href='javascript:fullScreen();' id='fullScreenBtn' $btnColor class='btn btn-link'><i class='icon icon-fullscreen'></i> {$this->lang->kanban->fullScreen}</a>";
$CRKanban = !(isset($this->config->CRKanban) and $this->config->CRKanban === '0' and $kanban->status === 'closed');
$CRKanban = !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed');
$printRegionBtn = ($CRKanban and (common::hasPriv('kanban', 'createRegion') or $printSetHeightBtn or common::hasPriv('kanban', 'performable') or common::hasPriv('kanban', 'enableArchived') or common::hasPriv('kanban', 'import') or common::hasPriv('kanban', 'setColumnWidth')));
$printKanbanBtn = (common::hasPriv('kanban', 'edit') or common::hasPriv('kanban', 'close') or common::hasPriv('kanban', 'delete'));
+1 -1
View File
@@ -172,7 +172,7 @@ $app->loadLang('productplan');
</div>
<div class='col-xs-2 card-actions' <?php if($kanban->performable) echo "id='performable'";?>>
<?php
$CRKanban = ($this->config->CRKanban or $kanban->status != 'closed');
$CRKanban = !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed');
$canRestore = (commonModel::hasPriv('kanban', 'restoreCard') and $CRKanban);
$canDelete = (commonModel::hasPriv('kanban', 'deleteCard') and $CRKanban);
@@ -35,7 +35,7 @@
<div class="item-body">
<div class="item parent-item hr" data-archived='<?php echo $column->archived;?>' data-id="<?php echo $column->id;?>">
<div class="title text-ellipsis"><?php echo $column->name;?></div>
<?php if(commonModel::hasPriv('kanban', 'restoreColumn') && $column->archived == '1' && ($this->config->CRKanban or $kanban->status != 'closed')) echo html::a(inlink('restoreColumn', "columnID={$column->id}"), $lang->kanban->restore, '', "class='btn btn-primary' target='hiddenwin'");?>
<?php if(commonModel::hasPriv('kanban', 'restoreColumn') && $column->archived == '1' && !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed')) echo html::a(inlink('restoreColumn', "columnID={$column->id}"), $lang->kanban->restore, '', "class='btn btn-primary' target='hiddenwin'");?>
</div>
<?php if(!empty($column->child) && $column->archived == '0'):?>
<?php $count = 1;?>
@@ -43,7 +43,7 @@
<?php $class = $count == count($column->child) ? 'hr' : '';?>
<div class="item child-item <?php echo $class;?>" data-id="<?php echo $childColumn->id;?>" data-archived='<?php echo $childColumn->archived;?>'>
<div class="title text-ellipsis"><label class="label label-child"><?php echo $lang->kanban->child;?></label><?php echo $childColumn->name;?></div>
<?php if(commonModel::hasPriv('kanban', 'restoreColumn') && $childColumn->archived == '1' && ($this->config->CRKanban or $kanban->status != 'closed')) echo html::a(inlink('restoreColumn', "columnID={$childColumn->id}"), $lang->kanban->restore, '', "class='btn btn-primary' target='hiddenwin'");?>
<?php if(commonModel::hasPriv('kanban', 'restoreColumn') && $childColumn->archived == '1' && !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed')) echo html::a(inlink('restoreColumn', "columnID={$childColumn->id}"), $lang->kanban->restore, '', "class='btn btn-primary' target='hiddenwin'");?>
</div>
<?php $count ++;?>
<?php endforeach;?>
+1 -1
View File
@@ -34,7 +34,7 @@
</div>
<div class='main-actions'>
<div class="btn-toolbar">
<?php if(!$card->deleted and !$card->archived and !($this->config->CRKanban === '0' and $kanban->status === 'closed')):?>
<?php if(!$card->deleted and !$card->archived and !(isset($this->config->CRKanban) and $this->config->CRKanban == '0' and $kanban->status == 'closed')):?>
<?php
//common::printLink('kanban', 'assigntoCard', "cardID=$card->id", "<i class='icon icon-hand-right'></i><span class='text'>{$lang->kanbancard->assign}</span>", '', "class='btn btn-link iframe' title='{$lang->kanbancard->assign}'", true, true);
if($kanban->archived)
+3 -1
View File
@@ -146,9 +146,11 @@ class messageModel extends model
if($isonlybody) unset($_GET['onlybody']);
$moduleName = $objectType == 'case' ? 'testcase' : $objectType;
$moduleName = $objectType == 'kanbancard' ? 'kanban' : $objectType;
$space = common::checkNotCN() ? ' ' : '';
$data = $user->realname . $space . $this->lang->action->label->$actionType . $space . $this->lang->action->objectTypes[$objectType];
$data .= ' ' . html::a($sysURL . helper::createLink($moduleName, 'view', "id=$objectID"), "[#{$objectID}::{$object->$field}]");
$dataID = $objectType == 'kanbancard' ? $object->kanban : $objectID;
$data .= ' ' . html::a($sysURL . helper::createLink($moduleName, 'view', "id=$dataID"), "[#{$objectID}::{$object->$field}]");
if($isonlybody) $_GET['onlybody'] = 'yes';
+20 -2
View File
@@ -305,6 +305,7 @@ EOF;
*/
public function story($type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->loadModel('story');
/* Save session. */
if($this->app->viewType != 'json') $this->session->set('storyList', $this->app->getURI(true), 'my');
@@ -316,7 +317,15 @@ EOF;
/* Append id for secend sort. */
$sort = common::appendOrder($orderBy);
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'story', false);
if($type == 'assignedBy')
{
$stories = $this->loadModel('my')->getAssignedByMe($this->app->user->account, '', $pager, $orderBy, '', 'story');
}
else
{
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'story', false);
}
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
/* Assign. */
@@ -350,6 +359,7 @@ EOF;
public function requirement($type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* Save session. */
$this->loadModel('story');
if($this->app->viewType != 'json') $this->session->set('storyList', $this->app->getURI(true), 'my');
/* Load pager. */
@@ -360,7 +370,15 @@ EOF;
/* Append id for secend sort. */
$sort = common::appendOrder($orderBy);
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'requirement');
if($type == 'assignedBy')
{
$stories = $this->loadModel('my')->getAssignedByMe($this->app->user->account, '', $pager, $orderBy, '', 'requirement');
}
else
{
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'requirement');
}
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
/* Assign. */
+1
View File
@@ -74,6 +74,7 @@ $lang->my->storyMenu->reviewByMe = 'ReviewByMe';
$lang->my->storyMenu->openedByMe = 'CreatedByMe';
$lang->my->storyMenu->reviewedByMe = 'ReviewedByMe';
$lang->my->storyMenu->closedByMe = 'ClosedByMe';
$lang->my->storyMenu->assignedByMe = 'AssignedByMe';
$lang->my->projectMenu = new stdclass();
$lang->my->projectMenu->doing = 'Doing';
+1
View File
@@ -74,6 +74,7 @@ $lang->my->storyMenu->reviewByMe = '待我评审';
$lang->my->storyMenu->openedByMe = '由我创建';
$lang->my->storyMenu->reviewedByMe = '由我评审';
$lang->my->storyMenu->closedByMe = '由我关闭';
$lang->my->storyMenu->assignedByMe = '由我指派';
$lang->my->projectMenu = new stdclass();
$lang->my->projectMenu->doing = '进行中';
+17 -4
View File
@@ -324,12 +324,14 @@ class myModel extends model
*/
public function getAssignedByMe($account, $limit = 0, $pager = null, $orderBy = "id_desc", $projectID = 0, $objectType = '')
{
$this->loadModel($objectType);
$objectList = $this->dao->select('t1.*')
->from($this->config->objectTables[$objectType])->alias('t1')
->leftJoin(TABLE_ACTION)->alias('t2')->on("t1.id = t2.objectID and t2.objectType='{$objectType}'")
$module = $objectType == 'requirement' ? 'story' : $objectType;
$this->loadModel($module);
$objectList = $this->dao->select('DISTINCT t1.*')
->from($this->config->objectTables[$module])->alias('t1')
->leftJoin(TABLE_ACTION)->alias('t2')->on("t1.id = t2.objectID and t2.objectType='{$module}'")
->where('t2.actor')->eq($account)
->andWhere('t2.action')->eq('assigned')
->beginIF($objectType == 'requirement' or $objectType == 'story')->andWhere('t1.type')->eq($objectType)->fi()
->andWhere('t1.deleted')->eq(0)
->orderBy($orderBy)
->page($pager, 't1.id')
@@ -358,7 +360,18 @@ class myModel extends model
$productPairs = $this->dao->select('id,name')->from(TABLE_PRODUCT)->where('id')->in($productList)->fetchPairs('id');
foreach($objectList as $bug) $bug->productName = zget($productPairs, $bug->product);
}
if($objectType == 'requirement' or $objectType == 'story')
{
$productList = array();
foreach($objectList as $story) $productList[$story->product] = $story->product;
$productPairs = $this->dao->select('id,name')->from(TABLE_PRODUCT)->where('id')->in($productList)->fetchPairs('id');
foreach($objectList as $story) $story->productTitle = zget($productPairs, $story->product);
$planList = array();
foreach($objectList as $story) $planList[$story->plan] = $story->plan;
$planPairs = $this->dao->select('id,title')->from(TABLE_PRODUCTPLAN)->where('id')->in($planList)->fetchPairs('id');
foreach($objectList as $story) $story->planTitle = zget($planPairs, $story->plan);
}
return $objectList;
}
}
+4 -3
View File
@@ -20,9 +20,10 @@
$recTotalLabel = " <span class='label label-light label-badge'>{$pager->recTotal}</span>";
if($app->rawMethod == 'contribute')
{
echo html::a(inlink($app->rawMethod, "mode=requirement&type=openedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->openedByMe}</span>" . ($type == 'openedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'openedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=reviewedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewedByMe}</span>" . ($type == 'reviewedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=closedBy&&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=openedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->openedByMe}</span>" . ($type == 'openedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'openedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=reviewedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewedByMe}</span>" . ($type == 'reviewedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=closedBy&&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=requirement&type=assignedBy&&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->assignedByMe}</span>" . ($type == 'assignedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedBy' ? ' btn-active-text' : '') . "'");
}
else
{
+1
View File
@@ -23,6 +23,7 @@
echo html::a(inlink($app->rawMethod, "mode=story&type=openedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->openedByMe}</span>" . ($type == 'openedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'openedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=story&type=reviewedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->reviewedByMe}</span>" . ($type == 'reviewedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=story&type=closedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=story&type=assignedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "<span class='text'>{$lang->my->storyMenu->assignedByMe}</span>" . ($type == 'assignedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedBy' ? ' btn-active-text' : '') . "'");
}
else
{
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
$config->product = new stdclass();
$config->product->orderBy = 'isClosed,order_desc';
$config->product->orderBy = 'isClosed,program_asc,order_asc';
$config->product->customBatchEditFields = 'line,PO,QD,RD,status,type,acl';
if($config->systemMode == 'new') $config->product->customBatchEditFields = 'program,' . $config->product->customBatchEditFields;
+1 -1
View File
@@ -1197,7 +1197,7 @@ class product extends control
* @access public
* @return void
*/
public function all($browseType = 'noclosed', $orderBy = 'program_asc', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function all($browseType = 'noclosed', $orderBy = 'program_asc,order_asc', $param = 0, $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* Load module and set session. */
$this->loadModel('program');
+3 -1
View File
@@ -13,7 +13,9 @@ $(function()
/* Set movable conditions. */
canMoveHere: function($ele, $target)
{
return $ele.data('parent') === $target.data('parent');
var canMove = true;
if($ele.hasClass('no-nest')) canMove = $target.hasClass('no-nest') ? true : false;
return $ele.data('parent') === $target.data('parent') && canMove;
},
start: function(e)
{
+2
View File
@@ -143,6 +143,7 @@ $lang->product->closedStory = 'Closed';
$lang->product->unclosed = 'Open';
$lang->product->unplan = 'Unplanned';
$lang->product->viewByUser = 'By User';
$lang->product->assignedByMe = 'AssignedByMe';
/* Product Kanban. */
$lang->product->myProduct = 'Products Ownedbyme';
@@ -213,6 +214,7 @@ $lang->product->featureBar['all']['all'] = $lang->product->allProduct;
$lang->product->featureBar['all']['noclosed'] = $lang->product->unclosed;
$lang->product->featureBar['all']['closed'] = $lang->product->statusList['closed'];
$lang->product->moreSelects['assignedbyme'] = $lang->product->assignedByMe;
$lang->product->moreSelects['closedbyme'] = $lang->product->closedByMe;
$lang->product->moreSelects['activestory'] = $lang->product->activeStory;
$lang->product->moreSelects['changedstory'] = $lang->product->changedStory;
+2
View File
@@ -143,6 +143,7 @@ $lang->product->closedStory = '已关闭';
$lang->product->unclosed = '未关闭';
$lang->product->unplan = "未计划";
$lang->product->viewByUser = '按用户查看';
$lang->product->assignedByMe = '我指派';
/* Product Kanban. */
$lang->product->myProduct = '我负责的' . $lang->productCommon;
@@ -213,6 +214,7 @@ $lang->product->featureBar['all']['all'] = '所有' . $lang->productCommon;
$lang->product->featureBar['all']['noclosed'] = $lang->product->unclosed;
$lang->product->featureBar['all']['closed'] = $lang->product->statusList['closed'];
$lang->product->moreSelects['assignedbyme'] = $lang->product->assignedByMe;
$lang->product->moreSelects['closedbyme'] = $lang->product->closedByMe;
$lang->product->moreSelects['activestory'] = $lang->product->activeStory;
$lang->product->moreSelects['changedstory'] = $lang->product->changedStory;
+1
View File
@@ -989,6 +989,7 @@ class productModel extends model
if($browseType == 'changedstory') $stories = $this->story->getByStatus($productID, $branch, $modules, 'changed', $type, $sort, $pager);
if($browseType == 'willclose') $stories = $this->story->get2BeClosed($productID, $branch, $modules, $type, $sort, $pager);
if($browseType == 'closedstory') $stories = $this->story->getByStatus($productID, $branch, $modules, 'closed', $type, $sort, $pager);
if($browseType == 'assignedbyme') $stories = $this->story->getByAssignedBy($productID, $branch, $modules, $this->app->user->account, $type, $sort, $pager);
return $stories;
}
+1 -1
View File
@@ -38,7 +38,7 @@
<?php if($product->type != 'normal'):?>
<tr>
<th><?php echo $lang->product->branch;?></th>
<td><?php echo html::select('branch', $branches, $defaultBranch, "class='form-control chosen'");?></td><td></td><td></td>
<td><?php echo html::select('branch', $branches, $branch, "class='form-control chosen'");?></td><td></td><td></td>
</tr>
<?php endif;?>
<?php endif;?>
+1 -1
View File
@@ -80,7 +80,7 @@ $lang->program->exRateNotNegative = 'The『exchange rate』should not be n
$lang->program->changePRJUnit = 'Update the budget unit of the project';
$lang->program->progress = 'Progress';
$lang->program->children = 'Child';
$lang->program->children = 'Add Child';
$lang->program->allInvest = 'Input';
$lang->program->teamCount = 'Team';
$lang->program->longTime = 'Long-Term';
+1 -1
View File
@@ -80,7 +80,7 @@ $lang->program->exRateNotNegative = '『汇率』不能是负数。';
$lang->program->changePRJUnit = '更新项目预算单位';
$lang->program->progress = '项目进度';
$lang->program->children = '子项目集';
$lang->program->children = '添加子项目集';
$lang->program->allInvest = '项目集总投入';
$lang->program->teamCount = '总人数';
$lang->program->longTime = '长期';
+6 -2
View File
@@ -44,13 +44,17 @@
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->program->create;?></h2>
<h2><?php echo isset($parentProgram->id) ? $lang->program->children : $lang->program->create;?></h2>
</div>
<form class='form-indicator main-form form-ajax' method='post' target='hiddenwin' id='dataform'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->program->parent;?></th>
<td><?php echo html::select('parent', $parents, isset($parentProgram->id) ? $parentProgram->id : 0, "class='form-control chosen' onchange=setBudgetTipsAndAclList(this.value)");?>
<td>
<?php $disabled = isset($parentProgram->id) ? 'disabled' : '';?>
<?php echo html::select('parent', $parents, isset($parentProgram->id) ? $parentProgram->id : 0, "class='form-control chosen' onchange=setBudgetTipsAndAclList(this.value) $disabled");?>
<?php if($disabled) echo html::hidden('parent', $parentProgram->id);?>
</td>
<td></td>
</tr>
<tr>
+61 -16
View File
@@ -971,11 +971,13 @@ class project extends control
$this->loadModel('bug');
$this->loadModel('user');
$this->loadModel('product');
$this->loadModel('datatable');
/* Save session. */
$this->session->set('bugList', $this->app->getURI(true), 'project');
$this->project->setMenu($projectID);
$product = $this->product->getById($productID);
$project = $this->project->getByID($projectID);
$type = strtolower($type);
$queryID = ($type == 'bysearch') ? (int)$param : 0;
@@ -983,7 +985,7 @@ class project extends control
$branchID = isset($products[$productID]) ? current($products[$productID]->branches) : 0;
$productPairs = array('0' => $this->lang->product->all);
foreach($products as $product) $productPairs[$product->id] = $product->name;
foreach($products as $productData) $productPairs[$productData->id] = $productData->name;
$this->lang->modulePageNav = $this->product->select($productPairs, $productID, 'project', 'bug', '', $branchID, 0, '', false);
/* Header and position. */
@@ -991,6 +993,8 @@ class project extends control
$position[] = html::a($this->createLink('project', 'browse', "projectID=$projectID"), $project->name);
$position[] = $this->lang->bug->common;
$executions = $this->loadModel('execution')->getPairs($projectID, 'all', 'empty|withdelete');
/* Load pager and get bugs, user. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
@@ -1008,22 +1012,63 @@ class project extends control
$actionURL = $this->createLink('project', 'bug', "projectID=$projectID&productID=$productID&orderBy=$orderBy&build=$build&type=bysearch&queryID=myQueryID");
$this->loadModel('execution')->buildBugSearchForm($products, $queryID, $actionURL, 'project');
$showBranch = false;
$branchOption = array();
$branchTagOption = array();
if($product and $product->type != 'normal')
{
/* Display of branch label. */
$showBranch = $this->loadModel('branch')->showBranch($productID);
/* Display status of branch. */
$branches = $this->loadModel('branch')->getList($productID, 0, 'all');
foreach($branches as $branchInfo)
{
$branchOption[$branchInfo->id] = $branchInfo->name;
$branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
}
}
$showModule = !empty($this->config->datatable->bugBrowse->showModule) ? $this->config->datatable->bugBrowse->showModule : '';
$storyIdList = $taskIdList = array();
foreach($bugs as $bug)
{
if($bug->story) $storyIdList[$bug->story] = $bug->story;
if($bug->task) $taskIdList[$bug->task] = $bug->task;
if($bug->toTask) $taskIdList[$bug->toTask] = $bug->toTask;
}
$storyList = $storyIdList ? $this->loadModel('story')->getByList($storyIdList) : array();
$taskList = $taskIdList ? $this->loadModel('task')->getByList($taskIdList) : array();
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->bugs = $bugs;
$this->view->tabID = 'bug';
$this->view->build = $this->loadModel('build')->getById($build);
$this->view->buildID = $this->view->build ? $this->view->build->id : 0;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $users;
$this->view->productID = $productID;
$this->view->project = $this->project->getById($projectID);
$this->view->branchID = empty($this->view->build->branch) ? $branchID : $this->view->build->branch;
$this->view->memberPairs = $memberPairs;
$this->view->type = $type;
$this->view->param = $param;
$this->view->title = $title;
$this->view->position = $position;
$this->view->bugs = $bugs;
$this->view->tabID = 'bug';
$this->view->build = $this->loadModel('build')->getById($build);
$this->view->buildID = $this->view->build ? $this->view->build->id : 0;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $users;
$this->view->productID = $productID;
$this->view->project = $this->project->getById($projectID);
$this->view->branchID = empty($this->view->build->branch) ? $branchID : $this->view->build->branch;
$this->view->memberPairs = $memberPairs;
$this->view->type = $type;
$this->view->param = $param;
$this->view->builds = $this->loadModel('build')->getBuildPairs($productID);
$this->view->users = $this->user->getPairs('noletter');
$this->view->memberPairs = $this->user->getPairs('noletter|noclosed');
$this->view->branchOption = $branchOption;
$this->view->branchTagOption = $branchTagOption;
$this->view->executions = $executions;
$this->view->modulePairs = $showModule ? $this->loadModel('tree')->getModulePairs($productID, 'bug', $showModule) : array();
$this->view->plans = $this->loadModel('productplan')->getPairs($productID);
$this->view->stories = $storyList;
$this->view->tasks = $taskList;
$this->view->projectPairs = $this->project->getPairsByProgram();
$this->display();
}
+40 -66
View File
@@ -11,6 +11,7 @@
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/datatable.fix.html.php';?>
<style>
#subHeader #dropMenu .col-left .list-group {margin-bottom: 0px; padding-top: 10px;}
#subHeader #dropMenu .col-left {padding-bottom: 0px;}
@@ -41,86 +42,54 @@
</p>
</div>
<?php else:?>
<form class='main-table' method='post' id='projectBugForm' data-ride="table">
<table class='table has-sort-head' id='bugList'>
<?php $canBatchAssignTo = common::hasPriv('bug', 'batchAssignTo');?>
<?php $vars = "projectID={$project->id}&productID=$productID&orderBy=%s&build=$buildID&type=$type&param=$param&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?>
<?php
$datatableId = $this->moduleName . ucfirst($this->methodName);
$useDatatable = (isset($config->datatable->$datatableId->mode) and $config->datatable->$datatableId->mode == 'datatable');
?>
<form class='main-table' method='post' id='projectBugForm' <?php if(!$useDatatable) echo "data-ride='table'";?>>
<div class="table-header fixed-right">
<nav class="btn-toolbar pull-right setting"></nav>
</div>
<?php
$vars = "projectID={$project->id}&productID=$productID&orderBy=%s&build=$buildID&type=$type&param=$param&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}";
if($useDatatable) include '../../common/view/datatable.html.php';
else include '../../common/view/tablesorter.html.php';
$setting = $this->datatable->getSetting('project');
$widths = $this->datatable->setFixedFieldWidth($setting);
$columns = 0;
$canBatchAssignTo = common::hasPriv('bug', 'batchAssignTo');
?>
<?php if(!$useDatatable) echo '<div class="table-responsive">';?>
<table class='table has-sort-head<?php if($useDatatable) echo ' datatable';?>' id='bugList' data-fixed-left-width='<?php echo $widths['leftWidth']?>' data-fixed-right-width='<?php echo $widths['rightWidth']?>'>
<thead>
<tr>
<th class='c-id'>
<?php if($canBatchAssignTo):?>
<div class="checkbox-primary check-all" title="<?php echo $lang->selectAll?>">
<label></label>
</div>
<?php endif;?>
<?php common::printOrderLink('id', $orderBy, $vars, $lang->idAB);?>
</th>
<th class='c-severity' title=<?php echo $lang->bug->severity;?>><?php common::printOrderLink('severity', $orderBy, $vars, $lang->bug->severityAB);?></th>
<th class='c-pri' title=<?php echo $lang->project->pri;?>><?php common::printOrderLink('pri', $orderBy, $vars, $lang->priAB);?></th>
<th><?php common::printOrderLink('title', $orderBy, $vars, $lang->bug->title);?></th>
<th class='c-user'><?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
<th class='c-user'><?php common::printOrderLink('assignedTo', $orderBy, $vars, $lang->assignedToAB);?></th>
<th class='c-user'><?php common::printOrderLink('resolvedBy', $orderBy, $vars, $lang->bug->resolvedBy);?></th>
<th class='c-resolution'><?php common::printOrderLink('resolution', $orderBy, $vars, $lang->bug->resolutionAB);?></th>
<th class='c-actions-5'><?php echo $lang->actions;?></th>
<?php
foreach($setting as $key => $value)
{
if($value->show)
{
$this->datatable->printHead($value, $orderBy, $vars, $canBatchAssignTo);
$columns ++;
}
}
?>
</tr>
</thead>
<?php
$hasCustomSeverity = false;
foreach($lang->bug->severityList as $severityKey => $severityValue)
{
if(!empty($severityKey) and (string)$severityKey != (string)$severityValue)
{
$hasCustomSeverity = true;
break;
}
}
?>
<tbody>
<?php foreach($bugs as $bug):?>
<?php
$canBeChanged = common::canBeChanged('bug', $bug);
$arrtibute = $canBeChanged ? '' : 'disabled';
$viewLink = helper::createLink('bug', 'view', "bugID=$bug->id");
?>
<tr>
<td class='cell-id'>
<?php if($canBatchAssignTo):?>
<?php echo html::checkbox('bugIDList', array($bug->id => ''), '', $arrtibute) . html::a($viewLink, sprintf('%03d', $bug->id), '', "data-app={$this->app->tab}");?>
<?php else:?>
<?php printf('%03d', $bug->id);?>
<?php endif;?>
</td>
<td>
<?php if($hasCustomSeverity):?>
<span class='<?php echo 'label-severity-custom';?>' title='<?php echo zget($lang->bug->severityList, $bug->severity);?>' data-severity='<?php echo $bug->severity;?>'><?php echo zget($lang->bug->severityList, $bug->severity, $bug->severity);?></span>
<?php else:?>
<span class='<?php echo 'label-severity';?>' title='<?php echo zget($lang->bug->severityList, $bug->severity);?>' data-severity='<?php echo $bug->severity;?>'></span>
<?php endif;?>
</td>
<td><span class='label-pri <?php echo 'label-pri-' . $bug->pri?>' title='<?php echo zget($lang->bug->priList, $bug->pri, $bug->pri)?>'><?php echo zget($lang->bug->priList, $bug->pri, $bug->pri)?></span></td>
<td class='text-left c-name' title="<?php echo $bug->title?>"><?php echo html::a($viewLink, $bug->title, null, "style='color: $bug->color' data-app={$this->app->tab}");?></td>
<td><?php echo zget($users, $bug->openedBy, $bug->openedBy);?></td>
<td class='c-assignedTo has-btn text-left'><?php $this->bug->printAssignedHtml($bug, $users);?></td>
<td><?php echo zget($users, $bug->resolvedBy, $bug->resolvedBy);?></td>
<td><?php echo zget($lang->bug->resolutionList, $bug->resolution);?></td>
<td class='c-actions'>
<?php
if($canBeChanged)
{
$params = "bugID=$bug->id";
common::printIcon('bug', 'confirmBug', $params, $bug, 'list', 'ok', '', 'iframe', true);
common::printIcon('bug', 'resolve', $params, $bug, 'list', 'checked', '', 'iframe', true);
common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true);
common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=$params,projectID={$bug->project}", $bug, 'list', 'copy');
common::printIcon('bug', 'edit', $params, $bug, 'list');
}
?>
</td>
<tr data-id='<?php echo $bug->id?>'>
<?php foreach($setting as $value) $this->bug->printCell($value, $bug, $users, $builds, $branchOption, $modulePairs, $executions, $plans, $stories, $tasks, $useDatatable ? 'datatable' : 'table', $projectPairs);?>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php if(!$useDatatable) echo '</div>';?>
<div class='table-footer'>
<?php if($canBatchAssignTo):?>
<div class="checkbox-primary check-all"><label><?php echo $lang->selectAll?></label></div>
@@ -166,4 +135,9 @@
</div>
<?php js::set('replaceID', 'bugList');?>
<?php js::set('browseType', $type);?>
<script>
<?php if(!empty($useDatatable)):?>
$(function(){$('#projectBugForm').table();})
<?php endif;?>
</script>
<?php include '../../common/view/footer.html.php';?>
+68 -11
View File
@@ -2437,6 +2437,7 @@ class storyModel extends model
* Get stories by assignedTo.
*
* @param int $productID
* @param string $branch
* @param string $account
* @param string $type requirement|story
* @param string $orderBy
@@ -2453,6 +2454,8 @@ class storyModel extends model
* Get stories by openedBy.
*
* @param int $productID
* @param int $branch
* @param string $modules
* @param string $account
* @param string $type requirement|story
* @param string $orderBy
@@ -2469,6 +2472,8 @@ class storyModel extends model
* Get stories by reviewedBy.
*
* @param int $productID
* @param int $branch
* @param string $modules
* @param string $account
* @param string $type requirement|story
* @param string $orderBy
@@ -2503,6 +2508,8 @@ class storyModel extends model
* Get stories by closedBy.
*
* @param int $productID
* @param int $branch
* @param string $modules
* @param string $account
* @param string $type requirement|story
* @param string $orderBy
@@ -2518,6 +2525,8 @@ class storyModel extends model
* Get stories by status.
*
* @param int $productID
* @param int $branch
* @param string $modules
* @param string $status
* @param string $type requirement|story
* @param string $orderBy
@@ -2533,14 +2542,15 @@ class storyModel extends model
/**
* Get stories by plan.
*
* @param int $productID
* @param int $branch
* @param array $modules
* @param int $plan
* @param string $type requirement|story
* @param string $orderBy
* @param object $pager
* @param int $productID
* @param int $branch
* @param array $modules
* @param int $plan
* @param string $type requirement|story
* @param string $orderBy
* @param object $pager
*
* @access public
* @return array
*/
public function getByPlan($productID, $branch, $modules, $plan, $type = 'story', $orderBy = '', $pager = null)
@@ -2548,6 +2558,24 @@ class storyModel extends model
return $this->getByField($productID, $branch, $modules, 'plan', $plan, $type, $orderBy, $pager);
}
/**
* Get stories by assignedBy.
*
* @param int $productID
* @param int $branch
* @param string $modules
* @param string $account
* @param string $type requirement|story
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getByAssignedBy($productID, $branch, $modules, $account, $type = 'story', $orderBy = '', $pager = null)
{
return $this->getByField($productID, $branch, $modules, 'assignedBy', $account, $type, $orderBy, $pager);
}
/**
* Get stories by a field.
*
@@ -2565,7 +2593,10 @@ class storyModel extends model
*/
public function getByField($productID, $branch, $modules, $fieldName, $fieldValue, $type = 'story', $orderBy = '', $pager = null, $operator = 'equal')
{
if(!$this->loadModel('common')->checkField(TABLE_STORY, $fieldName) and $fieldName != 'reviewBy') return array();
if(!$this->loadModel('common')->checkField(TABLE_STORY, $fieldName) and $fieldName != 'reviewBy' and $fieldName != 'assignedBy') return array();
$actionIDList = array();
if($fieldName == 'assignedBy') $actionIDList = $this->dao->select('objectID')->from(TABLE_ACTION)->where('objectType')->eq('story')->andWhere('action')->eq('assigned')->andWhere('actor')->eq($fieldValue)->fetchPairs('objectID', 'objectID');
$sql = $this->dao->select('t1.*')->from(TABLE_STORY)->alias('t1');
if($fieldName == 'reviewBy') $sql = $sql->leftJoin(TABLE_STORYREVIEW)->alias('t2')->on('t1.id = t2.story and t1.version = t2.version');
@@ -2575,13 +2606,14 @@ class storyModel extends model
->andWhere('t1.type')->eq($type)
->beginIF($branch != 'all')->andWhere("t1.branch")->eq($branch)->fi()
->beginIF($modules)->andWhere("t1.module")->in($modules)->fi()
->beginIF($operator == 'equal' and $fieldName != 'reviewBy')->andWhere('t1.' . $fieldName)->eq($fieldValue)->fi()
->beginIF($operator == 'include' and $fieldName != 'reviewBy')->andWhere('t1.' . $fieldName)->like("%$fieldValue%")->fi()
->beginIF($operator == 'equal' and $fieldName != 'reviewBy' and $fieldName != 'assignedBy')->andWhere('t1.' . $fieldName)->eq($fieldValue)->fi()
->beginIF($operator == 'include' and $fieldName != 'reviewBy' and $fieldName != 'assignedBy')->andWhere('t1.' . $fieldName)->like("%$fieldValue%")->fi()
->beginIF($fieldName == 'reviewBy')
->andWhere('t2.reviewer')->eq($this->app->user->account)
->andWhere('t2.result')->eq('')
->andWhere('t1.status')->in('draft,changed')
->fi()
->beginIF($fieldName == 'assignedBy')->andWhere('t1.id')->in($actionIDList)->andWhere('t1.status')->ne('closed')->fi()
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
@@ -2592,6 +2624,8 @@ class storyModel extends model
* Get to be closed stories.
*
* @param int $productID
* @param int $branch
* @param string $modules
* @param string $type requirement|story
* @param string $orderBy
* @param object $pager
@@ -4319,7 +4353,30 @@ class storyModel extends model
public function checkForceReview()
{
$forceReview = false;
if(!empty($this->config->story->forceReview)) $forceReview = strpos(",{$this->config->story->forceReview},", ",{$this->app->user->account},") !== false;
if(isset($this->config->story->forceReviewAll) and $this->config->story->forceReviewAll) return true;
$forceUsers = '';
if(!empty($this->config->story->forceReview)) $forceUsers = $this->config->story->forceReview;
if(!empty($this->config->story->forceReviewRoles) or !empty($this->config->story->forceReviewDepts))
{
$users = $this->dao->select('account')->from(TABLE_USER)
->where('deleted')->eq(0)
->andWhere(true, true)
->beginIF(!empty($this->config->story->forceReviewRoles))
->andWhere('role', true)->in($this->config->story->forceReviewRoles)
->andWhere('role')->ne('')
->markRight(1)
->fi()
->beginIF(!empty($this->config->story->forceReviewDepts))->orWhere('dept')->in($this->config->story->forceReviewDepts)->fi()
->markRight(1)
->fetchAll('account');
$forceUsers .= "," . implode(',', array_keys($users));
}
$forceReview = strpos(",{$forceUsers},", ",{$this->app->user->account},") !== false;
return $forceReview;
}
+9 -1
View File
@@ -8,7 +8,15 @@ $(function()
{
if($(this).val() !== '') left = $(this).val();
});
if(left === '0') return confirm(confirmRecord);
if(left == '0')
{
var confirmMsg = confirm(confirmRecord);
if(confirmMsg == false)
{
$('#submit').attr("disabled", false);
return false;
}
}
});
$('#recordForm .showinonlybody').each(function()
+6 -1
View File
@@ -2186,6 +2186,10 @@ class taskModel extends model
if(is_string($type)) $type = strtolower($type);
$fields = 'DISTINCT t1.*, t2.id AS storyID, t2.title AS storyTitle, t2.product, t2.branch, t2.version AS latestStoryVersion, t2.status AS storyStatus, t3.realname AS assignedToRealName';
$this->config->edition == 'max' && $fields .= ', t6.name as designName, t6.version as latestDesignVersion';
$actionIDList = array();
if($type == 'assignedbyme') $actionIDList = $this->dao->select('objectID')->from(TABLE_ACTION)->where('objectType')->eq('task')->andWhere('action')->eq('assigned')->andWhere('actor')->eq($this->app->user->account)->fetchPairs('objectID', 'objectID');
$tasks = $this->dao->select($fields)
->from(TABLE_TASK)->alias('t1')
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id')
@@ -2207,8 +2211,9 @@ class taskModel extends model
->markRight(1)
->fi()
->beginIF($type == 'delayed')->andWhere('t1.deadline')->gt('1970-1-1')->andWhere('t1.deadline')->lt(date(DT_DATE1))->andWhere('t1.status')->in('wait,doing')->fi()
->beginIF(is_array($type) or strpos(',all,undone,needconfirm,assignedtome,delayed,finishedbyme,myinvolved,', ",$type,") === false)->andWhere('t1.status')->in($type)->fi()
->beginIF(is_array($type) or strpos(',all,undone,needconfirm,assignedtome,delayed,finishedbyme,myinvolved,assignedbyme,', ",$type,") === false)->andWhere('t1.status')->in($type)->fi()
->beginIF($modules)->andWhere('t1.module')->in($modules)->fi()
->beginIF($type == 'assignedbyme')->andWhere('t1.id')->in($actionIDList)->andWhere('t1.status')->ne('closed')->fi()
->andWhere('t1.deleted')->eq(0)
->orderBy($orderBy)
->page($pager, 't1.id')
+47
View File
@@ -2769,6 +2769,53 @@ class userModel extends model
return $visionList;
}
/**
* Get users who have authority to create stories.
*
* @access public
* @return array
*/
public function getCanCreateStoryUsers()
{
$users = $this->getPairs('noclosed|nodeleted');
$groupList = $this->dao->select('*')->from(TABLE_USERGROUP)
->where('account')->in(array_keys($users))
->fetchGroup('account', 'group');
$hasPrivGroups = $this->dao->select('*')->from(TABLE_GROUPPRIV)
->where('module')->eq('story')
->andWhere('(method')->eq('create')
->orWhere('method')->eq('batchCreate')
->markRight(1)
->fetchAll('group');
foreach($users as $account => $user)
{
if(empty($user) or strpos($this->app->company->admins, ",{$account},") !== false) continue;
if(!isset($groupList[$account]))
{
unset($users[$account]);
continue;
}
$groups = $groupList[$account];
$hasPriv = false;
foreach($groups as $groupID => $group)
{
if(isset($hasPrivGroups[$groupID]))
{
$hasPriv = true;
break;
}
}
if(!$hasPriv) unset($users[$account]);
}
return $users;
}
/**
* Switch admin of ZenTao.
*
+1 -1
View File
@@ -417,7 +417,7 @@ class webhookModel extends model
$object = $this->dao->select('*')->from($this->config->objectTables[$objectType])->where('id')->eq($objectID)->fetch();
$field = $this->config->action->objectNameFields[$objectType];
$host = empty($webhook->domain) ? common::getSysURL() : $webhook->domain;
$viewLink = $this->getViewLink($objectType, $objectID);
$viewLink = $this->getViewLink($objectType == 'kanbancard' ? 'kanban' : $objectType, $objectType == 'kanbancard' ? $object->kanban : $objectID);
$objectTypeName = ($objectType == 'story' and $object->type == 'requirement') ? $this->lang->action->objectTypes['requirement'] : $this->lang->action->objectTypes[$objectType];
$title = $this->app->user->realname . $this->lang->action->label->$actionType . $objectTypeName;
$text = $title . ' ' . "[#{$objectID}::{$object->$field}](" . $host . $viewLink . ")";
+18 -3
View File
@@ -16,7 +16,7 @@ class Project
}
/**
* Test start a project.
* Test start a project.
*
* @param int $projectID
* @access public
@@ -35,8 +35,8 @@ class Project
/**
* Test create project.
*
* @param array $params
*
* @param array $params
* @access public
* @return void
*/
@@ -88,4 +88,19 @@ class Project
return $this->project->getByIdList($data['projectIdList']);
}
/**
* Test get all the projects under the program set to which an project belongs
*
* @param object $project
* @access public
* @return void
*/
public function getBrotherProjectsTest($project)
{
$projectIds = $this->project->getBrotherProjects($project);
if(dao::isError()) return array('message' => dao::getError());
return $projectIds;
}
}
+12 -7
View File
@@ -11,11 +11,12 @@ cid=1
pid=1
更新id为10的项目集信息 >> 测试更新项目集十
更新项目集名称为空时 >> 『项目集名称』不能为空。
当计划开始为空时更新项目集信息 >> 『计划开始』不能为空。
当计划完成为空时更新项目集信息 >> 『计划完成』不能为空。
当计划完成小于计划开始时 >> 『计划完成』应当大于『2020-10-10』。
项目集名称已经存在 >> 项目集名称』已经有『项目集1』这条记录了。如果您确定该记录已删除,请到后台-系统-数据-回收站还原。
项目集开始时间小于父项目集时 >> 父项目集的开始日期:2019-09-09,开始日期不能小于父项目集的开始日期;父项目集的完成日期:2019-09-09,完成日期不能大于父项目集的完成日期
父项目集的开始日期大于子项目集的开始日期时 >> 子项目集的最小开始日期:2022-03-26,父项目集的开始日期不能大于子项目集的最小开始日期
项目集开始时间小于父项目集 >> 项目集的开始日期:2022-03-26,开始日期不能小于父项目集的开始日期;父项目集的完成日期:2022-06-02,完成日期不能大于父项目集的完成日期
更新未开始的项目集实际开始时间 >> doing
*/
@@ -25,7 +26,7 @@ $data = array(
'parent' => '0',
'name' => '测试更新项目集十',
'begin' => '2020-10-10',
'end' => '2022-06-03',
'end' => '2022-09-03',
'acl' => 'private',
'budget' => '100',
'budgetUnit' => 'CNY',
@@ -52,10 +53,14 @@ $beginLtParentProgram = $data;
$beginLtParentProgram['parent'] = '9';
$beginLtParentProgram['begin'] = '2019-01-01';
$realBeganProgram = $data;
$realBeganProgram['realBegan'] = '2020-11-10';
r($program->update(10, $normalProgram)) && p('name') && e('测试更新项目集十'); // 正常更新项目集的情况
r($program->update(10, $emptyTitleProgram)) && p('message[name]:0') && e('『项目集名称』不能为空。'); // 更新项目集名称为空时
r($program->update(10, $emptyBeginProgram)) && p('message[begin]:0') && e('『计划开始』不能为空。'); // 当计划完成为空时更新项目集信息
r($program->update(10, $emptyEndProgram)) && p('message:end') && e('子项目的最大完成日期:2022-05-27,父项目的完成日期不能小于子项目的最大完成日期'); //当计划完成小于计划开始时
r($program->update(10, $emptyBeginProgram)) && p('message[begin]:0') && e('『计划开始』不能为空。'); // 当计划开始为空时更新项目集信息
r($program->update(10, $emptyEndProgram)) && p('message:end') && e('『计划完成』不能为空。'); // 当计划完成为空时更新项目集信息
r($program->update(10, $beginGtEndProgram)) && p('message:begin') && e('子项目集的最小开始日期:2022-02-01,父项目集的开始日期不能大于子项目集的最小开始日期'); // 父项目集的开始日期大于子项目集的开始日期时
r($program->update(10, $beginLtParentProgram)) && p('message:begin;message:end') && e('父项目集的开始日期:2022-02-09,开始日期不能小于父项目集的开始日期;父项目集的完成日期:2022-04-16,完成日期不能大于父项目集的完成日期'); // 项目集开始、结束日期和子项目不符的情况
r($program->update(10, $beginLtParentProgram)) && p('message:begin;message:end') && e('父项目集的开始日期:2022-03-26,开始日期不能小于父项目集的开始日期;父项目集的完成日期:2022-06-02,完成日期不能大于父项目集的完成日期'); // 项目集开始、结束日期和子项目不符的情况
r($program->update(10, $realBeganProgram)) && p('status') && e('doing'); // 更新未开始的项目集实际开始时间
system("./ztest init");
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env php
<?php
include dirname(dirname(dirname(__FILE__))) . '/lib/init.php';
include dirname(dirname(dirname(__FILE__))) . '/class/project.class.php';
su('admin');
/**
title=测试 projectModel->getBrotherProjects();
cid=1
pid=1
获取项目id为11同一个项目集下的所有项目个数 >> 9
获取id为731的顶级项目兄弟项目个数 >> 1
*/
global $tester;
$tester->app->loadConfig('execution');
$project = new Project();
$projectInst = $project->project->getById(11);
$programProjects = $project->getBrotherProjectsTest($projectInst);
$projectInst = $project->project->getById(731);
$depProjects = $project->getBrotherProjectsTest($projectInst);
r(count($programProjects)) && p() && e('9'); // 获取项目id为11同一个项目集下的所有项目个数
r(count($depProjects)) && p() && e('1'); // 获取id为731的顶级项目兄弟项目个数
-2
View File
@@ -91,8 +91,6 @@
if(config.debug > 2)
{
console.log(document.title || location.href, 'tid', _tid);
$(function()
{
$('#tid').prepend('<code class="bg-blue">localtid=' + _tid + '</code>');
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+4 -4
View File
File diff suppressed because one or more lines are too long
@@ -48,12 +48,13 @@ class xuanxuanMessage extends messageModel
$server = $this->loadModel('im')->getServer('zentao');
$onlybody = isset($_GET['onlybody']) ? $_GET['onlybody'] : '';
unset($_GET['onlybody']);
$url = $server . helper::createLink($objectType, 'view', "id=$objectID", 'html');
$dataID = $objectType == 'kanbancard' ? $object->kanban : $objectID;
$url = $server . helper::createLink($objectType == 'kanbancard' ? 'kanban' : $objectType, 'view', "id=$dataID", 'html');
$target = '';
if(!empty($object->assignedTo)) $target .= $object->assignedTo;
if(!empty($object->mailto)) $target .= ",{$object->mailto}";
if($objectType == 'mr' && !empty($object->createdBy)) $target .= ",{$object->createdBy}";
if(($objectType == 'mr' or $objectType == 'kanbancard') and !empty($object->createdBy)) $target .= ",{$object->createdBy}";
$target = trim($target, ',');
$target = $this->dao->select('id')->from(TABLE_USER)
->where('account')->in($target)