Merge branch 'kanban' into 'sprint/177_liumengyi_45823'
# Conflicts: # module/kanban/control.php
This commit is contained in:
@@ -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;
|
||||
@@ -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` (
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>";
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = '接口名称';
|
||||
|
||||
@@ -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,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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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):?>
|
||||
|
||||
@@ -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;?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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')
|
||||
{
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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')));
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -27,4 +27,3 @@
|
||||
</div>
|
||||
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;}
|
||||
@@ -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 + '®ionID=' + 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®ionID=' + 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}®ionID={$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';?>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
#lineName {border-left-color: gainsboro !important;}
|
||||
@@ -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;}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 = "所属项目集";
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'");?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;}
|
||||
|
||||
@@ -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=©ExecutionID=&planID=' + planID + '&confirm=&productID=' + productID), 'project')
|
||||
if(!projectID)
|
||||
{
|
||||
alert(projectNotEmpty);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$.apps.open(createLink('execution', 'create', 'projectID=' + projectID + '&executionID=©ExecutionID=&planID=' + planID + '&confirm=&productID=' + productID), 'project')
|
||||
}
|
||||
$('#projects').modal('hide');
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', 'td.content .more', function(e)
|
||||
{
|
||||
var $toggle = $(this);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 = "子计划";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -203,4 +203,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php js::set('projectNotEmpty', $lang->productplan->projectNotEmpty)?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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"');?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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}全部关联到此项目中";
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<?php echo html::a('#',"<i class='icon-cards-view'></i> ", '', "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"');?>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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) . ' ';?></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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user