Merge branch 'master' into 'sprint/32_zhouxin_fixbugs'

# Conflicts:
#   module/project/config.php
This commit is contained in:
李玉春
2022-01-06 04:01:16 +00:00
104 changed files with 866 additions and 500 deletions
+1 -1
View File
@@ -296,5 +296,5 @@ $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme');
/* Program privs.*/
$config->programPriv = new stdclass();
$config->programPriv->scrum = array('projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'caselib', 'doc', 'repo', 'meeting', 'stakeholder');
$config->programPriv->scrum = array('projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
$config->programPriv->waterfall = array_merge($config->programPriv->scrum, array('workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'issue', 'risk', 'opportunity', 'measrecord', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport'));
+1 -1
View File
@@ -136,7 +136,7 @@ REPLACE INTO `zt_stage` (`name`,`percent`,`type`,`createdBy`,`createdDate`,`edit
('发布','10','release','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'),
('总结评审','5','review','admin','2020-02-08 21:08:45','admin','2020-02-12 13:50:27','0');
INSERT INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`) VALUES
REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`) VALUES
('all','stage','typeList','request','需求', '1'),
('all','stage','typeList','design','设计', '1'),
('all','stage','typeList','dev','开发', '1'),
+1 -1
View File
@@ -557,7 +557,7 @@ class baseHelper
*/
static public function isZeroDate($date)
{
return substr($date, 0, 4) == '0000';
return (empty($date) or substr($date, 0, 4) == '0000');
}
/**
+1
View File
@@ -404,6 +404,7 @@ class control extends baseControl
foreach(explode(',', $requiredFields) as $requiredField)
{
if(empty($requiredField)) continue;
if(!isset($fields[$requiredField])) continue;
if(isset($_POST[$requiredField]) and $_POST[$requiredField] === '')
{
$message[$requiredField][] = sprintf($this->lang->error->notempty, $fields[$requiredField]->name);
+1 -1
View File
@@ -55,4 +55,4 @@ $config->action->majorList['execution'] = array('opened', 'edited');
$config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis,researchplan,researchreport,';
$config->action->needGetRelateField = ',story,productplan,release,task,build,bug,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,';
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,';
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,';
+1 -1
View File
@@ -60,7 +60,7 @@ class action extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$trashes = $this->action->getTrashes($type, $sort, $pager);
/* Title and position. */
+1
View File
@@ -53,6 +53,7 @@ $lang->action->confirmHideAll = 'Do you want to hide all the records?';
$lang->action->needEdit = '%s that you want to restore. Please edit it.';
$lang->action->historyEdit = 'The history EditBy cannot be empty.';
$lang->action->noDynamic = 'No dynamics. ';
$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.';
$lang->action->history = new stdclass();
$lang->action->history->action = 'Link';
+1
View File
@@ -53,6 +53,7 @@ $lang->action->confirmHideAll = '您确定要全部隐藏这些记录吗?';
$lang->action->needEdit = '要还原%s的名称或代号已经存在,请编辑更改。';
$lang->action->historyEdit = '历史记录编辑不能为空。';
$lang->action->noDynamic = '暂时没有动态。';
$lang->action->undeletedTips = '该数据在版本升级过程中未参与数据归并流程,不支持还原。';
$lang->action->history = new stdclass();
$lang->action->history->action = '关联日志';
+7
View File
@@ -1429,6 +1429,13 @@ class actionModel extends model
{
$action = $this->getById($actionID);
if($action->action != 'deleted') return;
if($this->config->systemMode == 'new' and $action->objectType == 'execution')
{
$execution = $this->dao->select('*')->from(TABLE_EXECUTION)->where('id')->eq($action->objectID)->fetch();
if($execution->deleted and empty($execution->project)) return print(js::error($this->lang->action->undeletedTips));
}
if($action->objectType == 'product')
{
$product = $this->dao->select('id,name,code,acl')->from(TABLE_PRODUCT)->where('id')->eq($action->objectID)->fetch();
+2 -2
View File
@@ -99,7 +99,7 @@ class api extends control
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$releases = $this->api->getReleaseByQuery($libID, $pager, $sort);
$this->view->releases = $releases;
@@ -185,7 +185,7 @@ class api extends control
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$structs = $this->api->getStructByQuery($libID, $pager, $sort);
@@ -31,7 +31,7 @@
<th><?php echo $lang->block->totalStory . ':';?></th>
<td><?php echo $totalData[$projectID]->allStories;?></td>
<th><?php echo $lang->block->totalPeople . ':';?></th>
<td><?php echo $totalData[$projectID]->teamCount ? html::a($this->createLink('project', 'manageMembers', 'projectID=' . $projectID), $totalData[$projectID]->teamCount) : 0;?></td>
<td><?php echo $totalData[$projectID]->teamCount ? html::a($this->createLink('project', 'team', 'projectID=' . $projectID), $totalData[$projectID]->teamCount) : 0;?></td>
<th><?php echo $lang->block->estimatedHours . ':';?></th>
<td><?php echo $totalData[$projectID]->estimate . $lang->execution->workHour;?></td>
<th><?php echo $lang->block->totalBug. ':';?></th>
+5
View File
@@ -189,6 +189,11 @@ $config->bug->datatable->fieldList['type']['fixed'] = 'no';
$config->bug->datatable->fieldList['type']['width'] = '90';
$config->bug->datatable->fieldList['type']['required'] = 'no';
$config->bug->datatable->fieldList['project']['title'] = 'project';
$config->bug->datatable->fieldList['project']['fixed'] = 'no';
$config->bug->datatable->fieldList['project']['width'] = '120';
$config->bug->datatable->fieldList['project']['required'] = 'no';
$config->bug->datatable->fieldList['execution']['title'] = 'execution';
$config->bug->datatable->fieldList['execution']['fixed'] = 'no';
$config->bug->datatable->fieldList['execution']['width'] = '120';
+3 -2
View File
@@ -148,7 +148,7 @@ class bug extends control
setcookie('qaBugOrder', $orderBy, 0, $this->config->webRoot, '', $this->config->cookieSecure, true);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
@@ -238,6 +238,7 @@ class bug extends control
$this->view->isProjectBug = ($productID and !$this->projectID) ? false : true;
$this->view->modulePairs = $showModule ? $this->tree->getModulePairs($productID, 'bug', $showModule) : array();
$this->view->showBranch = $showBranch;
$this->view->projectPairs = $this->loadModel('project')->getPairsByProgram();
$this->display();
}
@@ -548,7 +549,7 @@ class bug extends control
if($user) $productMembers[$assignedTo] = $user->realname;
}
$moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $branch === 'all' ? 0 : $branch);
$moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch);
if(empty($moduleOptionMenu)) die(js::locate(helper::createLink('tree', 'browse', "productID=$productID&view=story")));
/* Get products and projects. */
+38 -28
View File
@@ -2697,7 +2697,7 @@ class bugModel extends model
* @access public
* @return void
*/
public function printCell($col, $bug, $users, $builds, $branches, $modulePairs, $executions = array(), $plans = array(), $stories = array(), $tasks = array(), $mode = 'datatable')
public function printCell($col, $bug, $users, $builds, $branches, $modulePairs, $executions = array(), $plans = array(), $stories = array(), $tasks = array(), $mode = 'datatable', $projectPairs = array())
{
/* Check the product is closed. */
$canBeChanged = common::canBeChanged('bug', $bug);
@@ -2732,35 +2732,42 @@ class bugModel extends model
{
$class = "c-$id";
$title = '';
if($id == 'id') $class .= ' cell-id';
if($id == 'status')
switch($id)
{
$class .= ' bug-' . $bug->status;
$title = "title='" . $this->processStatus('bug', $bug) . "'";
}
if($id == 'confirmed')
{
$class .= ' text-center';
}
if($id == 'title')
{
$class .= ' text-left';
$title = "title='{$bug->title}'";
}
if($id == 'type')
{
$title = "title='" . zget($this->lang->bug->typeList, $bug->type) . "'";
}
if($id == 'assignedTo')
{
$class .= ' has-btn text-left';
if($bug->assignedTo == $account) $class .= ' red';
}
if($id == 'resolvedBy')
{
$class .= ' c-user';
$title = "title='" . zget($users, $bug->resolvedBy) . "'";
case 'id':
$class .= ' cell-id';
break;
case 'status':
$class .= ' bug-' . $bug->status;
$title = "title='" . $this->processStatus('bug', $bug) . "'";
break;
case 'confirmed':
$class .= ' text-center';
break;
case 'title':
$class .= ' text-left';
$title = "title='{$bug->title}'";
break;
case 'type':
$title = "title='" . zget($this->lang->bug->typeList, $bug->type) . "'";
break;
case 'assignedTo':
$class .= ' has-btn text-left';
if($bug->assignedTo == $account) $class .= ' red';
break;
case 'resolvedBy':
$class .= ' c-user';
$title = "title='" . zget($users, $bug->resolvedBy) . "'";
break;
case 'project':
$title = "title='" . zget($projectPairs, $bug->project, '') . "'";
break;
case 'resolvedBuild':
$class .= ' text-ellipsis';
$title = "title='" . $bug->resolvedBuild . "'";
break;
}
if($id == 'deadline' && isset($bug->delay) && $bug->status == 'active') $class .= ' delayed';
if(strpos(',type,execution,story,plan,task,openedBuild,', ",{$id},") !== false) $class .= ' text-ellipsis';
@@ -2809,6 +2816,9 @@ class bugModel extends model
case 'branch':
echo zget($branches, $bug->branch, '');
break;
case 'project':
echo zget($projectPairs, $bug->project, '');
break;
case 'execution':
echo zget($executions, $bug->execution, '');
break;
+1 -1
View File
@@ -306,7 +306,7 @@ $currentBrowseType = isset($lang->bug->mySelects[$browseType]) && in_array($brow
}
}?>
<?php else:?>
<?php foreach($setting as $value) $this->bug->printCell($value, $bug, $users, $builds, $branchOption, $modulePairs, $executions, $plans, $stories, $tasks, $useDatatable ? 'datatable' : 'table');?>
<?php foreach($setting as $value) $this->bug->printCell($value, $bug, $users, $builds, $branchOption, $modulePairs, $executions, $plans, $stories, $tasks, $useDatatable ? 'datatable' : 'table', $projectPairs);?>
<?php endif;?>
</tr>
<?php endforeach;?>
+1 -1
View File
@@ -289,7 +289,7 @@
<td>
<?php
if($bug->story) echo html::a($this->createLink('story', 'view', "storyID=$bug->story", '', true), "#$bug->story $bug->storyTitle", '', "class='iframe' data-width='80%'");
if($bug->storyStatus == 'active' and $bug->latestStoryVersion > $bug->storyVersion)
if($bug->storyStatus == 'active' and $bug->latestStoryVersion > $bug->storyVersion and common::hasPriv('bug', 'confirmStoryChange'))
{
echo "(<span class='warning'>{$lang->story->changed}</span> ";
echo html::a($this->createLink('bug', 'confirmStoryChange', "bugID=$bug->id"), $lang->confirm, 'hiddenwin');
+1 -1
View File
@@ -203,7 +203,7 @@ class caselib extends control
$this->caselib->buildSearchForm($libID, $libraries, $queryID, $actionURL);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* save session .*/
$cases = $this->caselib->getLibCases($libID, $browseType, $queryID, $moduleID, $sort, $pager);
+1 -1
View File
@@ -2022,7 +2022,7 @@ EOD;
* @access public
* @return string
*/
public function appendOrder($orderBy, $append = 'id')
public static function appendOrder($orderBy, $append = 'id')
{
if(empty($orderBy)) return $append;
-23
View File
@@ -1,24 +1 @@
<?php if($extView = $this->getExtViewFile(__FILE__)){include $extView; return helper::cd();}?>
<?php if($config->debug):?>
<?php css::import($jsRoot . 'zui/picker/zui.picker.min.css'); ?>
<?php js::import($jsRoot . 'zui/picker/zui.picker.min.js'); ?>
<?php endif;?>
<style>
.picker-single .picker-selection-remove{z-index: 1000;}
.picker-selection-single:after, .picker-multi.picker-focus .picker-selections:before{font-family: ZentaoIcon !important; content: '\f0d7' !important;}
</style>
<script>
var chooseUsersToMail = '<?php echo $lang->chooseUsersToMail;?>';
if($.fn.picker)
{
$(document).ready(function()
{
$(".picker-select[data-pickertype!='remote']").picker({chosenMode: true});
$("[data-pickertype='remote']").each(function()
{
var pickerremote = $(this).attr('data-pickerremote');
$(this).picker({chosenMode: true, remote: pickerremote});
})
});
}
</script>
+2 -2
View File
@@ -63,7 +63,7 @@ class company extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Build the search form. */
$queryID = $type == 'bydept' ? 0 : (int)$param;
@@ -205,7 +205,7 @@ class company extends control
/* Append id for secend sort. */
$order = $direction == 'next' ? 'date_desc' : 'date_asc';
$sort = $this->loadModel('common')->appendOrder($order);
$sort = common::appendOrder($order);
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$date = empty($date) ? '' : date('Y-m-d', $date);
+1 -1
View File
@@ -66,7 +66,7 @@
<?php $tab = '';?>
<?php if($action->objectType == 'meeting') $tab = $action->project ? "data-app='project'" : "data-app='my'";?>
<?php
if((isset($config->maxVersion) and strpos(",{$config->action->assetType},", ",{$action->objectType},") !== false) or empty($action->objectName))
if(empty($action->objectName))
{
echo '#' . $action->objectID;
}
+8
View File
@@ -0,0 +1,8 @@
$(function()
{
$('#modeTab').addClass('btn-active-text');
$('[name=mode]').change(function()
{
$(this).val() == 'new' ? $('#changeModeTips').removeClass('hidden') : $('#changeModeTips').addClass('hidden');
});
})
+1
View File
@@ -220,6 +220,7 @@ $lang->custom->menuTip = 'Click to show/hide the menu. Drag to switch d
$lang->custom->saveFail = 'Failed to save!';
$lang->custom->page = ' Page';
$lang->custom->changeClassicTip = 'The Program module will be hidden, if you switch to the classic mode.';
$lang->custom->changeModeTips = 'Historical deleted data does not participate in the data merging process. After switching the mode, it will not support restoration. Please know.';
$lang->custom->scoreStatus[1] = 'On';
$lang->custom->scoreStatus[0] = 'Off';
+1
View File
@@ -220,6 +220,7 @@ $lang->custom->menuTip = '点击显示或隐藏导航条目,拖拽来
$lang->custom->saveFail = '保存失败!';
$lang->custom->page = '页面';
$lang->custom->changeClassicTip = '切换为老版本的习惯后,系统将取消项目集功能。';
$lang->custom->changeModeTips = '历史删除数据不参与数据归并流程,切换模式后将不支持还原,请知悉';
$lang->custom->scoreStatus[1] = '开启';
$lang->custom->scoreStatus[0] = '关闭';
+4 -4
View File
@@ -32,12 +32,12 @@
</tr>
<tr>
<td></td>
<td><?php if($changedMode != 'yes') echo html::submitButton($lang->custom->switch);?></td>
<td>
<?php if($changedMode != 'yes') echo html::submitButton($lang->custom->switch);?>
<div id='changeModeTips' class='text-danger hidden'><?php echo $lang->custom->changeModeTips;?></div>
</td>
</tr>
</table>
</form>
</div>
<script>
$('#modeTab').addClass('btn-active-text');
</script>
<?php include '../../common/view/footer.html.php';?>
+32 -1
View File
@@ -92,7 +92,7 @@ class doc extends control
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
if($browseType == 'collectedbyme')
{
@@ -778,6 +778,37 @@ class doc extends control
$this->doc->saveDraft($docID);
}
/**
* Ajax get doclib whitelist.
*
* @param int $doclibID
* @param string $acl open|custom|private
* @access public
* @return string
*/
public function ajaxGetWhitelist($doclibID, $acl)
{
$doclib = $this->doc->getLibById($doclibID);
$users = $this->user->getPairs('noletter|noempty|noclosed');
if($doclib->acl != 'custom' and !empty($doclib->project) and $acl == 'custom')
{
$project = $this->loadModel('project')->getById($doclib->project);
$userList = array();
$projectTeams = $this->loadModel('user')->getTeamMemberPairs($doclib->project);
$stakeholders = $this->loadModel('stakeholder')->getStakeHolderPairs($doclib->project);
foreach($stakeholders as $stakeholder) $userList[$stakeholder] = $users[$stakeholder];
$userList += $projectTeams;
$whitelist = implode(',', array_keys($userList)) . $project->whitelist . ',' .$project->PM . ',' . $doclib->users;
return print(html::select('users[]', $users, $whitelist, "class='form-control chosen' multiple"));
}
return print(html::select('users[]', $users, $doclib->users, "class='form-control chosen' multiple"));
}
/**
* Show files.
*
+20
View File
@@ -24,16 +24,36 @@ function toggleAcl(acl, type)
if(acl == 'custom')
{
$('#whiteListBox').removeClass('hidden');
$('#groupWhiteListBox').removeClass('hidden');
}
else if(acl == 'private')
{
$('#whiteListBox').removeClass('hidden');
$('#groupWhiteListBox').addClass('hidden');
}
else
{
$('#whiteListBox').addClass('hidden');
}
if(type == 'lib')
{
var libType = $('input[name="type"]:checked').val();
var notice = typeof(noticeAcl[libType][acl]) != 'undefined' ? noticeAcl[libType][acl] : '';
$('#noticeAcl').html(notice);
if(libType == 'custom' && acl == 'private') $('#whiteListBox').addClass('hidden');
if(libType == 'project' && typeof(doclibID) != 'undefined')
{
link = createLink('doc', 'ajaxGetWhitelist', 'doclibID=' + doclibID + '&acl=' + acl);
$.get(link, function(users)
{
$('#users').replaceWith(users);
$('#users_chosen').remove();
$('#users').chosen();
})
}
}
else
{
+3 -1
View File
@@ -206,7 +206,9 @@ $lang->doc->selectLibType = 'Please select a type of doc library.';
$lang->doc->noticeAcl['lib']['product']['default'] = 'Users who can access the selected product can access it.';
$lang->doc->noticeAcl['lib']['product']['custom'] = 'Users who can access the selected product or users in the whiltelist can access it.';
$lang->doc->noticeAcl['lib']['project']['default'] = 'Users who can access the selected project can access it.';
$lang->doc->noticeAcl['lib']['project']['custom'] = 'Users who can access the selected project or users in the whiltelist can access it.';
$lang->doc->noticeAcl['lib']['project']['open'] = 'Users who can access the selected project can access it.';
$lang->doc->noticeAcl['lib']['project']['private'] = 'Users who can access the selected project or users in the whiltelist can access it.';
$lang->doc->noticeAcl['lib']['project']['custom'] = 'Users in the whiltelist can access it.';
$lang->doc->noticeAcl['lib']['execution']['default'] = "Users who can access the selected {$lang->executionCommon} can access it.";
$lang->doc->noticeAcl['lib']['execution']['custom'] = "Users who can access the selected {$lang->executionCommon} or users in the whiltelist can access it.";
$lang->doc->noticeAcl['lib']['custom']['open'] = 'All users can access it.';
+3 -1
View File
@@ -206,7 +206,9 @@ $lang->doc->selectLibType = '请选择文档库类型';
$lang->doc->noticeAcl['lib']['product']['default'] = '有所选产品访问权限的用户可以访问。';
$lang->doc->noticeAcl['lib']['product']['custom'] = '有所选产品访问权限或白名单里的用户可以访问。';
$lang->doc->noticeAcl['lib']['project']['default'] = "有所选项目访问权限的用户可以访问。";
$lang->doc->noticeAcl['lib']['project']['custom'] = "有所选项目访问权限或白名单里的用户可以访问。";
$lang->doc->noticeAcl['lib']['project']['open'] = "有所选项目访问权限的用户可以访问。";
$lang->doc->noticeAcl['lib']['project']['private'] = "有所选项目访问权限或白名单里的用户可以访问。";
$lang->doc->noticeAcl['lib']['project']['custom'] = "白名单的用户可以访问。";
$lang->doc->noticeAcl['lib']['execution']['default'] = "有所选{$lang->executionCommon}访问权限的用户可以访问。";
$lang->doc->noticeAcl['lib']['execution']['custom'] = "有所选{$lang->executionCommon}访问权限或白名单里的用户可以访问。";
$lang->doc->noticeAcl['lib']['custom']['open'] = '所有人都可以访问。';
+47 -17
View File
@@ -177,8 +177,7 @@ class docModel extends model
$lib->project = $execution->project;
}
if($lib->acl == 'private') $lib->users = $this->app->user->account;
if($lib->acl == 'custom')
if($lib->acl == 'custom' or $lib->acl == 'private')
{
$trimedUsers = ',' . trim($lib->users, ',') . ',';
if(strpos($trimedUsers, ',' . $this->app->user->account . ',') === false) $lib->users .= ',' . $this->app->user->account;
@@ -254,10 +253,20 @@ class docModel extends model
->join('groups', ',')
->join('users', ',')
->get();
if($lib->acl == 'private')
if($oldLib->type == 'project' or $oldLib->type == 'custom')
{
$libCreatedBy = $this->dao->select('*')->from(TABLE_ACTION)->where('objectType')->eq('doclib')->andWhere('objectID')->eq($libID)->andWhere('action')->eq('created')->fetch('actor');
$lib->users = $libCreatedBy ? $libCreatedBy : $this->app->user->account;
if($oldLib->type == 'custom')
{
if($lib->acl == 'private') $lib->users = $libCreatedBy ? $libCreatedBy : $this->app->user->account;
}
else
{
$openedBy = $this->dao->findById($oldLib->project)->from(TABLE_PROJECT)->fetch('openedBy');
if($lib->acl == 'private' and $lib->acl == 'custom') $lib->users .= ',' . $libCreatedBy ? $libCreatedBy : $openedBy;
}
}
$lib->name = trim($lib->name); //Temporary treatment: Code for bug #15528.
@@ -906,11 +915,17 @@ class docModel extends model
if(isset($object->addedBy) and $object->addedBy == $this->app->user->account) return true;
if(isset($object->users) and strpos(",{$object->users},", $account) !== false) return true;
if($object->project and $object->main)
if($object->project and $object->acl == 'private')
{
$stakeHolders = array();
$project = $this->loadModel('project')->getById($object->project);
$projectTeams = $this->loadModel('user')->getTeamMemberPairs($object->project);
$authorizedUsers = $this->user->getProjectAuthedUsers($project, '', $projectTeams, array_flip(explode(",", $project->whitelist)));
$stakeHolderList = $this->loadModel('stakeholder')->getStakeHolderPairs($object->project);
foreach($stakeHolderList as $stakeHolder) $stakeHolders[$stakeHolder] = $stakeHolder;
$authorizedUsers = $this->user->getProjectAuthedUsers($project, $stakeHolders, $projectTeams, array_flip(explode(",", $project->whitelist)));
if(strpos(",{$object->users},", $account) !== false) return true;
if(array_key_exists($this->app->user->account, array_filter($authorizedUsers))) return true;
}
@@ -1318,6 +1333,15 @@ class docModel extends model
/* Sort project. */
$orderedProjects = array();
/* Project permissions for DocLib whitelist */
if($this->app->tab == 'doc')
{
$myObjects = $this->dao->select('t2.id, t2.name')->from(TABLE_DOCLIB)->alias('t1')
->leftjoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
->where("CONCAT(',', t1.users, ',')")->like("%,{$this->app->user->account},%")
->fetchPairs();
}
$objects = $this->dao->select('*')->from(TABLE_PROJECT)
->where('type')->eq('project')
->andWhere('deleted')->eq(0)
@@ -1905,28 +1929,34 @@ class docModel extends model
*/
public function buildCreateButton4Doc($objectType, $objectID, $libID)
{
if(!common::hasPriv('doc', 'create') and !common::hasPriv('doc', 'createLib')) return '';
if($objectType == 'book')
{
$html = html::a(helper::createLink('doc', 'createLib', "type=$objectType&objectID=$objectID"), '<i class="icon icon-plus"></i> ' . $this->lang->doc->createBook, '', 'class="btn btn-secondary iframe"');
}
elseif($libID)
{
$html = "<div class='dropdown' id='createDropdown'>";
$html = "<div class='dropdown' id='createDropdown'>";
$html .= "<button class='btn btn-primary' type='button' data-toggle='dropdown'><i class='icon icon-plus'></i> " . $this->lang->doc->createAB . " <span class='caret'></span></button>";
$html .= "<ul class='dropdown-menu pull-right'>";
foreach($this->lang->doc->typeList as $typeKey => $typeName)
if(common::hasPriv('doc', 'create'))
{
$class = strpos($this->config->doc->officeTypes, $typeKey) !== false ? 'iframe' : '';
$icon = zget($this->config->doc->iconList, $typeKey);
$html .= "<li>";
$html .= html::a(helper::createLink('doc', 'create', "objectType=$objectType&objectID=$objectID&libID=$libID&moduleID=0&type=$typeKey", '', $class ? true : false), "<i class='icon-$icon icon'></i> " . $typeName, '', "class='$class' data-app='{$this->app->tab}'");
$html .= "</li>";
if($typeKey == 'url') $html .= '<li class="divider"></li>';
foreach($this->lang->doc->typeList as $typeKey => $typeName)
{
$class = strpos($this->config->doc->officeTypes, $typeKey) !== false ? 'iframe' : '';
$icon = zget($this->config->doc->iconList, $typeKey);
$html .= "<li>";
$html .= html::a(helper::createLink('doc', 'create', "objectType=$objectType&objectID=$objectID&libID=$libID&moduleID=0&type=$typeKey", '', $class ? true : false), "<i class='icon-$icon icon'></i> " . $typeName, '', "class='$class' data-app='{$this->app->tab}'");
$html .= "</li>";
if($typeKey == 'url') $html .= '<li class="divider"></li>';
}
}
if(common::hasPriv('doc', 'createLib'))
{
$html .= '<li class="divider"></li>';
if(common::hasPriv('doc', 'create')) $html .= '<li class="divider"></li>';
$html .= '<li>' . html::a(helper::createLink('doc', 'createLib', "type=$objectType&objectID=$objectID"), "<i class='icon-doc-lib icon'></i> " . $this->lang->doc->createLib, '', "class='iframe' data-width='70%'") . '</li>';
}
$html .= "</ul></div>";
@@ -2579,8 +2609,8 @@ EOT;
$tab = strpos(',doc,product,project,execution,', ",{$this->app->tab},") !== false ? $this->app->tab : 'doc';
if($tab != 'doc') $this->loadModel($tab)->setMenu($objectID);
$this->lang->TRActions = $this->buildCollectButton4Doc();
$this->lang->TRActions .= common::hasPriv('doc', 'create') ? $this->buildCreateButton4Doc($type, $objectID, $libID) : '';
$this->lang->TRActions = $this->buildCollectButton4Doc();
$this->lang->TRActions .= $this->buildCreateButton4Doc($type, $objectID, $libID);
return array($libs, $libID, $object, $objectID);
}
+1 -1
View File
@@ -59,7 +59,7 @@
<tr id='whiteListBox' class='hidden'>
<th><?php echo $lang->doc->whiteList;?></th>
<td>
<div class='input-group'>
<div id='groupWhiteListBox' class='input-group'>
<span class='input-group-addon groups-addon'><?php echo $lang->doclib->group?></span>
<?php echo html::select('groups[]', $groups, '', "class='form-control chosen' multiple")?>
</div>
+2 -1
View File
@@ -12,6 +12,7 @@
?>
<?php include '../../common/view/header.lite.html.php';?>
<?php include '../../common/view/chosen.html.php';?>
<?php js::set('doclibID', $lib->id);?>
<?php if($lib->type == 'product'):?>
<style> .chosen-container .chosen-results {max-height: 180px;}</style>
<?php else:?>
@@ -63,7 +64,7 @@
<tr id='whiteListBox' class='hidden'>
<th><?php echo $lang->doc->whiteList?></th>
<td>
<div class='input-group'>
<div id='groupWhiteListBox' class='input-group'>
<span class='input-group-addon groups-addon'><?php echo $lang->doclib->group?></span>
<?php echo html::select('groups[]', $groups, $lib->groups, "class='form-control chosen' multiple")?>
</div>
+9 -12
View File
@@ -175,7 +175,7 @@ class execution extends control
setcookie('executionTaskOrder', $orderBy, 0, $this->config->webRoot, '', false, true);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Header and position. */
$this->view->title = $execution->name . $this->lang->colon . $this->lang->execution->task;
@@ -268,7 +268,7 @@ class execution extends control
/* Get tasks and group them. */
if(empty($groupBy))$groupBy = 'story';
if(($groupBy == 'story') and ($execution->type == 'ops'))$groupBy = 'status';
$sort = $this->loadModel('common')->appendOrder($groupBy);
$sort = common::appendOrder($groupBy);
$tasks = $this->loadModel('task')->getExecutionTasks($executionID, $productID = 0, $status = 'all', $modules = 0, $sort);
$groupBy = str_replace('`', '', $groupBy);
$taskLang = $this->lang->task;
@@ -730,7 +730,7 @@ class execution extends control
setcookie('executionStoryOrder', $orderBy, 0, $this->config->webRoot, '', false, true);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$queryID = ($type == 'bysearch') ? $param : 0;
$execution = $this->commonAction($executionID);
@@ -885,7 +885,7 @@ class execution extends control
$this->app->loadClass('pager', $static = true);
if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
$pager = new pager($recTotal, $recPerPage, $pageID);
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$bugs = $this->bug->getExecutionBugs($executionID, $productID, $build, $type, $param, $sort, '', $pager);
$bugs = $this->bug->checkDelayedBugs($bugs);
$users = $this->user->getPairs('noletter');
@@ -1972,13 +1972,10 @@ class execution extends control
foreach($plans as $plan) $allPlans += $plan;
}
$userList = array();
$avatarPairs = $this->dao->select('account, avatar')->from(TABLE_USER)->where('deleted')->eq(0)->fetchPairs();
foreach($avatarPairs as $account => $avatar)
{
if(!$avatar) continue;
$userList[$account]['avatar'] = $avatar;
}
$userList = $this->dao->select('account, realname name, avatar')->from(TABLE_USER)->where('deleted')->eq(0)->fetchAll('account');
$userList['closed']['account'] = 'Closed';
$userList['closed']['name'] = 'Closed';
$userList['closed']['avatar'] = '';
$this->view->title = $this->lang->execution->kanban;
$this->view->position[] = html::a($this->createLink('execution', 'browse', "executionID=$executionID"), $execution->name);
@@ -2743,7 +2740,7 @@ class execution extends control
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Set the menu. If the executionID = 0, use the indexMenu instead. */
$this->execution->setMenu($executionID);
+1 -1
View File
@@ -39,7 +39,7 @@ function renderUserAvatar(user, objectType, objectID, size)
if(objectType == 'story' && !priv.canAssignStory) return $noPrivAvatar;
if(objectType == 'bug' && !priv.canAssignBug) return $noPrivAvatar;
return $('<a class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" href="' + link + '"/>').avatar({user: user});
return $('<a class="avatar has-text ' + avatarSizeClass + ' avatar-circle iframe" title="' + user.name + '" href="' + link + '"/>').avatar({user: user});
}
/**
+3 -3
View File
@@ -329,7 +329,7 @@ class executionModel extends model
->join('whitelist', ',')
->add('type', $type)
->stripTags($this->config->execution->editor->create['id'], $this->config->allowedTags)
->remove('products, workDays, delta, branch, uid, plans, teams, teamMembers')
->remove('products, workDays, delta, branch, uid, plans, teams, teamMembers, contactListMenu')
->get();
/* Check the workload format and total. */
@@ -487,7 +487,7 @@ class executionModel extends model
->setDefault('team', $this->post->name)
->join('whitelist', ',')
->stripTags($this->config->execution->editor->edit['id'], $this->config->allowedTags)
->remove('products, branch, uid, plans, syncStories')
->remove('products, branch, uid, plans, syncStories, contactListMenu')
->get();
if($this->config->systemMode == 'new' and (empty($execution->project) or $execution->project == $oldExecution->project)) $this->checkBeginAndEndDate($oldExecution->project, $execution->begin, $execution->end);
@@ -1324,7 +1324,7 @@ class executionModel extends model
$module = 'execution';
$method = 'testcase';
}
if($module == 'testtask' and ($method == 'view' || $method == 'create' || $method == 'edit'))
if($module == 'testtask' and ($method == 'view' || $method == 'create' || $method == 'edit' || $method == 'linkcase'))
{
$module = 'execution';
$method = 'testtask';
+6 -3
View File
@@ -227,9 +227,12 @@
</tr>
<tr class="hidden" id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, $whitelist, 'class="form-control chosen" multiple');?></td>
<td></td>
<td></td>
<td colspan='2'>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, $whitelist, 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<td colspan='4' class='text-center form-actions'>
+6 -2
View File
@@ -191,8 +191,12 @@
</tr>
<tr class="<?php if($execution->acl == 'open') echo 'hidden';?>" id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, $execution->whitelist, 'class="form-control chosen" multiple');?></td>
<td></td>
<td>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, $execution->whitelist, 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr><td colspan='3' class='text-center form-actions'><?php echo html::submitButton() . ' ' . html::backButton();?></td></tr>
</table>
+23 -9
View File
@@ -2510,12 +2510,20 @@ class gitlabModel extends model
public function createBranchPriv($gitlabID, $projectID, $branch = '')
{
$priv = fixer::input('post')->get();
if(empty($priv->name)) dao::$errors['name'][] = $this->lang->gitlab->branch->emptyPrivNameError;
$singleBranch = $this->apiGetSingleBranchPriv($gitlabID, $projectID, $priv->name);
if(empty($branch) && !empty($singleBranch->id)) dao::$errors['name'][] = $this->lang->gitlab->branch->issetPrivNameError;
if(dao::isError()) return false;
if(!empty($branch) && !empty($singleBranch->id)) $this->apiDeleteBranchPriv($gitlabID, $projectID, $branch);
if(empty($priv->name))
{
dao::$errors['name'][] = $this->lang->gitlab->branch->emptyPrivNameError;
return false;
}
$singleBranch = $this->apiGetSingleBranchPriv($gitlabID, $projectID, $priv->name);
if(empty($branch) && !empty($singleBranch->id))
{
dao::$errors['name'][] = $this->lang->gitlab->branch->issetPrivNameError;
return false;
}
if(!empty($branch) && !empty($singleBranch->id)) $this->apiDeleteBranchPriv($gitlabID, $projectID, $branch);
$response = $this->apiCreateBranchPriv($gitlabID, $projectID, $priv);
if(!empty($response->id))
@@ -2575,12 +2583,18 @@ class gitlabModel extends model
public function createTagPriv($gitlabID, $projectID, $tag = '')
{
$priv = fixer::input('post')->get();
if(empty($priv->name)) dao::$errors['name'][] = $this->lang->gitlab->tag->emptyPrivNameError;
if(dao::isError()) return false;
if(empty($priv->name))
{
dao::$errors['name'][] = $this->lang->gitlab->tag->emptyPrivNameError;
return false;
}
$singleTag = $this->apiGetSingleTagPriv($gitlabID, $projectID, $priv->name);
if(empty($tag) && !empty($singleTag->id)) dao::$errors['name'][] = $this->lang->gitlab->tag->issetPrivNameError;
if(dao::isError()) return false;
if(empty($tag) && !empty($singleTag->id))
{
dao::$errors['name'][] = $this->lang->gitlab->tag->issetPrivNameError;
return false;
}
if(!empty($tag) && !empty($singleTag->name)) $this->apiDeleteTagPriv($gitlabID, $projectID, $tag);
$response = $this->apiCreateTagPriv($gitlabID, $projectID, $priv);
+1 -1
View File
@@ -31,7 +31,7 @@
<tr>
<th></th>
<td class='text-center form-actions'>
<?php echo html::submitButton($lang->gitlab->branch->create);?>
<?php echo html::submitButton();?>
<?php $backLink = $this->session->gitlabBranchList ? $this->session->gitlabBranchList : inlink('browseBranch', "gitlibID=$gitlabID&projectID=$projectID");?>
<?php if(!isonlybody()) echo html::a($backLink, $lang->goback, '', 'class="btn btn-wide"');?>
</td>
+2
View File
@@ -25,10 +25,12 @@
<th><?php echo $lang->group->desc;?></th>
<td><?php echo html::textarea('desc', '', "rows=5 class=form-control");?></td>
</tr>
<?php if($this->app->tab != 'project'):?>
<tr>
<th><?php echo $lang->group->limited;?></th>
<td><?php echo html::checkbox('limited', '');?></td>
</tr>
<?php endif;?>
<tr>
<td colspan='2' class='text-center'><?php echo html::submitButton();?></td>
</tr>
+1 -1
View File
@@ -591,7 +591,7 @@ class kanbanModel extends model
$lane->order = $laneOrder;
$lane->items = $planList;
$lanes[] = $lane;
$lanes[] = $lane;
$laneOrder ++;
$colorIndex ++;
if($colorIndex == count($this->config->productplan->laneColorList)) $colorIndex = 0;
+1 -1
View File
@@ -91,7 +91,7 @@
$userCount = count($assignedToList);
?>
<?php if($userCount > 0):?>
<div class='users pull-right' title="<?php echo $members;?>">
<div class='users pull-right' title="<?php echo trim($members, ',');?>">
<?php
foreach($assignedToList as $account)
{
+1 -1
View File
@@ -105,7 +105,7 @@ $lang->mr->apiError->createMR = "Failed to create a merge request through API. R
$lang->mr->apiError->sudo = "Unable to operate with the GitLab account bound to the current user. Reason: %s";
$lang->mr->createFailedFromAPI = "Failed to create Merge Request.";
$lang->mr->hasSameOpenedMR = "There are duplicate and unclosed merge requests.";
$lang->mr->hasSameOpenedMR = "There are duplicate and unclosed merge requests: ID%u";
$lang->mr->accessGitlabFailed = "Unable to connect to the GitLab server.";
$lang->mr->reopenSuccess = "The merge request was reopened.";
$lang->mr->closeSuccess = "Merge request closed.";
+1 -1
View File
@@ -105,7 +105,7 @@ $lang->mr->apiError->createMR = "通过API创建合并请求失败,失败原
$lang->mr->apiError->sudo = "无法以当前用户绑定的GitLab账户进行操作,失败原因:%s";
$lang->mr->createFailedFromAPI = "创建合并请求失败。";
$lang->mr->hasSameOpenedMR = "存在重复并且未关闭的合并请求。";
$lang->mr->hasSameOpenedMR = "存在重复并且未关闭的合并请求: ID%u";
$lang->mr->accessGitlabFailed = "当前无法连接到GitLab服务器。";
$lang->mr->reopenSuccess = "已重新打开合并请求。";
$lang->mr->closeSuccess = "已关闭合并请求。";
+6 -5
View File
@@ -275,7 +275,7 @@ class mrModel extends model
{
$pipeline = $this->loadModel('job')->exec($MR->jobID);
if(!empty($pipeline->queue) && $MR->jobID != $oldMR->jobID)
if(!empty($pipeline->queue))
{
$compile = $this->loadModel('compile')->getByQueue($pipeline->queue);
$MR->compileID = $compile->id;
@@ -1462,7 +1462,7 @@ class mrModel extends model
{
if(empty($sourceProject) or empty($sourceBranch) or empty($targetProject) or empty($targetBranch)) return array('result' => 'success');
$dbOpenedCount = $this->dao->select('count(*) as count')->from(TABLE_MR)
$dbOpenedID = $this->dao->select('id')->from(TABLE_MR)
->where('gitlabID')->eq($gitlabID)
->andWhere('sourceProject')->eq($sourceProject)
->andWhere('sourceBranch')->eq($sourceBranch)
@@ -1470,10 +1470,11 @@ class mrModel extends model
->andWhere('targetBranch')->eq($targetBranch)
->andWhere('status')->eq('opened')
->andWhere('deleted')->eq('0')
->fetch('count');
if($dbOpenedCount > 0) return array('result' => 'fail', 'message' => $this->lang->mr->hasSameOpenedMR);
->fetch('id');
if(!empty($dbOpenedID)) return array('result' => 'fail', 'message' => sprintf($this->lang->mr->hasSameOpenedMR, $dbOpenedID));
if($this->apiGetSameOpened($gitlabID, $sourceProject, $sourceBranch, $targetProject, $targetBranch)) return array('result' => 'fail', 'message' => $this->lang->mr->hasSameOpenedMR);
$mr = $this->apiGetSameOpened($gitlabID, $sourceProject, $sourceBranch, $targetProject, $targetBranch);
if($mr) return array('result' => 'fail', 'message' => sprintf($this->lang->mr->errorLang[2], $mr->iid));
return array('result' => 'success');
}
+132 -11
View File
@@ -86,9 +86,128 @@ class my extends control
*/
public function work($mode = 'task', $type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->showWorkCount($orderBy, $recTotal, $recPerPage, $pageID);
echo $this->fetch('my', $mode, "type=$type&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
}
/**
* Show to-do work count.
*
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function showWorkCount($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->loadModel('task');
$this->loadModel('story');
$this->loadModel('bug');
$this->loadModel('testcase');
$this->loadModel('testtask');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Get the number of tasks assigned to me. */
$tasks = $this->task->getUserTasks($this->app->user->account, 'assignedTo', 0, $pager);
$taskCount = $pager->recTotal;
/* Get the number of stories assigned to me. */
$assignedToStories = $this->story->getUserStories($this->app->user->account, 'assignedTo', $orderBy, $pager, 'story', false);
$assignedToStoryCount = $pager->recTotal;
$reviewByStories = $this->story->getUserStories($this->app->user->account, 'reviewBy', $orderBy, $pager, 'story', false);
$reviewByStoryCount = $pager->recTotal;
$storyCount = $assignedToStoryCount + $reviewByStoryCount;
$requirementCount = 0;
$isOpenedURAndSR = $this->config->URAndSR;
if($isOpenedURAndSR)
{
/* Get the number of requirements assigned to me. */
$assignedRequirements = $this->story->getUserStories($this->app->user->account, 'assignedTo', $orderBy, $pager, 'requirement');
$assignedRequirementCount = $pager->recTotal;
$reviewByRequirements = $this->story->getUserStories($this->app->user->account, 'reviewBy', $orderBy, $pager, 'requirement');
$reviewByRequirementCount = $pager->recTotal;
$requirementCount = $assignedRequirementCount + $reviewByRequirementCount;
}
/* Get the number of bugs assigned to me. */
$bugs = $this->bug->getUserBugs($this->app->user->account, 'assignedTo', $orderBy, 0, $pager);
$bugCount = $pager->recTotal;
/* Get the number of testcases assigned to me. */
$cases = $this->testcase->getByAssignedTo($this->app->user->account, $orderBy, $pager, 'skip');
$caseCount = $pager->recTotal;
/* Get the number of testtasks assigned to me. */
$testTasks = $this->testtask->getByUser($this->app->user->account, $pager, $orderBy, 'assignedTo');
$testTaskCount = $pager->recTotal;
$issueCount = 0;
$riskCount = 0;
$reviewCount = 0;
$ncCount = 0;
$meetingCount = 0;
$isMaxVersion = isset($this->config->maxVersion) ? 1 : 0;
if($isMaxVersion)
{
$this->loadModel('issue');
$this->loadModel('risk');
$this->loadModel('review');
$this->loadModel('meeting');
/* Get the number of issues assigned to me. */
$issues = $this->issue->getUserIssues('assignedTo', $this->app->user->account, $orderBy, $pager);
$issueCount = $pager->recTotal;
/* Get the number of risks assigned to me. */
$risks = $this->risk->getUserRisks('assignedTo', $this->app->user->account, $orderBy, $pager);
$riskCount = $pager->recTotal;
/* Get the number of reviews assigned to me. */
$reviewList = $this->review->getUserReviews('wait', $orderBy, $pager);
$reviewCount = $pager->recTotal;
/* Get the number of nc assigned to me. */
$ncList = $this->my->getNcList('assignedToMe', $orderBy, $pager);
$ncCount = $pager->recTotal;
/* Get the number of meetings assigned to me. */
$meetings = $this->meeting->getListByUser('futureMeeting', $orderBy, 0, $pager);
$meetingCount = $pager->recTotal;
}
echo <<<EOF
<script>
var taskCount = $taskCount;
var storyCount = $storyCount;
var bugCount = $bugCount;
var caseCount = $caseCount;
var testTaskCount = $testTaskCount;
var isOpenedURAndSR = $isOpenedURAndSR;
if(isOpenedURAndSR !== 0) var requirementCount = $requirementCount;
var isMaxVersion = $isMaxVersion;
if(isMaxVersion !== 0)
{
var issueCount = $issueCount;
var riskCount = $riskCount;
var reviewCount = $reviewCount;
var ncCount = $ncCount;
var meetingCount = $meetingCount;
}
</script>
EOF;
}
/**
* My contribute view.
*
@@ -145,7 +264,7 @@ class my extends control
$this->view->position[] = $this->lang->my->todo;
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$todos = $this->loadModel('todo')->getList($type, $account, $status, 0, $pager, $sort);
$tasks = $this->loadModel('task')->getUserSuspendedTasks($account);
@@ -198,7 +317,7 @@ class my extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'story', false);
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
@@ -242,7 +361,7 @@ class my extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$stories = $this->loadModel('story')->getUserStories($this->app->user->account, $type, $sort, $pager, 'requirement');
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
@@ -286,7 +405,7 @@ class my extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Get tasks. */
$tasks = $this->loadModel('task')->getUserTasks($this->app->user->account, $type, 0, $pager, $sort);
@@ -361,7 +480,7 @@ class my extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$bugs = $this->loadModel('bug')->getUserBugs($this->app->user->account, $type, $sort, 0, $pager);
$bugs = $this->bug->checkDelayedBugs($bugs);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug', false);
@@ -413,7 +532,7 @@ class my extends control
$this->app->loadLang('testcase');
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->testTask;
$this->view->position[] = $this->lang->my->testTask;
@@ -456,7 +575,7 @@ class my extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$cases = array();
if($type == 'assigntome')
@@ -505,7 +624,7 @@ class my extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$docs = $this->doc->getDocsByBrowseType($type, 0, 0, $sort, $pager);
@@ -791,7 +910,7 @@ class my extends control
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Get users by dept. */
$user = $this->loadModel('user')->getById($this->app->user->account, 'account');
@@ -968,12 +1087,14 @@ class my extends control
/**
* Build contact lists.
*
* @param dropdownName
* @access public
* @return void
*/
public function buildContactLists()
public function buildContactLists($dropdownName = 'mailto')
{
$this->view->contactLists = $this->user->getContactLists($this->app->user->account, 'withnote');
$this->view->dropdownName = $dropdownName;
$this->display();
}
@@ -1086,7 +1207,7 @@ class my extends control
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* The header and position. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->dynamic;
+21 -2
View File
@@ -3,9 +3,28 @@ $(function()
if(typeof mode === 'string')
{
$('#subNavbar li[data-id=' + mode + ']').addClass('active');
if(typeof rawMethod === 'string' && rawMethod == 'work') $('#subNavbar li[data-id=' + mode + '] a').append('<span class="label label-light label-badge">' + total + '</span>');
if(typeof rawMethod === 'string' && rawMethod == 'work')
{
$("#subNavbar li[data-id='task'] a").append('<span class="label label-light label-badge">' + taskCount + '</span>');
$("#subNavbar li[data-id='story'] a").append('<span class="label label-light label-badge">' + storyCount + '</span>');
$("#subNavbar li[data-id='bug'] a").append('<span class="label label-light label-badge">' + bugCount + '</span>');
$("#subNavbar li[data-id='testcase'] a").append('<span class="label label-light label-badge">' + caseCount + '</span>');
$("#subNavbar li[data-id='testtask'] a").append('<span class="label label-light label-badge">' + testTaskCount + '</span>');
if(isOpenedURAndSR !== 0) $("#subNavbar li[data-id='requirement'] a").append('<span class="label label-light label-badge">' + requirementCount + '</span>');
if(isMaxVersion !== 0)
{
$("#subNavbar li[data-id='issue'] a").append('<span class="label label-light label-badge">' + issueCount + '</span>');
$("#subNavbar li[data-id='risk'] a").append('<span class="label label-light label-badge">' + riskCount + '</span>');
$("#subNavbar li[data-id='audit'] a").append('<span class="label label-light label-badge">' + reviewCount + '</span>');
$("#subNavbar li[data-id='nc'] a").append('<span class="label label-light label-badge">' + ncCount + '</span>');
$("#subNavbar li[data-id='myMeeting'] a").append('<span class="label label-light label-badge">' + meetingCount + '</span>');
}
}
}
var $scp = $('[data-id="changePassword"] a');
var $scp = $('[data-id="changePassword"] a');
if($scp.length > 0)
{
var sign = config.requestType == 'GET' ? '&' : '?';
+3 -3
View File
@@ -13,7 +13,7 @@
<?php
if($contactLists)
{
echo html::select('contactListMenu', $contactLists, '', "class='form-control chosen' onchange=\"setMailto('mailto', this.value)\"");
echo html::select('contactListMenu', $contactLists, '', "class='form-control chosen' onchange=\"setMailto('$dropdownName', this.value)\"");
}
else
{
@@ -28,7 +28,7 @@ else
<style>
#contactListMenu_chosen {width: 100px !important;}
#contactListMenu + .chosen-container {min-width: 100px;}
td > #mailto + .chosen-container .chosen-choices {border-radius: 2px 2px 0 0;}
td > #mailto + .chosen-container + #contactListMenu + .chosen-container > .chosen-single {border-radius: 0 0 2px 2px; border-top-width: 0; padding-top: 6px;}
td > <?php echo "#" . $dropdownName;?> + .chosen-container .chosen-choices {border-radius: 2px 2px 0 0;}
td > <?php echo "#" . $dropdownName;?> + .chosen-container + #contactListMenu + .chosen-container > .chosen-single {border-radius: 0 0 2px 2px; border-top-width: 0; padding-top: 6px;}
#contactListMenu + .chosen-container.chosen-container-active > .chosen-single {border-top-width: 1px !important; padding-top: 5px !important;}
</style>
+2 -2
View File
@@ -214,7 +214,7 @@ class product extends control
setcookie('productStoryOrder', $orderBy, 0, $this->config->webRoot, '', $this->config->cookieSecure, true);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
@@ -843,7 +843,7 @@ class product extends control
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
+2 -2
View File
@@ -577,7 +577,7 @@ class productModel extends model
->stripTags($this->config->product->editor->create['id'], $this->config->allowedTags)
->join('whitelist', ',')
->join('reviewer', ',')
->remove('uid,newLine,lineName')
->remove('uid,newLine,lineName,contactListMenu')
->get();
if(!empty($_POST['lineName']))
@@ -653,7 +653,7 @@ class productModel extends model
->join('whitelist', ',')
->join('reviewer', ',')
->stripTags($this->config->product->editor->edit['id'], $this->config->allowedTags)
->remove('uid,changeProjects')
->remove('uid,changeProjects,contactListMenu')
->get();
if($this->config->systemMode == 'new')
+1 -1
View File
@@ -599,7 +599,7 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
</div>
<div class="modal-body">
<div class='input-group'>
<?php echo html::select('plan', $productPlans[$productID], '', "class='form-control chosen' id='plan'");?>
<?php echo html::select('plan', zget($productPlans, $productID, ''), '', "class='form-control chosen' id='plan'");?>
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toTaskButton'", 'btn btn-primary');?></span>
</div>
</div>
+6 -2
View File
@@ -104,8 +104,12 @@
</tr>
<tr id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?></td>
<td></td>
<td colspan='2'>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
+6 -2
View File
@@ -87,8 +87,12 @@
</tr>
<tr class="<?php if($product->acl == 'open') echo 'hidden';?>" id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, $product->whitelist, 'class="form-control chosen" multiple');?></td>
<td></td>
<td>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, $product->whitelist, 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
+5 -5
View File
@@ -66,7 +66,7 @@ class productplan extends control
$this->executeHooks($planID);
if($parent > 0) $this->productplan->updateParentStatus($parent, 'opened');
if($parent > 0) $this->productplan->updateParentStatus($parent);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $planID));
@@ -248,7 +248,7 @@ class productplan extends control
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->session->set('productPlanList', $this->app->getURI(true), 'product');
$viewType = $this->cookie->viewType ? $this->cookie->viewType : 'list';
@@ -280,9 +280,9 @@ class productplan extends control
foreach($branches as $id => $name)
{
$plans = isset($planGroup[$product->id][$id]) ? array_filter($planGroup[$product->id][$id]) : array();
$plans = isset($planGroup[$product->id][$id]) ? array_filter($planGroup[$product->id][$id]) : array();
$branchPairs[$id] = $name . ' ' . count($plans);
$planCount += count($plans);
$planCount += count($plans);
}
$this->view->branches = array('all' => $this->lang->productplan->allAB . ' ' . $planCount) + $branchPairs;
@@ -344,7 +344,7 @@ class productplan extends control
if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->commonAction($plan->product, $plan->branch);
$products = $this->product->getProductPairsByProject($this->session->project);
+1 -1
View File
@@ -29,5 +29,5 @@ ol, ul {padding-left: 20px;}
#mainMenu .icon-checked, #mainMenu .icon-off, #mainMenu .icon-play, #mainMenu .icon-magic {margin-right: 3px;}
#mainMenu .page-title {display: flex;}
#mainMenu .btn-toolbar .page-title .label { top: 9px; margin-right: 10px; height: 16px;}
#mainMenu .btn-toolbar .page-title .label {top: 9px; margin-right: 10px; height: 16px;}
#mainMenu .btn-toolbar .page-title .text {max-width: 434px; overflow: hidden; white-space:nowrap; margin-right: 6px;}
-7
View File
@@ -33,12 +33,8 @@ function setPlanStatus(planID, status)
if(status != 'wait')
{
$('#future' + planID).closest('div').addClass('hidden');
$("input[name='begin[" + planID + "]']").closest('td').addClass('required');
$("input[name='end[" + planID + "]']").closest('td').addClass('required');
$("input[name='begin[" + planID + "]']").removeAttr('disabled');
$("input[name='end[" + planID + "]']").removeAttr('disabled');
$('.form-date').datetimepicker('update');
}
else
@@ -46,15 +42,12 @@ function setPlanStatus(planID, status)
var isFuture = $('#future' + planID).prop('checked');
$('#future' + planID).closest('div').removeClass('hidden');
$("input[name='begin[" + planID + "]']").closest('td').removeClass('required');
$("input[name='end[" + planID + "]']").closest('td').removeClass('required');
if(isFuture)
{
$("input[name='begin[" + planID + "]']").attr('disabled', 'disabled');
$("input[name='end[" + planID + "]']").attr('disabled', 'disabled');
}
}
}
+56 -47
View File
@@ -56,18 +56,18 @@ $(function()
$('#branch_chosen .chosen-single span').prepend('<i class="icon-delay"></i>');
$('#kanban').kanban(
{
data: kanbanData,
minColWidth: 290,
maxColWidth: 290,
maxColHeight: 460,
minColHeight: 190,
cardHeight: 80,
itemRender: renderKanbanItem,
virtualize: true,
data: kanbanData,
minColWidth: 290,
maxColWidth: 290,
maxColHeight: 460,
minColHeight: 190,
cardHeight: 80,
itemRender: renderKanbanItem,
virtualize: true,
droppable:
{
target: findDropColumns,
finish: handleFinishDrop,
target: findDropColumns,
finish: handleFinishDrop,
mouseButton: 'left'
}
});
@@ -77,7 +77,7 @@ $(function()
$.zui.ContextMenu.hide();
});
/* Init contextmenu */
/* Init contextmenu. */
$('#kanban').on('click', '[data-contextmenu]', function(event)
{
var $trigger = $(this);
@@ -86,30 +86,27 @@ $(function()
if(!menuCreator) return;
var options = $.extend({event: event, $trigger: $trigger}, $trigger.data());
var items = menuCreator(options);
var items = menuCreator(options);
if(!items || !items.length) return;
$.zui.ContextMenu.show(items, items.$options || {event: event});
});
}
/* Hide contextmenu when page scroll */
/* Hide contextmenu when page scroll. */
$(window).on('scroll', function()
{
$.zui.ContextMenu.hide();
});
});
/* Define menu creators */
window.menuCreators =
{
card: createCardMenu
};
/* Define menu creators. */
window.menuCreators = {card: createCardMenu};
/**
* Create card menu.
*
* @param object $options
* @param object options
* @access public
* @return array
*/
@@ -194,11 +191,14 @@ $(document).on('click', 'td.content .more', function(e)
}
});
/*
* Find drop columns
* @param {JQuery} $element Drag element
* @param {JQuery} $root Dnd root element
*/
/**
* Find drop columns.
*
* @param object $element Drag element
* @param object $root Dnd root element
* @access public
* @return object|bool
*/
function findDropColumns($element, $root)
{
var $col = $element.closest('.kanban-col');
@@ -216,42 +216,46 @@ function findDropColumns($element, $root)
if(colRules === true) return true;
var $newCol = $(this);
var newCol = $newCol.data();
var newCol = $newCol.data();
if(newCol.id === col.id) return false;
var $newLane = $newCol.closest('.kanban-lane');
var newLane = $newLane.data('lane');
var newLane = $newLane.data('lane');
return colRules.indexOf(newCol.type) > -1 && newLane.id === lane.id;
});
}
/**
* Handle finish drop task
* @param {Object} event Event object
* @returns {void}
* Handle finish drop card.
*
* @param object event
* @access public
* @return void
*/
function handleFinishDrop(event)
{
var $card = $(event.element); // The drag card
var $card = $(event.element); // The drag card.
var $dragCol = $card.closest('.kanban-lane-col');
var $dropCol = $(event.target);
/* Get d-n-d(drag and drop) infos 获取拖放操作相关信息 */
var card = $card.data('item');
/* Get d-n-d(drag and drop) infos. */
var card = $card.data('item');
var fromColType = $dragCol.data('type');
var toColType = $dropCol.data('type');
var kanbanID = $card.closest('.kanban').data('id');
var toColType = $dropCol.data('type');
var kanbanID = $card.closest('.kanban').data('id');
changeCardColType(card, fromColType, toColType, kanbanID);
}
/**
* Change column type for a card
* @param {Object} card Card object
* @param {String} fromColType The column type before change
* @param {String} toColType The column type after change
* @param {String} kanbanID Kanban ID
* Change column type for a card.
*
* @param object card
* @param string fromColType The column type before change
* @param string toColType The column type after change
* @param int kanbanID
* @access public
* @return void
*/
function changeCardColType(card, fromColType, toColType, kanbanID)
{
@@ -303,7 +307,12 @@ function changeCardColType(card, fromColType, toColType, kanbanID)
}
/**
* The function for rendering kanban item
* The function for rendering kanban item.
*
* @param object item
* @param object $item
* @access public
* @return void
*/
function renderKanbanItem(item, $item)
{
@@ -387,10 +396,10 @@ function renderKanbanItem(item, $item)
}
/**
* Get planID
* Get planID.
*
* @param object $obj
* @param int $branch
* @param object obj
* @param int branch
* @access public
* @return void
*/
@@ -428,9 +437,9 @@ if(!window.kanbanDropRules)
{
window.kanbanDropRules =
{
wait: ['doing'],
doing: ['done'],
done: ['doing', 'closed'],
wait: ['doing'],
doing: ['done'],
done: ['doing', 'closed'],
closed: ['doing']
}
}
+1 -5
View File
@@ -72,8 +72,6 @@ function setPlanStatus()
{
$('#checkBox').closest('div').addClass('hidden');
$('#future').val(0);
$('#begin').closest('td').addClass('required');
$('#end').closest('td').addClass('required');
$('#begin').removeAttr('disabled');
$('#end').parents('tr').show();
}
@@ -82,12 +80,10 @@ function setPlanStatus()
var isFuture = $('#future').prop('checked');
$('#checkBox').closest('div').removeClass('hidden');
$('#begin').closest('td').removeClass('required');
$('#end').closest('td').removeClass('required');
if(isFuture)
{
$('#begin').attr('disabled', 'disabled');
$('#end').val('').parents('tr').hide();
$('#end').parents('tr').hide();
}
}
}
+14 -14
View File
@@ -111,20 +111,20 @@ $lang->productplan->endList[93] = '3 Months';
$lang->productplan->endList[186] = '6 Months';
$lang->productplan->endList[365] = '1 Year';
$lang->productplan->errorNoTitle = 'ID %s title should not be empty.';
$lang->productplan->errorNoBegin = 'ID %s begin time should not be empty.';
$lang->productplan->errorNoEnd = 'ID %s end time should not be empty.';
$lang->productplan->beginGeEnd = 'ID %s begin time should not be >= end time.';
$lang->productplan->beginLetterParent = "The start date of the parent plan: %s, the start date cannot be less than the start date of the parent plan.";
$lang->productplan->endGreaterParent = "The completion date of the parent plan: %s, the completion date cannot be greater than the completion date of the parent plan.";
$lang->productplan->beginGreaterChild = "The start date of the child plan: %s, the start date cannot be greater than the start date of the child plan.";
$lang->productplan->endLetterChild = "The completion date of the child plan: %s, the completion date cannot be less than the completion date of the child plan.";
$lang->productplan->noLinkedProject = "The current product has not been linked with a project. Please enter the list of the product to link or create a project.";
$lang->productplan->enterProjectList = "Enter the list of the product";
$lang->productplan->beginGreaterChildTip = "The start date of the parent plan[%s]: %s, cannot be greater than the start date of the child plan: %s.";
$lang->productplan->endLetterChildTip = "The completion date of the parent plan[%s]: %s, cannot be less than the completion date of the child plan: %s.", ;
$lang->productplan->beginLetterParentTip = "The start date of the child plan[%s]: %s, cannot be less than the start date of the parent plan: %s.";
$lang->productplan->endGreaterParentTip = "The completion date of the child plan[%s]: %s, cannot be greater than the completion date of the parent plan: %s.";
$lang->productplan->errorNoTitle = 'ID %s title should not be empty.';
$lang->productplan->errorNoBegin = 'ID %s begin time should not be empty.';
$lang->productplan->errorNoEnd = 'ID %s end time should not be empty.';
$lang->productplan->beginGeEnd = 'ID %s begin time should not be >= end time.';
$lang->productplan->beginLetterParent = "The start date of the parent plan: %s, the start date cannot be less than the start date of the parent plan.";
$lang->productplan->endGreaterParent = "The completion date of the parent plan: %s, the completion date cannot be greater than the completion date of the parent plan.";
$lang->productplan->beginGreaterChild = "The start date of the child plan: %s, the start date cannot be greater than the start date of the child plan.";
$lang->productplan->endLetterChild = "The completion date of the child plan: %s, the completion date cannot be less than the completion date of the child plan.";
$lang->productplan->noLinkedProject = "The current product has not been linked with a project. Please enter the list of the product to link or create a project.";
$lang->productplan->enterProjectList = "Enter the list of the product";
$lang->productplan->beginGreaterChildTip = "The start date of the parent plan[%s]: %s, cannot be greater than the start date of the child plan: %s.";
$lang->productplan->endLetterChildTip = "The completion date of the parent plan[%s]: %s, cannot be less than the completion date of the child plan: %s.", ;
$lang->productplan->beginLetterParentTip = "The start date of the child plan[%s]: %s, cannot be less than the start date of the parent plan: %s.";
$lang->productplan->endGreaterParentTip = "The completion date of the child plan[%s]: %s, cannot be greater than the completion date of the parent plan: %s.";
$lang->productplan->featureBar['browse']['all'] = 'All';
$lang->productplan->featureBar['browse']['wait'] = 'Waiting';
+14 -14
View File
@@ -111,20 +111,20 @@ $lang->productplan->endList[93] = '三个月';
$lang->productplan->endList[186] = '半年';
$lang->productplan->endList[365] = '一年';
$lang->productplan->errorNoTitle = 'ID %s 标题不能为空';
$lang->productplan->errorNoBegin = 'ID %s 开始时间不能为空';
$lang->productplan->errorNoEnd = 'ID %s 结束时间不能为空';
$lang->productplan->beginGeEnd = 'ID %s 开始时间不能大于结束时间';
$lang->productplan->beginLetterParent = "父计划的开始日期:%s,开始日期不能小于父计划的开始日期";
$lang->productplan->endGreaterParent = "父计划的完成日期:%s,完成日期不能大于父计划的完成日期";
$lang->productplan->beginGreaterChild = "子计划的开始日期:%s,开始日期不能大于子计划的开始日期";
$lang->productplan->endLetterChild = "子计划的完成日期:%s,完成日期不能小于子计划的完成日期";
$lang->productplan->noLinkedProject = "当前产品还未关联项目,请进入产品的项目列表关联或创建一个项目";
$lang->productplan->enterProjectList = "进入产品的项目列表";
$lang->productplan->beginGreaterChildTip = "父计划[%s]的开始日期:%s,不能大于子计划的开始日期: %s";
$lang->productplan->endLetterChildTip = "父计划[%s]的完成日期:%s,不能小于子计划的完成日期: %s";
$lang->productplan->beginLetterParentTip = "子计划[%s]的开始日期:%s,不能小于父计划的开始日期: %s";
$lang->productplan->endGreaterParentTip = "子计划[%s]的完成日期:%s,不能大于父计划的完成日期: %s";
$lang->productplan->errorNoTitle = 'ID %s 标题不能为空';
$lang->productplan->errorNoBegin = 'ID %s 开始时间不能为空';
$lang->productplan->errorNoEnd = 'ID %s 结束时间不能为空';
$lang->productplan->beginGeEnd = 'ID %s 开始时间不能大于结束时间';
$lang->productplan->beginLetterParent = "父计划的开始日期:%s,开始日期不能小于父计划的开始日期";
$lang->productplan->endGreaterParent = "父计划的完成日期:%s,完成日期不能大于父计划的完成日期";
$lang->productplan->beginGreaterChild = "子计划的开始日期:%s,开始日期不能大于子计划的开始日期";
$lang->productplan->endLetterChild = "子计划的完成日期:%s,完成日期不能小于子计划的完成日期";
$lang->productplan->noLinkedProject = "当前产品还未关联项目,请进入产品的项目列表关联或创建一个项目";
$lang->productplan->enterProjectList = "进入产品的项目列表";
$lang->productplan->beginGreaterChildTip = "父计划[%s]的开始日期:%s,不能大于子计划的开始日期: %s";
$lang->productplan->endLetterChildTip = "父计划[%s]的完成日期:%s,不能小于子计划的完成日期: %s";
$lang->productplan->beginLetterParentTip = "子计划[%s]的开始日期:%s,不能小于父计划的开始日期: %s";
$lang->productplan->endGreaterParentTip = "子计划[%s]的完成日期:%s,不能大于父计划的完成日期: %s";
$lang->productplan->featureBar['browse']['all'] = '所有';
$lang->productplan->featureBar['browse']['wait'] = '未开始';
+63 -90
View File
@@ -401,7 +401,7 @@ class productplanModel extends model
}
if($plan->parentEnd != $this->config->productplan->future)
{
if($plan->end !==$this->config->productplan->future and $plan->end > $plan->parentEnd) dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $plan->parentEnd);
if($plan->end !== $this->config->productplan->future and $plan->end > $plan->parentEnd) dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $plan->parentEnd);
}
}
@@ -454,7 +454,7 @@ class productplanModel extends model
}
/**
* Update a plan
* Update a plan.
*
* @param int $planID
* @access public
@@ -464,21 +464,21 @@ class productplanModel extends model
{
$oldPlan = $this->dao->findByID((int)$planID)->from(TABLE_PRODUCTPLAN)->fetch();
$plan = fixer::input('post')->stripTags($this->config->productplan->editor->edit['id'], $this->config->allowedTags)
->setIF($this->post->future || empty($_POST['begin']), 'begin', $this->config->productplan->future)
->setIF($this->post->future || empty($_POST['end']), 'end', $this->config->productplan->future)
->setIF($this->post->future or empty($_POST['begin']), 'begin', $this->config->productplan->future)
->setIF($this->post->future or empty($_POST['end']), 'end', $this->config->productplan->future)
->remove('delta,uid,future')
->get();
if($oldPlan->parent > 0)
{
$parentPlan = $this->getByID($oldPlan->parent);
if($parentPlan->begin != $this->config->productplan->future)
if($parentPlan->begin !== $this->config->productplan->future)
{
if($plan->begin < $parentPlan->begin) dao::$errors['begin'] = sprintf($this->lang->productplan->beginLetterParent, $parentPlan->begin);
}
if($parentPlan->end != $this->config->productplan->future)
if($parentPlan->end !== $this->config->productplan->future)
{
if($plan->end !==$this->config->productplan->future and $plan->end > $parentPlan->end) dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $parentPlan->end);
if($plan->end !== $this->config->productplan->future and $plan->end > $parentPlan->end) dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $parentPlan->end);
}
}
elseif($oldPlan->parent == -1 and $plan->begin != $this->config->productplan->future)
@@ -496,25 +496,18 @@ class productplanModel extends model
if($maxEnd > $plan->end and $maxEnd != $this->config->productplan->future) dao::$errors['end'] = sprintf($this->lang->productplan->endLetterChildTip, $planID, $plan->end, $maxEnd);
}
$requiredFields = $oldPlan->status == 'wait' ? $this->config->productplan->edit->requiredFields : 'title, begin, end';
if($plan->status != 'wait' and $plan->begin == $this->config->productplan->future)
{
dao::$errors['begin'] = sprintf($this->lang->error->notempty, $this->lang->productplan->begin);
}
if($plan->status != 'wait' and $plan->end == $this->config->productplan->future)
{
dao::$errors['end'] = sprintf($this->lang->error->notempty, $this->lang->productplan->end);
}
if(dao::isError()) return false;
$plan = $this->loadModel('file')->processImgURL($plan, $this->config->productplan->editor->edit['id'], $this->post->uid);
$this->dao->update(TABLE_PRODUCTPLAN)
->data($plan)
->autoCheck()
->batchCheck($requiredFields, 'notempty')
->batchCheck($this->config->productplan->edit->requiredFields, 'notempty')
->checkIF(!$this->post->future && !empty($_POST['begin']) && !empty($_POST['end']), 'end', 'ge', $plan->begin)
->where('id')->eq((int)$planID)
->exec();
if(dao::isError()) return false;
if($oldPlan->parent > 0) $this->updateParentStatus($oldPlan->parent);
if(!dao::isError())
{
@@ -525,10 +518,10 @@ class productplanModel extends model
/**
* Start a plan.
*
* @param int $planID
*
* @param int $planID
* @access public
* @return array
* @return array
*/
public function start($planID)
{
@@ -554,7 +547,7 @@ class productplanModel extends model
if(dao::isError()) return false;
if($oldPlan->parent > 0) $this->updateParentStatus($oldPlan->parent, 'started');
if($oldPlan->parent > 0) $this->updateParentStatus($oldPlan->parent);
$this->file->updateObjectID($this->post->uid, $planID, 'productplan');
return common::createChanges($oldPlan, $plan);
@@ -567,7 +560,7 @@ class productplanModel extends model
* @param string $status doing|done|closed
* @param string $action started|finished|closed|activated
* @access public
* @return array
* @return bool
*/
public function updateStatus($planID, $status = '', $action = '')
{
@@ -584,7 +577,7 @@ class productplanModel extends model
$actionID = $this->loadModel('action')->create('productplan', $planID, $action);
$this->action->logHistory($actionID, $changes);
if($oldPlan->parent > 0) $this->updateParentStatus($oldPlan->parent, $action);
if($oldPlan->parent > 0) $this->updateParentStatus($oldPlan->parent);
return !dao::isError();
}
@@ -593,64 +586,48 @@ class productplanModel extends model
* Update a parent plan's status.
*
* @param int $parentID
* @param string $action started|finished|closed|activated
* @access public
* @return void
* @return bool
*/
public function updateParentStatus($parentID, $action)
public function updateParentStatus($parentID)
{
$parent = $this->getByID($parentID);
$childStatus = $this->dao->select('status')->from(TABLE_PRODUCTPLAN)->where('parent')->eq($parentID)->andWhere('deleted')->eq(0)->fetchPairs();
switch($action)
if(count($childStatus) == 1 and isset($childStatus['wait']))
{
case 'opened':
if($parent->status == 'done' or $parent->status == 'closed')
{
$parentStatus = 'doing';
$parentAction = 'createchild';
}
break;
case 'started':
if($parent->status != 'doing')
{
$parentStatus = 'doing';
$parentAction = $parent->status != 'doing' ? 'startedbychild' : '';
}
break;
case 'finished':
if(isset($childStatus['wait']) or isset($childStatus['doing'])) break;
if($parent->status != 'done')
{
$parentStatus = 'done';
$parentAction = 'finishedbychild';
}
break;
case 'closed':
if(count($childStatus) == 1 and $parent->status != 'closed')
{
$parentAction = 'closedbychild';
$parentStatus = 'closed';
}
break;
case 'activated':
if($parent->status != 'doing')
{
$parentStatus = 'doing';
$parentAction = $parent->status != 'doing' ? 'activatedbychild' : '';
}
break;
return;
}
elseif(count($childStatus) == 1 and isset($childStatus['closed']))
{
if($parent->status != 'closed')
{
$parentStatus = 'closed';
$parentAction = 'closedbychild';
}
}
elseif(!isset($childStatus['wait']) and !isset($childStatus['doing']))
{
if($parent->status != 'done')
{
$parentStatus = 'done';
$parentAction = 'finishedbychild';
}
}
else
{
if($parent->status != 'doing')
{
$parentStatus = 'doing';
$parentAction = $this->app->rawMethod == 'create' ? 'createchild' : 'activatedbychild';
}
}
if(!empty($parentStatus)) $this->dao->update(TABLE_PRODUCTPLAN)->set('status')->eq($parentStatus)->where('id')->eq($parentID)->exec();
if(!empty($parentAction)) $this->loadModel('action')->create('productplan', $parentID, $parentAction, '', $parentAction);
if(isset($parentStatus))
{
$this->dao->update(TABLE_PRODUCTPLAN)->set('status')->eq($parentStatus)->where('id')->eq($parentID)->exec();
$this->loadModel('action')->create('productplan', $parentID, $parentAction, '', $parentAction);
}
return !dao::isError();
}
@@ -684,21 +661,13 @@ class productplanModel extends model
$plan->begin = isset($data->begin[$planID]) ? $data->begin[$planID] : '';
$plan->end = isset($data->end[$planID]) ? $data->end[$planID] : '';
$plan->status = isset($data->status[$planID]) ? $data->status[$planID] : $oldPlans[$planID]->status;
$plan->parent = $oldPlans[$planID]->parent;
if(empty($plan->title)) die(js::alter(sprintf($this->lang->productplan->errorNoTitle, $planID)));
if($plan->begin > $plan->end and !empty($plan->end)) die(js::alert(sprintf($this->lang->productplan->beginGeEnd, $planID)));
if(!$isFuture and $plan->status != 'wait')
{
if($plan->begin == '') die(js::alert(sprintf($this->lang->productplan->errorNoBegin, $planID)));
if($plan->end == '') die(js::alert(sprintf($this->lang->productplan->errorNoEnd, $planID)));
if($plan->begin > $plan->end) die(js::alert(sprintf($this->lang->productplan->beginGeEnd, $planID)));
}
if($plan->begin == '' or $plan->end == '')
{
$plan->begin = $this->config->productplan->future;
$plan->end = $this->config->productplan->future;
}
if($plan->begin == '') $plan->begin = $this->config->productplan->future;
if($plan->end == '') $plan->end = $this->config->productplan->future;
foreach($extendFields as $extendField)
{
@@ -714,12 +683,13 @@ class productplanModel extends model
}
$changes = array();
$parents = array();
foreach($plans as $planID => $plan)
{
/*Determine whether the begin and end dates of the parent plan and the child plan are correct. */
if($oldPlans[$planID]->parent > 0)
$parentID = $oldPlans[$planID]->parent;
/* Determine whether the begin and end dates of the parent plan and the child plan are correct. */
if($parentID > 0)
{
$parentID = $oldPlans[$planID]->parent;
$parent = isset($plans[$parentID]) ? $plans[$parentID] : $this->getByID($parentID);
if($parent->begin != $this->config->productplan->future and $plan->begin != $this->config->productplan->future and $plan->begin < $parent->begin)
{
@@ -730,7 +700,7 @@ class productplanModel extends model
die(js::alert(sprintf($this->lang->productplan->endGreaterParentTip, $planID, $plan->end, $parent->end)));
}
}
elseif($oldPlans[$planID]->parent == -1 and $plan->begin != $this->config->productplan->future)
elseif($parentID == -1 and $plan->begin != $this->config->productplan->future)
{
$childPlans = $this->dao->select('*')->from(TABLE_PRODUCTPLAN)->where('parent')->eq($planID)->andWhere('deleted')->eq(0)->fetchAll('id');
$minBegin = $plan->begin;
@@ -748,12 +718,15 @@ class productplanModel extends model
$change = common::createChanges($oldPlans[$planID], $plan);
if($change)
{
if($parentID > 0 and !isset($parents[$parentID])) $parents[$parentID] = $parentID;
$this->dao->update(TABLE_PRODUCTPLAN)->data($plan)->autoCheck()->where('id')->eq($planID)->exec();
if(dao::isError()) die(js::error(dao::getError()));
$changes[$planID] = $change;
}
}
foreach($parents as $parent) $this->updateParentStatus($parent);
return $changes;
}
@@ -999,7 +972,7 @@ class productplanModel extends model
*/
public static function isClickable($plan, $action)
{
$action = strtolower($action);
$action = strtolower($action);
$clickable = commonModel::hasPriv('productplan', $action);
if(!$clickable) return false;
+9 -9
View File
@@ -38,28 +38,28 @@
</thead>
<tbody>
<?php foreach($plans as $plan):?>
<?php $isChecked = ($plan->begin == '2030-01-01' || $plan->end == '2030-01-01') ? 'checked="checked"' : '';?>
<?php $isChecked = ($plan->begin == '2030-01-01' or $plan->end == '2030-01-01') ? 'checked="checked"' : '';?>
<tr>
<td class='text-center'><?php echo $plan->id . html::hidden("id[$plan->id]", $plan->id);?></td>
<?php if($product->type != 'normal'):?>
<td class='text-left'>
<?php $disabled = $plan->parent == '-1' ? "disabled='disabled'" : '';?>
<?php $disabled = $plan->parent != -1 ? "disabled='disabled'" : '';?>
<?php echo html::select("branch[$plan->id]", $plan->parent == '-1' ? '' : $branchTagOption, $plan->branch, "onchange='getConflictStories($plan->id, this.value); 'class='form-control chosen' $disabled");?>
</td>
<?php endif;?>
<td title='<?php echo $plan->title?>'><?php echo html::input("title[$plan->id]", $plan->title, "class='form-control'")?></td>
<?php if($plan->parent != -1):?>
<td><?php echo html::select("status[$plan->id]", array_slice($lang->productplan->statusList,($plan->status == 'wait' ? 0 : 1)), $plan->status, "class='form-control chosen' onchange='setPlanStatus($plan->id, this.value)'");?></td>
<td><?php echo html::select("status[$plan->id]", array_slice($lang->productplan->statusList,($plan->status == 'wait' ? 0 : 1)), $plan->status, "class='form-control chosen' onchange='setPlanStatus($plan->id, this.value, $plan->parent)'");?></td>
<?php else:?>
<td><?php echo html::select("status[$plan->id]", array_slice($lang->productplan->statusList,($plan->status == 'wait' ? 0 : 1)), $plan->status, "class='form-control chosen' disabled onchange='setPlanStatus($plan->id, this.value)'");?></td>
<td><?php echo html::select("status[$plan->id]", array_slice($lang->productplan->statusList,($plan->status == 'wait' ? 0 : 1)), $plan->status, "class='form-control chosen' disabled onchange='setPlanStatus($plan->id, this.value, $plan->parent)'");?></td>
<?php endif;?>
<?php $required = $plan->status != 'wait' ? 'required' : '' ;?>
<?php $disabled = ($plan->begin == '2030-01-01' || $plan->end == '2030-01-01') ? 'disabled="disabled"' : '';?>
<?php $disabled = (($plan->begin == '2030-01-01' or $plan->end == '2030-01-01') and $plan->status == 'wait') ? 'disabled="disabled"' : '';?>
<?php if($plan->parent == -1 and ($plan->begin == '2030-01-01' or $plan->end == '2030-01-01')) $disabled = 'disabled="disabled"';?>
<?php if($plan->begin == '2030-01-01') $plan->begin = '';?>
<?php if($plan->end == '2030-01-01') $plan->end = '';?>
<td class=<?php echo $required;?>><?php echo html::input("begin[$plan->id]", $plan->begin, "class='form-control form-date' $disabled");?></td>
<td class=<?php echo $required;?>><?php echo html::input("end[$plan->id]", $plan->end, "class='form-control form-date' $disabled");?></td>
<?php $hidden = ($plan->status != 'wait') ? 'hidden' : '';?>
<td><?php echo html::input("begin[$plan->id]", $plan->begin, "class='form-control form-date' $disabled");?></td>
<td><?php echo html::input("end[$plan->id]", $plan->end, "class='form-control form-date' $disabled");?></td>
<?php $hidden = ($plan->status != 'wait' and $plan->parent != -1) ? 'hidden' : '';?>
<td><div class='checkbox-primary <?php echo $hidden;?>'><input type='checkbox' id="future<?php echo $plan->id; ?>" name='future[<?php echo $plan->id; ?>]' <?php echo $isChecked;?> onclick="changeDate(<?php echo $plan->id;?>);"/><label for='future<?php echo $plan->id; ?>'><?php echo $lang->productplan->future;?></label></div></td>
<?php foreach($extendFields as $extendField) echo "<td" . (($extendField->control == 'select' or $extendField->control == 'multi-select') ? " style='overflow:visible'" : '') . ">" . $this->loadModel('flow')->getFieldControl($extendField, $plan, $extendField->field . "[{$plan->id}]") . "</td>";?>
</tr>
+4 -5
View File
@@ -47,11 +47,10 @@
<?php endif;?>
</tr>
<tr>
<?php $required = $plan->status != 'wait' ? 'required' : '' ;?>
<?php $hidden = $plan->status != 'wait' ? 'hidden' : '';?>
<?php $checked = $plan->begin == '2030-01-01' || $plan->end == '2030-01-01' ? "checked='checked'" : '';?>
<?php $hidden = $plan->status != 'wait' ? 'hidden' : '';?>
<?php $checked = $plan->begin == '2030-01-01' || $plan->end == '2030-01-01' ? "checked='checked'" : '';?>
<th><?php echo $lang->productplan->begin;?></th>
<td><?php echo html::input('begin', $plan->begin != '2030-01-01' ? formatTime($plan->begin) : '', "class='form-control form-date' $required");?></td>
<td><?php echo html::input('begin', $plan->begin != '2030-01-01' ? formatTime($plan->begin) : '', "class='form-control form-date'");?></td>
<td>
<?php if($plan->status == 'wait'):?>
<div class='checkbox-primary <?php echo $hidden;?>' id='checkBox'>
@@ -63,7 +62,7 @@
</tr>
<tr>
<th><?php echo $lang->productplan->end;?></th>
<td><?php echo html::input('end', $plan->end != '2030-01-01' ? formatTime($plan->end) : '', "class='form-control form-date' $required");?></td>
<td><?php echo html::input('end', $plan->end != '2030-01-01' ? formatTime($plan->end) : '', "class='form-control form-date'");?></td>
<?php $deltaValue = $plan->end == '2030-01-01' ? 0 : (strtotime($plan->end) - strtotime($plan->begin)) / 3600 / 24 + 1;?>
<td colspan='2'><?php echo html::radio('delta', $lang->productplan->endList , $deltaValue, "onclick='computeEndDate(this.value)'");?></td>
</tr>
+2 -2
View File
@@ -593,7 +593,7 @@ class programModel extends model
->add('type', 'program')
->join('whitelist', ',')
->stripTags($this->config->program->editor->create['id'], $this->config->allowedTags)
->remove('delta,future')
->remove('delta,future,contactListMenu')
->get();
if($program->parent)
@@ -678,7 +678,7 @@ class programModel extends model
->setIF(!isset($this->post->budgetUnit), 'budgetUnit', $oldProgram->budgetUnit)
->join('whitelist', ',')
->stripTags($this->config->program->editor->edit['id'], $this->config->allowedTags)
->remove('uid,delta,future,syncPRJUnit,exchangeRate')
->remove('uid,delta,future,syncPRJUnit,exchangeRate,contactListMenu')
->get();
$program = $this->loadModel('file')->processImgURL($program, $this->config->program->editor->edit['id'], $this->post->uid);
+6 -3
View File
@@ -114,9 +114,12 @@
</tr>
<tr id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?></td>
<td></td>
<td></td>
<td colspan='2'>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<td colspan='4' class='text-center form-actions'>
+6 -3
View File
@@ -99,9 +99,12 @@
</tr>
<tr class="<?php if($program->acl == 'open') echo 'hidden';?>" id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, $program->whitelist, 'class="form-control chosen" multiple');?></td>
<td></td>
<td></td>
<td>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, $program->whitelist, 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<td colspan='4' class='text-center form-actions'>
+1 -1
View File
@@ -88,7 +88,7 @@ class programplan extends control
if($type == 'lists')
{
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->loadModel('datatable');
$plans = $this->programplan->getPlans($projectID, $this->productID, $sort);
}
+10 -5
View File
@@ -125,14 +125,19 @@ $config->project->datatable->fieldList['actions']['width'] = '180';
$config->project->datatable->fieldList['actions']['required'] = 'yes';
$config->project->datatable->fieldList['actions']['pri'] = '1';
$config->project->removePriv['project'] = array('index', 'browse', 'kanban', 'create', 'batchEdit', 'qa', 'updateOrder', 'createGuide', 'programTitle');
$config->project->removePriv['bug'] = array('batchChangePlan');
$config->project->removePriv['doc'] = array('catalog', 'index');
$config->project->removePriv['auditplan'] = array('delete');
$config->project->checkList = new stdclass();
$config->project->checkList->scrum = array('bug', 'execution', 'build', 'doc', 'release', 'testtask', 'case');
$config->project->checkList->waterfall = array('execution', 'design', 'doc', 'bug', 'case', 'build', 'release', 'testtask');
$config->project->maxCheckList = new stdclass();
$config->project->maxCheckList->scrum = array('bug', 'execution', 'build', 'doc', 'release', 'testtask', 'case', 'issue', 'risk');
$config->project->maxCheckList->waterfall = array('execution', 'design', 'doc', 'bug', 'case', 'build', 'release', 'testtask', 'review', 'build', 'researchplan', 'issue', 'risk', 'opportunity', 'auditplan', 'gapanalysis', 'meeting');
$config->project->removePriv['project'] = array('index', 'browse', 'kanban', 'create', 'batchEdit', 'qa', 'updateOrder', 'createGuide', 'programTitle', 'export');
$config->project->removePriv['bug'] = array('browse', 'batchChangePlan', 'batchCreate', 'batchEdit', 'batchConfirm', 'batchResolve', 'batchClose', 'batchActivate', 'report', 'batchChangeModule', 'batchChangeBranch');
$config->project->removePriv['testcase'] = array('browse', 'batchChangeModule', 'batchChangeBranch');
$config->project->removePriv['testtask'] = array('browse', 'view', 'start', 'activate', 'block', 'close');
$config->project->removePriv['doc'] = array('catalog', 'index');
$config->project->removePriv['repo'] = array('edit', 'delete', 'maintain', 'setRules');
$config->project->removePriv['testreport'] = array('browse');
$config->project->removePriv['auditplan'] = array('delete');
if(!isset($config->maxVersion)) $config->project->removePriv['stakeholder'] = array('issue', 'viewIssue', 'userIssue');
+4 -2
View File
@@ -808,7 +808,7 @@ class project extends control
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Set the pager. */
$this->app->loadClass('pager', $static = true);
@@ -925,7 +925,7 @@ class project extends control
/* Load pager and get bugs, user. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$bugs = $this->bug->getProjectBugs($projectID, $productID, $build, $type, $param, $sort, '', $pager);
$users = $this->user->getPairs('noletter');
@@ -1321,6 +1321,7 @@ class project extends control
$users = $this->user->getPairs('noclosed|nodeleted|devfirst|nofeedback');
$roles = $this->user->getUserRoles(array_keys($users));
$deptUsers = $dept === '' ? array() : $this->dept->getDeptUserPairs($dept);
$userInfos = $this->user->getUserDisplayInfos(array_keys($users), $dept);
$currentMembers = $this->project->getTeamMembers($projectID);
$members2Import = $this->project->getMembers2Import($copyProjectID, array_keys($currentMembers));
@@ -1331,6 +1332,7 @@ class project extends control
$this->view->project = $project;
$this->view->users = $users;
$this->view->deptUsers = $deptUsers;
$this->view->userInfos = $userInfos;
$this->view->roles = $roles;
$this->view->dept = $dept;
$this->view->depts = array('' => '') + $this->dept->getOptionMenu();
+7 -9
View File
@@ -36,7 +36,7 @@ function saveMembers()
$('#submit').removeClass('hidden');
if(result) $('#removeExecution').val('yes');
$('#submit').click();
})
});
}
}
@@ -50,9 +50,7 @@ function saveMembers()
*/
function setRole(account, roleID)
{
role = roles[account]; // get role according the account.
roleOBJ = $(document.getElementById('roles[' + roleID + ']')); // get role object.
roleOBJ.val(role) // set the role.
$('#roles\\[' + roleID + '\\]').val(roles[account]);
}
/**
@@ -65,11 +63,11 @@ function setRole(account, roleID)
function addItem(obj)
{
var item = $('#addItem').html().replace(/%i%/g, i);
$(obj).closest('tr').after('<tr class="addedItem">' + item + '</tr>');
var accounts = $(obj).closest('tr').next('tr').find('select:first');
accounts.trigger('liszt:updated');
accounts.chosen();
i ++;
var $tr = $('<tr class="addedItem">' + item + '</tr>').insertAfter($(obj).closest('tr'));
$tr.find('select:first')
.trigger('liszt:updated')
.picker({type: 'user'});
i++;
}
/**
+22 -7
View File
@@ -772,7 +772,7 @@ class projectModel extends model
->add('type', 'project')
->join('whitelist', ',')
->stripTags($this->config->project->editor->create['id'], $this->config->allowedTags)
->remove('products,branch,plans,delta,newProduct,productName,future')
->remove('products,branch,plans,delta,newProduct,productName,future,contactListMenu')
->get();
$linkedProductsCount = 0;
@@ -874,9 +874,24 @@ class projectModel extends model
/* Create doc lib. */
$this->app->loadLang('doc');
$authorizedUsers = array();
$authorizedUsers = $project->whitelist . ',' . $project->PM . ',' . $project->openedBy . ',';
$authorizedUsers = array_unique(explode(',', trim($authorizedUsers, ',')));
if($project->parent and $project->acl == 'program')
{
$stakeHolders = $this->loadModel('stakeholder')->getStakeHolderPairs($project->parent);
foreach($stakeHolders as $stakeHolder) $authorizedUsers[$stakeHolder] = $stakeHolder;
foreach(explode(',', $project->whitelist) as $white)
{
if(empty($white)) continue;
$authorizedUsers[$white] = $white;
}
$authorizedUsers[$project->PM] = $project->PM;
$authorizedUsers[$project->openedBy] = $project->openedBy;
$authorizedUsers[$program->PM] = $program->PM;
$authorizedUsers[$program->openedBy] = $program->openedBy;
}
$lib = new stdclass();
$lib->project = $projectID;
@@ -884,7 +899,7 @@ class projectModel extends model
$lib->type = 'project';
$lib->main = '1';
$lib->acl = $project->acl != 'program' ? $project->acl : 'custom';
if($project->acl == 'private') $lib->users = ',' . implode(',', $authorizedUsers) . ',';
$lib->users = ',' . implode(',', array_filter($authorizedUsers)) . ',';
$this->dao->insert(TABLE_DOCLIB)->data($lib)->exec();
$this->updateProducts($projectID);
@@ -985,7 +1000,7 @@ class projectModel extends model
->setIF($this->post->budget != 0, 'budget', round($this->post->budget, 2))
->join('whitelist', ',')
->stripTags($this->config->project->editor->edit['id'], $this->config->allowedTags)
->remove('products,branch,plans,delta,future')
->remove('products,branch,plans,delta,future,contactListMenu')
->get();
if($project->parent)
@@ -2079,8 +2094,8 @@ class projectModel extends model
$this->lang->switcherMenu = $this->getSwitcher($objectID, $this->app->rawModule, $this->app->rawMethod);
/* Reset project priv. */
$moduleName = $this->app->getModuleName();
$methodName = $this->app->getMethodName();
$moduleName = $this->app->rawModule;
$methodName = $this->app->rawMethod;
$this->loadModel('common')->resetProjectPriv($objectID);
if(!$this->common->isOpenMethod($moduleName, $methodName) and !commonModel::hasPriv($moduleName, $methodName)) $this->common->deny($moduleName, $methodName, false);
+6 -3
View File
@@ -159,9 +159,12 @@
</tr>
<tr class="hidden" id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?></td>
<td></td>
<td></td>
<td colspan='2'>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, '', 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->project->auth;?></th>
+1 -2
View File
@@ -36,7 +36,6 @@
<script>
$('.createButton').on('click', function()
{
$('#guideDialog').modal('hide');
window.location.reload();
$.closeModal();
});
</script>
+6 -3
View File
@@ -176,9 +176,12 @@
</tr>
<tr class="<?php if($project->acl == 'open') echo 'hidden';?>" id="whitelistBox">
<th><?php echo $lang->whitelist;?></th>
<td><?php echo html::select('whitelist[]', $users, $project->whitelist, 'class="form-control chosen" multiple');?></td>
<td></td>
<td></td>
<td>
<div class='input-group'>
<?php echo html::select('whitelist[]', $users, $project->whitelist, 'class="form-control chosen" multiple');?>
<?php echo $this->fetch('my', 'buildContactLists', "dropdownName='whitelist'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->project->auth;?></th>
+3 -2
View File
@@ -2,6 +2,7 @@
<?php js::set('projectID', $project->id);?>
<?php js::set('roles', $roles);?>
<?php js::set('deptID', $dept);?>
<?php js::set('pickerUsers', $userInfos);?>
<?php js::set('copyProjectID', $copyProjectID);?>
<?php js::set('oldAccountList', array_keys($currentMembers));?>
<?php js::set('unlinkExecutionMembers', $lang->project->unlinkExecutionMembers);?>
@@ -75,7 +76,7 @@
<?php foreach($members2Import as $member2Import):?>
<?php if(!isset($users[$member2Import->account])) continue;?>
<tr class='addedItem'>
<td><?php echo html::select("accounts[$i]", $users, $member2Import->account, "class='form-control chosen' onchange='setRole(this.value, $i)'");?></td>
<td><?php echo html::select("accounts[$i]", $users, $member2Import->account, "class='form-control user-picker' onchange='setRole(this.value, $i)'");?></td>
<td><?php echo html::input("roles[$i]", $member2Import->role, "class='form-control'");?></td>
<td><?php echo html::input("days[$i]", $project->days, "class='form-control'");?></td>
<td><?php echo html::input("hours[$i]", $member2Import->hours, "class='form-control'");?></td>
@@ -91,7 +92,7 @@
<?php for($j = 0; $j < 5; $j ++):?>
<tr class='addedItem'>
<td><?php echo html::select("accounts[$i]", $users, '', "class='form-control chosen' onchange='setRole(this.value, $i)'");?></td>
<td><?php echo html::select("accounts[$i]", $users, '', "class='form-control user-picker' onchange='setRole(this.value, $i)'");?></td>
<td><?php echo html::input("roles[$i]", '', "class='form-control'");?></td>
<td><?php echo html::input("days[$i]", $project->days, "class='form-control'");?></td>
<td><?php echo html::input("hours[$i]", $config->execution->defaultWorkhours, "class='form-control'");?></td>
+1 -1
View File
@@ -14,7 +14,7 @@
<?php js::set('confirmDelete', $lang->stakeholder->confirmDelete);?>
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<?php echo html::a($this->createLink('stakeholder', 'browse', 'browseType=all'), '<span class="text">' . $lang->stakeholder->browse . '</span>', '', 'class="btn btn-link btn-active-text"');?>
<?php echo html::a($this->createLink('stakeholder', 'browse', "projectID=$projectID"), '<span class="text">' . $lang->stakeholder->browse . '</span>', '', 'class="btn btn-link btn-active-text"');?>
</div>
<div class="btn-toolbar pull-right">
<?php if(common::hasPriv('stakeholder', 'batchcreate') and common::hasPriv('stakeholder', 'create')):?>
+1 -1
View File
@@ -1766,7 +1766,7 @@ class story extends control
}
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->view->title = $this->lang->story->zeroCase;
$this->view->position[] = html::a($this->createLink('testcase', 'browse', "productID=$productID"), $products[$productID]);
+4 -3
View File
@@ -2473,8 +2473,9 @@ class storyModel extends model
*/
public function getBySearch($productID, $branch = '', $queryID = 0, $orderBy = '', $executionID = '', $type = 'story', $excludeStories = '', $pager = null)
{
$this->loadModel('product');
$executionID = empty($executionID) ? 0 : $executionID;
$products = $this->loadModel('product')->getProducts($executionID);
$products = empty($executionID) ? $this->product->getList() : $this->product->getProducts($executionID);
$query = $queryID ? $this->loadModel('search')->getQuery($queryID) : '';
@@ -2734,7 +2735,7 @@ class storyModel extends model
* @access public
* @return array
*/
public function getExecutionStoryPairs($executionID = 0, $productID = 0, $branch = 0, $moduleIdList = 0, $type = 'full', $status = 'all')
public function getExecutionStoryPairs($executionID = 0, $productID = 0, $branch = 'all', $moduleIdList = 0, $type = 'full', $status = 'all')
{
if(defined('TUTORIAL')) return $this->loadModel('tutorial')->getExecutionStoryPairs();
$stories = $this->dao->select('t2.id, t2.title, t2.module, t2.pri, t2.estimate, t3.name AS product')
@@ -4469,7 +4470,7 @@ class storyModel extends model
public function sortStoriesOfPlan($planID, $sortIDList, $orderBy = 'id_desc', $pageID = 1, $recPerPage = 100)
{
/* Append id for secend sort. */
$orderBy = $this->loadModel('common')->appendOrder($orderBy);
$orderBy = common::appendOrder($orderBy);
/* Get all stories by plan. */
$stories = $this->getPlanStories($planID, 'all', $orderBy);
+2 -1
View File
@@ -481,8 +481,9 @@
{
if(!isset($executions[$task->execution])) continue;
$executionName = $executions[$task->execution];
$taskInfo = $task->id . '&nbsp<span class="label label-success label-outline">' . $this->lang->task->statusList[$task->status] . '</span>&nbsp' . $task->name;
$class = isonlybody() ? 'showinonlybody' : 'iframe';
echo "<li title='$task->name'>" . html::a($this->createLink('task', 'view', "taskID=$task->id", '', true), "[T]$task->id $task->name", '', "class=$class data-width='80%'");
echo "<li title='$task->name'>" . html::a($this->createLink('task', 'view', "taskID=$task->id", '', true), $taskInfo, '', "class=$class data-width='80%'");
echo html::a($this->createLink('execution', 'browse', "executionID=$task->execution"), $executionName, '', "class='text-muted'") . '</li>';
}
}
+1 -1
View File
@@ -1481,7 +1481,7 @@ class task extends control
$taskLang = $this->lang->task;
/* Create field lists. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $allExportFields);
foreach($fields as $key => $fieldName)
{
+2 -2
View File
@@ -137,7 +137,7 @@ class testcase extends control
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Get test cases. */
$cases = $this->testcase->getTestCases($productID, $branch, $browseType, $browseType == 'bysearch' ? $queryID : $suiteID, $moduleID, $sort, $pager);
@@ -480,7 +480,7 @@ class testcase extends control
$this->view->executionID = $executionID;
$this->view->productInfo = $productInfo;
$this->view->productName = $this->products[$productID];
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch === 'all' ? 0 : $branch);
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch);
$this->view->currentModuleID = $currentModuleID;
$this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('testcase', 'browse', "productID=$productID") : '';
$this->view->stories = $stories;
+17 -15
View File
@@ -70,25 +70,27 @@ $lang->testreport->bugStageList['generated'] = 'Generated Bugs';
$lang->testreport->bugStageList['legacy'] = 'Legacy Bugs';
$lang->testreport->bugStageList['resolved'] = 'Resolved Bugs';
$lang->testreport->caseSummary = 'Total <strong>%s</strong> cases. <strong>%s</strong> cases run. <strong>%s</strong> results generated. <strong>%s</strong> cases failed.';
$lang->testreport->buildSummary = 'Tested <strong>%s</strong> builds.';
$lang->testreport->confirmDelete = 'Do you want to delete this report?';
$lang->testreport->moreNotice = 'More features can be extended with reference to the ZenTao extension manual, or you can contact us at renee@easysoft.ltd for customization.';
$lang->testreport->exportNotice = "Exported By <a href='https://www.zentao.net' target='_blank' style='color:grey'>ZenTao</a>";
$lang->testreport->noReport = "No report has been generated. Please check it later.";
$lang->testreport->foundBugTip = "Bugs found in this build period and the affected build is in this test period.";
$lang->testreport->legacyBugTip = "Active bugs, or bugs that are not resolved in the test period.";
$lang->testreport->fromCaseBugTip = "Bugs found from the running of cases in the test period.";
$lang->testreport->errorTrunk = "You cannot create a Testing report for the trunk. Please modify the linked build!";
$lang->testreport->noTestTask = "No test requests for this {$lang->productCommon}, so no reports can be generated. Please go to {$lang->productCommon} which has test requests and then generate the report.";
$lang->testreport->noObjectID = "No test request or {$lang->executionCommon} is selected, so no report can be generated.";
$lang->testreport->moreProduct = "Testing reports can only be generated for the same {$lang->productCommon}.";
$lang->testreport->hiddenCase = "Hide %s use cases";
$lang->testreport->goalTip = "Descriptive information about the {$lang->execution->common} of this build";
$lang->testreport->caseSummary = 'Total <strong>%s</strong> cases. <strong>%s</strong> cases run. <strong>%s</strong> results generated. <strong>%s</strong> cases failed.';
$lang->testreport->buildSummary = 'Tested <strong>%s</strong> builds.';
$lang->testreport->confirmDelete = 'Do you want to delete this report?';
$lang->testreport->moreNotice = 'More features can be extended with reference to the ZenTao extension manual, or you can contact us at renee@easysoft.ltd for customization.';
$lang->testreport->exportNotice = "Exported By <a href='https://www.zentao.net' target='_blank' style='color:grey'>ZenTao</a>";
$lang->testreport->noReport = "No report has been generated. Please check it later.";
$lang->testreport->foundBugTip = "Bugs found in this build period and the affected build is in this test period.";
$lang->testreport->legacyBugTip = "Active bugs, or bugs that are not resolved in the test period.";
$lang->testreport->activatedBugTip = "Reactive bugs in the test period.";
$lang->testreport->fromCaseBugTip = "Bugs found from the running of cases in the test period.";
$lang->testreport->errorTrunk = "You cannot create a Testing report for the trunk. Please modify the linked build!";
$lang->testreport->noTestTask = "No test requests for this {$lang->productCommon}, so no reports can be generated. Please go to {$lang->productCommon} which has test requests and then generate the report.";
$lang->testreport->noObjectID = "No test request or {$lang->executionCommon} is selected, so no report can be generated.";
$lang->testreport->moreProduct = "Testing reports can only be generated for the same {$lang->productCommon}.";
$lang->testreport->hiddenCase = "Hide %s use cases";
$lang->testreport->goalTip = "Descriptive information about the {$lang->execution->common} of this build";
$lang->testreport->bugSummary = <<<EOD
Total <strong>%s</strong> Bugs reported <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->foundBugTip}'><i class='icon-help'></i></a>,
<strong>%s</strong> Bugs remained unresolved <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->legacyBugTip}'><i class='icon-help'></i></a>,
<strong>%s</strong> Bugs reactivated <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->activatedBugTip}'><i class='icon-help'></i></a>,
<strong>%s</strong> Bugs found from the running of cases<a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->fromCaseBugTip}'><i class='icon-help'></i></a>.
Bug Effective Rate <a data-toggle='tooltip' class='text-warning' title='Resolution is resolved or delayed / status is resolved or closed'><i class='icon-help'></i></a>: <strong>%s</strong>,Bugs-reported-from-cases rate<a data-toggle='tooltip' class='text-warning' title='Bugs created from cases / bugs'><i class='icon-help'></i></a>: <strong>%s</strong>
EOD;
+17 -15
View File
@@ -70,25 +70,27 @@ $lang->testreport->bugStageList['generated'] = '产生的Bug';
$lang->testreport->bugStageList['legacy'] = '遗留的Bug';
$lang->testreport->bugStageList['resolved'] = '解决的Bug';
$lang->testreport->caseSummary = '共有<strong>%s</strong>个用例,共执行<strong>%s</strong>个用例,产生了<strong>%s</strong>个结果,失败的用例有<strong>%s</strong>个。';
$lang->testreport->buildSummary = '共测试了<strong>%s</strong>个版本。';
$lang->testreport->confirmDelete = '是否删除该报告?';
$lang->testreport->moreNotice = '更多功能可以参考禅道扩展机制进行扩展,也可以联系我们进行定制。';
$lang->testreport->exportNotice = "由<a href='https://www.zentao.net' target='_blank' style='color:grey'>禅道项目管理软件</a>导出";
$lang->testreport->noReport = "暂无报告,请选择测试单生成测试报告。";
$lang->testreport->foundBugTip = "影响版本在测试轮次内,并且创建时间在测试时间范围内产生的Bug数。";
$lang->testreport->legacyBugTip = "Bug状态是激活,或Bug的解决时间在测试结束时间之后。";
$lang->testreport->fromCaseBugTip = "测试时间范围内,用例执行失败后创建的Bug。";
$lang->testreport->errorTrunk = "主干版本不能创建测试报告,请修改关联版本!";
$lang->testreport->noTestTask = "该{$lang->productCommon}下还没有关联非Trunk的测试单,不能创建报告。请先创建测试单,再创建。";
$lang->testreport->noObjectID = "没有选定测试单或{$lang->executionCommon},无法创建测试报告!";
$lang->testreport->moreProduct = "只能对同一个{$lang->productCommon}生成测试报告。";
$lang->testreport->hiddenCase = "隐藏 %s 个用例";
$lang->testreport->goalTip = "该版本所属{$lang->execution->common}的描述信息";
$lang->testreport->caseSummary = '共有<strong>%s</strong>个用例,共执行<strong>%s</strong>个用例,产生了<strong>%s</strong>个结果,失败的用例有<strong>%s</strong>个。';
$lang->testreport->buildSummary = '共测试了<strong>%s</strong>个版本。';
$lang->testreport->confirmDelete = '是否删除该报告?';
$lang->testreport->moreNotice = '更多功能可以参考禅道扩展机制进行扩展,也可以联系我们进行定制。';
$lang->testreport->exportNotice = "由<a href='https://www.zentao.net' target='_blank' style='color:grey'>禅道项目管理软件</a>导出";
$lang->testreport->noReport = "暂无报告,请选择测试单生成测试报告。";
$lang->testreport->foundBugTip = "影响版本在测试轮次内,并且创建时间在测试时间范围内产生的Bug数。";
$lang->testreport->legacyBugTip = "Bug状态是激活,或Bug的解决时间在测试结束时间之后。";
$lang->testreport->activatedBugTip = "在测试单时间范围内被激活的Bug数量。";
$lang->testreport->fromCaseBugTip = "测试时间范围内,用例执行失败后创建的Bug。";
$lang->testreport->errorTrunk = "主干版本不能创建测试报告,请修改关联版本!";
$lang->testreport->noTestTask = "该{$lang->productCommon}下还没有关联非Trunk的测试单,不能创建报告。请先创建测试单,再创建。";
$lang->testreport->noObjectID = "没有选定测试单或{$lang->executionCommon},无法创建测试报告!";
$lang->testreport->moreProduct = "只能对同一个{$lang->productCommon}生成测试报告。";
$lang->testreport->hiddenCase = "隐藏 %s 个用例";
$lang->testreport->goalTip = "该版本所属{$lang->execution->common}的描述信息";
$lang->testreport->bugSummary = <<<EOD
共发现<strong>%s</strong>个Bug <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->foundBugTip}'><i class='icon-help'></i></a>,
遗留<strong>%s</strong>个Bug <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->legacyBugTip}'><i class='icon-help'></i></a>。
重新激活<strong>%s</strong>个Bug <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->activatedBugTip}'><i class='icon-help'></i></a>。
用例执行产生<strong>%s</strong>个Bug <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->fromCaseBugTip}'><i class='icon-help'></i></a>。
有效Bug率(方案为已解决或延期 / 状态为已解决或已关闭):<strong>%s</strong>,用例发现Bug率(用例创建的Bug / 发现Bug数):<strong>%s</strong>
EOD;
+45
View File
@@ -141,6 +141,7 @@ class testreportModel extends model
$resolvedBugs = $this->dao->select('*')->from(TABLE_BUG)->where('product')->in($productIdList)->andWhere('resolvedDate')->ge($begin)->andWhere('resolvedDate')->le("$end 23:59:59")->andWhere('deleted')->eq(0)->fetchAll();
$foundBugs = array();
$legacyBugs = array();
$activatedBugs = array();
$byCaseNum = 0;
$buildIdList = array_keys($builds);
$taskIdList = array_keys($tasks);
@@ -168,6 +169,48 @@ class testreportModel extends model
$handleGroups['resolved'][$date] = 0;
}
$buildBugs = array();
$allBugs = $this->dao->select('*')->from(TABLE_BUG)->where('product')->in($productIdList)->andWhere('deleted')->eq(0)->fetchAll('id');
foreach($allBugs as $bug)
{
if(!empty(array_intersect(explode(',', $bug->openedBuild), $buildIdList))) $buildBugs[$bug->id] = $bug;
}
/* Get bug reactivated actions during the testreport. */
$actions = $this->dao->select('*')->from(TABLE_ACTION)
->where('objectType')->eq('bug')
->andWhere('action')->eq('activated')
->andWhere('date')->ge($begin)
->andWhere('date')->le($end . ' 23:59:59')
->andWhere('objectID')->in(array_keys($buildBugs))
->fetchGroup('objectID', 'id');
$actionIdList = array();
foreach($actions as $bugID => $action) $actionIdList = array_merge($actionIdList, array_keys($action));
$histories = $this->loadModel('action')->getHistory($actionIdList);
foreach($actions as $bugID => $actionList)
{
foreach($actionList as $actionID => $action)
{
$action->history = zget($histories, $actionID, array());
}
}
foreach($buildBugs as $bug)
{
$bugActions = zget($actions, $bug->id, array());
foreach($bugActions as $action)
{
foreach($action->history as $history)
{
if($history->field == 'openedBuild' and !in_array($history->new, $buildIdList)) continue;
$activatedBugs[$bug->id] = $bug;
}
}
}
/* Get the resolved bug data. */
foreach($resolvedBugs as $bug)
{
@@ -216,6 +259,7 @@ class testreportModel extends model
}
$resolvedBugs = 0;
$this->loadModel('action');
foreach($foundBugs as $bug)
{
$severityGroups[$bug->severity] = isset($severityGroups[$bug->severity]) ? $severityGroups[$bug->severity] + 1 : 1;
@@ -231,6 +275,7 @@ class testreportModel extends model
$bugInfo['foundBugs'] = count($foundBugs);
$bugInfo['legacyBugs'] = $legacyBugs;
$bugInfo['activatedBugs'] = $activatedBugs;
$bugInfo['countBugByTask'] = $byCaseNum;
$bugInfo['bugConfirmedRate'] = empty($resolvedBugs) ? 0 : round((zget($resolutionGroups, 'fixed', 0) + zget($resolutionGroups, 'postponed', 0)) / $resolvedBugs * 100, 2);
$bugInfo['bugCreateByCaseRate'] = empty($byCaseNum) ? 0 : round($byCaseNum / count($foundBugs) * 100, 2);
+2 -2
View File
@@ -83,8 +83,8 @@
<?php
echo '<div>' . $storySummary . '</div>';
echo '<div>' . sprintf($lang->testreport->buildSummary, empty($builds) ? 1 : count($builds)) . $caseSummary . '</div>';
echo '<div>' . sprintf($lang->testreport->bugSummary, $bugInfo['foundBugs'], count($legacyBugs), $bugInfo['countBugByTask'], $bugInfo['bugConfirmedRate'] . '%', $bugInfo['bugCreateByCaseRate'] . '%') . '</div>';
unset($bugInfo['countBugByTask']); unset($bugInfo['bugConfirmedRate']); unset($bugInfo['bugCreateByCaseRate']); unset($bugInfo['foundBugs']);
echo '<div>' . sprintf($lang->testreport->bugSummary, $bugInfo['foundBugs'], count($legacyBugs), count($bugInfo['activatedBugs']), $bugInfo['countBugByTask'], $bugInfo['bugConfirmedRate'] . '%', $bugInfo['bugCreateByCaseRate'] . '%') . '</div>';
unset($bugInfo['countBugByTask']); unset($bugInfo['bugConfirmedRate']); unset($bugInfo['bugCreateByCaseRate']); unset($bugInfo['foundBugs']); unset($bugInfo['activatedBugs']);
?>
</td>
<td></td>
+2 -2
View File
@@ -71,8 +71,8 @@
<?php
echo '<p>' . $storySummary . '</p>';
echo '<p>' . sprintf($lang->testreport->buildSummary, empty($builds) ? 1 : count($builds)) . $caseSummary . '</p>';
echo '<p>' . sprintf($lang->testreport->bugSummary, $bugInfo['foundBugs'], count($legacyBugs), $bugInfo['countBugByTask'], $bugInfo['bugConfirmedRate'] . '%', $bugInfo['bugCreateByCaseRate'] . '%') . '</p>';
unset($bugInfo['countBugByTask']); unset($bugInfo['bugConfirmedRate']); unset($bugInfo['bugCreateByCaseRate']); unset($bugInfo['foundBugs']);
echo '<p>' . sprintf($lang->testreport->bugSummary, $bugInfo['foundBugs'], count($legacyBugs), count($bugInfo['activatedBugs']), $bugInfo['countBugByTask'], $bugInfo['bugConfirmedRate'] . '%', $bugInfo['bugCreateByCaseRate'] . '%') . '</p>';
unset($bugInfo['countBugByTask']); unset($bugInfo['bugConfirmedRate']); unset($bugInfo['bugCreateByCaseRate']); unset($bugInfo['foundBugs']); unset($bugInfo['activatedBugs']);
?>
</td>
<td></td>
+2 -2
View File
@@ -78,8 +78,8 @@
<?php
echo '<p>' . $storySummary . '</p>';
echo '<p>' . sprintf($lang->testreport->buildSummary, empty($builds) ? 1 : count($builds)) . $caseSummary . '</p>';
echo '<p>' . sprintf($lang->testreport->bugSummary, $bugInfo['foundBugs'], count($legacyBugs), $bugInfo['countBugByTask'], $bugInfo['bugConfirmedRate'] . '%', $bugInfo['bugCreateByCaseRate'] . '%') . '</p>';
unset($bugInfo['countBugByTask']); unset($bugInfo['bugConfirmedRate']); unset($bugInfo['bugCreateByCaseRate']); unset($bugInfo['foundBugs']);
echo '<p>' . sprintf($lang->testreport->bugSummary, $bugInfo['foundBugs'], count($legacyBugs), count($bugInfo['activatedBugs']), $bugInfo['countBugByTask'], $bugInfo['bugConfirmedRate'] . '%', $bugInfo['bugCreateByCaseRate'] . '%') . '</p>';
unset($bugInfo['countBugByTask']); unset($bugInfo['bugConfirmedRate']); unset($bugInfo['bugCreateByCaseRate']); unset($bugInfo['foundBugs']); unset($bugInfo['activatedBugs']);
?>
</td>
</tr>
+2 -2
View File
@@ -71,7 +71,7 @@ class testsuite extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$suites = $this->testsuite->getSuites($productID, $sort, $pager);
if(empty($suites) and $pageID > 1)
@@ -169,7 +169,7 @@ class testsuite extends control
$this->session->set('caseList', $this->app->getURI(true), 'qa');
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
+5 -3
View File
@@ -106,7 +106,7 @@ class testtask extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Get tasks. */
$tasks = $this->testtask->getProductTasks($productID, $branch, $sort, $pager, $scopeAndStatus, $beginTime, $endTime);
@@ -178,7 +178,7 @@ class testtask extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testtask->common;
$this->view->position[] = html::a($this->createLink('testtask', 'browseUnits', "productID=$productID"), $this->products[$productID]);
@@ -503,7 +503,7 @@ class testtask extends control
}
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy, 't2.id');
$sort = common::appendOrder($orderBy, 't2.id');
/* Get test cases. */
$runs = $this->testtask->getTaskCases($productID, $browseType, $queryID, $moduleID, $sort, $pager, $task);
@@ -1314,6 +1314,8 @@ class testtask extends control
*/
public function results($runID, $caseID = 0, $version = 0)
{
if($this->app->tab == 'project') $this->loadModel('project')->setMenu($this->session->project);
if($runID)
{
$case = $this->testtask->getRunById($runID)->case;
+1 -1
View File
@@ -120,7 +120,7 @@
<?php endif; endif; $childId++;?>
</tr>
<?php endforeach;?>
<?php if($result->caseResult == 'fail'):?>
<?php if($result->caseResult == 'fail' and common::hasPriv('testcase', 'createBug')):?>
<tr>
<td></td><td></td><td></td><td></td><td></td><td></td>
<td><?php echo html::commonButton($lang->testcase->createBug, "onclick='createBug(this)'", "btn btn-primary createBtn");?></td>
+3 -1
View File
@@ -243,7 +243,7 @@ class treeModel extends model
{
/* If createdVersion <= 4.1, go to getOptionMenu(). */
$products = $this->loadModel('product')->getProductPairsByProject($rootID);
$branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products));
$branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products), 'noclosed');
if(!$this->isMergeModule($rootID, 'task') or !$products) return $this->getOptionMenu($rootID, 'task', $startModule);
@@ -277,10 +277,12 @@ class treeModel extends model
{
foreach($rootModules as $id => $rootModule)
{
$activeBranch = isset($branchGroups[$id]) ? array_keys($branchGroups[$id]) : array();
if($type == 'product')
{
$modules = $this->dao->select('*')->from(TABLE_MODULE)->where("((root = '" . (int)$rootID . "' and type = 'task' and parent != 0) OR (root = $id and type = 'story'))")
->beginIF($startModulePath)->andWhere('path')->like($startModulePath)->fi()
->beginIF(!empty($activeBranch))->andWhere('branch')->in($activeBranch)->fi()
->andWhere('deleted')->eq(0)
->orderBy('grade desc, branch, `order`, type')
->fetchAll('id');
+22 -6
View File
@@ -395,7 +395,7 @@ class upgrade extends control
$data->project = $_POST['projectType'] == 'execution' ? $projectList : $projectList[$projectProduct->project];
$data->product = $projectProduct->product;
$data->plan = $projectProduct->plan;
$data->branch = $projectProduct->branch;
$data->branch = $projectProduct->branch;
$this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec();
}
@@ -406,7 +406,7 @@ class upgrade extends control
/* Get no merged product and project count. */
$noMergedProductCount = $this->dao->select('count(*) as count')->from(TABLE_PRODUCT)->where('program')->eq(0)->fetch('count');
$noMergedSprintCount = $this->dao->select('count(*) as count')->from(TABLE_PROJECT)->where('grade')->eq(0)->andWhere('path')->eq('')->andWhere('type')->ne('program')->fetch('count');
$noMergedSprintCount = $this->dao->select('count(*) as count')->from(TABLE_PROJECT)->where('grade')->eq(0)->andWhere('path')->eq('')->andWhere('type')->ne('program')->andWhere('deleted')->eq(0)->fetch('count');
/* When all products and projects merged then finish and locate afterExec page. */
if(empty($noMergedProductCount) and empty($noMergedSprintCount))
@@ -443,6 +443,7 @@ class upgrade extends control
$noMergedSprints = $this->dao->select('t1.*')->from(TABLE_PROJECT)->alias('t1')
->leftJoin(TABLE_PROJECTPRODUCT)->alias('t2')->on('t1.id=t2.project')
->where('t1.project')->eq(0)
->andWhere('t1.deleted')->eq(0)
->andWhere('t1.type')->eq('sprint')
->andWhere('t2.product')->in(array_keys($noMergedProducts))
->orderBy('t1.id_desc')
@@ -487,6 +488,7 @@ class upgrade extends control
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
->where('t2.model')->eq('')
->andWhere('t2.project')->eq(0)
->andWhere('t2.deleted')->eq(0)
->andWhere('t2.type')->eq('sprint')
->fetchAll('id');
@@ -531,7 +533,13 @@ class upgrade extends control
/* Get no merged projects than is not linked product. */
if($type == 'sprint')
{
$noMergedSprints = $this->dao->select('*')->from(TABLE_PROJECT)->where('parent')->eq(0)->andWhere('path')->eq('')->orderBy('id_desc')->fetchAll('id');
$noMergedSprints = $this->dao->select('*')->from(TABLE_PROJECT)
->where('parent')->eq(0)
->andWhere('path')->eq('')
->andWhere('deleted')->eq(0)
->orderBy('id_desc')
->fetchAll('id');
$projectProducts = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)->where('project')->in(array_keys($noMergedSprints))->fetchGroup('project', 'product');
foreach($projectProducts as $sprintID => $products) unset($noMergedSprints[$sprintID]);
@@ -543,8 +551,16 @@ class upgrade extends control
/* Get no merged projects that link more then two products. */
if($type == 'moreLink')
{
$noMergedSprints = $this->dao->select('*')->from(TABLE_PROJECT)->where('parent')->eq(0)->andWhere('path')->eq('')->orderBy('id_desc')->fetchAll('id');
$projectProducts = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)->where('project')->in(array_keys($noMergedSprints))->fetchGroup('project', 'product');
$noMergedSprints = $this->dao->select('*')->from(TABLE_PROJECT)
->where('parent')->eq(0)
->andWhere('path')->eq('')
->andWhere('deleted')->eq(0)
->orderBy('id_desc')
->fetchAll('id');
$projectProducts = $this->dao->select('*')->from(TABLE_PROJECTPRODUCT)
->where('project')->in(array_keys($noMergedSprints))
->fetchGroup('project', 'product');
$productPairs = array();
foreach($projectProducts as $sprintID => $products)
@@ -591,7 +607,7 @@ class upgrade extends control
* Rename object in upgrade.
*
* @param string $type project|product|execution
* @param string $duplicateList
* @param string $duplicateList
* @access public
* @return void
*/
+2
View File
@@ -0,0 +1,2 @@
#selectedModeTips {padding-top: 10px}
#upgradeTips {padding-bottom: 10px;}
+1
View File
@@ -126,6 +126,7 @@ $lang->upgrade->mergeRepoTips = "Merge the selected version library under t
$lang->upgrade->needBuild4Add = 'Full text retrieval has been added in this upgrade. Need create index. Please go [Admin->System->BuildIndex] page to build index.';
$lang->upgrade->errorEngineInnodb = 'Your MySQL does not support InnoDB data table engine. Please modify it to MyISAM and try again.';
$lang->upgrade->duplicateProject = "Project name in the same program cannot be duplicate. Please adjust the duplicate names.";
$lang->upgrade->upgradeTips = "Historically deleted data cannot be upgraded, and restoration is not supported after the upgrade. Please be aware.";
$lang->upgrade->projectType['project'] = "Upgrade the historical {$lang->projectCommon} as a project";
$lang->upgrade->projectType['execution'] = "Upgrade the historical {$lang->projectCommon} as an execution";
+1
View File
@@ -126,6 +126,7 @@ $lang->upgrade->mergeRepoTips = "将选中的版本库归并到所选产品
$lang->upgrade->needBuild4Add = '本次升级需要创建索引。请到 [后台->系统->重建索引] 页面,重新创建索引。';
$lang->upgrade->errorEngineInnodb = '您当前的数据库不支持使用InnoDB数据表引擎,请修改为MyISAM后重试。';
$lang->upgrade->duplicateProject = "同一个项目集内项目名称不能重复,请调整重名的项目名称";
$lang->upgrade->upgradeTips = "历史删除数据不参与升级,升级后将不支持还原,请知悉";
$lang->upgrade->projectType['project'] = "把历史的{$lang->projectCommon}作为项目升级";
$lang->upgrade->projectType['execution'] = "把历史的{$lang->projectCommon}作为执行升级";
+2 -1
View File
@@ -39,12 +39,12 @@
<?php echo $lang->upgrade->to15Desc;?>
<?php $systemMode = isset($lang->upgrade->to15Mode['classic']) ? 'classic' : 'new';?>
<?php echo html::radio('mode', $lang->upgrade->to15Mode, $systemMode);?>
<p> </p>
<div id='selectedModeTips' class='text-info'><?php echo $lang->upgrade->selectedModeTips[$systemMode];?></div>
</div>
</div>
<hr/>
<div class='panel-footer text-center'>
<div id='upgradeTips' class='text-danger hidden'><?php echo $lang->upgrade->upgradeTips;?></div>
<?php echo html::submitButton($lang->upgrade->start . (strpos($this->app->getClientLang(), 'zh') === false ? ' ' : '') . $lang->upgrade->common);?>
</div>
</form>
@@ -57,6 +57,7 @@ $(function()
$('[name=mode]').change(function()
{
$('#selectedModeTips').html(selectedModeTips[$(this).val()]);
$(this).val() == 'new' ? $('#upgradeTips').removeClass('hidden') : $('#upgradeTips').addClass('hidden');
})
})
</script>
+9 -8
View File
@@ -74,7 +74,7 @@ class user extends control
$pager = pager::init($recTotal, $recPerPage, $pageID);
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
/* Get user, totos. */
$account = $user->account;
@@ -259,7 +259,7 @@ class user extends control
$this->app->loadLang('testcase');
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$this->view->title = $this->lang->user->common . $this->lang->colon . $this->lang->user->testTask;
$this->view->position[] = $this->lang->user->testTask;
@@ -301,7 +301,7 @@ class user extends control
$users = $this->loadModel('dept')->getDeptUserPairs($this->app->user->dept, 'id');
/* Append id for secend sort. */
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$cases = array();
if($type == 'case2Him')
@@ -1097,7 +1097,7 @@ class user extends control
/* Append id for secend sort. */
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
$sort = $this->loadModel('common')->appendOrder($orderBy);
$sort = common::appendOrder($orderBy);
$date = empty($date) ? '' : date('Y-m-d', $date);
$actions = $this->loadModel('action')->getDynamic($account, $period, $sort, $pager, 'all', 'all', 'all', $date, $direction);
@@ -1165,19 +1165,20 @@ class user extends control
* AJAX: get users from a contact list.
*
* @param int $contactListID
* @param string $dropdownName
* @access public
* @return string
*/
public function ajaxGetContactUsers($contactListID)
public function ajaxGetContactUsers($contactListID, $dropdownName = 'mailto')
{
$list = $contactListID ? $this->user->getContactListByID($contactListID) : '';
$users = $this->user->getPairs('devfirst|nodeleted', $list ? $list->userList : '', $this->config->maxCount);
if(isset($this->config->user->moreLink)) $this->config->moreLinks['mailto[]'] = $this->config->user->moreLink;
if(isset($this->config->user->moreLink)) $this->config->moreLinks[$dropdownName . "[]"] = $this->config->user->moreLink;
if(!$contactListID) return print(html::select('mailto[]', $users, '', "class='form-control chosen' multiple data-placeholder='{$this->lang->chooseUsersToMail}'"));
if(!$contactListID) return print(html::select($dropdownName . "[]", $users, '', "class='form-control chosen' multiple data-placeholder='{$this->lang->chooseUsersToMail}'"));
return print(html::select('mailto[]', $users, $list->userList, "class='form-control chosen' multiple data-placeholder='{$this->lang->chooseUsersToMail}'"));
return print(html::select($dropdownName . "[]", $users, $list->userList, "class='form-control chosen' multiple data-placeholder='{$this->lang->chooseUsersToMail}'"));
}
/**
+33
View File
@@ -205,6 +205,39 @@ class userModel extends model
return $users;
}
/**
* Get users info list for displaying
*
* @param array $accounts
* @param int $deptID
* @param string $type
* @access public
* @return array
*/
public function getUserDisplayInfos($accounts, $deptID = 0, $type = 'inside')
{
$users = $this->dao->select('account, realname, avatar, dept, role')->from(TABLE_USER)
->where('deleted')->eq(0)
->beginIF($accounts)->andWhere('type')->eq($type)->fi()
->beginIF($accounts)->andWhere('account')->in($accounts)->fi()
->fetchAll('account');
$depts = $this->loadModel('dept')->getDeptPairs($deptID);
$infos = array();
foreach($users as $info)
{
$info->roleName = zget($this->lang->user->roleList, $info->role, '');
if(isset($depts[$info->dept])) $info->deptName = $depts[$info->dept];
if(empty($info->avatar)) unset($info->avatar);
if(empty($info->realname)) unset($info->realname);
if(empty($info->role)) unset($info->role);
if(empty($info->roleName)) unset($info->roleName);
$infos[$info->account] = $info;
}
return $infos;
}
/**
* Get user info by ID.
*
-2
View File
@@ -16,7 +16,6 @@ $jsFiles[] = $jqueryRoot . 'lib.js';
$jsFiles[] = $jqueryRoot . 'tablesorter/min.js';
$jsFiles[] = $jqueryRoot . 'tablesorter/metadata.js';
$jsFiles[] = $jsRoot . 'zui/min.js';
$jsFiles[] = $jsRoot . 'zui/picker/zui.picker.min.js';
$jsFiles[] = $jsRoot . 'my.full.js';
/* Combine these js files. */
@@ -58,7 +57,6 @@ $themes = array_keys($lang->themes);
/* Create css files for every them and every lang. */
$zuiCode = str_replace(array('/*!', '../fonts'), array('/*', '../zui/fonts'), file_get_contents($themeRoot . 'zui/css/min.css'));
$zuiCode .= "\n" . str_replace('/*!', '/*', file_get_contents($jsRoot . 'zui/picker/zui.picker.min.css')) . "\n";
foreach($langs as $lang)
{
foreach($themes as $theme)
+1 -1
View File
@@ -136,7 +136,7 @@ function showMoreImage(obj)
*/
function setMailto(mailto, contactListID)
{
link = createLink('user', 'ajaxGetContactUsers', 'listID=' + contactListID);
link = createLink('user', 'ajaxGetContactUsers', 'listID=' + contactListID + '&dropdownName=' + mailto);
$.get(link, function(users)
{
$('#' + mailto).replaceWith(users);

Some files were not shown because too many files have changed in this diff Show More