Merge branch 'master' into lite

This commit is contained in:
王怡栋
2022-01-19 08:44:43 +08:00
254 changed files with 11731 additions and 2120 deletions
+1 -1
View File
@@ -1 +1 @@
16.0
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.0'; // 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.
+2 -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`');
@@ -296,5 +297,5 @@ $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme');
/* Program privs.*/
$config->programPriv = new stdclass();
$config->programPriv->scrum = array('projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
$config->programPriv->scrum = array('story', 'projectstory', 'projectrelease', 'project', 'build', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
$config->programPriv->waterfall = array_merge($config->programPriv->scrum, array('workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'issue', 'risk', 'opportunity', 'measrecord', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport'));
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+13
View File
@@ -388,6 +388,19 @@ CREATE TABLE IF NOT EXISTS `zt_testPlan` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_testTask` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`name` char(90) NOT NULL,
`product` mediumint(8) unsigned NOT NULL,
`project` mediumint(8) unsigned NOT NULL default '0',
`build` char(30) NOT NULL,
`begin` date NOT NULL,
`end` date NOT NULL,
`desc` text NOT NULL,
`status` char(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_todo` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`account` char(30) NOT NULL,
+15 -4
View File
@@ -1,4 +1,15 @@
ALTER TABLE `zt_project` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`;
ALTER TABLE `zt_product` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`;
ALTER TABLE `zt_doclib` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`;
ALTER TABLE `zt_kanban` ADD `project` mediumint(8) unsigned NOT NULL AFTER `space`;
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';
+4
View File
@@ -0,0 +1,4 @@
ALTER TABLE `zt_project` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`;
ALTER TABLE `zt_product` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`;
ALTER TABLE `zt_doclib` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `order`;
ALTER TABLE `zt_task` ADD `vision` varchar(10) NOT NULL DEFAULT 'common' AFTER `deleted`;
+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,
@@ -1011,6 +1021,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
`whitelist` text NOT NULL,
`order` mediumint(8) unsigned NOT NULL,
`vision` varchar(10) NOT NULL DEFAULT 'common',
`displayCards` smallint(6) NOT NULL default '0',
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `parent` (`parent`),
+81
View File
@@ -1,3 +1,84 @@
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 计划看板中实现卡片的排序功能
46670 实现计划卡片在不同状态列中的拖动功能
46669 批量编辑计划页面增加状态设置
46668 实现开启分支功能的计划看板视图
46667 实现计划看板中卡片的展现样式
46666 在计划看板中实现分支切换查看功能
46665 计划列表中已完成和已关闭的计划不能创建执行
46662 编辑计划页面增加状态设置
46661 计划详情页中增加开始、完成、关闭、激活操作
46660 计划列表的操作中增加开始、完成、关闭操作
46659 计划看板泳道设置默认的颜色
46658 实现未开启分支功能的计划看板视图
46657 计划列表增加列表和看板视图切换控件
46656 计划列表中增加已过期标签提醒
46655 计划列表筛选标签调整为按状态显示
46654 计划增加状态管理
47057 重新配置构建任务并修改合并请求,构建任务成功后合并请求详情页构建任务状态为改变
47053 构建任务的状态没有定时刷新
47052 禅道中受保护标签没有受保护标识
47051 合并请求时提示信息中有非中文字符
47050 自动创建的合并请求关联需求、Bug、任务页面有代码报错
47049 创建分支保护页面点击保存后有报错信息
47048 构建日志页面参数标签处显示乱码
47047 源分支存在重复未关闭的合并请求提示信息中建议把ID显示出来
46556 实现在禅道中编辑GitLab标签保护功能
46555 实现在禅道中展示标签保护列表的功能
46554 在禅道GitLab项目列表中添加标签保护列表的入口
46553 处理Z工具触发禅道创建GitLab合并请求失败的问题
46552 实现禅道中创建合并请求检查此源分支是否存在其他未关闭合并请求的功能
46551 实现在任务详情页面显示关联的合并请求信息的功能
46550 实现Bug详情页面其他相关中展示相关联合并请求的功能
46549 实现在需求详情页面相关信息中显示关联的合并请求信息的功能
46548 实现VS Code禅道插件自动生成源代码管理中的提交信息功能
46547 实现代码模块中版本信息中的注释支持点击跳转到对应需求、任务、Bug的功能
46546 优化VS Code禅道插件查看功能需要修改禅道配置文件的问题
46545 实现VS Code禅道插件提交信息自动补全的自定义触发功能
46544 实现在禅道中添加GitLab标签保护功能
46543 15.5版本项目设置项目权限重新定义不生效
46531 需求详情页面中执行任务名称前显示任务状态
46529 鼠标悬浮到卡片头像上时显示人员姓名
46527 项目集、产品、项目、执行权限白名单用户设置时可以选择联系人
46526 调整项目文档库的访问控制规则
46521 老版本升级数据合并时把已删除的数据过滤掉。
46520 待处理页面默认显示待处理项的数量
46519 Bug列表设置中自定义列增加所属项目字段
46518 测试单的报告增加重新激活Bug数的统计
2021-12-24 16.0
完成的需求
29649 实现禅道VS Code集成时判断所连接禅道版本兼容性功能
+36
View File
@@ -917,3 +917,39 @@ function htmlSpecialString($string, $flags = '', $encoding = 'UTF-8')
if(!$flags) $flags = defined('ENT_SUBSTITUTE') ? ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 : ENT_QUOTES;
return htmlspecialchars($string, $flags, $encoding);
}
if (!function_exists('array_column'))
{
function array_column(array $input, $columnKey, $indexKey = null)
{
$output = array();
foreach ($input as $row) {
$key = $value = null;
$keySet = $valueSet = false;
if (null !== $indexKey && array_key_exists($indexKey, $row)) {
$keySet = true;
$key = (string) $row[$indexKey];
}
if (null === $columnKey) {
$valueSet = true;
$value = $row;
} elseif (\is_array($row) && \array_key_exists($columnKey, $row)) {
$valueSet = true;
$value = $row[$columnKey];
}
if ($valueSet) {
if ($keySet) {
$output[$key] = $value;
} else {
$output[] = $value;
}
}
}
return $output;
}
}
+26 -12
View File
@@ -1964,23 +1964,37 @@ class baseRouter
$defaultParams[$name] = $default;
}
/**
* 根据PATH_INFO或者GET方式设置请求的参数。
* Set params according PATH_INFO or GET.
*/
if($this->config->requestType != 'GET')
if ('cli' === PHP_SAPI)
{
$this->setParamsByPathInfo($defaultParams);
if ($this->params)
{
$this->params = array_merge($defaultParams, $this->params);
}
else
{
$this->params = $defaultParams;
}
}
else
{
$this->setParamsByGET($defaultParams);
}
/**
* 根据PATH_INFO或者GET方式设置请求的参数。
* Set params according PATH_INFO or GET.
*/
if($this->config->requestType != 'GET')
{
$this->setParamsByPathInfo($defaultParams);
}
else
{
$this->setParamsByGET($defaultParams);
}
if($this->config->framework->filterParam == 2)
{
$_GET = validater::filterParam($_GET, 'get');
$_COOKIE = validater::filterParam($_COOKIE, 'cookie');
if($this->config->framework->filterParam == 2)
{
$_GET = validater::filterParam($_GET, 'get');
$_COOKIE = validater::filterParam($_COOKIE, 'cookie');
}
}
/* 调用该方法 Call the method. */
+54 -53
View File
@@ -137,59 +137,60 @@ $lang->action->objectTypes['kanbancard'] = 'Kanban Card';
/* Used to describe operation history. */
$lang->action->desc = new stdclass();
$lang->action->desc->common = '$date, <strong>$action</strong> by <strong>$actor</strong>.' . "\n";
$lang->action->desc->extra = '$date, <strong>$action</strong> as <strong>$extra</strong> by <strong>$actor</strong>.' . "\n";
$lang->action->desc->opened = '$date, created by <strong>$actor</strong> .' . "\n";
$lang->action->desc->openedbysystem = '$date, opened by system.' . "\n";
$lang->action->desc->created = '$date, created by <strong>$actor</strong> .' . "\n";
$lang->action->desc->added = '$date, added by <strong>$actor</strong> .' . "\n";
$lang->action->desc->changed = '$date, changed by <strong>$actor</strong> .' . "\n";
$lang->action->desc->edited = '$date, edited by <strong>$actor</strong> .' . "\n";
$lang->action->desc->assigned = '$date, <strong>$actor</strong> assigned to <strong>$extra</strong>.' . "\n";
$lang->action->desc->closed = '$date, closed by <strong>$actor</strong> .' . "\n";
$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n";
$lang->action->desc->deleted = '$date, deleted by <strong>$actor</strong> .' . "\n";
$lang->action->desc->deletedfile = '$date, <strong>$actor</strong> deleted <strong><i>$extra</i></strong>.' . "\n";
$lang->action->desc->editfile = '$date, <strong>$actor</strong> edited <strong><i>$extra</i></strong>.' . "\n";
$lang->action->desc->erased = '$date, deleted by <strong>$actor</strong> .' . "\n";
$lang->action->desc->undeleted = '$date, restored by <strong>$actor</strong> .' . "\n";
$lang->action->desc->hidden = '$date, hidden by <strong>$actor</strong> .' . "\n";
$lang->action->desc->commented = '$date, added by <strong>$actor</strong>.' . "\n";
$lang->action->desc->activated = '$date, activated by <strong>$actor</strong> .' . "\n";
$lang->action->desc->blocked = '$date, blocked by <strong>$actor</strong> .' . "\n";
$lang->action->desc->moved = '$date, moved by <strong>$actor</strong> , which was "$extra".' . "\n";
$lang->action->desc->confirmed = '$date, <strong>$actor</strong> confirmed the story change. The latest build is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->caseconfirmed = '$date, <strong>$actor</strong> confirmed the case change. The latest build is <strong>#$extra</strong>' . "\n";
$lang->action->desc->bugconfirmed = '$date, <strong>$actor</strong> confirmed Bug.' . "\n";
$lang->action->desc->frombug = '$date, converted from <strong>$actor</strong>. Its ID was <strong>$extra</strong>.';
$lang->action->desc->started = '$date, started by <strong>$actor</strong>.' . "\n";
$lang->action->desc->restarted = '$date, continued by <strong>$actor</strong>.' . "\n";
$lang->action->desc->delayed = '$date, postponed by <strong>$actor</strong>.' . "\n";
$lang->action->desc->suspended = '$date, suspended by <strong>$actor</strong>.' . "\n";
$lang->action->desc->recordestimate = '$date, recorded by <strong>$actor</strong> and it cost <strong>$extra</strong> hours.';
$lang->action->desc->editestimate = '$date, <strong>$actor</strong> edited Hour.';
$lang->action->desc->deleteestimate = '$date, <strong>$actor</strong> deleted Hour.';
$lang->action->desc->canceled = '$date, cancelled by <strong>$actor</strong>.' . "\n";
$lang->action->desc->svncommited = '$date, <strong>$actor</strong> committed and the build is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->gitcommited = '$date, <strong>$actor</strong> committed and the build is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->finished = '$date, finished by <strong>$actor</strong>.' . "\n";
$lang->action->desc->paused = '$date, paused by <strong>$actor</strong>.' . "\n";
$lang->action->desc->verified = '$date, verified by <strong>$actor</strong>.' . "\n";
$lang->action->desc->diff1 = '<strong><i>%s</i></strong> is changed. It was "%s" and it is "%s".<br />' . "\n";
$lang->action->desc->diff2 = '<strong><i>%s</i></strong> is changed. The difference is ' . "\n" . "<blockquote class='textdiff'>%s</blockquote>" . "\n<blockquote class='original'>%s</blockquote>";
$lang->action->desc->diff3 = 'File Name %s was changed to %s .' . "\n";
$lang->action->desc->linked2bug = '$date, linked to <strong>$extra</strong> by <strong>$actor</strong>';
$lang->action->desc->linked2testtask = '$date, linked to <strong>$extra</strong> by <strong>$actor</strong>';
$lang->action->desc->resolved = '$date, resolved by <strong>$actor</strong> ' . "\n";
$lang->action->desc->managed = '$date, by <strong>$actor</strong> managed.' . "\n";
$lang->action->desc->estimated = '$date, by <strong>$actor</strong> estimated.' . "\n";
$lang->action->desc->run = '$date, by <strong>$actor</strong> executed.' . "\n";
$lang->action->desc->syncprogram = '$date, started by <strong>$actor</strong>(starting the project sets the program status as Ongoing).' . "\n";
$lang->action->desc->syncproject = '$date, starting the execution sets the project status as Ongoing.' . "\n";
$lang->action->desc->syncexecution = '$date, starting the task sets the execution status as Ongoing.' . "\n";
$lang->action->desc->importfromgitlab = '$date, Issue associate created from gitlab by <strong>$actor</strong>.' . "\n";
$lang->action->desc->archived = '$date, archived by <strong>$actor</strong>.' . "\n";
$lang->action->desc->restore = '$date, restore by <strong>$actor</strong>.' . "\n";
$lang->action->desc->common = '$date, <strong>$action</strong> by <strong>$actor</strong>.' . "\n";
$lang->action->desc->extra = '$date, <strong>$action</strong> as <strong>$extra</strong> by <strong>$actor</strong>.' . "\n";
$lang->action->desc->opened = '$date, created by <strong>$actor</strong> .' . "\n";
$lang->action->desc->openedbysystem = '$date, opened by system.' . "\n";
$lang->action->desc->created = '$date, created by <strong>$actor</strong> .' . "\n";
$lang->action->desc->added = '$date, added by <strong>$actor</strong> .' . "\n";
$lang->action->desc->changed = '$date, changed by <strong>$actor</strong> .' . "\n";
$lang->action->desc->edited = '$date, edited by <strong>$actor</strong> .' . "\n";
$lang->action->desc->assigned = '$date, <strong>$actor</strong> assigned to <strong>$extra</strong>.' . "\n";
$lang->action->desc->closed = '$date, closed by <strong>$actor</strong> .' . "\n";
$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n";
$lang->action->desc->deleted = '$date, deleted by <strong>$actor</strong> .' . "\n";
$lang->action->desc->deletedfile = '$date, <strong>$actor</strong> deleted <strong><i>$extra</i></strong>.' . "\n";
$lang->action->desc->editfile = '$date, <strong>$actor</strong> edited <strong><i>$extra</i></strong>.' . "\n";
$lang->action->desc->erased = '$date, deleted by <strong>$actor</strong> .' . "\n";
$lang->action->desc->undeleted = '$date, restored by <strong>$actor</strong> .' . "\n";
$lang->action->desc->hidden = '$date, hidden by <strong>$actor</strong> .' . "\n";
$lang->action->desc->commented = '$date, added by <strong>$actor</strong>.' . "\n";
$lang->action->desc->activated = '$date, activated by <strong>$actor</strong> .' . "\n";
$lang->action->desc->blocked = '$date, blocked by <strong>$actor</strong> .' . "\n";
$lang->action->desc->moved = '$date, moved by <strong>$actor</strong> , which was "$extra".' . "\n";
$lang->action->desc->confirmed = '$date, <strong>$actor</strong> confirmed the story change. The latest build is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->caseconfirmed = '$date, <strong>$actor</strong> confirmed the case change. The latest build is <strong>#$extra</strong>' . "\n";
$lang->action->desc->bugconfirmed = '$date, <strong>$actor</strong> confirmed Bug.' . "\n";
$lang->action->desc->frombug = '$date, converted from <strong>$actor</strong>. Its ID was <strong>$extra</strong>.';
$lang->action->desc->started = '$date, started by <strong>$actor</strong>.' . "\n";
$lang->action->desc->restarted = '$date, continued by <strong>$actor</strong>.' . "\n";
$lang->action->desc->delayed = '$date, postponed by <strong>$actor</strong>.' . "\n";
$lang->action->desc->suspended = '$date, suspended by <strong>$actor</strong>.' . "\n";
$lang->action->desc->recordestimate = '$date, recorded by <strong>$actor</strong> and it cost <strong>$extra</strong> hours.';
$lang->action->desc->editestimate = '$date, <strong>$actor</strong> edited Hour.';
$lang->action->desc->deleteestimate = '$date, <strong>$actor</strong> deleted Hour.';
$lang->action->desc->canceled = '$date, cancelled by <strong>$actor</strong>.' . "\n";
$lang->action->desc->svncommited = '$date, <strong>$actor</strong> committed and the build is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->gitcommited = '$date, <strong>$actor</strong> committed and the build is <strong>#$extra</strong>.' . "\n";
$lang->action->desc->finished = '$date, finished by <strong>$actor</strong>.' . "\n";
$lang->action->desc->paused = '$date, paused by <strong>$actor</strong>.' . "\n";
$lang->action->desc->verified = '$date, verified by <strong>$actor</strong>.' . "\n";
$lang->action->desc->diff1 = '<strong><i>%s</i></strong> is changed. It was "%s" and it is "%s".<br />' . "\n";
$lang->action->desc->diff2 = '<strong><i>%s</i></strong> is changed. The difference is ' . "\n" . "<blockquote class='textdiff'>%s</blockquote>" . "\n<blockquote class='original'>%s</blockquote>";
$lang->action->desc->diff3 = 'File Name %s was changed to %s .' . "\n";
$lang->action->desc->linked2bug = '$date, linked to <strong>$extra</strong> by <strong>$actor</strong>';
$lang->action->desc->linked2testtask = '$date, linked to <strong>$extra</strong> by <strong>$actor</strong>';
$lang->action->desc->unlinkedfromtesttask = '$date, unlinked from <strong>$extra</strong> by <strong>$actor</strong>';
$lang->action->desc->resolved = '$date, resolved by <strong>$actor</strong> ' . "\n";
$lang->action->desc->managed = '$date, by <strong>$actor</strong> managed.' . "\n";
$lang->action->desc->estimated = '$date, by <strong>$actor</strong> estimated.' . "\n";
$lang->action->desc->run = '$date, by <strong>$actor</strong> executed.' . "\n";
$lang->action->desc->syncprogram = '$date, started by <strong>$actor</strong>(starting the project sets the program status as Ongoing).' . "\n";
$lang->action->desc->syncproject = '$date, starting the execution sets the project status as Ongoing.' . "\n";
$lang->action->desc->syncexecution = '$date, starting the task sets the execution status as Ongoing.' . "\n";
$lang->action->desc->importfromgitlab = '$date, Issue associate created from gitlab by <strong>$actor</strong>.' . "\n";
$lang->action->desc->archived = '$date, archived by <strong>$actor</strong>.' . "\n";
$lang->action->desc->restore = '$date, restore by <strong>$actor</strong>.' . "\n";
/* Used to describe the history of operations related to parent-child tasks. */
$lang->action->desc->createchildren = '$date, <strong>$actor</strong> created a child task <strong>$extra</strong>。' . "\n";
+54 -53
View File
@@ -137,59 +137,60 @@ $lang->action->objectTypes['kanbancard'] = '看板卡片';
/* 用来描述操作历史记录。*/
$lang->action->desc = new stdclass();
$lang->action->desc->common = '$date, <strong>$action</strong> by <strong>$actor</strong>。' . "\n";
$lang->action->desc->extra = '$date, <strong>$action</strong> as <strong>$extra</strong> by <strong>$actor</strong>。' . "\n";
$lang->action->desc->opened = '$date, 由 <strong>$actor</strong> 创建。' . "\n";
$lang->action->desc->openedbysystem = '$date, 由系统创建。' . "\n";
$lang->action->desc->created = '$date, 由 <strong>$actor</strong> 创建。' . "\n";
$lang->action->desc->added = '$date, 由 <strong>$actor</strong> 添加。' . "\n";
$lang->action->desc->changed = '$date, 由 <strong>$actor</strong> 变更。' . "\n";
$lang->action->desc->edited = '$date, 由 <strong>$actor</strong> 编辑。' . "\n";
$lang->action->desc->assigned = '$date, 由 <strong>$actor</strong> 指派给 <strong>$extra</strong>。' . "\n";
$lang->action->desc->closed = '$date, 由 <strong>$actor</strong> 关闭。' . "\n";
$lang->action->desc->closedbysystem = '$date, 由系统关闭。' . "\n";
$lang->action->desc->deleted = '$date, 由 <strong>$actor</strong> 删除。' . "\n";
$lang->action->desc->deletedfile = '$date, 由 <strong>$actor</strong> 删除了附件:<strong><i>$extra</i></strong>。' . "\n";
$lang->action->desc->editfile = '$date, 由 <strong>$actor</strong> 编辑了附件:<strong><i>$extra</i></strong>。' . "\n";
$lang->action->desc->erased = '$date, 由 <strong>$actor</strong> 删除。' . "\n";
$lang->action->desc->undeleted = '$date, 由 <strong>$actor</strong> 还原。' . "\n";
$lang->action->desc->hidden = '$date, 由 <strong>$actor</strong> 隐藏。' . "\n";
$lang->action->desc->commented = '$date, 由 <strong>$actor</strong> 添加备注。' . "\n";
$lang->action->desc->activated = '$date, 由 <strong>$actor</strong> 激活。' . "\n";
$lang->action->desc->blocked = '$date, 由 <strong>$actor</strong> 阻塞。' . "\n";
$lang->action->desc->moved = '$date, 由 <strong>$actor</strong> 移动,之前为 "$extra"。' . "\n";
$lang->action->desc->confirmed = '$date, 由 <strong>$actor</strong> 确认' . $lang->SRCommon . '变动,最新版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->caseconfirmed = '$date, 由 <strong>$actor</strong> 确认用例变动,最新版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->bugconfirmed = '$date, 由 <strong>$actor</strong> 确认Bug。' . "\n";
$lang->action->desc->frombug = '$date, 由 <strong>$actor</strong> Bug转化而来,Bug编号为 <strong>$extra</strong>。';
$lang->action->desc->started = '$date, 由 <strong>$actor</strong> 启动。' . "\n";
$lang->action->desc->restarted = '$date, 由 <strong>$actor</strong> 继续。' . "\n";
$lang->action->desc->delayed = '$date, 由 <strong>$actor</strong> 延期。' . "\n";
$lang->action->desc->suspended = '$date, 由 <strong>$actor</strong> 挂起。' . "\n";
$lang->action->desc->recordestimate = '$date, 由 <strong>$actor</strong> 记录工时,消耗 <strong>$extra</strong> 小时。';
$lang->action->desc->editestimate = '$date, 由 <strong>$actor</strong> 编辑工时。';
$lang->action->desc->deleteestimate = '$date, 由 <strong>$actor</strong> 删除工时。';
$lang->action->desc->canceled = '$date, 由 <strong>$actor</strong> 取消。' . "\n";
$lang->action->desc->svncommited = '$date, 由 <strong>$actor</strong> 提交代码,版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->gitcommited = '$date, 由 <strong>$actor</strong> 提交代码,版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->finished = '$date, 由 <strong>$actor</strong> 完成。' . "\n";
$lang->action->desc->paused = '$date, 由 <strong>$actor</strong> 暂停。' . "\n";
$lang->action->desc->verified = '$date, 由 <strong>$actor</strong> 验收。' . "\n";
$lang->action->desc->diff1 = '修改了 <strong><i>%s</i></strong>,旧值为 "%s",新值为 "%s"。<br />' . "\n";
$lang->action->desc->diff2 = '修改了 <strong><i>%s</i></strong>,区别为:' . "\n" . "<blockquote class='textdiff'>%s</blockquote>" . "\n<blockquote class='original'>%s</blockquote>";
$lang->action->desc->diff3 = '将文件名 %s 改为 %s 。' . "\n";
$lang->action->desc->linked2bug = '$date 由 <strong>$actor</strong> 关联到版本 <strong>$extra</strong>';
$lang->action->desc->linked2testtask = '$date 由 <strong>$actor</strong> 关联到测试单 <strong>$extra</strong>';
$lang->action->desc->resolved = '$date, 由 <strong>$actor</strong> 解决。' . "\n";
$lang->action->desc->managed = '$date, 由 <strong>$actor</strong> 维护。' . "\n";
$lang->action->desc->estimated = '$date, 由 <strong>$actor</strong> 估算。' . "\n";
$lang->action->desc->run = '$date, 由 <strong>$actor</strong> 执行。' . "\n";
$lang->action->desc->syncprogram = '$date, 由 <strong>$actor</strong> 启动(因项目开始而启动项目集)。' . "\n";
$lang->action->desc->syncproject = '$date, 系统判断由于执行开始,将项目状态置为进行中。' . "\n";
$lang->action->desc->syncexecution = '$date, 系统判断由于任务开始,将执行状态置为进行中。' . "\n";
$lang->action->desc->importfromgitlab = '$date, 由 <strong>$actor</strong> 从Gitlab的Issue关联创建。' . "\n";
$lang->action->desc->archived = '$date, 由 <strong>$actor</strong> 归档。' . "\n";
$lang->action->desc->restore = '$date, 由 <strong>$actor</strong> 还原。' . "\n";
$lang->action->desc->common = '$date, <strong>$action</strong> by <strong>$actor</strong>。' . "\n";
$lang->action->desc->extra = '$date, <strong>$action</strong> as <strong>$extra</strong> by <strong>$actor</strong>。' . "\n";
$lang->action->desc->opened = '$date, 由 <strong>$actor</strong> 创建。' . "\n";
$lang->action->desc->openedbysystem = '$date, 由系统创建。' . "\n";
$lang->action->desc->created = '$date, 由 <strong>$actor</strong> 创建。' . "\n";
$lang->action->desc->added = '$date, 由 <strong>$actor</strong> 添加。' . "\n";
$lang->action->desc->changed = '$date, 由 <strong>$actor</strong> 变更。' . "\n";
$lang->action->desc->edited = '$date, 由 <strong>$actor</strong> 编辑。' . "\n";
$lang->action->desc->assigned = '$date, 由 <strong>$actor</strong> 指派给 <strong>$extra</strong>。' . "\n";
$lang->action->desc->closed = '$date, 由 <strong>$actor</strong> 关闭。' . "\n";
$lang->action->desc->closedbysystem = '$date, 由系统关闭。' . "\n";
$lang->action->desc->deleted = '$date, 由 <strong>$actor</strong> 删除。' . "\n";
$lang->action->desc->deletedfile = '$date, 由 <strong>$actor</strong> 删除了附件:<strong><i>$extra</i></strong>。' . "\n";
$lang->action->desc->editfile = '$date, 由 <strong>$actor</strong> 编辑了附件:<strong><i>$extra</i></strong>。' . "\n";
$lang->action->desc->erased = '$date, 由 <strong>$actor</strong> 删除。' . "\n";
$lang->action->desc->undeleted = '$date, 由 <strong>$actor</strong> 还原。' . "\n";
$lang->action->desc->hidden = '$date, 由 <strong>$actor</strong> 隐藏。' . "\n";
$lang->action->desc->commented = '$date, 由 <strong>$actor</strong> 添加备注。' . "\n";
$lang->action->desc->activated = '$date, 由 <strong>$actor</strong> 激活。' . "\n";
$lang->action->desc->blocked = '$date, 由 <strong>$actor</strong> 阻塞。' . "\n";
$lang->action->desc->moved = '$date, 由 <strong>$actor</strong> 移动,之前为 "$extra"。' . "\n";
$lang->action->desc->confirmed = '$date, 由 <strong>$actor</strong> 确认' . $lang->SRCommon . '变动,最新版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->caseconfirmed = '$date, 由 <strong>$actor</strong> 确认用例变动,最新版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->bugconfirmed = '$date, 由 <strong>$actor</strong> 确认Bug。' . "\n";
$lang->action->desc->frombug = '$date, 由 <strong>$actor</strong> Bug转化而来,Bug编号为 <strong>$extra</strong>。';
$lang->action->desc->started = '$date, 由 <strong>$actor</strong> 启动。' . "\n";
$lang->action->desc->restarted = '$date, 由 <strong>$actor</strong> 继续。' . "\n";
$lang->action->desc->delayed = '$date, 由 <strong>$actor</strong> 延期。' . "\n";
$lang->action->desc->suspended = '$date, 由 <strong>$actor</strong> 挂起。' . "\n";
$lang->action->desc->recordestimate = '$date, 由 <strong>$actor</strong> 记录工时,消耗 <strong>$extra</strong> 小时。';
$lang->action->desc->editestimate = '$date, 由 <strong>$actor</strong> 编辑工时。';
$lang->action->desc->deleteestimate = '$date, 由 <strong>$actor</strong> 删除工时。';
$lang->action->desc->canceled = '$date, 由 <strong>$actor</strong> 取消。' . "\n";
$lang->action->desc->svncommited = '$date, 由 <strong>$actor</strong> 提交代码,版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->gitcommited = '$date, 由 <strong>$actor</strong> 提交代码,版本为<strong>#$extra</strong>。' . "\n";
$lang->action->desc->finished = '$date, 由 <strong>$actor</strong> 完成。' . "\n";
$lang->action->desc->paused = '$date, 由 <strong>$actor</strong> 暂停。' . "\n";
$lang->action->desc->verified = '$date, 由 <strong>$actor</strong> 验收。' . "\n";
$lang->action->desc->diff1 = '修改了 <strong><i>%s</i></strong>,旧值为 "%s",新值为 "%s"。<br />' . "\n";
$lang->action->desc->diff2 = '修改了 <strong><i>%s</i></strong>,区别为:' . "\n" . "<blockquote class='textdiff'>%s</blockquote>" . "\n<blockquote class='original'>%s</blockquote>";
$lang->action->desc->diff3 = '将文件名 %s 改为 %s 。' . "\n";
$lang->action->desc->linked2bug = '$date 由 <strong>$actor</strong> 关联到版本 <strong>$extra</strong>';
$lang->action->desc->linked2testtask = '$date 由 <strong>$actor</strong> 关联到测试单 <strong>$extra</strong>';
$lang->action->desc->unlinkedfromtesttask = '$date 由 <strong>$actor</strong> 从测试单 <strong>$extra</strong> 中移除';
$lang->action->desc->resolved = '$date, 由 <strong>$actor</strong> 解决。' . "\n";
$lang->action->desc->managed = '$date, 由 <strong>$actor</strong> 维护。' . "\n";
$lang->action->desc->estimated = '$date, 由 <strong>$actor</strong> 估算。' . "\n";
$lang->action->desc->run = '$date, 由 <strong>$actor</strong> 执行。' . "\n";
$lang->action->desc->syncprogram = '$date, 由 <strong>$actor</strong> 启动(因项目开始而启动项目集)。' . "\n";
$lang->action->desc->syncproject = '$date, 系统判断由于执行开始,将项目状态置为进行中。' . "\n";
$lang->action->desc->syncexecution = '$date, 系统判断由于任务开始,将执行状态置为进行中。' . "\n";
$lang->action->desc->importfromgitlab = '$date, 由 <strong>$actor</strong> 从Gitlab的Issue关联创建。' . "\n";
$lang->action->desc->archived = '$date, 由 <strong>$actor</strong> 归档。' . "\n";
$lang->action->desc->restore = '$date, 由 <strong>$actor</strong> 还原。' . "\n";
/* 用来描述和父子任务相关的操作历史记录。*/
$lang->action->desc->createchildren = '$date, 由 <strong>$actor</strong> 创建子任务 <strong>$extra</strong>。' . "\n";
+32 -7
View File
@@ -352,14 +352,18 @@ class actionModel extends model
}
elseif($actionName == 'linked2execution')
{
$name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('execution', 'view') ? html::a(helper::createLink('execution', 'view', "executionID=$action->execution"), $name) : $name;
$execution = $this->dao->select('name,type')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch();
$name = $execution->name;
$method = $execution->type == 'kanban' ? 'kanban' : 'view';
if($name) $action->extra = common::hasPriv('execution', $method) ? html::a(helper::createLink('execution', $method, "executionID=$action->execution"), $name) : $name;
}
elseif($actionName == 'linked2project')
{
$name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name');
$project = $this->dao->select('name,model')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch();
$productID = trim($action->product, ',');
if($name) $action->extra = common::hasPriv('project', 'view') ? html::a(helper::createLink('project', 'view', "projectID=$action->project"), $name) : $name;
$name = $project->name;
$method = $project->model == 'kanban' ? 'index' : 'view';
if($name) $action->extra = common::hasPriv('project', $method) ? html::a(helper::createLink('project', $method, "projectID=$action->project"), $name) : $name;
}
elseif($actionName == 'linked2plan')
{
@@ -381,6 +385,11 @@ class actionModel extends model
$name = $this->dao->select('name')->from(TABLE_RELEASE)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('release', 'view') ? html::a(helper::createLink('release', 'view', "releaseID=$action->extra&type={$action->objectType}"), $name) : $name;
}
elseif($actionName == 'linked2testtask')
{
$name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('testtask', 'view') ? html::a(helper::createLink('testtask', 'view', "taskID=$action->extra"), $name) : $name;
}
elseif($actionName == 'moved')
{
$name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name');
@@ -416,6 +425,11 @@ class actionModel extends model
$title = $this->dao->select('title')->from(TABLE_PRODUCTPLAN)->where('id')->eq($action->extra)->fetch('title');
if($title) $action->extra = common::hasPriv('productplan', 'view') ? html::a(helper::createLink('productplan', 'view', "planID=$action->extra"), "#$action->extra " . $title) : "#$action->extra " . $title;
}
elseif($actionName == 'unlinkedfromtesttask')
{
$name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = common::hasPriv('testtask', 'view') ? html::a(helper::createLink('testtask', 'view', "taskID=$action->extra"), $name) : $name;
}
elseif($actionName == 'tostory')
{
$title = $this->dao->select('title')->from(TABLE_STORY)->where('id')->eq($action->extra)->fetch('title');
@@ -611,6 +625,7 @@ class actionModel extends model
foreach($typeTrashes as $objectType => $objectIdList)
{
if(!isset($this->config->objectTables[$objectType])) continue;
if(!isset($this->config->action->objectNameFields[$objectType])) continue;
$objectIdList = array_unique($objectIdList);
$table = $this->config->objectTables[$objectType];
@@ -852,7 +867,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()
@@ -1023,12 +1038,16 @@ class actionModel extends model
{
$action->objectName .= $this->lang->action->label->startProgram;
}
if($action->objectType == 'branch' and $action->action == 'mergedbranch')
elseif($action->objectType == 'branch' and $action->action == 'mergedbranch')
{
if($action->objectID == 0) $action->objectName = $this->lang->branch->main;
$action->objectName = '"' . $action->extra . ' "' . $this->lang->action->to . ' "' . $action->objectName . '"';
}
elseif($action->objectType == 'user')
{
$user = $this->dao->select('id,realname')->from(TABLE_USER)->where('id')->eq($action->objectID)->fetch();
if($user) $action->objectName = $user->realname;
}
$projectID = isset($relatedProjects[$action->objectType][$action->objectID]) ? $relatedProjects[$action->objectType][$action->objectID] : 0;
@@ -1280,6 +1299,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();
+1 -1
View File
@@ -24,7 +24,7 @@
<table class='table table-form'>
<tr>
<th><?php echo $lang->api->version?></th>
<td style="width: 80%"><?php echo html::input('version', '', "class='form-control'")?></td>
<td style="width: 100%"><?php echo html::input('version', '', "class='form-control'")?></td>
</tr>
<tr>
<th><?php echo $lang->api->desc;?></th>
+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);
}
/**
+19
View File
@@ -106,6 +106,25 @@ $lang->block->spent = 'Has Been Spent';
$lang->block->budget = 'Budget';
$lang->block->left = 'Residuals';
$lang->block->titleList['flowchart'] = 'Flowchart';
$lang->block->titleList['statistic'] = 'Statistic';
$lang->block->titleList['recentproject'] = 'Recent Project';
$lang->block->titleList['assigntome'] = 'Assign To Me';
$lang->block->titleList['projectteam'] = 'Project Team';
$lang->block->titleList['project'] = 'Project';
$lang->block->titleList['dynamic'] = 'Dynamic';
$lang->block->titleList['list'] = 'List';
$lang->block->titleList['contribute'] = 'Contribute';
$lang->block->titleList['scrumoverview'] = 'Scrum Overview';
$lang->block->titleList['scrumtest'] = 'Scrum Test';
$lang->block->titleList['scrumlist'] = 'Scrum List';
$lang->block->titleList['sprint'] = 'Sprint';
$lang->block->titleList['projectdynamic'] = 'Project Dynamic';
$lang->block->titleList['bug'] = 'Bug';
$lang->block->titleList['case'] = 'Case';
$lang->block->titleList['testtask'] = 'Testtask';
$lang->block->default['waterfall']['project']['3']['title'] = 'Plan Gantt Chart';
$lang->block->default['waterfall']['project']['3']['block'] = 'waterfallgantt';
$lang->block->default['waterfall']['project']['3']['source'] = 'project';
+19
View File
@@ -106,6 +106,25 @@ $lang->block->spent = '已花费';
$lang->block->budget = '预算';
$lang->block->left = '剩余';
$lang->block->titleList['flowchart'] = '流程图';
$lang->block->titleList['statistic'] = '项目统计';
$lang->block->titleList['recentproject'] = '我近期参与的项目';
$lang->block->titleList['assigntome'] = '待处理';
$lang->block->titleList['projectteam'] = '项目人力投入';
$lang->block->titleList['project'] = '项目列表';
$lang->block->titleList['dynamic'] = '最新动态';
$lang->block->titleList['list'] = '我的待办';
$lang->block->titleList['contribute'] = '我的贡献';
$lang->block->titleList['scrumoverview'] = '项目概况';
$lang->block->titleList['scrumtest'] = '待测版本';
$lang->block->titleList['scrumlist'] = '迭代列表';
$lang->block->titleList['sprint'] = '迭代总览';
$lang->block->titleList['projectdynamic'] = '最新动态';
$lang->block->titleList['bug'] = '指派给我的Bug';
$lang->block->titleList['case'] = '指派给我的用例';
$lang->block->titleList['testtask'] = '待测版本列表';
$lang->block->default['waterfall']['project']['3']['title'] = "项目计划";
$lang->block->default['waterfall']['project']['3']['block'] = 'waterfallgantt';
$lang->block->default['waterfall']['project']['3']['source'] = 'project';
+2 -2
View File
@@ -35,7 +35,7 @@ $useGuest = $this->app->user->account == 'guest';
<?php $hasHeading = ($block->block != 'welcome');?>
<?php if($hasHeading):?>
<div class='panel-heading'>
<div class='panel-title'><?php echo $block->title;?></div>
<div class='panel-title'><?php echo zget($lang->block->titleList, $block->block);?></div>
<?php endif;?>
<nav class='panel-actions nav nav-default'>
<?php if(!empty($block->moreLink)) echo '<li>' . html::a($block->moreLink, '<i class="icon icon-more"></i>', '', "title='{$lang->more}'") . '</li>'; ?>
@@ -70,7 +70,7 @@ $useGuest = $this->app->user->account == 'guest';
<?php $hasHeading = ($block->block != 'welcome');?>
<?php if($hasHeading):?>
<div class='panel-heading'>
<div class='panel-title'><?php echo $block->title;?></div>
<div class='panel-title'><?php echo zget($lang->block->titleList, $block->block);?></div>
<?php endif;?>
<nav class='panel-actions nav nav-default'>
<?php if(!empty($block->moreLink)) echo '<li>' . html::a($block->moreLink, '<i class="icon icon-more"></i>', '', "title='{$lang->more}'") . '</li>';?>
@@ -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>
+66 -57
View File
@@ -61,7 +61,7 @@ class bug extends control
{
$products = $this->product->getPairs('', 0, 'program_asc');
}
if(empty($products) and !helper::isAjaxRequest()) die($this->locate($this->createLink('product', 'showErrorNone', "moduleName=$tab&activeMenu=bug&objectID=$objectID")));
if(empty($products) and !helper::isAjaxRequest()) return print($this->locate($this->createLink('product', 'showErrorNone', "moduleName=$tab&activeMenu=bug&objectID=$objectID")));
}
else
{
@@ -226,7 +226,7 @@ class bug extends control
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->moduleID = $moduleID;
$this->view->memberPairs = $this->user->getPairs('noletter|nodeleted');
$this->view->memberPairs = $this->user->getPairs('noletter|noclosed');
$this->view->branch = $branch;
$this->view->branchOption = $branchOption;
$this->view->branchTagOption = $branchTagOption;
@@ -345,7 +345,7 @@ class bug extends control
if(isset($fromObjectID))
{
$fromObject = $this->loadModel($fromObjectName)->getById($fromObjectID);
if(!$fromObject) die(js::error($this->lang->notFound) . js::locate('back', 'parent'));
if(!$fromObject) return print(js::error($this->lang->notFound) . js::locate('back', 'parent'));
$this->view->$fromObjectIDKey = $fromObjectID;
$this->view->$fromObjectName = $fromObject;
@@ -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';
@@ -428,7 +428,7 @@ class bug extends control
setcookie('bugModule', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
$location = $this->createLink('bug', 'browse', "productID={$this->post->product}&branch=$branch&browseType=byModule&param={$this->post->module}&orderBy=id_desc");
}
if($this->app->getViewType() == 'xhtml') $location = $this->createLink('bug', 'view', "bugID=$bugID");
if($this->app->getViewType() == 'xhtml') $location = $this->createLink('bug', 'view', "bugID=$bugID", 'html');
$response['locate'] = $location;
return $this->send($response);
}
@@ -550,7 +550,7 @@ class bug extends control
}
$moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch);
if(empty($moduleOptionMenu)) die(js::locate(helper::createLink('tree', 'browse', "productID=$productID&view=story")));
if(empty($moduleOptionMenu)) return print(js::locate(helper::createLink('tree', 'browse', "productID=$productID&view=story")));
/* Get products and projects. */
$products = $this->config->CRProduct ? $this->products : $this->product->getPairs('noclosed', 0, 'program_asc');
@@ -599,6 +599,8 @@ class bug extends control
/* Get executions. */
$executions = array(0 => '');
if(isset($projects[$projectID]) or $this->config->systemMode == 'classic') $executions += $this->product->getExecutionPairsByProduct($productID, $branch ? "0,$branch" : 0, 'id_desc', $projectID);
$execution = $executionID ? $this->loadModel('execution')->getByID($executionID) : '';
$executions = isset($executions[$executionID]) ? $executions : $executions + array($executionID => $execution->name);
/* Set custom. */
foreach(explode(',', $this->config->bug->list->customCreateFields) as $field) $customFields[$field] = $this->lang->bug->$field;
@@ -662,14 +664,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')
@@ -681,8 +684,8 @@ class bug extends control
setcookie('bugModule', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
/* If link from no head then reload. */
if(isonlybody()) die(js::reload('parent.parent'));
die(js::locate($this->createLink('bug', 'browse', "productID={$productID}&branch=$branch&browseType=unclosed&param=0&orderBy=id_desc"), 'parent'));
if(isonlybody()) return print(js::reload('parent.parent'));
return print(js::locate($this->createLink('bug', 'browse', "productID={$productID}&branch=$branch&browseType=unclosed&param=0&orderBy=id_desc"), 'parent'));
}
/* Get product, then set menu. */
@@ -693,8 +696,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 +741,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;
@@ -750,7 +754,7 @@ class bug extends control
$this->view->productID = $productID;
$this->view->stories = $stories;
$this->view->builds = $builds;
$this->view->users = $this->user->getPairs('devfirst|nodeleted');
$this->view->users = $this->user->getPairs('devfirst|noclosed');
$this->view->executions = array('' => '') + $this->product->getExecutionPairsByProduct($productID, $branch ? "0,$branch" : 0, 'id_desc', $projectID);
$this->view->executionID = $executionID;
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $branch === 'all' ? 0 : $branch);
@@ -773,7 +777,7 @@ class bug extends control
/* Judge bug exits or not. */
$bugID = (int)$bugID;
$bug = $this->bug->getById($bugID, true);
if(!$bug) die(js::error($this->lang->notFound) . js::locate($this->createLink('qa', 'index')));
if(!$bug) return print(js::error($this->lang->notFound) . js::locate($this->createLink('qa', 'index')));
$this->session->set('storyList', '', 'product');
$this->session->set('projectList', $this->app->getURI(true) . "#app={$this->app->tab}", 'project');
@@ -856,7 +860,7 @@ class bug extends control
}
else
{
die(js::error(dao::getError()));
return print(js::error(dao::getError()));
}
}
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
@@ -882,12 +886,12 @@ class bug extends control
{
$confirmURL = $this->createLink('task', 'view', "taskID=$bug->toTask");
$cancelURL = $this->server->HTTP_REFERER;
die(js::confirm(sprintf($this->lang->bug->remindTask, $bug->Task), $confirmURL, $cancelURL, 'parent', 'parent'));
return print(js::confirm(sprintf($this->lang->bug->remindTask, $bug->Task), $confirmURL, $cancelURL, 'parent', 'parent'));
}
}
}
if(isonlybody()) die(js::reload('parent.parent'));
die(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
if(isonlybody()) return print(js::reload('parent.parent'));
return print(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
}
/* Get the info of bug, current product and modue. */
@@ -993,7 +997,7 @@ class bug extends control
$this->view->branchTagOption = $branchTagOption;
$this->view->tasks = $this->task->getExecutionTaskPairs($bug->execution);
$this->view->testtasks = $this->loadModel('testtask')->getPairs($bug->product, $bug->execution, $bug->testtask);
$this->view->users = $this->user->getPairs('nodeleted', "$bug->assignedTo,$bug->resolvedBy,$bug->closedBy,$bug->openedBy");
$this->view->users = $this->user->getPairs('noclosed', "$bug->assignedTo,$bug->resolvedBy,$bug->closedBy,$bug->openedBy");
$this->view->openedBuilds = $openedBuilds;
$this->view->resolvedBuilds = array('' => '') + $openedBuilds + $oldResolvedBuild;
$this->view->actions = $this->action->getList('bug', $bugID);
@@ -1035,18 +1039,18 @@ class bug extends control
{
$confirmURL = $this->createLink('task', 'view', "taskID=$bug->toTask");
$cancelURL = $this->server->HTTP_REFERER;
die(js::confirm(sprintf($this->lang->bug->remindTask, $bug->task), $confirmURL, $cancelURL, 'parent', 'parent'));
return print(js::confirm(sprintf($this->lang->bug->remindTask, $bug->task), $confirmURL, $cancelURL, 'parent', 'parent'));
}
}
}
}
die(js::locate($this->session->bugList, 'parent'));
return print(js::locate($this->session->bugList, 'parent'));
}
$bugIDList = $this->post->bugIDList ? $this->post->bugIDList : die(js::locate($this->session->bugList, 'parent'));
$bugIDList = array_unique($bugIDList);
if(!$this->post->bugIDList) return print(js::locate($this->session->bugList, 'parent'));
/* Initialize vars.*/
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where('id')->in($bugIDList)->fetchAll('id');
$bugIDList = array_unique($this->post->bugIDList);
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where('id')->in($bugIDList)->fetchAll('id');
/* The bugs of a product. */
if($productID)
@@ -1129,7 +1133,7 @@ class bug extends control
$appendUsers[$bug->assignedTo] = $bug->assignedTo;
$appendUsers[$bug->resolvedBy] = $bug->resolvedBy;
}
$users = $this->user->getPairs('devfirst|nodeleted', $appendUsers, $this->config->maxCount);
$users = $this->user->getPairs('devfirst|noclosed', $appendUsers, $this->config->maxCount);
$users = array('' => '', 'ditto' => $this->lang->bug->ditto) + $users;
/* Assign. */
@@ -1170,14 +1174,14 @@ class bug extends control
{
$this->loadModel('action');
$changes = $this->bug->assign($bugID);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
$actionID = $this->action->create('bug', $bugID, 'Assigned', $this->post->comment, $this->post->assignedTo);
$this->action->logHistory($actionID, $changes);
$this->executeHooks($bugID);
if(isonlybody()) die(js::closeModal('parent.parent'));
die(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
if(isonlybody()) return print(js::closeModal('parent.parent'));
return print(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
}
if($this->app->tab == 'project')
@@ -1190,7 +1194,7 @@ class bug extends control
}
else
{
$users = $this->user->getPairs('nodeleted|nofeedback', $bug->assignedTo);
$users = $this->user->getPairs('noclosed|nofeedback', $bug->assignedTo);
}
$this->view->title = $this->products[$bug->product] . $this->lang->colon . $this->lang->bug->assignedTo;
@@ -1243,7 +1247,7 @@ class bug extends control
}
$allChanges = $this->bug->batchChangeBranch($bugIDList, $branchID, $oldBugs);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
foreach($allChanges as $bugID => $changes)
{
$this->loadModel('action');
@@ -1252,7 +1256,7 @@ class bug extends control
}
}
$this->loadModel('score')->create('ajax', 'batchOther');
die(js::locate($this->session->bugList, 'parent'));
return print(js::locate($this->session->bugList, 'parent'));
}
/**
@@ -1270,7 +1274,7 @@ class bug extends control
$bugIDList = array_unique($bugIDList);
unset($_POST['bugIDList']);
$allChanges = $this->bug->batchChangeModule($bugIDList, $moduleID);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
foreach($allChanges as $bugID => $changes)
{
$this->loadModel('action');
@@ -1279,7 +1283,7 @@ class bug extends control
}
}
$this->loadModel('score')->create('ajax', 'batchOther');
die(js::locate($this->session->bugList, 'parent'));
return print(js::locate($this->session->bugList, 'parent'));
}
/**
@@ -1297,7 +1301,7 @@ class bug extends control
$bugIDList = array_unique($bugIDList);
unset($_POST['bugIDList']);
$allChanges = $this->bug->batchChangePlan($bugIDList, $planID);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
foreach($allChanges as $bugID => $changes)
{
$this->loadModel('action');
@@ -1306,7 +1310,7 @@ class bug extends control
}
}
$this->loadModel('score')->create('ajax', 'batchOther');
die(js::locate($this->session->bugList, 'parent'));
return print(js::locate($this->session->bugList, 'parent'));
}
/**
@@ -1328,38 +1332,39 @@ class bug extends control
{
$this->loadModel('action');
$changes = $this->bug->assign($bugID);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
$actionID = $this->action->create('bug', $bugID, 'Assigned', $this->post->comment, $this->post->assignedTo);
$this->action->logHistory($actionID, $changes);
}
$this->loadModel('score')->create('ajax', 'batchOther');
}
if($type == 'product' || $type == 'my') die(js::locate($this->session->bugList, 'parent'));
if($type == 'execution') die(js::locate($this->createLink('execution', 'bug', "executionID=$objectID")));
if($type == 'project') die(js::locate($this->createLink('project', 'bug', "projectID=$objectID")));
if($type == 'product' || $type == 'my') return print(js::locate($this->session->bugList, 'parent'));
if($type == 'execution') return print(js::locate($this->createLink('execution', 'bug', "executionID=$objectID")));
if($type == 'project') return print(js::locate($this->createLink('project', 'bug', "projectID=$objectID")));
}
/**
* 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);
if(dao::isError()) die(js::error(dao::getError()));
$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);
$this->executeHooks($bugID);
if(isonlybody()) die(js::closeModal('parent.parent'));
die(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
if(isonlybody()) return print(js::closeModal('parent.parent'));
return print(js::locate($this->createLink('bug', 'view', "bugID=$bugID"), 'parent'));
}
$bug = $this->bug->getById($bugID);
@@ -1372,7 +1377,7 @@ class bug extends control
$this->view->position[] = $this->lang->bug->confirmBug;
$this->view->bug = $bug;
$this->view->users = $this->user->getPairs('nodeleted', $bug->assignedTo);
$this->view->users = $this->user->getPairs('noclosed', $bug->assignedTo);
$this->view->actions = $this->action->getList('bug', $bugID);
$this->display();
}
@@ -1385,27 +1390,29 @@ class bug extends control
*/
public function batchConfirm()
{
$bugIDList = $this->post->bugIDList ? $this->post->bugIDList : die(js::locate($this->session->bugList, 'parent'));
$bugIDList = array_unique($bugIDList);
if(!$this->post->bugIDList) return print(js::locate($this->session->bugList, 'parent'));
$bugIDList = array_unique($this->post->bugIDList);
$this->bug->batchConfirm($bugIDList);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
foreach($bugIDList as $bugID) $this->action->create('bug', $bugID, 'bugConfirmed');
$this->loadModel('score')->create('ajax', 'batchOther');
die(js::locate($this->session->bugList, 'parent'));
return print(js::locate($this->session->bugList, 'parent'));
}
/**
* 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);
@@ -1491,14 +1498,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);
@@ -1522,7 +1530,7 @@ class bug extends control
$this->view->position[] = $this->lang->bug->activate;
$this->view->bug = $bug;
$this->view->users = $this->user->getPairs('nodeleted', $bug->resolvedBy);
$this->view->users = $this->user->getPairs('noclosed', $bug->resolvedBy);
$this->view->builds = $this->loadModel('build')->getBuildPairs($productID, $bug->branch, 'noempty');
$this->view->actions = $this->action->getList('bug', $bugID);
@@ -1533,14 +1541,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);
+3 -2
View File
@@ -421,6 +421,7 @@ function loadProjectBuilds(projectID)
$('#openedBuild_chosen').remove();
$('#openedBuild').next('.picker').remove();
$("#openedBuild").chosen();
notice();
})
}
else
@@ -617,13 +618,13 @@ function notice()
if($('#openedBuild').find('option').length <= 1)
{
var html = '';
if($('#execution').length == 0 || $('#execution').val() == 0)
if(($('#execution').length == 0 || $('#execution').val() == 0) && ($('#project').length == 0 || $('#project').val() == 0))
{
var branch = $('#branch').val();
if(typeof(branch) == 'undefined') branch = 0;
var link = createLink('release', 'create', 'productID=' + $('#product').val() + '&branch=' + branch);
if(config.onlybody != 'yes') link += config.requestType == 'GET' ? '&onlybody=yes' : '?onlybody=yes';
html += '<a href="' + link + '" data-toggle="modal" data-type="iframe" style="padding-right:5px">' + createBuild + '</a> ';
html += '<a href="' + link + '" data-toggle="modal" data-type="iframe" style="padding-right:5px">' + createRelease + '</a> ';
html += '<a href="javascript:loadProductBuilds(' + $('#product').val() + ')">' + refresh + '</a>';
}
else
+68 -13
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);
}
@@ -1901,7 +1956,7 @@ class bugModel extends model
{
$data->name = isset($executions[$executionID]) ? $executions[$executionID] : $this->lang->report->undefined;
$data->title = $data->name;
if(mb_strlen($data->name) > $maxLength) $data->name = mb_substr($data->name, 0, $maxLength) . '...';
if(mb_strlen($data->name, 'UTF-8') > $maxLength) $data->name = mb_substr($data->name, 0, $maxLength, 'UTF-8') . '...';
}
return $datas;
}
+1 -1
View File
@@ -79,7 +79,7 @@
$branchTagOption = array();
$product = $this->product->getByID($bug->product);
$plans = $this->loadModel('productplan')->getPairs($bug->product, $branch);
$branches = $product->type == 'normal' ? array('' => '') : $this->loadModel('branch')->getList($product->id, 0 ,'all');;
$branches = $product->type == 'normal' ? array() : $this->loadModel('branch')->getList($product->id, 0 ,'all');
foreach($branches as $branchInfo)
{
$branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
+2 -2
View File
@@ -121,8 +121,8 @@ $currentBrowseType = isset($lang->bug->mySelects[$browseType]) && in_array($brow
}
else
{
$branch = $branch != 'all' ? $branch : 0;
$createBugLink = $this->createLink('bug', 'create', "productID=$productID&branch=$branch&extra=moduleID=$moduleID");
$selectedBranch = $branch != 'all' ? $branch : 0;
$createBugLink = $this->createLink('bug', 'create', "productID=$productID&branch=$selectedBranch&extra=moduleID=$moduleID");
}
$batchCreateLink = $this->createLink('bug', 'batchCreate', "productID=$productID&branch=$branch&executionID=0&moduleID=$moduleID");
+1 -1
View File
@@ -268,7 +268,7 @@ if($this->app->tab == 'project') js::set('objectID', $bug->project);
<tbody>
<tr class='text-top'>
<th class='thWidth'><?php echo $lang->bug->linkBug;?></th>
<td><?php echo html::a($this->createLink('bug', 'linkBugs', "bugID=$bug->id", '', true), $lang->bug->linkBugs, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'");?></td>
<td><?php if(common::hasPriv('bug', 'linkBugs')) echo html::a($this->createLink('bug', 'linkBugs', "bugID=$bug->id", '', true), $lang->bug->linkBugs, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'");?></td>
</tr>
<tr>
<th></th>
+2 -1
View File
@@ -62,7 +62,8 @@ class build extends control
elseif($this->app->tab == 'qa')
{
$execution = $this->execution->getByID($executionID);
$executions = $this->execution->getPairs($execution->project);
$projectID = $execution ? $execution->project : 0;
$executions = $this->execution->getPairs($projectID);
}
$executionList = empty($executions) ? array() : $this->execution->getByIdList(array_keys($executions));
+1 -1
View File
@@ -5,7 +5,7 @@
.step-actions {width: 100px;}
#steps .active td {transition: background-color .5s;}
#steps .step-steps {resize: none; max-height: 30px;}
#steps .step-expects {display: none;}
#steps .step-expects {resize: none;}
#steps .step-item .step-item-id {display: table-cell; min-width: 40px;}
#steps .step-item .step-id {color: transparent;}
#steps .step-id {font-weight: bold;}
+1 -1
View File
@@ -11,7 +11,7 @@
<td align='center'>
<input type='file' name='file' class='form-control'/>
</td>
<td>
<td class="w-150px">
<?php echo html::select('encode', $config->charsets[$this->cookie->lang], 'utf-8', "class='form-control'");?>
</td>
<td class="w-150px">
+2 -2
View File
@@ -128,7 +128,7 @@ class ci extends control
$productID = $this->dao->findById($firstCase->id)->from(TABLE_CASE)->fetch('product');
}
}
if(empty($productID)) die(json_encode(array('result' => 'fail', 'message' => 'productID is not found')));
if(empty($productID)) return print(json_encode(array('result' => 'fail', 'message' => 'productID is not found')));
/* Get testtaskID or create testtask. */
if(!empty($taskID))
@@ -179,6 +179,6 @@ class ci extends control
$taskID = $this->testtask->processAutoResult($taskID, $productID, $data['suites'], $data['cases'], $data['results'], $data['suiteNames'], $data['caseTitles'], $testType);
die(json_encode(array('result' => 'success')));
return print(json_encode(array('result' => 'success')));
}
}
+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 = '用例';
+2 -2
View File
@@ -532,11 +532,11 @@ 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';
$attr = 'data-toggle="modal" data-target="#guideDialog"';
$attr = 'data-toggle="modal"';
}
else
{
-4
View File
@@ -19,9 +19,5 @@
.dynamic.collapsed .dynamic-btn > .icon:before {content: '\f0d8';}
.c-project, .c-product, .c-execution {width: 110px !important; overflow: unset;}
<<<<<<< HEAD
.c-user,.c-order {width: 90px !important; overflow: unset;}
=======
.c-user, .c-order {width: 90px !important; overflow: unset;}
>>>>>>> xuan
.c-date {width: 200px;}
+27 -27
View File
@@ -1,29 +1,29 @@
<?php
/**
* The html template file of index method of convert module of ZenTaoPMS.
*
<?php
/**
* The html template file of index method of convert module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package ZenTaoPMS
* @version $Id: index.html.php 4129 2013-01-18 01:58:14Z wwccss $
*/
?>
<?php include '../../common/view/header.html.php';?>
<div class='container mw-700px'>
<div id='titlebar'>
<div class='heading'>
<span class='prefix'><?php echo html::icon('cloud-upload');?></span>
<strong><?php echo $lang->convert->common;?></strong>
</div>
</div>
<div class='alert mg-0 bd-0'>
<div class='content'>
<?php echo nl2br($lang->convert->desc);?>
<div class='text-center pdt-20'>
<?php echo html::a($this->createLink('convert', 'selectsource'), $lang->convert->start, '', 'class="btn btn-primary"');?>
</div>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package ZenTaoPMS
* @version $Id: index.html.php 4129 2013-01-18 01:58:14Z wwccss $
*/
?>
<?php include '../../common/view/header.html.php';?>
<div class='container mw-700px'>
<div id='titlebar'>
<div class='heading'>
<span class='prefix'><?php echo html::icon('cloud-upload');?></span>
<strong><?php echo $lang->convert->common;?></strong>
</div>
</div>
<div class='alert mg-0 bd-0'>
<div class='content'>
<?php echo nl2br($lang->convert->desc);?>
<div class='text-center pdt-20'>
<?php echo html::a($this->createLink('convert', 'selectsource'), $lang->convert->start, '', 'class="btn btn-primary"');?>
</div>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+20 -6
View File
@@ -134,9 +134,12 @@ class cron extends control
*/
public function ajaxExec($restart = false)
{
ignore_user_abort(true);
set_time_limit(0);
session_write_close();
if ('cli' !== PHP_SAPI)
{
ignore_user_abort(true);
set_time_limit(0);
session_write_close();
}
/* Check cron turnon. */
if(empty($this->config->global->cron)) die();
@@ -183,11 +186,21 @@ class cron extends control
/* Skip cron that status is running and run time is less than max. */
if($cronInfo->status == 'running' and (time() - strtotime($cronInfo->lastTime)) < $this->config->cron->maxRunTime) continue;
/* Skip cron that last time is more than this cron time. */
if($cronInfo->lastTime > $cron['time']->format(DT_DATETIME1)) die();
if ('cli' === PHP_SAPI)
{
if($cronInfo->lastTime >= $cron['time']->format(DT_DATETIME1)) continue;
}
else
{
if($cronInfo->lastTime > $cron['time']->format(DT_DATETIME1)) die();
}
if($now > $cron['time'])
{
$this->cron->changeStatus($id, 'running');
if (!$this->cron->changeStatusRunning($id, $cronInfo->lastTime))
{
continue;
}
$parsedCrons[$id]['time'] = $cron['cron']->getNextRunDate();
/* Execution command. */
@@ -239,11 +252,12 @@ class cron extends control
sleep($sleepTime);
/* Break while. */
if(connection_status() != CONNECTION_NORMAL) break;
if('cli' !== PHP_SAPI && connection_status() != CONNECTION_NORMAL) break;
if(((time() - $startedTime) / 3600 / 24) >= $this->config->cron->maxRunDays) break;
}
/* Revert cron status to stop. */
$this->cron->markCronStatus('stop', $configID);
}
}
+21
View File
@@ -94,6 +94,27 @@ class cronModel extends model
return dao::isError() ? false : true;
}
/**
* Change cron status to running
*
* @param int $cronID
* @param string $lastTime
* @access public
* @return bool|int
*/
public function changeStatusRunning($cronID, $lastTime)
{
$data = new stdclass();
$data->status = 'running';
$data->lastTime = date(DT_DATETIME1);
$rows = $this->dao->update(TABLE_CRON)->data($data)
->where('id')->eq($cronID)
->andWhere('status')->ne('running')
->andWhere('lastTime')->eq($lastTime)
->exec();
return dao::isError() ? false : $rows;
}
/**
* Log cron.
*
+8 -8
View File
@@ -298,7 +298,7 @@ class design extends control
$repos = $this->loadModel('repo')->getRepoPairs('project', $design->project);
$repoID = $repoID ? $repoID : key($repos);
if(empty($repoID)) die(js::locate(helper::createLink('repo', 'create', "objectID=$design->project")));
if(empty($repoID)) return print(js::locate(helper::createLink('repo', 'create', "objectID=$design->project")));
$repo = $this->loadModel('repo')->getRepoByID($repoID);
$revisions = $this->repo->getCommits($repo, '', 'HEAD', '', '', $begin, $end);
@@ -360,13 +360,13 @@ class design extends control
{
if($confirm == 'no')
{
die(js::confirm($this->lang->design->confirmUnlink, inlink('unlinkCommit', "designID=$designID&commitID=$commitID&confirm=yes")));
return print(js::confirm($this->lang->design->confirmUnlink, inlink('unlinkCommit', "designID=$designID&commitID=$commitID&confirm=yes")));
}
else
{
$this->design->unlinkCommit($designID, $commitID);
die(js::reload('parent'));
return print(js::reload('parent'));
}
}
@@ -446,7 +446,7 @@ class design extends control
{
if($confirm == 'no')
{
die(js::confirm($this->lang->design->confirmDelete, inlink('delete', "designID=$designID&confirm=yes")));
return print(js::confirm($this->lang->design->confirmDelete, inlink('delete', "designID=$designID&confirm=yes")));
}
else
{
@@ -454,7 +454,7 @@ class design extends control
$this->dao->delete()->from(TABLE_RELATION)->where('Atype')->eq('design')->andWhere('AID')->eq($designID)->andWhere('Btype')->eq('commit')->andwhere('relation')->eq('completedin')->exec();
$this->dao->delete()->from(TABLE_RELATION)->where('Atype')->eq('commit')->andWhere('BID')->eq($designID)->andWhere('Btype')->eq('design')->andwhere('relation')->eq('completedfrom')->exec();
die(js::locate($this->session->designList, 'parent'));
return print(js::locate($this->session->designList, 'parent'));
}
}
@@ -470,7 +470,7 @@ class design extends control
if($_POST)
{
$changes = $this->design->assign($designID);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return print(js::error(dao::getError()));
$this->loadModel('action');
if(!empty($changes))
@@ -479,8 +479,8 @@ class design extends control
$this->action->logHistory($actionID, $changes);
}
if(isonlybody()) die(js::closeModal('parent.parent', 'this'));
die(js::locate($this->createLink('design', 'browse'), 'parent'));
if(isonlybody()) return print(js::closeModal('parent.parent', 'this'));
return print(js::locate($this->createLink('design', 'browse'), 'parent'));
}
$design = $this->design->getByID($designID);
+1 -1
View File
@@ -401,7 +401,7 @@ class designModel extends model
* @access public
* @return void
*/
public function setMenu($projectID = 0, $products, $productID = 0)
public function setMenu($projectID, $products, $productID = 0)
{
if($this->app->rawMethod != 'browse') unset($this->lang->waterfall->menu->design['subMenu']->bysearch);
if(empty($products) || !$productID) return '';
+7 -13
View File
@@ -309,7 +309,7 @@ class doc extends control
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $docID));
$objectID = zget($lib, $lib->type, '');
$params = "type={$lib->type}&objectID=$objectID&libID={$lib->id}&docID=" . $docResult['id'];
$link = isonlybody() ? 'parent' : $this->createLink('doc', 'objectLibs', $params);
$link = isonlybody() ? 'parent' : $this->createLink('doc', 'objectLibs', $params, 'html');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link));
}
@@ -791,24 +791,18 @@ 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);
$userList = array();
$project = $this->loadModel('project')->getById($doclib->project);
$projectTeams = $this->loadModel('user')->getTeamMemberPairs($doclib->project);
$stakeholders = $this->loadModel('stakeholder')->getStakeHolderPairs($doclib->project);
foreach($stakeholders as $stakeholder) $userList[$stakeholder] = $users[$stakeholder];
$whitelist = implode(',', array_keys($projectTeams + $stakeholders)) . $project->whitelist . ',' . $project->PM . ',' . $doclib->users;
$userList += $projectTeams;
$whitelist = implode(',', array_keys($userList)) . $project->whitelist . ',' .$project->PM . ',' . $doclib->users;
$selectedUser = $whitelist;
}
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"));
}
return print(html::select('users[]', $users, $selectedUser, "class='form-control chosen' multiple"));
}
/**
+1 -1
View File
@@ -106,7 +106,7 @@ $lang->doc->custom = 'Custom Document Library';
$lang->doc->customAB = 'Custom Doc Lib';
$lang->doc->createLib = 'Document Library';
$lang->doc->allLibs = 'Library List';
$lang->doc->objectLibs = "{$lang->productCommon}/{$lang->executionCommon} Libraries";
$lang->doc->objectLibs = "Document View of Library";
$lang->doc->showFiles = 'Attachments';
$lang->doc->editLib = 'Edit Document Library';
$lang->doc->deleteLib = 'Delete Document Library';
+1 -1
View File
@@ -106,7 +106,7 @@ $lang->doc->custom = '自定义文档库';
$lang->doc->customAB = '自定义库';
$lang->doc->createLib = '创建文档库';
$lang->doc->allLibs = '文档库列表';
$lang->doc->objectLibs = "{$lang->productCommon}/{$lang->executionCommon}库列表";
$lang->doc->objectLibs = "文档库文档详情";
$lang->doc->showFiles = '附件库';
$lang->doc->editLib = '编辑文档库';
$lang->doc->deleteLib = '删除文档库';
+8 -9
View File
@@ -917,11 +917,10 @@ class docModel extends model
if($object->project and $object->acl == 'private')
{
$stakeHolders = array();
$project = $this->loadModel('project')->getById($object->project);
$projectTeams = $this->loadModel('user')->getTeamMemberPairs($object->project);
$stakeHolderList = $this->loadModel('stakeholder')->getStakeHolderPairs($object->project);
foreach($stakeHolderList as $stakeHolder) $stakeHolders[$stakeHolder] = $stakeHolder;
$stakeHolders = array();
$project = $this->loadModel('project')->getById($object->project);
$projectTeams = $this->loadModel('user')->getTeamMemberPairs($object->project);
$stakeHolders = $this->loadModel('stakeholder')->getStakeHolderPairs($object->project);
$authorizedUsers = $this->user->getProjectAuthedUsers($project, $stakeHolders, $projectTeams, array_flip(explode(",", $project->whitelist)));
@@ -1336,9 +1335,9 @@ class docModel extends model
/* Project permissions for DocLib whitelist. */
if($this->app->tab == 'doc')
{
$myObjects = $this->dao->select('t2.id, t2.name')->from(TABLE_DOCLIB)->alias('t1')
->leftjoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
->where("CONCAT(',', t1.users, ',')")->like("%,{$this->app->user->account},%")
$myObjects = $this->dao->select('t1.id, t1.name')->from(TABLE_PROJECT)->alias('t1')
->leftjoin(TABLE_DOCLIB)->alias('t2')->on('t2.project=t1.id')
->where("CONCAT(',', t2.users, ',')")->like("%,{$this->app->user->account},%")
->fetchPairs();
}
@@ -1378,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');
+4
View File
@@ -49,7 +49,11 @@
<?php $star = strpos($doc->collector, ',' . $this->app->user->account . ',') !== false ? 'icon-star text-yellow' : 'icon-star-empty';?>
<?php $collectTitle = strpos($doc->collector, ',' . $this->app->user->account . ',') !== false ? $lang->doc->cancelCollection : $lang->doc->collect;?>
<tr>
<?php if(common::hasPriv('doc', 'view')):?>
<td class="c-name"><?php echo html::a($this->createLink('doc', 'view', "docID=$doc->id&version=0", '', true), "<i class='icon icon-file-text text-muted'></i> &nbsp;" . $doc->title, '', "title='{$doc->title}' class='iframe' data-width='90%'");?></td>
<?php else:?>
<td class='c-name' title='<?php echo $doc->title;?>'><?php echo "<i class='icon icon-file-text text-muted'></i> {$doc->title}";?></td>
<?php endif;?>
<td class='c-name'>
<?php
if(!empty($doc->objectType))
+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');
+131 -20
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';
}
/**
@@ -124,10 +123,13 @@ class execution extends control
/* Set browse type. */
$browseType = strtolower($status);
/* Get products by execution. */
$execution = $this->commonAction($executionID, $status);
$executionID = $execution->id;
$products = $this->product->getProductPairsByProject($executionID);
if($execution->type == 'kanban') $this->locate($this->createLink('execution', 'kanban', "executionID=$executionID"));
/* Get products by execution. */
$products = $this->product->getProductPairsByProject($executionID);
setcookie('preExecutionID', $executionID, $this->config->cookieLife, $this->config->webRoot, '', false, true);
/* Save the recently five executions visited in the cookie. */
@@ -1263,6 +1265,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 +1300,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 +1388,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 +1452,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 +1474,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 +1533,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 +1541,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]);
@@ -1525,6 +1555,7 @@ class execution extends control
$this->loadModel('productplan');
$productPlans = array(0 => '');
$linkedBranches = array();
$linkedBranchList = array();
$linkedProducts = $this->product->getProducts($executionID);
$branches = $this->project->getBranchesByProject($executionID);
$plans = $this->productplan->getGroupByProduct(array_keys($linkedProducts), 'skipParent');
@@ -1538,6 +1569,7 @@ class execution extends control
if(!isset($allProducts[$productID])) $allProducts[$productID] = $linkedProduct->name;
foreach($branches[$productID] as $branchID => $branch)
{
$linkedBranchList[$branchID] = $branchID;
$linkedBranches[$productID][$branchID] = $branchID;
$productPlans[$productID][$branchID] = isset($plans[$productID][$branchID]) ? $plans[$productID][$branchID] : array();
if($branchID != BRANCH_MAIN and isset($plans[$productID][BRANCH_MAIN])) $productPlans[$productID][$branchID] += $plans[$productID][BRANCH_MAIN];
@@ -1581,7 +1613,7 @@ class execution extends control
$this->view->unmodifiableBranches = $unmodifiableBranches;
$this->view->multiBranchProducts = $this->product->getMultiBranchPairs();
$this->view->productPlans = $productPlans;
$this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($linkedProducts), $this->config->systemMode == 'new' ? $execution->project : 0);
$this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($linkedProducts), $this->config->systemMode == 'new' ? $execution->project : 0, 'noclosed', $linkedBranchList);
$this->display();
}
@@ -1683,6 +1715,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))
{
@@ -1755,6 +1788,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))
{
@@ -1790,6 +1824,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))
{
@@ -1832,6 +1867,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))
{
@@ -1920,6 +1956,67 @@ 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->user->getAvatarPairs();
foreach($avatarPairs as $account => $avatar)
{
if(!isset($users[$account])) continue;
$userList[$account]['realname'] = $users[$account];
$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
@@ -1927,18 +2024,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');
@@ -2301,6 +2396,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;
@@ -2308,6 +2409,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);
@@ -2316,7 +2418,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'));
}
}
@@ -2526,16 +2628,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");
@@ -2550,7 +2653,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'));
@@ -2870,7 +2973,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')
@@ -3282,10 +3385,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);
@@ -3308,7 +3412,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);
}
@@ -3319,7 +3423,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)));
}
@@ -3438,7 +3549,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;}
+90 -41
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,13 @@
.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 .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;}
.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;}
#mainMenu .divider {margin: 10px}
+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;
+74 -36
View File
@@ -63,16 +63,21 @@ class executionModel extends model
*/
public function setMenu($executionID, $buildID = 0, $extra = '')
{
$execution = $this->getByID($executionID);
if($execution and $execution->type == 'kanban')
{
$this->lang->execution->menu = new stdclass();
$this->lang->execution->accessDenied = str_replace($this->lang->executionCommon, $this->lang->execution->kanban, $this->lang->execution->accessDenied);
}
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 == 'stage' and $this->config->systemMode == 'new')
{
global $lang;
@@ -88,15 +93,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 +308,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 +383,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 +476,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 +527,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('account')->ne($oldExecution->openedBy)->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 +1058,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 +1206,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 +1232,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 +1323,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 +1333,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';
@@ -1674,15 +1678,16 @@ class executionModel extends model
/**
* Get branch pairs by product id list.
*
* @param array $products
* @param int $projectID
* @param string $param
* @param array $products
* @param int $projectID
* @param string $param
* @param string|array $appendBranch
* @access public
* @return array
*/
public function getBranchByProduct($products, $projectID = 0, $param = 'noclosed')
public function getBranchByProduct($products, $projectID = 0, $param = 'noclosed', $appendBranch = '')
{
$branchGroups = $this->loadModel('branch')->getByProducts($products, $param);
$branchGroups = $this->loadModel('branch')->getByProducts($products, $param, $appendBranch);
if($projectID)
{
@@ -2267,30 +2272,39 @@ 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;
if(empty($stories)) return false;
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];
@@ -2306,6 +2320,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');
}
/**
@@ -2477,6 +2493,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.
*
@@ -3020,13 +3053,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;
}
@@ -3210,13 +3246,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')
@@ -3225,6 +3262,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)):?>
+7
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>
@@ -172,6 +175,7 @@
</div>
</td>
</tr>
<?php if(empty($project) or $project->model != 'kanban'):?>
<tr>
<th><?php echo $lang->execution->teamname;?></th>
<td><?php echo html::input('team', $team, "class='form-control'");?></td>
@@ -181,6 +185,7 @@
<th><?php echo $lang->execution->copyTeam;?></th>
<td><?php echo html::select('teams', $teams, $copyExecutionID, "class='form-control chosen' data-placeholder='{$lang->execution->copyTeamTip}'"); ?></td>
</tr>
<?php endif;?>
<tr>
<th rowspan='2'><?php echo $lang->execution->owner;?></th>
<td>
@@ -210,10 +215,12 @@
</div>
</td>
</tr>
<?php if(empty($project) or $project->model != 'kanban'):?>
<tr>
<th><?php echo $lang->execution->team;?></th>
<td colspan='3'><?php echo html::select('teamMembers[]', $users, '', "class='form-control chosen' multiple"); ?></td>
</tr>
<?php endif;?>
<tr>
<th><?php echo (($from == 'execution') and ($config->systemMode == 'new')) ? $lang->execution->execDesc : $lang->execution->desc;?></th>
<td colspan='3'>
+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
@@ -87,7 +87,7 @@ $dataType = '';
</div>
<?php if($col == 'story' or $dataType == 'bug'):?>
<?php $desc = $col == 'story' ? $storySpecs[$content->id]->spec : $content->steps;?>
<div class='board-content'><?php echo mb_substr(strip_tags($desc, "<p><br/>"), 0, 90, 'utf8')?></div>
<div class='board-content'><?php echo mb_substr(strip_tags($desc, "<p><br>"), 0, 90, 'utf8')?></div>
<?php endif;?>
</div>
<div class="board-footer <?php echo $col == 'story' ? 'story' : 'task'?>">
+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';?>
+1 -1
View File
@@ -23,7 +23,7 @@
<?php if($removeCommands):?>
<div class='container mw-500px'>
<p><strong><?php echo $lang->extension->unremovedFiles;?></strong></p>
<code><?php echo join($removeCommands, '<br />');?></code>
<code><?php echo join('<br />', $removeCommands);?></code>
</div>
<?php endif;?>
<hr>
+1 -1
View File
@@ -26,7 +26,7 @@
<h3><?php echo $title;?></h3>
<?php if($removeCommands):?>
<p><strong><?php echo $lang->extension->unremovedFiles;?></strong></p>
<p><?php echo join($removeCommands, '<br />');?></p>
<p><?php echo join('<br />', $removeCommands);?></p>
<?php endif;?>
<p class='text-center'><?php echo html::commonButton($lang->extension->viewAvailable, 'onclick=parent.location.href="' . inlink('browse', 'type=available') . '"');?></p>
</div>
+1 -1
View File
@@ -51,7 +51,7 @@
if($removeCommands)
{
echo "<p class='strong'>{$lang->extension->unremovedFiles}</p>";
echo join($removeCommands, '<br />');
echo join('<br />', $removeCommands);
}
echo "<p class='text-center'>" . html::commonButton($lang->extension->viewAvailable, 'onclick=parent.location.href="' . inlink('browse', 'type=available') . '"') . '</p>';
?>
+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
@@ -501,7 +501,7 @@ class groupModel extends model
$data = new stdclass();
$data->group = $groupID;
$data->account = $account;
$data->project = implode($programs[$account], ',');
$data->project = implode(',', $programs[$account]);
$this->dao->replace(TABLE_USERGROUP)->data($data)->exec();
foreach($programs[$account] as $programID)
+2 -2
View File
@@ -95,7 +95,7 @@ class holiday extends control
{
if($confirm == 'no')
{
die(js::confirm($this->lang->holiday->confirmDelete, inLink('delete', "id=$id&confirm=yes")));
return print(js::confirm($this->lang->holiday->confirmDelete, inLink('delete', "id=$id&confirm=yes")));
}
else
{
@@ -111,7 +111,7 @@ class holiday extends control
$this->holiday->updateTaskRealDuration($holidayInformation->begin, $holidayInformation->end);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
die(js::reload('parent'));
return print(js::reload('parent'));
}
}
}
+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'>
+105 -34
View File
@@ -28,12 +28,13 @@ class kanban extends control
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->view->title = $this->lang->kanbanspace->common;
$this->view->spaceList = $this->kanban->getSpaceList($browseType, $pager);
$this->view->browseType = $browseType;
$this->view->pager = $pager;
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->usersAvatar = $this->user->getAvatarPairs();
$this->view->title = $this->lang->kanbanspace->common;
$this->view->spaceList = $this->kanban->getSpaceList($browseType, $pager);
$this->view->unclosedSpace = $this->kanban->getCanViewObjects('kanbanspace', 'noclosed');
$this->view->browseType = $browseType;
$this->view->pager = $pager;
$this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted');
$this->view->usersAvatar = $this->user->getAvatarPairs();
$this->display();
}
@@ -293,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'));
@@ -416,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))
{
@@ -430,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');
}
/**
@@ -457,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.
*
@@ -469,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
{
@@ -709,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));
@@ -915,17 +955,10 @@ class kanban extends control
*/
public function setColumn($columnID, $executionID = 0, $from = 'kanban')
{
$column = $this->kanban->getColumnById($columnID);
$column = $this->kanban->getColumnByID($columnID);
if($_POST)
{
/* Check lane column name is unique. */
$exist = $this->kanban->getColumnByName($this->post->name, $column->lane);
if($exist and $exist->id != $columnID and $from != 'kanban')
{
return $this->sendError($this->lang->kanban->noColumnUniqueName);
}
$changes = $this->kanban->updateLaneColumn($columnID, $column);
if(dao::isError()) return $this->sendError(dao::getError());
if($changes)
@@ -991,26 +1024,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));
}
@@ -1083,4 +1138,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'"));
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
.pri-selector .br-0 {padding-bottom: 5px;}
.required:after {left: 213px; z-index: 2;}
.pri-selector {z-index: 3;}
#estimate, .pri-selector {z-index: 3;}
.input-group > #name {padding-right: 16px;}
+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);
})
+93 -9
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);
}
/**
@@ -714,7 +718,7 @@ function createCardMenu(options)
{
if(moveColumns[i].parent > 0) parentColumns.push(moveColumns[i].parent);
if(moveColumns[i].id == card.column || $.inArray(moveColumns[i].id, parentColumns) >= 0) continue;
moveCardItems.push({label: moveColumns[i].name, onClick: function(){moveCard(card.id, moveColumns[i].id, card.lane, card.kanban, card.region);}});
moveCardItems.push({label: moveColumns[i].name, onClick: function(){moveCard(card.id, card.column, moveColumns[i].id, card.lane, card.lane, card.kanban, card.region);}});
}
moveCardItems = moveCardItems.reverse();
items.push({label: kanbanLang.moveCard, icon: 'move', items: moveCardItems});
@@ -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);
}
}
+16 -4
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)';
@@ -135,7 +140,6 @@ $lang->kanban->laneColor = 'Lane Color';
$lang->kanban->setColumn = 'Column Settings';
$lang->kanban->columnName = 'Column Name';
$lang->kanban->columnColor = 'Column Color';
$lang->kanban->noColumnUniqueName = 'The Kanban column name already exists.';
$lang->kanban->moveUp = 'Swimlane Up';
$lang->kanban->moveDown = 'Swimlane Down';
$lang->kanban->laneMove = 'Swimlane Move';
@@ -244,11 +248,19 @@ $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 as other lanes';
$lang->kanbanlane->modeList['independent'] = 'Independent Kanban column is adopted';
$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->kanbanregion = new stdclass();
$lang->kanbanregion->name = 'Region Name';
+14 -2
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'] = '私有(看板团队成员、白名单、空间负责人可访问)';
@@ -135,7 +140,6 @@ $lang->kanban->laneColor = '泳道颜色';
$lang->kanban->setColumn = '看板列设置';
$lang->kanban->columnName = '看板列名称';
$lang->kanban->columnColor = '看板列颜色';
$lang->kanban->noColumnUniqueName = '看板列名称已存在';
$lang->kanban->moveUp = '泳道上移';
$lang->kanban->moveDown = '泳道下移';
$lang->kanban->laneMove = '泳道移动';
@@ -244,12 +248,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 = '默认区域';
+820 -287
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -33,7 +33,7 @@
</tr>
<tr>
<th><?php echo $lang->kanban->owner;?></th>
<td><?php echo html::select('owner', $users, '', "class='form-control chosen'");?></td>
<td><?php echo html::select('owner', $users, '', "class='form-control chosen' data-drop_direction='down'");?></td>
</tr>
<tr>
<th><?php echo $lang->kanban->team;?></th>
+1 -1
View File
@@ -36,7 +36,7 @@
<div class='table-col w-120px'>
<div class="input-group">
<span class="input-group-addon fix-border br-0"><?php echo $lang->kanbancard->estimate;?></span>
<input type="text" name="estimate" id="estimate" class="form-control" autocomplete="off" placeholder=<?php echo $lang->kanbancard->lblHour;?>>
<?php echo html::input('estimate', '', "class='form-control' placeholder='{$lang->kanbancard->lblHour}' autocomplete='off'");?>
</div>
</div>
</div>
+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';?>
+1 -1
View File
@@ -33,7 +33,7 @@
</tr>
<tr>
<th><?php echo $lang->kanban->owner;?></th>
<td><?php echo html::select('owner', $users, $kanban->owner, "class='form-control chosen'");?></td>
<td><?php echo html::select('owner', $users, $kanban->owner, "class='form-control chosen' data-drop_direction='down'");?></td>
</tr>
<tr>
<th><?php echo $lang->kanban->team;?></th>
+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';?>
+6 -4
View File
@@ -21,7 +21,7 @@
<?php endforeach;?>
</div>
<div class="btn-toolbar pull-right">
<?php common::printLink('kanban', 'create', '', '<i class="icon icon-plus"></i> ' . $lang->kanban->create, '', 'class="btn btn-secondary iframe" data-width="75%"', '', true);?>
<?php if(!empty($unclosedSpace)) common::printLink('kanban', 'create', '', '<i class="icon icon-plus"></i> ' . $lang->kanban->create, '', 'class="btn btn-secondary iframe" data-width="75%"', '', true);?>
<?php common::printLink('kanban', 'createSpace', '', '<i class="icon icon-plus"></i> ' . $lang->kanban->createSpace, '', 'class="btn btn-primary iframe" data-width="75%"', '', true);?>
</div>
</div>
@@ -50,7 +50,7 @@
</div>
<div class='spaceActions pull-right'>
<?php $class = $space->status == 'closed' ? 'disabled' : '';?>
<?php common::printLink('kanban', 'create', "spaceID={$space->id}", '<i class="icon icon-plus"></i> ' . $lang->kanban->create, '', "class='iframe' data-width='75%'", '', true);?>
<?php if($space->status != 'closed') common::printLink('kanban', 'create', "spaceID={$space->id}", '<i class="icon icon-plus"></i> ' . $lang->kanban->create, '', "class='iframe' data-width='75%'", '', true);?>
<?php common::printLink('kanban', 'editSpace', "spaceID={$space->id}", '<i class="icon icon-cog-outline"></i> ' . $lang->kanban->setting, '', "class='iframe' data-width='75%'", '', true);?>
<?php common::printLink('kanban', 'closeSpace', "spaceID={$space->id}", '<i class="icon icon-off"></i> ' . $lang->close, '', "class='iframe {$class}'", '', true);?>
<?php common::printLink('kanban', 'deleteSpace', "spaceID={$space->id}", '<i class="icon icon-trash"></i> ' . $lang->delete, 'hiddenwin', '', '', true);?>
@@ -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(
@@ -22,6 +22,7 @@
#archivedCards .kanban-item > .info > .user {position: absolute; right: 0; top: 0}
#archivedCards .card-actions {position: relative; padding: 15px 10px; padding-top: 0px;}
#archivedCards .card-actions > .btn {display: block;}
[lang^='en'] #archivedCards .card-actions > .btn {width: 55px;}
#archivedCards .card-actions > .btn + .btn {margin-top: 10px;}
#archivedCards .info > .time {background-color: rgba(0, 0, 0, 0.15);}
#archivedCards .info > .users > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position:sticky; top: 3px; margin: 0 7px; border-radius: 50%; line-height: 32px;}
+4
View File
@@ -94,6 +94,8 @@ $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';
$lang->misc->releaseDate['15.7.1'] = '2021-11-02';
@@ -164,6 +166,8 @@ $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' => '');
$lang->misc->feature->all['15.7.1'][] = array('title' => 'Fix bug.', 'desc' => '');
+4
View File
@@ -94,6 +94,8 @@ $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';
$lang->misc->releaseDate['15.7.1'] = '2021-11-02';
@@ -164,6 +166,8 @@ $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' => '');
$lang->misc->feature->all['15.7.1'][] = 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'>
+5 -2
View File
@@ -207,8 +207,11 @@ class mr extends control
if(isset($MR->gitlabID)) $rawMR = $this->mr->apiGetSingleMR($MR->gitlabID, $MR->targetProject, $MR->mriid);
if($MR->synced and (!isset($rawMR->id) or (isset($rawMR->message) and $rawMR->message == '404 Not found') or empty($rawMR))) return $this->display();
$MR = $this->mr->apiSyncMR($MR); /* Sync MR from GitLab to ZentaoPMS. */
$this->loadModel('gitlab');
$this->loadModel('job');
/* Sync MR from GitLab to ZentaoPMS. */
$MR = $this->mr->apiSyncMR($MR);
$sourceProject = $this->gitlab->apiGetSingleProject($MR->gitlabID, $MR->sourceProject);
$targetProject = $this->gitlab->apiGetSingleProject($MR->gitlabID, $MR->targetProject);
$sourceBranch = $this->gitlab->apiGetSingleBranch($MR->gitlabID, $MR->sourceProject, $MR->sourceBranch);
@@ -228,7 +231,7 @@ class mr extends control
$product = $this->mr->getMRProduct($MR);
$this->view->compile = $this->loadModel('compile')->getById($MR->compileID);
$this->view->compileJob = $MR->jobID ? $this->loadModel('job')->getById($MR->jobID) : false;
$this->view->compileJob = $MR->jobID ? $this->job->getById($MR->jobID) : false;
$this->view->title = $this->lang->mr->view;
$this->view->MR = $MR;
-1
View File
@@ -139,7 +139,6 @@ $lang->mr->hasNoConflict = "可以合并";
$lang->mr->acceptMR = "合并";
$lang->mr->mergeFailed = "无法合并,请核对合并请求状态";
$lang->mr->mergeSuccess = "已成功合并";
$lang->mr->mergeSuccess = "已成功合并";
$lang->mr->todomessage = "项目中指派给你了";
+2 -2
View File
@@ -106,10 +106,10 @@
<div class='main-actions'>
<div class="btn-toolbar">
<?php common::printBack(inlink('browse', '')); ?>
<?php $acceptDisabled = ($MR->approvalStatus != 'approved' or ($MR->compileID != 0 and $MR->compileStatus != 'success')) ? ' disabled' : ''; ?>
<?php $acceptDisabled = ($MR->approvalStatus != 'approved' or (!empty($compile->id) and $compile->status != 'success')) ? ' disabled' : ''; ?>
<?php if($MR->synced and $rawMR->state == 'opened' and !$rawMR->has_conflicts) common::printIcon('mr', 'accept', "mr=$MR->id", $MR, 'button', 'flow', 'hiddenwin', 'mergeButton btn', false, $acceptDisabled, $lang->mr->acceptMR);?>
<?php if($MR->synced and $rawMR->state == 'opened'): ?>
<?php if($rawMR->has_conflicts or ($MR->compileID != 0 and $MR->compileStatus != 'success') or $MR->approvalStatus == 'approved'):?>
<?php if($rawMR->has_conflicts or (!empty($compile->id) and $compile->status != 'success') or $MR->approvalStatus == 'approved'):?>
<?php common::printIcon('mr', 'approval', "mr=$MR->id&action=approve", $MR, 'button', 'ok', 'hiddenwin', 'mergeButton', true, 'disabled', $lang->mr->approve);?>
<?php else:?>
<?php common::printIcon('mr', 'approval', "mr=$MR->id&action=approve", $MR, 'button', 'ok', 'hiddenwin', 'mergeButton btn iframe showinonlybody', true, '', $lang->mr->approve);?>
+20 -20
View File
@@ -154,10 +154,9 @@ class my extends control
$reviewCount = 0;
$ncCount = 0;
$meetingCount = 0;
$isMaxVersion = isset($this->config->maxVersion) ? 1 : 0;
if($isMaxVersion)
$isMax = isset($this->config->maxVersion) ? 1 : 0;
if($isMax)
{
$this->loadModel('issue');
$this->loadModel('risk');
$this->loadModel('review');
@@ -195,8 +194,8 @@ var testTaskCount = $testTaskCount;
var isOpenedURAndSR = $isOpenedURAndSR;
if(isOpenedURAndSR !== 0) var requirementCount = $requirementCount;
var isMaxVersion = $isMaxVersion;
if(isMaxVersion !== 0)
var isMax = $isMax;
if(isMax !== 0)
{
var issueCount = $issueCount;
var riskCount = $riskCount;
@@ -444,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;
@@ -973,13 +973,13 @@ EOF;
*/
public function changePassword()
{
if($this->app->user->account == 'guest') die(js::alert('guest') . js::locate('back'));
if($this->app->user->account == 'guest') return print(js::alert('guest') . js::locate('back'));
if(!empty($_POST))
{
$this->user->updatePassword($this->app->user->id);
if(dao::isError()) die(js::error(dao::getError()));
if(isonlybody()) die(js::closeModal('parent.parent', 'this'));
die(js::locate($this->createLink('my', 'index'), 'parent.parent'));
if(dao::isError()) return print(js::error(dao::getError()));
if(isonlybody()) return print(js::closeModal('parent.parent', 'this'));
return print(js::locate($this->createLink('my', 'index'), 'parent.parent'));
}
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->changePassword;
@@ -1007,14 +1007,14 @@ EOF;
{
$listID = $this->user->createContactList($data->newList, $data->users);
$this->user->setGlobalContacts($listID, isset($data->share));
if(isonlybody()) die(js::closeModal('parent.parent', '', ' function(){parent.parent.ajaxGetContacts(\'#mailto\')}'));
die(js::locate(inlink('manageContacts', "listID=$listID"), 'parent'));
if(isonlybody()) return print(js::closeModal('parent.parent', '', ' function(){parent.parent.ajaxGetContacts(\'#mailto\')}'));
return print(js::locate(inlink('manageContacts', "listID=$listID"), 'parent'));
}
elseif($data->mode == 'edit')
{
$this->user->updateContactList($data->listID, $data->listName, $data->users);
$this->user->setGlobalContacts($data->listID, isset($data->share));
die(js::locate(inlink('manageContacts', "listID={$data->listID}"), 'parent'));
return print(js::locate(inlink('manageContacts', "listID={$data->listID}"), 'parent'));
}
}
@@ -1075,19 +1075,19 @@ EOF;
{
if($confirm == 'no')
{
die(js::confirm($this->lang->user->contacts->confirmDelete, inlink('deleteContacts', "listID=$listID&confirm=yes")));
return print(js::confirm($this->lang->user->contacts->confirmDelete, inlink('deleteContacts', "listID=$listID&confirm=yes")));
}
else
{
$this->user->deleteContactList($listID);
die(js::locate(inlink('manageContacts'), 'parent'));
return print(js::locate(inlink('manageContacts'), 'parent'));
}
}
/**
* Build contact lists.
*
* @param dropdownName
* @param string $dropdownName
* @access public
* @return void
*/
@@ -1106,7 +1106,7 @@ EOF;
*/
public function profile()
{
if($this->app->user->account == 'guest') die(js::alert('guest') . js::locate('back'));
if($this->app->user->account == 'guest') return print(js::alert('guest') . js::locate('back'));
$this->app->loadConfig('user');
$this->app->loadLang('user');
@@ -1136,9 +1136,9 @@ EOF;
foreach($_POST as $key => $value) $this->setting->setItem("{$this->app->user->account}.common.$key", $value);
$this->setting->setItem("{$this->app->user->account}.common.preferenceSetted", 1);
if(isOnlybody()) die(js::closeModal('parent.parent'));
if(isOnlybody()) return print(js::closeModal('parent.parent'));
die(js::locate($this->createLink('my', 'index'), 'parent'));
return print(js::locate($this->createLink('my', 'index'), 'parent'));
}
$this->view->title = $this->lang->my->common . $this->lang->colon . $this->lang->my->preference;
@@ -1254,12 +1254,12 @@ EOF;
$this->loadModel('user');
if($confirm == 'no')
{
die(js::confirm($this->lang->user->confirmUnbind, $this->createLink('my', 'unbind', "confirm=yes")));
return print(js::confirm($this->lang->user->confirmUnbind, $this->createLink('my', 'unbind', "confirm=yes")));
}
else
{
$this->user->unbind($this->app->user->account);
die(js::locate($this->createLink('my', 'profile'), 'parent'));
return print(js::locate($this->createLink('my', 'profile'), 'parent'));
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ $(function()
if(isOpenedURAndSR !== 0) $("#subNavbar li[data-id='requirement'] a").append('<span class="label label-light label-badge">' + requirementCount + '</span>');
if(isMaxVersion !== 0)
if(isMax !== 0)
{
$("#subNavbar li[data-id='issue'] a").append('<span class="label label-light label-badge">' + issueCount + '</span>');
$("#subNavbar li[data-id='risk'] a").append('<span class="label label-light label-badge">' + riskCount + '</span>');

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