Merge branch 'sgm_fixbug'

This commit is contained in:
王怡栋
2022-01-18 08:57:18 +08:00
116 changed files with 4945 additions and 1480 deletions
+1 -1
View File
@@ -1 +1 @@
16.1
16.2
+1 -1
View File
@@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}}
if(!function_exists('getWebRoot')){function getWebRoot(){}}
/* 基本设置。Basic settings. */
$config->version = '16.1'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->version = '16.2'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it.
$config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP.
$config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time.
$config->timezone = 'Asia/Shanghai'; // 时区设置。 The time zone setting, for more see http://www.php.net/manual/en/timezones.php.
+1
View File
@@ -245,6 +245,7 @@ define('TABLE_KANBANCOLUMN', '`' . $config->db->prefix . 'kanbancolumn`');
define('TABLE_KANBANORDER', '`' . $config->db->prefix . 'kanbanorder`');
define('TABLE_KANBANGROUP', '`' . $config->db->prefix . 'kanbangroup`');
define('TABLE_KANBANCARD', '`' . $config->db->prefix . 'kanbancard`');
define('TABLE_KANBANCELL', '`' . $config->db->prefix . 'kanbancell`');
if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`');
if(!defined('TABLE_PROJECTSPEC')) define('TABLE_PROJECTSPEC', '`' . $config->db->prefix . 'projectspec`');
+15
View File
@@ -0,0 +1,15 @@
CREATE TABLE `zt_kanbancell` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`kanban` mediumint(8) NOT NULL,
`lane` mediumint(8) NOT NULL,
`column` mediumint(8) NOT NULL,
`type` char(30) NOT NULL,
`cards` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `zt_kanban` ADD `displayCards` smallint(6) NOT NULL default '0' AFTER `order`;
ALTER TABLE `zt_project` ADD `displayCards` smallint(6) NOT NULL default '0' AFTER `order`;
UPDATE `zt_grouppriv` SET `method` = 'taskKanban' WHERE `module` = 'execution' AND `method` = 'kanban';
+13 -2
View File
@@ -677,6 +677,7 @@ CREATE TABLE `zt_kanban` (
`archived` enum('0', '1') NOT NULL DEFAULT '0',
`status` enum('active','closed') NOT NULL default 'active',
`order` mediumint(8) NOT NULL DEFAULT '0',
`displayCards` smallint(6) NOT NULL default '0',
`createdBy` char(30) NOT NULL,
`createdDate` datetime NOT NULL,
`lastEditedBy` char(30) NOT NULL,
@@ -706,8 +707,6 @@ CREATE TABLE `zt_kanbancard` (
`kanban` mediumint(8) unsigned NOT NULL,
`region` mediumint(8) unsigned NOT NULL,
`group` mediumint(8) unsigned NOT NULL,
`lane` mediumint(8) unsigned NOT NULL,
`column` mediumint(8) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`pri` mediumint(8) unsigned NOT NULL,
`assignedTo` text NOT NULL,
@@ -731,6 +730,17 @@ CREATE TABLE `zt_kanbancard` (
`deleted` enum('0', '1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_kanbancell`;
CREATE TABLE `zt_kanbancell` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`kanban` mediumint(8) NOT NULL,
`lane` mediumint(8) NOT NULL,
`column` mediumint(8) NOT NULL,
`type` char(30) NOT NULL,
`cards` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_kanbangroup`;
CREATE TABLE `zt_kanbangroup` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
@@ -1010,6 +1020,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
`acl` char(30) NOT NULL DEFAULT 'open',
`whitelist` text NOT NULL,
`order` mediumint(8) unsigned NOT NULL,
`displayCards` smallint(6) NOT NULL default '0',
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `parent` (`parent`),
+31
View File
@@ -1,3 +1,34 @@
2022-01-17 16.2
28397 选择项目管理方式弹窗中增加专业研发看板
28398 实现专业研发看板项目的创建功能
28403 实现研发看板的新建按钮组功能
28405 实现专业研发看板在项目列表中的展示方式
28406 实现专业研发看板的默认视图功能
28420 打印研发看板的设置下拉菜单
28421 实现看板新增区域功能
29708 实现专业研发看板项目的看板视图
29709 实现研发看板卡片的更多设置菜单
29710 实现专业研发看板创建功能
29714 实现专业研发看板设置功能
29715 研发看板中实现区域的展开/收起功能
29717 研发看板中打印区域更多设置下拉菜单
29719 研发看板中实现区域的编辑功能
29720 研发看板中实现删除区域功能
29721 实现专业看板的全屏查看功能
29723 研发看板中实现泳道拖动排序功能
29724 研发看板中实现泳道组拖动排序功能
29725 研发看板中需求不支持拆分子需求
29726 研发看板中任务不支持拆分子任务
29728 研发看板中实现新增泳道功能
29729 研发看板中实现删除泳道功能
29730 研发看板中实现看板的开始/延期/挂起/关闭/删除操作
29732 鼠标悬浮到卡片头像上时显示人员姓名
29767 执行列表中增加看板icon标识
29890 看板的相关操作要记历史记录
30042 实现通用看板设置泳道高度功能
30043 实现执行看板设置泳道高度功能
30053 实现研发看板设置泳道高度功能
30068 通用看板中默认的模版横向不要有滚动条
2022-01-11 16.1
46672 实现计划卡片的更多设置功能
46671 计划看板中实现卡片的排序功能
+7 -1
View File
@@ -863,7 +863,7 @@ class actionModel extends model
/* Get actions. */
$actions = $this->dao->select('*')->from(TABLE_ACTION)
->where(1)
->where('objectType')->notIN('kanbanregion,kanbanlane,kanbancolumn')
->beginIF($period != 'all')->andWhere('date')->gt($begin)->fi()
->beginIF($period != 'all')->andWhere('date')->lt($end)->fi()
->beginIF($date)->andWhere('date' . ($direction == 'next' ? '<' : '>') . "'{$date}'")->fi()
@@ -1291,6 +1291,12 @@ class actionModel extends model
}
$action->objectLink = helper::createLink($moduleName, $methodName, $params);
if($action->objectType == 'execution')
{
$execution = $this->loadModel('execution')->getById($action->objectID);
if(!empty($execution) and $execution->type == 'kanban') $action->objectLink = helper::createLink('execution', 'kanban', "executionID={$action->objectID}");
}
if($action->objectType == 'doclib')
{
$docLib = $this->dao->select('type,product,project,execution,deleted')->from(TABLE_DOCLIB)->where('id')->eq($action->objectID)->fetch();
+2 -2
View File
@@ -905,7 +905,7 @@ class block extends control
foreach($projects as $projectID => $project)
{
if($project->model == 'scrum')
if($project->model == 'scrum' or $project->model == 'kanban')
{
$this->app->loadClass('pager', $static = true);
$pager = pager::init(0, 3, 1);
@@ -1809,7 +1809,7 @@ class block extends control
/* load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager(0, 3, 1);
$this->view->projects = $this->loadModel('project')->getInfoList('all', 30, 'id_desc', $pager);
$this->view->projects = $this->loadModel('project')->getInfoList('all', 'id_desc', $pager);
}
/**
@@ -134,7 +134,7 @@ $(function()
<?php foreach($projects as $project):?>
<div class="tab-pane fade<?php if($project->id == $selected) echo ' active in';?>" id="tab3Content<?php echo $project->id;?>">
<div class="table-row">
<?php if($project->model == 'scrum'):?>
<?php if($project->model == 'scrum' or $project->model == 'kanban'):?>
<div class='table-row'>
<div class="col-4 text-center">
<div><h4><?php echo $lang->block->story;?></h4></div>
+20 -14
View File
@@ -361,7 +361,7 @@ class bug extends control
/* Set from param if there is a object to transfer bug. */
setcookie('lastBugModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
$bugResult = $this->bug->create($from = isset($fromObjectIDKey) ? $fromObjectIDKey : '');
$bugResult = $this->bug->create($from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extras);
if(!$bugResult or dao::isError())
{
$response['result'] = 'fail';
@@ -662,14 +662,15 @@ class bug extends control
* @param int $productID
* @param int $executionID
* @param int $moduleID
* @param string $extra
* @access public
* @return void
*/
public function batchCreate($productID, $branch = '', $executionID = 0, $moduleID = 0)
public function batchCreate($productID, $branch = '', $executionID = 0, $moduleID = 0, $extra = '')
{
if(!empty($_POST))
{
$actions = $this->bug->batchCreate($productID, $branch);
$actions = $this->bug->batchCreate($productID, $branch, $extra);
/* Return bug id list when call the API. */
if($this->viewType == 'json')
@@ -693,8 +694,9 @@ class bug extends control
/* If executionID is setted, get builds and stories of this execution. */
if($executionID)
{
$builds = $this->loadModel('build')->getBuildPairs($productID, $branch, 'noempty', $executionID, 'execution');
$stories = $this->story->getExecutionStoryPairs($executionID);
$builds = $this->loadModel('build')->getBuildPairs($productID, $branch, 'noempty', $executionID, 'execution');
$stories = $this->story->getExecutionStoryPairs($executionID);
$execution = $this->loadModel('execution')->getById($executionID);
}
else
{
@@ -737,7 +739,7 @@ class bug extends control
$showFields = trim($showFields, ',');
}
$projectID = $this->lang->navGroup->bug == 'project' ? $this->session->project : 0;
$projectID = $this->lang->navGroup->bug == 'project' ? $this->session->project : (isset($execution) ? $execution->project : 0);
$this->view->customFields = $customFields;
$this->view->showFields = $showFields;
@@ -1344,14 +1346,15 @@ class bug extends control
* confirm a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function confirmBug($bugID)
public function confirmBug($bugID, $extra = '')
{
if(!empty($_POST))
{
$changes = $this->bug->confirm($bugID);
$changes = $this->bug->confirm($bugID, $extra);
if(dao::isError()) return print(js::error(dao::getError()));
$actionID = $this->action->create('bug', $bugID, 'bugConfirmed', $this->post->comment);
$this->action->logHistory($actionID, $changes);
@@ -1399,14 +1402,15 @@ class bug extends control
* Resolve a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function resolve($bugID)
public function resolve($bugID, $extra = '')
{
if(!empty($_POST))
{
$changes = $this->bug->resolve($bugID);
$changes = $this->bug->resolve($bugID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
@@ -1492,14 +1496,15 @@ class bug extends control
* Activate a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function activate($bugID)
public function activate($bugID, $extra = '')
{
if(!empty($_POST))
{
$changes = $this->bug->activate($bugID);
$changes = $this->bug->activate($bugID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
@@ -1534,14 +1539,15 @@ class bug extends control
* Close a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function close($bugID)
public function close($bugID, $extra = '')
{
if(!empty($_POST))
{
$changes = $this->bug->close($bugID);
$changes = $this->bug->close($bugID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
$actionID = $this->action->create('bug', $bugID, 'Closed', $this->post->comment);
+67 -12
View File
@@ -49,11 +49,15 @@ class bugModel extends model
* Create a bug.
*
* @param string $from object that is transfered to bug.
* @param string $extras.
* @access public
* @return array|bool
*/
public function create($from = '')
public function create($from = '', $extras = '')
{
$extras = str_replace(array(',', ' '), array('&', ''), $extras);
parse_str($extras, $output);
$now = helper::now();
$bug = fixer::input('post')
->setDefault('openedBy', $this->app->user->account)
@@ -100,7 +104,12 @@ class bugModel extends model
$this->file->saveUpload('bug', $bugID);
empty($bug->case) ? $this->loadModel('score')->create('bug', 'create', $bugID) : $this->loadModel('score')->create('bug', 'createFormCase', $bug->case);
if($bug->execution) $this->loadModel('kanban')->updateLane($bug->execution, 'bug');
if($bug->execution)
{
$this->loadModel('kanban');
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($bug->execution, $output['laneID'], $output['columnID'], 'bug', $bugID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($bug->execution, 'bug');
}
/* Callback the callable method to process the related data for object that is transfered to bug. */
if($from && is_callable(array($this, $this->config->bug->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->bug->fromObjects[$from]['callback']), $bugID);
@@ -115,13 +124,18 @@ class bugModel extends model
*
* @param int $productID
* @param int $branch
* @param string $extra
* @access public
* @return void
*/
public function batchCreate($productID, $branch = 0)
public function batchCreate($productID, $branch = 0, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
/* Load module and init vars. */
$this->loadModel('action');
$this->loadModel('kanban');
$branch = (int)$branch;
$productID = (int)$productID;
$now = helper::now();
@@ -257,8 +271,13 @@ class bugModel extends model
$bugID = $this->dao->lastInsertID();
$this->executeHooks($bugID);
if($bug->execution) $this->loadModel('kanban')->updateLane($bug->execution, 'bug');
if($bug->execution)
{
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($bug->execution, $output['laneID'], $output['columnID'], 'bug', $bugID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($bug->execution, 'bug');
}
/* When the bug is created by uploading the image, add the image to the file of the bug. */
$this->loadModel('score')->create('bug', 'create', $bugID);
if(!empty($data->uploadImage[$i]) and !empty($file))
@@ -907,11 +926,15 @@ class bugModel extends model
* Confirm a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function confirm($bugID)
public function confirm($bugID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$now = helper::now();
$oldBug = $this->getById($bugID);
@@ -929,7 +952,12 @@ class bugModel extends model
if(!dao::isError())
{
$this->loadModel('score')->create('bug', 'confirmBug', $oldBug);
if($oldBug->execution) $this->loadModel('kanban')->updateLane($oldBug->execution, 'bug');
if($oldBug->execution)
{
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldBug->execution, 'bug', $bugID);
if(isset($output['toColID'])) $this->kanban->moveCard($bugID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
}
return common::createChanges($oldBug, $bug);
}
}
@@ -964,11 +992,15 @@ class bugModel extends model
* Resolve a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function resolve($bugID)
public function resolve($bugID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$now = helper::now();
$oldBug = $this->getById($bugID);
$bug = fixer::input('post')
@@ -1042,7 +1074,12 @@ class bugModel extends model
if(!dao::isError())
{
$this->loadModel('score')->create('bug', 'resolve', $oldBug);
if($oldBug->execution) $this->loadModel('kanban')->updateLane($oldBug->execution, 'bug');
if($oldBug->execution)
{
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldBug->execution, 'bug', $bugID);
if(isset($output['toColID'])) $this->kanban->moveCard($bugID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
}
/* Link bug to build and release. */
$this->linkBugToBuild($bugID, $bug->resolvedBuild);
@@ -1218,11 +1255,15 @@ class bugModel extends model
* Activate a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function activate($bugID)
public function activate($bugID, $extra)
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$bugID = (int)$bugID;
$oldBug = $this->getById($bugID);
$solveBuild = $this->dao->select('id')
@@ -1262,7 +1303,12 @@ class bugModel extends model
$this->dao->update(TABLE_BUILD)->set('bugs')->eq($build->bugs)->where('id')->eq((int)$solveBuild)->exec();
}
if($oldBug->execution) $this->loadModel('kanban')->updateLane($oldBug->execution, 'bug');
if($oldBug->execution)
{
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldBug->execution, 'bug', $bugID);
if(isset($output['toColID'])) $this->kanban->moveCard($bugID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
}
$bug->activatedCount += 1;
return common::createChanges($oldBug, $bug);
}
@@ -1271,11 +1317,15 @@ class bugModel extends model
* Close a bug.
*
* @param int $bugID
* @param string $extra
* @access public
* @return void
*/
public function close($bugID)
public function close($bugID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$now = helper::now();
$oldBug = $this->getById($bugID);
$bug = fixer::input('post')
@@ -1291,7 +1341,12 @@ class bugModel extends model
->get();
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->where('id')->eq((int)$bugID)->exec();
if($oldBug->execution) $this->loadModel('kanban')->updateLane($oldBug->execution, 'bug');
if($oldBug->execution)
{
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldBug->execution, 'bug', $bugID);
if(isset($output['toColID'])) $this->kanban->moveCard($bugID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
}
return common::createChanges($oldBug, $bug);
}
+1
View File
@@ -117,6 +117,7 @@ $lang->icons['mail'] = 'envelope';
$lang->icons['trash'] = 'trash';
$lang->icons['extension'] = 'th-large';
$lang->icons['app'] = 'th-large';
$lang->icons['kanban'] = 'kanban';
$lang->icons['results'] = 'list-alt';
$lang->icons['create'] = 'plus';
+2
View File
@@ -29,6 +29,7 @@ $lang->and = 'and';
$lang->zentaoPMS = 'ZenTao';
$lang->pmsName = 'ALM';
$lang->proName = 'Pro';
$lang->bizName = 'Biz';
$lang->logoImg = 'zt-logo-en.png';
$lang->welcome = "%s ALM";
$lang->logout = 'Logout';
@@ -157,6 +158,7 @@ $lang->repo->common = 'Code';
$lang->report->common = 'Statistic';
$lang->system->common = 'System';
$lang->admin->common = 'Admin';
$lang->story->common = 'Story';
$lang->task->common = 'Task';
$lang->bug->common = 'Bug';
$lang->testcase->common = 'Testcase';
+6 -1
View File
@@ -289,6 +289,11 @@ $lang->waterfall->menu->design['subMenu']->dbds = array('link' => "{$lang->d
$lang->waterfall->menu->design['subMenu']->ads = array('link' => "{$lang->design->ADS}|design|browse|projectID=%s&productID=0&browseType=ADS");
$lang->waterfall->menu->design['subMenu']->bysearch = array('link' => '<a href="javascript:;" class="querybox-toggle"><i class="icon-search icon"></i> ' . $lang->searchAB . '</a>');
/* Kanban project menu. */
$lang->kanban->menu = new stdclass();
$lang->kanban->menuOrder = array();
$lang->kanban->dividerMenu = '';
/* Execution menu. */
$lang->execution->homeMenu = new stdclass();
$lang->execution->homeMenu->all = array('link' => "{$lang->execution->all}|execution|all|", 'alias' => 'batchedit');
@@ -296,7 +301,7 @@ if($config->systemMode == 'new') $lang->execution->homeMenu->executionkanban = a
$lang->execution->menu = new stdclass();
$lang->execution->menu->task = array('link' => "{$lang->task->common}|execution|task|executionID=%s", 'subModule' => 'task,tree', 'alias' => 'importtask,importbug');
$lang->execution->menu->kanban = array('link' => "$lang->executionKanban|execution|kanban|executionID=%s");
$lang->execution->menu->kanban = array('link' => "$lang->executionKanban|execution|taskkanban|executionID=%s");
$lang->execution->menu->burn = array('link' => "$lang->burn|execution|burn|executionID=%s");
$lang->execution->menu->view = array('link' => "$lang->view|execution|grouptask|executionID=%s", 'alias' => 'grouptask,tree,taskeffort,gantt,calendar,relation,maintainrelation');
$lang->execution->menu->story = array('link' => "$lang->SRCommon|execution|story|executionID=%s", 'subModule' => 'story', 'alias' => 'batchcreate,linkstory,storykanban');
+2
View File
@@ -29,6 +29,7 @@ $lang->and = '和';
$lang->zentaoPMS = '禅道';
$lang->pmsName = '开源版';
$lang->proName = '专业版';
$lang->bizName = '企业版';
$lang->logoImg = 'zt-logo.png';
$lang->welcome = "%s项目管理系统";
$lang->logout = '退出';
@@ -157,6 +158,7 @@ $lang->repo->common = '代码';
$lang->report->common = '统计';
$lang->system->common = '组织';
$lang->admin->common = '后台';
$lang->story->common = $lang->SRCommon;
$lang->task->common = '任务';
$lang->bug->common = 'Bug';
$lang->testcase->common = '用例';
+1 -1
View File
@@ -528,7 +528,7 @@ class commonModel extends model
$attr = "class='iframe' data-width='650px'";
break;
case 'project':
if(isset($config->maxVersion) and!defined('TUTORIAL'))
if(!defined('TUTORIAL'))
{
$params = "programID=0&copyProjectID=0&extra=from=global";
$createMethod = 'createGuide';
+4 -5
View File
@@ -791,6 +791,7 @@ class doc extends control
$doclib = $this->doc->getLibById($doclibID);
$users = $this->user->getPairs('noletter|noempty|noclosed');
$selectedUser = $doclib->users;
if($doclib->acl != 'custom' and !empty($doclib->project) and $acl == 'custom')
{
$project = $this->loadModel('project')->getById($doclib->project);
@@ -798,12 +799,10 @@ class doc extends control
$stakeholders = $this->loadModel('stakeholder')->getStakeHolderPairs($doclib->project);
$whitelist = implode(',', array_keys($projectTeams + $stakeholders)) . $project->whitelist . ',' . $project->PM . ',' . $doclib->users;
return print(html::select('users[]', $users, $whitelist, "class='form-control chosen' multiple"));
}
else
{
return print(html::select('users[]', $users, $doclib->users, "class='form-control chosen' multiple"));
$selectedUser = $whitelist;
}
return print(html::select('users[]', $users, $selectedUser, "class='form-control chosen' multiple"));
}
/**
+1 -1
View File
@@ -1377,7 +1377,7 @@ class docModel extends model
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->andWhere('type')->in('sprint,stage')
->andWhere('type')->in('sprint,stage,kanban')
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
->orderBy('order_asc')
->fetchAll('id');
+3
View File
@@ -11,6 +11,9 @@ $config->execution->list->exportFields = 'id,name,projectName,code,PM,end,status
$config->execution->modelList['scrum'] = 'sprint';
$config->execution->modelList['waterfall'] = 'stage';
$config->execution->modelList['kanban'] = 'kanban';
$config->execution->statusActions = array('start', 'putoff', 'suspend', 'close', 'activate');
global $lang, $app;
$app->loadLang('task');
+125 -18
View File
@@ -24,7 +24,7 @@ class execution extends control
* @var string
* @access public
*/
public $objectType;
public $objectType = 'execution';
/**
* Construct function, Set executions.
@@ -47,7 +47,6 @@ class execution extends control
{
if(!$this->executions and $this->methodName != 'index' and $this->methodName != 'create' and $this->app->getViewType() != 'mhtml') $this->locate($this->createLink('execution', 'create'));
}
$this->objectType = $this->config->systemMode == 'classic' ? 'project' : 'execution';
}
/**
@@ -125,7 +124,9 @@ class execution extends control
$browseType = strtolower($status);
/* Get products by execution. */
$execution = $this->commonAction($executionID, $status);
$execution = $this->commonAction($executionID, $status);
if($execution->type == 'kanban') $this->locate($this->createLink('execution', 'kanban', "executionID=$executionID"));
$executionID = $execution->id;
$products = $this->product->getProductPairsByProject($executionID);
setcookie('preExecutionID', $executionID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
@@ -1263,6 +1264,15 @@ class execution extends control
}
$project = $this->project->getByID($projectID);
if(!empty($project) and $project->model == 'kanban')
{
global $lang;
$executionLang = $lang->execution->common;
$lang->executionCommon = $lang->execution->kanban;
$lang->execution->common = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
$lang->execution->common = $executionLang;
}
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
@@ -1289,6 +1299,7 @@ class execution extends control
$this->view->tips = $this->fetch('execution', 'tips', "executionID=$executionID");
$this->view->executionID = $executionID;
$this->view->projectID = $projectID;
$this->view->project = $project;
$this->display();
exit;
}
@@ -1376,6 +1387,16 @@ class execution extends control
}
}
if(!empty($projectID) and $project->model == 'kanban')
{
$execution = $this->execution->getById($executionID);
$this->loadModel('kanban')->createRDKanban($execution);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
if($this->app->tab == 'project') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('project', 'index', "projectID=$projectID")));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('kanban', "executionID=$executionID")));
}
if(!empty($planID))
{
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('create', "projectID=$projectID&executionID=$executionID&copyExecutionID=&planID=$planID&confirm=no")));
@@ -1430,6 +1451,7 @@ class execution extends control
$this->view->copyExecution = isset($copyExecution) ? $copyExecution : '';
$this->view->from = $this->app->tab;
$this->view->isStage = (isset($project->model) and $project->model == 'waterfall') ? true : false;
$this->view->project = $project;
$this->display();
}
@@ -1451,6 +1473,14 @@ class execution extends control
$this->app->loadLang('stage');
$this->app->loadLang('programplan');
$browseExecutionLink = $this->createLink('execution', 'browse', "executionID=$executionID");
$execution = $this->execution->getById($executionID);
if($execution->type == 'kanban')
{
global $lang;
$lang->executionCommon = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
}
if(!empty($_POST))
{
@@ -1502,6 +1532,7 @@ class execution extends control
$this->executeHooks($executionID);
if($_POST['status'] == 'doing') $this->loadModel('common')->syncPPEStatus($executionID);
if($execution->type == 'kanban') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('view', "executionID=$executionID")));
}
@@ -1509,10 +1540,8 @@ class execution extends control
$this->execution->setMenu($executionID);
$executions = array('' => '') + $this->executions;
$execution = $this->execution->getById($executionID);
$managers = $this->execution->getDefaultManagers($executionID);
/* Remove current execution from the executions. */
unset($executions[$executionID]);
@@ -1685,6 +1714,7 @@ class execution extends control
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
@@ -1757,6 +1787,7 @@ class execution extends control
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
@@ -1792,6 +1823,7 @@ class execution extends control
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
@@ -1834,6 +1866,7 @@ class execution extends control
{
$execution = $this->commonAction($executionID);
$executionID = $execution->id;
if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban;
if(!empty($_POST))
{
@@ -1922,6 +1955,66 @@ class execution extends control
/**
* Kanban.
*
* @param int $executionID
* @param string $browseType
* @param string $orderBy
* @param string $groupBy
* @access public
* @return void
*/
public function kanban($executionID, $browseType = 'all', $orderBy = 'id_asc', $groupBy = 'default')
{
if(empty($groupBy)) $groupBy = 'default';
$this->lang->execution->menu = new stdclass();
$execution = $this->commonAction($executionID);
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy);
$executionActions = array();
foreach($this->config->execution->statusActions as $action)
{
if($this->execution->isClickable($execution, $action)) $executionActions[] = $action;
}
$userList = array();
$users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$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;
}
/* Get execution's product. */
$productID = 0;
$products = $this->loadModel('product')->getProducts($execution->project);
if($products) $productID = key($products);
$plans = $this->execution->getPlans($products);
$allPlans = array('' => '');
if(!empty($plans))
{
foreach($plans as $plan) $allPlans += $plan;
}
$this->view->title = $this->lang->kanban->view;
$this->view->users = $users;
$this->view->regions = $kanbanData;
$this->view->execution = $execution;
$this->view->userList = $userList;
$this->view->browseType = $browseType;
$this->view->orderBy = $orderBy;
$this->view->groupBy = $groupBy;
$this->view->productID = $productID;
$this->view->allPlans = $allPlans;
$this->view->kanbanData = $kanbanData;
$this->view->executionActions = $executionActions;
$this->display();
}
/**
* Task kanban.
*
* @param int $executionID
* @param string $browseType story|bug|task|all
* @param string $orderBy
@@ -1929,18 +2022,16 @@ class execution extends control
* @access public
* @return void
*/
public function kanban($executionID, $browseType = '', $orderBy = 'order_asc', $groupBy = '')
public function taskKanban($executionID, $browseType = '', $orderBy = 'order_asc', $groupBy = '')
{
if(empty($browseType)) $browseType = $this->session->kanbanType ? $this->session->kanbanType : 'all';
if(empty($groupBy) and $browseType != 'all') $groupBy = $this->session->{'kanbanGroupBy' . $browseType} ? $this->session->{'kanbanGroupBy' . $browseType} : 'default';
if(empty($groupBy) and $browseType == 'all') $groupBy = 'default';
if(empty($groupBy)) $groupBy = 'default';
/* Save to session. */
$uri = $this->app->getURI(true);
$this->app->session->set('taskList', $uri, 'execution');
$this->app->session->set('bugList', $uri, 'qa');
$this->app->session->set('kanbanType', $browseType, 'execution');
$this->app->session->set('kanbanGroupBy' . $browseType, $groupBy, 'execution');
/* Load language. */
$this->app->loadLang('story');
@@ -2303,6 +2394,12 @@ class execution extends control
if($tips) $tips = str_replace($this->lang->executionCommon, $this->lang->project->stage, $tips);
$this->lang->execution->confirmDelete = str_replace($this->lang->executionCommon, $this->lang->project->stage, $this->lang->execution->confirmDelete);
}
elseif($type == 'kanban')
{
global $lang;
$lang->executionCommon = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
}
echo js::confirm($tips . sprintf($this->lang->execution->confirmDelete, $this->executions[$executionID]), $this->createLink('execution', 'delete', "executionID=$executionID&confirm=yes"));
exit;
@@ -2310,6 +2407,7 @@ class execution extends control
else
{
/* Delete execution. */
$execution = $this->execution->getByID($executionID);
$this->dao->update(TABLE_EXECUTION)->set('deleted')->eq(1)->where('id')->eq($executionID)->exec();
$this->loadModel('action')->create('execution', $executionID, 'deleted', '', ACTIONMODEL::CAN_UNDELETED);
$this->execution->updateUserView($executionID);
@@ -2318,7 +2416,7 @@ class execution extends control
$this->executeHooks($executionID);
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
die(js::reload('parent'));
return print(js::reload('parent'));
}
}
@@ -2528,16 +2626,17 @@ class execution extends control
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @param string $extra
* @access public
* @return void
*/
public function linkStory($objectID = 0, $browseType = '', $param = 0, $recTotal = 0, $recPerPage = 50, $pageID = 1)
public function linkStory($objectID = 0, $browseType = '', $param = 0, $recTotal = 0, $recPerPage = 50, $pageID = 1, $extra = '')
{
$this->loadModel('story');
$this->loadModel('product');
/* Get projects, executions and products. */
$object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage');
$object = $this->project->getByID($objectID, $this->app->tab == 'project' ? 'project' : 'sprint,stage,kanban');
$products = $this->product->getProducts($objectID);
$browseLink = $this->createLink($this->app->tab == 'project' ? 'projectstory' : 'execution', 'story', "objectID=$objectID");
@@ -2552,7 +2651,7 @@ class execution extends control
if(!empty($_POST))
{
$this->execution->linkStory($objectID);
$this->execution->linkStory($objectID, array(), array(), $extra);
if($object->type != 'project' and $object->project != 0) $this->execution->linkStory($object->project);
if(isonlybody()) die(js::reload('parent'));
@@ -2872,7 +2971,7 @@ class execution extends control
$projects = $this->loadModel('program')->getProjectList(0, 'all', 0, 'order_asc', null, 0, 0, true);
$executionGroups = $this->dao->select('*')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->andWhere('type')->in('sprint,stage')
->andWhere('type')->in('sprint,stage,kanban')
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
->beginIF($this->config->systemMode == 'new')->andWhere('project')->in(array_keys($projects))->fi()
->orderBy('id_desc')
@@ -3284,10 +3383,11 @@ class execution extends control
* @param int $planID
* @param int $productID
* @param string $fromMethod
* @param string $extra
* @access public
* @return void
*/
public function importPlanStories($executionID, $planID, $productID = 0, $fromMethod = 'story')
public function importPlanStories($executionID, $planID, $productID = 0, $fromMethod = 'story', $extra = '')
{
$planStories = $planProducts = array();
$planStory = $this->loadModel('story')->getPlanStories($planID);
@@ -3310,7 +3410,7 @@ class execution extends control
$projectID = $this->dao->findByID($executionID)->from(TABLE_EXECUTION)->fetch('project');
$planStories = array_keys($planStory);
$this->execution->linkStory($executionID, $planStories, $planProducts);
$this->execution->linkStory($executionID, $planStories, $planProducts, $extra);
if($this->config->systemMode == 'new' and $executionID != $projectID) $this->execution->linkStory($projectID, $planStories, $planProducts);
}
@@ -3321,7 +3421,14 @@ class execution extends control
$moduleName = 'projectstory';
$param = "projectID=$executionID";
}
if($count != 0) echo js::alert(sprintf($this->lang->execution->haveDraft, $count)) . js::locate($this->createLink($moduleName, 'story', $param));
if($execution->type == 'kanban')
{
global $lang;
$lang->executionCommon = $lang->execution->kanban;
include $this->app->getModulePath('', 'execution') . 'lang/' . $this->app->getClientLang() . '.php';
}
if($count != 0) echo js::alert(sprintf($this->lang->execution->haveDraft, $count)) . js::locate($this->createLink($moduleName, $execution->type == 'sprint' ? 'story' : 'kanban', $param));
die(js::locate(helper::createLink($moduleName, $fromMethod, $param)));
}
@@ -3440,7 +3547,7 @@ class execution extends control
$enterTime = date('Y-m-d H:i:s', $enterTime);
$lastEditedTime = $this->dao->select("max(lastEditedTime) as lastEditedTime")->from(TABLE_KANBANLANE)->where('execution')->eq($executionID)->fetch('lastEditedTime');
if($lastEditedTime > $enterTime)
if($lastEditedTime > $enterTime or $groupBy != 'default')
{
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType, $groupBy);
die(json_encode($kanbanGroup));
+1
View File
@@ -22,3 +22,4 @@ td.flex span.project-type-label {margin-left: 5px; min-width: 36px;}
.c-progress, .c-burn {width: 60px;}
.c-percent, .c-realBegan, .c-end, .c-begin, .c-realEnd{width: 100px;}
.c-action {text-align: center;}
.c-name > span {margin-right: 2px;}
+89 -42
View File
@@ -1,5 +1,84 @@
#main {padding-bottom: 10px;}
#main > .container {max-width: 1960px!important; padding: 0 10px}
#TRAction .icon {padding: 0px 6px; font-size: 14px}
.has-error .form-control {border-color: #ff4268!important;}
.has-error .form-control:focus {box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 6px #f5b2a5!important;}
.has-error .form-control::placeholder {color: #ff4268!important;}
.label.text-red {background: #ff4268 !important; color: #ffffff;}
.dropdown-menu > li {padding: 0 10px;}
.dropdown-menu > li > a > .icon {position: relative; left: -5px;}
#kanbanContainer {padding-bottom: 0; margin-bottom: 0;}
#kanbanContainer.fullscreen {overflow: auto;}
.region {border: 1px solid #dcdcdc; background-color: #fff;}
.region + .region {margin-top: 20px;}
.region .region-header {padding: 10px;}
.region .region-header span {font-size: 14px; color: #999EAB}
.region .region-header label {color: #999; background: transparent; border: 1px solid #ddd; margin-left: 10px; margin-right: 10px}
.region .region-header .action {float: right}
.region .region-header .icon-double-angle-up,.icon-double-angle-down {cursor: pointer;}
.region .kanban-header-sub-cols .kanban-header-col > .title {max-width: 100% !important;min-width:240px;}
.region .sort .region-header {cursor: move;}
.region .kanban {padding: 0px 10px 10px 10px; min-height: unset; overflow: auto}
.region .kanban .form-actions {margin: 0;}
.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;}
.region .kanban-board + .kanban-board {margin-top: 20px;}
.region .kanban-board > .kanban-header > .kanban-group-header {padding: 8px 2px; width: 20px;}
.region .kanban-board > .kanban-header > .kanban-group-header:hover {cursor: move;}
.region .kanban-board.sort > .kanban-header {padding-left: 0;}
.region .kanban-header {border-bottom: none!important; min-width: max-content; min-width: -moz-max-content;}
.region .kanban-header-col > .title {max-width: 80% !important;}
.region .kanban-header-col > .title {margin: 0}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial; max-width: 140px !important;}
.region .kanban-header-col > .title > .text-grey {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .count {opacity: .5; font-weight: bold; color: #8b91a2;}
.region .kanban-header-col > .title > .error {color: #333333; padding-left: 2px; font-size: 10px; padding-top: 1px; padding-right: 2px;}
.kanban-affixed .kanban-header-col > .title > .text {color: #fff!important}
.region .kanban-header-col > .actions {position: relative; right: -30px;}
.region .kanban-header-parent-col > .actions {position: absolute; right: 0px;}
.region .kanban-header-sub-cols .actions {position: absolute; right: 0px;}
.region .kanban-lane {position: relative; border-bottom: none!important; min-height: 200px !important}
.region .kanban-lane > .kanban-lane-name > .text {margin: auto 0; max-height: 150px; overflow: hidden; text-overflow: ellipsis}
.region .kanban-lane > .kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
.region .kanban-lane > .kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
.region .kanban-lane > .kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
.region .kanban-lane > .kanban-lane-name:hover > .actions {opacity: 1;}
.region .kanban-lane.sort > .kanban-lane-name {cursor: move;}
.region .kanban-lane-col {max-height: unset !important; overflow: auto!important; position: relative;}
.region .kanban-lane-col.has-scrollbar > .kanban-lane-actions {position: absolute; background-color: inherit; bottom: 0; left: 0; right: 0;}
.region .kanban-lane-items {overflow: auto; padding-bottom: 10px;}
.region .kanban-item {position: relative}
.kanban-card {height: auto !important; padding: 8px 14px !important; min-height: 60px;}
.cardcolor {width:40px; height: 14px; float: left; margin-left: 5px; margin-right: 5px; margin-top: 2px; border-radius: 2px;}
#cardcolormenu {padding:2px 2px; width: 100px;}
.kanban-col[data-type=ADD] {display: none;}
.kanban-col[data-type=EMPTY] {display: none;}
.kanban-item:hover .title {padding-right: 10px;}
.gray .actions {display:none;}
.c-type {width: 150px !important; overflow: unset;}
.c-group {width: 190px !important; overflow: unset;}
.c-type {margin-left: 0px !important;}
#kanbanScaleControl {width: 100px;}
#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;}
#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;}
#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;}
#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;}
#kanbanActionMenu {top: 24px; right: auto;}
.kanban + .kanban {margin-top: 15px}
.kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
@@ -16,43 +95,11 @@
.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-header-col > .actions {padding-top: 22px}
.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;}
.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
.kanban-lane-name:hover > .actions {opacity: 1;}
.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
#kanbanContainer {margin: 0;}
#kanbanContainer > .panel-body {overflow: auto;}
#kanbans .kanban {min-height: initial;}
#kanbans .kanban-lane {min-height: 150px; margin-top: 2px;}
#kanbans .kanban-affixed > .kanban-header {top: 100px;}
#kanbans .kanban-card[data-scale-size="4"] {padding: 8px;}
#kanbans .kanban-card[data-scale-size="3"] {padding: 3px 4px;}
#kanbans .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;}
#kanbans .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;}
#kanbans .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;}
#kanbans .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;}
#kanbans .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;}
#kanbans .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;}
#kanbanScaleControl {width: 100px;}
#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;}
#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;}
#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;}
#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;}
#type_chosen .icon-kanban {padding-right: 10px; font-size: 15px;}
.dropdown-menu a {text-align: left;}
.scrollbar-hover {max-height: 2000px; overflow: scroll !important;}
.c-type {width: 150px !important; overflow: unset;}
.c-group {width: 190px !important; overflow: unset;}
.c-type {margin-left: 0px !important;}
#kanban .kanban-card[data-scale-size="4"] {padding: 8px;}
#kanban .kanban-card[data-scale-size="3"] {padding: 3px 4px;}
#kanban .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;}
#kanban .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;}
#kanban .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;}
#kanban .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;}
#kanban .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;}
#kanban .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;}
+58
View File
@@ -0,0 +1,58 @@
#main {padding-bottom: 10px;}
#main > .container {max-width: 1960px!important; padding: 0 10px}
.kanban + .kanban {margin-top: 15px}
.kanban-card > .title {display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
.kanban-card > .infos {position: relative; margin-top: 5px}
.kanban-card > .infos > .info + .info {margin-left: 8px}
.kanban-card > .infos > .info-id,
.kanban-card > .infos > .info-deadline,
.kanban-card > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
.kanban-card > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
.kanban-card > .infos > .label-severity {transform: scale(.75)}
.kanban-card > .infos > .avatar {position: absolute; right: 0; top: 0}
.kanban-card > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
.kanban-card:hover > .actions {opacity: 1;}
.kanban-card > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-card > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-header-col > .actions {padding-top: 22px}
.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;}
.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
.kanban-lane-name:hover > .actions {opacity: 1;}
.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
.kanban-affixed .kanban-header-col > .title > .text,
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
#kanbanContainer {margin: 0;}
#kanbanContainer > .panel-body {overflow: auto;}
#kanbans .kanban {min-height: initial;}
#kanbans .kanban-lane {min-height: 150px; margin-top: 2px;}
#kanbans .kanban-affixed > .kanban-header {top: 100px;}
#kanbans .kanban-card[data-scale-size="4"] {padding: 8px;}
#kanbans .kanban-card[data-scale-size="3"] {padding: 3px 4px;}
#kanbans .kanban-card[data-scale-size="3"] > .title {white-space: normal; max-height: 100%;}
#kanbans .kanban-card[data-scale-size="2"] {padding: 3px 4px; overflow: hidden;}
#kanbans .kanban-card[data-scale-size="2"] > .title {display: inline; line-height: 18px; white-space: normal; max-height: 100%; word-break: break-all;}
#kanbans .kanban-card[data-scale-size="2"] > .infos {display: inline-block; height: 18px; vertical-align: top; margin: 0;}
#kanbans .kanban-card[data-scale-size="2"] > .infos > .info-pri {transform: scale(.8); vertical-align: top;}
#kanbans .kanban-card[data-scale-size="2"] > .infos > .avatar {display: inline-block; transform: scale(.8); position: relative; top: -2px; vertical-align: top; margin-right: -1px;}
#kanbanScaleControl {width: 100px;}
#kanbanScaleControl .input-group-addon {background: #fff; padding: 5px;}
#kanbanScaleControl > .input-group-btn:first-child > .btn {border-radius: 16px 0 0 16px; border-right-color: transparent;}
#kanbanScaleControl > .input-group-btn:last-child > .btn {border-radius: 0 16px 16px 0; border-left-color: transparent;}
#kanbanScaleControl > .input-group-btn > .btn:hover {border-color: #b8bfce;}
#type_chosen .icon-kanban {padding-right: 10px; font-size: 15px;}
.dropdown-menu a {text-align: left;}
.scrollbar-hover {max-height: 2000px; overflow: scroll !important;}
.c-type {width: 150px !important; overflow: unset;}
.c-group {width: 190px !important; overflow: unset;}
.c-type {margin-left: 0px !important;}
+1 -1
View File
@@ -1,6 +1,6 @@
function setCopyProject(executionID)
{
location.href = createLink('execution', 'create', 'projectID=&executionID=0&copyExecutionID=' + executionID);
location.href = createLink('execution', 'create', 'projectID=' + projectID + '&executionID=0&copyExecutionID=' + executionID);
}
$(function()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+107 -100
View File
@@ -10,105 +10,106 @@
* @link http://www.zentao.net
*/
/* Fields. */
$lang->execution->allExecutions = 'All ' . $lang->execution->common . 's';
$lang->execution->allExecutionAB = 'Execution List';
$lang->execution->id = $lang->executionCommon . ' ID';
$lang->execution->type = $lang->executionCommon . 'Type';
$lang->execution->name = $lang->executionCommon . 'Name';
$lang->execution->code = $lang->executionCommon . 'Code';
$lang->execution->projectName = 'Project';
$lang->execution->execName = 'Execution Name';
$lang->execution->execCode = 'Execution Code';
$lang->execution->execType = 'Execution Type';
$lang->execution->stage = 'Stage';
$lang->execution->pri = 'Priority';
$lang->execution->openedBy = 'OpenedBy';
$lang->execution->openedDate = 'OpenedDate';
$lang->execution->closedBy = 'ClosedBy';
$lang->execution->closedDate = 'ClosedDate';
$lang->execution->canceledBy = 'CanceledBy';
$lang->execution->canceledDate = 'CanceledDate';
$lang->execution->begin = 'Planned Begin';
$lang->execution->end = 'Planned End';
$lang->execution->dateRange = 'Duration';
$lang->execution->realBeganAB = 'Actual Begin';
$lang->execution->realEndAB = 'Actual End';
$lang->execution->realBegan = 'Actual Begin';
$lang->execution->realEnd = 'Actual End';
$lang->execution->to = 'To';
$lang->execution->days = ' Days';
$lang->execution->day = ' Days';
$lang->execution->workHour = ' Hours';
$lang->execution->workHourUnit = 'H';
$lang->execution->totalHours = ' Hours';
$lang->execution->totalDays = ' Days';
$lang->execution->status = $lang->executionCommon . 'Status';
$lang->execution->execStatus = 'Status';
$lang->execution->subStatus = 'Sub Status';
$lang->execution->desc = $lang->executionCommon . 'Description';
$lang->execution->execDesc = 'Description';
$lang->execution->owner = 'Owner';
$lang->execution->PO = "{$lang->executionCommon} Owner";
$lang->execution->PM = "{$lang->executionCommon} Manager";
$lang->execution->execPM = "Execution Manager";
$lang->execution->QD = 'Test Manager';
$lang->execution->RD = 'Release Manager';
$lang->execution->release = 'Release';
$lang->execution->acl = 'Access Control';
$lang->execution->teamname = 'Team Name';
$lang->execution->updateOrder = 'Rank';
$lang->execution->order = "Rank {$lang->executionCommon}";
$lang->execution->orderAB = "Rank";
$lang->execution->products = "Link {$lang->productCommon}";
$lang->execution->whitelist = 'Whitelist';
$lang->execution->addWhitelist = 'Add Whitelist';
$lang->execution->unbindWhitelist = 'Remove Whitelist';
$lang->execution->totalEstimate = 'Estimates';
$lang->execution->totalConsumed = 'Cost';
$lang->execution->totalLeft = 'Left';
$lang->execution->progress = ' Progress';
$lang->execution->hours = 'Estimates: %s, Cost: %s, Left: %s.';
$lang->execution->viewBug = 'Bugs';
$lang->execution->noProduct = "No {$lang->productCommon} yet.";
$lang->execution->createStory = "Create Story";
$lang->execution->storyTitle = "Story Name";
$lang->execution->all = "All {$lang->executionCommon}s";
$lang->execution->undone = 'Unfinished ';
$lang->execution->unclosed = 'Unclosed';
$lang->execution->typeDesc = "OPS {$lang->executionCommon} has no {$lang->SRCommon}, Bug, Build, or Test features.";
$lang->execution->mine = 'Mine: ';
$lang->execution->involved = 'Mine';
$lang->execution->other = 'Others';
$lang->execution->deleted = 'Deleted';
$lang->execution->delayed = 'Delayed';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "Adjust {$lang->executionCommon} Begin and End";
$lang->execution->readjustTask = 'Adjust Task Begin and End';
$lang->execution->effort = 'Effort';
$lang->execution->storyEstimate = 'Story Estimate';
$lang->execution->newEstimate = 'New Estimate';
$lang->execution->reestimate = 'Reestimate';
$lang->execution->selectRound = 'Select Round';
$lang->execution->average = 'Average';
$lang->execution->relatedMember = 'Team';
$lang->execution->watermark = 'Exported by ZenTao';
$lang->execution->burnXUnit = '(Date)';
$lang->execution->burnYUnit = '(Hours)';
$lang->execution->waitTasks = 'Waiting Tasks';
$lang->execution->viewByUser = 'By User';
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
$lang->execution->noLinkProduct = "Stage not linked {$lang->productCommon}";
$lang->execution->recent = 'Recent visits: ';
$lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . 'available to copy.';
$lang->execution->noTeam = 'No team members at the moment';
$lang->execution->or = ' or ';
$lang->execution->selectProject = 'Please select project';
$lang->execution->unfoldClosed = 'Unfold Closed';
$lang->execution->editName = 'Edit Name';
$lang->execution->setWIP = 'WIP Settings';
$lang->execution->sortColumn = 'Kanban Card Sorting';
$lang->execution->batchCreateStroy = "Batch create {$lang->SRCommon}";
$lang->execution->batchCreateTask = 'Batch create task';
$lang->execution->allExecutions = 'All ' . $lang->execution->common . 's';
$lang->execution->allExecutionAB = 'Execution List';
$lang->execution->id = $lang->executionCommon . ' ID';
$lang->execution->type = $lang->executionCommon . 'Type';
$lang->execution->name = $lang->executionCommon . 'Name';
$lang->execution->code = $lang->executionCommon . 'Code';
$lang->execution->projectName = 'Project';
$lang->execution->execName = 'Execution Name';
$lang->execution->execCode = 'Execution Code';
$lang->execution->execType = 'Execution Type';
$lang->execution->stage = 'Stage';
$lang->execution->pri = 'Priority';
$lang->execution->openedBy = 'OpenedBy';
$lang->execution->openedDate = 'OpenedDate';
$lang->execution->closedBy = 'ClosedBy';
$lang->execution->closedDate = 'ClosedDate';
$lang->execution->canceledBy = 'CanceledBy';
$lang->execution->canceledDate = 'CanceledDate';
$lang->execution->begin = 'Planned Begin';
$lang->execution->end = 'Planned End';
$lang->execution->dateRange = 'Duration';
$lang->execution->realBeganAB = 'Actual Begin';
$lang->execution->realEndAB = 'Actual End';
$lang->execution->realBegan = 'Actual Begin';
$lang->execution->realEnd = 'Actual End';
$lang->execution->to = 'To';
$lang->execution->days = ' Days';
$lang->execution->day = ' Days';
$lang->execution->workHour = ' Hours';
$lang->execution->workHourUnit = 'H';
$lang->execution->totalHours = ' Hours';
$lang->execution->totalDays = ' Days';
$lang->execution->status = $lang->executionCommon . 'Status';
$lang->execution->execStatus = 'Status';
$lang->execution->subStatus = 'Sub Status';
$lang->execution->desc = $lang->executionCommon . 'Description';
$lang->execution->execDesc = 'Description';
$lang->execution->owner = 'Owner';
$lang->execution->PO = "{$lang->executionCommon} Owner";
$lang->execution->PM = "{$lang->executionCommon} Manager";
$lang->execution->execPM = "Execution Manager";
$lang->execution->QD = 'Test Manager';
$lang->execution->RD = 'Release Manager';
$lang->execution->release = 'Release';
$lang->execution->acl = 'Access Control';
$lang->execution->teamname = 'Team Name';
$lang->execution->updateOrder = 'Rank';
$lang->execution->order = "Rank {$lang->executionCommon}";
$lang->execution->orderAB = "Rank";
$lang->execution->products = "Link {$lang->productCommon}";
$lang->execution->whitelist = 'Whitelist';
$lang->execution->addWhitelist = 'Add Whitelist';
$lang->execution->unbindWhitelist = 'Remove Whitelist';
$lang->execution->totalEstimate = 'Estimates';
$lang->execution->totalConsumed = 'Cost';
$lang->execution->totalLeft = 'Left';
$lang->execution->progress = ' Progress';
$lang->execution->hours = 'Estimates: %s, Cost: %s, Left: %s.';
$lang->execution->viewBug = 'Bugs';
$lang->execution->noProduct = "No {$lang->productCommon} yet.";
$lang->execution->createStory = "Create Story";
$lang->execution->storyTitle = "Story Name";
$lang->execution->all = "All {$lang->executionCommon}s";
$lang->execution->undone = 'Unfinished ';
$lang->execution->unclosed = 'Unclosed';
$lang->execution->typeDesc = "OPS {$lang->executionCommon} has no {$lang->SRCommon}, Bug, Build, or Test features.";
$lang->execution->mine = 'Mine: ';
$lang->execution->involved = 'Mine';
$lang->execution->other = 'Others';
$lang->execution->deleted = 'Deleted';
$lang->execution->delayed = 'Delayed';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "Adjust {$lang->executionCommon} Begin and End";
$lang->execution->readjustTask = 'Adjust Task Begin and End';
$lang->execution->effort = 'Effort';
$lang->execution->storyEstimate = 'Story Estimate';
$lang->execution->newEstimate = 'New Estimate';
$lang->execution->reestimate = 'Reestimate';
$lang->execution->selectRound = 'Select Round';
$lang->execution->average = 'Average';
$lang->execution->relatedMember = 'Team';
$lang->execution->watermark = 'Exported by ZenTao';
$lang->execution->burnXUnit = '(Date)';
$lang->execution->burnYUnit = '(Hours)';
$lang->execution->waitTasks = 'Waiting Tasks';
$lang->execution->viewByUser = 'By User';
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
$lang->execution->noLinkProduct = "Stage not linked {$lang->productCommon}";
$lang->execution->recent = 'Recent visits: ';
$lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . 'available to copy.';
$lang->execution->noTeam = 'No team members at the moment';
$lang->execution->or = ' or ';
$lang->execution->selectProject = 'Please select project';
$lang->execution->unfoldClosed = 'Unfold Closed';
$lang->execution->editName = 'Edit Name';
$lang->execution->setWIP = 'WIP Settings';
$lang->execution->sortColumn = 'Kanban Card Sorting';
$lang->execution->batchCreateStroy = "Batch create {$lang->SRCommon}";
$lang->execution->batchCreateTask = 'Batch create task';
$lang->execution->kanbanNoLinkProduct = "Kanban not linked {$lang->productCommon}";
/* Fields of zt_team. */
$lang->execution->root = 'Root';
@@ -163,7 +164,7 @@ global $config;
if($config->systemMode == 'new')
{
$lang->execution->aclList['private'] = 'Private (for team members and execution stakeholders)';
$lang->execution->aclList['open'] = 'Inherited Execution ACL (for who can access the current execution)';
$lang->execution->aclList['open'] = 'Inherited Project ACL (for who can access the current project)';
}
else
{
@@ -171,6 +172,9 @@ else
$lang->execution->aclList['open'] = "Public (Users who can visit {$lang->executionCommon} can access it.)";
}
$lang->execution->kanbanAclList['private'] = 'Private';
$lang->execution->kanbanAclList['open'] = 'Inherited Project';
$lang->execution->storyPoint = 'Story Point';
$lang->execution->burnByList['left'] = 'View by remaining hours';
@@ -247,6 +251,8 @@ $lang->execution->iteration = 'Iterations';
$lang->execution->iterationInfo = '%s Iterations';
$lang->execution->viewAll = 'View All';
$lang->execution->testreport = 'Test Report';
$lang->execution->taskKanban = 'Task Kanban';
$lang->execution->RDKanban = 'Research & Development Kanban';
/* Group browsing. */
$lang->execution->allTasks = 'All';
@@ -422,6 +428,7 @@ $lang->printKanban->typeList['increment'] = 'Increment';
$lang->execution->typeList[''] = '';
$lang->execution->typeList['stage'] = 'Stage';
$lang->execution->typeList['sprint'] = $lang->executionCommon;
$lang->execution->typeList['kanban'] = 'Kanban';
$lang->execution->featureBar['task']['all'] = $lang->execution->allTasks;
$lang->execution->featureBar['task']['unclosed'] = $lang->execution->unclosed;
+106 -99
View File
@@ -10,105 +10,106 @@
* @link http://www.zentao.net
*/
/* 字段列表。*/
$lang->execution->allExecutions = '所有' . $lang->execution->common;
$lang->execution->allExecutionAB = "{$lang->execution->common}列表";
$lang->execution->id = $lang->executionCommon . '编号';
$lang->execution->type = $lang->executionCommon . '类型';
$lang->execution->name = $lang->executionCommon . '名称';
$lang->execution->code = $lang->executionCommon . '代号';
$lang->execution->projectName = '所属项目';
$lang->execution->execName = "{$lang->execution->common}名称";
$lang->execution->execCode = "{$lang->execution->common}代号";
$lang->execution->execType = "{$lang->execution->common}类型";
$lang->execution->stage = '阶段';
$lang->execution->pri = '优先级';
$lang->execution->openedBy = '由谁创建';
$lang->execution->openedDate = '创建日期';
$lang->execution->closedBy = '由谁关闭';
$lang->execution->closedDate = '关闭日期';
$lang->execution->canceledBy = '由谁取消';
$lang->execution->canceledDate = '取消日期';
$lang->execution->begin = '计划开始';
$lang->execution->end = '计划完成';
$lang->execution->dateRange = '起始日期';
$lang->execution->realBeganAB = '实际开始';
$lang->execution->realEndAB = '实际完成';
$lang->execution->realBegan = '实际开始日期';
$lang->execution->realEnd = '实际完成日期';
$lang->execution->to = '至';
$lang->execution->days = '可用工作日';
$lang->execution->day = '天';
$lang->execution->workHour = '工时';
$lang->execution->workHourUnit = 'h';
$lang->execution->totalHours = '可用工时';
$lang->execution->totalDays = '可用工日';
$lang->execution->status = $lang->executionCommon . '状态';
$lang->execution->execStatus = "{$lang->execution->common}状态";
$lang->execution->subStatus = '子状态';
$lang->execution->desc = $lang->executionCommon . '描述';
$lang->execution->execDesc = "{$lang->execution->common}描述";
$lang->execution->owner = '负责人';
$lang->execution->PO = $lang->productCommon . '负责人';
$lang->execution->PM = $lang->executionCommon . '负责人';
$lang->execution->execPM = "{$lang->execution->common}负责人";
$lang->execution->QD = '测试负责人';
$lang->execution->RD = '发布负责人';
$lang->execution->release = '发布';
$lang->execution->acl = '访问控制';
$lang->execution->teamname = '团队名称';
$lang->execution->updateOrder = '排序';
$lang->execution->order = $lang->executionCommon . '排序';
$lang->execution->orderAB = '排序';
$lang->execution->products = '相关' . $lang->productCommon;
$lang->execution->whitelist = '白名单';
$lang->execution->addWhitelist = '添加白名单';
$lang->execution->unbindWhitelist = '删除白名单';
$lang->execution->totalEstimate = '预计';
$lang->execution->totalConsumed = '消耗';
$lang->execution->totalLeft = '剩余';
$lang->execution->progress = '进度';
$lang->execution->hours = '预计 %s 消耗 %s 剩余 %s';
$lang->execution->viewBug = '查看bug';
$lang->execution->noProduct = "无{$lang->executionCommon}";
$lang->execution->createStory = "提{$lang->SRCommon}";
$lang->execution->storyTitle = "{$lang->SRCommon}名称";
$lang->execution->all = '所有';
$lang->execution->undone = '未完成';
$lang->execution->unclosed = '未关闭';
$lang->execution->typeDesc = "运维{$lang->executionCommon}没有{$lang->SRCommon}、bug、版本、测试功能。";
$lang->execution->mine = '我负责:';
$lang->execution->involved = '我参与';
$lang->execution->other = '其他';
$lang->execution->deleted = '已删除';
$lang->execution->delayed = '已延期';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "调整{$lang->executionCommon}起止时间";
$lang->execution->readjustTask = '顺延任务的起止时间';
$lang->execution->effort = '日志';
$lang->execution->storyEstimate = '需求估算';
$lang->execution->newEstimate = '新一轮估算';
$lang->execution->reestimate = '重新估算';
$lang->execution->selectRound = '选择轮次';
$lang->execution->average = '平均值';
$lang->execution->relatedMember = '相关成员';
$lang->execution->watermark = '由禅道导出';
$lang->execution->burnXUnit = '(日期)';
$lang->execution->burnYUnit = '(工时)';
$lang->execution->waitTasks = '待处理';
$lang->execution->viewByUser = '按用户查看';
$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}";
$lang->execution->noLinkProduct = "阶段没有关联{$lang->productCommon}";
$lang->execution->recent = '近期访问:';
$lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon . '来复制';
$lang->execution->noTeam = '暂时没有团队成员';
$lang->execution->or = '或';
$lang->execution->selectProject = '请选择项目';
$lang->execution->unfoldClosed = '展开已结束';
$lang->execution->editName = '编辑名称';
$lang->execution->setWIP = '在制品数量设置(WIP)';
$lang->execution->sortColumn = '看板列卡片排序';
$lang->execution->batchCreateStroy = "批量新建{$lang->SRCommon}";
$lang->execution->batchCreateTask = '批量建任务';
$lang->execution->allExecutions = '所有' . $lang->execution->common;
$lang->execution->allExecutionAB = "{$lang->execution->common}列表";
$lang->execution->id = $lang->executionCommon . '编号';
$lang->execution->type = $lang->executionCommon . '类型';
$lang->execution->name = $lang->executionCommon . '名称';
$lang->execution->code = $lang->executionCommon . '代号';
$lang->execution->projectName = '所属项目';
$lang->execution->execName = "{$lang->execution->common}名称";
$lang->execution->execCode = "{$lang->execution->common}代号";
$lang->execution->execType = "{$lang->execution->common}类型";
$lang->execution->stage = '阶段';
$lang->execution->pri = '优先级';
$lang->execution->openedBy = '由谁创建';
$lang->execution->openedDate = '创建日期';
$lang->execution->closedBy = '由谁关闭';
$lang->execution->closedDate = '关闭日期';
$lang->execution->canceledBy = '由谁取消';
$lang->execution->canceledDate = '取消日期';
$lang->execution->begin = '计划开始';
$lang->execution->end = '计划完成';
$lang->execution->dateRange = '起始日期';
$lang->execution->realBeganAB = '实际开始';
$lang->execution->realEndAB = '实际完成';
$lang->execution->realBegan = '实际开始日期';
$lang->execution->realEnd = '实际完成日期';
$lang->execution->to = '至';
$lang->execution->days = '可用工作日';
$lang->execution->day = '天';
$lang->execution->workHour = '工时';
$lang->execution->workHourUnit = 'h';
$lang->execution->totalHours = '可用工时';
$lang->execution->totalDays = '可用工日';
$lang->execution->status = $lang->executionCommon . '状态';
$lang->execution->execStatus = "{$lang->execution->common}状态";
$lang->execution->subStatus = '子状态';
$lang->execution->desc = $lang->executionCommon . '描述';
$lang->execution->execDesc = "{$lang->execution->common}描述";
$lang->execution->owner = '负责人';
$lang->execution->PO = $lang->productCommon . '负责人';
$lang->execution->PM = $lang->executionCommon . '负责人';
$lang->execution->execPM = "{$lang->execution->common}负责人";
$lang->execution->QD = '测试负责人';
$lang->execution->RD = '发布负责人';
$lang->execution->release = '发布';
$lang->execution->acl = '访问控制';
$lang->execution->teamname = '团队名称';
$lang->execution->updateOrder = '排序';
$lang->execution->order = $lang->executionCommon . '排序';
$lang->execution->orderAB = '排序';
$lang->execution->products = '相关' . $lang->productCommon;
$lang->execution->whitelist = '白名单';
$lang->execution->addWhitelist = '添加白名单';
$lang->execution->unbindWhitelist = '删除白名单';
$lang->execution->totalEstimate = '预计';
$lang->execution->totalConsumed = '消耗';
$lang->execution->totalLeft = '剩余';
$lang->execution->progress = '进度';
$lang->execution->hours = '预计 %s 消耗 %s 剩余 %s';
$lang->execution->viewBug = '查看bug';
$lang->execution->noProduct = "无{$lang->executionCommon}";
$lang->execution->createStory = "提{$lang->SRCommon}";
$lang->execution->storyTitle = "{$lang->SRCommon}名称";
$lang->execution->all = '所有';
$lang->execution->undone = '未完成';
$lang->execution->unclosed = '未关闭';
$lang->execution->typeDesc = "运维{$lang->executionCommon}没有{$lang->SRCommon}、bug、版本、测试功能。";
$lang->execution->mine = '我负责:';
$lang->execution->involved = '我参与';
$lang->execution->other = '其他';
$lang->execution->deleted = '已删除';
$lang->execution->delayed = '已延期';
$lang->execution->product = $lang->execution->products;
$lang->execution->readjustTime = "调整{$lang->executionCommon}起止时间";
$lang->execution->readjustTask = '顺延任务的起止时间';
$lang->execution->effort = '日志';
$lang->execution->storyEstimate = '需求估算';
$lang->execution->newEstimate = '新一轮估算';
$lang->execution->reestimate = '重新估算';
$lang->execution->selectRound = '选择轮次';
$lang->execution->average = '平均值';
$lang->execution->relatedMember = '相关成员';
$lang->execution->watermark = '由禅道导出';
$lang->execution->burnXUnit = '(日期)';
$lang->execution->burnYUnit = '(工时)';
$lang->execution->waitTasks = '待处理';
$lang->execution->viewByUser = '按用户查看';
$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}";
$lang->execution->noLinkProduct = "阶段没有关联{$lang->productCommon}";
$lang->execution->recent = '近期访问:';
$lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon . '来复制';
$lang->execution->noTeam = '暂时没有团队成员';
$lang->execution->or = '或';
$lang->execution->selectProject = '请选择项目';
$lang->execution->unfoldClosed = '展开已结束';
$lang->execution->editName = '编辑名称';
$lang->execution->setWIP = '在制品数量设置(WIP)';
$lang->execution->sortColumn = '看板列卡片排序';
$lang->execution->batchCreateStroy = "批量新建{$lang->SRCommon}";
$lang->execution->batchCreateTask = '批量建任务';
$lang->execution->kanbanNoLinkProduct = "看板没有关联{$lang->productCommon}";
/* Fields of zt_team. */
$lang->execution->root = '源ID';
@@ -171,6 +172,9 @@ else
$lang->execution->aclList['open'] = "公开(有{$lang->executionCommon}视图权限即可访问)";
}
$lang->execution->kanbanAclList['private'] = '私有';
$lang->execution->kanbanAclList['open'] = '继承项目';
$lang->execution->storyPoint = '故事点';
$lang->execution->burnByList['left'] = '按剩余工时查看';
@@ -247,6 +251,8 @@ $lang->execution->iteration = '版本迭代';
$lang->execution->iterationInfo = '迭代%s次';
$lang->execution->viewAll = '查看所有';
$lang->execution->testreport = '测试报告';
$lang->execution->taskKanban = '任务看板';
$lang->execution->RDKanban = '研发看板';
/* 分组浏览。*/
$lang->execution->allTasks = '所有';
@@ -422,6 +428,7 @@ $lang->printKanban->typeList['increment'] = '增量';
$lang->execution->typeList[''] = '';
$lang->execution->typeList['stage'] = '阶段';
$lang->execution->typeList['sprint'] = $lang->executionCommon;
$lang->execution->typeList['kanban'] = '看板';
$lang->execution->featureBar['task']['all'] = $lang->execution->allTasks;
$lang->execution->featureBar['task']['unclosed'] = $lang->execution->unclosed;
+61 -29
View File
@@ -65,13 +65,14 @@ class executionModel extends model
{
if(!$this->app->user->admin and strpos(",{$this->app->user->view->sprints},", ",$executionID,") === false and !defined('TUTORIAL') and $executionID != 0) die(js::error($this->lang->execution->accessDenied) . js::locate('back'));
$executions = $this->loadModel('execution')->getPairs(0, 'all', 'nocode');
$executions = $this->getPairs(0, 'all', 'nocode');
if(!$executionID and $this->session->execution) $executionID = $this->session->execution;
if(!$executionID or !in_array($executionID, array_keys($executions))) $executionID = key($executions);
$this->session->set('execution', $executionID);
/* Unset story, bug, build and testtask if type is ops. */
$execution = $this->getByID($executionID);
if($execution and $execution->type == 'kanban') $this->lang->execution->menu = new stdclass();
if($execution and $execution->type == 'stage' and $this->config->systemMode == 'new')
{
@@ -88,15 +89,6 @@ class executionModel extends model
unset($this->lang->execution->menu->build);
}
/* Hide story and qa menu when execution is story or design type. */
/*
if($execution and ($execution->attribute == 'story' or $execution->attribute == 'design'))
{
unset($this->lang->execution->menu->story);
unset($this->lang->execution->menu->qa);
}
*/
if($executions and (!isset($executions[$executionID]) or !$this->checkPriv($executionID))) $this->accessDenied();
$moduleName = $this->app->getModuleName();
@@ -312,6 +304,12 @@ class executionModel extends model
return false;
}
if($type == 'kanban' and empty($this->post->products[0]))
{
dao::$errors['message'][] = $this->lang->execution->kanbanNoLinkProduct;
return false;
}
$this->config->execution->create->requiredFields .= ',project';
}
@@ -381,7 +379,7 @@ class executionModel extends model
$creatorExists = false;
$teamMembers = array();
$this->loadModel('kanban')->createExecutionLane($executionID);
if((isset($project) and $project->model != 'kanban') or empty($project)) $this->loadModel('kanban')->createExecutionLane($executionID);
/* Save order. */
$this->dao->update(TABLE_EXECUTION)->set('`order`')->eq($executionID * 5)->where('id')->eq($executionID)->exec();
@@ -474,10 +472,6 @@ class executionModel extends model
return false;
}
/* Get team and language item. */
$team = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution');
$this->lang->execution->team = $this->lang->execution->teamname;
/* Get the data from the post. */
$execution = fixer::input('post')
->setDefault('lastEditedBy', $this->app->user->account)
@@ -529,6 +523,12 @@ class executionModel extends model
->limit(1)
->exec();
if($oldExecution->type == 'kanban') $this->dao->delete()->from(TABLE_TEAM)->where('root')->eq((int)$executionID)->andWhere('type')->eq('execution')->andWhere('role')->ne('')->exec();
/* Get team and language item. */
$this->lang->execution->team = $this->lang->execution->teamname;
$team = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution');
$changedAccounts = array();
$teamMembers = array();
foreach($this->config->execution->ownerFields as $ownerField)
@@ -1054,7 +1054,7 @@ class executionModel extends model
/* Order by status's content whether or not done */
$executions = $this->dao->select('*, IF(INSTR("done,closed", status) < 2, 0, 1) AS isDone, INSTR("doing,wait,suspended,closed", status) AS sortStatus')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->beginIF($type == 'all')->andWhere('type')->in('stage,sprint')->fi()
->beginIF($type == 'all')->andWhere('type')->in('stage,sprint,kanban')->fi()
->beginIF($projectID and $this->config->systemMode == 'new')->andWhere('project')->eq($projectID)->fi()
->beginIF($type != 'all' and $this->config->systemMode == 'new')->andWhere('type')->eq($type)->fi()
->beginIF(strpos($mode, 'withdelete') === false)->andWhere('deleted')->eq(0)->fi()
@@ -1202,7 +1202,7 @@ class executionModel extends model
public function getIdList($projectID, $status = 'all')
{
return $this->dao->select('id')->from(TABLE_EXECUTION)
->where('type')->in('sprint,stage')
->where('type')->in('sprint,stage,kanban')
->andWhere('deleted')->eq('0')
->beginIF($projectID)->andWhere('project')->eq($projectID)->fi()
->beginIF($status == 'undone')->andWhere('status')->notIN('done,closed')->fi()
@@ -1228,7 +1228,7 @@ class executionModel extends model
$orderBy = (isset($project->model) and $project->model == 'waterfall') ? 'begin_asc,id_asc' : 'begin_desc,id_desc';
$executions = $this->dao->select('*')->from(TABLE_EXECUTION)
->where('type')->in('stage,sprint')
->where('type')->in('stage,sprint,kanban')
->andWhere('deleted')->eq('0')
->beginIF($projectID)->andWhere('project')->eq((int)$projectID)->fi()
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
@@ -1319,7 +1319,7 @@ class executionModel extends model
$module = 'execution';
$method = 'task';
}
if($module == 'testcase' and ($method == 'view' || $method == 'edit' || $method == 'batchedit'))
if($module == 'testcase' and ($method == 'view' or $method == 'edit' or $method == 'batchedit'))
{
$module = 'execution';
$method = 'testcase';
@@ -1329,7 +1329,7 @@ class executionModel extends model
$module = 'execution';
$method = 'testtask';
}
if($module == 'build' and ($method == 'edit' || $method= 'view'))
if($module == 'build' and ($method == 'edit' or $method == 'view'))
{
$module = 'execution';
$method = 'build';
@@ -2268,14 +2268,15 @@ class executionModel extends model
/**
* Link story.
*
* @param int $executionID
* @param array $stories
* @param array $products
* @param int $executionID
* @param array $stories
* @param array $products
* @param string $extra
*
* @access public
* @return bool
*/
public function linkStory($executionID, $stories = array(), $products = array())
public function linkStory($executionID, $stories = array(), $products = array(), $extra = '')
{
if(empty($executionID)) return false;
if(empty($stories)) $stories = $this->post->stories;
@@ -2283,16 +2284,23 @@ class executionModel extends model
if(empty($products)) $products = $this->post->products;
$this->loadModel('action');
$this->loadModel('kanban');
$versions = $this->loadModel('story')->getVersions($stories);
$linkedStories = $this->dao->select('*')->from(TABLE_PROJECTSTORY)->where('project')->eq($executionID)->orderBy('order_desc')->fetchPairs('story', 'order');
$lastOrder = reset($linkedStories);
$storyList = $this->dao->select('id, status, branch')->from(TABLE_STORY)->where('id')->in(array_values($stories))->fetchAll('id');
$execution = $this->getById($executionID);
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
foreach($stories as $key => $storyID)
{
$notAllowedStatus = $this->app->rawMethod == 'batchcreate' ? 'closed' : 'draft,closed';
if(strpos($notAllowedStatus, $storyList[$storyID]->status) !== false) continue;
if(isset($linkedStories[$storyID])) continue;
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($executionID, $output['laneID'], $output['columnID'], 'story', $storyID);
$data = new stdclass();
$data->project = $executionID;
$data->product = (int)$products[$storyID];
@@ -2308,6 +2316,8 @@ class executionModel extends model
$action = $executionID == $this->session->project ? 'linked2project' : 'linked2execution';
$this->action->create('story', $storyID, $action, '', $executionID);
}
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($executionID, 'story');
}
/**
@@ -2479,6 +2489,23 @@ class executionModel extends model
->fetchAll('account');
}
/**
* Get members by execution id list.
*
* @param array $executionIdList
* @access public
* @return void
*/
public function getMembersByIdList($executionIdList)
{
return $this->dao->select("t1.root, t1.account, t2.realname")->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account')
->where('t1.root')->in($executionIdList)
->andWhere('t1.type')->eq('execution')
->andWhere('t2.deleted')->eq('0')
->fetchGroup('root');
}
/**
* Get the skip members of the team.
*
@@ -3022,13 +3049,16 @@ class executionModel extends model
*/
public static function isClickable($execution, $action)
{
$action = strtolower($action);
$action = strtolower($action);
$clickable = commonModel::hasPriv('execution', $action);
if(!$clickable) return false;
if($action == 'start') return $execution->status == 'wait';
if($action == 'close') return $execution->status != 'closed';
if($action == 'suspend') return $execution->status == 'wait' or $execution->status == 'doing';
if($action == 'putoff') return $execution->status == 'wait' or $execution->status == 'doing';
if($action == 'activate') return $execution->status == 'suspended' or $execution->status == 'closed';
if($action == 'delete') return $execution->status == 'wait' or $execution->status == 'doing';
return true;
}
@@ -3212,13 +3242,14 @@ class executionModel extends model
/**
* Get Kanban tasks
*
* @param int $executionID
* @param string $orderBy
* @param object $pager
* @param int $executionID
* @param string $orderBy
* @param object $pager
* @param array|string $excludeTasks
* @access public
* @return void
*/
public function getKanbanTasks($executionID, $orderBy = 'status_asc, id_desc', $pager = null)
public function getKanbanTasks($executionID, $orderBy = 'status_asc, id_desc', $pager = null, $excludeTasks = '')
{
$tasks = $this->dao->select('t1.*, t2.id AS storyID, t2.title AS storyTitle, t2.version AS latestStoryVersion, t2.status AS storyStatus, t3.realname AS assignedToRealName')
->from(TABLE_TASK)->alias('t1')
@@ -3227,6 +3258,7 @@ class executionModel extends model
->where('t1.execution')->eq((int)$executionID)
->andWhere('t1.deleted')->eq(0)
->andWhere('t1.parent')->ge(0)
->beginIF($excludeTasks)->andWhere('t1.id')->notIN($excludeTasks)->fi()
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
@@ -72,6 +72,11 @@ foreach($executions as $projectID => $projectExecutions)
foreach($projectExecutions as $index => $execution)
{
$kanbanLink = $this->createLink('execution', 'kanban', "executionID=%s");
$taskLink = $this->createLink('execution', 'task', "executionID=%s");
if($execution->type != 'kanban' and $link == $kanbanLink) $link = $taskLink;
if($execution->type == 'kanban' and $link != $kanbanLink) $link = $kanbanLink;
$selected = $execution->id == $executionID ? 'selected' : '';
if($execution->status != 'done' and $execution->status != 'closed' and ($execution->PM == $this->app->user->account or isset($execution->teams[$this->app->user->account])))
{
+3 -4
View File
@@ -118,11 +118,10 @@
<?php printf('%03d', $execution->id);?>
</td>
<td class='text-left c-name <?php if(!empty($execution->children)) echo 'has-child';?> flex' title='<?php echo $execution->name?>'>
<?php if(isset($this->config->maxVersion)):?>
<span class='project-type-label label label-outline <?php echo $execution->type == 'sprint' ? 'label-info' : 'label-warning';?>'><?php echo $lang->execution->typeList[$execution->type]?></span>
<?php endif;?>
<span class='project-type-label label label-outline <?php echo $execution->type == 'stage' ? 'label-warning' : 'label-info';?>'><?php echo $lang->execution->typeList[$execution->type]?></span>
<?php
echo !empty($execution->children) ? $execution->name : html::a($this->createLink('execution', 'task', 'execution=' . $execution->id), $execution->name, '', "class='text-ellipsis'");
$executionLink = $execution->projectModel == 'kanban' ? html::a($this->createLink('execution', 'kanban', 'executionID=' . $execution->id), $execution->name, '', "class='text-ellipsis'") : html::a($this->createLink('execution', 'task', 'execution=' . $execution->id), $execution->name, '', "class='text-ellipsis'");
echo !empty($execution->children) ? $execution->name : $executionLink;
if(isset($execution->delay)) echo "<span class='label label-danger label-badge'>{$lang->execution->delayed}</span> ";
?>
<?php if(!empty($execution->children)):?>
+3
View File
@@ -42,6 +42,7 @@
<?php js::set('projectCommon', $lang->project->common);?>
<?php js::set('multiBranchProducts', $multiBranchProducts);?>
<?php js::set('systemMode', $config->systemMode);?>
<?php js::set('projectID', $projectID);?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
@@ -88,6 +89,7 @@
</div>
</td><td></td><td></td>
</tr>
<?php if(empty($project) or $project->model != 'kanban'):?>
<tr>
<th><?php echo (($from == 'execution') and ($config->systemMode == 'new')) ? $lang->execution->execType : $lang->execution->type;?></th>
<td>
@@ -104,6 +106,7 @@
</td>
<td class='muted' colspan='2'><div id='lifeTimeTips'><?php echo $lang->execution->typeDesc;?></div></td>
</tr>
<?php endif;?>
<?php if($isStage):?>
<tr>
<th><?php echo $lang->stage->percent;?></th>
+2
View File
@@ -66,6 +66,7 @@
</div>
</td>
</tr>
<?php if($execution->type != 'kanban'):?>
<tr>
<th><?php echo $lang->execution->type;?></th>
<td>
@@ -81,6 +82,7 @@
?>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->execution->teamname;?></th>
<td><?php echo html::input('team', $execution->team, "class='form-control'");?></td>
+138 -100
View File
@@ -1,15 +1,94 @@
<?php
/**
* The kanban view file of execution module of ZenTaoPMS.
* The kanban file of execution module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Mengyi Liu<liumengyi@easycorp.ltd>
* @package execution
* @version $Id: kanban.html.php $
* @version $Id: kanban.html.php 935 2022-01-11 16:49:24Z $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kanban.html.php';?>
<?php
$laneCount = 0;
if($groupBy == 'default')
{
foreach($regions as $region) $laneCount += $region->laneCount;
}
js::set('regions', $regions);
js::set('browseType', $browseType);
js::set('kanbanData', $kanbanData);
js::set('orderBy', $orderBy);
js::set('groupBy', $groupBy);
js::set('execution', $execution);
js::set('productID', $productID);
js::set('kanbanLang', $lang->kanban);
js::set('kanbanlaneLang', $lang->kanbanlane);
js::set('storyLang', $lang->story);
js::set('executionLang', $lang->execution);
js::set('bugLang', $lang->bug);
js::set('taskLang', $lang->task);
js::set('deadlineLang', $lang->task->deadlineAB);
js::set('kanbancolumnLang', $lang->kanbancolumn);
js::set('kanbancardLang', $lang->kanbancard);
js::set('executionID', $execution->id);
js::set('laneCount', $laneCount);
js::set('userList', $userList);
js::set('noAssigned', $lang->kanbancard->noAssigned);
js::set('users', $users);
js::set('displayCards', $execution->displayCards);
js::set('colorListLang', $lang->kanbancard->colorList);
js::set('colorList', $this->config->kanban->cardColorList);
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
$canEditRegion = commonModel::hasPriv('kanban', 'editRegion');
$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion');
$canCreateLane = commonModel::hasPriv('kanban', 'createLane');
$canCreateTask = common::hasPriv('task', 'create');
$canBatchCreateTask = common::hasPriv('task', 'batchCreate');
$canCreateBug = common::hasPriv('bug', 'create');
$canBatchCreateBug = common::hasPriv('bug', 'batchCreate');
$canCreateStory = ($productID and common::hasPriv('story', 'create'));
$canBatchCreateStory = ($productID and common::hasPriv('story', 'batchCreate'));
$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory'));
$canLinkStoryByPlane = ($productID and common::hasPriv('execution', 'importplanstories'));
$hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane);
$hasTaskButton = ($canCreateTask or $canBatchCreateTask);
$hasBugButton = ($canCreateBug or $canBatchCreateBug);
js::set('priv',
array(
'canCreateTask' => $canCreateTask,
'canBatchCreateTask' => $canBatchCreateTask,
'canCreateBug' => $canCreateBug,
'canBatchCreateBug' => $canBatchCreateBug,
'canCreateStory' => $canCreateStory,
'canBatchCreateStory' => $canBatchCreateStory,
'canLinkStory' => $canLinkStory,
'canLinkStoryByPlane' => $canLinkStoryByPlane,
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
'canPauseTask' => common::hasPriv('task', 'pause'),
'canCancelTask' => common::hasPriv('task', 'cancel'),
'canCloseTask' => common::hasPriv('task', 'close'),
'canActivateTask' => common::hasPriv('task', 'activate'),
'canStartTask' => common::hasPriv('task', 'start'),
'canAssignBug' => common::hasPriv('bug', 'assignto'),
'canConfirmBug' => common::hasPriv('bug', 'confirmBug'),
'canActivateBug' => common::hasPriv('bug', 'activate')
)
);
js::set('hasStoryButton', $hasStoryButton);
js::set('hasBugButton', $hasBugButton);
js::set('hasTaskButton', $hasTaskButton);
?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class="input-control space c-type">
@@ -34,50 +113,33 @@
</div>
<div class='btn-toolbar pull-right'>
<?php
$link = $this->createLink('task', 'export', "execution=$executionID&orderBy=$orderBy&type=kanban");
if(common::hasPriv('task', 'export')) echo html::a($link, "<i class='icon-export muted'></i> " . $lang->export, '', "class='btn btn-link iframe export' data-width='700'");
?>
<?php if($canBeChanged):?>
<div class='btn-group' style="margin-right: 0">
<button type='button' class='btn btn-link dropdown-toggle' data-toggle='dropdown' id='importAction'>
<i class='icon-import muted'></i> <?php echo $lang->import ?>
<span class='caret'></span>
</button>
<ul class='dropdown-menu' id='importActionMenu'>
<?php
$misc = common::hasPriv('execution', 'importTask') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importTask') ? $this->createLink('execution', 'importTask', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importTask, '', $misc) . "</li>";
echo html::a('javascript:fullScreen()', "<i class='icon-fullscreen muted'></i> " . $lang->kanban->fullScreen, '', "class='btn btn-link'");
$actions = '';
$printSettingBtn = (common::hasPriv('kanban', 'createRegion') or (common::hasPriv('kanban', 'setLaneHeight')) or common::hasPriv('execution', 'edit') or common::hasPriv('execution', 'close') or common::hasPriv('execution', 'delete') or !empty($executionActions));
$misc = common::hasPriv('execution', 'importBug') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importBug') ? $this->createLink('execution', 'importBug', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importBug, '', $misc) . "</li>";
?>
</ul>
</div>
if($printSettingBtn)
{
$actions .= html::a('javascript:;', "<i class='icon icon-cog-outline'></i>" . $lang->kanban->setting, '', "data-toggle='dropdown' class='btn btn-link'");
$actions .= "<ul id='kanbanActionMenu' class='dropdown-menu pull-right'>";
$width = $this->app->getClientLang() == 'en' ? '70%' : '60%';
if(common::hasPriv('kanban', 'createRegion')) $actions .= '<li>' . html::a(helper::createLink('kanban', 'createRegion', "kanbanID=$execution->id&from=execution", '', true), '<i class="icon icon-plus"></i>' . $lang->kanban->createRegion, '', "class='iframe btn btn-link text-left'") . '</li>';
if(common::hasPriv('kanban', 'setLaneHeight')) $actions .= '<li>' . html::a(helper::createLink('kanban', 'setLaneHeight', "kanbanID=$execution->id&from=execution", '', true), '<i class="icon icon-size-height"></i>' . $lang->kanban->laneHeight, '', "class='iframe btn btn-link text-left' data-width=$width") . '</li>';
$kanbanActions = '';
if(common::hasPriv('execution', 'edit')) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'edit', "executionID=$execution->id", '', true), '<i class="icon icon-edit"></i>' . $lang->kanban->edit, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('start', $executionActions)) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'start', "executionID=$execution->id", '', true), '<i class="icon icon-play"></i>' . $lang->execution->start, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('putoff', $executionActions)) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'putoff', "executionID=$execution->id", '', true), '<i class="icon icon-calendar"></i>' . $lang->execution->putoff, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('suspend', $executionActions)) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'suspend', "executionID=$execution->id", '', true), '<i class="icon icon-pause"></i>' . $lang->execution->suspend, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('close', $executionActions)) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'close', "executionID=$execution->id", '', true), '<i class="icon icon-off"></i>' . $lang->execution->close, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('activate', $executionActions)) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'activate', "executionID=$execution->id", '', true), '<i class="icon icon-magic"></i>' . $lang->execution->activate, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(common::hasPriv('execution', 'delete')) $kanbanActions .= '<li>' . html::a(helper::createLink('execution', 'delete', "executionID=$execution->id"), '<i class="icon icon-trash"></i>' . $lang->delete, 'hiddenwin', "class='btn btn-link text-left'") . '</li>';
if($kanbanActions)
{
$actions .= ((common::hasPriv('kanban', 'createRegion') or common::hasPriv('kanban', 'setLaneHeight')) and (common::hasPriv('execution', 'edit') or common::hasPriv('execution', 'delete') or !empty($executionActions))) ? "<div class='divider'></div>" . $kanbanActions : $kanbanActions;
}
$actions .= "</ul>";
}
<?php
echo "<div class='btn-group menu-actions'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
echo "<ul class='dropdown-menu pull-right'>";
if(common::hasPriv('execution', 'printKanban')) echo '<li>' .html::a($this->createLink('execution', 'printKanban', "executionID=$executionID"), "<i class='icon-printer muted'></i> " . $lang->execution->printKanban, '', "class='iframe btn btn-link' id='printKanban' title='{$lang->execution->printKanban}' data-width='500'") . '</li>';
echo '<li>' .html::a('javascript:fullScreen()', "<i class='icon-fullscreen muted'></i> " . $lang->execution->fullScreen, '', "class='btn btn-link' title='{$lang->execution->fullScreen}' data-width='500'") . '</li>';
echo '</ul></div>';
?>
<?php
$checkObject = new stdclass();
$checkObject->execution = $executionID;
$canCreateTask = common::hasPriv('task', 'create', $checkObject);
$canBatchCreateTask = common::hasPriv('task', 'batchCreate', $checkObject);
$canCreateBug = common::hasPriv('bug', 'create');
$canBatchCreateBug = common::hasPriv('bug', 'batchCreate');
$canCreateStory = ($productID and common::hasPriv('story', 'create'));
$canBatchCreateStory = ($productID and common::hasPriv('story', 'batchCreate'));
$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory'));
$canLinkStoryByPlane = ($productID and common::hasPriv('execution', 'importplanstories'));
$hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane);
$hasTaskButton = ($canCreateTask or $canBatchCreateTask);
$hasBugButton = ($canCreateBug or $canBatchCreateBug);
echo $actions;
?>
<?php if($canCreateTask or $canBatchCreateTask or $canCreateBug or $canBatchCreateBug or $canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane):?>
<div class='dropdown' id='createDropdown'>
@@ -96,18 +158,42 @@
</ul>
</div>
<?php endif;?>
<?php else:?>
<?php $canCreateTask = $canBatchCreateTask = $canCreateBug = $canBatchCreateBug = $canCreateStory = $canBatchCreateStory = $canLinkStory = $canLinkStoryByPlane = false;?>
<?php endif;?>
</div>
</div>
<?php if($groupBy == 'default'):?>
<div class='panel' id='kanbanContainer'>
<div class='panel-body'>
<div id='kanbans'></div>
<div id="kanban" data-id='<?php echo $execution->id;?>'>
<?php foreach($regions as $region):?>
<div class="region<?php if($canSortRegion) echo ' sort';?>" data-id="<?php echo $region->id;?>">
<div class="region-header dropdown">
<span class="strong"><?php echo $region->name;?></span>
<label class="label label-region"><?php echo $this->lang->kanbanlane->common . ' ' . $region->laneCount;?></label>
<span><i class="icon icon-chevron-double-up" data-id="<?php echo $region->id;?>"></i></span>
<span class='regionActions'>
<?php if($canEditRegion || $canCreateLane || $canDeleteRegion):?>
<button class="btn btn-link action" type="button" data-toggle="dropdown"><i class="icon icon-ellipsis-v"></i></button>
<ul class="dropdown-menu pull-right">
<?php if($canEditRegion) echo '<li>' . html::a(helper::createLink('kanban', 'editRegion', "regionID={$region->id}", '', 1), '<i class="icon icon-edit"></i>' . $this->lang->kanban->editRegion, '', 'class="iframe" data-toggle="modal" data-width="600px"') . '</li>';?>
<?php if($canCreateLane) echo '<li>' . html::a(helper::createLink('kanban', 'createLane', "executionID={$execution->id}&regionID={$region->id}&from=execution", '', 1), '<i class="icon icon-plus"></i>' . $this->lang->kanban->createLane, '', "class='iframe'") . '</li>';?>
<?php if($canDeleteRegion and count($regions) > 1) echo '<li>' . html::a(helper::createLink('kanban', 'deleteRegion', "regionID={$region->id}"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->deleteRegion, "hiddenwin") . '</li>';?>
</ul>
<?php endif;?>
</span>
</div>
<div id='kanban<?php echo $region->id;?>' data-id='<?php echo $region->id;?>' class='kanban'></div>
</div>
<?php endforeach;?>
</div>
</div>
</div>
<?php else:?>
<div class='panel' id='kanbanContainer'>
<div class='panel-body region'>
<div id='kanbans' class='kanban'></div>
</div>
</div>
<?php endif;?>
<div class="modal fade" id="linkStoryByPlan">
<div class="modal-dialog mw-500px">
<div class="modal-content">
@@ -124,52 +210,4 @@
</div>
</div>
</div>
<?php js::set('executionID', $executionID);?>
<?php js::set('productID', $productID);?>
<?php js::set('kanbanGroup', $kanbanGroup);?>
<?php js::set('kanbanList', array_keys($kanbanGroup));?>
<?php js::set('browseType', $browseType);?>
<?php js::set('groupBy', $groupBy);?>
<?php
js::set('priv',
array(
'canEditName' => common::hasPriv('kanban', 'setColumn'),
'canSetWIP' => common::hasPriv('kanban', 'setWIP'),
'canSetLane' => common::hasPriv('kanban', 'setLane'),
'canMoveLane' => common::hasPriv('kanban', 'laneMove'),
'canSortCards' => common::hasPriv('kanban', 'cardsSort'),
'canCreateTask' => $canCreateTask,
'canBatchCreateTask' => $canBatchCreateTask,
'canCreateBug' => $canCreateBug,
'canBatchCreateBug' => $canBatchCreateBug,
'canCreateStory' => $canCreateStory,
'canBatchCreateStory' => $canBatchCreateStory,
'canLinkStory' => $canLinkStory,
'canLinkStoryByPlane' => $canLinkStoryByPlane,
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
'canPauseTask' => common::hasPriv('task', 'pause'),
'canCancelTask' => common::hasPriv('task', 'cancel'),
'canCloseTask' => common::hasPriv('task', 'close'),
'canActivateTask' => common::hasPriv('task', 'activate'),
'canStartTask' => common::hasPriv('task', 'start'),
'canAssignBug' => common::hasPriv('bug', 'assignto'),
'canConfirmBug' => common::hasPriv('bug', 'confirmBug'),
'canActivateBug' => common::hasPriv('bug', 'activate')
)
);
?>
<?php js::set('executionLang', $lang->execution);?>
<?php js::set('storyLang', $lang->story);?>
<?php js::set('taskLang', $lang->task);?>
<?php js::set('bugLang', $lang->bug);?>
<?php js::set('editName', $lang->execution->editName);?>
<?php js::set('setWIP', $lang->execution->setWIP);?>
<?php js::set('sortColumn', $lang->execution->sortColumn);?>
<?php js::set('kanbanLang', $lang->kanban);?>
<?php js::set('deadlineLang', $lang->task->deadlineAB);?>
<?php js::set('noAssigned', $lang->task->noAssigned);?>
<?php js::set('userList', $userList);?>
<?php js::set('entertime', time());?>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -58,7 +58,7 @@
}
}
echo "<li id='kanbanTab'>"; common::printLink('execution', 'kanban', "executionID=$executionID", $lang->execution->kanban); echo '</li>';
echo "<li id='kanbanTab'>"; common::printLink('execution', 'taskKanban', "executionID=$executionID", $lang->execution->kanban); echo '</li>';
if($execution->type == 'sprint' or $execution->type == 'waterfall')
{
echo "<li id='burnTab'>";
+181
View File
@@ -0,0 +1,181 @@
<?php
/**
* The task kanban view file of execution module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @package execution
* @version $Id: taskkanban.html.php $
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kanban.html.php';?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class="input-control space c-type">
<?php echo html::select('type', $lang->kanban->type, $browseType, 'class="form-control chosen" data-max_drop_width="215"');?>
</div>
<?php if($browseType != 'all'):?>
<div class="input-control space c-group">
<?php echo html::select('group', $lang->kanban->group->$browseType, $groupBy, 'class="form-control chosen" data-max_drop_width="215"');?>
</div>
<?php endif;?>
</div>
<div class='input-group pull-left not-fix-input-group' id='kanbanScaleControl'>
<span class='input-group-btn'>
<button class='btn btn-icon' type='button' data-type='-'><i class='icon icon-minuse-solid-circle text-muted'></i></button>
</span>
<span class='input-group-addon'>
<span id='kanbanScaleSize'>1</span><?php echo $lang->execution->kanbanCardsUnit; ?>
</span>
<span class='input-group-btn'>
<button class='btn btn-icon' type='button' data-type='+'><i class='icon icon-plus-solid-circle text-muted'></i></button>
</span>
</div>
<div class='btn-toolbar pull-right'>
<?php
$link = $this->createLink('task', 'export', "execution=$executionID&orderBy=$orderBy&type=kanban");
if(common::hasPriv('task', 'export')) echo html::a($link, "<i class='icon-export muted'></i> " . $lang->export, '', "class='btn btn-link iframe export' data-width='700'");
?>
<?php if($canBeChanged):?>
<div class='btn-group' style="margin-right: 0">
<button type='button' class='btn btn-link dropdown-toggle' data-toggle='dropdown' id='importAction'>
<i class='icon-import muted'></i> <?php echo $lang->import ?>
<span class='caret'></span>
</button>
<ul class='dropdown-menu' id='importActionMenu'>
<?php
$misc = common::hasPriv('execution', 'importTask') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importTask') ? $this->createLink('execution', 'importTask', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importTask, '', $misc) . "</li>";
$misc = common::hasPriv('execution', 'importBug') ? '' : "class=disabled";
$link = common::hasPriv('execution', 'importBug') ? $this->createLink('execution', 'importBug', "execution=$execution->id") : '#';
echo "<li $misc>" . html::a($link, $lang->execution->importBug, '', $misc) . "</li>";
?>
</ul>
</div>
<?php
echo "<div class='btn-group menu-actions'>";
echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");
echo "<ul class='dropdown-menu pull-right'>";
if(common::hasPriv('kanban', 'setLaneHeight'))
{
$width = $this->app->getClientLang() == 'en' ? '70%' : '60%';
echo '<li>' .html::a($this->createLink('kanban', 'setLaneHeight', "executionID=$executionID&from=execution", '', true), "<i class='icon-size-height'></i> " . $lang->kanban->laneHeight, '', "class='iframe btn btn-link' title='{$lang->kanban->laneHeight}' data-width=$width") . '</li>';
}
if(common::hasPriv('execution', 'printKanban')) echo '<li>' .html::a($this->createLink('execution', 'printKanban', "executionID=$executionID"), "<i class='icon-printer muted'></i> " . $lang->execution->printKanban, '', "class='iframe btn btn-link' id='printKanban' title='{$lang->execution->printKanban}' data-width='500'") . '</li>';
echo '<li>' .html::a('javascript:fullScreen()', "<i class='icon-fullscreen muted'></i> " . $lang->execution->fullScreen, '', "class='btn btn-link' title='{$lang->execution->fullScreen}' data-width='500'") . '</li>';
echo '</ul></div>';
?>
<?php
$checkObject = new stdclass();
$checkObject->execution = $executionID;
$canCreateTask = common::hasPriv('task', 'create', $checkObject);
$canBatchCreateTask = common::hasPriv('task', 'batchCreate', $checkObject);
$canCreateBug = common::hasPriv('bug', 'create');
$canBatchCreateBug = common::hasPriv('bug', 'batchCreate');
$canCreateStory = ($productID and common::hasPriv('story', 'create'));
$canBatchCreateStory = ($productID and common::hasPriv('story', 'batchCreate'));
$canLinkStory = ($productID and common::hasPriv('execution', 'linkStory'));
$canLinkStoryByPlane = ($productID and common::hasPriv('execution', 'importplanstories'));
$hasStoryButton = ($canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane);
$hasTaskButton = ($canCreateTask or $canBatchCreateTask);
$hasBugButton = ($canCreateBug or $canBatchCreateBug);
?>
<?php if($canCreateTask or $canBatchCreateTask or $canCreateBug or $canBatchCreateBug or $canCreateStory or $canBatchCreateStory or $canLinkStory or $canLinkStoryByPlane):?>
<div class='dropdown' id='createDropdown'>
<button class='btn btn-primary' type='button' data-toggle='dropdown'><i class='icon icon-plus'></i> <?php echo $this->lang->create;?> <span class='caret'></span></button>
<ul class='dropdown-menu pull-right'>
<?php if($canCreateStory) echo '<li>' . html::a(helper::createLink('story', 'create', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id", '', true), $lang->execution->createStory, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateStory) echo '<li>' . html::a(helper::createLink('story', 'batchCreate', "productID=$productID&branch=0&moduleID=0&story=0&execution=$execution->id", '', true), $lang->execution->batchCreateStroy, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php if($canLinkStory) echo '<li>' . html::a(helper::createLink('execution', 'linkStory', "execution=$execution->id", '', true), $lang->execution->linkStory, '', "class='iframe' data-width='90%'") . '</li>';?>
<?php if($canLinkStoryByPlane) echo '<li>' . html::a('#linkStoryByPlan', $lang->execution->linkStoryByPlan, '', 'data-toggle="modal"') . '</li>';?>
<?php if($hasStoryButton and $hasBugButton) echo '<li class="divider"></li>';?>
<?php if($canCreateBug) echo '<li>' . html::a(helper::createLink('bug', 'create', "productID=$productID&branch=0&extra=executionID=$execution->id", '', true), $lang->bug->create, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateBug) echo '<li>' . html::a(helper::createLink('bug', 'batchCreate', "productID=$productID&branch=0&executionID=$execution->id", '', true), $lang->bug->batchCreate, '', "class='iframe'") . '</li>';?>
<?php if(($hasStoryButton or $hasBugButton) and $hasTaskButton) echo '<li class="divider"></li>';?>
<?php if($canCreateTask) echo '<li>' . html::a(helper::createLink('task', 'create', "execution=$execution->id", '', true), $lang->task->create, '', "class='iframe'") . '</li>';?>
<?php if($canBatchCreateTask) echo '<li>' . html::a(helper::createLink('task', 'batchCreate', "execution=$execution->id", '', true), $lang->execution->batchCreateTask, '', "class='iframe'") . '</li>';?>
</ul>
</div>
<?php endif;?>
<?php else:?>
<?php $canCreateTask = $canBatchCreateTask = $canCreateBug = $canBatchCreateBug = $canCreateStory = $canBatchCreateStory = $canLinkStory = $canLinkStoryByPlane = false;?>
<?php endif;?>
</div>
</div>
<div class='panel' id='kanbanContainer'>
<div class='panel-body'>
<div id='kanbans'></div>
</div>
</div>
<div class="modal fade" id="linkStoryByPlan">
<div class="modal-dialog mw-500px">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="icon icon-close"></i></button>
<h4 class="modal-title"><?php echo $lang->execution->linkStoryByPlan;?></h4><?php echo '(' . $lang->execution->linkStoryByPlanTips . ')';?>
</div>
<div class="modal-body">
<div class='input-group'>
<?php echo html::select('plan', $allPlans, '', "class='form-control chosen' id='plan'");?>
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toStoryButton'", 'btn btn-primary');?></span>
</div>
</div>
</div>
</div>
</div>
<?php js::set('executionID', $executionID);?>
<?php js::set('productID', $productID);?>
<?php js::set('kanbanGroup', $kanbanGroup);?>
<?php js::set('kanbanList', array_keys($kanbanGroup));?>
<?php js::set('browseType', $browseType);?>
<?php js::set('groupBy', $groupBy);?>
<?php
js::set('priv',
array(
'canEditName' => common::hasPriv('kanban', 'setColumn'),
'canSetWIP' => common::hasPriv('kanban', 'setWIP'),
'canSetLane' => common::hasPriv('kanban', 'setLane'),
'canMoveLane' => common::hasPriv('kanban', 'laneMove'),
'canSortCards' => common::hasPriv('kanban', 'cardsSort'),
'canCreateTask' => $canCreateTask,
'canBatchCreateTask' => $canBatchCreateTask,
'canCreateBug' => $canCreateBug,
'canBatchCreateBug' => $canBatchCreateBug,
'canCreateStory' => $canCreateStory,
'canBatchCreateStory' => $canBatchCreateStory,
'canLinkStory' => $canLinkStory,
'canLinkStoryByPlane' => $canLinkStoryByPlane,
'canAssignTask' => common::hasPriv('task', 'assignto'),
'canAssignStory' => common::hasPriv('story', 'assignto'),
'canFinishTask' => common::hasPriv('task', 'finish'),
'canPauseTask' => common::hasPriv('task', 'pause'),
'canCancelTask' => common::hasPriv('task', 'cancel'),
'canCloseTask' => common::hasPriv('task', 'close'),
'canActivateTask' => common::hasPriv('task', 'activate'),
'canStartTask' => common::hasPriv('task', 'start'),
'canAssignBug' => common::hasPriv('bug', 'assignto'),
'canConfirmBug' => common::hasPriv('bug', 'confirmBug'),
'canActivateBug' => common::hasPriv('bug', 'activate')
)
);
?>
<?php js::set('executionLang', $lang->execution);?>
<?php js::set('storyLang', $lang->story);?>
<?php js::set('taskLang', $lang->task);?>
<?php js::set('bugLang', $lang->bug);?>
<?php js::set('editName', $lang->execution->editName);?>
<?php js::set('setWIP', $lang->execution->setWIP);?>
<?php js::set('sortColumn', $lang->execution->sortColumn);?>
<?php js::set('kanbanLang', $lang->kanban);?>
<?php js::set('deadlineLang', $lang->task->deadlineAB);?>
<?php js::set('noAssigned', $lang->task->noAssigned);?>
<?php js::set('userList', $userList);?>
<?php js::set('entertime', time());?>
<?php js::set('displayCards', $execution->displayCards);?>
<?php include '../../common/view/footer.html.php';?>
+6 -2
View File
@@ -674,6 +674,7 @@ $lang->resource->kanban->cardsSort = 'cardsSort';
$lang->resource->kanban->viewArchivedColumn = 'viewArchivedColumn';
$lang->resource->kanban->viewArchivedCard = 'viewArchivedCard';
$lang->resource->kanban->restoreCard = 'restoreCard';
$lang->resource->kanban->setLaneHeight = 'setLaneHeight';
$lang->kanban->methodOrder[5] = 'space';
$lang->kanban->methodOrder[10] = 'createSpace';
@@ -718,6 +719,7 @@ $lang->kanban->methodOrder[195] = 'viewArchivedColumn';
$lang->kanban->methodorder[200] = 'viewArchivedCard';
$lang->kanban->methodorder[205] = 'archiveColumn';
$lang->kanban->methodorder[210] = 'restoreCard';
$lang->kanban->methodorder[215] = 'setLaneHeight';
/* Execution. */
$lang->resource->execution = new stdclass();
@@ -759,7 +761,7 @@ $lang->resource->execution->linkStory = 'linkStory';
$lang->resource->execution->unlinkStory = 'unlinkStory';
$lang->resource->execution->batchUnlinkStory = 'batchUnlinkStory';
$lang->resource->execution->updateOrder = 'updateOrder';
$lang->resource->execution->kanban = 'kanban';
$lang->resource->execution->taskKanban = 'taskKanban';
$lang->resource->execution->printKanban = 'printKanbanAction';
$lang->resource->execution->tree = 'treeAction';
$lang->resource->execution->treeTask = 'treeOnlyTask';
@@ -775,6 +777,7 @@ $lang->resource->execution->addWhitelist = 'addWhitelist';
$lang->resource->execution->unbindWhitelist = 'unbindWhitelist';
$lang->resource->execution->storyEstimate = 'storyEstimate';
$lang->resource->execution->executionkanban = 'kanbanAction';
$lang->resource->execution->kanban = 'RDKanban';
//if($config->systemMode == 'classic') $lang->resource->project->list = 'list';
//$lang->execution->methodOrder[0] = 'index';
@@ -816,7 +819,7 @@ $lang->execution->methodOrder[170] = 'linkStory';
$lang->execution->methodOrder[175] = 'unlinkStory';
$lang->execution->methodOrder[180] = 'batchUnlinkStory';
$lang->execution->methodOrder[185] = 'updateOrder';
$lang->execution->methodOrder[190] = 'kanban';
$lang->execution->methodOrder[190] = 'taskKanban';
$lang->execution->methodOrder[195] = 'printKanban';
$lang->execution->methodOrder[200] = 'kanbanHideCols';
$lang->execution->methodOrder[205] = 'kanbanColsColor';
@@ -832,6 +835,7 @@ $lang->execution->methodOrder[250] = 'addWhitelist';
$lang->execution->methodOrder[255] = 'unbindWhitelist';
$lang->execution->methodOrder[260] = 'storyEstimate';
$lang->execution->methodOrder[265] = 'executionkanban';
$lang->execution->methodOrder[270] = 'kanban';
/* Task. */
$lang->resource->task = new stdclass();
+1 -1
View File
@@ -814,7 +814,7 @@ $(function()
event.stopPropagation();
});
$("#proLink").click(function(event)
$("#bizLink").click(function(event)
{
var $upgradeContent = $('#upgradeContent').toggle();
if(!$upgradeContent.is(':hidden'))
+1 -1
View File
@@ -77,7 +77,7 @@ js::set('showFeatures', $showFeatures);
<ul id='bars' class='nav nav-default'></ul>
<div id='poweredBy'>
<div id="globalBarLogo">
<a href='javascript:void(0)' id='proLink' class='btn btn-link' style='color: #B57D4F;'><span class='upgrade'><?php echo $lang->proName;?></span> <i class='text-danger icon-pro-version'></i></a>
<a href='javascript:void(0)' id='bizLink' class='btn btn-link' style='color: #B57D4F;'><span class='upgrade'><?php echo $lang->bizName;?></span> <i class='text-danger icon-pro-version'></i></a>
<a href='<?php echo $lang->website;?>' class="btn btn-sm btn-link" target='_blank' title='<?php echo $config->version;?>'>
<i class="icon icon-zentao" style="font-size: 24px;"></i>
<span class='version'><?php echo $lang->pmsName . $config->version;?></span>
+2
View File
@@ -38,6 +38,7 @@
<?php endif;?>
</td>
</tr>
<!--
<tr>
<td colspan='2'>
<h5><?php echo $lang->install->promotion?></h5>
@@ -56,6 +57,7 @@
</div>
</td>
</tr>
-->
</table>
</div>
<div class='modal-footer'>
+97 -20
View File
@@ -294,18 +294,19 @@ class kanban extends control
* Create a region.
*
* @param int $kanbanID
* @param string $from kanban|execution
* @access public
* @return void
*/
public function createRegion($kanbanID)
public function createRegion($kanbanID, $from = 'kanban')
{
if(!empty($_POST))
{
$kanban = $this->kanban->getByID($kanbanID);
$kanban = $from == 'execution' ? $this->loadModel('execution')->getByID($kanbanID) : $this->kanban->getByID($kanbanID);
$copyRegionID = (int)$_POST['region'];
unset($_POST['region']);
$regionID = $this->kanban->createRegion($kanban, '', $copyRegionID);
$regionID = $this->kanban->createRegion($kanban, '', $copyRegionID, $from);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
@@ -417,10 +418,11 @@ class kanban extends control
*
* @param int $kanbanID
* @param int $regionID
* @param string $from kanban|execution
* @access public
* @return void
*/
public function createLane($kanbanID, $regionID)
public function createLane($kanbanID, $regionID, $from = 'kanban')
{
if(!empty($_POST))
{
@@ -431,8 +433,12 @@ class kanban extends control
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
$this->view->lanes = $this->kanban->getLanePairsByRegion($regionID);
$this->display();
$this->view->lanes = $this->kanban->getLanePairsByRegion($regionID, $from == 'kanban' ? 'all' : 'story');
$this->view->from = $from;
$this->view->regionID = $regionID;
if($from == 'kanban') $this->display();
if($from == 'execution') $this->display('kanban', 'createexeclane');
}
/**
@@ -458,6 +464,34 @@ class kanban extends control
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess));
}
/**
* Set lane height.
*
* @param int $kanbanID
* @param string $from kanban|execution
* @access public
* @return void
*/
public function setLaneHeight($kanbanID, $from = 'kanban')
{
if(!empty($_POST))
{
$this->kanban->setLaneHeight($kanbanID, $from);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent'));
}
$kanban = $from == 'execution' ? $this->loadModel('execution')->getByID($kanbanID) : $this->kanban->getByID($kanbanID);
$this->view->heightType = $kanban->displayCards > 2 ? 'custom' : 'auto';
$this->view->displayCards = $kanban->displayCards ? $kanban->displayCards : '';
$this->display();
}
/**
* Delete a lane.
*
@@ -470,7 +504,10 @@ class kanban extends control
{
if($confirm == 'no')
{
die(js::confirm($this->lang->kanbanlane->confirmDelete, $this->createLink('kanban', 'deleteLane', "laneID=$laneID&confirm=yes"), ''));
$laneType = $this->kanban->getLaneById($laneID)->type;
$confirmTip = in_array($laneType, array('story', 'task', 'bug')) ? sprintf($this->lang->kanbanlane->confirmDeleteTip, $this->lang->{$laneType}->common) : $this->lang->kanbanlane->confirmDelete;
die(js::confirm($confirmTip, $this->createLink('kanban', 'deleteLane', "laneID=$laneID&confirm=yes"), ''));
}
else
{
@@ -710,15 +747,17 @@ class kanban extends control
* Move a card.
*
* @param int $cardID
* @param int $fromColID
* @param int $toColID
* @param int $fromLaneID
* @param int $toLaneID
* @param int $kanbanID
* @access public
* @return void
*/
public function moveCard($cardID, $toColID, $toLaneID, $kanbanID)
public function moveCard($cardID, $fromColID, $toColID, $fromLaneID, $toLaneID, $kanbanID)
{
$this->kanban->moveCard($cardID, $toColID, $toLaneID);
$this->kanban->moveCard($cardID, $fromColID, $toColID, $fromLaneID, $toLaneID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$kanbanGroup = $this->kanban->getKanbanData($kanbanID);
die(json_encode($kanbanGroup));
@@ -992,26 +1031,48 @@ class kanban extends control
*
* @param int $cardID
* @param int $fromColID
* @param string $toColType
* @param int $toColID
* @param int $fromLaneID
* @param int $toLaneID
* @param int $executionID
* @param string $browseType
* @param string $browseType
* @param string $groupBy
* @param int $regionID
* @param string $orderBy
* @access public
* @return json
* @return void
*/
public function ajaxMoveCard($cardID = 0, $fromColID = 0, $toColType = 'ready', $executionID = 0, $browseType = 'all', $groupBy = '')
public function ajaxMoveCard($cardID = 0, $fromColID = 0, $toColID = 0, $fromLaneID = 0, $toLaneID = 0, $executionID = 0, $browseType = 'all', $groupBy = '', $regionID = 0, $orderBy = '')
{
$fromColumn = $this->dao->select('*')->from(TABLE_KANBANCOLUMN)->where('id')->eq($fromColID)->fetch();
$toColumn = $this->dao->select('*')->from(TABLE_KANBANCOLUMN)->where('type')->eq($toColType)->andWhere('lane')->eq($fromColumn->lane)->fetch();
$fromCell = $this->dao->select('id, cards')->from(TABLE_KANBANCELL)
->where('kanban')->eq($executionID)
->andWhere('lane')->eq($fromLaneID)
->andWhere('`column`')->eq($fromColID)
->fetch();
$fromCards = str_replace(",$cardID,", ',', $fromColumn->cards);
$toCell = $this->dao->select('id, cards')->from(TABLE_KANBANCELL)
->where('kanban')->eq($executionID)
->andWhere('lane')->eq($toLaneID)
->andWhere('`column`')->eq($toColID)
->fetch();
$fromCards = str_replace(",$cardID,", ',', $fromCell->cards);
$fromCards = $fromCards == ',' ? '' : $fromCards;
$toCards = ",$cardID," . ltrim($toColumn->cards, ',');
$toCards = ",$cardID," . ltrim($toCell->cards, ',');
$this->dao->update(TABLE_KANBANCOLUMN)->set('cards')->eq($fromCards)->where('id')->eq($fromColumn->id)->exec();
$this->dao->update(TABLE_KANBANCOLUMN)->set('cards')->eq($toCards)->where('id')->eq($toColumn->id)->exec();
$this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($fromCards)
->where('kanban')->eq($executionID)
->andWhere('lane')->eq($fromLaneID)
->andWhere('`column`')->eq($fromColID)
->exec();
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy);
$this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($toCards)
->where('kanban')->eq($executionID)
->andWhere('lane')->eq($toLaneID)
->andWhere('`column`')->eq($toColID)
->exec();
$kanbanGroup = $regionID == 0 ? $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy) : $this->kanban->getRDKanban($executionID, $browseType, $orderBy, $regionID, $groupBy);
die(json_encode($kanbanGroup));
}
@@ -1084,4 +1145,20 @@ class kanban extends control
$this->view->method = $methodName;
$this->display();
}
/**
* Ajax get lanes by region id.
*
* @param int $regionID
* @param string $type all|story|task|bug
* @access public
* @return string
*/
public function ajaxGetLanes($regionID, $type = 'all')
{
$lanes = $this->kanban->getLanePairsByRegion($regionID, $type);
if(empty($lanes)) return;
return print(html::select('otherLane', $lanes, '', "class='form-control'"));
}
}
+3 -2
View File
@@ -58,8 +58,9 @@
.region .kanban-lane-items {overflow: auto; padding-bottom: 10px;}
.region .kanban-item {position: relative}
.region .kanban-item > .kanban-card > .title {line-height: 18px;}
.region .kanban-item > .kanban-card > .title:hover {color: #2272eb}
.region .kanban-item > .kanban-card > .info {margin-top: 5px; position: relative;}
.region .kanban-item > .kanban-card > .info {margin-top: 4px; position: relative;}
.region .kanban-item > .kanban-card > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;}
.region .kanban-item > .kanban-card > .info > .estimate{margin-left: 4px; color: #999; background-color: rgb(255, 255, 255); padding: 3px 0px}
.region .kanban-item > .kanban-card > .info > .time {margin-left: 4px; font-size: 12px; background: rgb(242, 242, 242)}
@@ -82,7 +83,7 @@
.region .kanban-item .has-color .info > .label-pri {border-color: #FFFFFF;}
.region .kanban-item .item-more {position: absolute; right: 6px; top: 0px;}
.kanban-card {height: auto !important; padding: 8px 14px !important}
.kanban-card {height: auto !important; padding: 8px 14px !important; min-height: 60px;}
.kanban-card > .title {word-break: break-all;}
.kanban-card > .info {position: relative; margin-top: 5px}
.kanban-card > .info > .info + .info {margin-left: 8px}
-1
View File
@@ -7,4 +7,3 @@ $(document).ready(function()
$('#otherLane').parents('tr').toggle($(this).val() == 'sameAsOther');
});
})
+17
View File
@@ -0,0 +1,17 @@
/**
* Set card count.
*
* @param string $heightType
* @access public
* @return void
*/
function setCardCount(heightType)
{
heightType != 'custom' ? $('#cardBox').addClass('hidden') : $('#cardBox').removeClass('hidden');
}
$(function()
{
var heightType = $("[name='heightType']:checked").val();
setCardCount(heightType);
})
+92 -8
View File
@@ -374,16 +374,19 @@ function showErrorMessager(message)
* Move a card.
*
* @param int $cardID
* @param int $fromColID
* @param int $toColID
* @param int $fromLaneID
* @param int $toLaneID
* @param int $kanbanID
* @param int $regionID
* @access public
* @return string
*/
function moveCard(cardID, toColID, toLaneID, kanbanID, regionID)
function moveCard(cardID, fromColID, toColID, fromLaneID, toLaneID, kanbanID, regionID)
{
if(!cardID) return false;
var url = createLink('kanban', 'moveCard', 'cardID=' + cardID + '&toColID=' + toColID + '&toLaneID=' + toLaneID + '&kanbanID=' + kanbanID);
var url = createLink('kanban', 'moveCard', 'cardID=' + cardID + '&fromColID='+ fromColID + '&toColID=' + toColID + '&fromLaneID='+ fromLaneID + '&toLaneID=' + toLaneID + '&kanbanID=' + kanbanID);
return $.ajax(
{
method: 'post',
@@ -460,6 +463,7 @@ function updateRegion(regionID, regionData = [])
if(!regionData) regionData = regions[regionID];
$region.data('zui.kanban').render(regionData.groups);
resetRegionHeight('open');
return true;
}
@@ -586,7 +590,7 @@ function handleDropTask($element, event, kanban)
if(oldCol.id === newCol.id && newLane.id === oldLane.id) return false;
var cardID = $card.data().id;
moveCard(cardID, newCol.id, newLane.id, kanbanID, regionID);
moveCard(cardID, oldCol.id, newCol.id, oldLane.id, newLane.id, kanbanID, regionID);
}
/**
@@ -762,6 +766,20 @@ function createColumnMenu(options)
return items;
}
/** Calculate column height */
function calcColHeight(col, lane, colCards, colHeight, kanban)
{
if(!isMultiLanes) return 0;
var options = kanban.options;
if(!options.displayCards) return 0;
var displayCards = +(options.displayCards || 2);
if (typeof displayCards !== 'number' || displayCards < 2) displayCards = 2;
return (displayCards * (options.cardHeight + options.cardSpace) + options.cardSpace);
}
/* Define menu creators */
window.menuCreators =
{
@@ -775,16 +793,20 @@ window.menuCreators =
*/
function initKanban($kanban)
{
var id = $kanban.data('id');
var region = regions[id];
var id = $kanban.data('id');
var region = regions[id];
var displayCards = window.displayCards == 'undefined' ? 2 : window.displayCards;
$kanban.kanban(
{
data: region.groups,
maxColHeight: 510,
calcColHeight: calcColHeight,
fluidBoardWidth: false,
minColWidth: 300,
maxColWidth: 300,
minColWidth: 285,
maxColWidth: 285,
cardHeight: 60,
displayCards: displayCards,
createColumnText: kanbanLang.createColumn,
addItemText: '',
itemRender: renderKanbanItem,
@@ -813,7 +835,7 @@ function initKanban($kanban)
*/
$(function()
{
if($.cookie('isFullScreen') == 1) fullScreen();
window.isMultiLanes = laneCount > 1;
/* Init first kanban */
$('.kanban').each(function()
@@ -826,6 +848,7 @@ $(function()
$(this).toggleClass('icon-chevron-double-up icon-chevron-double-down');
$(this).parents('.region').find('.kanban').toggle();
hideKanbanAction();
resetRegionHeight($(this).hasClass('icon-chevron-double-up') ? 'open' : 'close');
});
$('.region-header').on('click', '.action', hideKanbanAction);
@@ -1012,4 +1035,65 @@ $(function()
if(sortType == 'lane') $cards.show();
}
});
resetRegionHeight('open');
});
/**
* Reset region height according to window height.
*
* @param string fold
* @access public
* @return void
*/
function resetRegionHeight(fold)
{
var regionCount = 0;
if($.isEmptyObject(regions)) return false;
for(var i in regions)
{
regionCount += 1;
if(regionCount > 1) return false;
}
var regionID = Object.keys(regions)[0];
var region = regions[regionID].groups;
var groupCount = 0;
if($.isEmptyObject(region)) return false;
for(var j in region)
{
groupCount += 1;
if(groupCount > 1) return false;
}
var group = region[0];
var laneCount = 0;
if($.isEmptyObject(group.lanes)) return false;
for(var h in group.lanes)
{
laneCount += 1;
if(laneCount > 1) return false;
}
var regionHeaderHeight = $('.region-header').outerHeight();
if(fold == 'open')
{
var windowHeight = $(window).height();
var headerHeight = $('#mainHeader').outerHeight();
var mainPadding = $('#main').css('padding-top');
var panelBorder = $('.panel').css('border-top-width');
var bodyPadding = $('.panel-body').css('padding-top');
var height = windowHeight - (parseInt(mainPadding) * 2) - (parseInt(bodyPadding) * 2) - headerHeight - (parseInt(panelBorder) * 2);
var regionPadding = $('.kanban').css('padding-bottom');
var columnHeight = $('.kanban-header').outerHeight();
$('.region').css('height', height);
$('.kanban-lane').css('height', height - regionHeaderHeight - parseInt(regionPadding) - columnHeight);
}
else
{
$('.region').css('height', regionHeaderHeight);
}
}
+17 -3
View File
@@ -18,6 +18,8 @@ $lang->kanban->deleteRegion = 'Delete Region';
$lang->kanban->createLane = 'Create Lane';
$lang->kanban->editLane = 'Edit Lane';
$lang->kanban->sortLane = 'Sort Lane';
$lang->kanban->laneHeight = 'Lane Height';
$lang->kanban->setLaneHeight = 'Set Lane Height';
$lang->kanban->deleteLane = 'Delete Lane';
$lang->kanban->createColumn = 'Create Column';
$lang->kanban->editColumn = 'Edit Column';
@@ -73,15 +75,18 @@ $lang->kanban->closedBy = 'Closed By';
$lang->kanban->closedDate = 'Closed Date';
$lang->kanban->empty = 'No Kanban';
$lang->kanban->teamSumCount = '%s people in total';
$lang->kanban->cardCount = 'Card Count';
$lang->kanban->createColumnOnLeft = 'Create Column On Left';
$lang->kanban->createColumnOnRight = 'Create Column On Right';
$lang->kanban->accessDenied = "You have no access to the kanban.";
$lang->kanban->confirmDelete = 'Do you want to delete this?';
$lang->kanban->cardCountTip = 'Please enter the number of cards';
$lang->kanban->aclGroup['open'] = 'Open';
$lang->kanban->aclGroup['private'] = 'Private';
$lang->kanban->aclGroup['extend'] = 'Extend';
$lang->kanban->aclList['extend'] = 'Extend (Accessible with space view permissions)';
$lang->kanban->aclList['private'] = 'Private (For the kanban team, whitelist members and space owner only)';
@@ -244,11 +249,20 @@ $lang->kanbanlane->default = 'Default Lane';
$lang->kanbanlane->column = 'Lane Kanban Column';
$lang->kanbanlane->otherlane = 'Select Existed Lane';
$lang->kanbanlane->color = 'Lane Color';
$lang->kanbanlane->WIPType = 'Lane WIP Type';
$lang->kanbanlane->confirmDelete = 'Are you sure to delete this lane? After deleting the lane, all data (columns and cards) in the lane will also be deleted.';
$lang->kanbanlane->confirmDelete = 'Are you sure to delete this lane? After deleting the lane, all data (columns and cards) in the lane will also be deleted.';
$lang->kanbanlane->confirmDeleteTip = 'Are you sure to delete this lane? After deleting the lane, all %s in the lane will be hidden.';
$lang->kanbanlane->modeList['sameAsOther'] = 'Use the same Kanban column';
$lang->kanbanlane->modeList['independent'] = 'Independent Kanban column';
$lang->kanbanlane->heightTypeList['auto'] = 'Adaptive (Adaptive according to the card height)';
$lang->kanbanlane->heightTypeList['custom'] = 'Custom (Customize lane height based on number of cards)';
$lang->kanbanlane->error = new stdclass();
$lang->kanbanlane->error->mustBeInt = 'The number of cards must be a positive integer greater than 2.';
$lang->kanbanlane->modeList['sameAsOther'] = 'Use the same Kanban column as other lanes';
$lang->kanbanlane->modeList['independent'] = 'Independent Kanban column is adopted';
$lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = 'Region Name';
+14 -1
View File
@@ -18,6 +18,8 @@ $lang->kanban->deleteRegion = '删除区域';
$lang->kanban->createLane = '创建泳道';
$lang->kanban->editLane = '泳道设置';
$lang->kanban->sortLane = '泳道排序';
$lang->kanban->laneHeight = '泳道高度';
$lang->kanban->setLaneHeight = '设置泳道高度';
$lang->kanban->deleteLane = '删除泳道';
$lang->kanban->createColumn = '创建看板列';
$lang->kanban->editColumn = '编辑看板列';
@@ -73,15 +75,18 @@ $lang->kanban->closedBy = '由谁关闭';
$lang->kanban->closedDate = '关闭日期';
$lang->kanban->empty = '暂时没有看板';
$lang->kanban->teamSumCount = '共%s人';
$lang->kanban->cardCount = '卡片数量';
$lang->kanban->createColumnOnLeft = '在左侧添加看板列';
$lang->kanban->createColumnOnRight = '在右侧添加看板列';
$lang->kanban->accessDenied = '您无权访问该看板';
$lang->kanban->confirmDelete = '您确认删除吗?';
$lang->kanban->cardCountTip = '请输入卡片数量';
$lang->kanban->aclGroup['open'] = '公开';
$lang->kanban->aclGroup['private'] = '私有';
$lang->kanban->aclGroup['extend'] = '继承空间';
$lang->kanban->aclList['extend'] = '继承空间访问权限(能访问当前空间,即可访问)';
$lang->kanban->aclList['private'] = '私有(看板团队成员、白名单、空间负责人可访问)';
@@ -244,12 +249,20 @@ $lang->kanbanlane->default = '默认泳道';
$lang->kanbanlane->column = '泳道看板列';
$lang->kanbanlane->otherlane = '选择共享看板列的泳道';
$lang->kanbanlane->color = '泳道颜色';
$lang->kanbanlane->WIPType = '泳道在制品类型';
$lang->kanbanlane->confirmDelete = '您确认删除该泳道吗?删除泳道后,该泳道中所有数据(列、卡片)也会被删除。';
$lang->kanbanlane->confirmDelete = '您确认删除该泳道吗?删除泳道后,该泳道中所有数据(列、卡片)也会被删除。';
$lang->kanbanlane->confirmDeleteTip = '您确认删除该泳道吗?删除泳道后,该泳道中所有的%s将被隐藏。';
$lang->kanbanlane->modeList['sameAsOther'] = '与其他泳道使用相同看板列';
$lang->kanbanlane->modeList['independent'] = '采用独立的看板列';
$lang->kanbanlane->heightTypeList['auto'] = '自适应(根据卡片高度自适应)';
$lang->kanbanlane->heightTypeList['custom'] = '自定义(根据卡片数量自定义泳道高度)';
$lang->kanbanlane->error = new stdclass();
$lang->kanbanlane->error->mustBeInt = '卡片数量必须是大于2的正整数。';
$lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = '区域名称';
$lang->kanbanregion->default = '默认区域';
+795 -258
View File
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
<?php
/**
* The createexeclane file of kanban module of ZenTaoPMS.
*
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Shujie Tian <tianshujie@easycorp.ltd>
* @package kanban
* @version $Id: createexeclane.html.php 935 2022-01-12 17:22:24Z $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('colorList',$config->kanban->laneColorList);?>
<?php js::set('regionID', $regionID);?>
<?php js::set('lanes', $lanes);?>
<style>
.table-form>tbody>tr>th {width: 150px;}
#otherLane_chosen {top: -8px;}
#laneTypeBox {line-height: 16px;}
.margin-bottom {margin-bottom: -6px;}
</style>
<div id='mainContent' class='main-content'>
<div class='main-header'>
<h2>
<?php echo $lang->kanban->createLane;?>
</h2>
</div>
<form class='load-indicator main-form form-ajax' method='post' enctype='multipart/form-data'>
<table class='table table-form'>
<tbody>
<tr>
<th><?php echo $lang->kanbanlane->name;?></th>
<td colspan='3'>
<div class='required required-wrapper'></div>
<?php echo html::input('name', '', "class='form-control'");?>
</td>
</tr>
<tr>
<th><?php echo $lang->kanbanlane->WIPType;?></th>
<td id='laneTypeBox'><?php echo html::radio('laneType', $lang->kanban->laneTypeList, 'story');?></td>
</tr>
<tr>
<th><?php echo $lang->kanbanlane->column;?></th>
<?php $mode = empty($lanes) ? 'independent' : 'sameAsOther';?>
<td id='modeBox'><?php echo html::radio('mode', $lang->kanbanlane->modeList, $mode, '', 'block');?></td>
<td id='laneBox' <?php if(empty($lanes)) echo 'hidden';?>><?php echo html::select('otherLane', $lanes, '', "class='form-control chosen'");?></td>
</tr>
<tr>
<th><?php echo $lang->kanbanlane->color;?></th>
<td colspan='3'>
<div id='color-picker'></div>
<?php echo html::input('color', '#7ec5ff', "class='hidden'");?>
</td>
</tr>
<tr>
<td class='text-center form-actions' colspan='4'>
<?php echo html::submitButton();?>
<?php echo html::commonButton($lang->cancel, "data-dismiss='modal'", 'btn btn-wide');?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
<script>
$(document).ready(function()
{
initColorPicker();
if($.isEmptyObject(lanes))
{
$('#modesameAsOther').closest('div').addClass('hidden');
$('#laneBox').addClass('hidden');
$('#modeindependent').attr('checked', 'checked');
$('#modeindependent').closest('div').addClass('margin-bottom');
}
$('input[name=mode]').change(function()
{
$('#otherLane').parents('td').toggle($(this).val() == 'sameAsOther');
if($(this).val() == 'sameAsOther') $('#laneBox').removeClass('hidden');
});
$('input[name=laneType]').change(function()
{
var laneType = $(this).val();
var link = createLink('kanban', 'ajaxGetLanes', 'regionID=' + regionID + '&type=' + laneType);
$.get(link, function(lanes)
{
if(lanes)
{
$('#modesameAsOther').parents('div').removeClass('hidden');
$('#otherLane').replaceWith(lanes);
$('#otherLane_chosen').remove();
$('#otherLane').chosen();
$('#modeindependent').closest('div').removeClass('margin-bottom');
}
else
{
$('#modesameAsOther').closest('div').addClass('hidden');
$('#laneBox').addClass('hidden');
$('#modeindependent').attr('checked', 'checked');
$('#modeindependent').closest('div').addClass('margin-bottom');
}
})
});
})
</script>
<?php include '../../common/view/footer.html.php';?>
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* The setlaneheight file of kanban module of ZenTaoPMS.
*
* @copyright Copyright 2009-2022 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Shujie Tian <tianshujie@easycorp.ltd>
* @package kanban
* @version $Id: setlaneheight.html.php 935 2022-01-11 10:49:24Z $
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->kanban->setLaneHeight;?></h2>
</div>
<form class='main-form form-ajax' method='post' enctype='multipart/form-data' id='dataform'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->kanban->laneHeight;?></th>
<td><?php echo nl2br(html::radio('heightType', $lang->kanbanlane->heightTypeList, $heightType, "onclick='setCardCount(this.value);'"));?></td>
</tr>
<tr class="hidden" id='cardBox'>
<th class='c-count'><?php echo $lang->kanban->cardCount;?></th>
<td><?php echo html::input('displayCards', $displayCards, "class='form-control' required placeholder='{$lang->kanban->cardCountTip}' autocomplete='off'");?></td>
</tr>
<tr>
<td colspan='2' class='text-center form-actions'>
<?php echo html::submitButton();?>
</td>
</tr>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+4 -2
View File
@@ -141,9 +141,11 @@
<div class='kanban-members-total pull-left'><?php echo sprintf($lang->kanban->teamSumCount, $teamCount);?></div>
</div>
<div class='kanbanAcl'>
<?php $icon = $kanban->acl == 'open' ? 'unlock' : 'lock';?>
<?php $icon = 'unlock';?>
<?php if($kanban->acl == 'private') $icon = 'lock';?>
<?php if($kanban->acl == 'extend') $icon = 'inherit-space';?>
<i class="<?php echo 'icon-' . $icon;?>"></i>
<?php echo zget($lang->kanban->aclGroup, $kanban->acl == 'open' ? 'open' : 'private', '');?>
<?php echo zget($lang->kanban->aclGroup, $kanban->acl, '');?>
</div>
</div>
</div>
+1
View File
@@ -30,6 +30,7 @@ js::set('noAssigned', $lang->kanbancard->noAssigned);
js::set('users', $users);
js::set('colorListLang', $lang->kanbancard->colorList);
js::set('colorList', $this->config->kanban->cardColorList);
js::set('displayCards', $kanban->displayCards);
js::set('priv',
array(
+2
View File
@@ -94,6 +94,7 @@ $lang->misc->feature->downloadFile = 'Download introduction';
$lang->misc->feature->tutorialDesc = '<p>ZenTao 15.0 has new functions, and you know how to use it through the "<strong>Tutorial</strong>".</p><p>Click your [<span style="color: #0c60e1">Avatar-Theme-Young Blue</span>] to set it.</p>';
$lang->misc->feature->themeDesc = '<p>ZenTao 15.0+ a new "Youth Blue" theme, the pages are more beautiful and the experience is more friendly.</p><p>Click your [<span style="color: #0c60e1">Avatar-Theme-Young Blue</span>] to set it.</p>';
$lang->misc->releaseDate['16.2'] = '2022-01-17';
$lang->misc->releaseDate['16.1'] = '2022-01-11';
$lang->misc->releaseDate['16.0'] = '2021-12-24';
$lang->misc->releaseDate['16.0.beta1'] = '2021-12-06';
@@ -165,6 +166,7 @@ $lang->misc->releaseDate['7.2.stable'] = '2015-05-22';
$lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
$lang->misc->feature->all['16.2'][] = array('title' => 'Add kanban model project, fix bugs.', 'desc' => '');
$lang->misc->feature->all['16.1'][] = array('title' => 'Plan to add status management and Kanban view, upgrade process optimization, fix bugs.', 'desc' => '');
$lang->misc->feature->all['16.0'][] = array('title' => 'New general board, improve branch management, fix bugs.', 'desc' => '');
$lang->misc->feature->all['16.0.beta1'][] = array('title' => 'Add waterfall model project, add task kanban, improved branch management, and fixed bugs.', 'desc' => '');
+2
View File
@@ -94,6 +94,7 @@ $lang->misc->feature->downloadFile = '下载新版本功能介绍文档';
$lang->misc->feature->tutorialDesc = "<p>禅道15系列新增了多项功能,您可以通过“<strong>新手引导教程</strong>”快速了解禅道的基本使用方法。</p><p>通过鼠标经过 [<span style='color: #0c60e1'>头像-新手引导</span>],点击新手引导,即可进入新手引导教程。</p>";
$lang->misc->feature->themeDesc = "<p>禅道15系列上线了全新的“青春蓝”主题,页面呈现更加美观,体验更加友好。</p><p>通过鼠标经过 [<span style='color: #0c60e1'>头像-主题-青春蓝</span>],点击青春蓝,即可设置成功。</p>";
$lang->misc->releaseDate['16.2'] = '2022-01-17';
$lang->misc->releaseDate['16.1'] = '2022-01-11';
$lang->misc->releaseDate['16.0'] = '2021-12-24';
$lang->misc->releaseDate['16.0.beta1'] = '2021-12-06';
@@ -165,6 +166,7 @@ $lang->misc->releaseDate['7.2.stable'] = '2015-05-22';
$lang->misc->releaseDate['7.1.stable'] = '2015-03-07';
$lang->misc->releaseDate['6.3.stable'] = '2014-11-07';
$lang->misc->feature->all['16.2'][] = array('title' => '新增专业研发看板,可以创建看板模型项目,修复Bug。', 'desc' => '');
$lang->misc->feature->all['16.1'][] = array('title' => '计划增加状态管理和看板视图,升级流程优化,修复Bug。', 'desc' => '');
$lang->misc->feature->all['16.0'][] = array('title' => '新增通用看板,完善分支管理,修复Bug。', 'desc' => '');
$lang->misc->feature->all['16.0.beta1'][] = array('title' => '新增瀑布模型项目,新增任务看板,完善分支管理和细节,修复Bug。', 'desc' => '');
+1 -1
View File
@@ -7,7 +7,7 @@
<div class='container mw-900px' id="zentaoLinks">
<div class='row'>
<?php foreach($lang->misc->zentao as $label => $groupItems):?>
<?php if(strpos(',labels,icons,version,', ",$label,") !== false) continue; ?>
<?php if(strpos(',labels,icons,version,others,', ",$label,") !== false) continue; ?>
<div class='col-sm-2'>
<div class='panel <?php echo $label;?>'>
<div class='panel-heading'>
+1
View File
@@ -443,6 +443,7 @@ EOF;
$this->view->tasks = $tasks;
$this->view->summary = $this->loadModel('execution')->summary($tasks);
$this->view->type = $type;
$this->view->kanbanList = $this->execution->getPairs(0, 'kanban');
$this->view->recTotal = $recTotal;
$this->view->recPerPage = $recPerPage;
$this->view->pageID = $pageID;
+1 -1
View File
@@ -114,7 +114,7 @@ $lang->my->executionLinkList = array();
if($config->systemMode == 'new')
{
$lang->my->executionLinkList['execution-all'] = 'Enter the execution list by default, you can view all executions';
$lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration';
$lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task or kanban view, and you can view the task information under the current iteration';
$lang->my->executionLinkList['execution-executionkanban'] = 'By default, you can enter the execution Kanban to view the execution status of projects in progress';
}
if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "By default, enter the list of the most recently {$lang->executionCommon} task, and you can view the task information under the current {$lang->executionCommon}";
+1 -1
View File
@@ -114,7 +114,7 @@ $lang->my->executionLinkList = array();
if($config->systemMode == 'new')
{
$lang->my->executionLinkList['execution-all'] = '默认进入执行列表,可以查看所有的执行';
$lang->my->executionLinkList['execution-task'] = '默认进入最近一个执行的任务列表,可以查看当前迭代下的任务信息';
$lang->my->executionLinkList['execution-task'] = '默认进入最近一个执行的任务列表或看板视图,可以查看当前迭代下的任务信息';
$lang->my->executionLinkList['execution-executionkanban'] = '默认进入执行看板,可以查看进行中项目的执行情况';
}
if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "默认进入最近一个{$lang->executionCommon}的任务列表,可以查看当前{$lang->executionCommon}下的任务信息";
+8 -7
View File
@@ -131,14 +131,15 @@
}
else
{
if($task->status != 'pause') common::printIcon('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project);
if($task->status == 'pause') common::printIcon('task', 'restart', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project);
common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project);
common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, '', '', $task->project);
$attr = isset($kanbanList[$task->execution]) ? "disabled" : '';
if($task->status != 'pause') common::printIcon('task', 'start', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
if($task->status == 'pause') common::printIcon('task', 'restart', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
common::printIcon('task', 'close', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
common::printIcon('task', 'finish', "taskID=$task->id", $task, 'list', '', '', 'iframe', true);
common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true, '', '', $task->project);
common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, "data-width='95%'", '', $task->project);
common::printIcon('task', 'batchCreate', "executionID=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id&ifame=true", $task, 'list', 'split', '', 'iframe', true, "data-width='95%'", $this->lang->task->children, $task->project);
common::printIcon('task', 'recordEstimate', "taskID=$task->id", $task, 'list', 'time', '', 'iframe', true);
common::printIcon('task', 'edit', "taskID=$task->id", $task, 'list', '', '', 'iframe', true, "data-width='95%'");
common::printIcon('task', 'batchCreate', "executionID=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id&ifame=true", $task, 'list', 'split', '', 'iframe', true, "data-width='95%' $attr", $this->lang->task->children);
}
}
?>
+1 -1
View File
@@ -1465,7 +1465,7 @@ class productModel extends model
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t2.deleted')->eq(0)
->andWhere('t1.product')->eq($productID)
->andWhere('t2.type')->in('sprint,stage')
->andWhere('t2.type')->in('sprint,stage,kanban')
->fetch();
$product->stories = $stories;
+1 -1
View File
@@ -161,7 +161,7 @@ $(function()
}
})
$('#tabContent [data-ride="tree"]').tree('expand');
$('#swapper [data-ride="tree"]').tree('expand');
$('#swapper #dropMenu .search-box').on('onSearchChange', function(event, value)
{
+1 -1
View File
@@ -99,7 +99,7 @@ class productplanModel extends model
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
->where('t1.product')->eq($product)
->andWhere('t1.plan')->in(array_keys($plans))
->andWhere('t2.type')->in('sprint,stage')
->andWhere('t2.type')->in('sprint,stage,kanban')
->fetchPairs('plan', 'project');
$storyCountInTable = $this->dao->select('plan,count(story) as count')->from(TABLE_PLANSTORY)->where('plan')->in($planIdList)->groupBy('plan')->fetchPairs('plan', 'count');
+3
View File
@@ -0,0 +1,3 @@
.project-type-label.label-outline {width: 50px; min-width: 50px;}
.project-type-label.label {overflow: unset !important; text-overflow: unset !important; white-space: unset !important;}
[lang^='en'] .project-name > .label-warning {width: 55px !important;}
+2 -2
View File
@@ -205,7 +205,7 @@ class programModel extends model
/* Get doing executions. */
$doingExecutions = $this->dao->select('id, project, name, end')->from(TABLE_EXECUTION)
->where('type')->in('sprint,stage')
->where('type')->in('sprint,stage,kanban')
->andWhere('status')->eq('doing')
->andWhere('deleted')->eq(0)
->beginIF(!$this->app->user->admin)->andWhere('id')->in($this->app->user->view->sprints)->fi()
@@ -1254,7 +1254,7 @@ class programModel extends model
$executions = $this->dao->select('id')->from(TABLE_EXECUTION)
->where('deleted')->eq(0)
->andWhere('project')->in($projectKeys)
->andWhere('type')->in('sprint,stage')
->andWhere('type')->in('sprint,stage,kanban')
->fetchAll('id');
/* Get all tasks and compute totalEstimate, totalConsumed, totalLeft, progress according to them. */
-4
View File
@@ -15,10 +15,6 @@
js::set('programID', $programID);
js::set('browseType', $browseType);
?>
<style>
.project-type-label.label-outline {width: 50px; min-width: 50px;}
.project-type-label.label {overflow: unset !important; text-overflow: unset !important; white-space: unset !important;}
</style>
<div id="mainMenu" class="clearfix">
<div class="btn-toolBar pull-left">
<?php foreach($lang->program->featureBar as $key => $label):?>
+29 -5
View File
@@ -53,7 +53,7 @@ class project extends control
unset($fields[$key]);
}
$projects = $this->project->getInfoList($status, 30, $orderBy, null);
$projects = $this->project->getInfoList($status, $orderBy);
$users = $this->loadModel('user')->getPairs('noletter');
$this->loadModel('product');
@@ -222,10 +222,11 @@ class project extends control
* Project index view.
*
* @param int $projectID
* @param string $browseType
* @access public
* @return void
*/
public function index($projectID = 0)
public function index($projectID = 0, $browseType = 'all')
{
$projectID = $this->project->saveState($projectID, $this->project->getPairsByProgram());
@@ -240,6 +241,27 @@ class project extends control
if(!$projectID) $this->locate($this->createLink('project', 'browse'));
setCookie("lastProject", $projectID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
if($project->model == 'kanban')
{
$kanbanList = $this->loadModel('execution')->getList($projectID, 'all', $browseType);
$executionActions = array();
foreach($kanbanList as $kanbanID => $kanban)
{
foreach($this->config->execution->statusActions as $action)
{
if($this->execution->isClickable($kanban, $action)) $executionActions[$kanbanID][] = $action;
}
if($this->execution->isClickable($kanban, 'delete')) $executionActions[$kanbanID][] = 'delete';
}
$this->view->kanbanList = $kanbanList;
$this->view->browseType = $browseType;
$this->view->memberGroup = $this->execution->getMembersByIdList(array_keys($kanbanList));
$this->view->usersAvatar = $this->loadModel('user')->getAvatarPairs();
$this->view->executionActions = $executionActions;
}
$this->view->title = $this->lang->project->common . $this->lang->colon . $this->lang->project->index;
$this->view->position[] = $this->lang->project->index;
$this->view->project = $project;
@@ -352,6 +374,8 @@ class project extends control
$this->loadModel('execution');
$this->loadModel('product');
if($model == 'kanban') unset($this->lang->project->authList['reset']);
if($_POST)
{
$projectID = $this->project->create();
@@ -506,6 +530,7 @@ class project extends control
$project = $this->project->getByID($projectID);
$programID = $project->parent;
$this->project->setMenu($projectID);
if($project->model == 'kanban') unset($this->lang->project->authList['reset']);
if($_POST)
{
@@ -576,8 +601,7 @@ class project extends control
}
}
$canChangeModel = $this->project->checkCanChangeModel($projectID, $project->model);
$disableModel = $canChangeModel == true ? '' : 'disabled';
if($project->model != 'kanban') $canChangeModel = $this->project->checkCanChangeModel($projectID, $project->model);
$this->view->title = $this->lang->project->edit;
$this->view->position[] = $this->lang->project->edit;
@@ -604,7 +628,7 @@ class project extends control
$this->view->availableBudget = $this->program->getBudgetLeft($parentProject) + (float)$project->budget;
$this->view->budgetUnitList = $this->project->getBudgetUnitList();
$this->view->model = $project->model;
$this->view->disableModel = $disableModel;
$this->view->disableModel = (isset($canChangeModel) and $canChangeModel == true) ? '' : 'disabled';
$this->display();
}
+2
View File
@@ -1,3 +1,4 @@
body {margin-bottom: 25px;}
.side-col .detail-content {padding-left: 10px; margin-top: 0px;}
.side-col {padding-right: 20px; width: 18%;}
#sidebar>.cell {width: 100%;}
@@ -16,3 +17,4 @@ th.c-name {width: 360px !important;}
{
th.c-name {width: auto;}
}
[lang^='en'] .project-name > .label-warning {width: 55px !important;}
+30
View File
@@ -0,0 +1,30 @@
#cards > .col {width: 25% !important;}
#cards .panel {margin: 0 0; border: 1px solid #DCDCDC; border-radius: 4px; box-shadow: none; cursor: pointer; height: 160px; margin-bottom: 5px;}
#cards .panel:hover {border-color: #006AF1; box-shadow: 0 0 10px 0 rgba(0,0,100,.25);}
#cards .panel-heading {padding: 4px 2px 10px 16px;}
#cards .kanban-name {overflow: hidden; white-space:nowrap; width: 88%; float: left; padding-top: 8px;}
#cards .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-1px !important;}
#cards .kanban-actions {float: right; visibility: hidden;}
#cards .kanban-actions .dropdown-menu.pull-right {top:31px; right: -84px; left: auto;}
#cards .kanban-actions .dropdown-menu.pull-left {top:31px; right:-1px; left: auto;}
#cards .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-6px;}
#cards .panel-body {padding: 0 16px 16px;}
#cards .kanban-desc {color: #838a9d; word-break:break-all; height: 70px; overflow: hidden; display: -webkit-box; float:left;}
#cards .kanban-footer {position: absolute; bottom: 10px; right: 10px; left: 15px;}
#cards .kanban-members {float: left; height: 24px; line-height: 24px;}
#cards .kanban-members > div {display: inline-block; height: 24px;}
#cards .kanban-members > div + div {margin-left: -5px;}
#cards .kanban-members > div > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 1px;}
#cards .kanban-members > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position: relative; border-radius: 50%; top: 3px; margin: 0 3px;}
#cards .kanban-members > span:before,
#cards .kanban-members > span:after {content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0; border-radius: 50%}
#cards .kanban-members > span:before {left: -4px;}
#cards .kanban-members > span:after {right: -4px;}
#cards .kanban-members-total {display: inline-block; margin-left: 6px; position: relative; top: 3px}
#cards .kanbanAcl {position: absolute; right: 0px; bottom: 0px; color: #838a9d;}
.kanbans .kanban-actions {float: right; visibility: hidden;}
.kanbans .kanban-actions .dropdown-menu.pull-right {top:31px; right: -84px; left: auto;}
.kanbans .kanban-actions .dropdown-menu.pull-left {top:31px; right:-1px; left: auto;}
.label-doing {background: #ff5d5d}
+21
View File
@@ -0,0 +1,21 @@
/* Make cards clickable. */
$('#cards').on('click', '.panel', function(e)
{
if(!$(e.target).closest('.kanban-actions').length)
{
$.apps.open($(this).data('url'));
}
});
/* Display drop-down menu.*/
$('.panel').mouseenter(function(e)
{
$('.kanban-actions' + e.currentTarget.parentElement.dataset.id).css('visibility','visible');
});
/* Hide drop-down menu. */
$('.panel').mouseleave(function(e)
{
$('.kanban-actions').css('visibility','hidden');
$('.dropdown').removeClass('open');
});
+7 -2
View File
@@ -155,6 +155,8 @@ $lang->project->etc = " , etc";
$lang->project->product = 'Product';
$lang->project->branch = 'Branch';
$lang->project->plan = 'Plan';
$lang->project->kanban = 'Kanban';
$lang->project->createKanban = 'Create Kanban';
/* Project Kanban. */
$lang->project->typeList = array();
@@ -220,6 +222,7 @@ $lang->project->currencySymbol['SGD'] = 'S$';
$lang->project->modelList['scrum'] = "Scrum";
$lang->project->modelList['waterfall'] = "CMMI";
$lang->project->modelList['kanban'] = "Kanban";
$lang->project->featureBar['all'] = 'All';
$lang->project->featureBar['doing'] = 'Going';
@@ -252,6 +255,10 @@ $lang->project->endList[186] = 'Half yearly';
$lang->project->endList[365] = 'One year';
$lang->project->endList[999] = 'Longtime';
$lang->project->scrumTitle = 'Agile Development Management';
$lang->project->waterfallTitle = 'Waterfall Project Management';
$lang->project->kanbanTitle = 'Kanban Project Management';
$lang->project->empty = 'No project.';
$lang->project->nextStep = 'Next step';
$lang->project->hoursUnit = '%s hours';
@@ -260,7 +267,6 @@ $lang->project->lastIteration = "Recent {$lang->executionCommon}";
$lang->project->ongoingStage = 'Ongoing stage';
$lang->project->scrum = 'Scrum';
$lang->project->waterfall = 'Waterfall';
$lang->project->waterfallTitle = 'Waterfall Project Management';
$lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.';
$lang->project->confirmDelete = "Do you want to delete [%s]?";
$lang->project->emptyPM = 'No manager';
@@ -278,7 +284,6 @@ $lang->project->programTitle['end'] = 'End-level project only';
$lang->project->accessDenied = 'Access denied to this project';
$lang->project->chooseProgramType = 'Select management type';
$lang->project->scrumTitle = 'Agile Development Management';
$lang->project->cannotCreateChild = 'The project has contents, so you cannot add a child project. You can create a parent project for this one and then add a child project for the parent project.';
$lang->project->hasChildren = 'This project has a child project, so it cannot be deleted.';
$lang->project->confirmDelete = 'Do you want to delete this project?';
+7 -2
View File
@@ -155,6 +155,8 @@ $lang->project->etc = "等";
$lang->project->product = '所属产品';
$lang->project->branch = '所属分支';
$lang->project->plan = '所属计划';
$lang->project->createKanban = '添加看板';
$lang->project->kanban = '看板';
/* Project Kanban. */
$lang->project->typeList = array();
@@ -220,6 +222,7 @@ $lang->project->currencySymbol['SGD'] = 'S$';
$lang->project->modelList['scrum'] = "Scrum";
$lang->project->modelList['waterfall'] = "瀑布";
$lang->project->modelList['kanban'] = "看板";
$lang->project->featureBar['all'] = '所有';
$lang->project->featureBar['doing'] = '进行中';
@@ -252,6 +255,10 @@ $lang->project->endList[186] = '半年';
$lang->project->endList[365] = '一年';
$lang->project->endList[999] = '长期';
$lang->project->scrumTitle = '敏捷开发全流程项目管理';
$lang->project->waterfallTitle = '瀑布式项目管理';
$lang->project->kanbanTitle = '专业研发看板项目管理';
$lang->project->empty = '暂时没有项目';
$lang->project->nextStep = '下一步';
$lang->project->hoursUnit = '%s 工时';
@@ -260,7 +267,6 @@ $lang->project->lastIteration = "近期{$lang->executionCommon}";
$lang->project->ongoingStage = '进行中的阶段';
$lang->project->scrum = 'Scrum';
$lang->project->waterfall = '瀑布';
$lang->project->waterfallTitle = '瀑布式项目管理';
$lang->project->cannotCreateChild = '该项目已经有实际的内容,无法直接添加子项目。您可以为当前项目创建一个父项目,然后在新的父项目下面添加子项目。';
$lang->project->confirmDelete = "您确定要删除吗?";
$lang->project->emptyPM = '暂无';
@@ -278,7 +284,6 @@ $lang->project->programTitle['end'] = '只显示最后一级项目集';
$lang->project->accessDenied = '您无权访问该项目!';
$lang->project->chooseProgramType = '选择项目管理方式';
$lang->project->scrumTitle = '敏捷开发全流程项目管理';
$lang->project->cannotCreateChild = '该项目已经有实际的内容,无法直接添加子项目。您可以为当前项目创建一个父项目,然后在新的父项目下面添加子项目。';
$lang->project->hasChildren = '该项目有子项目存在,不能删除。';
$lang->project->confirmDelete = "您确定删除项目[%s]吗?";
+17 -16
View File
@@ -37,7 +37,7 @@ class projectModel extends model
echo(js::alert($this->lang->project->accessDenied));
$this->session->set('project', '');
die(js::locate(helper::createLink('project', 'index')));
return print(js::locate(helper::createLink('project', 'index')));
}
/**
@@ -189,13 +189,12 @@ class projectModel extends model
* Get project info.
*
* @param string $status
* @param int $itemCounts
* @param string $orderBy
* @param int $pager
* @access public
* @return array
*/
public function getInfoList($status = 'undone', $itemCounts = 30, $orderBy = 'order_desc', $pager = null)
public function getInfoList($status = 'undone', $orderBy = 'order_desc', $pager = null)
{
/* Init vars. */
$projects = $this->loadModel('program')->getProjectList(0, $status, 0, $orderBy, $pager, 0, 1);
@@ -632,7 +631,7 @@ class projectModel extends model
/**
* Get project pairs by model and project.
*
* @param string $model all|scrum|waterfall
* @param string $model all|scrum|waterfall|kanban
* @param int $programID
* @access public
* @return array
@@ -1569,7 +1568,8 @@ class projectModel extends model
$prefix = '';
$suffix = '';
if($project->model === 'waterfall') $prefix = "<span class='project-type-label label label-outline label-warning'>{$this->lang->project->waterfall}</span> ";
if($project->model === 'scrum') $prefix = "<span class='project-type-label label label-outline label-info'>{$this->lang->project->scrum}</span> ";
if($project->model === 'scrum') $prefix = "<span class='project-type-label label label-outline label-info'>{$this->lang->project->scrum}</span> ";
if($project->model === 'kanban') $prefix = "<span class='project-type-label label label-outline label-info'>{$this->lang->project->kanban}</span> ";
if(isset($project->delay)) $suffix = "<span class='label label-danger label-badge'>{$this->lang->project->statusList['delay']}</span>";
if(!empty($suffix) || !empty($prefix)) echo '<div class="project-name' . (empty($prefix) ? '' : ' has-prefix') . (empty($suffix) ? '' : ' has-suffix') . '">';
if(!empty($prefix)) echo $prefix;
@@ -1633,20 +1633,21 @@ class projectModel extends model
}
$from = $project->from == 'project' ? 'project' : 'pgmproject';
$iframe = $this->app->tab == 'program' ? 'iframe' : '';
$onlyBody = $this->app->tab == 'program' ? true : '';
$iframe = ($this->app->tab == 'program' or $project->model == 'kanban') ? 'iframe' : '';
$onlyBody = ($this->app->tab == 'program' or $project->model == 'kanban') ? true : '';
$dataApp = $this->config->systemMode == 'classic' ? "data-app=execution" : "data-app=project";
common::printIcon($moduleName, 'edit', "projectID=$project->id", $project, 'list', 'edit', '', $iframe, $onlyBody, $dataApp, '', $project->id);
common::printIcon($moduleName, 'manageMembers', "projectID=$project->id", $project, 'list', 'group', '', '', '', $dataApp, $this->lang->execution->team, $project->id);
if($this->config->systemMode == 'new') common::printIcon('project', 'group', "projectID=$project->id&programID=$programID", $project, 'list', 'lock', '', '', '', $dataApp, '', $project->id);
$attr = $project->model == 'kanban' ? " disabled='disabled'" : '';
common::printIcon($moduleName, 'edit', "projectID=$project->id", $project, 'list', 'edit', '', $iframe, $onlyBody, $dataApp);
common::printIcon($moduleName, 'manageMembers', "projectID=$project->id", $project, 'list', 'group', '', '', '', $dataApp . $attr, $this->lang->execution->team);
if($this->config->systemMode == 'new') common::printIcon('project', 'group', "projectID=$project->id&programID=$programID", $project, 'list', 'lock', '', '', '', $dataApp . $attr);
if(common::hasPriv($moduleName, 'manageProducts') || common::hasPriv($moduleName, 'whitelist') || common::hasPriv($moduleName, 'delete'))
{
echo "<div class='btn-group'>";
echo "<button type='button' class='btn dropdown-toggle' data-toggle='context-dropdown' title='{$this->lang->more}'><i class='icon-more-alt'></i></button>";
echo "<ul class='dropdown-menu pull-right text-center' role='menu'>";
common::printIcon($moduleName, 'manageProducts', "projectID=$project->id", $project, 'list', 'link', '', 'btn-action', '', $dataApp, $this->lang->project->manageProducts, $project->id);
if($this->config->systemMode == 'new') common::printIcon('project', 'whitelist', "projectID=$project->id&module=project&from=$from", $project, 'list', 'shield-check', '', 'btn-action', '', $dataApp, '', $project->id);
common::printIcon($moduleName, 'manageProducts', "projectID=$project->id", $project, 'list', 'link', '', 'btn-action', '', $dataApp . $attr, $this->lang->project->manageProducts);
if($this->config->systemMode == 'new') common::printIcon('project', 'whitelist', "projectID=$project->id&module=project&from=$from", $project, 'list', 'shield-check', '', 'btn-action', '', $dataApp . $attr);
if(common::hasPriv($moduleName, 'delete')) echo html::a(helper::createLink($moduleName, "delete", "projectID=$project->id"), "<i class='icon-trash'></i>", 'hiddenwin', "class='btn btn-action' title='{$this->lang->project->delete}'");
echo "</ul>";
echo "</div>";
@@ -1776,7 +1777,7 @@ class projectModel extends model
$type = 'project';
if($this->config->systemMode == 'new')
{
if($project->type == 'sprint' or $project->type == 'stage') $type = 'execution';
if($project->type == 'sprint' or $project->type == 'stage' or $project->type == 'kanban') $type = 'execution';
}
$members = $this->dao->select("t1.account, if(t2.deleted='0', t2.realname, t1.account) as realname")->from(TABLE_TEAM)->alias('t1')
@@ -1834,9 +1835,9 @@ class projectModel extends model
->fetchPairs();
}
$executions = $this->dao->select('t1.*,t2.name projectName')->from(TABLE_EXECUTION)->alias('t1')
$executions = $this->dao->select('t1.*,t2.name projectName, t2.model as projectModel')->from(TABLE_EXECUTION)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->where('t1.type')->in('sprint,stage')
->where('t1.type')->in('sprint,stage,kanban')
->beginIF($projectID != 0)->andWhere('t1.project')->eq($projectID)->fi()
->beginIF(!empty($myExecutionIDList))->andWhere('t1.id')->in(array_keys($myExecutionIDList))->fi()
->beginIF($status == 'undone')->andWhere('t1.status')->notIN('done,closed')->fi()
@@ -1849,7 +1850,7 @@ class projectModel extends model
}
else
{
$executions = $this->dao->select('t2.*,t3.name projectName')->from(TABLE_PROJECTPRODUCT)->alias('t1')
$executions = $this->dao->select('t2.*,t3.name projectName, t3.model as projectModel')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.project=t2.id')
->leftJoin(TABLE_PROJECT)->alias('t3')->on('t2.project=t3.id')
->where('t1.product')->eq($productID)
+6 -3
View File
@@ -72,9 +72,12 @@ foreach($projects as $programID => $programProjects)
foreach($programProjects as $index => $project)
{
$selected = $project->id == $projectID ? 'selected' : '';
$link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id);
$projectName = $project->model == 'scrum' ? '<i class="icon icon-sprint"></i> ' . $project->name : '<i class="icon icon-waterfall"></i> ' . $project->name;
$selected = $project->id == $projectID ? 'selected' : '';
$link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id);
$icon = '<i class="icon icon-sprint"></i> ';
if($project->model != 'scrum') $icon = "<i class='icon icon-{$project->model}'></i> ";
$projectName = $icon . $project->name;
if($project->status != 'done' and $project->status != 'closed' and $project->PM == $this->app->user->account)
{
+8 -6
View File
@@ -84,10 +84,8 @@
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->project->empty;?></span>
<?php if(isset($this->config->maxVersion) and !defined('TUTORIAL')):?>
<?php if(!defined('TUTORIAL')):?>
<?php common::printLink('project', 'createGuide', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info" data-toggle="modal" data-target="#guideDialog"');?>
<?php elseif($this->config->systemMode == 'new'):?>
<?php common::printLink('project', 'create', 'mode=scrum', '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info"');?>
<?php else:?>
<?php common::printLink('execution', 'create', '', '<i class="icon icon-plus"></i> ' . $lang->execution->create, '', 'class="btn btn-info"');?>
<?php endif;?>
@@ -102,9 +100,11 @@
<span class="label label-<?php echo $status;?>"><?php echo $lang->project->statusList[$status];?></span>
</div>
<div class='panel-heading'>
<?php if(isset($config->maxVersion) and $project->model === 'waterfall'):?>
<?php if($project->model === 'waterfall'):?>
<span class='project-type-label label label-warning label-outline'><i class='icon icon-waterfall'></i></span>
<?php elseif(isset($config->maxVersion)):?>
<?php elseif($project->model === 'kanban'):?>
<span class='project-type-label label label-info label-outline'><i class='icon icon-kanban'></i></span>
<?php else:?>
<span class='project-type-label label label-info label-outline'><i class='icon icon-sprint'></i></span>
<?php endif;?>
<strong class='project-name' title='<?php echo $project->name;?>'><?php echo html::a(helper::createLink('project', 'index', "projectID=$projectID"), $project->name);?></strong>
@@ -174,7 +174,9 @@
<?php echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");?>
<ul class='dropdown-menu pull-right'>
<?php
common::printIcon('project', 'edit', "projectID=$project->id", $project, 'list', 'edit', '', 'btn-action');
$iframe = $project->model == 'kanban' ? ' iframe' : '';
$onlyBody = $project->model == 'kanban' ? true : '';
common::printIcon('project', 'edit', "projectID=$project->id", $project, 'list', 'edit', '', 'btn-action' . $iframe, $onlyBody);
common::printIcon('project', 'start', "projectID=$project->id", $project, 'list', 'play', '', 'iframe btn-action', true);
common::printIcon('project', 'suspend', "projectID=$project->id", $project, 'list', 'pause', '', 'iframe btn-action', true);
common::printIcon('project', 'close', "projectID=$project->id", $project, 'list', 'off', '', 'iframe btn-action', true);
@@ -73,8 +73,6 @@
<span class="text-muted"><?php echo $lang->project->empty;?></span>
<?php if(!defined('TUTORIAL')):?>
<?php if(common::hasPriv('project', 'create')) common::printLink('project', 'createGuide', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info" data-toggle="modal"');?>
<?php elseif($this->config->systemMode == 'new'):?>
<?php common::printLink('project', 'create', "mode=scrum&programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-info"');?>
<?php else:?>
<?php common::printLink('execution', 'create', '', '<i class="icon icon-plus"></i> ' . $lang->execution->create, '', 'class="btn btn-info"');?>
<?php endif;?>
+9 -2
View File
@@ -15,20 +15,27 @@
<button class="close" data-dismiss="modal">x</button>
<h2 class='text-center'><?php echo $lang->project->chooseProgramType; ?></h2>
<div class='row'>
<div class='col-xs-6'>
<div class='col-xs-4'>
<div class='project-type text-center'>
<?php echo html::a($this->createLink("project", "create", "model=scrum&programID=$programID&copyProjectID=0&extra=productID=$productID,branchID=$branchID"), "<img class='project-type-img' data-type='scrum' src='{$config->webRoot}theme/default/images/main/scrum.png'>", '', "data-app='project' class='createButton'")?>
<h3><?php echo $lang->project->scrum; ?></h3>
<p><?php echo $lang->project->scrumTitle; ?></p>
</div>
</div>
<div class='col-xs-6'>
<div class='col-xs-4'>
<div class='project-type text-center'>
<?php echo html::a($this->createLink("project", "create", "model=waterfall&programID=$programID&copyProjectID=0&extra=productID=$productID,branchID=$branchID"), "<img class='project-type-img' data-type='waterfall' src='{$config->webRoot}theme/default/images/main/waterfall.png'>", '', "data-app='project' class='createButton'")?>
<h3><?php echo $lang->project->waterfall; ?></h3>
<p><?php echo $lang->project->waterfallTitle; ?></p>
</div>
</div>
<div class='col-xs-4'>
<div class='project-type text-center'>
<?php echo html::a($this->createLink("project", "create", "model=kanban&programID=$programID&copyProjectID=0&extra=productID=$productID,branchID=$branchID"), "<img class='project-type-img' data-type='kanban' src='{$config->webRoot}theme/default/images/main/kanban.png'>", '', "data-app='project' class='createButton'")?>
<h3><?php echo $lang->project->kanban;?></h3>
<p><?php echo $lang->project->kanbanTitle;?></p>
</div>
</div>
</div>
</div>
</div>
+2
View File
@@ -34,10 +34,12 @@
</div>
<form class='form-indicator main-form form-ajax' method='post' target='hiddenwin' id='dataform'>
<table class='table table-form'>
<?php if($project->model != 'kanban'):?>
<tr>
<th><?php echo $lang->project->model;?></th>
<td><?php echo html::select('model', $lang->project->modelList, $model, "class='form-control chosen' required $disableModel");?></td>
</tr>
<?php endif;?>
<tr>
<th class='w-120px'><?php echo $lang->program->parent;?></th>
<?php
+111
View File
@@ -10,5 +10,116 @@
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php if($project->model != 'kanban'):?>
<?php echo $this->fetch('block', 'dashboard', "module=project&type={$project->model}&projectID={$project->id}");?>
<?php else:?>
<div class='clearfix' id='mainMenu'>
<div class='btn-toolbar pull-left'>
<?php
foreach($lang->project->featureBar as $label => $labelName)
{
$active = $browseType == $label ? 'btn-active-text' : '';
echo html::a($this->createLink('project', 'index', "projectID=$project->id&browseType=" . $label), '<span class="text">' . $labelName . '</span> ' . ($browseType == $label ? "<span class='label label-light label-badge'>" . (int)count($kanbanList) . '</span>' : ''), '', "class='btn btn-link $active'");
}
?>
</div>
<div class='btn-toolbar pull-right'>
<?php common::printLink('execution', 'create', "projectID=$project->id", '<i class="icon icon-plus"></i> ' . $lang->project->createKanban, '', 'class="btn btn-primary"');?>
</div>
</div>
<div id="mainContent">
<div class="row cell" id='cards'>
<?php if(empty($kanbanList)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->noData;?></span>
<?php common::printLink('execution', 'create', "projectID=$project->id", '<i class="icon icon-plus"></i> ' . $lang->project->createKanban, '', 'class="btn btn-info"');?>
</p>
</div>
<?php else:?>
<?php $kanbanCount = 0;?>
<?php foreach ($kanbanList as $kanbanID => $kanban):?>
<div class='col' data-id='<?php echo $kanbanID?>'>
<div class='panel' data-url='<?php echo $this->createLink('execution', 'kanban', "kanbanID=$kanbanID");?>'>
<div class='panel-heading'>
<div class='kanban-name'>
<?php if(!helper::isZeroDate($kanban->end) && $kanban->end < helper::today()):?>
<span class="label label-doing"><?php echo $lang->project->statusList['delay'];?></span>
<?php else:?>
<span class="label label-<?php echo $kanban->status;?>"><?php echo zget($lang->execution->statusList, $kanban->status);?></span>
<?php endif;?>
<strong title='<?php echo $kanban->name;?>'><?php echo $kanban->name;?></strong>
</div>
<?php
$canActions = (common::hasPriv('execution','edit') or (!empty($executionActions) and isset($executionActions[$kanbanID])));
$kanbanCount ++;
?>
<?php if($canActions):?>
<div class='kanban-actions kanban-actions<?php echo $kanbanID;?>'>
<div class='dropdown'>
<?php echo html::a('javascript:;', "<i class='icon icon-ellipsis-v'></i>", '', "data-toggle='dropdown' class='btn btn-link'");?>
<ul class='dropdown-menu <?php echo $kanbanCount % 4 == 0 ? 'pull-left' : 'pull-right';?>'>
<?php
if(common::hasPriv('execution','edit'))
{
$this->app->loadLang('kanban');
echo '<li>';
common::printLink('execution', 'edit', "executionID={$kanbanID}", '<i class="icon icon-edit"></i> ' . $lang->kanban->edit, '', "class='iframe' data-width='75%'", '', true);
echo '</li>';
}
if(!empty($executionActions[$kanbanID]))
{
if(in_array('start', $executionActions[$kanbanID])) echo '<li>' . html::a(helper::createLink('execution', 'start', "executionID=$kanbanID", '', true), '<i class="icon icon-play"></i>' . $lang->execution->start, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('putoff', $executionActions[$kanbanID])) echo '<li>' . html::a(helper::createLink('execution', 'putoff', "executionID=$kanbanID", '', true), '<i class="icon icon-calendar"></i>' . $lang->execution->putoff, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('suspend', $executionActions[$kanbanID])) echo '<li>' . html::a(helper::createLink('execution', 'suspend', "executionID=$kanbanID", '', true), '<i class="icon icon-pause"></i>' . $lang->execution->suspend, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('close', $executionActions[$kanbanID])) echo '<li>' . html::a(helper::createLink('execution', 'close', "executionID=$kanbanID", '', true), '<i class="icon icon-off"></i>' . $lang->execution->close, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('activate', $executionActions[$kanbanID])) echo '<li>' . html::a(helper::createLink('execution', 'activate', "executionID=$kanbanID", '', true), '<i class="icon icon-magic"></i>' . $lang->execution->activate, '', "class='iframe btn btn-link text-left' data-width='75%'") . '</li>';
if(in_array('delete', $executionActions[$kanbanID])) echo '<li>' . html::a(helper::createLink('execution', 'delete', "executionID=$kanbanID&confirm=no&kanban=yes", '', true), '<i class="icon icon-trash"></i>' . $lang->kanban->delete, '', "target='hiddenwin'") . '</li>';
}?>
</ul>
</div>
</div>
<?php endif;?>
</div>
<div class='panel-body'>
<div class='kanban-desc' title="<?php echo strip_tags(htmlspecialchars_decode($kanban->desc));?>"><?php echo strip_tags(htmlspecialchars_decode($kanban->desc));?></div>
<div class='kanban-footer'>
<div class="clearfix">
<?php $members = zget($memberGroup, $kanbanID, array());?>
<?php if(!empty($members)):?>
<div class='kanban-members pull-left'>
<?php $count = 0;?>
<?php foreach($members as $member):?>
<?php if($count > 1) break;?>
<?php $count ++;?>
<div title="<?php echo $member->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$member->account], 'account' => $member->account)); ?>
</div>
<?php endforeach;?>
<?php if(count($members) > 3):?>
<?php echo '<span>…</span>';?>
<?php $lastMember = end($members);?>
<div title="<?php echo $lastMember->realname;?>">
<?php echo html::smallAvatar(array('avatar' => $usersAvatar[$lastMember->account], 'account' => $lastMember->account)); ?>
</div>
<?php endif;?>
</div>
<div class='kanban-members-total pull-left'><?php echo sprintf($lang->project->teamSumCount, count($members));?></div>
<?php endif;?>
<div class='kanbanAcl'>
<?php $icon = 'inherit-space';?>
<?php if($kanban->acl == 'private') $icon = 'lock';?>
<i class="<?php echo 'icon-' . $icon;?>"></i>
<?php echo zget($lang->execution->kanbanAclList, $kanban->acl, '');?>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endforeach;?>
<?php endif;?>
</div>
</div>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -49,7 +49,7 @@ class projectstoryModel extends model
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id')
->leftJoin(TABLE_EXECUTION)->alias('t3')->on('t1.project=t3.id')
->where('t1.story')->in($storyIdList)
->andWhere('t3.type')->in('sprint,stage')
->andWhere('t3.type')->in('sprint,stage,kanban')
->andWhere('t3.project')->eq($projectID)
->andWhere('t3.deleted')->eq(0)
->fetchAll('id');
+1 -1
View File
@@ -1060,7 +1060,7 @@ class reportModel extends model
{
$projectStatus = $this->dao->select('t1.id,t1.status')->from(TABLE_PROJECT)->alias('t1')
->leftJoin(TABLE_TEAM)->alias('t2')->on("t1.id=t2.root")
->where('t1.type')->in($this->config->systemMode == 'classic' ? 'sprint,stage' : 'project')
->where('t1.type')->in($this->config->systemMode == 'classic' ? 'sprint,stage,kanban' : 'project')
->beginIF($this->config->systemMode == 'classic')->andWhere('t2.type')->eq('execution')->fi()
->beginIF($this->config->systemMode == 'new')->andWhere('t2.type')->eq('project')->fi()
->beginIF(!empty($accounts))->andWhere('t2.account')->in($accounts)->fi()
+1
View File
@@ -98,5 +98,6 @@ $lang->search->objectTypeList['story'] = $lang->SRCommon;
$lang->search->objectTypeList['requirement'] = $lang->URCommon;
$lang->search->objectTypeList['stage'] = 'stage';
$lang->search->objectTypeList['sprint'] = $lang->executionCommon;
$lang->search->objectTypeList['kanban'] = 'kanban';
$lang->search->objectTypeList['commonIssue'] = 'Issue';
$lang->search->objectTypeList['stakeholderIssue'] = 'Stakeholder Issue';
+1
View File
@@ -98,5 +98,6 @@ $lang->search->objectTypeList['story'] = $lang->SRCommon;
$lang->search->objectTypeList['requirement'] = $lang->URCommon;
$lang->search->objectTypeList['stage'] = '阶段';
$lang->search->objectTypeList['sprint'] = $lang->executionCommon;
$lang->search->objectTypeList['kanban'] = '看板';
$lang->search->objectTypeList['commonIssue'] = '问题';
$lang->search->objectTypeList['stakeholderIssue'] = '干系人问题';
+8 -1
View File
@@ -609,9 +609,16 @@ class searchModel extends model
$record->url = helper::createLink($module, $method, "id={$record->objectID}", '', false, $issue->project);
$record->extraType = empty($issue->owner) ? 'commonIssue' : 'stakeholderIssue';
}
elseif($module == 'project')
{
$projectModel = $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($record->objectID)->fetch('model');
$method = $projectModel == 'kanban' ? 'index' : 'view';
$record->url = helper::createLink('project', $method, "id={$record->objectID}");
}
elseif($module == 'execution')
{
$execution = $this->dao->select('id,type,project')->from(TABLE_EXECUTION)->where('id')->eq($record->objectID)->fetch();
$method = $execution->type == 'kanban' ? 'kanban' : $method;
$record->url = helper::createLink('execution', $method, "id={$record->objectID}");
$record->extraType = empty($execution->type) ? '' : $execution->type;
}
@@ -1015,7 +1022,7 @@ class searchModel extends model
->where('1')
->beginIF($type == 'program')->andWhere('type')->eq('program')->fi()
->beginIF($type == 'project')->andWhere('type')->eq('project')->fi()
->beginIF($type == 'execution')->andWhere('type')->in('stage,sprint')->fi()
->beginIF($type == 'execution')->andWhere('type')->in('stage,sprint,kanban')->fi()
->beginIF(isset($data->deleted))->andWhere('t1.deleted')->eq(0)->fi();
}
return $query;
+17 -3
View File
@@ -95,7 +95,7 @@ class story extends control
$response['message'] = $this->lang->saveSuccess;
setcookie('lastStoryModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
$storyResult = $this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '');
$storyResult = $this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extra);
if(!$storyResult or dao::isError())
{
$response['result'] = 'fail';
@@ -105,6 +105,19 @@ class story extends control
$storyID = $storyResult['id'];
$productID = $this->post->product ? $this->post->product : $productID;
$execution = $this->dao->findById((int)$objectID)->from(TABLE_EXECUTION)->fetch();
if(!empty($execution) and $execution->type == 'kanban')
{
if(isset($output['laneID']) and isset($output['columnID']))
{
$this->loadModel('kanban')->addKanbanCell($objectID, $output['laneID'], $output['columnID'], 'story', $storyID);
}
else
{
$this->loadModel('kanban')->updateLane($objectID, 'story');
}
}
if($storyResult['status'] == 'exists')
{
$response['message'] = sprintf($this->lang->duplicate, $this->lang->story->common);
@@ -355,10 +368,11 @@ class story extends control
* @param int $executionID
* @param int $plan
* @param string $type requirement|story
* @param string $extra for example feedbackID=0
* @access public
* @return void
*/
public function batchCreate($productID = 0, $branch = 0, $moduleID = 0, $storyID = 0, $executionID = 0, $plan = 0, $type = 'story')
public function batchCreate($productID = 0, $branch = 0, $moduleID = 0, $storyID = 0, $executionID = 0, $plan = 0, $type = 'story', $extra = '')
{
$this->lang->product->switcherMenu = $this->product->getSwitcher($productID);
@@ -412,7 +426,7 @@ class story extends control
{
$products = array();
foreach($mails as $story) $products[$story->storyID] = $productID;
$this->execution->linkStory($executionID, $stories, $products);
$this->execution->linkStory($executionID, $stories, $products, $extra);
if($executionID != $this->session->project) $this->execution->linkStory($this->session->project, $stories, $products);
}
-1
View File
@@ -86,7 +86,6 @@ $lang->story->batchToTaskTips = "This action will create a task with the same na
$lang->story->successToTask = "Converted to task.";
$lang->story->storyRound = '%s time estimation';
$lang->story->common = 'Story';
$lang->story->id = 'ID';
$lang->story->parent = 'Parent';
$lang->story->product = $lang->productCommon;
-1
View File
@@ -86,7 +86,6 @@ $lang->story->batchToTaskTips = "此操作会创建与所选{$lang->SRCommon}同
$lang->story->successToTask = '批量转任务成功';
$lang->story->storyRound = '第 %s 轮估算';
$lang->story->common = $lang->SRCommon;
$lang->story->id = '编号';
$lang->story->parent = '父需求';
$lang->story->product = "所属{$lang->productCommon}";
+27 -5
View File
@@ -44,7 +44,7 @@ class storyModel extends model
$story->executions = $this->dao->select('t1.project, t2.name, t2.status')->from(TABLE_PROJECTSTORY)->alias('t1')
->leftJoin(TABLE_EXECUTION)->alias('t2')->on('t1.project = t2.id')
->where('t1.story')->eq($storyID)
->andWhere('t2.type')->in('sprint,stage')
->andWhere('t2.type')->in('sprint,stage,kanban')
->orderBy('t1.`order` DESC')
->fetchAll('project');
$story->tasks = $this->dao->select('id, name, assignedTo, execution, status, consumed, `left`,type')->from(TABLE_TASK)->where('story')->eq($storyID)->andWhere('deleted')->eq(0)->orderBy('id DESC')->fetchGroup('execution');
@@ -183,11 +183,15 @@ class storyModel extends model
* @param int $executionID
* @param int $bugID
* @param string $from
* @param string $extra
* @access public
* @return int|bool the id of the created story or false when error.
*/
public function create($executionID = 0, $bugID = 0, $from = '')
public function create($executionID = 0, $bugID = 0, $from = '', $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
if(isset($_POST['reviewer'])) $_POST['reviewer'] = array_filter($_POST['reviewer']);
if(!$this->post->needNotReview and empty($_POST['reviewer']))
{
@@ -280,7 +284,10 @@ class storyModel extends model
{
$this->linkStory($executionID, $this->post->product, $storyID);
if($this->config->systemMode == 'new' and $executionID != $this->session->project) $this->linkStory($this->session->project, $this->post->product, $storyID);
$this->loadModel('kanban')->updateLane($executionID, 'story');
$this->loadModel('kanban');
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($executionID, $output['laneID'], $output['columnID'], 'story', $storyID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($executionID, 'story');
}
if(is_array($this->post->URS))
@@ -437,7 +444,7 @@ class storyModel extends model
foreach($stories->title as $i => $title)
{
$module = $stories->module[$i] == 'ditto' ? $module : $stories->module[$i];
$plan = $stories->plan[$i] == 'ditto' ? $plan : $stories->plan[$i];
$plan = isset($stories->plan[$i]) ? ($stories->plan[$i] == 'ditto' ? $plan : $stories->plan[$i]) : 0;
$pri = $stories->pri[$i] == 'ditto' ? $pri : $stories->pri[$i];
$source = $stories->source[$i] == 'ditto' ? $source : $stories->source[$i];
$stories->module[$i] = (int)$module;
@@ -1976,6 +1983,8 @@ class storyModel extends model
*/
public function setStage($storyID)
{
$this->loadModel('kanban');
$storyID = (int)$storyID;
$account = $this->app->user->account;
@@ -2007,7 +2016,11 @@ class storyModel extends model
{
$this->dao->update(TABLE_STORY)->set('stage')->eq('closed')->where('id')->eq($storyID)->exec();
foreach($stages as $branch => $stage) $this->dao->replace(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq('closed')->exec();
foreach($executions as $execution => $branch) $this->dao->replace(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq('closed')->exec();
foreach($executions as $execution => $branch)
{
$this->dao->replace(TABLE_STORYSTAGE)->set('story')->eq($storyID)->set('branch')->eq($branch)->set('stage')->eq('closed')->exec();
$this->kanban->updateLane($execution, 'story', $storyID);
}
return false;
}
@@ -2160,6 +2173,15 @@ class storyModel extends model
$this->dao->update(TABLE_STORY)->set('stage')->eq(current($stages))->where('id')->eq($storyID)->exec();
}
$currentStory = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch();
if($story->stage != $currentStory->stage)
{
foreach($executions as $executionID => $branch)
{
$this->kanban->updateLane($executionID, 'story', $storyID);
}
}
return;
}
+31 -19
View File
@@ -129,6 +129,11 @@ class task extends control
$this->action->create('task', $taskID, 'Opened', '');
}
$this->loadModel('kanban');
$kanbanID = $execution->type == 'kanban' ? $executionID : $_POST['execution'];
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($kanbanID, $output['laneID'], $output['columnID'], 'task', $taskID);
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($kanbanID, 'task');
if($todoID > 0)
{
$this->dao->update(TABLE_TODO)->set('status')->eq('done')->where('id')->eq($todoID)->exec();
@@ -246,15 +251,16 @@ class task extends control
/**
* Batch create task.
*
* @param int $executionID
* @param int $storyID
* @param int $iframe
* @param int $taskID
* @param int $executionID
* @param int $storyID
* @param int $iframe
* @param int $taskID
* @param string $extra
*
* @access public
* @return void
*/
public function batchCreate($executionID = 0, $storyID = 0, $moduleID = 0, $taskID = 0, $iframe = 0)
public function batchCreate($executionID = 0, $storyID = 0, $moduleID = 0, $taskID = 0, $iframe = 0, $extra = '')
{
$this->execution->getLimitedExecution();
$limitedExecutions = !empty($_SESSION['limitedExecutions']) ? $_SESSION['limitedExecutions'] : '';
@@ -293,7 +299,7 @@ class task extends control
if(!empty($_POST))
{
$mails = $this->task->batchCreate($executionID);
$mails = $this->task->batchCreate($executionID, $extra);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$taskIDList = array();
@@ -795,10 +801,11 @@ class task extends control
* Start a task.
*
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function start($taskID)
public function start($taskID, $extra = '')
{
$this->commonAction($taskID);
@@ -807,7 +814,7 @@ class task extends control
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->start($taskID);
$changes = $this->task->start($taskID, $extra);
if(dao::isError())
{
@@ -961,17 +968,18 @@ class task extends control
* Finish a task.
*
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function finish($taskID)
public function finish($taskID, $extra = '')
{
$this->commonAction($taskID);
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->finish($taskID);
$changes = $this->task->finish($taskID, $extra);
if(dao::isError())
{
if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
@@ -1047,17 +1055,18 @@ class task extends control
* Pause task.
*
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function pause($taskID)
public function pause($taskID, $extra = '')
{
$this->commonAction($taskID);
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->pause($taskID);
$changes = $this->task->pause($taskID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
if($this->post->comment != '' or !empty($changes))
@@ -1123,18 +1132,19 @@ class task extends control
/**
* Close a task.
*
* @param int $taskID
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function close($taskID)
public function close($taskID, $extra = '')
{
$this->commonAction($taskID);
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->close($taskID);
$changes = $this->task->close($taskID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
@@ -1255,17 +1265,18 @@ class task extends control
* Cancel a task.
*
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function cancel($taskID)
public function cancel($taskID, $extra = '')
{
$this->commonAction($taskID);
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->cancel($taskID);
$changes = $this->task->cancel($taskID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
if($this->post->comment != '' or !empty($changes))
@@ -1291,17 +1302,18 @@ class task extends control
* Activate a task.
*
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function activate($taskID)
public function activate($taskID, $extra = '')
{
$this->commonAction($taskID);
if(!empty($_POST))
{
$this->loadModel('action');
$changes = $this->task->activate($taskID);
$changes = $this->task->activate($taskID, $extra);
if(dao::isError()) die(js::error(dao::getError()));
if($this->post->comment != '' or !empty($changes))
+70 -27
View File
@@ -137,8 +137,6 @@ class taskModel extends model
$this->dao->insert(TABLE_TASKSPEC)->data($taskSpec)->autoCheck()->exec();
if(dao::isError()) return false;
$this->loadModel('kanban')->updateLane($executionID, 'task');
if($this->post->story) $this->loadModel('story')->setStage($this->post->story);
if($this->post->selectTestStory)
{
@@ -233,13 +231,15 @@ class taskModel extends model
* Create a batch task.
*
* @param int $executionID
* @param string $extra
* @access public
* @return void
*/
public function batchCreate($executionID)
public function batchCreate($executionID, $extra = '')
{
/* Load module and init vars. */
$this->loadModel('action');
$this->loadModel('kanban');
$now = helper::now();
$mails = array();
$storyIDs = array();
@@ -247,6 +247,9 @@ class taskModel extends model
$preStory = 0;
$tasks = fixer::input('post')->get();
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
/* Judge whether the current task is a parent. */
$parentID = !empty($this->post->parent[0]) ? $this->post->parent[0] : 0;
@@ -401,6 +404,8 @@ class taskModel extends model
$this->executeHooks($taskID);
if(isset($output['laneID']) and isset($output['columnID'])) $this->kanban->addKanbanCell($executionID, $output['laneID'], $output['columnID'], 'task', $taskID);
$actionID = $this->action->create('task', $taskID, 'Opened', '');
if(!dao::isError()) $this->loadModel('score')->create('task', 'create', $taskID);
@@ -409,11 +414,7 @@ class taskModel extends model
$mails[$i]->actionID = $actionID;
}
if(!dao::isError())
{
$this->loadModel('score')->create('ajax', 'batchCreate');
$this->loadModel('kanban')->updateLane($executionID, 'task');
}
if(!dao::isError()) $this->loadModel('score')->create('ajax', 'batchCreate');
if($parentID > 0 && !empty($taskID))
{
@@ -456,6 +457,8 @@ class taskModel extends model
$actionID = $this->action->create('task', $parentID, 'createChildren', '', trim($childTasks, ','));
if(!empty($changes)) $this->action->logHistory($actionID, $changes);
}
if(!isset($output['laneID']) or !isset($output['columnID'])) $this->kanban->updateLane($executionID, 'task');
return $mails;
}
@@ -1014,8 +1017,7 @@ class taskModel extends model
if($this->post->story != false) $this->loadModel('story')->setStage($this->post->story);
if($task->status == 'done') $this->loadModel('score')->create('task', 'finish', $taskID);
if($task->status == 'closed') $this->loadModel('score')->create('task', 'close', $taskID);
if($task->status != $oldTask->status) $this->loadModel('kanban')->updateLane($task->execution, 'task');
if($task->status != $oldTask->status) $this->loadModel('kanban')->updateLane($task->execution, 'task', $taskID);
$this->loadModel('action');
$changed = $task->parent != $oldTask->parent;
if($oldTask->parent > 0)
@@ -1293,7 +1295,7 @@ class taskModel extends model
if($task->status == 'done') $this->loadModel('score')->create('task', 'finish', $taskID);
if($task->status == 'closed') $this->loadModel('score')->create('task', 'close', $taskID);
if($task->status != $oldTask->status) $this->loadModel('kanban')->updateLane($oldTask->execution, 'task');
if($task->status != $oldTask->status) $this->loadModel('kanban')->updateLane($oldTask->execution, 'task', $oldTask->id);
$allChanges[$taskID] = common::createChanges($oldTask, $task);
}
else
@@ -1388,12 +1390,16 @@ class taskModel extends model
/**
* Start a task.
*
* @param int $taskID
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function start($taskID)
public function start($taskID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$oldTask = $this->getById($taskID);
if($oldTask->status == 'doing') dao::$errors[] = $this->lang->task->error->alreadyStarted;
if(!empty($oldTask->team))
@@ -1469,7 +1475,10 @@ class taskModel extends model
$this->computeBeginAndEnd($oldTask->parent);
}
if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story);
$this->loadModel('kanban')->updateLane($oldTask->execution, 'task');
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldTask->execution, 'task', $taskID);
if(isset($output['toColID'])) $this->kanban->moveCard($taskID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
if(!dao::isError()) return common::createChanges($oldTask, $task);
}
@@ -1608,12 +1617,16 @@ class taskModel extends model
/**
* Finish a task.
*
* @param int $taskID
* @param int $taskID
* @param string $extra
* @access public
* @return void
*/
public function finish($taskID)
public function finish($taskID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$oldTask = $this->getById($taskID);
$now = helper::now();
$today = helper::today();
@@ -1731,7 +1744,10 @@ class taskModel extends model
if($task->status == 'done' && !dao::isError())
{
$this->loadModel('score')->create('task', 'finish', $taskID);
$this->loadModel('kanban')->updateLane($oldTask->execution, 'task');
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldTask->execution, 'task', $taskID);
if(isset($output['toColID'])) $this->kanban->moveCard($taskID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
}
if(!dao::isError()) return common::createChanges($oldTask, $task);
}
@@ -1740,11 +1756,15 @@ class taskModel extends model
* Pause task
*
* @param int $taskID
* @param string $extra
* @access public
* @return array
*/
public function pause($taskID)
public function pause($taskID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$oldTask = $this->getById($taskID);
$task = fixer::input('post')
@@ -1757,18 +1777,26 @@ class taskModel extends model
$this->dao->update(TABLE_TASK)->data($task)->autoCheck()->where('id')->eq((int)$taskID)->exec();
if($oldTask->parent > 0) $this->updateParentStatus($taskID);
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldTask->execution, 'task', $taskID);
if(isset($output['toColID'])) $this->kanban->moveCard($taskID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
if(!dao::isError()) return common::createChanges($oldTask, $task);
}
/**
* Close a task.
*
* @param int $taskID
* @param int $taskID
* @param string $extra
* @access public
* @return array
*/
public function close($taskID)
public function close($taskID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$oldTask = $this->dao->select('*')->from(TABLE_TASK)->where('id')->eq($taskID)->fetch();
$now = helper::now();
@@ -1791,7 +1819,10 @@ class taskModel extends model
if($oldTask->parent > 0) $this->updateParentStatus($taskID);
if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story);
$this->loadModel('score')->create('task', 'close', $taskID);
$this->loadModel('kanban')->updateLane($oldTask->execution, 'task');
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldTask->execution, 'task', $taskID);
if(isset($output['toColID'])) $this->kanban->moveCard($taskID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
return common::createChanges($oldTask, $task);
}
@@ -1800,13 +1831,17 @@ class taskModel extends model
/**
* Cancel a task.
*
* @param int $taskID
* @param int $taskID
* @param string $extra
*
* @access public
* @return array
*/
public function cancel($taskID)
public function cancel($taskID, $extra = '')
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
$oldTask = $this->getById($taskID);
$now = helper::now();
@@ -1831,7 +1866,9 @@ class taskModel extends model
$this->dao->update(TABLE_TASK)->set('assignedTo=openedBy')->where('parent')->eq((int)$taskID)->exec();
}
if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story);
$this->loadModel('kanban')->updateLane($oldTask->execution, 'task');
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldTask->execution, 'task', $taskID);
if(isset($output['toColID'])) $this->kanban->moveCard($taskID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
if(!dao::isError()) return common::createChanges($oldTask, $task);
}
@@ -1839,13 +1876,17 @@ class taskModel extends model
/**
* Activate a task.
*
* @param int $taskID
* @param int $taskID
* @param string $extra
*
* @access public
* @return array
*/
public function activate($taskID)
public function activate($taskID, $extra)
{
$extra = str_replace(array(',', ' '), array('&', ''), $extra);
parse_str($extra, $output);
if(strpos($this->config->task->activate->requiredFields, 'comment') !== false and !$this->post->comment)
{
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->comment);
@@ -1897,7 +1938,9 @@ class taskModel extends model
$this->computeWorkingHours($taskID);
}
if($oldTask->story) $this->loadModel('story')->setStage($oldTask->story);
$this->loadModel('kanban')->updateLane($oldTask->execution, 'task');
$this->loadModel('kanban');
if(!isset($output['toColID'])) $this->kanban->updateLane($oldTask->execution, 'task', $taskID);
if(isset($output['toColID'])) $this->kanban->moveCard($taskID, $output['fromColID'], $output['toColID'], $output['fromLaneID'], $output['toLaneID']);
if(!dao::isError()) return common::createChanges($oldTask, $task);
}
+2
View File
@@ -35,10 +35,12 @@
?>
<form class='main-form form-ajax' method='post' enctype='multipart/form-data' id='dataform'>
<table class='table table-form'>
<?php if($execution->type != 'kanban'):?>
<tr>
<th><?php echo $lang->task->execution;?></th>
<td><?php echo html::select('execution', $executions, $execution->id, "class='form-control chosen' onchange='loadAll(this.value)' required");?></td><td></td><td></td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->task->type;?></th>
<td><?php echo html::select('type', $lang->task->typeList, $task->type, "class='form-control chosen' onchange='setOwners(this.value)' required");?></td>
+1 -1
View File
@@ -171,7 +171,7 @@
<?php if(!isonlybody()) echo "<div class='divider'></div>";?>
<?php if(!$task->deleted):?>
<?php
if(empty($task->team) or empty($task->children)) common::printIcon('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'button', 'split', '', '', '', "title='{$lang->task->children}'", $lang->task->children);
if((empty($task->team) or empty($task->children)) and $execution->type != 'kanban') common::printIcon('task', 'batchCreate', "execution=$task->execution&storyID=$task->story&moduleID=$task->module&taskID=$task->id", $task, 'button', 'split', '', '', '', "title='{$lang->task->children}'", $lang->task->children);
common::printIcon('task', 'assignTo', "executionID=$task->execution&taskID=$task->id", $task, 'button', '', '', 'iframe', true, '', empty($task->team) ? $lang->task->assignTo : $lang->task->transfer);
common::printIcon('task', 'start', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
common::printIcon('task', 'restart', "taskID=$task->id", $task, 'button', '', '', 'iframe showinonlybody', true);
+2 -2
View File
@@ -1,13 +1,13 @@
.adbox {margin-bottom: 0px; border: 0px; box-shadow: none;}
.adbox .panel-body {padding-bottom: 0px; border: 0px;}
.col-md-6 {width: 49% !important;}
.col-md-6 {width: 100% !important;}
.row .divider {display: block; width: 2px; height: 450px; background-color: rgba(0,0,0,.05); float: left;}
.row .col-md-6 .message {padding-left: 50px; padding-top: 50px;}
.row .col-md-6 .message #tohome {margin-top: 50px;}
.row .col-md-6 .message p {font-size: 16px;}
.row .col-md-6 .message .icon-check-circle {font-size: 40px; color: green;}
.row .col-md-6 .message .icon-check-circle {font-size: 85px; color: green;}
.card {
position: relative;
+2
View File
@@ -818,6 +818,8 @@ function hiddenProject()
*/
function setProgramByProduct(product)
{
if(product.length == 0) return;
var programID = product.attr('data-programid');
$(':checkbox[data-productid]').each(function()
{
+1 -1
View File
@@ -14,7 +14,7 @@ $lang->upgrade->start = 'Start';
$lang->upgrade->result = 'Result';
$lang->upgrade->fail = 'Failed';
$lang->upgrade->successTip = 'Successed';
$lang->upgrade->success = "<p><i class='icon icon-check-circle'></i></p><p>Congratulations!</p><p>Your ZenTao is updated.</p>";
$lang->upgrade->success = "<p><i class='icon icon-check-circle'></i></p><p>Congratulations! Your ZenTao is updated.</p>";
$lang->upgrade->tohome = 'Visit ZenTao';
$lang->upgrade->license = 'ZenTao is under Z PUBLIC LICENSE(ZPL) 1.2.';
$lang->upgrade->warnning = 'Warning!';
+1
View File
@@ -149,3 +149,4 @@ $lang->upgrade->fromVersions['15_7'] = '15.7';
$lang->upgrade->fromVersions['15_7_1'] = '15.7.1';
$lang->upgrade->fromVersions['16_0_beta1'] = '16.0.beta1';
$lang->upgrade->fromVersions['16_0'] = '16.0';
$lang->upgrade->fromVersions['16_1'] = '16.1';
+1 -1
View File
@@ -14,7 +14,7 @@ $lang->upgrade->start = '开始';
$lang->upgrade->result = '升级结果';
$lang->upgrade->fail = '升级失败';
$lang->upgrade->successTip = '升级成功';
$lang->upgrade->success = "<p><i class='icon icon-check-circle'></i></p><p>恭喜您!</p><p>您的禅道已经成功升级。</p>";
$lang->upgrade->success = "<p><i class='icon icon-check-circle'></i></p><p>恭喜您!您的禅道已经成功升级。</p>";
$lang->upgrade->tohome = '访问禅道';
$lang->upgrade->license = '禅道项目管理软件已更换授权协议至 Z PUBLIC LICENSE(ZPL) 1.2';
$lang->upgrade->warnning = '警告';
+82 -2
View File
@@ -745,6 +745,11 @@ class upgradeModel extends model
$this->saveLogs('Execute 16_0');
$this->execSQL($this->getUpgradeFile('16.0'));
$this->appendExec('16_0');
case '16_1':
$this->saveLogs('Execute 16_1');
$this->execSQL($this->getUpgradeFile('16.1'));
$this->moveKanbanData();
$this->appendExec('16_1');
}
$this->deletePatch();
@@ -953,6 +958,7 @@ class upgradeModel extends model
case '15_7_1': $confirmContent .= file_get_contents($this->getUpgradeFile('15.7.1'));
case '16_0_beta1': $confirmContent .= file_get_contents($this->getUpgradeFile('16.0.beta1'));
case '16_0': $confirmContent .= file_get_contents($this->getUpgradeFile('16.0'));
case '16_1': $confirmContent .= file_get_contents($this->getUpgradeFile('16.1'));
}
return str_replace('zt_', $this->config->db->prefix, $confirmContent);
}
@@ -4285,7 +4291,7 @@ class upgradeModel extends model
}
}
$executionPairs = $this->dao->select('id')->from(TABLE_PROJECT)->where('id')->in($projectIdList)->andWhere('type')->in('sprint,stage')->fetchAll('id', 'id');
$executionPairs = $this->dao->select('id')->from(TABLE_PROJECT)->where('id')->in($projectIdList)->andWhere('type')->in('sprint,stage,kanban')->fetchAll('id', 'id');
foreach($userViews as $account => $userView)
{
$projects = zget($accountProjects, $account, array());
@@ -4856,7 +4862,7 @@ class upgradeModel extends model
$this->personnel->updateWhitelist($whitelist, 'product', $product->id, 'whitelist', 'upgrade', 'increase');
}
$customSprints = $this->dao->select('*')->from(TABLE_PROJECT)->where('whitelist')->ne('')->andWhere('type')->in('sprint,stage')->fetchAll('id');
$customSprints = $this->dao->select('*')->from(TABLE_PROJECT)->where('whitelist')->ne('')->andWhere('type')->in('sprint,stage,kanban')->fetchAll('id');
$whitelistACL = $this->dao->select('account')->from(TABLE_ACL)->where('objectID')->in(array_keys($customSprints))->andWhere('objectType')->eq('sprint')->andWhere('type')->eq('whitelist')->fetchPairs('account');
foreach($customSprints as $sprint)
{
@@ -5365,6 +5371,80 @@ class upgradeModel extends model
return true;
}
/**
* Move kanban card data to kanbancell table.
*
* @access public
* @return void
*/
public function moveKanbanData()
{
/* Move common kanban data. */
$cards = $this->dao->select('id,kanban,`column`,lane')->from(TABLE_KANBANCARD)->fetchAll('id');
$cellGroup = array();
foreach($cards as $cardID => $card)
{
if(!$card->lane or !$card->column) continue;
$key = $card->kanban . '-' . $card->lane . '-' . $card->column;
$cards = isset($cellGroup[$key]) ? $cellGroup[$key] . "$cardID," : ",$cardID,";
$cellGroup[$key] = $cards;
}
foreach($cellGroup as $key => $cards)
{
$key = explode('-', $key);
if(!is_array($key)) continue;
$cell = new stdclass();
$cell->kanban = $key[0];
$cell->lane = $key[1];
$cell->column = $key[2];
$cell->type = 'card';
$cell->cards = $cards;
$this->dao->insert(TABLE_KANBANCELL)->data($cell)->exec();
}
/* Drop group kanban data. */
$groupLanePairs = $this->dao->select('id')->from(TABLE_KANBANLANE)->where('`groupby`')->ne('')->fetchPairs();
if(!empty($groupLanePairs))
{
$this->dao->delete()->from(TABLE_KANBANLANE)->where('id')->in($groupLanePairs)->exec();
$this->dao->delete()->from(TABLE_KANBANCOLUMN)->where('lane')->in($groupLanePairs)->exec();
}
/* Move execution kanban data. */
$executionKanban = $this->dao->select('t1.id as `lane`, t1.execution, t1.type, t2.id as `column`, t2.cards')->from(TABLE_KANBANLANE)->alias('t1')
->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.id = t2.lane')
->where('t1.execution')->gt(0)
->fetchGroup('lane');
foreach($executionKanban as $laneID => $laneGroup)
{
foreach($laneGroup as $colData)
{
if(!$laneID or !$colData->column) continue;
$cell = new stdclass();
$cell->kanban = $colData->execution;
$cell->lane = $laneID;
$cell->column = $colData->column;
$cell->type = $colData->type;
$cell->cards = $colData->cards;
$this->dao->insert(TABLE_KANBANCELL)->data($cell)->exec();
}
}
/* Drop unused field. */
$this->dao->exec("ALTER TABLE " . TABLE_KANBANCARD . " DROP COLUMN `lane`;");
$this->dao->exec("ALTER TABLE " . TABLE_KANBANCARD . " DROP COLUMN `column`;");
$this->dao->exec("ALTER TABLE " . TABLE_KANBANCOLUMN . " DROP COLUMN `lane`;");
$this->dao->exec("ALTER TABLE " . TABLE_KANBANCOLUMN . " DROP COLUMN `cards`;");
}
/**
* Adjust for bug required field.
*
+3 -1
View File
@@ -18,11 +18,12 @@
<div class='modal-body'>
<div class='row'>
<div class='col-md-6'>
<div class='message mgb-10'>
<div class='message mgb-10 text-center'>
<strong><?php echo $lang->upgrade->success?></strong>
<div><?php echo html::a('index.php', $lang->upgrade->tohome, '', "class='btn btn-primary btn-wide' id='tohome'")?></div>
</div>
</div>
<!--
<div class='divider'></div>
<div class='col-md-6'>
<div class='panel adbox'>
@@ -39,6 +40,7 @@
</div>
</div>
</div>
-->
</div>
</div>
</div>
+5 -5
View File
@@ -1092,7 +1092,7 @@ class userModel extends model
*/
public function getObjects($account, $type = 'execution', $status = 'all', $orderBy = 'id_desc', $pager = null)
{
$objectType = $type == 'execution' ? 'sprint,stage' : $type;
$objectType = $type == 'execution' ? 'sprint,stage,kanban' : $type;
$myObjectsList = $this->dao->select('t1.*,t2.*')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.root = t2.id')
->where('t1.type')->eq($type)
@@ -1624,7 +1624,7 @@ class userModel extends model
if($allProducts === null) $allProducts = $this->dao->select('id,PO,QD,RD,createdBy,acl,whitelist,program,createdBy')->from(TABLE_PRODUCT)->where('acl')->ne('open')->fetchAll('id');
if($allProjects === null) $allProjects = $this->dao->select('id,PO,PM,QD,RD,acl,type,path,parent,openedBy')->from(TABLE_PROJECT)->where('acl')->ne('open')->andWhere('type')->eq('project')->fetchAll('id');
if($allPrograms === null) $allPrograms = $this->dao->select('id,PO,PM,QD,RD,acl,type,path,parent,openedBy')->from(TABLE_PROGRAM)->where('acl')->ne('open')->andWhere('type')->eq('program')->fetchAll('id');
if($allSprints === null) $allSprints = $this->dao->select('id,PO,PM,QD,RD,acl,project,path,parent,type,openedBy')->from(TABLE_PROJECT)->where('acl')->eq('private')->beginIF($this->config->systemMode == 'new')->andWhere('type')->in('sprint,stage')->fi()->fetchAll('id');
if($allSprints === null) $allSprints = $this->dao->select('id,PO,PM,QD,RD,acl,project,path,parent,type,openedBy')->from(TABLE_PROJECT)->where('acl')->eq('private')->beginIF($this->config->systemMode == 'new')->andWhere('type')->in('sprint,stage,kanban')->fi()->fetchAll('id');
/* Get teams. */
if($teams === null)
@@ -1869,7 +1869,7 @@ class userModel extends model
/* Set opened sprints and stages into userview. */
$openedSprints = $this->dao->select('id')->from(TABLE_PROJECT)
->where('acl')->eq('open')
->beginIF($this->config->systemMode == 'new')->andWhere('type')->in('sprint,stage')->fi()
->beginIF($this->config->systemMode == 'new')->andWhere('type')->in('sprint,stage,kanban')->fi()
->beginIF($this->config->systemMode == 'new')->andWhere('project')->in($userView->projects)->fi()
->fetchAll('id');
@@ -2303,11 +2303,11 @@ class userModel extends model
}
/* Judge sprint auth. */
if(($project->type == 'sprint' || $project->type == 'stage') && $project->acl == 'private')
if(($project->type == 'sprint' or $project->type == 'stage' or $project->type == 'kanban') and $project->acl == 'private' and $this->config->systemMode == 'new')
{
$parent = $this->dao->select('openedBy,PM')->from(TABLE_PROJECT)->where('id')->eq($project->project)->fetch();
if(empty($parent)) return false;
if($parent->PM == $account || $parent->openedBy == $account) return true;
if($parent->PM == $account or $parent->openedBy == $account) return true;
}
return false;

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