Merge branch 'kanban' into 'sprint/177_liumengyi_45823'

# Conflicts:
#   module/kanban/control.php
This commit is contained in:
孙广明
2021-12-09 06:41:49 +00:00
103 changed files with 1941 additions and 249 deletions
+8
View File
@@ -237,9 +237,13 @@ define('TABLE_RELATION', '`' . $config->db->prefix . 'relation`');
define('TABLE_REPOHISTORY', '`' . $config->db->prefix . 'repohistory`');
define('TABLE_REPOFILES', '`' . $config->db->prefix . 'repofiles`');
define('TABLE_REPOBRANCH', '`' . $config->db->prefix . 'repobranch`');
define('TABLE_KANBAN', '`' . $config->db->prefix . 'kanban`');
define('TABLE_KANBANSPACE', '`' . $config->db->prefix . 'kanbanspace`');
define('TABLE_KANBANREGION', '`' . $config->db->prefix . 'kanbanregion`');
define('TABLE_KANBANLANE', '`' . $config->db->prefix . 'kanbanlane`');
define('TABLE_KANBANCOLUMN', '`' . $config->db->prefix . 'kanbancolumn`');
define('TABLE_KANBANORDER', '`' . $config->db->prefix . 'kanbanorder`');
define('TABLE_KANBANGROUP', '`' . $config->db->prefix . 'kanbangroup`');
if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`');
if(!defined('TABLE_PROJECTSPEC')) define('TABLE_PROJECTSPEC', '`' . $config->db->prefix . 'projectspec`');
@@ -278,9 +282,13 @@ $config->objectTables['job'] = TABLE_JOB;
$config->objectTables['team'] = TABLE_TEAM;
$config->objectTables['pipeline'] = TABLE_PIPELINE;
$config->objectTables['mr'] = TABLE_MR;
$config->objectTables['kanban'] = TABLE_KANBAN;
$config->objectTables['kanbanspace'] = TABLE_KANBANSPACE;
$config->objectTables['kanbanregion'] = TABLE_KANBANREGION;
$config->objectTables['kanbancolumn'] = TABLE_KANBANCOLUMN;
$config->objectTables['kanbanlane'] = TABLE_KANBANLANE;
$config->objectTables['kanbanorder'] = TABLE_KANBANORDER;
$config->objectTables['kanbangroup'] = TABLE_KANBANGROUP;
$config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme');
@@ -100,7 +100,7 @@ CREATE TABLE `zt_apistruct`
`attribute` text NULL,
`addedBy` varchar(30) NOT NULL DEFAULT 0,
`addedDate` datetime NOT NULL,
`editEdBy` varchar(30) NOT NULL DEFAULT 0,
`editedBy` varchar(30) NOT NULL DEFAULT 0,
`editedDate` datetime NOT NULL,
`deleted` enum ('0', '1') NOT NULL DEFAULT '0',
primary key (`id`)
@@ -177,6 +177,8 @@ CREATE TABLE `zt_bug` (
`confirmed` tinyint(1) NOT NULL DEFAULT 0,
`activatedCount` smallint(6) NOT NULL,
`activatedDate` datetime NOT NULL,
`feedbackBy` varchar(100) NOT NULL,
`notifyEmail` varchar(100) NOT NULL,
`mailto` text DEFAULT NULL,
`openedBy` varchar(30) NOT NULL DEFAULT '',
`openedDate` datetime NOT NULL,
@@ -379,6 +381,37 @@ CREATE TABLE `zt_dept` (
KEY `parent` (`parent`),
KEY `path` (`path`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_design` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`project` varchar(255) NOT NULL,
`product` varchar(255) NOT NULL,
`commit` text NOT NULL,
`commitedBy` varchar(30) NOT NULL,
`execution` mediumint(8) unsigned NOT NULL DEFAULT '0',
`name` varchar(255) NOT NULL,
`status` varchar(30) NOT NULL,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
`assignedTo` varchar(30) NOT NULL,
`assignedBy` varchar(30) NOT NULL,
`assignedDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
`story` char(30) NOT NULL,
`desc` text NOT NULL,
`version` smallint(6) NOT NULL,
`type` char(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_designspec` (
`design` mediumint(8) NOT NULL,
`version` smallint(6) NOT NULL,
`name` varchar(255) NOT NULL,
`desc` text NOT NULL,
`files` varchar(255) NOT NULL,
UNIQUE KEY `design` (`design`,`version`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_doc` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`project` mediumint(8) unsigned NOT NULL,
@@ -545,6 +578,18 @@ CREATE TABLE `zt_history` (
PRIMARY KEY (`id`),
KEY `action` (`action`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_holiday` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL DEFAULT '',
`type` enum('holiday', 'working') NOT NULL DEFAULT 'holiday',
`desc` text NOT NULL,
`year` char(4) NOT NULL,
`begin` date NOT NULL,
`end` date NOT NULL,
PRIMARY KEY (`id`),
KEY `year` (`year`),
KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_job` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
@@ -570,6 +615,32 @@ CREATE TABLE `zt_job` (
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_kanbancolumn` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`lane` mediumint(8) NOT NULL DEFAULT '0',
`parent` mediumint(8) NOT NULL DEFAULT '0',
`type` char(30) NOT NULL,
`name` varchar(255) NOT NULL DEFAULT '',
`color` char(30) NOT NULL,
`limit` smallint(6) NOT NULL DEFAULT '-1',
`order` mediumint(8) NOT NULL DEFAULT '0',
`cards` text NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_kanbanlane` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`execution` mediumint(8) NOT NULL DEFAULT '0',
`type` char(30) NOT NULL,
`groupby` char(30) NOT NULL,
`extra` char(30) NOT NULL,
`name` varchar(255) NOT NULL DEFAULT '',
`color` char(30) NOT NULL,
`order` smallint(6) NOT NULL DEFAULT '0',
`lastEditedTime` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_lang` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`lang` varchar(30) NOT NULL,
@@ -633,6 +704,23 @@ CREATE TABLE `zt_mr` (
`deleted` enum('0','1') NOT NULL DEFAULT '0',
`status` char(30) NOT NULL,
`mergeStatus` char(30) NOT NULL,
`approver` varchar(255) NOT NULL,
`approvalStatus` char(30) NOT NULL,
`needApproved` enum('0','1') NOT NULL DEFAULT '0',
`needCI` enum('0','1') NOT NULL DEFAULT '0',
`repoID` mediumint(8) unsigned NOT NULL,
`jobID` mediumint(8) unsigned NOT NULL,
`compileID` mediumint(8) unsigned NOT NULL,
`compileStatus` char(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_mrapproval` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`mrID` mediumint(8) unsigned NOT NULL,
`account` varchar(255) NOT NULL,
`date` datetime NOT NULL,
`action` char(30) NOT NULL,
`comment` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_notify` (
@@ -699,6 +787,7 @@ CREATE TABLE `zt_product` (
`RD` varchar(30) NOT NULL,
`acl` enum('open','private','custom') NOT NULL DEFAULT 'open',
`whitelist` text NOT NULL,
`reviewer` varchar(255) NOT NULL,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`createdVersion` varchar(20) NOT NULL,
@@ -941,6 +1030,18 @@ CREATE TABLE `zt_searchindex` (
FULLTEXT KEY `content` (`content`),
FULLTEXT KEY `title` (`title`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_stage` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`percent` varchar(255) NOT NULL,
`type` varchar(255) NOT NULL,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_stakeholder` (
`id` mediumint(8) NOT NULL AUTO_INCREMENT,
`objectID` mediumint(8) NOT NULL,
@@ -994,6 +1095,8 @@ CREATE TABLE `zt_story` (
`linkStories` varchar(255) NOT NULL,
`duplicateStory` mediumint(8) unsigned NOT NULL,
`version` smallint(6) NOT NULL DEFAULT 1,
`feedbackBy` varchar(100) NOT NULL,
`notifyEmail` varchar(100) NOT NULL,
`URChanged` enum('0','1') NOT NULL DEFAULT '0',
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
@@ -1360,3 +1463,18 @@ CREATE TABLE `zt_webhook` (
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `zt_weeklyreport`(
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`project` mediumint(8) unsigned NOT NULL,
`weekStart` date NOT NULL,
`pv` float(9,2) NOT NULL,
`ev` float(9,2) NOT NULL,
`ac` float(9,2) NOT NULL,
`sv` float(9,2) NOT NULL,
`cv` float(9,2) NOT NULL,
`staff` smallint(5) unsigned NOT NULL,
`progress` varchar(255) NOT NULL,
`workload` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `week` (`project`,`weekStart`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+1
View File
@@ -5,6 +5,7 @@ ALTER TABLE `zt_branch` ADD `createdDate` date NOT NULL AFTER `desc`;
ALTER TABLE `zt_branch` ADD `closedDate` date NOT NULL AFTER `createdDate`;
ALTER TABLE `zt_projectstory` ADD `branch` mediumint(8) NOT NULL AFTER `product`;
ALTER TABLE `zt_projectproduct` ADD PRIMARY KEY `project_product_branch` (`project`, `product`, `branch`), DROP INDEX `PRIMARY`;
ALTER TABLE `zt_apistruct` CHANGE COLUMN `editEdBy` `editedBy` varchar(30) NOT NULL DEFAULT 0;
-- DROP TABLE IF EXISTS `zt_kanbanlane`;
CREATE TABLE IF NOT EXISTS `zt_kanbanlane` (
+8 -5
View File
@@ -84,22 +84,25 @@ CREATE TABLE `zt_kanbancard` (
-- DROP TABLE IF EXISTS `zt_kanbangroup`;
CREATE TABLE `zt_kanbangroup` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`kanban` mediumint(8) unsigned NOT NULL,
`region` mediumint(8) unsigned NOT NULL,
`column` mediumint(8) unsigned NOT NULL,
`lane` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`column`, `lane`)
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_kanbanorder`;
CREATE TABLE `zt_kanbanorder` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`parent` mediumint(8) unsigned NOT NULL,
`parentID` mediumint(8) unsigned NOT NULL,
`parentType` varchar(20) NOT NULL,
`type` varchar(20) NOT NULL,
`objectID` mediumint(8) unsigned NOT NULL,
`objectType` varchar(20) NOT NULL,
`account` varchar(30) NOT NULL,
`order` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `zt_kanbanlane` ADD COLUMN `region` mediumint(8) unsigned NOT NULL AFTER `type`;
ALTER TABLE `zt_kanbanlane` ADD COLUMN `group` mediumint(8) unsigned NOT NULL AFTER `region`;
ALTER TABLE `zt_kanbancolumn` ADD COLUMN `region` mediumint(8) unsigned NOT NULL AFTER `type`;
ALTER TABLE `zt_kanbancolumn` ADD COLUMN `group` mediumint(8) unsigned NOT NULL AFTER `region`;
+9 -6
View File
@@ -102,7 +102,7 @@ CREATE TABLE `zt_apistruct` (
`attribute` text NULL,
`addedBy` varchar(30) NOT NULL DEFAULT 0,
`addedDate` datetime NOT NULL,
`editEdBy` varchar(30) NOT NULL DEFAULT 0,
`editedBy` varchar(30) NOT NULL DEFAULT 0,
`editedDate` datetime NOT NULL,
`deleted` enum ('0', '1') NOT NULL DEFAULT '0',
primary key (`id`)
@@ -725,17 +725,18 @@ CREATE TABLE `zt_kanbancard` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_kanbangroup`;
CREATE TABLE `zt_kanbangroup` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`kanban` mediumint(8) unsigned NOT NULL,
`region` mediumint(8) unsigned NOT NULL,
`column` mediumint(8) unsigned NOT NULL,
`lane` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`column`, `lane`)
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_kanbanorder`;
CREATE TABLE `zt_kanbanorder` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`parent` mediumint(8) unsigned NOT NULL,
`parentID` mediumint(8) unsigned NOT NULL,
`parentType` varchar(20) NOT NULL,
`type` varchar(20) NOT NULL,
`objectID` mediumint(8) NOT NULL,
`objectType` varchar(20) NOT NULL,
`account` varchar(30) NOT NULL,
`order` int NOT NULL,
PRIMARY KEY (`id`)
@@ -746,6 +747,7 @@ CREATE TABLE IF NOT EXISTS `zt_kanbanlane` (
`execution` mediumint(8) NOT NULL DEFAULT '0',
`type` char(30) NOT NULL,
`region` mediumint(8) unsigned NOT NULL,
`group` mediumint(8) unsigned NOT NULL,
`groupby` char(30) NOT NULL,
`extra` char(30) NOT NULL,
`name` varchar(255) NOT NULL DEFAULT '',
@@ -759,6 +761,7 @@ CREATE TABLE IF NOT EXISTS `zt_kanbanlane` (
CREATE TABLE IF NOT EXISTS `zt_kanbancolumn` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`lane` mediumint(8) NOT NULL DEFAULT '0',
`group` mediumint(8) NOT NULL DEFAULT '0',
`parent` mediumint(8) NOT NULL DEFAULT '0',
`type` char(30) NOT NULL,
`region` mediumint(8) unsigned NOT NULL,
+3
View File
@@ -90,6 +90,9 @@ class gitlab
/**
* Get files info.
*
* The API path requested is: "GET /projects/:id/repository/files/:file_path".
* Known issue of GitLab API: if a '%' in 'file_path', GitLab API will show a error 'file_path should be a valid file path'.
*
* @param string $path
* @param string $ref
* @access public
+4 -16
View File
@@ -721,7 +721,7 @@ class actionModel extends model
$actionDesc = str_replace('$extra', $action->extra, $desc['main']);
}
if($action->objectType == 'story' and $action->action = 'reviewed' and strpos($action->extra, ',') !== false)
if($action->objectType == 'story' and $action->action == 'reviewed' and strpos($action->extra, ',') !== false)
{
list($extra, $reason) = explode(',', $extra);
$desc['reason'] = $this->lang->$objectType->{$desc['reason']};
@@ -882,31 +882,19 @@ class actionModel extends model
}
/* If the sql not include 'product', add check purview for product. */
if(strpos($actionQuery, $allProducts) === false)
{
if(!in_array($productID, array_keys($products))) return array();
}
else
if(strpos($actionQuery, $allProducts) !== false)
{
$actionQuery = str_replace($allProducts, '1', $actionQuery);
}
/* If the sql not include 'project', add check purview for project. */
if(strpos($actionQuery, $allProjects) === false)
{
$actionQuery = $actionQuery . ' AND `project`' . helper::dbIN(array_keys($projects));
}
else
if(strpos($actionQuery, $allProjects) !== false)
{
$actionQuery = str_replace($allProjects, '1', $actionQuery);
}
/* If the sql not include 'execution', add check purview for execution. */
if(strpos($actionQuery, $allExecutions) === false)
{
$actionQuery = $actionQuery . ' AND `execution`' . helper::dbIN(array_keys($executions));
}
else
if(strpos($actionQuery, $allExecutions) !== false)
{
$actionQuery = str_replace($allExecutions, '1', $actionQuery);
}
+5 -3
View File
@@ -69,7 +69,7 @@ class api extends control
$this->view->typeList = $this->api->getTypeList($libID);
}
$this->setMenu($libID);
$this->setMenu($libID, $moduleID);
$this->view->isRelease = $release > 0;
$this->view->release = $release;
@@ -462,6 +462,7 @@ class api extends control
{
$now = helper::now();
$params = fixer::input('post')
->trim('title,path')
->remove('type')
->skipSpecial('params,response')
->add('addedBy', $this->app->user->account)
@@ -601,10 +602,11 @@ class api extends control
* Set doc menu by method name.
*
* @param int $libID
* @param int $moduleID
* @access public
* @return void
*/
private function setMenu($libID = 0)
private function setMenu($libID = 0, $moduleID = 0)
{
common::setMenuVars('doc', $libID);
@@ -629,7 +631,7 @@ class api extends control
if(common::hasPriv('api', 'create'))
{
$menu .= "<li>";
$menu .= html::a(helper::createLink('api', 'create', "libID=$libID"), "<i class='icon-rich-text icon'></i> " . $this->lang->api->apiDoc, '', "data-app='{$this->app->tab}'");
$menu .= html::a(helper::createLink('api', 'create', "libID=$libID&moduleID=$moduleID"), "<i class='icon-rich-text icon'></i> " . $this->lang->api->apiDoc, '', "data-app='{$this->app->tab}'");
$menu .= "</li>";
}
+1 -1
View File
@@ -41,7 +41,7 @@ $lang->api->noUniqueName = '接口库名已存在。';
$lang->api->noUniqueVersion = '版本已存在。';
$lang->api->version = '版本';
$lang->api->createStruct = '创建数据结构';
$lang->api->editStruct = '修改数据结构';
$lang->api->editStruct = '编辑数据结构';
$lang->api->deleteStruct = '删除数据结构';
$lang->api->create = '创建接口';
$lang->api->title = '接口名称';
+9 -8
View File
@@ -209,20 +209,17 @@ class apiModel extends model
$now = helper::now();
$account = $this->app->user->account;
$data = fixer::input('post')
->remove('type')
->skipSpecial('params,response')
->add('editedBy', $account)
->add('editedDate', $now)
->add('version', $oldApi->version)
->setDefault('product,module', 0)
->remove('type')
->get();
$data->id = $oldApi->id;
$data->version = $oldApi->version + 1;
$apiSpec = $this->getApiSpecByData($data);
$changes = common::createChanges($oldApi, $data);
if(!empty($changes)) $data->version = $oldApi->version + 1;
$this->dao->replace(TABLE_API_SPEC)->data($apiSpec)->exec();
unset($data->id);
$this->dao->update(TABLE_API)
->data($data)
->autoCheck()
@@ -230,7 +227,11 @@ class apiModel extends model
->where('id')->eq($apiID)
->exec();
return common::createChanges($oldApi, $data);
$data->id = $apiID;
$apiSpec = $this->getApiSpecByData($data);
$this->dao->replace(TABLE_API_SPEC)->data($apiSpec)->exec();
return $changes;
}
/**
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="main-col" data-min-width="400">
<div class="main-col" data-min-width="500">
<div id="mainContent" class="main-row in">
<div class="main-col col-8">
<div class="cell" id="content">
+1 -1
View File
@@ -68,7 +68,7 @@ js::set('struct_paramsType', $lang->struct->paramsType);
<tr>
<th><?php echo $lang->api->module;?></th>
<td>
<span id='moduleBox'><?php echo html::select('module', $moduleOptionMenu, '', "class='form-control chosen'");?></span>
<span id='moduleBox'><?php echo html::select('module', $moduleOptionMenu, $moduleID, "class='form-control chosen'");?></span>
</td>
<td></td>
</tr>
+3 -1
View File
@@ -42,8 +42,10 @@
</div>
<?php if(!$moduleTree):?>
<hr class="space">
<?php if(!$isRelease):?>
<div class="text-center text-muted tips"><?php echo $lang->api->noModule;?></div>
<?php endif;?>
<?php endif;?>
<?php echo $moduleTree;?>
</div>
</div>
@@ -71,7 +73,7 @@
</div>
</div>
<?php else:?>
<div class="cell">
<div class="cell main-col" data-min-width="400">
<div class="detail">
<ul class="list-group">
<?php foreach($apiList as $api):?>
+1 -1
View File
@@ -41,7 +41,7 @@
<td><?php echo zget($users, $release->addedBy, '');?></td>
<td class="c-date"><?php echo $release->addedDate;?></td>
<td class='c-actions'>
<?php echo html::a($this->createLink('api', 'deleteRelease', "libID=$libID&id=$release->id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->api->delete}' class='btn'");?>
<?php if(common::hasPriv('api', 'deleteRelease')) echo html::a($this->createLink('api', 'deleteRelease', "libID=$libID&id=$release->id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->api->deleteRelease}' class='btn'");?>
</td>
</tr>
<?php endforeach;?>
+2 -2
View File
@@ -49,8 +49,8 @@
<td class="c-date"><?php echo $struct->addedDate;?></td>
<td class='c-actions'>
<?php
if(common::hasPriv('api', 'editStruct')) echo html::a($this->createLink('api', 'editStruct', "libID=$libID&structID=$struct->id"), '<i class="icon-edit"></i>', '', "title='{$lang->api->edit}' class='btn'");
if(common::hasPriv('api', 'deleteStruct')) echo html::a($this->createLink('api', 'deleteStruct', "libID=$libID&structID=$struct->id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->api->delete}' class='btn'");
if(common::hasPriv('api', 'editStruct')) echo html::a($this->createLink('api', 'editStruct', "libID=$libID&structID=$struct->id"), '<i class="icon-edit"></i>', '', "title='{$lang->api->editStruct}' class='btn'");
if(common::hasPriv('api', 'deleteStruct')) echo html::a($this->createLink('api', 'deleteStruct', "libID=$libID&structID=$struct->id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->api->deleteStruct}' class='btn'");
?>
</td>
</tr>
+1 -1
View File
@@ -701,7 +701,7 @@ class block extends control
$this->session->set('buildList', $this->app->getURI(true), 'execution');
if(preg_match('/[^a-zA-Z0-9_]/', $this->params->type)) die();
$this->view->testtasks = $this->dao->select('t1.*,t2.name as productName,t3.name as buildName,t4.name as projectName')->from(TABLE_TESTTASK)->alias('t1')
$this->view->testtasks = $this->dao->select('distinct t1.*,t2.name as productName,t3.name as buildName,t4.name as projectName')->from(TABLE_TESTTASK)->alias('t1')
->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')
->leftJoin(TABLE_BUILD)->alias('t3')->on('t1.build=t3.id')
->leftJoin(TABLE_PROJECT)->alias('t4')->on('t1.execution=t4.id')
+10 -1
View File
@@ -407,7 +407,16 @@ class bug extends control
if($this->app->tab == 'execution')
{
$location = $this->session->bugList ? $this->session->bugList : $this->createLink('execution', 'bug', "executionID={$output['executionID']}");
if(!preg_match("/(m=|\/)execution(&f=|-)bug(&|-|\.)?/", $this->session->bugList))
{
$location = $this->session->bugList;
}
else
{
$executionID = $this->post->execution ? $this->post->execution : $output['executionID'];
$location = $this->createLink('execution', 'bug', "executionID=$executionID");
}
}
elseif($this->app->tab == 'project')
{
+1 -1
View File
@@ -93,7 +93,7 @@
if($this->app->tab != 'product')
{
common::printIcon('bug', 'toStory', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", $bug, 'button', $lang->icons['story'], '', '', '', "data-app='" . $this->app->tab . "'", $lang->bug->toStory);
common::printIcon('bug', 'toStory', "product=$bug->product&branch=$bug->branch&module=0&story=0&execution=0&bugID=$bug->id", $bug, 'button', $lang->icons['story'], '', '', '', "data-app='product'", $lang->bug->toStory);
common::printIcon('bug', 'createCase', $convertParams, $bug, 'button', 'sitemap');
}
+1
View File
@@ -54,6 +54,7 @@ class ciModel extends model
*/
public function syncCompileStatus($compile)
{
/* Max retry times is: 3. */
if($compile->times >= 3)
{
$this->dao->update(TABLE_COMPILE)->set('status')->eq('failure')->where('id')->eq($compile->id)->exec();
+3 -2
View File
@@ -145,9 +145,10 @@ class commonModel extends model
->where('t2.id')->eq($taskID)
->fetch();
$today = helper::today();
if($execution->status == 'wait')
{
$this->dao->update(TABLE_EXECUTION)->set('status')->eq('doing')->where('id')->eq($execution->id)->exec();
$this->dao->update(TABLE_EXECUTION)->set('status')->eq('doing')->set('realBegan')->eq($today)->where('id')->eq($execution->id)->exec();
$this->loadModel('action')->create('execution', $execution->id, 'syncexecution');
}
return $execution;
@@ -849,7 +850,7 @@ class commonModel extends model
$method = $linkPart[2];
/* Skip some pages that do not require permissions.*/
if($currentModule == 'report' and $method == 'annualData') continue;
if($currentModule == 'report' and $method == 'annualData') continue;
if($currentModule == 'my' and $currentMethod == 'team') continue;
if(common::hasPriv($currentModule, $method))
+2
View File
@@ -27,5 +27,7 @@ function submitForm(type)
$('.modal-body #insert').val(type == 'insert' ? 1 : 0);
$('#importNoticeModal .form-actions .btn').addClass('disabled');
$("button[data-target='#importNoticeModal']").closest('form').submit();
setTimeout(function(){$('#importNoticeModal .form-actions .btn').removeClass('disabled');}, 1000);
}
</script>
+2 -2
View File
@@ -178,8 +178,8 @@ class compileModel extends model
$this->dao->update(TABLE_COMPILE)->data($compile)->where('id')->eq($compileID)->exec();
$this->dao->update(TABLE_JOB)
->set('lastStatus')->eq($build->status)
->set('lastExec')->eq($build->updateDate)
->set('lastStatus')->eq($compile->status)
->set('lastExec')->eq($compile->updateDate)
->where('id')->eq($job->id)
->exec();
+1 -1
View File
@@ -401,7 +401,7 @@ class design extends control
public function revision($repoID = 0, $projectID = 0)
{
$repo = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('id')->eq($repoID)->fetch();
$repoURL = $this->createLink('repo', 'revision', "repoID=$repo->repo&objectID=$projectID&revistion=$repo->revision");
$repoURL = $this->createLink('repo', 'revision', "repoID=$repo->repo&objectID=$projectID&revistion=$repo->revision", '', true);
header("location:" . $repoURL);
}
+1 -1
View File
@@ -249,7 +249,7 @@ class designModel extends model
$design->commit = '';
$relations = $this->loadModel('common')->getRelations('design', $designID, 'commit');
foreach($relations as $relation) $design->commit .= html::a(helper::createLink('design', 'revision', "repoID=$relation->BID&projectID={$design->project}"), "#$relation->BID", '_blank');
foreach($relations as $relation) $design->commit .= html::a(helper::createLink('design', 'revision', "repoID=$relation->BID&projectID={$design->project}"), "#$relation->BID", '', "class=iframe");
return $this->loadModel('file')->replaceImgURL($design, 'desc');
}
+1 -1
View File
@@ -69,7 +69,7 @@
</tr>
<tr>
<th><?php echo $lang->design->story;?></th>
<td><?php echo $design->story ? html::a($this->createLink('story', 'view', "id=$design->story"), zget($stories, $design->story)) : '';?></td>
<td><?php echo $design->story ? html::a($this->createLink('story', 'view', "id=$design->story", '', true), zget($stories, $design->story), '',"class=iframe") : '';?></td>
</tr>
<tr>
<th><?php echo $lang->design->submission;?></th>
+15 -3
View File
@@ -189,19 +189,31 @@ class doc extends control
if(!empty($_POST))
{
$changes = $this->doc->updateLib($libID);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
return $this->send($response);
}
if($changes)
{
$actionID = $this->action->create('docLib', $libID, 'edited');
$this->action->logHistory($actionID, $changes);
}
$docLib = $this->doc->getLibById($libID);
$docLib = $this->doc->getLibById($libID);
$objectID = 0;
if(strpos('product,project,execution', $docLib->type) !== false)
{
$libType = $docLib->type;
$objectID = $docLib->$libType;
}
$hasLibPriv = $this->doc->checkPrivLib($docLib) ? 1 : 0;
$response['message'] = $this->lang->saveSuccess;
$response['result'] = 'success';
$response['closeModal'] = true;
$response['callback'] = "redirectParentWindow($hasLibPriv, $libID)";
$response['callback'] = "redirectParentWindow($hasLibPriv, $libID, $objectID)";
return $this->send($response);
}
+3 -2
View File
@@ -8,11 +8,12 @@ $(function()
*
* @param int hasLibPriv
* @param int libID
* @param int objectID
* @access public
* @return void
*/
function redirectParentWindow(hasLibPriv, libID)
function redirectParentWindow(hasLibPriv, libID, objectID)
{
var link = hasLibPriv ? createLink('doc', 'tableContents', 'type=' + libType + '&objectID=0&libID=' + libID) : createLink('doc', 'tableContents', 'type=' + libType);
var link = hasLibPriv ? createLink('doc', 'tableContents', 'type=' + libType + '&objectID=' + objectID + '&libID=' + libID) : createLink('doc', 'tableContents', 'type=' + libType);
parent.location.href = link;
}
+1 -1
View File
@@ -133,7 +133,7 @@ $(function()
$('#content').on('click', '.outline .outline-toggle i.icon-angle-right', function()
{
$('.article-content').width('85%');
$('.article-content').css('width', '85%');
$('.outline').css({'min-width' : '180px', 'border-left' : '2px solid #efefef'});
$(this).removeClass('icon-angle-right').addClass('icon-angle-left').css('left', '-9px');
$('.outline-content').show();
+1
View File
@@ -2562,6 +2562,7 @@ EOT;
if($this->app->tab == 'doc') $this->app->rawMethod = $type;
$object = $this->dao->select('id,name,status')->from($table)->where('id')->eq($objectID)->fetch();
if(empty($object)) die(js::locate(helper::createLink($type, 'create')));
}
+18 -16
View File
@@ -893,7 +893,7 @@ class execution extends control
$productPairs = array('0' => $this->lang->product->all);
foreach($products as $product) $productPairs[$product->id] = $product->name;
$this->lang->modulePageNav = $this->product->select($productPairs, $productID, 'execution', 'bug', '', $branchID, 0, '', false);
$this->lang->modulePageNav = $this->product->select($productPairs, $productID, 'execution', 'bug', $executionID, $branchID, 0, '', false);
/* Header and position. */
$title = $execution->name . $this->lang->colon . $this->lang->execution->bug;
@@ -921,20 +921,21 @@ class execution extends control
$this->execution->buildBugSearchForm($products, $queryID, $actionURL);
/* Assign. */
$this->view->title = $title;
$this->view->position = $position;
$this->view->bugs = $bugs;
$this->view->tabID = 'bug';
$this->view->build = $this->loadModel('build')->getById($build);
$this->view->buildID = $this->view->build ? $this->view->build->id : 0;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $users;
$this->view->productID = $productID;
$this->view->branchID = empty($this->view->build->branch) ? $branchID : $this->view->build->branch;
$this->view->memberPairs = $memberPairs;
$this->view->type = $type;
$this->view->param = $param;
$this->view->title = $title;
$this->view->position = $position;
$this->view->bugs = $bugs;
$this->view->tabID = 'bug';
$this->view->build = $this->loadModel('build')->getById($build);
$this->view->buildID = $this->view->build ? $this->view->build->id : 0;
$this->view->pager = $pager;
$this->view->orderBy = $orderBy;
$this->view->users = $users;
$this->view->productID = $productID;
$this->view->branchID = empty($this->view->build->branch) ? $branchID : $this->view->build->branch;
$this->view->memberPairs = $memberPairs;
$this->view->type = $type;
$this->view->param = $param;
$this->view->defaultProduct = (empty($productID) and !empty($products)) ? current(array_keys($products)) : $productID;
$this->display();
}
@@ -1967,7 +1968,7 @@ class execution extends control
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType, $groupBy);
if(empty($kanbanGroup))
{
$this->kanban->createLanes($executionID, $browseType, $groupBy);
$this->kanban->createExecutionLane($executionID, $browseType, $groupBy);
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy);
}
@@ -2430,6 +2431,7 @@ class execution extends control
$this->view->unmodifiableBranches = $unmodifiableBranches;
$this->view->linkedBranches = $linkedBranches;
$this->view->branchGroups = $this->execution->getBranchByProduct(array_keys($allProducts), $this->config->systemMode == 'new' ? $execution->project : 0);
$this->view->allBranches = $this->execution->getBranchByProduct(array_keys($allProducts), $this->config->systemMode == 'new' ? $execution->project : 0, 'all');
$this->display();
}
+1 -1
View File
@@ -18,7 +18,7 @@ td.flex span.project-type-label {margin-left: 5px; min-width: 36px;}
@-moz-document url-prefix() {.main-table tbody > tr.table-children > td:first-child::before {width: 4px;}}
.c-status, .c-date, .c-percent, .c-attribute {width: 80px;}
.c-estimate, .c-consumed, .c-left {width: 70px;}
.c-estimate, .c-consumed, .c-left, .c-pm {width: 70px;}
.c-progress, .c-burn {width: 60px;}
.c-realBegan, .c-end, .c-begin, .c-realEnd{width: 100px;}
.c-action {text-align: center;}
+18 -21
View File
@@ -283,30 +283,27 @@ function renderColumnCount($count, count, col)
function renderHeaderCol($col, col, $header, kanban)
{
if(col.asParent) $col = $col.children('.kanban-header-col');
if(!$col.children('.actions').length)
var $actions = $('<div class="actions" />');
var printStoryButton = printTaskButton = printBugButton = false;
if(priv.canCreateStory || priv.canBatchCreateStory || priv.canLinkStory || priv.canLinkStoryByPlane) printStoryButton = true;
if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true;
if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true;
if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton))
{
var $actions = $('<div class="actions" />');
var printStoryButton = printTaskButton = printBugButton = false;
if(priv.canCreateStory || priv.canBatchCreateStory || priv.canLinkStory || priv.canLinkStoryByPlane) printStoryButton = true;
if(priv.canCreateTask || priv.canBatchCreateTask) printTaskButton = true;
if(priv.canCreateBug || priv.canBatchCreateBug) printBugButton = true;
if((col.type === 'backlog' && printStoryButton) || (col.type === 'wait' && printTaskButton) || (col.type == 'unconfirmed' && printBugButton))
{
$actions.append([
'<a data-contextmenu="columnCreate" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '" class="text-primary">',
'<i class="icon icon-expand-alt"></i>',
'</a>'
].join(''));
}
$actions.append([
'<a data-contextmenu="column" title="' + kanbanLang.moreAction + '" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>'
'<a data-contextmenu="columnCreate" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '" class="text-primary">',
'<i class="icon icon-expand-alt"></i>',
'</a>'
].join(''));
$actions.appendTo($col);
}
$actions.append([
'<a data-contextmenu="column" title="' + kanbanLang.moreAction + '" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '">',
'<i class="icon icon-ellipsis-v"></i>',
'</a>'
].join(''));
$actions.appendTo($col);
}
/**
@@ -612,7 +609,7 @@ function createColumnMenu(options)
var items = [];
if(priv.canEditName) items.push({label: executionLang.editName, url: $.createLink('kanban', 'setColumn', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe', attrs: {'data-width': '500px'}})
if(priv.canSetWIP) items.push({label: executionLang.setWIP, url: $.createLink('kanban', 'setWIP', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe', attrs: {'data-width': '500px'}})
if(priv.canSortCards) items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards})
//if(priv.canSortCards) items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards})
return items;
}
+4 -3
View File
@@ -381,7 +381,7 @@ class executionModel extends model
$creatorExists = false;
$teamMembers = array();
$this->loadModel('kanban')->createLanes($executionID);
$this->loadModel('kanban')->createExecutionLane($executionID);
/* Save order. */
$this->dao->update(TABLE_EXECUTION)->set('`order`')->eq($executionID * 5)->where('id')->eq($executionID)->exec();
@@ -1676,12 +1676,13 @@ class executionModel extends model
*
* @param array $products
* @param int $projectID
* @param string $param
* @access public
* @return array
*/
public function getBranchByProduct($products, $projectID = 0)
public function getBranchByProduct($products, $projectID = 0, $param = 'noclosed')
{
$branchGroups = $this->loadModel('branch')->getByProducts($products, 'noclosed');
$branchGroups = $this->loadModel('branch')->getByProducts($products, $param);
if($projectID)
{
+8 -8
View File
@@ -139,10 +139,10 @@
<?php if($isStage):?>
<td><?php echo $execution->percent . '%';?></td>
<td><?php echo zget($lang->stage->typeList, $execution->attribute, '');?></td>
<td><?php echo $execution->begin;?></td>
<td><?php echo $execution->end;?></td>
<td><?php echo $execution->realBegan;?></td>
<td><?php echo $execution->realEnd;?></td>
<td><?php echo helper::isZeroDate($execution->begin) ? '' : $execution->begin;?></td>
<td><?php echo helper::isZeroDate($execution->end) ? '' : $execution->end;?></td>
<td><?php echo helper::isZeroDate($execution->realBegan) ? '' : $execution->realBegan;?></td>
<td><?php echo helper::isZeroDate($execution->realEnd) ? '' : $execution->realEnd;?></td>
<td class="c-actions text-center c-actions">
<?php
common::printIcon('execution', 'start', "executionID={$execution->id}", $execution, 'list', '', '', 'iframe', true);
@@ -213,10 +213,10 @@
<?php if($isStage):?>
<td><?php echo $child->percent . '%';?></td>
<td><?php echo zget($lang->stage->typeList, $child->attribute, '');?></td>
<td><?php echo $child->begin;?></td>
<td><?php echo $child->end;?></td>
<td><?php echo $child->realBegan;?></td>
<td><?php echo $child->realEnd;?></td>
<td><?php echo helper::isZeroDate($child->begin) ? '' : $child->begin;?></td>
<td><?php echo helper::isZeroDate($child->end) ? '' : $child->end;?></td>
<td><?php echo helper::isZeroDate($child->realBegan) ? '' : $child->realBegan;?></td>
<td><?php echo helper::isZeroDate($child->realEnd) ? '' : $child->realEnd;?></td>
<td class="c-actions text-center c-actions">
<?php
common::printIcon('execution', 'start', "executionID={$child->id}", $child, 'list', '', '', 'iframe', true);
+2 -2
View File
@@ -26,7 +26,7 @@
</div>
<div class="btn-toolbar pull-right">
<?php common::printLink('bug', 'export', "productID=$productID&orderBy=$orderBy&browseType=&executionID=$execution->id", "<i class='icon icon-export muted'> </i> " . $lang->bug->export, '', "class='btn btn-link export'");?>
<?php if(common::canModify('execution', $execution)) common::printLink('bug', 'create', "productID=$productID&branch=$branchID&extras=executionID=$execution->id", "<i class='icon icon-plus'></i> " . $lang->bug->create, '', "class='btn btn-primary'");?>
<?php if(common::canModify('execution', $execution)) common::printLink('bug', 'create', "productID=$defaultProduct&branch=$branchID&extras=executionID=$execution->id", "<i class='icon icon-plus'></i> " . $lang->bug->create, '', "class='btn btn-primary'");?>
</div>
</div>
<div id="mainContent">
@@ -36,7 +36,7 @@
<p>
<span class="text-muted"><?php echo $lang->bug->noBug;?></span>
<?php if(common::canModify('execution', $execution) and common::hasPriv('bug', 'create')):?>
<?php echo html::a($this->createLink('bug', 'create', "productID=$productID&branch=$branchID&extra=executionID=$execution->id"), "<i class='icon icon-plus'></i> " . $lang->bug->create, '', "class='btn btn-info' data-app='execution'");?>
<?php echo html::a($this->createLink('bug', 'create', "productID=$defaultProduct&branch=$branchID&extra=executionID=$execution->id"), "<i class='icon icon-plus'></i> " . $lang->bug->create, '', "class='btn btn-info' data-app='execution'");?>
<?php endif;?>
</p>
</div>
@@ -28,17 +28,17 @@
<?php foreach($allProducts as $productID => $productName):?>
<?php if(isset($linkedProducts[$productID])):?>
<?php foreach($linkedBranches[$productID] as $branchID):?>
<?php if(($execution->grade == 1 and in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches))) $attr = "disabled='disabled'";?>
<?php if(($execution->grade == 1 and !(in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches)))) $attr = '';?>
<?php if(($execution->grade < 2 and in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches))) $attr = "disabled='disabled'";?>
<?php if(($execution->grade < 2 and !(in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches)))) $attr = '';?>
<?php $title = (in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches)) ? $lang->execution->notAllowRemoveProducts : $productName;?>
<?php $checked = 'checked';?>
<div class='col-sm-4'>
<div class='product <?php echo $checked . (isset($branchGroups[$productID]) ? ' has-branch' : '')?>'>
<div class='product <?php echo $checked . (isset($allBranches[$productID]) ? ' has-branch' : '')?>'>
<div class="checkbox-primary" title='<?php echo $title;?>'>
<?php echo "<input type='checkbox' name='products[$i]' value='$productID' $checked $attr id='products{$productID}'>";?>
<label class='text-ellipsis checkbox-inline' for='<?php echo 'products' . $productID;?>' title='<?php echo $productName;?>'><?php echo $productName;?></label>
</div>
<?php if(isset($branchGroups[$productID][$branchID])) echo html::select("branch[$i]", $branchGroups[$productID], $branchID, "class='form-control chosen' disabled='disabled'");?>
<?php if(isset($allBranches[$productID][$branchID])) echo html::select("branch[$i]", $allBranches[$productID], $branchID, "class='form-control chosen' disabled='disabled'");?>
</div>
</div>
<?php if(!empty($attr)) echo html::hidden("products[$i]", $productID);?>
@@ -60,12 +60,13 @@
<div class='col-sm-4'>
<div class='product<?php echo isset($branchGroups[$productID]) ? ' has-branch' : ''?>'>
<div class="checkbox-primary" title='<?php echo $productName;?>'>
<?php echo "<input type='checkbox' name='products[$productID]' value='$productID' id='products{$productID}'>";?>
<?php echo "<input type='checkbox' name='products[$i]' value='$productID' id='products{$productID}'>";?>
<label class='text-ellipsis checkbox-inline' for='<?php echo 'products' . $productID;?>'><?php echo $productName;?></label>
</div>
<?php if(isset($branchGroups[$productID])) echo html::select("branch[$productID]", $branchGroups[$productID], '', "class='form-control chosen'");?>
<?php if(isset($branchGroups[$productID])) echo html::select("branch[$i]", $branchGroups[$productID], '', "class='form-control chosen'");?>
</div>
</div>
<?php $i++;?>
<?php endforeach;?>
</div>
</div>
+6 -6
View File
@@ -411,7 +411,7 @@ class gitlabModel extends model
if(is_numeric($host)) $host = $this->getApiRoot($host);
if(strpos($host, 'http://') !== 0 and strpos($host, 'https://') !== 0) return false;
$url = sprintf($apiRoot, $api);
$url = sprintf($host, $api);
return json_decode(commonModel::http($url, $data, $options));
}
@@ -635,7 +635,7 @@ class gitlabModel extends model
*
* @param int $gitlabID
* @access public
* @return void
* @return array
*/
public function apiGetProjects($gitlabID)
{
@@ -1715,9 +1715,9 @@ class gitlabModel extends model
/**
* Create webhook for zentao.
*
* @param int $products
* @param int $gitlabID
* @param int $projectID
* @param array $products
* @param int $gitlabID
* @param int $projectID
* @access public
* @return bool
*/
@@ -1830,7 +1830,7 @@ class gitlabModel extends model
if($value) $issue->$field = $value;
}
if($isset($issue->assignee_id) and $issue->assignee_id == 'closed') unset($issue->assignee_id);
if(isset($issue->assignee_id) and $issue->assignee_id == 'closed') unset($issue->assignee_id);
/* issue->state is null when creating it, we should put status_event when updating it. */
if(isset($issue->state) and $issue->state == 'closed') $issue->state_event = 'close';
-1
View File
@@ -27,4 +27,3 @@
</div>
<?php include '../../common/view/footer.html.php';?>
+2 -2
View File
@@ -120,7 +120,7 @@ class job extends control
$this->view->repoTypes = $repoTypes;
$this->view->products = array(0 => '') + $this->loadModel('product')->getProductPairsByProject($this->projectID);
$this->view->jenkinsServerList = $this->loadModel('jenkins')->getPairs();
$this->view->jenkinsServerList = array('' => '') + $this->loadModel('jenkins')->getPairs();
$this->display();
}
@@ -352,7 +352,7 @@ class job extends control
if($productLeft == $productRight) $matchedProducts[$productName] = $productRight;
}
}
die(json_encode($matchedProduct));
die(json_encode($matchedProducts));
}
$productName = $this->loadModel('product')->getByID($repo->product)->name;
+5 -4
View File
@@ -378,7 +378,7 @@ class jobModel extends model
if($job->triggerType == 'tag')
{
$lastTag = $this->getLastTagByRepo($repo);
$lastTag = $this->getLastTagByRepo($repo, $job);
if($lastTag)
{
$build->tag = $lastTag;
@@ -444,7 +444,7 @@ class jobModel extends model
/**
* Exec gitlab pipeline.
*
* @param int $job
* @param object $job
* @access public
* @return void
*/
@@ -486,11 +486,12 @@ class jobModel extends model
/**
* Get last tag of one repo.
*
* @param object $repo
* @param object $repo
* @param object $job
* @access public
* @return void
*/
public function getLastTagByRepo($repo)
public function getLastTagByRepo($repo, $job)
{
if($repo->SCM == 'Subversion')
{
+3
View File
@@ -2,6 +2,9 @@
global $lang;
$config->kanban = new stdclass();
$config->kanban->require = new stdclass();
$config->kanban->require->createregion = 'name';
$config->kanban->setwip = new stdclass();
$config->kanban->setlane = new stdclass();
$config->kanban->setlaneColumn = new stdclass();
+17
View File
@@ -114,6 +114,23 @@ class kanban extends control
$this->display();
}
/**
* View a kanban.
*
* @param int $kanbanID
* @access public
* @return void
*/
public function view($kanbanID)
{
$kanban = $this->kanban->getByID($kanbanID);
$this->view->regions = $this->kanban->getKanbanData($kanbanID);
$this->view->kanban = $kanban;
$this->display();
}
/**
* Set WIP.
*
+81
View File
@@ -0,0 +1,81 @@
body {background-color: #fff}
#mainNavbar + #mainContent {background-color: #fff;}
#mainNavbar + #mainContent.scrollbar-hover {margin-top: 0px; overflow: scroll;}
#TRAction .icon {padding: 0px 6px; font-size: 14px}
#searchbox {display: none;}
.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 {color: #ff4268;}
.dropdown-menu > li {padding: 0 10px;}
.dropdown-menu > li > a > .icon {position: relative; left: -5px;}
.region {border: 1px solid #dcdcdc; margin-bottom: 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.sort .region-header {cursor: move;}
.region .kanban {padding: 0px 10px 10px 10px; min-height: unset;}
.region .kanban .form-actions {margin: 0;}
.region .kanban .form-actions .btn {margin-right: 10px; min-width: 50px;}
.region .kanban-board {overflow-x: auto; min-width: 100%!important;}
.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 {margin: 0}
.region .kanban-header-col > .title > span {display: inline-block; overflow: hidden; padding-right:2px; position: initial;}
.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: 5px}
.region .kanban-lane {position: relative; border-bottom: none!important; min-height: 200px}
.region .kanban-lane {min-width: max-content; min-width: -moz-max-content}
.region .kanban-lane > .kanban-lane-name > .text {margin: auto 0; max-height: 100px;}
.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.drop-to {outline: 3px solid rgba(255, 190, 57, 0.5)!important; background-color: rgba(255, 190, 57, .1);}
.region .kanban-lane-col.can-drop-here {outline: none; background-color: rgba(61, 198, 253, .075);}
.region .kanban-lane-col.has-scrollbar {padding-bottom: 50px;}
.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}
.region .kanban-item > .title {display: block; max-height: 38px; overflow: hidden; color: inherit; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;}
.region .kanban-item > .title:hover {color: #2272eb}
.region .kanban-item > .info {margin-top: 5px; position: relative;}
.region .kanban-item > .info > .pri {height: 14px; border-width: 1px; font-size: 12px; line-height: 12px; min-width: 14px; padding: 0 1px;}
.region .kanban-item > .info > .time {margin-left: 10px; font-size: 12px}
.region .kanban-item > .info > .user {position: absolute; right: 0; top: 0}
.region .kanban-item > .actions {position: absolute; top: 0px; right: 4px; opacity: 0;}
.region .kanban-item > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
.region .kanban-item > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
.region .kanban-item:hover > .actions {opacity: 1;}
.region .kanban-item.has-color .title,
.region .kanban-item.has-color .actions .icon-more-v,
.region .kanban-item.has-color .info > .label-pri,
.region .kanban-item.has-color .info > .label-light {color: #FFFFFF;}
.region .kanban-item.has-color .info > .label-pri {border-color: #FFFFFF;}
.region .kanban-item .item-more {position: absolute; right: 6px; top: 0px;}
.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;}
+848
View File
@@ -0,0 +1,848 @@
function loadMore(type)
{
var method = 'browseArchived' + type;
var selector = '#more' + type + 's';
var link = createLink('kanban', method, 'kanbanID=' + kanbanID);
$(selector).load(link, function()
{
var windowHeight = $(window).height();
$(selector + ' .panel-body').css('height', windowHeight - 100);
$(selector + ' .avatar').renderAvatar();
$(selector).animate({right: 0}, 500);
});
}
/**
* Display the kanban in full screen.
*
* @access public
* @return void
*/
function fullScreen()
{
var element = document.getElementById('mainContent');
var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen;
if(requestMethod)
{
var afterEnterFullscreen = function()
{
$('#mainContent').addClass('scrollbar-hover');
$.cookie('isFullScreen', 1);
};
var whenFailEnterFullscreen = function(error)
{
$.cookie('isFullScreen', 0);
};
try
{
var result = requestMethod.call(element);
if(result && (typeof result.then === 'function' || result instanceof window.Promise))
{
result.then(afterEnterFullscreen).catch(whenFailEnterFullscreen);
}
else
{
afterEnterFullscreen();
}
}
catch (error)
{
whenFailEnterFullscreen(error);
}
}
}
/**
* Exit full screen.
*
* @access public
* @return void
*/
function exitFullScreen()
{
$('#mainContent').removeClass('scrollbar-hover');
$.cookie('isFullScreen', 0);
}
document.addEventListener('fullscreenchange', function (e)
{
if(!document.fullscreenElement) exitFullScreen();
});
document.addEventListener('webkitfullscreenchange', function (e)
{
if(!document.webkitFullscreenElement) exitFullScreen();
});
document.addEventListener('mozfullscreenchange', function (e)
{
if(!document.mozFullScreenElement) exitFullScreen();
});
document.addEventListener('msfullscreenChange', function (e)
{
if(!document.msfullscreenElement) exitFullScreen();
});
/**
* Render header of a column.
*/
function renderHeaderCol($column, column, $header, kanbanData)
{
/* Render group header. */
var privs = kanbanData.actions;
if(privs.includes('sortGroup'))
{
var groups = regions[column.region].groups;
if($header.closest('.kanban').data('zui.kanban'))
{
groups = $header.closest('.kanban').data('zui.kanban').data;
}
if(groups.length > 1)
{
$column.closest('.kanban-board').addClass('sort');
$column.closest('.kanban-header').find('.kanban-group-header').remove();
$column.closest('.kanban-header').prepend('<div class="kanban-group-header"><i class="icon icon-md icon-move"></i></div>');
}
}
/* Render more menu. */
if(!$column.children('.actions').length) $column.append('<div class="actions"></div>');
var $actions = $column.children('.actions');
$actions.attr('title', lang.more);
$actions.html('<button class="btn btn-link action" data-contextmenu="column" data-column="' + column.id + '" data-kanban="' + kanban.id + '"><i class="icon icon-more-v"></i></button>');
}
/**
* Render items count of a column.
*/
function renderCount($count, count, column)
{
/* Render WIP. */
var limit = !column.limit || column.limit == '-1' ? '<i class="icon icon-md icon-infinite"></i>' : column.limit;
if($count.parent().find('.limit').length)
{
$count.parent().find('.limit').html(limit);
}
else
{
$count.parent().find('.count').before("<span class='include-first text-grey'>(</span>");
$count.parent().find('.count').after("<span class='divider text-grey'>/</span><span class='limit text-grey'>" + limit + "</span><span class='include-last text-grey'>)</span>");
}
if(column.limit != -1 && column.limit < count)
{
$count.parents('.title').parent('.kanban-header-col').css('background-color', '#F6A1A1');
$count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 200);
$count.css('color', '#E33030');
if(!$count.parent().find('.error').length) $count.parent().find('.include-last').after("<span class='error text-grey'>(" + kanbanLang.limitExceeded + ")</span>");
}
else
{
$count.parents('.title').parent('.kanban-header-col').css('background-color', 'transparent');
$count.parents('.title').find('.text').css('max-width', $count.parents('.title').width() - 120);
$count.css('color', '#8B91A2');
$count.parent().find('.error').remove();
}
}
function renderLaneName($lane, lane, $kanban, columns, kanban)
{
var canEdit = lane.actions.includes('editLane');
var canSort = lane.actions.includes('sortLane') && kanban.lanes.length > 1;
var canDelete = lane.actions.includes('deleteLane');
$lane.parent().toggleClass('sort', canSort);
if(!$lane.children('.actions').length && (canEdit || canDelete))
{
$([
'<div class="actions" title="' + lang.more + '">',
'<a data-contextmenu="lane" data-lane="' + lane.id + '" data-kanban="' + kanban.id + '">',
'<i class="icon icon-more-v"></i>',
'</a>',
'</div>'
].join('')).appendTo($lane);
}
}
/**
* The function for rendering kanban item
*/
function renderKanbanItem(item, $item)
{
if(item.status == 'done')
{
$item.addClass('gray');
}
var $title = $item.children('.title');
if(!$title.length)
{
$title = $('<a class="title" data-toggle="modal" data-width="80%"></a>')
.appendTo($item);
}
$title.text(item.name);
$title.attr('href', createLink('sys.task', 'view', 'taskID=' + item.id + '&from=kanban'));
var $more = $item.children('.actions');
if(!$more.length)
{
$(
[
'<div class="actions" title="' + lang.more + '">',
'<button class="btn btn-link action" data-contextmenu="task" data-id="' + item.id + '" data-status="' + item.status + '">',
'<i class="icon icon-more-v"></i>',
'</button>',
'</div>'
].join('')).appendTo($item);
}
var $info = $item.children('.info');
if(!$info.length) $info = $(
[
'<div class="info">',
'<span class="pri"></span>',
'<span class="time label label-light"></span>',
'<div class="user"></div>',
'</div>'
].join('')).appendTo($item);
$item.data('task', item);
$info.children('.pri')
.attr('class', 'pri label-pri label-pri-' + item.pri)
.text(item.pri);
var $time = $info.children('.time');
if(item.deadline && item.deadline !== '0000-00-00')
{
var deadline = $.zui.createDate(item.deadline);
var today = $.zui.createDate(today);
var isExpired = deadline.getTime() < today.getTime();
var dateFormat = (today.getFullYear() === deadline.getFullYear() ? 'MM-dd ' : 'yyyy-MM-dd ') + kanbanLang.deadline;
$time.text($.zui.formatDate(deadline, dateFormat))
.toggleClass('text-red', isExpired)
.show();
}
else
{
$time.hide();
}
var $user = $info.children('.user');
if(item.assignedTo)
{
var member = members[item.assignedTo] || {account: item.assignedTo};
var canAssign = item.status !== 'closed' && item.status !== 'cancel';
var $avatar = $(
canAssign ? '<a class="avatar avatar-sm avatar-user" data-toggle="modal"></a>'
: '<div class="avatar avatar-sm avatar-user"></div>'
).attr('data-name', member.realname || member.account)
.attr('data-id', member.id || member.account);
if(member.avatar)
{
$avatar.append('<img src="' + member.avatar + '" />');
}
$avatar.renderAvatar();
if(canAssign)
{
$avatar.attr('href', createLink('sys.task', 'assignto', 'taskID=' + item.id + '&fromKanban=1'));
}
$user.empty()
.append($avatar)
.attr('title', member.realname || member.account)
.show();
}
else
{
$user.hide();
}
$item.css('background-color', item.color);
$item.toggleClass('has-color', item.color != '#FFFFFF');
if(item.color == '#FFFFFF')
{
$item.find('.info > .label-light').css('background-color', '#F2F2F2');
}
else
{
var colors = {'#E01B1B': '#DD5858', '#F7B500': '#ECC046', '#288427': '#609F60'}
$item.find('.info > .label-light').css('background-color', colors[item.color]);
}
}
/**
* Show error message
* @param {string|object} message Message
*/
function showErrorMessager(message)
{
var html = false;
if(message instanceof Error)
{
message = message.message;
}
else if(typeof message === 'object')
{
html = [];
$.each(message, function(key, msg)
{
html.push($.isArray(msg) ? msg.join('') : String(msg));
});
message = html.join('<br/>');
}
else
{
message = String(message);
}
if(typeof message === 'string' && message.length)
{
$.zui.messager.danger(message, {html: !!html});
}
}
/**
* Update kanban
* @param {string} [regionID] Region ID
* @param {string} [groupID] Group ID
* @param {function(Error)} [callback] Callback on completed
*/
function updateRegion(regionID, groupID, callback)
{
if(!regionID) return false;
if(typeof groupID == 'function')
{
callback = groupID;
groupID = 0;
}
var url = createLink('kanban', 'ajaxGetData', 'kanbanID=' + kanbanID + '&regionID=' + regionID + '&group=' + (groupID || 0));
$.ajax(
{
method: 'get',
dataType: 'json',
url: url,
success: function(response)
{
var kanban = $('#kanban' + regionID).data('zui.kanban');
if(groupID) kanban.renderKanban(response.data);
else kanban.render(response.data);
typeof callback === 'function' && callback();
},
error: function(xhr, status, error)
{
showErrorMessager(error || lang.timeout);
typeof callback === 'function' && callback(error);
}
});
}
/**
* Hide kanban action
*/
function hideKanbanAction()
{
$('.kanban').attr('data-action-enabled', null);
$('.contextmenu').removeClass('contextmenu-show');
$('.contextmenu .contextmenu-menu').removeClass('open').removeClass('in');
$('#moreTasks, #moreColumns').animate({right: -400}, 500);
}
/**
* Open form for adding task
* @param {JQuery} $element Trigger element
*/
function openAddTaskForm($element)
{
var regionID = $element.closest('.kanban').data('id');
var groupID = $element.closest('.kanban-board').data('id');
var laneID = $element.closest('.kanban-lane').data('id');
var columnID = $element.closest('.kanban-col').data('id');
var status = $element.closest('.kanban-col').data('type');
var modalUrl = createLink('sys.task', 'create', 'kanbanID=' + kanbanID + '&from=kanban&regionID=' + regionID + '&groupID=' + groupID + '&laneID=' + laneID + '&columnID=' + columnID + '&status=' + status);
$.zui.modalTrigger.show(
{
url: modalUrl,
width: '1000px'
});
hideKanbanAction();
}
/**
* Reset lane height according to window height.
*/
function resetLaneHeight()
{
var maxHeight = '360px';
if(laneCount < 2)
{
var windowHeight = $(window).height();
var marginTop = $('#mainContent').css('margin-top');
var headerHeight = $('.kanban > .kanban-board:first > .kanban-header').outerHeight();
var actionHeight = $('.kanban > .kanban-board:first > .kanban-lane > .kanban-col:first > .kanban-lane-actions').outerHeight();
maxHeight = windowHeight - parseInt(marginTop) - headerHeight - actionHeight;
}
$('.kanban-lane-items').css('max-height', maxHeight);
}
/**
* Close modal and update kanban data.
*/
function closeModalAndUpdateKanban(regionID)
{
setTimeout(function()
{
$.zui.closeModal();
updateRegion(regionID);
}, 1200);
}
/**
* Status change map
*/
var statusChangeMap =
{
wait: ['doing', 'done', 'cancel'],
doing: ['done', 'cancel'],
done: ['doing', 'closed'],
cancel: ['doing', 'closed'],
closed: ['doing']
};
/**
* Find drop columns
* @param {JQuery} $element Drag element
* @param {JQuery} $root Dnd root element
*/
function findDropColumns($element, $root)
{
var $task = $element;
var task = $task.data('task');
var status = task.status;
var $col = $task.closest('.kanban-col');
var col = $col.data();
var lane = $col.closest('.kanban-lane').data('lane');
var allStatusCanChange = statusChangeMap[status];
hideKanbanAction();
return $root.find('.kanban-lane-col:not([data-type="EMPTY"],[data-type=""])').filter(function()
{
var $newCol = $(this);
var newCol = $newCol.data();
var $newLane = $newCol.closest('.kanban-lane');
var newLane = $newLane.data('lane');
$newCol.addClass('can-drop-here');
return true;
});
}
/**
* Handle drop task
* @param {Object} event Drop event object
*/
function handleDropTask($element, event, kanban)
{
if(!event.target || !event.isNew) return;
var $task = $element;
var $oldCol = $task.closest('.kanban-col');
var $newCol = $(event.target).closest('.kanban-col');
var oldCol = $oldCol.data();
var newCol = $newCol.data();
var oldLane = $oldCol.closest('.kanban-lane').data('lane');
var newLane = $newCol.closest('.kanban-lane').data('lane');
var kanbanID = $task.closest('.kanban-board').data('id');
var regionID = $task.closest('.kanban').data('id');
if(oldCol.id === newCol.id && newLane.id === oldLane.id) return false;
var newStatus = newCol.type;
var task = $task.data('task');
/* Task status not change */
if(newStatus === task.status && task.kanbanLane !== newLane.id)
{
var url = createLink('sys.task', 'move', 'taskID=' + task.id + '&groupID=' + newLane.group);
return $.ajax(
{
method: 'post',
dataType: 'json',
url: url,
data: {lane: newLane.id},
success: function(data)
{
if(data && data.result === 'success') updateRegion(regionID);
else showErrorMessager(data && data.message);
},
error: function(xhr, status, error)
{
showErrorMessager(error || lang.timeout);
}
});
}
/* Show dialog to user for changing status */
var methodToChangeStatus =
{
wait: 'activate',
doing: 'start',
done: 'finish',
cancel: 'cancel',
closed: 'close',
};
$.getJSON(createLink('task', 'move', 'taskID=' + task.id + '&groupID=' + newLane.group + '&laneID=' + newLane.id + '&columnID=' + newCol.id), function(response)
{
if(response.result == 'success') updateRegion(regionID);
});
}
/**
* Handle finish drop task
*/
function handleFinishDrop()
{
$('.kanban').find('.can-drop-here').removeClass('can-drop-here');
}
/**
* Adjust add button postion in column
*/
function adjustAddBtnPosition($kanban)
{
if(!$kanban)
{
$('.kanban').children('.kanban-board').each(function()
{
adjustAddBtnPosition($(this));
});
return;
}
$kanban.find('.kanban-lane-col:not([data-type="EMPTY"])').each(function()
{
var $col = $(this);
var items = $col.children('.kanban-lane-items')[0];
$col.toggleClass('has-scrollbar', items.scrollHeight > items.clientHeight);
});
}
/**
* Kanban action handlers
*/
var kanbanActionHandlers =
{
addItem: openAddTaskForm,
dropItem: handleDropTask
};
/**
* Handle kanban action
*/
function handleKanbanAction(action, $element, event, kanban)
{
$('.kanban').attr('data-action-enabled', action);
var handler = kanbanActionHandlers[action];
if(handler) handler($element, event, kanban);
}
function processMinusBtn()
{
var columnCount = $('#splitTable .child-column').size();
if(columnCount > 2 && columnCount < 10)
{
$('#splitTable .btn-plus').show();
$('#splitTable .btn-close').show();
}
else if(columnCount <= 2)
{
$('#splitTable .btn-close').hide();
}
else if(columnCount >= 10)
{
$('#splitTable .btn-plus').hide();
}
}
function createLaneMenu(options)
{
var lane = options.$trigger.closest('.kanban-lane').data('lane');
var privs = lane.actions;
if(!privs.length) return [];
var items = [];
if(privs.includes('editLane')) items.push({label: kanbanLang.editLane, icon: '', url: createLink('kanban', 'editLane', 'lane=' + lane.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('deleteLane')) items.push({label: kanbanLang.deleteLane, icon: '', url: createLink('kanban', 'deleteLane', 'lane=' + lane.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbanlaneLang.confirmDelete, 'data-confirmDetail': kanbanlaneLang.confirmDeleteDetail}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
function createTaskMenu(options)
{
var task = options.$trigger.closest('.kanban-item').data('task');
var privs = task.actions;
if(!privs.length) return [];
var actions = {
'edit': {'label': taskLang.edit, 'icon': '', 'url': createLink('task', 'edit', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal', 'data-width': '1125px'}},
'move': {'label': taskLang.move, 'icon': '', 'url': createLink('task', 'move', "taskID=" + task.id + "&groupID=" + task.kanbanGroup), 'attrs': {'data-toggle': 'modal', 'data-width': 400}},
'copy': {'label': taskLang.copy, 'icon': '', 'url': createLink('task', 'copy', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal', 'data-width': '1000px'}},
'start': {'label': taskLang.start, 'icon': '', 'url': createLink('task', 'start', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'finish': {'label': taskLang.finish, 'icon': '', 'url': createLink('task', 'finish', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'activate': {'label': taskLang.activate, 'icon': '', 'url': createLink('task', 'activate', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'cancel': {'label': taskLang.cancel, 'icon': '', 'url': createLink('task', 'cancel', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'close': {'label': taskLang.close, 'icon': '', 'url': createLink('task', 'close', "taskID=" + task.id + "&from=kanban"), 'attrs': {'data-toggle': 'modal'}},
'archive': {'label': taskLang.archive, 'icon': '', 'url': createLink('task', 'archive', "taskID=" + task.id + "&from=kanban"), 'attrs': {'class': 'confirmer', 'data-confirmTitle': taskLang.confirmArchive, 'data-confirmDetail': taskLang.confirmArchiveDetail, 'data-confirmButton': taskLang.archive, 'data-confirming': taskLang.archiving}},
'delete': {'label': taskLang.delete, 'icon': '', 'url': createLink('task', 'delete', "taskID=" + task.id + '&from=kanban'), 'attrs': {'class': 'confirmer', 'data-confirmTitle': taskLang.confirmDelete, 'data-confirmDetail': taskLang.confirmDeleteDetail}},
'setColor': {'label': taskLang.setColor, 'icon': '', 'url': createLink('task', 'setColor', "taskID=" + task.id), 'attrs': {'data-toggle': 'modal', 'data-width': 400}},
};
var items = [];
$.each(actions, function(actionKey, actionItem)
{
if(privs.includes(actionKey)) items.push({label: actionItem.label, icon: '', url: actionItem.url, attrs: actionItem.attrs});
});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
function createColumnMenu(options)
{
var column = options.$trigger.closest('.kanban-col').data('col');
var privs = column.actions;
if(!privs.length) return [];
var items = [];
if(privs.includes('setWIP')) items.push({label: kanbanLang.WIP, icon: '', url: createLink('kanban', 'setWIP', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('editColumn')) items.push({label: kanbanLang.editColumn, icon: '', url: createLink('kanban', 'editColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('createColumn'))
{
items.push({label: kanbanLang.createColumnOnLeft, icon: '', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=left'), attrs: {'data-toggle': 'modal'}});
items.push({label: kanbanLang.createColumnOnRight, icon: '', url: createLink('kanban', 'createColumn', 'columnID=' + column.id + '&position=right'), attrs: {'data-toggle': 'modal'}});
}
if(privs.includes('copyColumn')) items.push({label: kanbanLang.copyColumn, icon: '', url: createLink('kanban', 'copyColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('splitColumn')) items.push({label: kanbanLang.splitColumn, icon: '', url: createLink('kanban', 'splitColumn', 'columnID=' + column.id), attrs: {'data-toggle': 'modal'}});
if(privs.includes('archiveColumn')) items.push({label: kanbanLang.archiveColumn, icon: '', url: createLink('kanban', 'archiveColumn', 'columnID=' + column.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbancolumnLang.confirmArchive, 'data-confirmDetail': kanbancolumnLang.confirmArchiveDetail, 'data-confirmButton': lang.archive, 'data-confirming': lang.archiving}});
if(privs.includes('deleteColumn')) items.push({label: kanbanLang.deleteColumn, icon: '', url: createLink('kanban', 'deleteColumn', 'columnID=' + column.id), className: 'confirmer', attrs: {'data-confirmTitle': kanbancolumnLang.confirmDelete, 'data-confirmDetail': kanbancolumnLang.confirmDeleteDetail}});
var bounds = options.$trigger[0].getBoundingClientRect();
items.$options = {x: bounds.right, y: bounds.top};
return items;
}
/* Define menu creators */
window.menuCreators =
{
task: createTaskMenu,
lane: createLaneMenu,
column: createColumnMenu
};
/**
* init Kanban
*/
function initKanban($kanban)
{
var id = $kanban.data('id');
var region = regions[id];
$kanban.kanban(
{
data: region.groups,
maxColHeight: 510,
createColumnText: kanbanLang.createColumn,
addItemText: '',
itemRender: renderKanbanItem,
onAction: handleKanbanAction,
onRenderKanban: adjustAddBtnPosition,
onRenderLaneName: renderLaneName,
onRenderHeaderCol: renderHeaderCol,
onRenderCount: renderCount,
droppable:
{
target: findDropColumns,
finish: handleFinishDrop,
mouseButton: 'left'
}
}).on('click', '.action-cancel', hideKanbanAction);
}
/**
* Init when page ready
*/
$(function()
{
if($.cookie('isFullScreen') == 1) fullScreen();
/* Init first kanban */
$('.kanban').each(function()
{
initKanban($(this));
});
$('.icon-double-angle-up,.icon-double-angle-down').on('click', function()
{
$(this).toggleClass('icon-double-angle-up icon-double-angle-down');
$(this).parents('.region').find('.kanban').toggle();
hideKanbanAction();
});
$('.region-header').on('click', '.action', hideKanbanAction);
$('#TRAction').on('click', '.btn', hideKanbanAction);
/* Hide action box when user click document */
$(document).on('click', function(e)
{
$('.kanban').each(function()
{
var currentAction = $(this).kanban().attr('data-action-enabled');
var canHideAction = (currentAction === 'headerMore' || currentAction === 'editLaneName')
&& !$(e.target).closest('.action,.action-box').length;
if(canHideAction) hideKanbanAction();
});
});
/* Init contextmenu */
$('#kanban').on('click', '[data-contextmenu]', function(event)
{
var $trigger = $(this);
var menuType = $trigger.data('contextmenu');
var menuCreator = window.menuCreators[menuType];
if(!menuCreator) return;
var options = $.extend({event: event, $trigger: $trigger}, $trigger.data());
var items = menuCreator(options);
if(!items || !items.length) return;
$.zui.ContextMenu.show(items, items.$options || {event: event});
});
/* Adjust the add button position on window resize */
$(window).on('resize', function(a)
{
adjustAddBtnPosition();
});
resetLaneHeight();
/* Hide contextmenu when page scroll */
$(window).on('scroll', function()
{
$.zui.ContextMenu.hide();
});
$(document).on('click', '#splitTable .btn-plus', function()
{
var tr = $(this).closest('tr');
tr.after($('#childTpl').html().replace(/key/g, key));
tr.next().find('input[name^=color]').colorPicker();
key++;
processMinusBtn();
return false;
});
/* Remove a trade detail item. */
$(document).on('click', '#splitTable .btn-close', function()
{
$(this).closest('tr').remove();
processMinusBtn();
return false;
});
/* Init sortable */
var sortType = '';
$('#kanban').sortable(
{
selector: '.region, .kanban-board, .kanban-lane',
trigger: '.region.sort > .region-header, .kanban-board.sort > .kanban-header > .kanban-group-header, .kanban-lane.sort > .kanban-lane-name',
targetSelector: function($ele)
{
/* Sort regions */
if($ele.hasClass('region'))
{
sortType = 'region';
return $ele.parent().children('.region');
}
/* Sort boards */
if($ele.hasClass('kanban-board'))
{
sortType = 'board';
return $ele.parent().children('.kanban-board');
}
/* Sort lanes */
if($ele.hasClass('kanban-lane'))
{
sortType = 'lane';
return $ele.parent().children('.kanban-lane');
}
/* Sort lanes */
if($ele.hasClass('kanban-item'))
{
sortType = 'item';
return $ele.parent().children('.kanban-item');
}
},
start: function(e)
{
if(sortType == 'region')
{
$('.icon-double-angle-up').attr('class', 'icon-double-angle-down');
$('.region').find('.kanban').hide();
hideKanbanAction();
}
},
finish: function(e)
{
var url = '';
var orders = [];
e.list.each(function(index, data)
{
orders.push(data.item.data('id'));
});
if(sortType == 'region')
{
$('.icon-double-angle-down').attr('class', 'icon-double-angle-up');
$('.region').find('.kanban').show();
}
if(sortType == 'board')
{
var region = e.element.parent().data('id');
url = createLink('kanban', 'sortGroup', 'region=' + region + '&groups=' + orders.join(','));
}
if(sortType == 'lane')
{
var region = e.element.parent().parent().data('id');
url = createLink('kanban', 'sortLane', 'region=' + region + '&lanes=' + orders.join(','));
}
if(sortType == 'item')
{
url = createLink('task', 'sort', 'kanbanID=' + kanbanID + '&tasks=' + orders.join(','));
}
if(!url) return true;
$.getJSON(url, function(response)
{
if(response.result == 'fail' && response.message.length)
{
bootbox.alert(response.message);
setTimeout(function(){return location.reload()}, 3000);
}
});
}
});
});
+12 -1
View File
@@ -81,6 +81,7 @@ $lang->kanban->laneGroup = '泳道分组';
$lang->kanban->cardsSort = '卡片排序';
$lang->kanban->moreAction = '更多操作';
$lang->kanban->noGroup = '无';
$lang->kanban->limitExceeded = '超出在制品限制';
$lang->kanban->error = new stdclass();
$lang->kanban->error->mustBeInt = '在制品数量必须是正整数。';
@@ -140,9 +141,19 @@ $lang->kanbanspace->featureBar['my'] = '我的空间';
$lang->kanbanspace->featureBar['other'] = '其他空间';
$lang->kanbanspace->featureBar['closed'] = '已关闭';
$lang->kanban->defaultColumn = array('未开始', '进行中', '已完成', '已关闭');
$lang->kanbancolumn = new stdclass();
$lang->kanbancolumn->name = $lang->kanban->columnName;
$lang->kanbancolumn->limit = $lang->kanban->WIPCount;
$lang->kanbanlane = new stdclass();
$lang->kanbanlane->name = $lang->kanban->laneName;
$lang->kanbanlane->name = $lang->kanban->laneName;
$lang->kanbanlane->common = '泳道';
$lang->kanbanlane->default = '默认泳道';
$lang->kanbanregion = new stdclass();
$lang->kanbanregion->name = '看板区域';
$lang->kanbanregion->default = '默认区域';
$lang->kanbancard = new stdclass();
+467 -5
View File
@@ -13,6 +13,394 @@
<?php
class kanbanModel extends model
{
/**
* Init a kanban.
*
* @param int $kanbanID
* @param string $type default|new
* @access public
* @return void
*/
public function initKanban($kanbanID, $type = 'default')
{
$kanban = $this->getByID($kanbanID);
$function = $type == 'default' ? 'createDefaultRegion' : 'createRegion';
$regionID = $this->$function($kanban);
if(dao::isError()) return false;
$groupID = $this->createGroup($kanban->id, $regionID);
if(dao::isError()) return false;
$this->createDefaultLane($kanban, $regionID, $groupID);
if(dao::isError()) return false;
$this->createDefaultColumns($kanban, $regionID, $groupID);
if(dao::isError()) return false;
}
/**
* Create a kanban group.
*
* @param int $kanbanID
* @param int $regionID
* @access public
* @return int
*/
public function createGroup($kanbanID, $regionID)
{
$maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER)
->where('objectType')->eq('group')
->andWhere('parentID')->eq($regionID)
->andWhere('parentType')->eq('region')
->andWhere('account')->eq('')
->fetch('maxOrder');
$order = $maxOrder ? $maxOrder + 1 : 1;
$group = new stdclass();
$group->kanban = $kanbanID;
$group->region = $regionID;
$this->dao->insert(TABLE_KANBANGROUP)->data($group)->autoCheck()->exec();
if(dao::isError()) return false;
$groupID = $this->dao->lastInsertID();
$this->saveOrder($regionID, 'region', $groupID, 'group', '', $order);
return $groupID;
}
/**
* Create a default kanban region.
*
* @param object $kanban
* @access public
* @return int
*/
public function createDefaultRegion($kanban)
{
$region = new stdclass();
$region->name = $this->lang->kanbanregion->default;
$region->kanban = $kanban->id;
$region->space = $kanban->space;
$region->createdBy = $this->app->user->account;
$region->createdDate = helper::today();
return $this->createRegion($kanban, $region);
}
/**
* Create a new region.
*
* @param object $kanban
* @param object $region
* @access public
* @return int
*/
public function createRegion($kanban, $region = null)
{
$account = $this->app->user->account;
$order = 1;
if(!$region)
{
$maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANORDER)
->where('objectType')->eq('region')
->andWhere('parentID')->eq($kanban->id)
->andWhere('parentType')->eq('kanban')
->andWhere('account')->eq('')
->fetch('maxOrder');
$order = $maxOrder + 1;
$region = fixer::input('post')
->add('kanban', $kanban->id)
->add('space', $kanban->space)
->add('createdBy', $account)
->add('createdDate', helper::today())
->get();
}
$this->dao->insert(TABLE_KANBANREGION)->data($region)
->batchCheck($this->config->kanban->require->createregion, 'notempty')
->check('name', 'unique', "kanban = {$kanban->id} AND deleted = '0'")
->autoCheck()
->exec();
$regionID = $this->dao->lastInsertID();
$this->loadModel('action')->create('kanbanRegion', $regionID, 'Created');
$this->saveOrder($kanban->id, 'kanban', $regionID, 'region', $account, $order);
return $regionID;
}
/**
* Create default lane.
*
* @param object $kanban
* @param int $regionID
* @param int $groupID
* @access public
* @return int
*/
public function createDefaultLane($kanban, $regionID, $groupID)
{
$lane = new stdclass();
$lane->name = $this->lang->kanbanlane->default;
$lane->group = $groupID;
$lane->region = $regionID;
$lane->type = 'common';
$lane->lastEditedTime = helper::now();
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
$laneID = $this->dao->lastInsertId();
$this->saveOrder($regionID, 'region', $laneID, 'lane', '', 1);
return $laneID;
}
/**
* Create default kanban columns.
*
* @param object $kanban
* @param int $regionID
* @param int $groupID
* @access public
* @return void
*/
public function createDefaultColumns($kanban, $regionID, $groupID)
{
$index = 1;
foreach($this->lang->kanban->defaultColumn as $columnName)
{
$column = new stdclass();
$column->region = $regionID;
$column->group = $groupID;
$column->name = $columnName;
$column->type = $index;
$column->limit = -1;
$this->dao->insert(TABLE_KANBANCOLUMN)->data($column)->exec();
$this->saveOrder($regionID, 'region', $this->dao->lastInsertID(), 'column', '', $index);
$index ++;
}
return !dao::isError();
}
/**
* Save kanban object order.
*
* @param int $parentID
* @param string $parentType
* @param int $objectID
* @param string $objectType
* @param string $account
* @param int $order
* @access public
* @return void
*/
public function saveOrder($parentID, $parentType, $objectID, $objectType, $account, $order)
{
$kanbanOrder = new stdclass();
$kanbanOrder->parentID = $parentID;
$kanbanOrder->parentType = $parentType;
$kanbanOrder->objectID = $objectID;
$kanbanOrder->objectType = $objectType;
$kanbanOrder->account = $account;
$kanbanOrder->order = $order;
$this->dao->insert(TABLE_KANBANORDER)->data($kanbanOrder)->exec();
return !dao::isError();
}
/**
* Get kanban by id.
*
* @param int $kanbanID
* @access public
* @return object
*/
public function getByID($kanbanID)
{
return $this->dao->findByID($kanbanID)->from(TABLE_KANBAN)->fetch();
}
/**
* Get kanban data.
*
* @param int $kanbanID
* @access public
* @return void
*/
public function getKanbanData($kanbanID)
{
$kanbanData = array();
$actions = array('sortGroup');
$regions = $this->getRegionPairs($kanbanID);
$groupGroup = $this->getGroupGroupByRegions(array_keys($regions));
$laneGroup = $this->getLaneGroupByRegions(array_keys($regions));
$columnGroup = $this->getColumnGroupByRegions(array_keys($regions));
//$taskGroup = $this->getTaskGroupByProject($kanbanID);
foreach($regions as $regionID => $regionName)
{
$region = new stdclass();
$region->id = $regionID;
$region->name = $regionName;
$region->laneCount = 0;
$groups = zget($groupGroup, $regionID, array());
foreach($groups as $group)
{
$lanes = zget($laneGroup, $group->id, array());
if(!$lanes) continue;
foreach($lanes as $lane) $lane->items = isset($taskGroup[$lane->id]) ? $taskGroup[$lane->id] : array();
$group->columns = zget($columnGroup, $group->id, array());
$group->lanes = $lanes;
$group->actions = array();
foreach($actions as $action)
{
if(commonModel::hasPriv('kanban', $action)) $group->actions[] = $action;
}
$region->groups[] = $group;
$region->laneCount += count($lanes);
}
$kanbanData[$regionID] = $region;
}
return $kanbanData;
}
/**
* Get ordered region pairs.
*
* @param int $kanbanID
* @access public
* @return array
*/
public function getRegionPairs($kanbanID)
{
return $this->dao->select('id,name')->from(TABLE_KANBANREGION)
->where('kanban')->eq($kanbanID)
->andWhere('deleted')->eq('0')
->orderBy('id_asc')
->fetchPairs();
}
/**
* Get kanban group by regions.
*
* @param array $regions
* @access public
* @return array
*/
public function getGroupGroupByRegions($regions)
{
return $this->dao->select('*')->from(TABLE_KANBANGROUP)
->where('region')->in($regions)
->orderBy('id_asc')
->fetchGroup('region');
}
/**
* Get lane group by regions.
*
* @param array $regions
* @access public
* @return array
*/
public function getLaneGroupByRegions($regions)
{
$laneGroup = $this->dao->select('*')->from(TABLE_KANBANLANE)
->where('deleted')->eq('0')
->andWhere('region')->in($regions)
->orderBy('order')
->fetchGroup('group');
$actions = array('editLane', 'sortLane', 'deleteLane');
foreach($laneGroup as $lanes)
{
foreach($lanes as $lane)
{
$lane->actions = array();
foreach($actions as $action)
{
if($this->isClickable($lane, $action)) $lane->actions[] = $action;
}
}
}
return $laneGroup;
}
/**
* Get column group by regions.
*
* @param array $regions
* @access public
* @return array
*/
public function getColumnGroupByRegions($regions)
{
$columnGroup = $this->dao->select("*")->from(TABLE_KANBANCOLUMN)
->where('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->andWhere('region')->in($regions)
->orderBy('order')
->fetchGroup('group', 'id');
$actions = array('createColumn', 'copyColumn', 'editColumn', 'splitColumn', 'setWIP', 'archiveColumn', 'restoreColumn', 'deleteColumn');
/* Group by parent. */
$parentColumnGroup = array();
foreach($columnGroup as $group => $columns)
{
foreach($columns as $column)
{
$column->actions = array();
/* Judge column action priv. */
foreach($actions as $action)
{
if($this->isClickable($column, $action)) $column->actions[] = $action;
}
if($column->parent) continue;
$parentColumnGroup[$group][] = $column;
}
}
$columnData = array();
foreach($parentColumnGroup as $group => $parentColumns)
{
foreach($parentColumns as $parentColumn)
{
$columnData[$group][] = $parentColumn;
foreach($columnGroup[$group] as $column)
{
if($column->parent == $parentColumn->id)
{
$parentColumn->asParent = true;
$column->parentType = 'column' . $column->parent;
$columnData[$group][] = $column;
}
}
}
}
return $columnData;
}
/**
* Get Kanban by execution id.
*
@@ -235,7 +623,7 @@ class kanbanModel extends model
* @access public
* @return void
*/
public function createLanes($executionID, $type = 'all', $groupBy = 'default')
public function createExecutionLane($executionID, $type = 'all', $groupBy = 'default')
{
if($groupBy == 'default' or $type == 'all')
{
@@ -246,7 +634,7 @@ class kanbanModel extends model
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
$laneID = $this->dao->lastInsertId();
$this->createColumns($laneID, $type, $executionID);
$this->createExecutionColumns($laneID, $type, $executionID);
}
}
else
@@ -295,7 +683,7 @@ class kanbanModel extends model
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
$laneID = $this->dao->lastInsertId();
$this->createColumns($laneID, $type, $executionID, $groupBy, $groupKey);
$this->createExecutionColumns($laneID, $type, $executionID, $groupBy, $groupKey);
}
}
}
@@ -311,7 +699,7 @@ class kanbanModel extends model
* @access public
* @return void
*/
public function createColumns($laneID, $type, $executionID, $groupBy = '', $groupValue = '')
public function createExecutionColumns($laneID, $type, $executionID, $groupBy = '', $groupValue = '')
{
$objects = array();
@@ -644,7 +1032,7 @@ class kanbanModel extends model
$this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
$laneID = $this->dao->lastInsertId();
$this->createColumns($laneID, $type, $executionID, $groupBy, $groupKey);
$this->createExecutionColumns($laneID, $type, $executionID, $groupBy, $groupKey);
}
$this->resetLaneOrder($executionID, $type, $groupBy);
@@ -1011,4 +1399,78 @@ class kanbanModel extends model
}
return $menus;
}
/**
* Check if user can execute an action.
*
* @param object $object
* @param string $action
* @access public
* @return bool
*/
public function isClickable($object, $action)
{
$action = strtolower($action);
$clickable = commonModel::hasPriv('kanban', $action);
if(!$clickable) return false;
switch($action)
{
case 'sortlane' :
case 'deletelane' :
if($object->deleted != '0') return false;
$count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANLANE)
->where('deleted')->eq('0')
->andWhere('region')->eq($object->region)
->beginIF($action == 'sortlane')->andWhere('`group`')->eq($object->group)->fi()
->fetch('count');
return $count > 1;
case 'createcolumn' :
case 'copycolumn' :
case 'splitcolumn' :
if($object->parent) return false; // The current column is a child column.
$count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN)
->where('parent')->eq($object->id)
->andWhere('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->fetch('count');
return $count == 0; // The column has child columns.
case 'restoreColumn' :
if($object->parent)
{
$parent = $this->getColumnByID($object->parent);
if($parent->deleted == '1' || $parent->archived == '1') return false;
}
return $object->archived == '1';
case 'archivecolumn' :
//if($object->archived != '0') return false; // The column has been archived.
case 'deletecolumn' :
if($object->deleted != '0') return false;
if($object->parent)
{
$childrenCount = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN)
->where('parent')->eq($object->parent)
->andWhere('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->fetch('count');
return $childrenCount > 2;
}
$count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANCOLUMN)
->where('region')->eq($object->region)
->andWhere('parent')->eq(0)
->andWhere('`group`')->eq($object->group)
->andWhere('deleted')->eq('0')
//->andWhere('archived')->eq('0')
->fetch('count');
return $count > 1;
}
return true;
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kanban.html.php';?>
<?php
$laneCount = 0;
foreach($regions as $region) $laneCount += $region->laneCount;
js::set('regions', $regions);
js::set('kanbanLang', $lang->kanban);
js::set('kanbanlaneLang', $lang->kanbanlane);
js::set('kanbancolumnLang', $lang->kanbancolumn);
js::set('taskLang', $lang->kanbancard);
js::set('kanbanID', $kanban->id);
//js::set('members', $members);
js::set('laneCount', $laneCount);
js::set('today', helper::today());
$canSortRegion = commonModel::hasPriv('kanban', 'sortRegion') && count($regions) > 1;
$canEditRegion = commonModel::hasPriv('kanban', 'editRegion');
$canDeleteRegion = commonModel::hasPriv('kanban', 'deleteRegion');
$canCreateLane = commonModel::hasPriv('kanban', 'createLane');
?>
<div id="kanban">
<?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>
<i class="icon icon-double-angle-up"></i>
<?php if($canEditRegion || $canCreateLane || $canDeleteRegion):?>
<button class="btn btn-link action" type="button" data-toggle="dropdown"><i class="icon icon-more-v"></i></button>
<ul class="dropdown-menu pull-right">
<?php if($canEditRegion) echo '<li>' . html::a(inlink('editRegion', "regionID={$region->id}"), $this->lang->kanban->editRegion, "data-toggle='modal'") . '</li>';?>
<?php if($canCreateLane) echo '<li>' . html::a(inlink('createLane', "kanbanID={$project->id}&regionID={$region->id}"), $this->lang->kanban->createLane, "data-toggle='modal'") . '</li>';?>
<?php if($canDeleteRegion) echo '<li>' . html::a(inlink('deleteRegion', "regionID={$region->id}"), $this->lang->kanban->deleteRegion, "class='confirmer' data-confirmTitle='{$lang->kanbanregion->confirmDelete}' data-confirmDetail='{$lang->kanbanregion->confirmDeleteDetail}'") . '</li>';?>
</ul>
<?php endif;?>
</div>
<div id='kanban<?php echo $region->id;?>' data-id='<?php echo $region->id;?>' class='kanban'></div>
</div>
<?php endforeach;?>
</div>
<div id='moreTasks'></div>
<div id='moreColumns'></div>
<?php include '../../common/view/footer.html.php';?>
+1 -4
View File
@@ -47,10 +47,7 @@ class messageModel extends model
$objectActions = array();
foreach($this->config->message->objectTypes as $objectType => $actions)
{
foreach($actions as $action)
{
$objectActions[$objectType][$action] = str_replace($this->lang->webhook->trimWords, '', $this->lang->action->label->$action);
}
foreach($actions as $action) $objectActions[$objectType][$action] = $this->lang->message->label->$action;
}
return $objectActions;
}
+5 -5
View File
@@ -96,7 +96,7 @@
}
else
{
echo html::a($this->createLink('task', 'view', "taskID=$task->id", '', '', $task->project), $task->name, null, "style='color: $task->color' data-group='execution'");
echo html::a($this->createLink('task', 'view', "taskID=$task->id", '', '', $task->project), $task->name, null, "style='color: $task->color'");
}
?>
<?php if(!empty($task->children)) echo '<a class="task-toggle" data-id="' . $task->id . '"><i class="icon icon-angle-double-right"></i></a>';?>
@@ -104,7 +104,7 @@
<?php if($config->systemMode == 'new'):?>
<td class='c-project' title="<?php echo $task->projectName;?>"><?php echo html::a($this->createLink('project', 'index', "projectID=$task->project"), $task->projectName);?></td>
<?php endif;?>
<td class='c-project' title="<?php echo $task->executionName;?>"><?php echo html::a($this->createLink('execution', 'task', "executionID=$task->execution"), $task->executionName, '', "data-group='execution'");?></td>
<td class='c-project' title="<?php echo $task->executionName;?>"><?php echo html::a($this->createLink('execution', 'task', "executionID=$task->execution"), $task->executionName, '');?></td>
<?php if($type != 'openedBy'): ?>
<td class='c-user'><?php echo zget($users, $task->openedBy);?></td>
<?php endif;?>
@@ -171,7 +171,7 @@
<?php if($config->systemMode == 'new'):?>
<td class='c-project' title="<?php echo $child->projectName;?>"><?php echo html::a($this->createLink('project', 'view', "projectID=$child->project"), $child->projectName);?></td>
<?php endif;?>
<td class='c-project' title="<?php echo $child->projectName;?>"><?php echo html::a($this->createLink('execution', 'task', "executionID=$child->project"), $child->executionName, '', "data-group='execution'");?></td>
<td class='c-project' title="<?php echo $child->projectName;?>"><?php echo html::a($this->createLink('execution', 'task', "executionID=$child->project"), $child->executionName, '');?></td>
<?php if($type != 'openedBy'): ?>
<td class='c-user'><?php echo zget($users, $child->openedBy);?></td>
<?php endif;?>
@@ -208,8 +208,8 @@
common::printIcon('task', 'finish', "taskID=$child->id", $child, 'list', '', '', 'iframe', true, '', '', $child->project);
common::printIcon('task', 'recordEstimate', "taskID=$child->id", $child, 'list', 'time', '', 'iframe', true, '', '', $child->project);
common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list', '', '', '', '', 'data-group="execution"', '', $child->project);
common::printIcon('task', 'batchCreate', "executionID=$child->execution&storyID=$child->story&moduleID=$child->module&taskID=$child->id&iframe=true", $child, 'list', 'split', '', 'iframe', true, 'data-group="execution"', $this->lang->task->children, $child->project);
common::printIcon('task', 'edit', "taskID=$child->id", $child, 'list', '', '', '', '', '', '', $child->project);
common::printIcon('task', 'batchCreate', "executionID=$child->execution&storyID=$child->story&moduleID=$child->module&taskID=$child->id&iframe=true", $child, 'list', 'split', '', 'iframe', true, '', $this->lang->task->children, $child->project);
}
}
?>
+1
View File
@@ -1266,6 +1266,7 @@ class product extends control
if($_POST)
{
$this->product->manageLine();
if(dao::isError()) die(js::error(dao::getError()));
die(js::reload('parent'));
}
-1
View File
@@ -1 +0,0 @@
#lineName {border-left-color: gainsboro !important;}
+2 -2
View File
@@ -6,5 +6,5 @@ td.normal {color: #00da88;}
td.acl {white-space: nowrap;}
.row > .col-sm-12:first-child {padding-bottom: 20px;}
.c-product ,.c-release ,.c-code {width: 100px !important;}
.c-openedBy ,.c-acl ,.c-common {width:110px !important;}
.c-bugs {width:120px !important;}
.c-openedBy ,.c-acl ,.c-prs ,.c-common {width:110px !important;}
.c-bugs ,.c-type {width:120px !important;}
+4 -2
View File
@@ -1,6 +1,6 @@
$(function()
{
if(programID) $('#line_chosen').addClass('hidden');
if(programID || systemMode == 'classic') $('#line_chosen').addClass('hidden');
$('#lineName').css('border-left-color', '');
})
/**
@@ -61,12 +61,14 @@ function toggleLine(obj)
{
$('form .line-no-exist').removeClass('hidden');
$('form .line-exist').addClass('hidden');
$('#line_chosen').addClass('hidden');
$line.attr('disabled', 'disabled');
}
else
{
$('form .line-exist').removeClass('hidden');
$('#line').removeClass('hidden');
$('form .line-no-exist').addClass('hidden');
$('#line_chosen').removeClass('hidden');
$line.removeAttr('disabled');
}
}
+1
View File
@@ -87,6 +87,7 @@ $lang->product->programChangeTip = "The projects linked with this {$lang->pr
$lang->product->notChangeProgramTip = "The {$lang->SRCommon} of {$lang->productCommon} has been linked to the following projects, please cancel the link before proceeding";
$lang->product->confirmChangeProgram = "The projects linked with this {$lang->productCommon}: %s is also linked with other products, whether to transfer projects to the modified program set.";
$lang->product->changeProgramError = "The {$lang->SRCommon} of this {$lang->productCommon} has been linked to the project, please unlink it before proceeding";
$lang->product->programEmpty = 'Program should not be empty!';
$lang->product->id = 'ID';
$lang->product->program = "Program";
+2 -1
View File
@@ -24,7 +24,7 @@ $lang->product->mine = '我负责';
$lang->product->other = '其他';
$lang->product->closed = '已关闭';
$lang->product->updateOrder = '排序';
$lang->product->all = "{$lang->productCommon}列表";
$lang->product->all = "所有{$lang->productCommon}";
$lang->product->manageLine = "维护{$lang->productCommon}线";
$lang->product->newLine = "新建{$lang->productCommon}线";
$lang->product->export = '导出数据';
@@ -87,6 +87,7 @@ $lang->product->programChangeTip = "如下项目只关联了该{$lang->produ
$lang->product->notChangeProgramTip = "该{$lang->productCommon}的{$lang->SRCommon}已经关联到如下项目,请取消关联后再操作";
$lang->product->confirmChangeProgram = "如下项目既关联了该{$lang->productCommon}又关联了其他{$lang->productCommon},请确认是否继续关联该{$lang->productCommon},勾选后将取消与其他{$lang->productCommon}的关联关系,同时转移至新项目集下。";
$lang->product->changeProgramError = "该{$lang->productCommon}的{$lang->SRCommon}已经关联到项目,请取消关联后再操作";
$lang->product->programEmpty = '项目集不能为空';
$lang->product->id = '编号';
$lang->product->program = "所属项目集";
+7 -1
View File
@@ -810,7 +810,13 @@ class productModel extends model
$maxOrder = $maxOrder ? $maxOrder : 0;
foreach($data->modules as $id => $name)
{
if(!$name) continue;
if(empty($name)) continue;
if($this->config->systemMode == 'new' and empty($data->programs[$id]))
{
dao::$errors[] = $this->lang->product->programEmpty;
return false;
}
$line->name = strip_tags(trim($name));
$line->root = $data->programs[$id];
+1 -1
View File
@@ -132,7 +132,7 @@ $projectIDParam = $isProjectStory ? "projectID=$projectID&" : '';
<div class="btn-toolbar pull-right">
<?php if($this->app->rawModule != 'projectstory') common::printIcon('story', 'report', "productID=$productID&branchID=$branch&storyType=$storyType&browseType=$browseType&moduleID=$moduleID&chartType=pie", '', 'button', 'bar-chart muted'); ?>
<div class="btn-group">
<button class="btn btn-link" data-toggle="dropdown"><i class="icon icon-export muted"></i> <span class="text"><?php echo $lang->export ?></span> <span class="caret"></span></button>
<button class="btn pull-right btn-link" data-toggle="dropdown"><i class="icon icon-export muted"></i> <span class="text"><?php echo $lang->export ?></span> <span class="caret"></span></button>
<ul class="dropdown-menu" id='exportActionMenu'>
<?php
$tab = $isProjectStory ? 'project' : 'product';
+6 -6
View File
@@ -34,7 +34,7 @@
<?php if($config->systemMode == 'classic' or $programID):?>
<td>
<div class='input-group'>
<?php echo html::select("line", $lines, '', "class='form-control hidden line-exist'");?>
<?php echo html::select("line", $lines, '', "class='form-control hidden line-exist chosen'");?>
<?php echo html::input("lineName", '', "class='form-control line-no-exist'");?>
<?php if(count($lines)):?>
<span class='input-group-addon'>
@@ -56,23 +56,23 @@
</tr>
<tr>
<th><?php echo $lang->product->code;?></th>
<td><?php echo html::input('code', '', "class='form-control' required");?></td><td></td>
<td><?php echo html::input('code', '', "class='form-control' required");?></td>
</tr>
<tr>
<th><?php echo $lang->product->PO;?></th>
<td><?php echo html::select('PO', $poUsers, $this->app->user->account, "class='form-control chosen'");?></td><td></td>
<td><?php echo html::select('PO', $poUsers, $this->app->user->account, "class='form-control chosen'");?></td>
</tr>
<tr>
<th><?php echo $lang->product->QD;?></th>
<td><?php echo html::select('QD', $qdUsers, '', "class='form-control chosen'");?></td><td></td>
<td><?php echo html::select('QD', $qdUsers, '', "class='form-control chosen'");?></td>
</tr>
<tr>
<th><?php echo $lang->product->RD;?></th>
<td><?php echo html::select('RD', $rdUsers, '', "class='form-control chosen'");?></td><td></td>
<td><?php echo html::select('RD', $rdUsers, '', "class='form-control chosen'");?></td>
</tr>
<tr>
<th><?php echo $lang->product->reviewer;?></th>
<td><?php echo html::select('reviewer[]', $users, '', "class='form-control chosen' multiple");?></td><td></td>
<td><?php echo html::select('reviewer[]', $users, '', "class='form-control chosen' multiple");?></td>
</tr>
<tr>
<th><?php echo $lang->product->type;?></th>
+6 -4
View File
@@ -32,9 +32,11 @@
<thead>
<tr>
<th class='c-id'><?php echo $lang->idAB;?></th>
<th><?php echo $lang->project->name;?></th>
<?php if($config->systemMode == 'new'):?>
<th class='c-program'><?php echo $lang->program->common;?></th>
<th><?php echo $lang->project->name;?></th>
<?php else:?>
<th><?php echo $lang->execution->name;?></th>
<?php endif;?>
<th class='c-user text-left'><?php echo $lang->project->PM;?></th>
<th class='c-date'><?php echo $lang->project->begin;?></th>
@@ -51,6 +53,9 @@
<?php foreach($projectStats as $project):?>
<tr>
<td><?php printf('%03d', $project->id);?></td>
<?php if($config->systemMode == 'new'):?>
<td title='<?php echo $project->programName;?>' class='text-ellipsis'><?php echo $project->programName;?></td>
<?php endif;?>
<td class='text-left'>
<?php
if($config->systemMode == 'new')
@@ -63,9 +68,6 @@
}
?>
</td>
<?php if($config->systemMode == 'new'):?>
<td title='<?php echo $project->programName;?>' class='text-ellipsis'><?php echo $project->programName;?></td>
<?php endif;?>
<td class='padding-right'>
<?php $userID = isset($PMList[$project->PM]) ? $PMList[$project->PM]->id : ''?>
<?php if(!empty($project->PM)) echo html::a($this->createLink('user', 'profile', "userID=$userID", '', true), zget($users, $project->PM), '', "data-toggle='modal' data-type='iframe' data-width='800'");?>
+1 -13
View File
@@ -73,32 +73,20 @@
<table class="table table-data data-basic">
<tbody>
<tr>
<?php if(!empty($product->code)):?>
<th class="c-code"><?php echo $lang->product->code;?></th>
<td><strong><?php echo $product->code;?></strong></td>
<?php else:?>
<th><?php echo $lang->product->type;?></th>
<td><strong><?php echo zget($lang->product->typeList, $product->type);?></strong></td>
<?php endif;?>
<th class="c-openedBy"><?php echo $lang->story->openedBy?></th>
<td colspan="2"><strong><?php echo zget($users, $product->createdBy);?></strong></td>
</tr>
<tr>
<?php if(!empty($product->code)):?>
<th><?php echo $lang->product->type;?></th>
<td><strong><?php echo zget($lang->product->typeList, $product->type);?></strong></td>
<?php else:?>
<th><?php echo $lang->productCommon . $lang->product->status;?></th>
<td class="<?php echo $product->status;?>"><strong><?php echo zget($lang->product->statusList, $product->status);?></strong></td>
<?php endif;?>
<th><?php echo $lang->story->openedDate?></th>
<td colspan="2"><strong><?php echo formatTime($product->createdDate, DT_DATE1);?></strong></td>
</tr>
<tr>
<?php if(!empty($product->code)):?>
<th class="w-80px"><?php echo $lang->productCommon ." ". $lang->product->status;?></th>
<th class="c-type"><?php echo $lang->productCommon . $lang->product->status;?></th>
<td class="<?php echo $product->status;?>"><strong><?php echo zget($lang->product->statusList, $product->status);?></strong></td>
<?php endif;?>
<th class="c-acl"><?php echo $lang->product->acl;?></th>
<td <?php echo empty($product->code) ? "colspan='4'" : "colspan='2'";?>><strong><?php echo $lang->product->aclList[$product->acl];?></strong></td>
</tr>
+1 -1
View File
@@ -298,7 +298,7 @@ class productplan extends control
if($plan->parent == '-1') $this->view->childrenPlans = $this->productplan->getChildren($plan->id);
$this->loadModel('datatable');
$this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story');
$this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, 'all');
$this->view->title = "PLAN #$plan->id $plan->title/" . zget($products, $plan->product, '');
$this->view->position[] = $this->lang->productplan->view;
$this->view->planStories = $planStories;
+1 -1
View File
@@ -18,5 +18,5 @@ td.c-branch {overflow: hidden; text-align: left !important; text-overflow: ellip
.c-title {width: 160px;}
.c-branch {width: 100px;}
.c-story {width: 80px;}
.c-execution {width: 70px;}
.c-execution {width: 120px;}
.c-bug, .c-hour {width: 60px;}
+11 -1
View File
@@ -8,6 +8,7 @@ $(document).on('click', '.task-toggle', function(e)
e.stopPropagation();
e.preventDefault();
});
$(function()
{
$('#productplanList tbody tr').each(function()
@@ -24,10 +25,19 @@ $(function()
{
var projectID = $('#project').val();
var planID = $('#planID').val();
$.apps.open(createLink('execution', 'create', 'projectID=' + projectID + '&executionID=&copyExecutionID=&planID=' + planID + '&confirm=&productID=' + productID), 'project')
if(!projectID)
{
alert(projectNotEmpty);
return false;
}
else
{
$.apps.open(createLink('execution', 'create', 'projectID=' + projectID + '&executionID=&copyExecutionID=&planID=' + planID + '&confirm=&productID=' + productID), 'project')
}
$('#projects').modal('hide');
});
});
$(document).on('click', 'td.content .more', function(e)
{
var $toggle = $(this);
+3 -4
View File
@@ -41,7 +41,6 @@ function computeEndDate(delta)
$('#begin').on('change', function()
{
$("#end").val('');
$("input:radio[name='delta']").attr("checked",false);
});
@@ -54,13 +53,13 @@ $('#future').on('change', function()
{
if($(this).prop('checked'))
{
$('#begin').val('').attr('disabled', 'disabled');
$('#end').val('').parents('tr').hide();
$('#begin').attr('disabled', 'disabled');
$('#end').attr('disabled', 'disabled').parents('tr').hide();
}
else
{
$('#begin').removeAttr('disabled');
$('#end').val('').parents('tr').show();
$('#end').removeAttr('disabled').parents('tr').show();
}
});
+4 -1
View File
@@ -43,11 +43,14 @@ $('#future').on('change', function()
{
if($(this).prop('checked'))
{
$('#begin').val('').attr('disabled', 'disabled');
$('#begin').attr('disabled', 'disabled');
$('#end').val('').parents('tr').hide();
}
else
{
var begin = $('#begin').val();
if(begin == '') $('#begin').val(today);
$('#begin').removeAttr('disabled');
$('#end').parents('tr').show();
}
+3 -2
View File
@@ -32,7 +32,7 @@ $lang->productplan->linkedStories = 'Linked Stories';
$lang->productplan->unlinkedStories = 'Unlinked Stories';
$lang->productplan->updateOrder = 'Order';
$lang->productplan->createChildren = "Create Child Plans";
$lang->productplan->createExecution = "Create {$lang->executionCommon}";
$lang->productplan->createExecution = "Create {$lang->execution->common}";
$lang->productplan->linkBug = "Link Bug";
$lang->productplan->unlinkBug = "Unlink Bug";
@@ -48,6 +48,7 @@ $lang->productplan->confirmUnlinkBug = "Do you want to unlink this bug?";
$lang->productplan->noPlan = 'No plans yet. ';
$lang->productplan->cannotDeleteParent = 'Cannot delete parent plan';
$lang->productplan->selectProjects = "Please select the project";
$lang->productplan->projectNotEmpty = 'Project cannot be empty.';
$lang->productplan->nextStep = "Next step";
$lang->productplan->id = 'ID';
@@ -62,7 +63,7 @@ $lang->productplan->future = 'TBD';
$lang->productplan->stories = 'Story';
$lang->productplan->bugs = 'Bug';
$lang->productplan->hour = $lang->hourCommon;
$lang->productplan->execution = $lang->executionCommon;
$lang->productplan->execution = $lang->execution->common;
$lang->productplan->parent = "Parent Plan";
$lang->productplan->parentAB = "Parent";
$lang->productplan->children = "Child Plan";
+3 -2
View File
@@ -32,7 +32,7 @@ $lang->productplan->linkedStories = $lang->SRCommon;
$lang->productplan->unlinkedStories = "未关联{$lang->SRCommon}";
$lang->productplan->updateOrder = '排序';
$lang->productplan->createChildren = "创建子计划";
$lang->productplan->createExecution = "创建{$lang->executionCommon}";
$lang->productplan->createExecution = "创建{$lang->execution->common}";
$lang->productplan->linkBug = "关联Bug";
$lang->productplan->unlinkBug = "移除Bug";
@@ -48,6 +48,7 @@ $lang->productplan->confirmUnlinkBug = "您确认移除该Bug吗?";
$lang->productplan->noPlan = "暂时没有计划。";
$lang->productplan->cannotDeleteParent = "不能删除父计划";
$lang->productplan->selectProjects = "请选择所属项目";
$lang->productplan->projectNotEmpty = '所属项目不能为空。';
$lang->productplan->nextStep = "下一步";
$lang->productplan->id = '编号';
@@ -62,7 +63,7 @@ $lang->productplan->future = '待定';
$lang->productplan->stories = "{$lang->SRCommon}数";
$lang->productplan->bugs = 'Bug数';
$lang->productplan->hour = $lang->hourCommon;
$lang->productplan->execution = $lang->executionCommon;
$lang->productplan->execution = $lang->execution->common;
$lang->productplan->parent = "父计划";
$lang->productplan->parentAB = "父";
$lang->productplan->children = "子计划";
+12
View File
@@ -382,6 +382,18 @@ class productplanModel extends model
->setIF($this->post->future || empty($_POST['end']), 'end', '2030-01-01')
->remove('delta,uid,future')
->get();
if(!empty($plan->parentBegin))
{
if($plan->begin < $plan->parentBegin) dao::$errors['begin'] = sprintf($this->lang->productplan->beginLetterParent, $plan->parentBegin);
}
if(!empty($plan->parentEnd))
{
if($plan->end !=='2030-01-01' and $plan->end > $plan->parentEnd) dao::$errors['end'] = sprintf($this->lang->productplan->endGreaterParent, $plan->parentEnd);
}
unset($plan->parentBegin);
unset($plan->parentEnd);
if(!$this->post->future and strpos($this->config->productplan->create->requiredFields, 'begin') !== false and empty($_POST['begin']))
{
dao::$errors['begin'] = sprintf($this->lang->error->notempty, $this->lang->productplan->begin);
+1
View File
@@ -203,4 +203,5 @@
</div>
</div>
</div>
<?php js::set('projectNotEmpty', $lang->productplan->projectNotEmpty)?>
<?php include '../../common/view/footer.html.php';?>
+5 -2
View File
@@ -27,7 +27,10 @@
<?php if($parent):?>
<tr>
<th><?php echo $lang->productplan->parent;?></th>
<td class='muted'><?php echo $parentPlan->title;?></td><td></td><td></td>
<td class='muted'><?php echo $parentPlan->title;?>
<?php echo html::hidden('parentBegin', $parentPlan->begin);?>
<?php echo html::hidden('parentEnd', $parentPlan->end);?>
</td><td></td><td></td>
</tr>
<?php else:?>
<tr>
@@ -50,7 +53,7 @@
<th><?php echo $lang->productplan->begin;?></th>
<td><?php echo html::input('begin', formatTime($begin), "class='form-control form-date'");?></td>
<td>
<div class='checkbox-primary'>
<div class='checkbox-primary'>
<input type='checkbox' id='future' name='future' value='1' />
<label for='future'><?php echo $lang->productplan->future;?></label>
</div>
+1
View File
@@ -14,6 +14,7 @@
<?php include '../../common/view/kindeditor.html.php';?>
<?php js::set('weekend', $config->execution->weekend);?>
<?php js::import($jsRoot . 'misc/date.js');?>
<?php js::set('today', helper::today());?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
+1 -1
View File
@@ -513,7 +513,7 @@ class program extends control
$this->view->dept = $dept;
$this->view->depts = array('' => '') + $this->dept->getOptionMenu();
$this->view->stakeholders = $this->program->getStakeholders($programID, 't1.id_desc');
$this->view->parentStakeholders = $this->program->getStakeholdersByList($parentIdList);
$this->view->parentStakeholders = $this->program->getStakeholdersByPrograms($parentIdList);
$this->display();
}
+2 -1
View File
@@ -183,12 +183,13 @@ class programModel extends model
->fetchGroup('product');
/* Get all products linked projects. */
$projectGroup = $this->dao->select('t1.product, t2.id, t2.name, t2.status, t2.end')->from(TABLE_PROJECTPRODUCT)->alias('t1')
$projectGroup = $this->dao->select('DISTINCT t1.product, t2.id, t2.name, t2.status, t2.end')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')
->on('t1.project = t2.id')
->where('t2.deleted')->eq(0)
->andWhere('t1.product')->in($productPairs)
->andWhere('t2.type')->eq('project')
->andWhere('t2.status')->in('wait,doing')
->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->projects)->fi()
->fetchGroup('product');
@@ -19,8 +19,8 @@
<?php echo html::a($this->createLink('program', 'craetestakeholder', "programID={$programID}"), "<span class='text'> {$lang->program->createStakeholder}</span>");?>
</span>
<div class='input-group space w-200px'>
<span class='input-group-addon'><?php echo $lang->project->selectDept?></span>
<?php echo html::select('dept', $depts, $dept, "class='form-control chosen' onchange='setDeptUsers(this)' data-placeholder='{$lang->project->selectDeptTitle}'");?>
<span class='input-group-addon'><?php echo $lang->execution->selectDept?></span>
<?php echo html::select('dept', $depts, $dept, "class='form-control chosen' onchange='setDeptUsers(this)' data-placeholder='{$lang->execution->selectDeptTitle}'");?>
</div>
<?php if($program->parent):?>
<?php echo html::a($this->createLink('program', 'createStakeholder', "programID=$programID&dept=&parent=$path"), $lang->program->importStakeholder, '', 'class="btn btn-primary"');?>
+1 -1
View File
@@ -85,7 +85,7 @@
</tr>
<tr>
<th><?php echo $lang->project->realBegan;?></th>
<td><?php echo html::input('realBegan', $program->realBegan, "class='form-control form-date'");?></td><td></td><td></td>
<td><?php echo html::input('realBegan', helper::isZeroDate($program->realBegan) ? '' : $program->realBegan, "class='form-control form-date'");?></td>
</tr>
<tr>
<th><?php echo $lang->program->desc;?></th>
+2 -2
View File
@@ -7,8 +7,8 @@
<?php echo html::a($this->createLink('project', 'managemembers', "projectID={$project->id}"), "<span class='text'> {$lang->project->manageMembers}</span>");?>
</span>
<div class='input-group space w-200px'>
<span class='input-group-addon'><?php echo $lang->project->selectDept?></span>
<?php echo html::select('dept', $depts, $dept, "class='form-control chosen' onchange='setDeptUsers(this)' data-placeholder='{$lang->project->selectDeptTitle}'");?>
<span class='input-group-addon'><?php echo $lang->execution->selectDept?></span>
<?php echo html::select('dept', $depts, $dept, "class='form-control chosen' onchange='setDeptUsers(this)' data-placeholder='{$lang->execution->selectDeptTitle}'");?>
</div>
</div>
</div>
+2 -1
View File
@@ -456,7 +456,7 @@ class project extends control
$this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('project', 'browse') : '';
$this->view->pmUsers = $this->loadModel('user')->getPairs('noclosed|nodeleted|pmfirst');
$this->view->users = $this->user->getPairs('noclosed|nodeleted');
$this->view->copyProjects = $this->project->getPairsByModel();
$this->view->copyProjects = $this->project->getPairsByModel($model);
$this->view->products = $products;
$this->view->allProducts = array('0' => '') + $this->program->getProductPairs($programID, 'assign', 'noclosed');
$this->view->productPlans = array('0' => '') + $productPlans;
@@ -1797,6 +1797,7 @@ class project extends control
$this->view->unmodifiableBranches = $unmodifiableBranches;
$this->view->unmodifiableMainBranches = $unmodifiableMainBranches;
$this->view->branchGroups = $this->loadModel('branch')->getByProducts(array_keys($allProducts), 'ignoreNormal|noclosed');
$this->view->allBranches = $this->branch->getByProducts(array_keys($allProducts), 'ignoreNormal');
$this->display();
}
+1 -1
View File
@@ -172,7 +172,7 @@ $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26'
$lang->project->productNotEmpty = 'Please link products or create products.';
$lang->project->existProductName = 'Product name already exists.';
$lang->project->changeProgram = '%s > Change project';
$lang->project->changeProgramTip = 'After modifying the project set, the products linked with the project will also modify the project set to which it belongs. Please confirm whether to modify it.';
$lang->project->changeProgramTip = 'Once the program is edited, the product that is linked to this program will be changed. Do you want to edit it?';
$lang->project->linkedProjectsTip = 'Linked projects are as follows';
$lang->project->multiLinkedProductsTip = 'The following products linked to this project are also linked to other projects, please unlink before proceeding.';
$lang->project->linkStoryByPlanTips = "This action will associate all {$lang->SRCommon} under the selected plan to this project";
+1 -1
View File
@@ -172,7 +172,7 @@ $lang->project->laneColorList = array('#32C5FF', '#006AF1', '#9D28B2', '#FF8F26'
$lang->project->productNotEmpty = '请关联产品或创建产品。';
$lang->project->existProductName = '产品名称已存在。';
$lang->project->changeProgram = '%s > 修改项目集';
$lang->project->changeProgramTip = '修改项目集后,该项目关联的产品也会同时修改所属项目集,请确认是否修改。';
$lang->project->changeProgramTip = '修改项目集后,该项目关联产品的项目集也会被修改,请确认是否修改。';
$lang->project->linkedProjectsTip = '关联的项目如下';
$lang->project->multiLinkedProductsTip = '该项目关联的如下产品还关联了其他项目,请取消关联后再操作';
$lang->project->linkStoryByPlanTips = "此操作会将所选计划下面的{$lang->SRCommon}全部关联到此项目中";
+1 -1
View File
@@ -72,7 +72,7 @@
<?php echo html::a('#',"<i class='icon-cards-view'></i> &nbsp;", '', "class='btn btn-icon text-primary' title='{$lang->project->bycard}' id='switchButton' data-type='bycard'");?>
</div>
<?php common::printLink('project', 'export', "status=$browseType&orderBy=$orderBy", "<i class='icon-export muted'> </i>" . $lang->export, '', "class='btn btn-link export'")?>
<?php if(isset($this->config->maxVersion) and !defined('TUTORIAL')):?>
<?php if(!defined('TUTORIAL')):?>
<?php common::printLink('project', 'createGuide', "programID=$programID", '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-primary create-project-btn" data-toggle="modal" data-target="#guideDialog"');?>
<?php else:?>
<?php common::printLink('project', 'create', 'mode=scrum', '<i class="icon icon-plus"></i> ' . $lang->project->create, '', 'class="btn btn-primary create-project-btn"');?>
@@ -19,8 +19,8 @@
<?php echo html::a($this->createLink('project', 'craetestakeholder', "projectID={$projectID}"), "<span class='text'> {$lang->project->createStakeholder}</span>");?>
</span>
<div class='input-group space w-200px'>
<span class='input-group-addon'><?php echo $lang->project->selectDept?></span>
<?php echo html::select('dept', $depts, $dept, "class='form-control chosen' onchange='setDeptUsers(this)' data-placeholder='{$lang->project->selectDeptTitle}'");?>
<span class='input-group-addon'><?php echo $lang->execution->selectDept?></span>
<?php echo html::select('dept', $depts, $dept, "class='form-control chosen' onchange='setDeptUsers(this)' data-placeholder='{$lang->execution->selectDeptTitle}'");?>
</div>
<?php if($project->parent):?>
<?php echo html::a($this->createLink('project', 'createStakeholder', "projectID=$projectID&dept=&parent=$path"), $lang->project->importStakeholder, '', 'class="btn btn-primary"');?>
+5 -4
View File
@@ -32,12 +32,12 @@
<?php $title = (in_array($productID, $unmodifiableProducts) and in_array($branchID, $unmodifiableBranches)) ? $lang->project->notAllowRemoveProducts : $productName;?>
<?php $checked = 'checked';?>
<div class='col-sm-4'>
<div class='product <?php echo $checked . (isset($branchGroups[$productID]) ? ' has-branch' : '')?>'>
<div class='product <?php echo $checked . (isset($allBranches[$productID]) ? ' has-branch' : '')?>'>
<div class="checkbox-primary" title='<?php echo $title;?>'>
<?php echo "<input type='checkbox' name='products[$i]' value='$productID' $checked id='products{$productID}' $isDisabled>";?>
<label class='text-ellipsis checkbox-inline' for='<?php echo 'products' . $productID;?>' title='<?php echo $productName;?>'><?php echo $productName;?></label>
</div>
<?php if(isset($branchGroups[$productID][$branchID])) echo html::select("branch[$i]", $branchGroups[$productID], $branchID, "class='form-control chosen' disabled='disabled'");?>
<?php if(isset($allBranches[$productID][$branchID])) echo html::select("branch[$i]", $allBranches[$productID], $branchID, "class='form-control chosen' disabled='disabled'");?>
</div>
</div>
<?php if(!empty($isDisabled)) echo html::hidden("products[$i]", $productID);?>
@@ -58,12 +58,13 @@
<div class='col-sm-4'>
<div class='product<?php echo isset($branchGroups[$productID]) ? ' has-branch' : ''?>'>
<div class="checkbox-primary" title='<?php echo $productName;?>'>
<?php echo "<input type='checkbox' name='products[$productID]' value='$productID' id='products{$productID}'>";?>
<?php echo "<input type='checkbox' name='products[$i]' value='$productID' id='products{$productID}'>";?>
<label class='text-ellipsis checkbox-inline' for='<?php echo 'products' . $productID;?>'><?php echo $productName;?></label>
</div>
<?php if(isset($branchGroups[$productID])) echo html::select("branch[$productID]", $branchGroups[$productID], '', "class='form-control chosen'");?>
<?php if(isset($branchGroups[$productID])) echo html::select("branch[$i]", $branchGroups[$productID], '', "class='form-control chosen'");?>
</div>
</div>
<?php $i++;?>
<?php endforeach;?>
</div>
</div>
+3 -1
View File
@@ -230,7 +230,9 @@ class repoModel extends model
->autoCheck()
->exec();
if(!dao::isError()) $this->rmClientVersionFile();
if(dao::isError()) return false;
$this->rmClientVersionFile();
$repoID = $this->dao->lastInsertID();
+2
View File
@@ -19,9 +19,11 @@ $typeInfo = $type == 'file' ? '&type=file' : '';
<?php include '../../common/view/header.html.php';?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php if(!isonlybody()):?>
<?php $browseLink = $app->session->revisionList != false ? $app->session->revisionList : $this->repo->createLink('browse', "repoID={$repoID}&branchID=" . base64_encode($branchID) . "&objectID=$objectID{$preDir}");?>
<?php echo html::a($browseLink, "<i class='icon icon-back'></i> " . $lang->goback, '', "class='btn btn-link'");?>
<div class="divider"></div>
<?php endif;?>
<div class="page-title">
<?php echo $lang->repo->revisionA . ' ' . ($repo->SCM == 'Subversion' ? $revision : $this->repo->getGitRevisionName($revision, $log->commit));?>
</div>
+1 -2
View File
@@ -2706,12 +2706,11 @@ class storyModel extends model
->where('t1.project')->eq((int)$executionID)
->andWhere('t2.deleted')->eq(0)
->beginIF($productID)->andWhere('t2.product')->eq((int)$productID)->fi()
->beginIF($branch != 'all')->andWhere('t2.branch')->eq($branch)->fi()
->beginIF($branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi()
->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi()
->beginIF($status == 'unclosed')->andWhere('t2.status')->ne('closed')->fi()
->orderBy('t1.`order` desc')
->fetchAll();
return empty($stories) ? array() : $this->formatStories($stories, $type);
}
+12 -4
View File
@@ -312,10 +312,18 @@ class task extends control
$this->view->customFields = $customFields;
$this->view->showFields = $this->config->task->custom->batchCreateFields;
$story = $this->story->getByID($storyID);
if($story)
{
$moduleID = $story->module;
$stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', $moduleID, 'short');
}
else
{
$stories = $this->story->getExecutionStoryPairs($executionID, 0, 'all', 0, 'short');
}
$stories = $this->story->getExecutionStoryPairs($executionID, 0, 0, 0, 'short');
$members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');
$members = $this->loadModel('user')->getTeamMemberPairs($executionID, 'execution', 'nodeleted');
$showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
$modules = $this->loadModel('tree')->getTaskOptionMenu($executionID, 0, 0, $showAllModule ? 'allModule' : '');
@@ -333,7 +341,7 @@ class task extends control
$this->view->modules = $modules;
$this->view->parent = $taskID;
$this->view->storyID = $storyID;
$this->view->story = $this->story->getByID($storyID);
$this->view->story = $story;
$this->view->storyTasks = $this->task->getStoryTaskCounts(array_keys($stories), $executionID);
$this->view->members = $members;
$this->view->moduleID = $moduleID;
+1 -1
View File
@@ -38,7 +38,7 @@ function toggleCheck(obj)
/* Get select of stories.*/
function setStories(moduleID, executionID, num)
{
link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=0&moduleID=' + moduleID + '&storyID=0&num=' + num + '&type=short');
link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID + '&storyID=0&num=' + num + '&type=short');
$.get(link, function(stories)
{
var storyID = $('#story' + num).val();
+1 -1
View File
@@ -273,7 +273,7 @@ function loadModuleRelated()
/* Get select of stories.*/
function setStories(moduleID, executionID)
{
link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=0&moduleID=' + moduleID);
link = createLink('story', 'ajaxGetExecutionStories', 'executionID=' + executionID + '&productID=0&branch=all&moduleID=' + moduleID);
$.get(link, function(stories)
{
var storyID = $('#story').val();
+3 -4
View File
@@ -91,18 +91,17 @@
$currentStory = $storyID;
$type = '';
$member = '';
$module = $story ? $story->module : $moduleID;
}
else
{
$currentStory = $type = $member = $module = 'ditto';
$currentStory = $type = $member = $moduleID = 'ditto';
}
?>
<?php $pri = 3;?>
<tr>
<td class='text-center'><?php echo $i + 1;?></td>
<td <?php echo zget($visibleFields, 'module', "class='hidden'")?> style='overflow:visible'>
<?php echo html::select("module[$i]", $modules, $module, "class='form-control chosen' onchange='setStories(this.value, $execution->id, $i)'")?>
<?php echo html::select("module[$i]", $modules, $moduleID, "class='form-control chosen' onchange='setStories(this.value, $execution->id, $i)'")?>
<?php echo html::hidden("parent[$i]", $parent);?>
</td>
<?php if($execution->type != 'ops'):?>
@@ -171,7 +170,7 @@
<tr>
<td class='text-center'>%s</td>
<td <?php echo zget($visibleFields, 'module', "class='hidden'")?> style='overflow:visible'>
<?php echo html::select("module[%s]", $modules, $module, "class='form-control chosen' onchange='setStories(this.value, $execution->id, \"%s\")'")?>
<?php echo html::select("module[%s]", $modules, $moduleID, "class='form-control chosen' onchange='setStories(this.value, $execution->id, \"%s\")'")?>
<?php echo html::hidden("parent[%s]", $parent);?>
</td>
<td <?php echo zget($visibleFields, 'story', "class='hidden'");?> style='overflow: visible'>
+7 -5
View File
@@ -548,10 +548,6 @@ class testcase extends control
$storyPairs = $this->loadModel('story')->getProductStoryPairs($productID, $branch === 'all' ? 0 : $branch);
$storyPairs += $storyID ? array($storyID => $story->id . ':' . $story->title) : array('');
/* Set module option menu. */
$moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch === 'all' ? 0 : $branch);
$moduleOptionMenu['ditto'] = $this->lang->testcase->ditto;
/* Set custom. */
$product = $this->product->getById($productID);
foreach(explode(',', $this->config->testcase->customBatchCreateFields) as $field)
@@ -589,6 +585,11 @@ class testcase extends control
$branches = $this->loadModel('branch')->getPairs($productID, 'active');
}
/* Set module option menu. */
$moduleOptionMenu = $this->tree->getOptionMenu($productID, 'case', 0, $branch === 'all' ? 0 : $branch);
$moduleOptionMenu['ditto'] = $this->lang->testcase->ditto;
$this->view->customFields = $customFields;
$this->view->showFields = $showFields;
@@ -1860,7 +1861,7 @@ class testcase extends control
$branchModules = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0, empty($branches) ? array(0) : array_keys($branches));
foreach($branchModules as $branchID => $moduleList)
{
foreach($moduleList as $moduleID => $moduleName) $modules[$moduleID] = $moduleName;
foreach($moduleList as $moduleID => $moduleName) $modules[$branchID][$moduleID] = $moduleName;
}
if(!empty($maxImport) and file_exists($tmpFile))
@@ -2048,6 +2049,7 @@ class testcase extends control
$this->view->product = $this->products[$productID];
$this->view->maxImport = $maxImport;
$this->view->dataInsert = $insert;
$this->display();
}
+2 -2
View File
@@ -75,13 +75,13 @@ $(function()
echo "<sub class='gray' style='vertical-align:sub;'>{$lang->testcase->new}</sub>";
}
echo html::hidden("product[$key]", $productID);
if(!empty($branches)) echo html::hidden("branch[$key]", (isset($case->branch) and $case->branch !== '') ? $case->branch : ((!empty($case->id) and isset($cases[$case->id]) and !empty($cases[$case->id]->branch)) ? $cases[$case->id]->branch : $branch));
if(!empty($branches)) echo html::hidden("branch[$key]", !empty($case->branch) ? $case->branch : ((!empty($case->id) and isset($cases[$case->id]) and !empty($cases[$case->id]->branch)) ? $cases[$case->id]->branch : $branch));
echo html::hidden("keywords[$key]", isset($case->keywords) ? $case->keywords : "");
?>
</td>
<td><?php echo html::input("title[$key]", htmlSpecialString($case->title, ENT_QUOTES), "class='form-control'")?></td>
<td style='overflow:visible'>
<?php $caseModules = (isset($case->branch) and $case->branch != 0) ? $modules[BRANCH_MAIN] + $modules[$case->branch] : $modules;?>
<?php $caseModules = (isset($case->branch) and $case->branch != 0) ? $modules[BRANCH_MAIN] + $modules[$case->branch] : $modules[BRANCH_MAIN];?>
<?php echo html::select("module[$key]", $caseModules, isset($case->module) ? $case->module : ((!empty($case->id) and isset($cases[$case->id])) ? $cases[$case->id]->module : ''), "class='form-control chosen moduleChange'")?>
</td>
<td style='overflow:visible'>
+1 -1
View File
@@ -189,7 +189,7 @@ class testreport extends control
$taskPairs = array();
$scopeAndStatus[0] = 'local';
$scopeAndStatus[1] = 'totalStatus';
$tasks = $this->testtask->getProductTasks($productID, empty($objectID) ? 0 : $task->branch, 'id_desc', null, $scopeAndStatus);
$tasks = $this->testtask->getProductTasks($productID, empty($objectID) ? 'all' : $task->branch, 'id_desc', null, $scopeAndStatus);
foreach($tasks as $testTask)
{
if($testTask->build == 'trunk') continue;
+1
View File
@@ -84,6 +84,7 @@ $lang->testreport->noTestTask = "No test requests for this {$lang->productCo
$lang->testreport->noObjectID = "No test request or {$lang->executionCommon} is selected, so no report can be generated.";
$lang->testreport->moreProduct = "Testing reports can only be generated for the same {$lang->productCommon}.";
$lang->testreport->hiddenCase = "Hide %s use cases";
$lang->testreport->goalTip = "Descriptive information about the {$lang->execution->common} of this build";
$lang->testreport->bugSummary = <<<EOD
Total <strong>%s</strong> Bugs reported <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->foundBugTip}'><i class='icon-help'></i></a>,
+1
View File
@@ -84,6 +84,7 @@ $lang->testreport->noTestTask = "该{$lang->productCommon}下还没有关联
$lang->testreport->noObjectID = "没有选定测试单或{$lang->executionCommon},无法创建测试报告!";
$lang->testreport->moreProduct = "只能对同一个{$lang->productCommon}生成测试报告。";
$lang->testreport->hiddenCase = "隐藏 %s 个用例";
$lang->testreport->goalTip = "该版本所属{$lang->execution->common}的描述信息";
$lang->testreport->bugSummary = <<<EOD
共发现<strong>%s</strong>个Bug <a data-toggle='tooltip' class='text-warning' title='{$lang->testreport->foundBugTip}'><i class='icon-help'></i></a>,
+6 -1
View File
@@ -67,11 +67,16 @@
<td colspan='2'><?php echo html::input('title', $reportTitle, "class='form-control'")?></td>
<td></td>
</tr>
<?php if(!empty($execution->desc)):?>
<tr>
<th><?php echo $lang->testreport->goal?></th>
<td colspan='2'><?php echo isset($execution->desc) ? $execution->desc : '';?></td>
<td colspan='2'>
<?php echo $execution->desc;?>
<a data-toggle='tooltip' class='text-warning' title='<?php echo $lang->testreport->goalTip;?>'><i class='icon-help'></i></a>
</td>
<td></td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->testreport->profile?></th>
<td colspan='2'>
+6 -1
View File
@@ -55,11 +55,16 @@
<td colspan='2'><?php echo html::input('title', $report->title, "class='form-control'")?></td>
<td></td>
</tr>
<?php if(!empty($execution->desc)):?>
<tr>
<th><?php echo $lang->testreport->goal?></th>
<td colspan='2'><?php echo $execution->desc?></td>
<td colspan='2'>
<?php echo $execution->desc?>
<a data-toggle='tooltip' class='text-warning' title='<?php echo $lang->testreport->goalTip;?>'><i class='icon-help'></i></a>
</td>
<td></td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->testreport->profile?></th>
<td colspan='2'>
+5 -2
View File
@@ -63,10 +63,13 @@
<th><?php echo $lang->testreport->members?></th>
<td colspan='2'><?php foreach(explode(',', $report->members) as $member)echo zget($users, $member) . ' &nbsp; ';?></td>
</tr>
<?php if(!empty($execution)):?>
<?php if(!empty($execution->desc)):?>
<tr>
<th><?php echo $lang->testreport->goal?></th>
<td colspan='2'><?php echo $execution->desc?></td>
<td colspan='2'>
<?php echo $execution->desc?>
<a data-toggle='tooltip' class='text-warning' title='<?php echo $lang->testreport->goalTip;?>'><i class='icon-help'></i></a>
</td>
</tr>
<?php endif;?>
<tr>
+1
View File
@@ -60,6 +60,7 @@ class testsuiteModel extends model
public function create($productID)
{
$suite = fixer::input('post')
->trim('name')
->stripTags($this->config->testsuite->editor->create['id'], $this->config->allowedTags)
->setIF($this->config->systemMode == 'new' and $this->lang->navGroup->testsuite != 'qa', 'project', $this->session->project)
->add('product', (int)$productID)
+2 -1
View File
@@ -515,7 +515,8 @@ class todoModel extends model
$stmt = $this->dao->select('*')->from(TABLE_TODO)
->where('deleted')->eq('0')
->andWhere('assignedTo', true)->eq($account)
->beginIF($type == 'assignedtoother')->andWhere('account', true)->eq($account)->fi()
->beginIF($type != 'assignedtoother')->andWhere('assignedTo', true)->eq($account)->fi()
->orWhere('finishedBy')->eq($account)
->markRight(1)
->beginIF($begin)->andWhere('date')->ge($begin)->fi()
-2
View File
@@ -170,8 +170,6 @@ class tree extends control
elseif($from == 'project')
{
$this->lang->navGroup->tree = 'project';
$this->lang->tree->menu = $this->lang->project->menu;
$this->lang->tree->menuOrder = $this->lang->project->menuOrder;
/* The project parameter needs to be present when the tree module belongs to the project grouping. */
if($this->session->docList && $this->session->project && strpos($this->session->docList, 'project') === false) $this->session->set('docList', $this->session->docList . '?project=' . $this->session->project, 'project');

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