Merge branch 'master' into 'sprint/master_zhouxudong_56362'

# Conflicts:
#   module/kanban/model.php
This commit is contained in:
王怡栋
2022-06-09 06:32:06 +00:00
58 changed files with 588 additions and 98 deletions
+2 -2
View File
@@ -62,8 +62,8 @@ class usersEntry extends entry
$password = $this->request('password', '') ? md5($this->request('password')) : '';
$visions = $this->request('visions', 'rnd');
if(is_array($visions)) $visions = implode(',', $visions);
$visions = $this->request('visions', array('rnd'));
if(!is_array($visions)) $visions = explode(',', $visions);
$this->setPost('password1', $password);
$this->setPost('password2', $password);
+1
View File
@@ -1,3 +1,4 @@
DELETE from zt_report where `code` = 'product-invest';
ALTER TABLE `zt_review` ADD docVersion smallint(6) NOT NULL AFTER `doc`;
DELETE FROM `zt_workflowaction` WHERE `module`='story' AND `action`='browse';
-1
View File
@@ -6791,7 +6791,6 @@ CREATE OR REPLACE VIEW `ztv_daybugresolve` AS select count(*) AS `bugresolve`,le
CREATE OR REPLACE VIEW `ztv_dayactions` AS select count(*) AS `actions`,left(`zt_action`.`date`,10) AS `day` from `zt_action` group by left(`zt_action`.`date`,10);
REPLACE INTO `zt_report` (`code`, `name`, `module`, `sql`, `vars`, `langs`, `params`, `step`, `desc`, `addedBy`, `addedDate`) VALUES
('product-invest', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u6295\\u5165\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u6295\\u5165\\u8868\",\"en\":\"Product Investment\"}', ',product', 'select t1.id,t1.name,1 as projects, round(t3.consumed,2) as consumed from TABLE_PRODUCT as t1\r\n left join TABLE_PROJECTPRODUCT as t2 on t1.id=t2.product\r\n left join ztv_projectsummary as t3 on t2.project=t3.project\r\n left join TABLE_PROJECT as t4 on t2.project=t4.id\r\n left join TABLE_PROGRAM as t5 on t1.program=t5.id\r\n where t1.deleted=\'0\' and t4.deleted=\'0\' and t4.type=\'project\'\r\norder by t5.`order` asc, t1.line desc, t1.`order` asc', '', '{\"projects\":{\"zh-cn\":\"\\u9879\\u76ee\\u6570\",\"zh-tw\":\"\\u9879\\u76ee\\u6570\",\"en\":\"Projects\"},\"consumed\":{\"zh-cn\":\"\\u5df2\\u6d88\\u8017\\u5de5\\u65f6\",\"zh-tw\":\"\\u5df2\\u6d88\\u8017\\u5de5\\u65f6\",\"en\":\"Cost(h)\"}}', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"projects\",\"consumed\"],\"reportType\":[\"sum\",\"sum\"],\"sumAppend\":[\"projects\",\"consumed\"]}', 2, '{\"zh-cn\":\"\\u5217\\u51fa\\u6bcf\\u4e2a\\u4ea7\\u54c1\\u7684\\u9879\\u76ee\\u603b\\u6570\\uff0c\\u5df2\\u7ecf\\u6d88\\u8017\\u7684\\u5de5\\u65f6\\u3002\",\"zh-tw\":\"\\u5217\\u51fa\\u6bcf\\u500b\\u7522\\u54c1\\u7684\\u9805\\u76ee\\u7e3d\\u6578\\uff0c\\u5df2\\u7d93\\u6d88\\u8017\\u7684\\u5de5\\u6642\\u3002\",\"en\":\"Number of projects and consumed hours.\"}', 'admin', '2015-07-20 14:21:31'),
('product-progress', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u5b8c\\u6210\\u5ea6\\u7edf\\u8ba1\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u5b8c\\u6210\\u5ea6\\u7d71\\u8a08\\u8868\",\"en\":\"Product Progress\"}', ',product', 'select t1.*,t2.name, (case when t1.status = \'closed\' or t1.stage = \'released\' then 1 else 0 end) as done, 1 as count from TABLE_STORY as t1 \r\nleft join TABLE_PRODUCT as t2 on t1.product=t2.id \r\nleft join TABLE_PROGRAM as t3 on t2.program=t3.id \r\nwhere t1.deleted=\'0\' and t2.deleted=\'0\'\r\norder by t3.`order` asc, t2.line desc, t2.`order` asc', '', '{\"count\":{\"zh-cn\":\"\\u9700\\u6c42\\u6570\",\"zh-tw\":\"\\u9700\\u6c42\\u6570\",\"en\":\"Stories\"},\"done\":{\"zh-cn\":\"\\u5b8c\\u6210\\u6570\",\"zh-tw\":\"\\u5b8c\\u6210\\u6570\",\"en\":\"Done\"}}', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"count\",\"done\"],\"reportType\":[\"sum\",\"sum\"],\"sumAppend\":[\"count\",\"done\"],\"percent\":{\"1\":\"1\"},\"contrast\":{\"1\":\"count\"},\"showAlone\":{\"1\":\"1\"}}', 2, '{\"zh-cn\":\"\\u6309\\u7167\\u4ea7\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u603b\\u6570\\uff0c\\u5b8c\\u6210\\u7684\\u603b\\u6570(\\u72b6\\u6001\\u662f\\u5173\\u95ed\\uff0c\\u6216\\u8005\\u7814\\u53d1\\u9636\\u6bb5\\u662f\\u53d1\\u5e03)\\uff0c\\u5b8c\\u6210\\u7684\\u767e\\u5206\\u6bd4\\u3002\",\"zh-tw\":\"\\u6309\\u7167\\u7522\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u7e3d\\u6578\\uff0c\\u5b8c\\u6210\\u7684\\u7e3d\\u6578(\\u72c0\\u614b\\u662f\\u95dc\\u9589\\uff0c\\u6216\\u8005\\u7814\\u767c\\u968e\\u6bb5\\u662f\\u767c\\u5e03)\\uff0c\\u5b8c\\u6210\\u7684\\u767e\\u5206\\u6bd4\\u3002\",\"en\":\"Number of total stories,done stories(state is closed, or stage is released), percent of completion.\"}', 'admin', '2015-07-21 15:07:48'),
('story-status', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u9700\\u6c42\\u72b6\\u6001\\u5206\\u5e03\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u9700\\u6c42\\u72c0\\u614b\\u5206\\u5e03\\u8868\",\"en\":\"Story Status\"}', ',product', 'select t1.*,t2.name from TABLE_STORY as t1\r\n left join TABLE_PRODUCT as t2 on t1.product=t2.id \r\nleft join TABLE_PROGRAM as t3 on t2.program=t3.id \r\nwhere t1.deleted=\'0\' and t2.deleted=\'0\'\r\norder by t3.`order` asc, t2.line desc, t2.`order` asc', '', '', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"status\"],\"reportType\":[\"count\"],\"sumAppend\":[\"\"],\"reportTotal\":[\"1\"]}', 2, '{\"zh-cn\":\"\\u6309\\u7167\\u4ea7\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u603b\\u6570\\uff0c\\u72b6\\u6001\\u7684\\u5206\\u5e03\\u60c5\\u51b5\\u3002\",\"zh-tw\":\"\\u6309\\u7167\\u7522\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u7e3d\\u6578\\uff0c\\u72c0\\u614b\\u7684\\u5206\\u5e03\\u60c5\\u6cc1\\u3002\",\"en\":\"Total number and status distribution of stories.\"}', 'admin', '2015-07-21 15:35:38'),
('story-stage', '{\"zh-cn\":\"\\u4ea7\\u54c1\\u9700\\u6c42\\u9636\\u6bb5\\u5206\\u5e03\\u8868\",\"zh-tw\":\"\\u7522\\u54c1\\u9700\\u6c42\\u968e\\u6bb5\\u5206\\u5e03\\u8868\",\"en\":\"Story Stage\"}', ',product', 'select t1.*,t2.name from TABLE_STORY as t1\r\n left join TABLE_PRODUCT as t2 on t1.product=t2.id \r\nleft join TABLE_PROGRAM as t3 on t2.program=t3.id \r\nwhere t1.deleted=\'0\' and t2.deleted=\'0\'\r\norder by t3.`order` asc, t2.line desc, t2.`order` asc', '', '', '{\"group1\":\"name\",\"group2\":\"\",\"reportField\":[\"stage\"],\"reportType\":[\"count\"],\"sumAppend\":[\"\"],\"reportTotal\":[\"1\"]}', 2, '{\"zh-cn\":\"\\u6309\\u7167\\u4ea7\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u603b\\u6570\\uff0c\\u7814\\u53d1\\u9636\\u6bb5\\u7684\\u5206\\u5e03\\u60c5\\u51b5\\u3002\",\"zh-tw\":\"\\u6309\\u7167\\u7522\\u54c1\\u5217\\u51fa\\u9700\\u6c42\\u7e3d\\u6578\\uff0c\\u7814\\u767c\\u968e\\u6bb5\\u7684\\u5206\\u5e03\\u60c5\\u6cc1\\u3002\",\"en\":\"Total number and stage distribution of stories \"}', 'admin', '2015-07-21 15:38:34'),
+5 -1
View File
@@ -85,7 +85,7 @@ public function getKanban4Group($executionID, $browseType, $groupBy, $searchValu
$columnData[$columnID]['actions'] = $column->actions;
$cardOrder = 1;
$objects = zget($cardGroup, $columnID, array());
$objects = zget($cardGroup, $column->columnType, array());
foreach($objects as $object)
{
if(empty($object)) continue;
@@ -104,10 +104,14 @@ public function getKanban4Group($executionID, $browseType, $groupBy, $searchValu
if($browseType == 'task')
{
if($searchValue != '' and strpos($object->name, $searchValue) === false) continue;
$cardData['name'] = $object->name;
$cardData['status'] = $object->status;
$cardData['left'] = $object->left;
}
else
{
if($searchValue != '' and strpos($object->name, $searchValue) === false) continue;
$cardData['title'] = $object->title;
}
@@ -37,17 +37,17 @@
</div>
<?php else:?>
<div class='kanban-cards'>
<?php
<?php
$kanbanview = 'kanban';
if($this->cookie->kanbanview) $kanbanview = $this->cookie->kanbanview;
if(!common::hasPriv('execution', $kanbanview))
{
foreach (explode('|', 'kanban|task|calendar|gantt|tree|grouptask') as $view)
foreach (explode('|', 'kanban|task|calendar|gantt|tree|grouptask') as $view)
{
if(common::hasPriv('execution', $view))
{
$kanbanview = $view;
$kanbanview = $view;
break;
}
}
@@ -93,24 +93,24 @@
</div>
<div class="kanban-footer">
<div class='kanban-members pull-left'>
<?php $teams = $memberGroup[$kanban->id];?>
<?php $teams = isset($memberGroup[$kanban->id]) ? $memberGroup[$kanban->id] : array();?>
<?php $count = 0;?>
<?php foreach($teams as $member):?>
<?php if($count > 2) break;?>
<?php $count ++;?>
<?php if($count > 2) break;?>
<?php $count ++;?>
<div title="<?php echo zget($users, $member->account);?>">
<?php echo html::smallAvatar(array('avatar' => zget($usersAvatar, $member->account), 'account' => $member->account, 'name' => isset($member->realname) ? $member->realname : '')); ?>
</div>
<?php endforeach;?>
<?php if(count($teams) > 4):?>
<?php echo '<span>… </span>';?>
<?php endif;?>
<?php echo '<span>… </span>';?>
<?php endif;?>
<?php if(count($teams) > 3):?>
<?php $lastMember = end($teams);?>
<div title="<?php echo $lastMember->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$lastMember->account], 'account' => $lastMember->account, 'name' => $lastMember->realname), 'avatar-circle avatar-' . key($lastMember)); ?>
</div>
<?php endif;?>
<div title="<?php echo $lastMember->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$lastMember->account], 'account' => $lastMember->account, 'name' => $lastMember->realname), 'avatar-circle avatar-' . key($lastMember)); ?>
</div>
<?php endif;?>
</div>
<div class='kanban-members-count pull-left'><?php echo sprintf($lang->project->teamSumCount, count($teams));?></div>
<div class='kanban-acl pull-right'>
+1 -1
View File
@@ -105,7 +105,7 @@ class model extends baseModel
$conditions = json_decode($action->conditions);
if($action->extensionType == 'extend')
{
$title = $action->name;
if($icon != 'copy' and $methodName != 'create') $title = $action->name;
if($conditions) $enabled = $this->loadModel('flow')->checkConditions($conditions, $data);
}
else
+17
View File
@@ -518,6 +518,13 @@ class router extends baseRouter
/* Remove module and method. */
$params = array_slice($params, 2); // $params = array(1);
if($moduleName == 'flow' and $methodName == 'browse')
{
$mode = 'browse';
if(count($params) > 0 and !is_numeric($params[0])) $mode = array_shift($params);
array_unshift($params, $mode);
}
array_unshift($params, $methodName); // $params = array('operate', 1);
array_unshift($params, $moduleName); // $params = array('flow', 'operate', 1);
@@ -537,6 +544,16 @@ class router extends baseRouter
$params = array_reverse($params); // $params = array('id' => 1);
if($moduleName == 'flow' and $methodName == 'browse')
{
$mode = zget($params, 'mode', 'browse');
if(is_numeric($mode)) $mode = 'browse';
$params['mode'] = $mode;
$get = array_reverse($_GET);
$get['mode'] = $mode;
$_GET = array_reverse($get);
}
$params[$this->config->methodVar] = $methodName; // $param = array('id' => 1, 'f' => 'operate');
$params[$this->config->moduleVar] = $moduleName; // $param = array('id' => 1, 'f' => 'operate', 'm' => 'flow');
+1
View File
@@ -61,6 +61,7 @@ $lang->bug->openedDate = 'ReportedDate';
$lang->bug->openedDateAB = 'Reported';
$lang->bug->openedBuild = 'Open Build';
$lang->bug->assignedTo = 'AssignTo';
$lang->bug->assignedToMe = 'AssignToMe';
$lang->bug->assignBug = 'AssignedTo';
$lang->bug->assignedToAB = 'AssignedTo';
$lang->bug->assignedDate = 'AssignedDate';
+1
View File
@@ -61,6 +61,7 @@ $lang->bug->openedDate = '创建日期';
$lang->bug->openedDateAB = '创建日期';
$lang->bug->openedBuild = '影响版本';
$lang->bug->assignedTo = '指派给';
$lang->bug->assignedToMe = '指派给我';
$lang->bug->assignBug = '指派给';
$lang->bug->assignedToAB = '指派给';
$lang->bug->assignedDate = '指派日期';
+50 -4
View File
@@ -1601,18 +1601,62 @@ class bugModel extends model
* @param int $limit
* @param object $pager
* @param int $executionID
* @param int $queryID
* @access public
* @return void
* @return array
*/
public function getUserBugs($account, $type = 'assignedTo', $orderBy = 'id_desc', $limit = 0, $pager = null, $executionID = 0)
public function getUserBugs($account, $type = 'assignedTo', $orderBy = 'id_desc', $limit = 0, $pager = null, $executionID = 0, $queryID = 0)
{
if(!$this->loadModel('common')->checkField(TABLE_BUG, $type)) return array();
$moduleName = $this->app->rawMethod == 'work' ? 'workBug' : 'contributeBug';
$queryName = $moduleName . 'Query';
$formName = $moduleName . 'Form';
$bugIDList = array();
if($moduleName == 'contributeBug')
{
$bugsAssignedByMe = $this->loadModel('my')->getAssignedByMe($account, 0, $pager, $orderBy, 0, 'bug');
foreach($bugsAssignedByMe as $bugID => $bug)
{
$bugIDList[$bugID] = $bugID;
}
}
if($queryID)
{
$query = $this->loadModel('search')->getQuery($queryID);
if($query)
{
$this->session->set($queryName, $query->sql);
$this->session->set($formName, $query->form);
}
else
{
$this->session->set($queryName, ' 1 = 1');
}
}
else
{
if($this->session->$queryName == false) $this->session->set($queryName, ' 1 = 1');
}
$query = $this->session->$queryName;
$query = preg_replace('/`(\w+)`/', 't1.`$1`', $query);
if($type != 'bySearch' and !$this->loadModel('common')->checkField(TABLE_BUG, $type)) return array();
$bugs = $this->dao->select('t1.*,t2.name as productName')->from(TABLE_BUG)->alias('t1')
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id')
->where('t1.deleted')->eq(0)
->beginIF($type == 'bySearch')->andWhere($query)->fi()
->beginIF($executionID)->andWhere('t1.execution')->eq($executionID)->fi()
->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('t1.status')->ne('closed')->fi()
->beginIF($type != 'all')->andWhere("t1.`$type`")->eq($account)->fi()
->beginIF($type != 'all' and $type != 'bySearch')->andWhere("t1.`$type`")->eq($account)->fi()
->beginIF($type == 'bySearch' and $moduleName == 'workBug')->andWhere("t1.assignedTo")->eq($account)->fi()
->beginIF($type == 'bySearch' and $moduleName == 'contributeBug')
->andWhere('t1.openedBy', 1)->eq($account)
->orWhere('t1.closedBy')->eq($account)
->orWhere('t1.resolvedBy')->eq($account)
->orWhere('t1.id')->in($bugIDList)
->markRight(1)
->fi()
->orderBy($orderBy)
->beginIF($limit > 0)->limit($limit)->fi()
->page($pager)
@@ -1878,6 +1922,8 @@ class bugModel extends model
if(!empty($this->config->isINT)) $firstLetter = '';
$users[$account] = $firstLetter . ($user->realname ? $user->realname : $user->account);
}
$users = $this->loadModel('user')->processAccountSort($users);
return array('' => '') + $users;
}
+2 -2
View File
@@ -16,8 +16,8 @@
<div class='cell'>
<div class='list-group'>
<?php
$reruiredModules = ksort($config->custom->requiredModules);
foreach($requiredModules as $requiredModule)
ksort($config->custom->requiredModules);
foreach($config->custom->requiredModules as $requiredModule)
{
$requiredModuleName = zget($lang->custom->moduleName, $requiredModule, $lang->$requiredModule->common);
echo html::a(inlink('required', "module=$requiredModule"), $requiredModuleName, '', "id='{$requiredModule}Tab'");
+1 -1
View File
@@ -102,7 +102,7 @@
}
elseif($doc->contentType == 'markdown')
{
echo "<textarea id='markdownContent' /></textarea>";
echo "<textarea id='markdownContent'></textarea>";
}
else
{
+1 -1
View File
@@ -116,7 +116,7 @@ js::set('docID', $doc->id);
}
elseif($doc->contentType == 'markdown')
{
echo "<textarea id='markdownContent' /></textarea>";
echo "<textarea id='markdownContent'></textarea>";
}
else
{
+2
View File
@@ -51,6 +51,7 @@ $config->execution->search['fields']['desc'] = $lang->task->desc;
$config->execution->search['fields']['assignedTo'] = $lang->task->assignedTo;
$config->execution->search['fields']['pri'] = $lang->task->pri;
if($app->tab == 'my') $config->execution->search['fields']['project'] = $lang->task->project;
$config->execution->search['fields']['execution'] = $lang->task->execution;
$config->execution->search['fields']['module'] = $lang->task->module;
$config->execution->search['fields']['estimate'] = $lang->task->estimate;
@@ -86,6 +87,7 @@ $config->execution->search['params']['desc'] = array('operator' => 'in
$config->execution->search['params']['assignedTo'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->execution->search['params']['pri'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->task->priList);
if($app->tab == 'my') $config->execution->search['params']['project'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->execution->search['params']['execution'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->execution->search['params']['module'] = array('operator' => 'belong', 'control' => 'select', 'values' => '');
$config->execution->search['params']['estimate'] = array('operator' => '=', 'control' => 'input', 'values' => '');
+6 -3
View File
@@ -115,6 +115,7 @@ class execution extends control
$this->loadModel('datatable');
$this->loadModel('setting');
$this->loadModel('product');
$this->loadModel('user');
if(common::hasPriv('execution', 'create')) $this->lang->TRActions = html::a($this->createLink('execution', 'create'), "<i class='icon icon-sm icon-plus'></i> " . $this->lang->execution->create, '', "class='btn btn-primary'");
@@ -211,6 +212,7 @@ class execution extends control
/* team member pairs. */
$memberPairs = array();
foreach($this->view->teamMembers as $key => $member) $memberPairs[$key] = $member->realname;
$memberPairs = $this->user->processAccountSort($memberPairs);
$showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
$extra = (isset($this->config->execution->task->allModule) && $this->config->execution->task->allModule == 1) ? 'allModule' : '';
@@ -227,7 +229,7 @@ class execution extends control
$this->view->orderBy = $orderBy;
$this->view->browseType = $browseType;
$this->view->status = $status;
$this->view->users = $this->loadModel('user')->getPairs('noletter|all');
$this->view->users = $this->user->getPairs('noletter|all');
$this->view->param = $param;
$this->view->executionID = $executionID;
$this->view->execution = $execution;
@@ -918,6 +920,7 @@ class execution extends control
{
$memberPairs[$key] = $member->realname;
}
$memberPairs = $this->user->processAccountSort($memberPairs);
/* Build the search form. */
$actionURL = $this->createLink('execution', 'bug', "executionID=$executionID&productID=$productID&orderBy=$orderBy&build=$build&type=bysearch&queryID=myQueryID");
@@ -1496,7 +1499,7 @@ class execution extends control
$this->view->name = $name;
$this->view->code = $code;
$this->view->team = $team;
$this->view->teams = array(0 => '', $projectID => (isset($project->name) ? $project->name : '')) + $this->execution->getCanCopyObjects((int)$projectID);
$this->view->teams = array(0 => '') + $this->execution->getCanCopyObjects((int)$projectID);
$this->view->allProjects = array(0 => '') + $this->project->getPairsByModel('all', 0, 'noclosed');
$this->view->executionID = $executionID;
$this->view->productID = $productID;
@@ -2730,7 +2733,7 @@ class execution extends control
$currentMembers = $this->execution->getTeamMembers($executionID);
$members2Import = $this->execution->getMembers2Import($team2Import, array_keys($currentMembers));
$teams2Import = $this->loadModel('personnel')->getCopiedObjects($executionID, 'sprint');
$teams2Import = $this->loadModel('personnel')->getCopiedObjects($executionID, 'sprint', true);
$teams2Import = array('' => '') + $teams2Import;
/* Append users for get users. */
+19 -5
View File
@@ -720,6 +720,17 @@ function renderCount($count, count, column)
}
}
/**
* Alert to link product.
*
* @access public
* @return void
*/
function tips()
{
bootbox.alert(needLinkProducts);
}
/**
* Render header of a column.
*/
@@ -754,10 +765,11 @@ function renderHeaderCol($column, column, $header, kanbanData)
var printMoreBtn = (columnPrivs.includes('setColumn') || columnPrivs.includes('setWIP'));
/* Render more menu. */
if(((column.type == 'backlog' && hasStoryButton) || (column.type == 'wait' && hasTaskButton) || (column.type == 'unconfirmed' && hasBugButton)) && $actions.children('.text-primary').length == 0)
if(column.type == 'backlog' || column.type == 'wait' || column.type == 'unconfirmed' && $actions.children('.text-primary').length == 0)
{
var tips = productID ? '' : 'onclick="tips()"';
$actions.append([
'<a data-contextmenu="columnCreate" data-type="' + column.type + '" data-kanban="' + kanban.id + '" data-parent="' + (column.parentType || '') + '" class="text-primary">',
'<a data-contextmenu="columnCreate" data-type="' + column.type + '" data-kanban="' + kanban.id + '" data-parent="' + (column.parentType || '') + '" class="text-primary"' + ((column.type !== 'wait') ? tips : '') + '>',
'<i class="icon icon-expand-alt"></i>',
'</a>'
].join(''));
@@ -820,14 +832,16 @@ function updateRegion(regionID, regionData = [])
if(!regionData) regionData = regions[regionID];
var data = groupBy == 'default' ? regionData.groups : regionData;
if(data == null)
if(data == null || data.length == 0)
{
$("div[data-id^=" + regionID + "].region").hide();
if(groupBy == 'default') $("div[data-id^=" + regionID + "].region").hide();
if(groupBy != 'default') $("div[data-id^=" + regionID + "].kanban").hide();
return false;
}
else
{
$("div[data-id^=" + regionID + "].region").show();
if(groupBy == 'default') $("div[data-id^=" + regionID + "].region").show();
if(groupBy != 'default') $("div[data-id^=" + regionID + "].kanban").show();
}
$region.empty();
$region.data('zui.kanban').render(data);
+14 -2
View File
@@ -325,6 +325,17 @@ function renderColumnCount($count, count, col)
}
}
/**
* Alert to link product.
*
* @access public
* @return void
*/
function tips()
{
bootbox.alert(needLinkProducts);
}
/**
* Render header column
* @param {JQuery} $col Header column element
@@ -343,10 +354,11 @@ function renderHeaderCol($col, col, $header, kanban)
if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true;
if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true;
if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton))
if(col.type === 'backlog' || col.type === 'wait' || col.type == 'unconfirmed')
{
var tips = productID ? '' : 'onclick="tips()"';
$actions.append([
'<a data-contextmenu="columnCreate" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '" class="text-primary">',
'<a data-contextmenu="columnCreate" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '" class="text-primary"' + ((col.laneType !== 'task') ? tips : '') + '>',
'<i class="icon icon-expand-alt"></i>',
'</a>'
].join(''));
+2
View File
@@ -271,6 +271,7 @@ $lang->execution->treeOnlyStory = 'Show Story Only';
$lang->execution->storyKanban = 'Story Kanban';
$lang->execution->storySort = 'Rank Story';
$lang->execution->importPlanStory = $lang->executionCommon . ' is created!\nDo you want to import stories that have been linked to the plan?';
$lang->execution->needLinkProducts = 'The execution has not been linked with any product, and the related functions cannot be used. Please link the product first and try again.';
$lang->execution->iteration = 'Iterations';
$lang->execution->iterationInfo = '%s Iterations';
$lang->execution->viewAll = 'View All';
@@ -396,6 +397,7 @@ $lang->execution->projectNotEmpty = 'Project cannot be empty.';
$lang->execution->confirmStoryToTask = $lang->SRCommon . '%s are converted to tasks in the current. Do you want to convert them anyways?';
$lang->execution->ge = "『%s』should be >= actual begin『%s』.";
$lang->execution->storyDragError = "The {$lang->SRCommon} is still a draft or has been changed, please drag it after the review";
$lang->execution->countTip = ' (%s member)';
$lang->execution->pleaseInput = "Enter";
/* Statistics. */
+3 -1
View File
@@ -51,7 +51,7 @@ $lang->execution->canceledBy = '由谁取消';
$lang->execution->canceledDate = '取消日期';
$lang->execution->begin = '计划开始';
$lang->execution->end = '计划完成';
$lang->execution->dateRange = '起始日期';
$lang->execution->dateRange = '计划起止日期';
$lang->execution->realBeganAB = '实际开始';
$lang->execution->realEndAB = '实际完成';
$lang->execution->realBegan = '实际开始日期';
@@ -271,6 +271,7 @@ $lang->execution->treeOnlyStory = "树状图只看{$lang->SRCommon}";
$lang->execution->storyKanban = "{$lang->SRCommon}看板";
$lang->execution->storySort = "{$lang->SRCommon}排序";
$lang->execution->importPlanStory = '创建' . $lang->executionCommon . '成功!\n是否导入计划关联的相关' . $lang->SRCommon . '';
$lang->execution->needLinkProducts = '该执行还未关联任何产品,相关功能无法使用,请先关联产品后再试。';
$lang->execution->iteration = '版本迭代';
$lang->execution->iterationInfo = '迭代%s次';
$lang->execution->viewAll = '查看所有';
@@ -396,6 +397,7 @@ $lang->execution->projectNotEmpty = '所属项目不能为空。';
$lang->execution->confirmStoryToTask = '%s' . $lang->SRCommon . '已经在当前' . $lang->execution->common . '中转了任务,请确认是否重复转任务。';
$lang->execution->ge = "『%s』应当不小于实际开始时间『%s』。";
$lang->execution->storyDragError = "{$lang->SRCommon}还是草稿或已变更状态,请评审通过后再拖动";
$lang->execution->countTip = '%s人)';
$lang->execution->pleaseInput = "请输入";
/* 统计。*/
+19 -2
View File
@@ -2753,14 +2753,31 @@ class executionModel extends model
$objectPairs = $this->dao->select('id,name')->from(TABLE_PROJECT)
->where('deleted')->eq(0)
->andWhere('project', true)->eq($projectID)
->andWhere('type')->ne('project')
->andWhere('(project')->eq($projectID)
->orWhere('id')->eq($projectID)
->markRight(1)
->orWhere('id')->eq($projectID)
->orderBy('type_asc,openedDate_desc')
->limit('9')
->fetchPairs();
$countPairs = $this->dao->select('root, COUNT(*) as count')->from(TABLE_TEAM)
->where('( type')->eq('project')
->andWhere('root')->eq($projectID)
->markRight(1)
->orWhere('( type')->eq('execution')
->andWhere('root')->in(array_keys($objectPairs))
->markRight(1)
->groupBy('root')
->fetchPairs('root');
foreach($objectPairs as $objectID => $objectName)
{
$memberCount = zget($countPairs, $objectID, 0);
$countTip = $memberCount > 1 ? str_replace('member', 'members', $this->lang->execution->countTip) : $this->lang->execution->countTip;
$objectPairs[$objectID] = $objectName . sprintf($countTip, $memberCount);
}
return $objectPairs;
}
+1 -3
View File
@@ -53,6 +53,7 @@ js::set('vision', $this->config->vision);
js::set('productCount', count($productNames));
js::set('executionID', $execution->id);
js::set('reviewStoryParis', $reviewStoryParis);
js::set('needLinkProducts', $lang->execution->needLinkProducts);
js::set('rdSearchValue', '');
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
@@ -111,9 +112,6 @@ js::set('priv',
'canUnlinkStory' => common::hasPriv('execution', 'unlinkStory'),
)
);
js::set('hasStoryButton', $hasStoryButton);
js::set('hasBugButton', $hasBugButton);
js::set('hasTaskButton', $hasTaskButton);
?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
@@ -214,4 +214,5 @@ js::set('priv',
<?php js::set('fluidBoard', $execution->fluidBoard);?>
<?php js::set('displayCards', $execution->displayCards);?>
<?php js::set('reviewStoryParis', $reviewStoryParis);?>
<?php js::set('needLinkProducts', $lang->execution->needLinkProducts);?>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -1606,7 +1606,7 @@ class kanban extends control
$this->view->canEdit = $from == 'RDKanban' ? 0 : 1;
$this->view->column = $column;
$this->view->title = $column->name . $this->lang->colon . $this->lang->kanban->setColumn;
$this->view->title = $column->name . $this->lang->colon . $this->lang->kanban->editColumn;
$this->display();
}
+6 -2
View File
@@ -7,7 +7,9 @@
#spaceList .spaceActions > a{margin-left: 10px;}
.space .menu {margin-left: 5px; margin-right:5px;}
.space .table-empty-tip {padding: 40px 10px;}
.space .spaceTitle.pull-left {overflow: hidden; white-space: nowrap; max-width: calc(100% - 150px)}
.space .spaceTitle.pull-left {overflow: hidden; white-space: nowrap; max-width: calc(100% - 255px)}
.spaceName {font-size: 16px; font-weight: bold;}
.spaceTitle .icon-cube {font-size: 16px;}
.spaceDesc {font-size: 13px; color: #838A9D; margin: -5px 0 5px 18px;}
#more .setting {margin-left: -25px; min-width: 80px}
@@ -16,7 +18,7 @@
.kanbans .panel {margin: 0 0; border: 1px solid #DCDCDC; border-radius: 4px; box-shadow: none; cursor: pointer; height: 160px; margin-bottom: 5px;}
.kanbans .panel:hover {border-color: #006AF1; box-shadow: 0 0 10px 0 rgba(0,0,100,.25);}
.kanbans .panel-heading {padding: 4px 2px 10px 16px;}
.kanbans .kanban-name {overflow: hidden; white-space:nowrap; width: 88%; float: left; padding-top: 8px;}
.kanbans .kanban-name {overflow: hidden; white-space:nowrap; width: 78%; float: left; padding-top: 8px;}
.kanbans .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-1px !important;}
.kanbans .kanban-actions {float: right; visibility: hidden;}
.kanbans .kanban-actions .dropdown-menu.pull-right {top:31px; right: -84px; left: auto;}
@@ -36,6 +38,8 @@
.kanbans .kanban-members > span:after {right: -4px;}
.kanbans .kanban-members-total {display: inline-block; margin-left: 6px; position: relative; top: 3px}
.kanbans .kanbanAcl {position: absolute; right: 0px; bottom: 2px; color: #838a9d;}
.kanbans .kanban-label {margin: 6px 6px 0px 0px; float: left; color: #ff8a53; border: 1px solid #ff8a53;}
@media screen and (max-width: 1366px){.kanbans .kanban-name {width: 68%;}}
a.disabled {pointer-events: none;}
+7 -2
View File
@@ -9,10 +9,10 @@
.dropdown-menu > li {padding: 0 10px;}
.dropdown-menu > li > a > .icon {position: relative; left: -5px;}
#kanbanContainer {padding-bottom: 0; margin-bottom: 0;}
#kanbanContainer {padding-bottom: 0; margin-bottom: 0; border: unset;}
#kanbanContainer.fullscreen {overflow: auto;}
.region {border: 1px solid #dcdcdc; background-color: #fff;}
.region {border: 0px solid #dcdcdc; background-color: #fff;}
.region + .region {margin-top: 20px;}
.region .region-header {padding: 10px;}
.region .region-header strong {font-size: 14px; color: #999EAB}
@@ -136,3 +136,8 @@
@-moz-document url-prefix(){ .region .kanban-lane-items{scrollbar-width:thin; padding:5px 0px 5px 5px !important;} .region .kanban-card .info .user{right: -6px !important;}}
#archivedCards .table-empty-tip {border-style: hidden;}
#archivedColumns .table-empty-tip {border-style: hidden;}
#main > .container {padding: 0px 0px 0px 10px; margin: -10px 0px 0px 0px;}
@media screen and (max-width: 1366px){#main > .container {padding: 0px 10px 0px 10px; margin: -10px 0px 0px 0px;}}
.panel-body {padding: 0px;}
#kanbanContainer {background: #efefef; box-shadow: none;}
+37 -2
View File
@@ -1116,6 +1116,7 @@ function createCardMenu(options)
var items = [];
if(privs.includes('editCard') && card.fromType == '') items.push({label: kanbanLang.editCard, icon: 'edit', url: createLink('kanban', 'editCard', 'cardID=' + card.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width': '80%'}});
if(privs.includes('deleteCard')) items.push({label: card.fromType == '' ? kanbanLang.deleteCard : kanbanLang.removeCard, icon: card.fromType == '' ? 'trash' : 'unlink', url: createLink('kanban', 'deleteCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
if(privs.includes('performable') && kanban.performable == 1)
{
if(card.status == 'done')
@@ -1127,6 +1128,25 @@ function createCardMenu(options)
items.push({label: kanbanLang.finishCard, icon: 'checked', onClick: function(){finishCard(card.id, card.kanban, card.region);}});
}
}
if(privs.includes('archiveCard') && kanban.archived == '1') items.push({label: kanbanLang.archiveCard, icon: 'card-archive', url: createLink('kanban', 'archiveCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
var editCard = (privs.includes('editCard') && card.fromType == '') ? true : false;
var deleteCard = privs.includes('deleteCard');
var archiveCard = (privs.includes('archiveCard') && kanban.archived == '1') ? true : false;
var performable = (privs.includes('performable') && kanban.performable == 1) ? true : false;
var moveCard = privs.includes('moveCard');
var setCardColor = privs.includes('setCardColor');
var basicActions = (editCard || deleteCard || archiveCard) ? true : false;
var otherActions = (moveCard || setCardColor) ? true : false;
if((performable || basicActions) && otherActions)
{
items.push({type: 'divider'});
}
if(privs.includes('moveCard'))
{
var moveCardItems = [];
@@ -1150,7 +1170,7 @@ function createCardMenu(options)
moveCardItems = moveCardItems.reverse();
items.push({label: kanbanLang.moveCard, icon: 'move', items: moveCardItems});
}
if(privs.includes('archiveCard') && kanban.archived == '1') items.push({label: kanbanLang.archiveCard, icon: 'card-archive', url: createLink('kanban', 'archiveCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
if(privs.includes('setCardColor'))
{
var cardColoritems = [];
@@ -1164,7 +1184,6 @@ function createCardMenu(options)
};
items.push({label: kanbanLang.cardColor, icon: 'color', items: cardColoritems});
}
if(privs.includes('deleteCard')) items.push({label: card.fromType == '' ? kanbanLang.deleteCard : kanbanLang.removeCard, icon: card.fromType == '' ? 'trash' : 'unlink', url: createLink('kanban', 'deleteCard', 'cardID=' + card.id), attrs: {'target': 'hiddenwin'}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
@@ -1180,6 +1199,15 @@ function createColumnMenu(options)
var items = [];
if(privs.includes('setColumn')) items.push({label: kanbanLang.editColumn, icon: 'edit', url: createLink('kanban', 'setColumn', 'columnID=' + column.id, '', 'true'), className: 'iframe', attrs: {'data-toggle': 'modal'}});
if(privs.includes('setWIP')) items.push({label: kanbanLang.setWIP, icon: 'alert', url: createLink('kanban', 'setWIP', 'columnID=' + column.id), className: 'iframe', attrs: {'data-toggle': 'modal', 'data-width' : '500px'}});
var basicActions = (privs.includes('setColumn') || privs.includes('setWIP')) ? true : false;
var columnActions = (privs.includes('splitColumn') || privs.includes('createColumn') || privs.includes('copyColumn')) ? true : false;
if(basicActions && columnActions)
{
items.push({type: 'divider'});
}
if(privs.includes('splitColumn')) items.push({label: kanbanLang.splitColumn, icon: 'col-split', url: createLink('kanban', 'splitColumn', 'columnID=' + column.id, '', true), className: 'iframe', attrs: {'data-toggle': 'modal'}});
if(privs.includes('createColumn'))
{
@@ -1187,6 +1215,13 @@ function createColumnMenu(options)
items.push({label: kanbanLang.createColumnOnRight, icon: 'col-add-right', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=right'), className: 'iframe', attrs: {'data-toggle': 'modal'}});
}
if(privs.includes('copyColumn')) items.push({label: kanbanLang.copyColumn, icon: 'copy', url: createLink('kanban', 'copyColumn', 'columnID=' + column.id), className: 'iframe', attrs: {'data-toggle': 'modal'}});
var otherActions = ((privs.includes('archiveColumn') && kanban.archived == '1') || privs.includes('deleteColumn')) ? true : false;
if(columnActions && otherActions)
{
items.push({type: 'divider'});
}
if(privs.includes('archiveColumn') && kanban.archived == '1') items.push({label: kanbanLang.archiveColumn, icon: 'card-archive', url: createLink('kanban', 'archiveColumn', 'columnID=' + column.id), attrs: {'target': 'hiddenwin'}});
if(privs.includes('deleteColumn')) items.push({label: kanbanLang.deleteColumn, icon: 'trash', url: createLink('kanban', 'deleteColumn', 'columnID=' + column.id), attrs: {'target': 'hiddenwin'}});
+4 -1
View File
@@ -1,5 +1,6 @@
<?php
/* Actions. */
$lang->kanban->noCard = 'No card';
$lang->kanban->create = 'Create Kanban';
$lang->kanban->createSpace = 'Create Space';
$lang->kanban->editSpace = 'Edit Space';
@@ -80,6 +81,7 @@ $lang->kanban->importBuild = 'Build';
$lang->kanban->allKanban = 'All Kanban';
$lang->kanban->allProjects = 'All ' . ($this->config->systemMode == 'classic' ? $lang->executionCommon : 'Projects');
$lang->kanban->allProducts = 'All Products';
$lang->kanban->mine = 'Mine';
/* Fields. */
$lang->kanban->space = 'Space';
@@ -99,7 +101,8 @@ $lang->kanban->closed = 'Closed';
$lang->kanban->closedBy = 'Closed By';
$lang->kanban->closedDate = 'Closed Date';
$lang->kanban->empty = 'No Kanban';
$lang->kanban->teamSumCount = '%s people in total';
$lang->kanban->teamSumCount = '%s Pers';
$lang->kanban->cardsCount = '%s Card';
$lang->kanban->cardCount = 'Card Count';
$lang->kanban->activatedBy = 'Activated By';
$lang->kanban->activatedDate = 'Activated Date';
+3
View File
@@ -1,5 +1,6 @@
<?php
/* Actions. */
$lang->kanban->noCard = '暂无卡片';
$lang->kanban->create = '创建看板';
$lang->kanban->createSpace = '创建空间';
$lang->kanban->editSpace = '设置空间';
@@ -80,6 +81,7 @@ $lang->kanban->importBuild = '版本';
$lang->kanban->allKanban = '所有看板';
$lang->kanban->allProjects = '所有' . ($this->config->systemMode == 'classic' ? $lang->executionCommon : '项目');
$lang->kanban->allProducts = '所有产品';
$lang->kanban->mine = '我负责';
/* Fields. */
$lang->kanban->space = '所属空间';
@@ -100,6 +102,7 @@ $lang->kanban->closedBy = '由谁关闭';
$lang->kanban->closedDate = '关闭日期';
$lang->kanban->empty = '暂时没有看板';
$lang->kanban->teamSumCount = '共%s人';
$lang->kanban->cardsCount = '共%s张卡片';
$lang->kanban->cardCount = '卡片数量';
$lang->kanban->activatedBy = '由谁激活';
$lang->kanban->activatedDate = '激活日期';
+43 -14
View File
@@ -1415,7 +1415,6 @@ class kanbanModel extends model
if($lane->type == 'task')
{
if($searchValue != '' and strpos($object->name, $searchValue) === false) continue;
$cardData['name'] = $object->name;
$cardData['status'] = $object->status;
$cardData['left'] = $object->left;
@@ -1423,7 +1422,6 @@ class kanbanModel extends model
}
else
{
if($searchValue != '' and strpos($object->title, $searchValue) === false) continue;
$cardData['title'] = $object->title;
}
@@ -3072,28 +3070,41 @@ class kanbanModel extends model
{
$actions .= "<a data-toggle='dropdown' $btnColor class='btn btn-link dropdown-toggle setting' type='button'>" . '<i class="icon icon-cog-outline"></i> ' . $this->lang->kanban->setting . '</a>';
$actions .= "<ul id='kanbanActionMenu' class='dropdown-menu text-left'>";
if(common::hasPriv('kanban', 'createRegion') and $CRKanban) $actions .= '<li>' . html::a(helper::createLink('kanban', 'createRegion', "kanbanID=$kanban->id", '', true), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createRegion, '', "class='iframe btn btn-link'") . '</li>';
$importWidth = $this->app->getClientLang() == 'en' ? '700' : '550';
if(common::hasPriv('kanban', 'import') and $CRKanban) $actions .= '<li>' . html::a(helper::createLink('kanban', 'import', "kanbanID=$kanban->id", '', true), '<i class="icon icon-import"></i>' . $this->lang->kanban->import, '', "class='iframe btn btn-link' data-width=$importWidth") . '</li>';
if(common::hasPriv('kanban', 'enableArchived') and $CRKanban) $actions .= '<li>' . html::a(helper::createLink('kanban', 'enableArchived', "kanbanID=$kanban->id", '', true), '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->archived, '', "class='iframe btn btn-link' data-width=400") . '</li>';
$columnActions = '';
if(common::hasPriv('kanban', 'setColumnWidth') and $CRKanban) $columnActions .= '<li>' . html::a(helper::createLink('kanban', 'setColumnWidth', "kanbanID=$kanban->id", '', true), '<i class="icon icon-size-width"></i>' . $this->lang->kanban->columnWidth, '', "class='iframe btn btn-link' data-width=400") . '</li>';
if($printSetHeightBtn and $CRKanban)
{
$width = $this->app->getClientLang() == 'en' ? '750' : '650';
$actions .= '<li>' . html::a(helper::createLink('kanban', 'setLaneHeight', "kanbanID=$kanban->id", '', true), '<i class="icon icon-size-height"></i>' . $this->lang->kanban->laneHeight, '', "class='iframe btn btn-link' data-width='$width'") . '</li>';
$width = $this->app->getClientLang() == 'en' ? '750' : '650';
$columnActions .= '<li>' . html::a(helper::createLink('kanban', 'setLaneHeight', "kanbanID=$kanban->id", '', true), '<i class="icon icon-size-height"></i>' . $this->lang->kanban->laneHeight, '', "class='iframe btn btn-link' data-width='$width'") . '</li>';
}
if(common::hasPriv('kanban', 'setColumnWidth') and $CRKanban) $actions .= '<li>' . html::a(helper::createLink('kanban', 'setColumnWidth', "kanbanID=$kanban->id", '', true), '<i class="icon icon-size-width"></i>' . $this->lang->kanban->columnWidth, '', "class='iframe btn btn-link' data-width=400") . '</li>';
if(common::hasPriv('kanban', 'performable') and $CRKanban) $actions .= '<li>' . html::a(helper::createLink('kanban', 'performable', "kanbanID=$kanban->id", '', true), '<i class="icon icon-checked"></i>' . $this->lang->kanban->manageProgress, '', "class='iframe btn btn-link' data-width=40%") . '</li>';
$actions .= $columnActions;
$commonActions = '';
if(common::hasPriv('kanban', 'createRegion') and $CRKanban) $commonActions .= '<li>' . html::a(helper::createLink('kanban', 'createRegion', "kanbanID=$kanban->id", '', true), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createRegion, '', "class='iframe btn btn-link'") . '</li>';
$importWidth = $this->app->getClientLang() == 'en' ? '700' : '550';
if(common::hasPriv('kanban', 'import') and $CRKanban) $commonActions .= '<li>' . html::a(helper::createLink('kanban', 'import', "kanbanID=$kanban->id", '', true), '<i class="icon icon-import"></i>' . $this->lang->kanban->import, '', "class='iframe btn btn-link' data-width=$importWidth") . '</li>';
if(common::hasPriv('kanban', 'enableArchived') and $CRKanban) $commonActions .= '<li>' . html::a(helper::createLink('kanban', 'enableArchived', "kanbanID=$kanban->id", '', true), '<i class="icon icon-card-archive"></i>' . $this->lang->kanban->archived, '', "class='iframe btn btn-link' data-width=400") . '</li>';
if(common::hasPriv('kanban', 'performable') and $CRKanban) $commonActions .= '<li>' . html::a(helper::createLink('kanban', 'performable', "kanbanID=$kanban->id", '', true), '<i class="icon icon-checked"></i>' . $this->lang->kanban->manageProgress, '', "class='iframe btn btn-link' data-width=40%") . '</li>';
if($columnActions and $commonActions)
{
$actions .= "<li class='divider'></li>";
}
$actions .= $commonActions;
$kanbanActions = '';
if(common::hasPriv('kanban', 'edit')) $kanbanActions .= '<li>' . html::a(helper::createLink('kanban', 'edit', "kanbanID=$kanban->id", '', true), '<i class="icon icon-edit"></i>' . $this->lang->kanban->edit, '', "class='iframe btn btn-link' data-width='75%'") . '</li>';
if(common::hasPriv('kanban', 'close') and $kanban->status == 'active') $kanbanActions .= '<li>' . html::a(helper::createLink('kanban', 'close', "kanbanID=$kanban->id", '', true), '<i class="icon icon-off"></i>' . $this->lang->kanban->close, '', "class='iframe btn btn-link'") . '</li>';
if(common::hasPriv('kanban', 'activate') and $kanban->status == 'closed') $kanbanActions .= '<li>' . html::a(helper::createLink('kanban', 'activate', "kanbanID=$kanban->id", '', true), '<i class="icon icon-magic"></i>' . $this->lang->kanban->activate, '', "class='iframe btn btn-link'") . '</li>';
if(common::hasPriv('kanban', 'delete')) $kanbanActions .= '<li>' . html::a(helper::createLink('kanban', 'delete', "kanbanID=$kanban->id"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->delete, 'hiddenwin', "class='btn btn-link'") . '</li>';
if($kanbanActions)
if($commonActions and $kanbanActions)
{
$actions .= $printRegionBtn ? "<li class='divider'></li>" . $kanbanActions : $kanbanActions;
$actions .= "<li class='divider'></li>";
}
$actions .= $kanbanActions;
$actions .= "</ul>";
}
@@ -3431,11 +3442,29 @@ class kanbanModel extends model
*/
public function getGroupBySpaceList($spaceIdList, $kanbanIdList = '')
{
return $this->dao->select('*')->from(TABLE_KANBAN)
$spaceList = $this->dao->select('*')->from(TABLE_KANBAN)
->where('deleted')->eq(0)
->andWhere('space')->in($spaceIdList)
->beginIF($kanbanIdList)->andWhere('id')->in($kanbanIdList)->fi()
->fetchGroup('space', 'id');
$kanbanIDList = array();
foreach($spaceList as $kanbanList) $kanbanIDList = array_merge_recursive($kanbanIDList, array_keys($kanbanList));
$cardsCount = $this->dao->select('kanban, COUNT(*) as count')->from(TABLE_KANBANCARD)
->where('deleted')->eq(0)
->andWhere('kanban')->in($kanbanIDList)
->groupBy('kanban')
->fetchPairs('kanban');
foreach($spaceList as $kanbanList)
{
foreach($kanbanList as $kanban)
{
$kanban->cardsCount = zget($cardsCount, $kanban->id, 0);
}
}
return $spaceList;
}
/**
+9 -3
View File
@@ -40,12 +40,12 @@
<div class='spaceTitle pull-left'>
<div><i class='icon-cube'></i></div>
<div>
<h4 title="<?php echo $space->name;?>">
<p class='spaceName' title="<?php echo $space->name;?>">
<?php if($space->status == 'closed'):?>
<span class="label label-closed"><?php echo $lang->kanban->closed;?></span>
<?php endif;?>
<?php echo $space->name;?>
</h4>
</p>
</div>
<p class="spaceDesc text-ellipsis text-primary" title='<?php echo empty($space->desc) ? $lang->kanban->emptyDesc : $space->desc;?>'><?php echo empty($space->desc) ? $lang->kanban->emptyDesc : $space->desc;?></p>
</div>
@@ -72,6 +72,9 @@
<div class='col' data-id='<?php echo $kanbanID?>'>
<div class='panel' data-url='<?php echo $this->createLink('kanban', 'view', "kanbanID=$kanbanID");?>'>
<div class='panel-heading'>
<?php if($browseType == 'cooperation' and $kanban->owner == $this->app->user->account):?>
<span class="label label-outline label-info kanban-label"><?php echo $lang->kanban->mine;?></span>
<?php endif;?>
<div class='kanban-name'>
<?php if($kanban->status == 'closed'):?>
<span class="label label-closed"><?php echo $lang->kanban->closed;?></span>
@@ -158,7 +161,10 @@
<?php endif;?>
</div>
<?php endif;?>
<div class='kanban-members-total pull-left'><?php echo sprintf($lang->kanban->teamSumCount, $teamCount);?></div>
<?php $teamCountLang = ($teamCount > 1) ? $lang->kanban->teamSumCount : str_replace("Pers", "Person", $lang->kanban->teamSumCount);?>
<div class='kanban-members-total pull-left'><?php echo sprintf($teamCountLang, $teamCount);?></div>
<?php $cardsCount = ($kanban->cardsCount > 1) ? str_replace("Card", "Cards", $lang->kanban->cardsCount) : $lang->kanban->cardsCount;?>
<div class='kanban-members-total pull-right'><?php echo empty($kanban->cardsCount) ? $lang->kanban->noCard : sprintf($cardsCount, $kanban->cardsCount);?></div>
</div>
</div>
</div>
Regular → Executable
+8 -2
View File
@@ -89,8 +89,14 @@ class messageModel extends model
}
}
$moduleName = $objectType == 'case' ? 'testcase' : $objectType;
$this->loadModel('mail')->sendmail($objectID, $actionID);
if($objectType == 'feedback')
{
$this->loadModel('feedback')->sendmail($objectID, $actionID);
}
else
{
$this->loadModel('mail')->sendmail($objectID, $actionID);
}
if(defined('RUN_MODE') and RUN_MODE == 'api') $config->requestType = $requestType;
}
+20 -5
View File
@@ -411,9 +411,12 @@ EOF;
* @access public
* @return void
*/
public function task($type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function task($type = 'assignedTo', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->loadModel('task');
$this->loadModel('execution');
$queryID = ($type == 'bySearch') ? (int)$param : 0;
/* Save session. */
if($this->app->viewType != 'json') $this->session->set('taskList', $this->app->getURI(true), 'execution');
@@ -430,9 +433,13 @@ EOF;
{
$tasks = $this->my->getAssignedByMe($this->app->user->account, 0, $pager, $sort, 0, 'task');
}
elseif($type == 'bySearch')
{
$tasks = $this->my->getTasksBySearch($this->app->user->account, 0, $pager, $sort, $queryID);
}
else
{
$tasks = $this->task->getUserTasks($this->app->user->account, $type, 0, $pager, $sort);
$tasks = $this->task->getUserTasks($this->app->user->account, $type, 0, $pager, $sort, $queryID);
}
$parents = array();
@@ -473,6 +480,9 @@ EOF;
/* Get the story language configuration. */
$this->app->loadLang('story');
$actionURL = $this->createLink('my', $this->app->rawMethod, "mode=task&browseType=bySearch&queryID=myQueryID");
$this->my->buildTaskSearchForm($queryID, $actionURL);
/* Assign. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->task;
$this->view->position[] = $this->lang->my->task;
@@ -505,12 +515,13 @@ EOF;
* @access public
* @return void
*/
public function bug($type = 'assignedTo', $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
public function bug($type = 'assignedTo', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* Save session. load Lang. */
$this->loadModel('bug');
if($this->app->viewType != 'json') $this->session->set('bugList', $this->app->getURI(true), 'qa');
$this->app->loadLang('bug');
$queryID = ($type == 'bySearch') ? (int)$param : 0;
if($this->app->viewType != 'json') $this->session->set('bugList', $this->app->getURI(true), 'qa');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
@@ -525,12 +536,16 @@ EOF;
}
else
{
$bugs = $this->loadModel('bug')->getUserBugs($this->app->user->account, $type, $sort, 0, $pager);
$bugs = $this->loadModel('bug')->getUserBugs($this->app->user->account, $type, $sort, 0, $pager, '', $queryID);
}
$bugs = $this->bug->checkDelayedBugs($bugs);
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug', false);
$actionURL = $this->createLink('my', $this->app->rawMethod, "mode=bug&browseType=bySearch&queryID=myQueryID");
$this->my->buildBugSearchForm($queryID, $actionURL);
/* assign. */
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->bug;
$this->view->position[] = $this->lang->my->bug;
+1
View File
@@ -44,6 +44,7 @@ $lang->my->storyConcept = 'Story Concept';
$lang->my->pri = 'Priority';
$lang->my->alert = 'You can click on your profile at the top right and select "Preference" to modify your information. ';
$lang->my->assignedToMe = 'AssignedToMe';
$lang->my->byQuery = 'Search';
$lang->my->indexAction = 'My Index';
$lang->my->calendarAction = 'My Calendar';
+1
View File
@@ -44,6 +44,7 @@ $lang->my->storyConcept = $config->URAndSR ? '默认需求概念组合' : '
$lang->my->pri = '优先级';
$lang->my->alert = '后续您可以点击右上方的头像,选择“个性化设置”修改信息。';
$lang->my->assignedToMe = '指派给我';
$lang->my->byQuery = '搜索';
$lang->my->indexAction = '地盘仪表盘';
$lang->my->calendarAction = '我的日程';
+164
View File
@@ -458,4 +458,168 @@ class myModel extends model
}
return $cases;
}
/**
* Build search form for task page of work.
*
* @param int $queryID
* @param string $actionURL
* @access public
* @return void
*/
public function buildTaskSearchForm($queryID, $actionURL)
{
$rawMethod = $this->app->rawMethod;
$this->loadModel('execution');
$this->app->loadConfig('execution');
$this->config->execution->search['module'] = $rawMethod . 'Task';
$this->config->execution->search['actionURL'] = $actionURL;
$this->config->execution->search['queryID'] = $queryID;
if($rawMethod == 'work')
{
unset($this->config->execution->search['fields']['closedReason']);
unset($this->config->execution->search['fields']['closedBy']);
unset($this->config->execution->search['fields']['canceledBy']);
unset($this->config->execution->search['fields']['closedDate']);
unset($this->config->execution->search['fields']['canceledDate']);
}
$projects = $this->loadModel('project')->getPairsByProgram();
$this->config->execution->search['params']['project']['values'] = $projects + array('all' => $this->lang->project->allProjects);
$executions = $this->execution->getPairs();
$this->config->execution->search['params']['execution']['values'] = $executions + array('all' => $this->lang->execution->allExecutions);
$this->config->execution->search['params']['module']['values'] = $this->loadModel('tree')->getAllModulePairs();
$this->loadModel('search')->setSearchParams($this->config->execution->search);
}
/**
* Get tasks by search.
*
* @param string $account
* @param int $limit
* @param object $pager
* @param string $orderBy
* @param int $queryID
* @access public
* @return array
*/
public function getTasksBySearch($account, $limit = 0, $pager = null, $orderBy = 'id_desc', $queryID = 0)
{
$moduleName = $this->app->rawMethod == 'work' ? 'workTask' : 'contributeTask';
$queryName = $moduleName . 'Query';
$formName = $moduleName . 'Form';
$taskIDList = array();
if($moduleName == 'contributeTask')
{
$tasksAssignedByMe = $this->getAssignedByMe($account, 0, $pager, $orderBy, 0, 'task');
foreach($tasksAssignedByMe as $taskID => $task)
{
$taskIDList[$taskID] = $taskID;
}
}
if($queryID)
{
$query = $this->loadModel('search')->getQuery($queryID);
if($query)
{
$this->session->set($queryName, $query->sql);
$this->session->set($formName, $query->form);
}
else
{
$this->session->set($queryName, ' 1 = 1');
}
}
else
{
if($this->session->$queryName == false) $this->session->set($queryName, ' 1 = 1');
}
$query = $this->session->$queryName;
$query = preg_replace('/`(\w+)`/', 't1.`$1`', $query);
$query = str_replace('t1.`project`', 't2.`project`', $query);
$tasks = $this->dao->select('t1.*, t2.id as executionID, t2.name as executionName, t2.type as executionType, t3.id as storyID, t3.title as storyTitle, t3.status AS storyStatus, t3.version AS latestStoryVersion')
->from(TABLE_TASK)->alias('t1')
->leftJoin(TABLE_EXECUTION)->alias('t2')->on("t1.execution = t2.id")
->leftJoin(TABLE_STORY)->alias('t3')->on('t1.story = t3.id')
->leftJoin(TABLE_PROJECT)->alias('t4')->on("t1.project = t4.id")
->where($query)
->andWhere('t1.deleted')->eq(0)
->andWhere('t2.deleted')->eq(0)
->andWhere('(t2.status')->ne('suspended')->orWhere('t4.status')->ne('suspended')->markRight(1)
->beginIF($moduleName == 'workTask')->andWhere("t1.assignedTo")->eq($account)
->beginIF($moduleName == 'contributeTask')
->andWhere('t1.openedBy', 1)->eq($account)
->orWhere('t1.closedBy')->eq($account)
->orWhere('t1.canceledBy')->eq($account)
->orWhere('t1.finishedby', 1)->eq($account)
->orWhere('t1.finishedList')->like("%,{$account},%")
->orWhere('t1.id')->in($taskIDList)
->markRight(1)
->fi()
->beginIF($this->config->vision)->andWhere('t1.vision')->eq($this->config->vision)->fi()
->beginIF($this->config->vision)->andWhere('t2.vision')->eq($this->config->vision)->fi()
->beginIF(!$this->app->user->admin)->andWhere('t1.execution')->in($this->app->user->view->sprints)->fi()
->orderBy($orderBy)
->beginIF($limit > 0)->limit($limit)->fi()
->page($pager)
->fetchAll('id');
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'task', false);
$taskTeam = $this->dao->select('*')->from(TABLE_TEAM)->where('root')->in(array_keys($tasks))->andWhere('type')->eq('task')->fetchGroup('root');
if(!empty($taskTeam))
{
foreach($taskTeam as $taskID => $team) $tasks[$taskID]->team = $team;
}
if($tasks) return $this->loadModel('task')->processTasks($tasks);
return array();
}
/**
* Build search form for bug page of work.
*
* @param int $queryID
* @param string $actionURL
* @access public
* @return void
*/
public function buildBugSearchForm($queryID, $actionURL)
{
$rawMethod = $this->app->rawMethod;
$this->loadModel('bug');
$this->app->loadConfig('bug');
$products = $this->loadModel('product')->getPairs();
$this->config->bug->search['module'] = $rawMethod . 'Bug';
$this->config->bug->search['actionURL'] = $actionURL;
$this->config->bug->search['queryID'] = $queryID;
if($rawMethod == 'work')
{
unset($this->config->bug->search['fields']['closedDate']);
unset($this->config->bug->search['fields']['closedBy']);
}
if($this->config->systemMode == 'new') $this->config->bug->search['params']['project']['values'] = $this->loadModel('project')->getPairsByProgram() + array('all' => $this->lang->bug->allProject) + array('' => '');
$this->config->bug->search['params']['execution']['values'] = $this->loadModel('execution')->getPairs();
$this->config->bug->search['params']['product']['values'] = $products + array('' => '');
$this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs();
$this->config->bug->search['params']['module']['values'] = $this->loadModel('tree')->getAllModulePairs();
$this->config->bug->search['params']['severity']['values'] = array(0 => '') + $this->lang->bug->severityList;
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs($products);
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
$this->loadModel('search')->setSearchParams($this->config->bug->search);
}
}
+9 -1
View File
@@ -25,15 +25,23 @@
echo html::a(inlink($app->rawMethod, "mode=$mode&type=closedBy"), "<span class='text'>{$lang->bug->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=assignedBy"), "<span class='text'>{$lang->bug->assignedByMe}</span>" . ($type == 'assignedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedBy' ? ' btn-active-text' : '') . "'");
}
else
{
echo html::a(inlink($app->rawMethod, "mode=$mode&type=assignedTo"), "<span class='text'>{$lang->bug->assignedToMe}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
}
?>
</div>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->my->byQuery;?></a>
</div>
<div id="mainContent">
<?php $dataModule = $app->rawMethod == 'work' ? 'workBug' : 'contributeBug';?>
<?php if(empty($bugs)):?>
<div class="cell<?php if($type == 'bySearch') echo ' show';?>" id="queryBox" data-module=<?php echo $dataModule;?>></div>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->bug->noBug;?></span></p>
</div>
<?php else:?>
<div class="cell<?php if($type == 'bySearch') echo ' show';?>" id="queryBox" data-module=<?php echo $dataModule;?>></div>
<form id='myBugForm' class="main-table table-bug" data-ride="table" method="post" action='<?php echo $this->createLink('bug', 'batchEdit', "productID=0");?>'>
<?php
$canBatchEdit = (common::hasPriv('bug', 'batchEdit') and $type == 'assignedTo');
@@ -43,7 +51,7 @@
$canBatchAction = ($canBatchEdit or $canBatchConfirm or $canBatchClose or $canBatchAssignTo);
?>
<table class="table has-sort-head table-fixed" id='bugList'>
<?php $vars = "mode=$mode&type=$type&orderBy=%s&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID"; ?>
<?php $vars = "mode=$mode&type=$type&param=myQueryID&orderBy=%s&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID"; ?>
<thead>
<tr>
<th class="c-id">
+17 -8
View File
@@ -18,29 +18,38 @@
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<?php $recTotalLabel = " <span class='label label-light label-badge'>{$pager->recTotal}</span>"; ?>
<?php if($app->rawMethod == 'contribute'):?>
<?php
echo html::a(inlink($app->rawMethod, "mode=$mode&type=openedBy"), "<span class='text'>{$lang->my->taskMenu->openedByMe}</span>" . ($type == 'openedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'openedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=finishedBy"), "<span class='text'>{$lang->my->taskMenu->finishedByMe}</span>" . ($type == 'finishedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'finishedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=closedBy"), "<span class='text'>{$lang->my->taskMenu->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=canceledBy"), "<span class='text'>{$lang->my->taskMenu->canceledByMe}</span>" . ($type == 'canceledBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'canceledBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=assignedBy"), "<span class='text'>{$lang->my->taskMenu->assignedByMe}</span>" . ($type == 'assignedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedBy' ? ' btn-active-text' : '') . "'");
if($app->rawMethod == 'contribute')
{
echo html::a(inlink($app->rawMethod, "mode=$mode&type=openedBy"), "<span class='text'>{$lang->my->taskMenu->openedByMe}</span>" . ($type == 'openedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'openedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=finishedBy"), "<span class='text'>{$lang->my->taskMenu->finishedByMe}</span>" . ($type == 'finishedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'finishedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=closedBy"), "<span class='text'>{$lang->my->taskMenu->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=canceledBy"), "<span class='text'>{$lang->my->taskMenu->canceledByMe}</span>" . ($type == 'canceledBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'canceledBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink($app->rawMethod, "mode=$mode&type=assignedBy"), "<span class='text'>{$lang->my->taskMenu->assignedByMe}</span>" . ($type == 'assignedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedBy' ? ' btn-active-text' : '') . "'");
}
else
{
echo html::a(inlink($app->rawMethod, "mode=$mode&type=assignedTo"), "<span class='text'>{$lang->my->taskMenu->assignedToMe}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
}
?>
<?php endif;?>
</div>
<a class="btn btn-link querybox-toggle" id='bysearchTab'><i class="icon icon-search muted"></i> <?php echo $lang->my->byQuery;?></a>
</div>
<div id="mainContent">
<?php $dataModule = $app->rawMethod == 'work' ? 'workTask' : 'contributeTask';?>
<?php if(empty($tasks)):?>
<div class="cell<?php if($type == 'bySearch') echo ' show';?>" id="queryBox" data-module=<?php echo $dataModule;?>></div>
<div class="table-empty-tip">
<p><span class="text-muted"><?php echo $lang->task->noTask;?></span></p>
</div>
<?php else:?>
<div class="cell<?php if($type == 'bySearch') echo ' show';?>" id="queryBox" data-module=<?php echo $dataModule;?>></div>
<form id='myTaskForm' class="main-table table-task skip-iframe-modal" method="post">
<?php $canBatchEdit = (common::hasPriv('task', 'batchEdit') and $type == 'assignedTo');?>
<?php $canBatchClose = (common::hasPriv('task', 'batchClose') and $type != 'closedBy');?>
<div class="table-responsive">
<table class="table has-sort-head table-fixed" id='taskTable'>
<?php $vars = "mode=$mode&type=$type&orderBy=%s&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID"; ?>
<?php $vars = "mode=$mode&type=$type&param=myQueryID&orderBy=%s&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID"; ?>
<thead>
<tr>
<th class="c-id">
+1
View File
@@ -38,3 +38,4 @@ $lang->personnel->copy = 'Copy Whitelist';
$lang->personnel->openedPGMTip = 'The program is public and can be accessed by users with Program privileges.';
$lang->personnel->selectObjectTips = 'Select %s to copy its whitelist';
$lang->personnel->countTip = ' (%s member)';
+1
View File
@@ -38,3 +38,4 @@ $lang->personnel->copy = 'Copy Whitelist';
$lang->personnel->openedPGMTip = 'The program is public and can be accessed by users with Program privileges.';
$lang->personnel->selectObjectTips = 'Select %s to copy its whitelist';
$lang->personnel->countTip = ' (%s member)';
+1
View File
@@ -38,3 +38,4 @@ $lang->personnel->copy = 'Copy Whitelist';
$lang->personnel->openedPGMTip = 'The program is public and can be accessed by users with Program privileges.';
$lang->personnel->selectObjectTips = 'Select %s to copy its whitelist';
$lang->personnel->countTip = ' (%s member)';
+1
View File
@@ -38,3 +38,4 @@ $lang->personnel->copy = 'Copy Whitelist';
$lang->personnel->openedPGMTip = 'The program is public and can be accessed by users with Program privileges.';
$lang->personnel->selectObjectTips = 'Select %s to copy its whitelist';
$lang->personnel->countTip = ' (%s member)';
+1
View File
@@ -38,3 +38,4 @@ $lang->personnel->copy = '复制白名单';
$lang->personnel->openedPGMTip = '该项目集是公开状态,有项目集视图权限的人员即可访问。';
$lang->personnel->selectObjectTips = '请选择一个%s白名单来复制';
$lang->personnel->countTip = '%s人)';
+1
View File
@@ -38,3 +38,4 @@ $lang->personnel->copy = '複製白名單';
$lang->personnel->openedPGMTip = '該項目集是公開狀態,有項目集視圖權限的人員即可訪問。';
$lang->personnel->selectObjectTips = '請選擇一個%s白名單來複制';
$lang->personnel->countTip = '%s人)';
+24 -1
View File
@@ -466,10 +466,11 @@ class personnelModel extends model
*
* @param int $objectID
* @param int $objectType
* @param bool $addCount
* @access public
* @return array
*/
public function getCopiedObjects($objectID, $objectType)
public function getCopiedObjects($objectID, $objectType, $addCount = false)
{
$objects = array();
@@ -512,6 +513,28 @@ class personnelModel extends model
}
unset($objects[$objectID]);
if($addCount)
{
$objectType = $objectType == 'sprint' ? 'execution' : $objectType;
$countPairs = $this->dao->select('root, COUNT(*) as count')->from(TABLE_TEAM)
->where('type')->eq($objectType)
->andWhere('root')->in(array_keys($objects))
->beginIF($objectType == 'execution')
->orWhere('( type')->eq('project')
->andWhere('root')->eq($parentID)->markRight(1)
->fi()
->groupBy('root')
->fetchPairs('root');
foreach($objects as $objectID => $objectName)
{
$memberCount = zget($countPairs, $objectID, 0);
$countTip = $memberCount > 1 ? str_replace('member', 'members', $this->lang->personnel->countTip) : $this->lang->personnel->countTip;
$objects[$objectID] = $objectName . sprintf($countTip, $memberCount);
}
}
return $objects;
}
+2
View File
@@ -34,6 +34,7 @@ $config->product->search['fields']['estimate'] = $lang->story->estimate;
$config->product->search['fields']['source'] = $lang->story->source;
$config->product->search['fields']['sourceNote'] = $lang->story->sourceNote;
$config->product->search['fields']['fromBug'] = $lang->story->fromBug;
$config->product->search['fields']['category'] = $lang->story->category;
$config->product->search['fields']['openedBy'] = $lang->story->openedBy;
$config->product->search['fields']['reviewedBy'] = $lang->story->reviewedBy;
@@ -68,6 +69,7 @@ $config->product->search['params']['estimate'] = array('operator' => '=',
$config->product->search['params']['source'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->story->sourceList);
$config->product->search['params']['sourceNote'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->product->search['params']['fromBug'] = array('operator' => '=', 'control' => 'input', 'values' => '');
$config->product->search['params']['category'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->story->categoryList);
$config->product->search['params']['openedBy'] = array('operator' => '=', 'control' => 'select', 'values' => 'users');
$config->product->search['params']['reviewedBy'] = array('operator' => 'include', 'control' => 'select', 'values' => 'users');
+1 -1
View File
@@ -30,6 +30,6 @@ $config->productplan->search['fields']['end'] = $lang->productplan->end;
$config->productplan->search['params']['id'] = array('operator' => '=', 'control' => 'input', 'values' => '');
$config->productplan->search['params']['title'] = array('operator' => 'include', 'control' => 'input', 'values' => '');
$config->productplan->search['params']['branch'] = array('operator' => '=', 'control' => 'select', 'values' => '');
$config->productplan->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->productplan->statusList);
$config->productplan->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => array('' => '') + $lang->productplan->statusList);
$config->productplan->search['params']['begin'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
$config->productplan->search['params']['end'] = array('operator' => '=', 'control' => 'input', 'values' => '', 'class' => 'date');
+17 -1
View File
@@ -1455,7 +1455,7 @@ class project extends control
$this->view->depts = array('' => '') + $this->dept->getOptionMenu();
$this->view->currentMembers = $currentMembers;
$this->view->members2Import = $members2Import;
$this->view->teams2Import = array('' => '') + $this->loadModel('personnel')->getCopiedObjects($projectID, 'project');
$this->view->teams2Import = array('' => '') + $this->loadModel('personnel')->getCopiedObjects($projectID, 'project', true);
$this->view->copyProjectID = $copyProjectID;
$this->display();
}
@@ -1993,4 +1993,20 @@ class project extends control
}
echo json_encode($response);
}
/**
* AJAX: get executions of a project in html select.
*
* @param int $projectID
* @param int $executionID
* @access public
* @return void
*/
public function ajaxGetExecutions($projectID, $executionID = 0)
{
$executions = array('' => '') + $this->loadModel('execution')->getPairs($projectID, 'all');
if($this->app->getViewType() == 'json') return print(json_encode($executionList));
return print(html::select('execution', $executions, $executionID, "class='form-control'"));
}
}
+1
View File
@@ -62,6 +62,7 @@ $lang->project->workdaysExceed = 'No more than『%s』working days';
$lang->project->teamMembersCount = ', there are %s team members.';
$lang->project->budgetNumber = '『Budget』must be numbers.';
$lang->project->budgetGe0 = '『Budget』must be greater than or equal to 0.';
$lang->project->allProjects = 'All Projects';
/* Fields. */
$lang->project->common = 'Project';
+1
View File
@@ -62,6 +62,7 @@ $lang->project->workdaysExceed = '可用工作日不能超过『%s』天';
$lang->project->teamMembersCount = ',团队成员共%s人。';
$lang->project->budgetNumber = '『预算』金额必须为数字。';
$lang->project->budgetGe0 = '『预算』金额必须大于等于0。';
$lang->project->allProjects = '所有项目';
/* Fields. */
$lang->project->common = '项目';
+1 -1
View File
@@ -98,7 +98,7 @@ $lang->story->sourceNote = '来源备注';
$lang->story->fromBug = '来源Bug';
$lang->story->title = "{$lang->SRCommon}名称";
$lang->story->type = "需求类型";
$lang->story->category = "";
$lang->story->category = "";
$lang->story->color = '标题颜色';
$lang->story->toBug = '转Bug';
$lang->story->spec = "描述";
+1 -1
View File
@@ -2986,7 +2986,7 @@ class storyModel extends model
->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
->beginIF($status == 'unclosed')->andWhere('t2.status')->ne('closed')->fi()
->beginIF($status == 'review')->andWhere('t2.status')->in('draft,changed')->fi()
->orderBy('t1.`order` desc')
->orderBy('t1.`order` desc, t1.`story` desc')
->fetchAll('id');
return empty($stories) ? array() : $this->formatStories($stories, $type);
}
+3
View File
@@ -5,3 +5,6 @@
.pull-left{max-width: 80%;}
.MRThWidth {width: 90px !important;}
.btn-edit-comment {z-index: 100;}
#assignedTo a {border-color: transparent;}
#assignedTo a:hover {border-color: rgba(0,0,0,.2);}
+2 -3
View File
@@ -112,7 +112,7 @@
<th class='w-40px'> <?php echo $lang->task->lblPri;?></th>
<th> <?php echo $lang->task->name;?></th>
<th class='w-100px'><?php echo $lang->task->deadline;?></th>
<th class='w-80px'> <?php echo $lang->task->assignedTo;?></th>
<th class='w-120px'> <?php echo $lang->task->assignedTo;?></th>
<th class='w-80px'> <?php echo $lang->task->status;?></th>
<th class='w-60px visible-lg'><?php echo $lang->task->consumedAB . $lang->task->lblHour;?></th>
<th class='w-60px visible-lg'><?php echo $lang->task->leftAB . $lang->task->lblHour;?></th>
@@ -132,13 +132,12 @@
</td>
<td class='text-left' title='<?php echo $child->name;?>'><a class="iframe" data-width="90%" href="<?php echo $this->createLink('task', 'view', "taskID=$child->id", '', true); ?>"><?php echo $child->name;?></a></td>
<td><?php echo $child->deadline;?></td>
<td><?php echo zget($users, $child->assignedTo);?></td>
<td id='assignedTo'><?php $this->task->printAssignedHtml($child, $users);?></td>
<td><?php echo $this->processStatus('task', $child);?></td>
<td class='visible-lg'><?php echo $child->consumed;?></td>
<td class='visible-lg'><?php echo $child->left;?></td>
<td class='c-actions'>
<?php
common::printIcon('task', 'assignTo', "executionID=$child->execution&taskID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'start', "taskID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'activate', "taskID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'close', "taskID=$child->id", $child, 'list', '', '', 'iframe showinonlybody', true);
+5 -1
View File
@@ -652,6 +652,9 @@ class upgradeModel extends model
case 'biz7.0.beta1':
$this->processViewFields();
break;
case 'biz7.0':
$this->processFlowPosition();
break;
}
}
@@ -943,7 +946,8 @@ class upgradeModel extends model
case '16_5_beta1': $confirmContent .= file_get_contents($this->getUpgradeFile('16.5.beta1'));
case '16_5': $confirmContent .= file_get_contents($this->getUpgradeFile('16.5'));
case '17_0_beta1': $confirmContent .= file_get_contents($this->getUpgradeFile('17.0.beta1'));
case '17_0_beta2':
case '17_0_beta2': $confirmContent .= file_get_contents($this->getUpgradeFile('17.0.beta2'));
case '17_0': $confirmContent .= file_get_contents($this->getUpgradeFile('17.0'));
}
return $confirmContent;
+26
View File
@@ -136,6 +136,9 @@ class userModel extends model
}
}
/* Put the current user first. */
$users = $this->processAccountSort($users);
/* Append empty, closed, and guest users. */
if(strpos($params, 'noempty') === false) $users = array('' => '') + $users;
if(strpos($params, 'noclosed') === false) $users = $users + array('closed' => 'Closed');
@@ -2623,6 +2626,10 @@ class userModel extends model
if(!empty($this->config->isINT)) $firstLetter = '';
$users[$account] = $firstLetter . ($user->realname ? $user->realname : $user->account);
}
/* Put the current user first. */
$users = $this->processAccountSort($users);
return array('' => '') + $users;
}
@@ -2828,4 +2835,23 @@ class userModel extends model
$admins = explode(',', trim($company->admins, ','));
$this->app->user = $this->dao->select('*')->from(TABLE_USER)->where('account')->eq($admins[0])->fetch();
}
/**
* Put the current user first.
*
* @param array $users
* @access public
* @return array
*/
public function processAccountSort($users = array())
{
if(isset($users[$this->app->user->account]))
{
$currentUser = array();
$currentUser[$this->app->user->account] = $users[$this->app->user->account];
unset($users[$this->app->user->account]);
$users = $currentUser + $users;
}
return $users;
}
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,5 +1,5 @@
/*!
* ZUI: ZUI Kanban View - v1.10.0 - 2022-06-02
* ZUI: ZUI Kanban View - v1.10.0 - 2022-06-08
* http://openzui.com
* GitHub: https://github.com/easysoft/zui.git
* Copyright (c) 2022 cnezsoft.com; Licensed MIT
File diff suppressed because one or more lines are too long