Merge branch 'master' into zentaopms_12.0.1

This commit is contained in:
wangyidong
2020-02-26 18:27:08 +08:00
89 changed files with 4188 additions and 1102 deletions
+1
View File
@@ -69,6 +69,7 @@ zentaoxx:
cp -r xuan/xxb/module/common/view/marked.html.php zentaoxx/module/common/view
cp -r xuan/xxb/module/common/view/footer.modal.html.php zentaoxx/module/common/view
cp -r xuan/xxb/module/common/view/version.html.php zentaoxx/module/common/view
cp -r xuan/xxb/module/license zentaoxx/module/
mkdir -p zentaoxx/www/js/
cp -r xuan/xxb/www/js/markedjs zentaoxx/www/js/
cp -r xuan/xxb/www/js/version.js zentaoxx/www/js/
+3 -3
View File
@@ -141,7 +141,7 @@ echo $deletelog > $basePath/deletelog.sh
echo "deletelog.sh ok"
# cron
if [ ! -d "$basePath/cron" ]; then
if [ ! -d "$basePath/cron" ]; then
mkdir $basePath/cron
fi
echo "# system cron." > $basePath/cron/sys.cron
@@ -149,8 +149,8 @@ echo "#min hour day month week command." >> $basePath/cron/sys.cron
echo "0 1 * * * $basePath/dailyreminder.sh # dailyreminder." >> $basePath/cron/sys.cron
echo "1 1 * * * $basePath/backup.sh # backup database and file." >> $basePath/cron/sys.cron
echo "1 23 * * * $basePath/computeburn.sh # compute burndown chart." >> $basePath/cron/sys.cron
echo "1-59/2 * * * * $basePath/syncsvn.sh # sync subversion." >> $basePath/cron/sys.cron
echo "1-59/2 * * * * $basePath/syncgit.sh # sync git." >> $basePath/cron/sys.cron
echo "1-59/5 * * * * $basePath/syncsvn.sh # sync subversion." >> $basePath/cron/sys.cron
echo "1-59/5 * * * * $basePath/syncgit.sh # sync git." >> $basePath/cron/sys.cron
echo "1-59/5 * * * * $basePath/sendmail.sh # async send mail." >> $basePath/cron/sys.cron
echo "1-59/5 * * * * $basePath/sendwebhook.sh # async send webhook." >> $basePath/cron/sys.cron
echo "1 1 * * * $basePath/createcycle.sh # create cycle todo." >> $basePath/cron/sys.cron
+9 -6
View File
@@ -163,12 +163,15 @@ define('TABLE_TESTSUITE', '`' . $config->db->prefix . 'testsuite`');
define('TABLE_SUITECASE', '`' . $config->db->prefix . 'suitecase`');
define('TABLE_TESTREPORT', '`' . $config->db->prefix . 'testreport`');
define('TABLE_ENTRY', '`' . $config->db->prefix . 'entry`');
define('TABLE_WEBHOOK', '`' . $config->db->prefix . 'webhook`');
define('TABLE_LOG', '`' . $config->db->prefix . 'log`');
define('TABLE_SCORE', '`' . $config->db->prefix . 'score`');
define('TABLE_NOTIFY', '`' . $config->db->prefix . 'notify`');
define('TABLE_OAUTH', '`' . $config->db->prefix . 'oauth`');
define('TABLE_ENTRY', '`' . $config->db->prefix . 'entry`');
define('TABLE_WEBHOOK', '`' . $config->db->prefix . 'webhook`');
define('TABLE_LOG', '`' . $config->db->prefix . 'log`');
define('TABLE_SCORE', '`' . $config->db->prefix . 'score`');
define('TABLE_NOTIFY', '`' . $config->db->prefix . 'notify`');
define('TABLE_OAUTH', '`' . $config->db->prefix . 'oauth`');
define('TABLE_JENKINS', '`' . $config->db->prefix . 'jenkins`');
define('TABLE_INTEGRATION', '`' . $config->db->prefix . 'integration`');
define('TABLE_COMPILE', '`' . $config->db->prefix . 'compile`');
define('TABLE_REPO', '`' . $config->db->prefix . 'repo`');
define('TABLE_REPOHISTORY', '`' . $config->db->prefix . 'repohistory`');
+1 -1
View File
@@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS `zt_repofiles` (
KEY `revision` (`revision`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `zt_bug` CHANGE `caseVersion` `caseVersion` smallint(6) NOT NULL AFTER `case`;
ALTER TABLE `zt_bug` CHANGE `caseVersion` `caseVersion` smallint(6) NOT NULL DEFAULT 1 AFTER `case`;
ALTER TABLE `zt_bug` ADD `repo` mediumint(8) unsigned NOT NULL AFTER `result`;
ALTER TABLE `zt_bug` ADD `lines` varchar(10) COLLATE 'utf8_general_ci' NOT NULL AFTER `repo`;
ALTER TABLE `zt_bug` ADD `v1` varchar(40) COLLATE 'utf8_general_ci' NOT NULL AFTER `lines`;
+68
View File
@@ -0,0 +1,68 @@
CREATE TABLE `zt_jenkins` (
`id` smallint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`url` varchar(255) DEFAULT NULL,
`account` varchar(30) DEFAULT NULL,
`password` varchar(30) NOT NULL,
`encrypt` varchar(30) NOT NULL DEFAULT 'plain',
`token` varchar(255) DEFAULT 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_cijob` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`repo` mediumint(8) unsigned NOT NULL,
`jenkins` mediumint(8) unsigned NOT NULL,
`jenkinsJob` varchar(500) NOT NULL,
`triggerType` varchar(255) NOT NULL,
`scheduleType` varchar(255) NOT NULL,
`cronExpression` varchar(255) DEFAULT NULL,
`scheduleDay` varchar(255) DEFAULT NULL,
`scheduleTime` varchar(255) DEFAULT NULL,
`scheduleInterval` mediumint(8) DEFAULT NULL,
`tagKeywords` varchar(255) DEFAULT NULL,
`commentKeywords` varchar(255) DEFAULT 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',
`lastExec` datetime DEFAULT NULL,
`lastStatus` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_cibuild` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`cijob` mediumint(8) unsigned NOT NULL,
`queueItem` mediumint(8) NOT NULL,
`status` varchar(255) NOT NULL,
`logs` text,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`updateDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `zt_cijob` ADD `svnFolder` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `triggerType`;
INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`, `lastTime`) VALUES
('1', '1', '*', '*', '*', 'moduleName=ci&methodName=buildTodayJob', '创建周期性任务', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('*/5', '*', '*', '*', '*', 'moduleName=ci&methodName=checkBuildStatus', '同步Jenkins任务状态', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('*/5', '*', '*', '*', '*', 'moduleName=ci&methodName=exec', '执行Jenkins任务', 'zentao', 1, 'normal', '0000-00-00 00:00:00');
ALTER TABLE `zt_cibuild` RENAME TO `zt_compile`;
ALTER TABLE `zt_cijob` RENAME TO `zt_integration`;
ALTER TABLE `zt_integration`
DROP `scheduleType`,
DROP `cronExpression`,
DROP `scheduleTime`,
DROP `scheduleInterval`;
+117 -2
View File
@@ -89,7 +89,7 @@ CREATE TABLE IF NOT EXISTS `zt_bug` (
`duplicateBug` mediumint(8) unsigned NOT NULL,
`linkBug` varchar(255) NOT NULL,
`case` mediumint(8) unsigned NOT NULL,
`caseVersion` smallint(6) NOT NULL default '1',
`caseVersion` smallint(6) NOT NULL DEFAULT '1',
`result` mediumint(8) unsigned NOT NULL,
`repo` mediumint(8) unsigned NOT NULL,
`entry` varchar(255) NOT NULL,
@@ -1045,6 +1045,118 @@ CREATE TABLE `zt_score` (
KEY `method` (`method`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_repo`;
CREATE TABLE IF NOT EXISTS `zt_repo` (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`path` varchar(255) NOT NULL,
`prefix` varchar(100) NOT NULL,
`encoding` varchar(20) NOT NULL,
`SCM` varchar(10) NOT NULL,
`client` varchar(100) NOT NULL,
`commits` mediumint(8) unsigned NOT NULL,
`account` varchar(30) NOT NULL,
`password` varchar(30) NOT NULL,
`encrypt` varchar(30) NOT NULL DEFAULT 'plain',
`acl` text NOT NULL,
`synced` tinyint(1) NOT NULL DEFAULT '0',
`lastSync` datetime NOT NULL,
`desc` TEXT NULL,
`deleted` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_repobranch`;
CREATE TABLE IF NOT EXISTS `zt_repobranch` (
`repo` mediumint(8) unsigned NOT NULL,
`revision` mediumint(8) unsigned NOT NULL,
`branch` varchar(255) NOT NULL,
UNIQUE KEY `repo_revision_branch` (`repo`,`revision`,`branch`),
KEY `branch` (`branch`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_repohistory`;
CREATE TABLE IF NOT EXISTS `zt_repohistory` (
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
`repo` mediumint(9) NOT NULL,
`revision` varchar(40) NOT NULL,
`commit` mediumint(8) unsigned NOT NULL,
`comment` text NOT NULL,
`committer` varchar(100) NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `repo` (`repo`,`revision`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_repofiles`;
CREATE TABLE IF NOT EXISTS `zt_repofiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`repo` mediumint(8) unsigned NOT NULL,
`revision` mediumint(8) unsigned NOT NULL,
`path` varchar(255) NOT NULL,
`parent` varchar(255) NOT NULL,
`type` varchar(20) NOT NULL,
`action` char(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `path` (`path`),
KEY `parent` (`parent`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_jenkins` (
`id` smallint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`url` varchar(255) DEFAULT NULL,
`account` varchar(30) DEFAULT NULL,
`password` varchar(30) NOT NULL,
`encrypt` varchar(30) NOT NULL DEFAULT 'plain',
`token` varchar(255) DEFAULT 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_integration` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`repo` mediumint(8) unsigned NOT NULL,
`jenkins` mediumint(8) unsigned NOT NULL,
`jenkinsJob` varchar(500) NOT NULL,
`triggerType` varchar(255) NOT NULL,
`svnFolder` varchar(255) NOT NULL,
`scheduleType` varchar(255) NOT NULL,
`cronExpression` varchar(255) DEFAULT NULL,
`scheduleDay` varchar(255) DEFAULT NULL,
`scheduleTime` varchar(255) DEFAULT NULL,
`scheduleInterval` mediumint(8) DEFAULT NULL,
`tagKeywords` varchar(255) DEFAULT NULL,
`commentKeywords` varchar(255) DEFAULT 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',
`lastExec` datetime DEFAULT NULL,
`lastStatus` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `zt_compile` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`cijob` mediumint(8) unsigned NOT NULL,
`queueItem` mediumint(8) NOT NULL,
`status` varchar(255) NOT NULL,
`logs` text,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`updateDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`, `lastTime`) VALUES
('*', '*', '*', '*', '*', '', '监控定时任务', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('30', '23', '*', '*', '*', 'moduleName=project&methodName=computeburn', '更新燃尽图', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
@@ -1055,7 +1167,10 @@ INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type
('*/5', '*', '*', '*', '*', 'moduleName=mail&methodName=asyncSend', '异步发信', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('*/5', '*', '*', '*', '*', 'moduleName=webhook&methodName=asyncSend', '异步发送Webhook', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('*/5', '*', '*', '*', '*', 'moduleName=admin&methodName=deleteLog', '删除过期日志', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('1', '1', '*', '*', '*', 'moduleName=todo&methodName=createCycle', '生成周期性待办', 'zentao', 1, 'normal', '0000-00-00 00:00:00');
('1', '1', '*', '*', '*', 'moduleName=todo&methodName=createCycle', '生成周期性待办', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('1', '1', '*', '*', '*', 'moduleName=ci&methodName=buildTodayJob', '创建周期性任务', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('*/5', '*', '*', '*', '*', 'moduleName=ci&methodName=checkBuildStatus', '同步Jenkins任务状态', 'zentao', 1, 'normal', '0000-00-00 00:00:00'),
('*/5', '*', '*', '*', '*', 'moduleName=ci&methodName=exec', '执行Jenkins任务', 'zentao', 1, 'normal', '0000-00-00 00:00:00');
INSERT INTO `zt_group` (`id`, `name`, `role`, `desc`) VALUES
(1, 'ADMIN', 'admin', 'for administrator'),
+6 -6
View File
@@ -2,19 +2,19 @@ Z PUBLIC LICENSE 1.2
Authorization
Z PUBLIC LICENSE, also known as ZPL Agreement, is drafted by QingDao Nature Easy Soft Network Technology Co,LTD. (,www.cnezsoft.com).
Anyone can use the agreement to publish open source software, and modify the blank underlined part of the following text of the agreement accordingly.
No other text of the agreement shall be changed. QingDao Nature Easy Soft Network Technology Co,LTD has the final authority to interpret the terms of the agreement.
Z PUBLIC LICENSE, also known as ZPL Agreement, is drafted by EasyCorp(en.easycorp.ltd).
Anyone can use the agreement to publish open source software, and modify the blank in the following text of the agreement accordingly.
No other text of the agreement shall be changed. EasyCorp has the final interpretation of the terms in the agreement.
Preface
ZenTaoPMS (Hereinafter referred to as "the software") developed by Nature EasySoft Network Tecnology Co.ltd, QingDao, China (www.cnezsoft.com) (hereinafter referred to I). I'm entitled to all copyright of the software.
ZenTao ALM (Hereinafter referred to as "the software") developed by EasyCorp (en.easycorp.ltd) (hereinafter referred to I). I'm entitled to all copyright of the software.
The software is released as open source software. You are authorized to use the software as long as you are in compliance with this agreement.
By installation of the software, you agree that a contractual relationship between you and me is automatically established.
You are obliged to fully comply with all the terms of this agreement unless you choose to stop using the software or you have signed additional contracts with me.
You are obliged to fully comply with all the terms of this agreement unless you choose to stop using the software or you have signed additional agreement with me.
My Contact:
Email: co@easysoft.ltd
Email: renee@easysoft.ltd
Site: http://www.zentao.pm
We agree:
@@ -1,9 +1,20 @@
<?php
class Git
class ParseGit
{
public $client;
public $root;
/**
* Construct
*
* @param string $client
* @param string $root
* @param string $username
* @param string $password
* @param string $encoding
* @access public
* @return void
*/
public function __construct($client, $root, $username, $password, $encoding = 'UTF-8')
{
putenv('LC_CTYPE=en_US.UTF-8');
@@ -16,6 +27,14 @@ class Git
exec("{$this->client} config core.quotepath false");
}
/**
* List files.
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
public function ls($path, $revision = 'HEAD')
{
$path = ltrim($path, DIRECTORY_SEPARATOR);
@@ -29,7 +48,7 @@ class Git
$infos = array();
foreach($list as $entry)
{
{
list($mod, $kind, $revision, $size, $name) = preg_split('/[\t ]+/', $entry);
/* Get commit info. */
@@ -70,6 +89,29 @@ class Git
return $infos;
}
/**
* Get tags
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
public function tags($path, $revision = 'HEAD')
{
chdir($this->root);
$cmd = escapeCmd("$this->client tag --sort=taggerdate");
$list = execCmd($cmd . ' 2>&1', 'array', $result);
if($result) return array();
return $list;
}
/**
* Get branch
*
* @access public
* @return array
*/
public function branch()
{
chdir($this->root);
@@ -92,6 +134,14 @@ class Git
return $branches;
}
/**
* Get last log.
*
* @param string $path
* @param int $count
* @access public
* @return array
*/
public function getLastLog($path, $count = 10)
{
$path = ltrim($path, DIRECTORY_SEPARATOR);
@@ -104,6 +154,16 @@ class Git
return $logs;
}
/**
* Get logs
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @param int $count
* @access public
* @return array
*/
public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0)
{
$path = ltrim($path, DIRECTORY_SEPARATOR);
@@ -116,7 +176,7 @@ class Git
$logs = array();
chdir($this->root);
$list = execCmd(escapeCmd("$this->client log --stat=1024 --name-status -1 $fromRevision -- $path"), 'array');
$list = execCmd(escapeCmd("$this->client log --stat=1024 --name-status --stat-name-width=1000 -1 $fromRevision -- $path"), 'array');
$logs = $this->parseLog($list);
return $logs;
}
@@ -130,12 +190,20 @@ class Git
$revisions = "$fromRevision..$toRevision";
}
chdir($this->root);
$list = execCmd(escapeCmd("$this->client log $count $revisions -- $path"), 'array');
$list = execCmd(escapeCmd("$this->client log --stat=1024 --name-status --stat-name-width=1000 $count $revisions -- $path"), 'array');
$logs = $this->parseLog($list);
return $logs;
}
/**
* Blame file
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
public function blame($path, $revision)
{
$path = ltrim($path, DIRECTORY_SEPARATOR);
@@ -178,6 +246,15 @@ class Git
return $blames;
}
/**
* Diff file.
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @access public
* @return array
*/
public function diff($path, $fromRevision, $toRevision)
{
$path = ltrim($path, DIRECTORY_SEPARATOR);
@@ -193,6 +270,14 @@ class Git
return $lines;
}
/**
* Cat file.
*
* @param string $entry
* @param string $revision
* @access public
* @return string
*/
public function cat($entry, $revision = 'HEAD')
{
chdir($this->root);
@@ -203,6 +288,14 @@ class Git
return $content;
}
/**
* Get info.
*
* @param string $entry
* @param string $revision
* @access public
* @return object
*/
public function info($entry, $revision = 'HEAD')
{
chdir($this->root);
@@ -235,6 +328,13 @@ class Git
return $info;
}
/**
* Parse diff.
*
* @param array $lines
* @access public
* @return array
*/
public function parseDiff($lines)
{
if(empty($lines)) return array();
@@ -323,6 +423,14 @@ class Git
return $diffs;
}
/**
* Get commit count.
*
* @param int $commits
* @param string $lastVersion
* @access public
* @return int
*/
public function getCommitCount($commits = 0, $lastVersion = '')
{
chdir($this->root);
@@ -330,6 +438,12 @@ class Git
return execCmd(escapeCmd("$this->client rev-list --count $revision -- ./"), 'string');
}
/**
* Get first revision.
*
* @access public
* @return string
*/
public function getFirstRevision()
{
chdir($this->root);
@@ -337,6 +451,12 @@ class Git
return $list[0];
}
/**
* Get latest revision
*
* @access public
* @return string
*/
public function getLatestRevision()
{
chdir($this->root);
@@ -345,6 +465,15 @@ class Git
return $list[0];
}
/**
* Get commits.
*
* @param string $version
* @param int $count
* @param string $branch
* @access public
* @return array
*/
public function getCommits($version = '', $count = 0, $branch = '')
{
if($version == 'HEAD' and $branch) $version = $branch;
@@ -397,6 +526,13 @@ class Git
return $logs;
}
/**
* Parse log.
*
* @param array $logs
* @access public
* @return array
*/
public function parseLog($logs)
{
$parsedLogs = array();
+135 -1
View File
@@ -3,38 +3,110 @@ class scm
{
public $engine;
/**
* Set engine.
*
* @param object $repo
* @access public
* @return void
*/
public function setEngine($repo)
{
$className = $repo->SCM;
if($className == 'Git') $className = 'ParseGit';
if(!class_exists($className)) require(strtolower($className) . '.class.php');
$this->engine = new $className($repo->client, $repo->path, $repo->account, $repo->password, $repo->encoding);
}
/**
* List files.
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
public function ls($path, $revision = 'HEAD')
{
return $this->engine->ls($path, $revision);
}
/**
* Get tags.
*
* @param string $path
* @param string $revision
* @param bool $onlyDir
* @access public
* @return array
*/
public function tags($path, $revision, $onlyDir = true)
{
return $this->engine->tags($path, $revision, $onlyDir);
}
/**
* Get branch.
*
* @access public
* @return array
*/
public function branch()
{
return $this->engine->branch();
}
/**
* Get log.
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @param int $count
* @access public
* @return array
*/
public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0)
{
return $this->engine->log($path, $fromRevision, $toRevision);
}
/**
* Blame file.
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
public function blame($path, $revision)
{
return $this->engine->blame($path, $revision);
}
/**
* Get last log.
*
* @param string $path
* @param int $count
* @access public
* @return array
*/
public function getLastLog($path, $count = 10)
{
return $this->engine->getLastLog($path, $count);
}
/**
* Diff file.
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @param string $parse
* @access public
* @return array
*/
public function diff($path, $fromRevision = 0, $toRevision = 'HEAD', $parse = 'yes')
{
$diffs = $this->engine->diff($path, $fromRevision, $toRevision);
@@ -43,37 +115,89 @@ class scm
return $this->engine->parseDiff($diffs);
}
/**
* Cat file.
*
* @param string $entry
* @param string $revision
* @access public
* @return string
*/
public function cat($entry, $revision = 'HEAD')
{
return $this->engine->cat($entry, $revision);
}
/**
* Get info.
*
* @param string $entry
* @param string $revision
* @access public
* @return object
*/
public function info($entry, $revision = 'HEAD')
{
return $this->engine->info($entry, $revision);
}
/**
* Get commit count
*
* @param int $commits
* @param string $lastVersion
* @access public
* @return int
*/
public function getCommitCount($commits = 0, $lastVersion = 0)
{
return $this->engine->getCommitCount($commits, $lastVersion);
}
/**
* Get latest revision.
*
* @access public
* @return string
*/
public function getLatestRevision()
{
return $this->engine->getLatestRevision();
}
/**
* Get first revision.
*
* @access public
* @return string
*/
public function getFirstRevision()
{
return $this->engine->getFirstRevision();
}
/**
* Get commits.
*
* @param string $version
* @param int $count
* @param string $branch
* @access public
* @return array
*/
public function getCommits($version = '', $count = 0, $branch = '')
{
return $this->engine->getCommits($version, $count, $branch);
}
}
/**
* Escape command.
*
* @param string $cmd
* @access public
* @return string
*/
function escapeCmd($cmd)
{
$codes = array('#', '&', ';', '`', '|', '*', '?', '~', '<', '>', '^', '[', ']', '{', '}', '$', ',', '\x0A', '\xFF');
@@ -82,6 +206,16 @@ function escapeCmd($cmd)
return $cmd;
}
/**
* Execute command.
*
* @param string $cmd
* @param string $return
* @param int $result
* @param string $type
* @access public
* @return array|string
*/
function execCmd($cmd, $return = 'string', &$result = 0, $type = 'utf-8')
{
if(file_exists(dirname(__FILE__) . '/config.php')) include dirname(__FILE__) . '/config.php';
+152
View File
@@ -9,6 +9,17 @@ class Subversion
public $remote;
public $encoding;
/**
* Construct
*
* @param string $client
* @param string $root
* @param string $account
* @param string $password
* @param string $encoding
* @access public
* @return void
*/
public function __construct($client, $root, $account, $password, $encoding = 'UTF-8')
{
putenv('LC_CTYPE=en_US.UTF-8');
@@ -22,6 +33,14 @@ class Subversion
if($this->encoding == 'utf-8') $this->encoding = 'gbk';
}
/**
* List files.
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
public function ls($path, $revision = 'HEAD')
{
$resourcePath = $path;
@@ -65,11 +84,47 @@ class Subversion
return $infos;
}
/**
* Get tags.
*
* @param string $path
* @param string $revision
* @param bool $onlyDir
* @access public
* @return array
*/
public function tags($path, $revision = 'HEAD', $onlyDir = true)
{
$infos = $this->ls($path, $revision);
$tags = array();
foreach($infos as $info)
{
if($onlyDir and $info->kind != 'dir') continue;
$tags[$info->name] = $info->name;
}
return $tags;
}
/**
* Get branch.
*
* @access public
* @return array
*/
public function branch()
{
return array();
}
/**
* Get last log.
*
* @param string $path
* @param int $count
* @access public
* @return array
*/
public function getLastLog($path, $count = 10)
{
$resourcePath = $path;
@@ -110,6 +165,17 @@ class Subversion
return $logs;
}
/**
* Get log.
*
* @param string $path
* @param int $fromRevision
* @param string $toRevision
* @param int $count
* @param bool $quiet
* @access public
* @return array
*/
public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0, $quiet = false)
{
$resourcePath = $path;
@@ -165,6 +231,14 @@ class Subversion
return $logs;
}
/**
* Blame file.
*
* @param string $path
* @param int $revision
* @access public
* @return array
*/
public function blame($path, $revision)
{
$resourcePath = $path;
@@ -225,6 +299,15 @@ class Subversion
return $blames;
}
/**
* Diff file.
*
* @param string $path
* @param int $fromRevision
* @param int $toRevision
* @access public
* @return array
*/
public function diff($path, $fromRevision, $toRevision)
{
$resourcePath = $path;
@@ -241,6 +324,14 @@ class Subversion
return $lines;
}
/**
* Cat file.
*
* @param string $entry
* @param string $revision
* @access public
* @return string
*/
public function cat($entry, $revision = 'HEAD')
{
$resourcePath = $entry;
@@ -256,16 +347,26 @@ class Subversion
return $content;
}
/**
* Get info.
*
* @param string $entry
* @param string $revision
* @access public
* @return object
*/
public function info($entry, $revision = 'HEAD')
{
$resourcePath = $entry;
$entry = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($entry)) . '"';
$svnInfo = $this->replaceAuth(escapeCmd($this->buildCMD($entry, 'info', "-r $revision --xml")));
$svninfo = execCmd($svnInfo, 'string', $result);
if($result)
{
$entry = '"' . $this->root . '/' . $resourcePath . '"';
$svnInfo = $this->replaceAuth(escapeCmd($this->buildCMD($entry, 'info', "-r $revision --xml")));
$svninfo = execCmd($svnInfo, 'string', $result);
if($result) $svninfo = '';
}
@@ -285,6 +386,13 @@ class Subversion
return $info;
}
/**
* Parse diff.
*
* @param array $lines
* @access public
* @return array
*/
public function parseDiff($lines)
{
if(empty($lines)) return array();
@@ -358,6 +466,14 @@ class Subversion
return $diffs;
}
/**
* Get commit count.
*
* @param int $commits
* @param int $lastVersion
* @access public
* @return int
*/
public function getCommitCount($commits = 0, $lastVersion = 0)
{
if(empty($commits)) $commits = 0;
@@ -381,6 +497,12 @@ class Subversion
return $commits;
}
/**
* Get first revision.
*
* @access public
* @return int
*/
public function getFirstRevision()
{
$logs = $this->log('', 0, 'HEAD', 1, $quiet = true);
@@ -389,12 +511,26 @@ class Subversion
return $firstLog->revision;
}
/**
* Get latest revision.
*
* @access public
* @return int
*/
public function getLatestRevision()
{
$info = $this->info('');
return $info->cRevision;
}
/**
* Get commits.
*
* @param string $version
* @param int $count
* @access public
* @return array
*/
public function getCommits($version = '', $count = 0)
{
$count = $count == 0 ? '' : "--limit $count";
@@ -443,11 +579,27 @@ class Subversion
return $logs;
}
/**
* Replace svn auth.
*
* @param string $cmd
* @access public
* @return string
*/
public function replaceAuth($cmd)
{
return str_replace(array('@account@', '@password@'), array($this->account, $this->password), $cmd);
}
/**
* Build command.
*
* @param string $path
* @param string $action
* @param string $param
* @access public
* @return string
*/
public function buildCMD($path, $action, $param)
{
if($this->ssh)
+9 -10
View File
@@ -143,21 +143,20 @@ class backup extends control
$backupFiles = glob("{$this->backupPath}*.*");
if(!empty($backupFiles))
{
$time = time();
$time = time();
$zfile = $this->app->loadClass('zfile');
foreach($backupFiles as $file)
{
if($time - filemtime($file) > $this->config->backup->holdDays * 24 * 3600) unlink($file);
if($time - filemtime($file) > $this->config->backup->holdDays * 24 * 3600)
{
$rmFunc = is_file($file) ? 'removeFile' : 'removeDir';
$zfile->{$rmFunc}($file);
}
}
}
if($reload == 'yes')
{
die(js::reload('parent'));
}
else
{
echo $this->lang->backup->success->backup . "\n";
}
if($reload == 'yes') die(js::reload('parent'));
echo $this->lang->backup->success->backup . "\n";
}
/**
+15 -2
View File
@@ -541,10 +541,11 @@ class bug extends control
* View a bug.
*
* @param int $bugID
* @param string $form
* @access public
* @return void
*/
public function view($bugID)
public function view($bugID, $from = 'bug')
{
/* Judge bug exits or not. */
$bug = $this->bug->getById($bugID, true);
@@ -555,7 +556,18 @@ class bug extends control
if($bug->assignedTo == $this->app->user->account) $this->loadModel('action')->read('bug', $bugID);
/* Set menu. */
$this->bug->setMenu($this->products, $bug->product, $bug->branch);
if($from == 'bug')
{
$this->bug->setMenu($this->products, $bug->product, $bug->branch);
}
elseif($from == 'repo')
{
session_write_close();
$this->lang->set('menugroup.bug', 'repo');
$repos = $this->loadModel('repo')->getRepoPairs();
$this->repo->setMenu($repos);
$this->lang->bug->menu = $this->lang->repo->menu;
}
/* Get product info. */
$productID = $bug->product;
@@ -576,6 +588,7 @@ class bug extends control
$this->view->modulePath = $this->tree->getParents($bug->module);
$this->view->bugModule = empty($bug->module) ? '' : $this->tree->getById($bug->module);
$this->view->bug = $bug;
$this->view->from = $from;
$this->view->branchName = $this->session->currentProductType == 'normal' ? '' : zget($branches, $bug->branch, '');
$this->view->users = $this->user->getPairs('noletter');
$this->view->actions = $this->action->getList('bug', $bugID);
+1 -1
View File
@@ -154,7 +154,7 @@ $lang->bug->lblLastEdited = '最后修改';
$lang->bug->lblResolved = '由谁解决';
$lang->bug->allUsers = '加载所有用户';
$lang->bug->allBuilds = '所有';
$lang->bug->createBuild = '新建';
$lang->bug->createBuild = '创建';
/* legend列表。*/
$lang->bug->legendBasicInfo = '基本信息';
+1 -1
View File
@@ -154,7 +154,7 @@ $lang->bug->lblLastEdited = '最後修改';
$lang->bug->lblResolved = '由誰解決';
$lang->bug->allUsers = '加載所有用戶';
$lang->bug->allBuilds = '所有';
$lang->bug->createBuild = '新建';
$lang->bug->createBuild = '创建';
/* legend列表。*/
$lang->bug->legendBasicInfo = '基本信息';
+1
View File
@@ -181,6 +181,7 @@ class bugModel extends model
{
$this->loadModel('action');
$branch = (int)$branch;
$productID = (int)$productID;
$now = helper::now();
$actions = array();
$data = fixer::input('post')->get();
+1
View File
@@ -579,6 +579,7 @@ class caselibModel extends model
$this->loadModel('action');
$now = helper::now();
$libID = (int)$libID;
$cases = fixer::input('post')->get();
$batchNum = count(reset($cases));
+122
View File
@@ -0,0 +1,122 @@
<?php
/**
* The control file of ci module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package product
* @version $Id: ${FILE_NAME} 5144 2020/1/8 8:10 下午 chenqi@cnezsoft.com $
* @link http://www.zentao.net
*/
class ci extends control
{
/**
* ci constructor.
* @param string $moduleName
* @param string $methodName
*/
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
$this->ci->setMenu();
}
/**
* Build today job.
*
* @access public
* @return void
*/
public function buildTodayJob()
{
$scheduleJobs = $this->loadModel('integration')->getListByTriggerType('schedule');
$week = date('w');
$this->loadModel('compile');
foreach($scheduleJobs as $job)
{
if(strpos($job->scheduleDay, $week) !== false) $this->compile->createByIntegration($job->id);
}
echo 'success';
}
/**
* Exec compile.
*
* @access public
* @return void
*/
public function exec()
{
$compiles = $this->loadModel('compile')->getUnexecutedList();
foreach($compiles as $compile) $this->compile->execByCompile($compile);
$integrations = $this->loadModel('integration')->getListByTriggerType('tag');
$repoIdList = array();
$repos = array();
foreach($integrations as $integration) $repoIdList[$integration->id] = $integration->id;
if($repoIdList) $repos = $this->loadModel('repo')->getByIdList($repoIdList);
foreach($integrations as $integration)
{
$repo = zget($repos, $integration->repo, null);
if(empty($repo)) continue;
$scm = $repo->SCM == 'Git' ? 'git' : 'svn';
$savedTag = $this->loadModel($scm)->getSavedTag($repo->id);
$tags = $this->$scm->getRepoTags($repo, $scm == 'svn' ? $integration->svnFolder : '');
if(!empty($tags))
{
$arriveLastTag = false;
foreach($tags as $tag)
{
/* Get the last build tag position */
if($scm == 'git')
{
if(!empty($savedTag) && !$arriveLastTag) continue;
if(!empty($savedTag) && $tag == $savedTag)
{
$arriveLastTag = true;
continue;
}
}
elseif($scm == 'svn')
{
if(isset($savedTag[$tag])) continue;
$tag = rtrim($repo->path , '/') . '/' . trim($integration->svnFolder, '/') . '/' . $tag;
}
$tagData = new stdclass();
$tagData->PARAM_TAG = $tag;
$this->compile->execByIntegration($integration->id, $tagData);
}
if($scm == 'svn') $tag = json_encode($tags);
$this->$scm->saveLastTag($tag, $repo->id);
}
}
echo 'success';
}
/**
* Send a request to jenkins to check build status.
*
* @access public
* @return void
*/
public function checkBuildStatus()
{
$this->ci->checkBuildStatus();
if(dao::isError())
{
echo json_encode(dao::getError());
}
else
{
echo 'success';
}
}
}
+6
View File
@@ -0,0 +1,6 @@
<?php
$lang->ci->common = 'CI';
$lang->ci->at = ' at ';
$lang->ci->job = 'Job';
$lang->ci->task = 'Job';
$lang->ci->history = 'Build';
+6
View File
@@ -0,0 +1,6 @@
<?php
$lang->ci->common = '持续集成';
$lang->ci->at = '于';
$lang->ci->job = '构建';
$lang->ci->task = '任务';
$lang->ci->history = '历史';
+129
View File
@@ -0,0 +1,129 @@
<?php
/**
* The model file of ci module of ZenTaoPMS.
* @author Chenqi <chenqi@cnezsoft.com>
* @package product
* @version $Id: $
* @link http://www.zentao.net
*/
class ciModel extends model
{
/**
* Set menu.
*
* @access public
* @return void
*/
public function setMenu()
{
$repoID = $this->session->repoID;
$moduleName = $this->app->getModuleName();
foreach($this->lang->{$moduleName}->menu as $key => $menu) common::setMenuVars($this->lang->{$moduleName}->menu, $key, $repoID);
$this->lang->{$moduleName}->menuOrder = $this->lang->ci->menuOrder;
}
/**
* Send a request to jenkins to check build status.
*
* @access public
* @return bool
*/
public function checkBuildStatus()
{
$compiles = $this->dao->select('t1.*, t2.jenkinsJob, t3.name jenkinsName,t3.serviceUrl,t3.account,t3.token,t3.password')
->from(TABLE_COMPILE)->alias('t1')
->leftJoin(TABLE_INTEGRATION)->alias('t2')->on('t1.cijob=t2.id')
->leftJoin(TABLE_JENKINS)->alias('t3')->on('t2.jenkins=t3.id')
->where('t1.status')->ne('success')
->andWhere('t1.status')->ne('fail')
->andWhere('t1.status')->ne('timeout')
->andWhere('t1.createdDate')->gt(date(DT_DATETIME1, strtotime("-1 day")))
->fetchAll();
foreach($compiles as $compile)
{
$jenkinsServer = $compile->serviceUrl;
$jenkinsUser = $compile->account;
$jenkinsPassword = $compile->token ? $compile->token : base64_decode($compile->password);
$jenkinsAuth = '://' . $jenkinsUser . ':' . $jenkinsPassword . '@';
$jenkinsServer = str_replace('://', $jenkinsAuth, $jenkinsServer);
$queueUrl = sprintf('%s/queue/item/%s/api/json', $jenkinsServer, $compile->queueItem);
$response = common::http($queueUrl);
if(strripos($response, "404") > -1)
{
/* Queue expired, use another api. */
$infoUrl = sprintf('%s/job/%s/%s/api/json', $jenkinsServer, $compile->jenkinsJob, $compile->queueItem);
$response = common::http($infoUrl);
$buildInfo = json_decode($response);
$result = strtolower($buildInfo->result);
$this->updateBuildStatus($compile, $result);
$logUrl = sprintf('%s/job/%s/%s/consoleText', $jenkinsServer, $compile->jenkinsJob, $compile->queueItem);
$response = common::http($logUrl);
$logs = json_decode($response);
$this->dao->update(TABLE_COMPILE)->set('logs')->eq($response)->where('id')->eq($compile->id)->exec();
}
else
{
$queueInfo = json_decode($response);
if(!empty($queueInfo->executable))
{
$buildUrl = $queueInfo->executable->url . 'api/json?pretty=true';
$buildUrl = str_replace('://', $jenkinsAuth, $buildUrl);
$response = common::http($buildUrl);
$buildInfo = json_decode($response);
if($buildInfo->building)
{
$this->updateBuildStatus($compile, 'building');
}
else
{
$result = strtolower($buildInfo->result);
$this->updateBuildStatus($compile, $result);
$logUrl = $buildInfo->url . 'logText/progressiveText/api/json';
$logUrl = str_replace('://', $jenkinsAuth, $logUrl);
$response = common::http($logUrl);
$logs = json_decode($response);
$this->dao->update(TABLE_COMPILE)->set('logs')->eq($response)->where('id')->eq($compile->id)->exec();
}
}
}
}
}
/**
* Update ci build status.
*
* @param object $build
* @param string $status
* @access public
* @return bool
*/
public function updateBuildStatus($build, $status)
{
$this->dao->update(TABLE_COMPILE)->set('status')->eq($status)->where('id')->eq($build->id)->exec();
$this->dao->update(TABLE_INTEGRATION)->set('lastExec')->eq(helper::now())->set('lastStatus')->eq($status)->where('id')->eq($build->cijob)->exec();
}
/**
* @param $url
* @return false|mixed|string
*/
public function sendRequest($url, $data)
{
if(!empty($data->PARAM_TAG)) $data->PARAM_REVISION = '';
$response = common::http($url, $data, true);
if(preg_match("!Location: .*item/(.*)/!", $response, $matches)) return $matches[1];
return 0;
}
}
+17 -6
View File
@@ -101,6 +101,7 @@ $lang->loading = 'Loading...';
$lang->notFound = 'Not found!';
$lang->notPage = 'Sorry, the features you are visiting are in development!';
$lang->showAll = '[[Show All]]';
$lang->selectedItems = 'Seleted <strong>{0}</strong> items';
$lang->future = 'Waiting';
$lang->year = 'Year';
@@ -122,7 +123,7 @@ $lang->menu->my = '<span>Dashboard</span>|my|index';
$lang->menu->product = $lang->productCommon . '|product|index|locate=no';
$lang->menu->project = $lang->projectCommon . '|project|index|locate=no';
$lang->menu->qa = 'Test|qa|index';
$lang->menu->repo = 'Code|repo|log';
$lang->menu->ci = 'CI|repo|browse';
$lang->menu->doc = 'Doc|doc|index';
$lang->menu->report = 'Report|report|index';
$lang->menu->company = 'Company|company|index';
@@ -331,11 +332,17 @@ $lang->caselib->menu->testsuite = array('link' => 'Suite|testsuite|browse|');
$lang->caselib->menu->report = array('link' => 'Report|testreport|browse|');
$lang->caselib->menu->caselib = array('link' => 'Case Library|caselib|browse|libID=%s', 'alias' => 'create,createcase,view,edit,batchcreatecase,showimport', 'subModule' => 'tree,testcase');
$lang->repo = new stdclass();
$lang->repo->menu = new stdclass();
$lang->repo->menu->browse = array('link' =>'Browse|repo|log|repoID=%s&entry=', 'alias' => 'diff,view,revision,showsynccomment');
$lang->repo->menu->settings = 'Settings|repo|settings|repoID=%s';
$lang->repo->menu->delete = array('link' => 'Delete|repo|delete|repoID=%s', 'target' => 'hiddenwin');
$lang->ci = new stdclass();
$lang->ci->menu = new stdclass();
$lang->ci->menu->browse = array('link' =>'Code|repo|browse|repoID=%s', 'alias' => 'diff,view,revision,log,blame,showsynccomment');
$lang->ci->menu->job = array('link' =>'Build|ci|browsejob', 'alias' => 'createjob,editjob,browsebuild,viewbuildlogs');
$lang->ci->menu->maintain = array('link' =>'Repo|repo|maintain', 'alias' => 'create,edit');
$lang->ci->menu->jenkins = array('link' =>'Jenkins|jenkins|browse', 'alias' => 'create,edit');
$lang->repo = new stdclass();
$lang->jenkins = new stdclass();
$lang->repo->menu = $lang->ci->menu;
$lang->jenkins->menu = $lang->ci->menu;
/* Doc menu settings. */
$lang->doc = new stdclass();
@@ -475,6 +482,9 @@ $lang->menugroup->entry = 'admin';
$lang->menugroup->webhook = 'admin';
$lang->menugroup->message = 'admin';
$lang->menugroup->repo = 'ci';
$lang->menugroup->jenkins = 'ci';
/* Error info. */
$lang->error = new stdclass();
$lang->error->companyNotFound = "The domain %s cannot be found!";
@@ -501,6 +511,7 @@ $lang->error->pasteImg = 'Images are not allowed to be pasted in your bro
$lang->error->noData = 'No data.';
$lang->error->editedByOther = 'This record might have been changed. Please refresh and try to edit again!';
$lang->error->tutorialData = 'No data can be imported in tutorial mode. Please quit tutorial first!';
$lang->error->noCurlExt = 'No Curl module installed';
/* Page info. */
$lang->pager = new stdclass();
+9 -4
View File
@@ -4,7 +4,7 @@ $lang->menuOrder[5] = 'my';
$lang->menuOrder[10] = 'product';
$lang->menuOrder[15] = 'project';
$lang->menuOrder[20] = 'qa';
$lang->menuOrder[25] = 'repo';
$lang->menuOrder[25] = 'ci';
$lang->menuOrder[30] = 'doc';
$lang->menuOrder[35] = 'report';
$lang->menuOrder[40] = 'company';
@@ -77,9 +77,14 @@ $lang->testsuite->menuOrder = $lang->testcase->menuOrder;
$lang->caselib->menuOrder = $lang->testcase->menuOrder;
$lang->testreport->menuOrder = $lang->testcase->menuOrder;
$lang->repo->menuOrder[5] = 'browse';
$lang->repo->menuOrder[15] = 'settings';
$lang->repo->menuOrder[20] = 'delete';
$lang->ci->menuOrder[5] = 'browse';
$lang->ci->menuOrder[10] = 'job';
$lang->ci->menuOrder[15] = 'maintain';
$lang->ci->menuOrder[20] = 'jenkins';
$lang->ci->menuOrder[25] = 'match';
$lang->repo->menuOrder = $lang->ci->menuOrder;
$lang->jenkins->menuOrder = $lang->ci->menuOrder;
/* doc menu order. */
$lang->doc->menuOrder[5] = 'list';
+25 -7
View File
@@ -101,6 +101,7 @@ $lang->loading = '稍候...';
$lang->notFound = '抱歉,您访问的对象并不存在!';
$lang->notPage = '抱歉,您访问的功能正在开发中!';
$lang->showAll = '[[全部显示]]';
$lang->selectedItems = '已选择 <strong>{0}</strong> 项';
$lang->future = '未来';
$lang->year = '年';
@@ -122,7 +123,7 @@ $lang->menu->my = '<span> 我的地盘</span>|my|index';
$lang->menu->product = $lang->productCommon . '|product|index|locate=no';
$lang->menu->project = $lang->projectCommon . '|project|index|locate=no';
$lang->menu->qa = '测试|qa|index';
$lang->menu->repo = '代码|repo|log';
$lang->menu->ci = '集成|repo|browse';
$lang->menu->doc = '文档|doc|index';
$lang->menu->report = '统计|report|index';
$lang->menu->company = '组织|company|index';
@@ -331,11 +332,22 @@ $lang->caselib->menu->testsuite = array('link' => '套件|testsuite|browse|');
$lang->caselib->menu->report = array('link' => '报告|testreport|browse|');
$lang->caselib->menu->caselib = array('link' => '用例库|caselib|browse|libID=%s', 'alias' => 'create,createcase,view,edit,batchcreatecase,showimport', 'subModule' => 'tree,testcase');
$lang->repo = new stdclass();
$lang->repo->menu = new stdclass();
$lang->repo->menu->browse = array('link' =>'浏览|repo|log|repoID=%s&entry=', 'alias' => 'diff,view,revision,showsynccomment');
$lang->repo->menu->settings = '设置|repo|settings|repoID=%s';
$lang->repo->menu->delete = array('link' => '删除|repo|delete|repoID=%s', 'target' => 'hiddenwin');
$lang->ci = new stdclass();
$lang->ci->menu = new stdclass();
$lang->ci->menu->browse = array('link' =>'代码|repo|browse|repoID=%s', 'alias' => 'diff,view,revision,log,blame,showsynccomment');
$lang->ci->menu->job = array('link' =>'构建|integration|browse', 'subModule' => 'compile,integration');
$lang->ci->menu->maintain = array('link' =>'版本库|repo|maintain', 'alias' => 'create,edit');
$lang->ci->menu->jenkins = array('link' =>'Jenkins|jenkins|browse', 'alias' => 'create,edit');
$lang->ci->menu->match = array('link' =>'匹配设置|repo|setmatchcomment');
$lang->repo = new stdclass();
$lang->jenkins = new stdclass();
$lang->compile = new stdclass();
$lang->integration = new stdclass();
$lang->repo->menu = $lang->ci->menu;
$lang->jenkins->menu = $lang->ci->menu;
$lang->compile->menu = $lang->ci->menu;
$lang->integration->menu = $lang->ci->menu;
/* 文档视图菜单设置。*/
$lang->doc = new stdclass();
@@ -381,7 +393,7 @@ $lang->admin->menu = new stdclass();
$lang->admin->menu->index = array('link' => '首页|admin|index', 'alias' => 'register,certifytemail,certifyztmobile,ztcompany');
$lang->admin->menu->message = array('link' => '通知|message|index', 'subModule' => 'message,mail,webhook');
$lang->admin->menu->custom = array('link' => '自定义|custom|set', 'subModule' => 'custom');
$lang->admin->menu->sso = array('link' => '集成|admin|sso');
$lang->admin->menu->sso = array('link' => '集成|admin|sso', 'subModule' => '');
$lang->admin->menu->extension = array('link' => '插件|extension|browse', 'subModule' => 'extension');
$lang->admin->menu->dev = array('link' => '二次开发|dev|api', 'alias' => 'db', 'subModule' => 'dev,entry');
$lang->admin->menu->translate = array('link' => '翻译|dev|translate');
@@ -475,6 +487,11 @@ $lang->menugroup->entry = 'admin';
$lang->menugroup->webhook = 'admin';
$lang->menugroup->message = 'admin';
$lang->menugroup->repo = 'ci';
$lang->menugroup->jenkins = 'ci';
$lang->menugroup->compile = 'ci';
$lang->menugroup->integration = 'ci';
/* 错误提示信息。*/
$lang->error = new stdclass();
$lang->error->companyNotFound = "您访问的域名 %s 没有对应的公司。";
@@ -501,6 +518,7 @@ $lang->error->pasteImg = '您的浏览器不支持粘贴图片!';
$lang->error->noData = '没有数据';
$lang->error->editedByOther = '该记录可能已经被改动。请刷新页面重新编辑!';
$lang->error->tutorialData = '新手模式下不会插入数据,请退出新手模式操作';
$lang->error->noCurlExt = '服务器未安装Curl模块。';
/* 分页信息。*/
$lang->pager = new stdclass();
+4 -6
View File
@@ -662,11 +662,8 @@ class commonModel extends model
echo '<li>' . html::a(helper::createLink('my', 'index'), $lang->zentaoPMS) . '</li>';
if($moduleName != 'index')
{
if(!isset($lang->menu->$mainMenu))
{
echo "</ul>";
return;
}
if(!isset($lang->menu->$mainMenu)) return print("</ul>");
$menuLink = $lang->menu->$mainMenu;
list($menuLabel, $module, $method) = explode('|', $menuLink);
echo '<li>' . html::a(helper::createLink($module, $method), $menuLabel) . '</li>';
@@ -1823,7 +1820,7 @@ EOD;
* @access public
* @return string
*/
public static function http($url, $data = null)
public static function http($url, $data = null, $optHeader = false)
{
global $lang, $app;
if(!extension_loaded('curl')) return json_encode(array('result' => 'fail', 'message' => $lang->error->noCurlExt));
@@ -1844,6 +1841,7 @@ EOD;
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLINFO_HEADER_OUT, TRUE);
if($optHeader) curl_setopt($curl, CURLOPT_HEADER, true);
if(!empty($data))
{
curl_setopt($curl, CURLOPT_POST, true);
+74
View File
@@ -0,0 +1,74 @@
<?php
/**
* The control file of compile of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package compile
* @version $Id$
* @link http://www.zentao.net
*/
class compile extends control
{
/**
* Construct
*
* @param string $moduleName
* @param string $methodName
* @access public
* @return void
*/
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
$this->loadModel('ci')->setMenu();
}
/**
* Browse jenkins build.
*
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browse($jobID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->view->title = $this->lang->ci->job . $this->lang->colon . $this->lang->compile->browse;
$this->view->position[] = html::a($this->createLink('integration', 'browse'), $this->lang->ci->job);
$this->view->position[] = $this->lang->compile->browse;
$this->view->buildList = $this->compile->getList($jobID, $orderBy, $pager);
$this->view->job = $this->loadModel('integration')->getByID($jobID);
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* View jenkins build logs.
*
* @param int $buildID
* @access public
* @return void
*/
public function logs($buildID)
{
$build = $this->compile->getByID($buildID);
$this->view->logs = str_replace("\r\n","<br />", $build->logs);
$this->view->build = $build;
$this->view->title = $this->lang->ci->job . $this->lang->colon . $this->lang->compile->logs;
$this->view->position[] = html::a($this->createLink('integration', 'browse'), $this->lang->ci->job);
$this->view->position[] = html::a($this->createLink('compile', 'browse', "jobID=" . $build->cijob), $this->lang->compile->browse);
$this->view->position[] = $this->lang->compile->logs;
$this->display();
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
$lang->compile->browse = 'Build Histories';
$lang->compile->logs = 'Build Logs';
$lang->compile->id = 'ID';
$lang->compile->name = 'Name';
$lang->compile->status = 'Build Status';
$lang->compile->time = 'Build Time';
$lang->compile->statusList['success'] = 'Success';
$lang->compile->statusList['fail'] = 'Fail';
$lang->compile->statusList['created'] = 'Created';
$lang->compile->statusList['building'] = 'Building';
$lang->compile->statusList['create_fail'] = 'Fail to create';
$lang->compile->statusList['timeout'] = 'Exec Timeout';
+15
View File
@@ -0,0 +1,15 @@
<?php
$lang->compile->browse = '构建历史';
$lang->compile->logs = '构建日志';
$lang->compile->id = 'ID';
$lang->compile->name = '名称';
$lang->compile->status = '构建状态';
$lang->compile->time = '构建时间';
$lang->compile->statusList['success'] = '成功';
$lang->compile->statusList['fail'] = '失败';
$lang->compile->statusList['created'] = '新建';
$lang->compile->statusList['building'] = '构建中';
$lang->compile->statusList['create_fail'] = '创建失败';
$lang->compile->statusList['timeout'] = '执行超时';
+153
View File
@@ -0,0 +1,153 @@
<?php
/**
* The model file of compile module of ZenTaoCMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package compile
* @version $Id$
* @link http://www.zentao.net
*/
class compileModel extends model
{
/**
* Get build list.
*
* @param int $jobID
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getList($jobID, $orderBy = 'id_desc', $pager = null)
{
return $this->dao->select('t1.id, t1.name, t1.status, t1.createdDate, t2.triggerType, t3.name as repoName, t4.name as jenkinsName')->from(TABLE_COMPILE)->alias('t1')
->leftJoin(TABLE_INTEGRATION)->alias('t2')->on('t1.cijob=t2.id')
->leftJoin(TABLE_REPO)->alias('t3')->on('t2.repo=t3.id')
->leftJoin(TABLE_JENKINS)->alias('t4')->on('t2.jenkins=t4.id')
->where('t1.deleted')->eq('0')
->andWhere('t1.cijob')->eq($jobID)
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
}
/**
* Get by id
*
* @param int $buildID
* @access public
* @return object
*/
public function getByID($buildID)
{
return $this->dao->select('*')->from(TABLE_COMPILE)->where('id')->eq($buildID)->fetch();
}
/**
* Get unexecuted list.
*
* @access public
* @return array
*/
public function getUnexecutedList()
{
return $this->dao->select('*')->from(TABLE_COMPILE)->where('status')->eq('')->andWhere('deleted')->eq('0')->fetchAll();
}
/**
* Save build by job
*
* @param object $job
* @access public
* @return void
*/
public function createByIntegration($integrationID)
{
$integration = $this->dao->select('id,name')->from(TABLE_INTEGRATION)->where('id')->eq($integrationID)->fetch();
$build = new stdClass();
$build->cijob = $integration->id;
$build->name = $integration->name;
$build->createdBy = $this->app->user->account;
$build->createdDate = helper::now();
$this->dao->insert(TABLE_COMPILE)->data($build)->exec();
}
/**
* Execute compile
*
* @param object $compile
* @access public
* @return bool
*/
public function execByCompile($compile, $data = null)
{
$integration = $this->dao->select('t1.id as jobId,t1.name as jobName,t1.repo,t1.jenkinsJob,t2.name as jenkinsName,t2.serviceUrl,t2.account,t2.token,t2.password')
->from(TABLE_INTEGRATION)->alias('t1')
->leftJoin(TABLE_JENKINS)->alias('t2')->on('t1.jenkins=t2.id')
->where('t1.id')->eq($compile->cijob)
->fetch();
if(!$integration) return false;
$buildUrl = $this->getBuildUrl($integration);
$build = new stdclass();
$build->queueItem = $this->loadModel('ci')->sendRequest($buildUrl, $data);
$build->status = $build->queueItem ? 'created' : 'create_fail';
$this->dao->update(TABLE_COMPILE)->data($build)->where('id')->eq($compile->id)->exec();
return !dao::isError();
}
/**
* Execute by integration.
*
* @param int $compile
* @access public
* @return bool
*/
public function execByIntegration($integrationID, $data = null)
{
$integration = $this->dao->select('t1.id as jobId,t1.name as jobName,t1.repo,t1.jenkinsJob,t2.name as jenkinsName,t2.serviceUrl,t2.account,t2.token,t2.password')
->from(TABLE_INTEGRATION)->alias('t1')
->leftJoin(TABLE_JENKINS)->alias('t2')->on('t1.jenkins=t2.id')
->where('t1.id')->eq($integrationID)
->fetch();
if(!$integration) return false;
$buildUrl = $this->getBuildUrl($integration);
$build = new stdClass();
$build->cijob = $integration->jobId;
$build->name = $integration->jobName;
$build->queueItem = $this->loadModel('ci')->sendRequest($buildUrl, $data);
$build->status = $build->queueItem ? 'created' : 'create_fail';
$build->createdBy = $this->app->user->account;
$build->createdDate = helper::now();
$this->dao->insert(TABLE_COMPILE)->data($build)->exec();
return !dao::isError();
}
/**
* Get build url.
*
* @param object $jenkins
* @access public
* @return string
*/
public function getBuildUrl($jenkins)
{
$jenkinsServer = $jenkins->serviceUrl;
$jenkinsUser = $jenkins->account;
$jenkinsPassword = $jenkins->token ? $jenkins->token : base64_decode($jenkins->password);
$jenkinsAuth = '://' . $jenkinsUser . ':' . $jenkinsPassword . '@';
$jenkinsServer = str_replace('://', $jenkinsAuth, $jenkinsServer);
$buildUrl = sprintf('%s/job/%s/buildWithParameters/api/json', $jenkinsServer, $jenkins->jenkinsJob);
return $buildUrl;
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
/**
* The browse view file of compile module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package compile
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<?php
echo html::a($this->createLink('integration', 'browse'), "<span class='text'>{$lang->ci->task}</span>", '', "class='btn btn-link'");
echo html::a($this->createLink('compile', 'browse'), "<span class='text'>{$lang->ci->history}</span>", '', "class='btn btn-link btn-active-text'");
?>
</div>
</div>
<div id='mainContent'>
<form class='main-table' id='ajaxForm' method='post'>
<table id='buildList' class='table has-sort-head table-fixed'>
<thead>
<tr>
<?php $vars = "jobID={$job->id}&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?>
<th class='w-60px'><?php common::printOrderLink('id', $orderBy, $vars, $lang->compile->id);?></th>
<th class='w-200px text-left'><?php common::printOrderLink('name', $orderBy, $vars, $lang->compile->name);?></th>
<th class='w-200px text-left'><?php echo $lang->integration->repo;?></th>
<th class='w-200px text-left'><?php echo $lang->integration->jenkins;?></th>
<th class='w-200px text-left'><?php echo $lang->integration->triggerType;?></th>
<th class='w-150px text-left'><?php common::printOrderLink('status', $orderBy, $vars, $lang->compile->status);?></th>
<th class='text-left'><?php common::printOrderLink('createdDate', $orderBy, $vars, $lang->compile->time);?></th>
<th class='w-100px c-actions-4'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
<?php foreach ($buildList as $id => $build): ?>
<tr>
<td class='text-center'><?php echo $id; ?></td>
<td class='text' title='<?php echo $build->name; ?>'><?php echo $build->name; ?></td>
<td class='text' title='<?php echo $build->repoName; ?>'><?php echo $build->repoName; ?></td>
<td class='text' title='<?php echo $build->jenkinsName; ?>'><?php echo $build->jenkinsName; ?></td>
<?php $triggerType = zget($lang->integration->triggerTypeList, $build->triggerType);?>
<td class='text' title='<?php echo $triggerType;?>'><?php echo $triggerType;?></td>
<?php $buildStatus = zget($lang->compile->statusList, $build->status);?>
<td class='text' title='<?php echo $buildStatus;?>'><?php echo $buildStatus;?></td>
<td class='text' title='<?php echo $build->createDate; ?>'><?php echo $build->createdDate; ?></td>
<td class='c-actions text-center'>
<?php common::printIcon('compile', 'logs', "buildID=$id", '', 'list', 'file-text', '', '', '', '', $lang->compile->logs);?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if($buildList):?>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
<?php endif; ?>
</form>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* The browse view file of compile module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package compile
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<div class="page-title">
<strong><?php echo $lang->compile->logs;?></strong>
</div>
</div>
<div class="btn-toolbar pull-right">
<?php echo html::a(helper::createLink('compile', "browse", "jobId=$build->cijob"), "<i class='icon icon-back icon-sm'></i> ". $lang->goback, '', "class='btn btn-secondary'");?>
</div>
</div>
<div id='mainContent'>
<div class='main-content'><?php echo $logs;?></div>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+6 -3
View File
@@ -211,9 +211,12 @@ class cron extends control
}
/* Save log. */
$log = '';
$time = $now->format('G:i:s');
$log = "$time task " . $id . " executed,\ncommand: $cron[command].\nreturn : $return.\noutput : $output\n";
$log = '';
$time = $now->format('G:i:s');
$output = "\n";
if(strlen($output) > 100) $output = "\n" . $output;
$log = "$time task " . $id . " executed,\ncommand: $cron[command].\nreturn : $return.\noutput : $output\n";
$this->cron->logCron($log);
unset($log);
}
+3 -4
View File
@@ -149,8 +149,7 @@ class file extends control
/* Down the file. */
$fileName = $file->title;
if(!preg_match("/\.{$file->extension}$/", $fileName)) $fileName .= '.' . $file->extension;
$fileData = file_get_contents($file->realPath);
$this->sendDownHeader($fileName, $file->extension, $fileData);
$this->sendDownHeader($fileName, $file->extension, $file->realPath, 'file');
}
}
else
@@ -244,9 +243,9 @@ class file extends control
* @access public
* @return void
*/
public function sendDownHeader($fileName, $fileType, $content)
public function sendDownHeader($fileName, $fileType, $content, $type = 'content')
{
$this->file->sendDownHeader($fileName, $fileType, $content);
$this->file->sendDownHeader($fileName, $fileType, $content, $type);
}
/**
+10 -2
View File
@@ -869,7 +869,7 @@ class fileModel extends model
* @access public
* @return void
*/
public function sendDownHeader($fileName, $fileType, $content)
public function sendDownHeader($fileName, $fileType, $content, $type = 'content')
{
/* Clean the ob content to make sure no space or utf-8 bom output. */
$obLevel = ob_get_level();
@@ -893,6 +893,14 @@ class fileModel extends model
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Pragma: no-cache");
header("Expires: 0");
die($content);
if($type == 'content') die($content);
if($type == 'file' and file_exists($content))
{
$chunkSize = 1024 * 1024;
$handle = fopen($content, "r");
while(!feof($handle)) echo fread($handle, $chunkSize);
fclose($handle);
die();
}
}
}
-25
View File
@@ -1,27 +1,2 @@
<?php
/**
* encodings: 提交日志的编码,比如GBK,可以用逗号连接起来的多个。
* client: Git客户端执行文件的路径,windows下面是git.exe的路径,linux下面比如/usr/bin/git
* repos可以是多个,需要设定某一个库的访问路径。
*
* encodeings: the encoding of the comment,can be a list.
* client: the git client binary path. Unser windows, find the path of git.exe. Under linux, try /usr/bin/git
* Can set multi repos, ervery one should set the path.
*
* 例子:
* $config->git->client = '/usr/bin/git'; // c:\git\git.exe
* $config->git->repos['pms']['path'] = '/home/user/repo/pms'; // c:\repo\pms
*
*/
$config->git = new stdClass();
$config->git->encodings = 'utf-8';
$config->git->client = '';
$i = 1;
$config->git->repos[$i]['path'] = '';
$config->git->repos[$i]['encoding'] = 'utf-8';
/*
$i ++;
$config->git->repos[$i]['path'] = '';
*/
+7 -6
View File
@@ -37,10 +37,9 @@ class git extends control
$path = helper::safe64Decode($path);
if(common::hasPriv('repo', 'diff'))
{
$repos = $this->loadModel('repo')->getAllRepos();
$repos = $this->loadModel('repo')->getListBySCM('Git', 'haspriv');
foreach($repos as $repo)
{
if($repo->SCM != 'Git') continue;
if(strpos($path, $repo->path) === 0)
{
$entry = $this->repo->encodePath(str_replace($repo->path, '', $path));
@@ -72,10 +71,9 @@ class git extends control
$path = helper::safe64Decode($path);
if(common::hasPriv('repo', 'view'))
{
$repos = $this->loadModel('repo')->getAllRepos();
$repos = $this->loadModel('repo')->getListBySCM('Git', 'haspriv');
foreach($repos as $repo)
{
if($repo->SCM != 'Git') continue;
if(strpos($path, $repo->path) === 0)
{
$entry = $this->repo->encodePath(str_replace($repo->path, '', $path));
@@ -121,9 +119,11 @@ class git extends control
}
$parsedObjects = array('stories' => array(), 'tasks' => array(), 'bugs' => array());
$this->loadModel('repo');
foreach($parsedLogs as $log)
{
$objects = $this->git->parseComment($log->msg);
$objects = $this->repo->parseComment($log->msg);
if($objects)
{
$this->git->saveAction2PMS($objects, $log, $repoRoot);
@@ -181,7 +181,8 @@ class git extends control
$parsedFiles[$action][] = ltrim($path, '/');
}
$objects = $this->git->parseComment($message);
$objects = $this->loadModel('repo')->parseComment($message);
if($objects)
{
$log = new stdclass();
+165 -150
View File
@@ -71,6 +71,7 @@ class gitModel extends model
{
parent::__construct();
$this->loadModel('action');
$this->loadModel('repo');
}
/**
@@ -87,51 +88,59 @@ class gitModel extends model
$this->setLogRoot();
$this->setRestartFile();
foreach($this->repos as $name => $repo)
foreach($this->repos as $repo)
{
$this->printLog("begin repo $name");
$repo = (object)$repo;
$repo->name = $name;
$this->printLog("begin repo $repo->id");
if(!$this->setRepo($repo)) return false;
$savedRevision = $this->getSavedRevision();
$this->printLog("start from revision $savedRevision");
$logs = $this->getRepoLogs($repo, $savedRevision);
if(empty($logs)) continue;
$this->printLog("get " . count($logs) . " logs");
$this->printLog('begin parsing logs');
$latestRevision = $logs[0]->revision;
foreach($logs as $log)
$logs = $this->getRepoLogs($repo, $savedRevision);
$objects = array();
if(!empty($logs))
{
$this->printLog("parsing log {$log->revision}");
if($log->revision == $savedRevision)
$this->printLog("get " . count($logs) . " logs");
$this->printLog('begin parsing logs');
$latestRevision = $logs[0]->revision;
foreach($logs as $log)
{
$this->printLog("{$log->revision} alread parsed, commit it");
continue;
$this->printLog("parsing log {$log->revision}");
if($log->revision == $savedRevision)
{
$this->printLog("{$log->revision} alread parsed, commit it");
continue;
}
$this->printLog("comment is\n----------\n" . trim($log->msg) . "\n----------");
$objects = $this->repo->parseComment($log->msg);
if($objects)
{
$this->printLog('extract' .
' story:' . join(' ', $objects['stories']) .
' task:' . join(' ', $objects['tasks']) .
' bug:' . join(',', $objects['bugs']));
$this->saveAction2PMS($objects, $log, $repo->encoding);
}
else
{
$this->printLog('no objects found' . "\n");
}
}
$this->printLog("comment is\n----------\n" . trim($log->msg) . "\n----------");
$objects = $this->parseComment($log->msg);
if($objects)
{
$this->printLog('extract' .
' story:' . join(' ', $objects['stories']) .
' task:' . join(' ', $objects['tasks']) .
' bug:' . join(',', $objects['bugs']));
$this->saveAction2PMS($objects, $log);
}
else
{
$this->printLog('no objects found' . "\n");
}
$this->saveLastRevision($latestRevision);
$this->printLog("save revision $latestRevision");
$this->deleteRestartFile();
$this->printLog("\n\nrepo #" . $repo->id . ': ' . $repo->path . " finished");
}
$this->saveLastRevision($latestRevision);
$this->printLog("save revision $latestRevision");
$this->deleteRestartFile();
$this->printLog("\n\nrepo $name finished");
// exe ci jobs in log
$cijobIdList = zget($objects, 'integrations', array());
$this->loadModel('compile');
foreach($cijobIdList as $id) $this->compile->execByIntegration($id);
}
}
@@ -177,13 +186,46 @@ class gitModel extends model
*/
public function setRepos()
{
if(!$this->config->git->repos)
$repos = $this->loadModel('repo')->getListBySCM('Git');
$gitRepos = array();
$paths = array();
foreach($repos as $repo)
{
echo "You must set one git repo.\n";
return false;
if(!isset($paths[$repo->path]))
{
unset($repo->acl);
unset($repo->desc);
$gitRepos[] = $repo;
$paths[$repo->path] = $repo->path;
}
}
$this->repos = $this->config->git->repos;
if(isset($this->config->git->repos))
{
foreach($this->config->git->repos as $i => $repo)
{
$repoPath = $repo['path'];
if(empty($repoPath)) continue;
if(isset($paths[$repoPath])) continue;
$gitRepo = new stdclass();
$gitRepo->id = "c{$i}";
$gitRepo->client = $this->config->git->client;
$gitRepo->path = $repoPath;
$gitRepo->prefix = '';
$gitRepo->SCM = 'Git';
$gitRepo->account = '';
$gitRepo->password = '';
$gitRepo->encoding = zget($repo, 'encoding', $this->config->git->client);
$gitRepos[] = $gitRepo;
$paths[$repoPath] = $repoPath;
}
}
if(empty($gitRepos)) echo "You must set one git repo.\n";
$this->repos = $gitRepos;
return true;
}
@@ -195,15 +237,11 @@ class gitModel extends model
*/
public function getRepos()
{
$repos = array();
if(!$this->config->git->repos) return $repos;
$repos = $this->setRepos();
$repoPairs = array();
foreach($repos as $repo) $repoPairs[] = $repo->path;
foreach($this->config->git->repos as $repo)
{
if(empty($repo['path'])) continue;
$repos[] = $repo['path'];
}
return $repos;
return $repoPairs;
}
/**
@@ -218,40 +256,49 @@ class gitModel extends model
$this->setClient($repo);
if(empty($this->client)) return false;
$this->setLogFile($repo->name);
$this->setLogFile($repo->id);
$this->setTagFile($repo->id);
$this->setRepoRoot($repo);
return true;
}
/**
* Set the git binary client of a repo.
*
* @param object $repo
*
* @param object $repo
* @access public
* @return bool
*/
public function setClient($repo)
{
if($this->config->git->client == '')
{
echo "You must set the git client file.\n";
return false;
}
$this->client = $this->config->git->client;
$this->client = $repo->client;
return true;
}
/**
* Set the log file of a repo.
*
* @param string $repoName
*
* @param string $repoId
* @access public
* @return void
*/
public function setLogFile($repoName)
public function setLogFile($repoId)
{
$this->logFile = $this->logRoot . $repoName;
$this->logFile = $this->logRoot . $repoId . '.log';
}
/**
* Set the tag file of a repo.
*
* @param string $repoId
* @access public
* @return void
*/
public function setTagFile($repoId)
{
$this->setLogRoot();
$this->tagFile = $this->logRoot . $repoId . '.tag';
}
/**
@@ -266,6 +313,20 @@ class gitModel extends model
$this->repoRoot = $repo->path;
}
/**
* get tags histories for repo.
*
* @param object $repo
* @access public
* @return void
*/
public function getRepoTags($repo)
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
return $scm->tags('');
}
/**
* Get repo logs.
*
@@ -276,40 +337,22 @@ class gitModel extends model
*/
public function getRepoLogs($repo, $fromRevision)
{
$parsedLogs = array();
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
$logs = $scm->log('', $fromRevision);
if(empty($logs)) return false;
/* The git log command. */
chdir($this->repoRoot);
exec("{$this->client} config core.quotepath false");
if($fromRevision)
foreach($logs as $log)
{
$cmd = "$this->client log --stat=1024 --stat-name-width=1000 --name-status $fromRevision..HEAD";
}
else
{
$cmd = "$this->client log --stat=1024 --stat-name-width=1000 --name-status";
}
exec($cmd, $list, $return);
$log->author = $log->committer;
$log->msg = $log->comment;
$log->date = $log->time;
if(!$list and $return)
{
echo "Some error occers: \nThe command is $cmd\n";
return false;
/* Process files. */
$log->files = array();
foreach($log->change as $file => $info) $log->files[$info['action']][] = $file;
}
if(!$list and !$return) return array();
/* Process logs. */
$logs = array();
$i = 0;
foreach($list as $line)
{
if(strpos($line, 'commit ') === 0) $i++;
$logs[$i][] = $line;
}
foreach($logs as $log) $parsedLogs[] = $this->convertLog($log);
return $parsedLogs;
return $logs;
}
/**
@@ -354,63 +397,6 @@ class gitModel extends model
return $parsedLog;
}
/**
* Parse the comment of git, extract object id list from it.
*
* @param string $comment
* @access public
* @return array
*/
public function parseComment($comment)
{
$stories = array();
$tasks = array();
$bugs = array();
// bug|story|task(case insensitive) + some space + #|:|:(Chinese) + id lists(maybe join with space or ,)
// $comment = "bug # 1,2,3,4 Bug:1 2 3 4 5 story:9999,1234566 story:456,1234566";
$commonReg = "(?:\s){0,}(?:#|:|:){0,}([0-9, ]{1,})";
$taskReg = '/task' . $commonReg . '/i';
$storyReg = '/story' . $commonReg . '/i';
$bugReg = '/bug' . $commonReg . '/i';
if(preg_match_all($storyReg, $comment, $result)) $stories = join(' ', $result[1]);
if(preg_match_all($taskReg, $comment, $result)) $tasks = join(' ', $result[1]);
if(preg_match_all($bugReg, $comment, $result)) $bugs = join(' ', $result[1]);
if($stories) $stories = array_unique(explode(' ', str_replace(',', ' ', $stories)));
if($tasks) $tasks = array_unique(explode(' ', str_replace(',', ' ', $tasks)));
if($bugs) $bugs = array_unique(explode(' ', str_replace(',', ' ', $bugs)));
if(!$stories and !$tasks and !$bugs) return array();
return array('stories' => $stories, 'tasks' => $tasks, 'bugs' => $bugs);
}
/**
* Convert the comment to uft-8.
*
* @param string $comment
* @access public
* @return string
*/
public function iconvComment($comment)
{
/* Get encodings. */
$encodings = str_replace(' ', '', isset($this->config->git->encodings) ? $this->config->git->encodings : '');
if($encodings == '') return $comment;
$encodings = explode(',', $encodings);
/* Try convert. */
foreach($encodings as $encoding)
{
if($encoding == 'utf-8') continue;
$result = helper::convertEncoding($comment, $encoding, 'utf-8');
if($result) return $result;
}
return $comment;
}
/**
* Diff a url.
*
@@ -520,13 +506,14 @@ class gitModel extends model
* @access public
* @return void
*/
public function saveAction2PMS($objects, $log, $repoRoot = '')
public function saveAction2PMS($objects, $log, $repoRoot = '', $encodings = 'utf-8')
{
$action = new stdclass();
$action->actor = $log->author;
$action->action = 'gitcommited';
$action->date = $log->date;
$action->comment = htmlspecialchars($this->iconvComment($log->msg));
$action->comment = htmlspecialchars($this->repo->iconvComment($log->msg, $encodings));
$action->extra = substr($log->revision, 0, 10);
$changes = $this->createActionChanges($log, $repoRoot);
@@ -738,7 +725,34 @@ class gitModel extends model
*/
public function saveLastRevision($revision)
{
file_put_contents($this->logFile, $revision);
$ret = file_put_contents($this->logFile, $revision);
}
/**
* Get the saved tag.
*
* @access public
* @return int
*/
public function getSavedTag($repoID = 0)
{
if($repoID) $this->setTagFile($repoID);
if(!file_exists($this->tagFile)) return 0;
if(file_exists($this->restartFile)) return 0;
return trim(file_get_contents($this->tagFile));
}
/**
* Save the last revision.
*
* @param int $tag
* @access public
* @return void
*/
public function saveLastTag($tag, $repoId = 0)
{
if($repoId) $this->setTagFile($repoId);
file_put_contents($this->tagFile, $tag);
}
/**
@@ -753,6 +767,7 @@ class gitModel extends model
echo helper::now() . " $log\n";
}
/**
* Build URL.
*
+3 -2
View File
@@ -686,8 +686,9 @@ $lang->resource->repo->browse = 'browse';
$lang->resource->repo->view = 'view';
$lang->resource->repo->log = 'log';
$lang->resource->repo->revision = 'revisionAction';
$lang->resource->repo->settings = 'settings';
$lang->resource->repo->create = 'create';
$lang->resource->repo->blame = 'blameAction';
$lang->resource->repo->create = 'createAction';
$lang->resource->repo->edit = 'editAction';
$lang->resource->repo->delete = 'delete';
$lang->resource->repo->showSyncComment = 'showSyncComment';
$lang->resource->repo->diff = 'diffAction';
+3
View File
@@ -165,6 +165,9 @@ $lang->install->cronList['moduleName=mail&methodName=asyncSend'] = 'Asynchr
$lang->install->cronList['moduleName=webhook&methodName=asyncSend'] = 'Asynchronize sending Webhook';
$lang->install->cronList['moduleName=admin&methodName=deleteLog'] = 'Delete overdue logs';
$lang->install->cronList['moduleName=todo&methodName=createCycle'] = 'Create recurring todos';
$lang->install->cronList['moduleName=ci&methodName=buildTodayJob'] = 'Create recurring jenkins';
$lang->install->cronList['moduleName=ci&methodName=checkBuildStatus'] = 'Synchronize Jenkins Status';
$lang->install->cronList['moduleName=ci&methodName=exec'] = 'Execute Jenkins';
$lang->install->success = "Installed!";
$lang->install->login = 'Login ZenTao';
+3
View File
@@ -165,6 +165,9 @@ $lang->install->cronList['moduleName=mail&methodName=asyncSend'] = '异步
$lang->install->cronList['moduleName=webhook&methodName=asyncSend'] = '异步发送Webhook';
$lang->install->cronList['moduleName=admin&methodName=deleteLog'] = '删除过期日志';
$lang->install->cronList['moduleName=todo&methodName=createCycle'] = '生成周期性待办';
$lang->install->cronList['moduleName=ci&methodName=buildTodayJob'] = '创建周期性任务';
$lang->install->cronList['moduleName=ci&methodName=checkBuildStatus'] = '同步Jenkins任务状态';
$lang->install->cronList['moduleName=ci&methodName=exec'] = '执行Jenkins任务';
$lang->install->success = "安装成功";
$lang->install->login = '登录禅道管理系统';
+6
View File
@@ -0,0 +1,6 @@
<?php
$config->integration = new stdclass();
$config->integration->create = new stdclass();
$config->integration->edit = new stdclass();
$config->integration->create->requiredFields = 'name,repo,jenkins,jenkinsJob,triggerType';
$config->integration->edit->requiredFields = 'name,repo,jenkins,jenkinsJob,triggerType';
+163
View File
@@ -0,0 +1,163 @@
<?php
/**
* The control file of integration of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package integration
* @version $Id$
* @link http://www.zentao.net
*/
class integration extends control
{
/**
* Construct
*
* @param string $moduleName
* @param string $methodName
* @access public
* @return void
*/
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
$this->loadModel('ci')->setMenu();
}
/**
* Browse ci job.
*
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->app->loadLang('compile');
$this->view->jobList = $this->integration->getList($orderBy, $pager);
$this->view->title = $this->lang->ci->job . $this->lang->colon . $this->lang->integration->browse;
$this->view->position[] = $this->lang->ci->job;
$this->view->position[] = $this->lang->integration->browse;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* Create a ci job.
*
* @access public
* @return void
*/
public function create()
{
if($_POST)
{
$this->integration->create();
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->app->loadLang('action');
$this->view->title = $this->lang->ci->job . $this->lang->colon . $this->lang->integration->create;
$this->view->position[] = html::a(inlink('browse'), $this->lang->ci->job);
$this->view->position[] = $this->lang->integration->create;
$repoList = $this->loadModel('repo')->getList();
$repoPairs = array(0 => '');
$repoTypes = array();
foreach($repoList as $repo)
{
$repoPairs[$repo->id] = $repo->name;
$repoTypes[$repo->id] = $repo->SCM;
}
$this->view->repoPairs = $repoPairs;
$this->view->repoTypes = $repoTypes;
$this->view->jenkinsList = $this->loadModel('jenkins')->getPairs();
$this->display();
}
/**
* Edit a ci job.
*
* @param int $id
* @access public
* @return void
*/
public function edit($id)
{
$job = $this->integration->getByID($id);
if($_POST)
{
$this->integration->update($id);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->app->loadLang('action');
$this->view->title = $this->lang->ci->job . $this->lang->colon . $this->lang->integration->edit;
$this->view->position[] = html::a(inlink('browse'), $this->lang->ci->job);
$this->view->position[] = $this->lang->integration->edit;
$repo = $this->loadModel('repo')->getRepoByID($job->repo);
$repoList = $this->repo->getList();
$repoPairs = array(0 => '', $repo->id => $repo->name);
$repoTypes[$repo->id] = $repo->SCM;
foreach($repoList as $repo)
{
$repoPairs[$repo->id] = $repo->name;
$repoTypes[$repo->id] = $repo->SCM;
}
$this->view->repoPairs = $repoPairs;
$this->view->repoTypes = $repoTypes;
$this->view->job = $job;
$this->view->jenkinsList = $this->loadModel('jenkins')->getPairs();
$this->view->jenkinsJobs = $this->jenkins->getTasks($job->jenkins);
$this->display();
}
/**
* Delete a ci job.
*
* @param int $id
* @access public
* @return void
*/
public function delete($id, $confirm = 'no')
{
if($confirm != 'yes') die(js::confirm($this->lang->integration->confirmDelete, inlink('delete', "jobID=$id&confirm=yes")));
$this->integration->delete(TABLE_INTEGRATION, $id);
die(js::reload('parent'));
}
/**
* Exec a ci job.
*
* @param int $id
* @access public
* @return void
*/
public function exec($id)
{
$result = $this->integration->exec($id);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
if(!$result) $this->send(array('result' => 'fail', 'message' => 'not found'));
$this->send(array('result' => 'success'));
}
}
+4
View File
@@ -0,0 +1,4 @@
.row.text-with-input .col{line-height: 32px;}
.only-pick-time thead th, .only-pick-time tfoot th {color: transparent !important;}
.checkbox-primary.checkbox-inline{display:inline-block !important;}
.checkbox-primary.checkbox-inline label{padding-left:5px !important;}
+4
View File
@@ -0,0 +1,4 @@
.row.text-with-input .col {line-height: 32px;}
.only-pick-time thead th, .only-pick-time tfoot th {color: transparent !important;}
.checkbox-primary.checkbox-inline{display:inline-block !important;}
.checkbox-primary.checkbox-inline label{padding-left:5px !important;}
+106
View File
@@ -0,0 +1,106 @@
$('#repo').change(function()
{
var repoID = $(this).val();
var type = 'Git';
if(typeof(repoTypes[repoID]) != 'undefined') type = repoTypes[repoID];
$('.svn-fields').toggleClass('hidden', type != 'Subversion');
$('#repoType').val(type);
if(type == 'Subversion')
{
$('#svnFolderBox').html("<div class='load-indicator loading'></div>");
$.getJSON(createLink('repo', 'ajaxGetSVNTags', 'repoID=' + repoID), function(svnTags)
{
var tags = svnTags['tags'];
var parents = svnTags['parent'];
html = "<select id='svnFolder' name='svnFolder' class='form-control'>";
for(tag in parents)
{
var info = parents[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
for(tag in tags)
{
var info = tags[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
html += '</select>';
$('#svnFolderBox').html(html);
$('#svnFolderBox #svnFolder').chosen();
})
}
})
$(document).on('change', '#svnFolder', function()
{
var repoID = $('#repo').val();
var selectedTag = $(this).val();
var encodePath = $(this).find("option:selected").attr('data-encodePath');
$('#svnFolderBox').html("<div class='load-indicator loading'></div>");
$.getJSON(createLink('repo', 'ajaxGetSVNTags', 'repoID=' + repoID + '&path=' + encodePath), function(svnTags)
{
var tags = svnTags['tags'];
var parents = svnTags['parent'];
html = "<select id='svnFolder' name='svnFolder' class='form-control'>";
for(tag in parents)
{
var info = parents[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
for(tag in tags)
{
var info = tags[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
html += '</select>';
$('#svnFolderBox').html(html);
$('#svnFolderBox #svnFolder').val(selectedTag).chosen();
})
})
$('#triggerType').change(function()
{
var type = $(this).val();
if(type == 'tag')
{
$('.comment-fields').addClass('hidden');
$('.custom-fields').addClass('hidden');
}
else if(type == 'commit')
{
$('.comment-fields').removeClass('hidden');
$('.custom-fields').addClass('hidden');
}
else if(type == 'schedule')
{
$('.comment-fields').addClass('hidden');
$('.custom-fields').removeClass('hidden');
}
});
$('#jenkins').change(function()
{
var jenkinsID = $(this).val();
$('#jenkinsJobBox').html("<div class='load-indicator loading'></div>");
$.getJSON(createLink('jenkins', 'ajaxGetTasks', 'jenkinsID=' + jenkinsID), function(tasks)
{
html = "<select id='jenkinsJob' name='jenkinsJob' class='form-control'>";
for(taskKey in tasks)
{
var task = tasks[taskKey];
html += "<option value='" + taskKey + "'>" + task + "</option>";
}
html += '</select>';
$('#jenkinsJobBox').html(html);
$('#jenkinsJobBox #jenkinsJob').chosen();
})
})
$(function()
{
$('#repo').change();
$('#triggerType').change();
});
+123
View File
@@ -0,0 +1,123 @@
$('#repo').change(function()
{
var repoID = $(this).val();
var type = 'Git';
if(typeof(repoTypes[repoID]) != 'undefined') type = repoTypes[repoID];
$('.svn-fields').toggleClass('hidden', type != 'Subversion');
$('#repoType').val(type);
if(type == 'Subversion')
{
$('#svnFolderBox').html("<div class='load-indicator loading'></div>");
var params = 'repoID=' + repoID;
if(jobRepo == repoID) params = 'repoID=' + repoID + '&path=' + encodeSVNFolder;
$.getJSON(createLink('repo', 'ajaxGetSVNTags', params), function(svnTags)
{
var tags = svnTags['tags'];
var parents = svnTags['parent'];
html = "<select id='svnFolder' name='svnFolder' class='form-control'>";
for(tag in parents)
{
var info = parents[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
for(tag in tags)
{
var info = tags[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
html += '</select>';
$('#svnFolderBox').html(html);
$('#svnFolderBox #svnFolder').val(svnFolder).chosen();
})
}
})
$(document).on('change', '#svnFolder', function()
{
var repoID = $('#repo').val();
var selectedTag = $(this).val();
var encodePath = $(this).find("option:selected").attr('data-encodePath');
$('#svnFolderBox').html("<div class='load-indicator loading'></div>");
$.getJSON(createLink('repo', 'ajaxGetSVNTags', 'repoID=' + repoID + '&path=' + encodePath), function(svnTags)
{
var tags = svnTags['tags'];
var parents = svnTags['parent'];
html = "<select id='svnFolder' name='svnFolder' class='form-control'>";
for(tag in parents)
{
var info = parents[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
for(tag in tags)
{
var info = tags[tag];
html += "<option value='" + info['path'] + "' data-encodePath='" + info['encodePath'] + "'>" + info['path'] + "</option>";
}
html += '</select>';
$('#svnFolderBox').html(html);
$('#svnFolderBox #svnFolder').val(selectedTag).chosen();
})
})
$('#triggerType').change(function()
{
var type = $(this).val();
if(type == 'tag')
{
$('.comment-fields').addClass('hidden');
$('.custom-fields').addClass('hidden');
}
else if(type == 'commit')
{
$('.comment-fields').removeClass('hidden');
$('.custom-fields').addClass('hidden');
}
else if(type == 'schedule')
{
$('.comment-fields').addClass('hidden');
$('.custom-fields').removeClass('hidden');
}
});
$('#jenkins').change(function()
{
var jenkinsID = $(this).val();
$('#jenkinsJobBox').html("<div class='load-indicator loading'></div>");
$.getJSON(createLink('jenkins', 'ajaxGetTasks', 'jenkinsID=' + jenkinsID), function(tasks)
{
html = "<select id='jenkinsJob' name='jenkinsJob' class='form-control'>";
for(taskKey in tasks)
{
var task = tasks[taskKey];
html += "<option value='" + taskKey + "'>" + task + "</option>";
}
html += '</select>';
$('#jenkinsJobBox').html(html);
$('#jenkinsJobBox #jenkinsJob').chosen();
})
})
$(function()
{
$('#repo').change();
$('#triggerType').change();
});
function execJob(id)
{
$.ajax(
{
type: "POST",
url: createLink('integration', 'exec', 'id=' + id),
data: {},
datatype: "json",
success: function(data)
{
$('.exe-job-button').tooltip('show', sendExec);
}
});
}
+31
View File
@@ -0,0 +1,31 @@
<?php
$lang->integration->browse = 'Browse Integration';
$lang->integration->create = 'Create Integration';
$lang->integration->edit = 'Edit Integration';
$lang->integration->execNow = 'Execute now';
$lang->integration->delete = 'Delete Integration';
$lang->integration->confirmDelete = 'Do you want to delete this Build?';
$lang->integration->id = 'ID';
$lang->integration->name = 'Name';
$lang->integration->repo = 'Repo';
$lang->integration->svnFolder = 'SVN Tag Watch Path';
$lang->integration->jenkins = 'Jenkins Server';
$lang->integration->buildType = 'Build Type';
$lang->integration->jenkinsJob = 'Jenkins Task';
$lang->integration->triggerType = 'Trigger';
$lang->integration->scheduleDay = 'Custom Days';
$lang->integration->lastExec = 'Last Executed';
$lang->integration->example = 'e.g.';
$lang->integration->commitEx = '%build% %integration% %id%15, to build Jenkins job that id is 15.';
$lang->integration->cronSample = 'e.g. 0 0 2 * * 2-6/1 means 2:00 a.m. every weekday.';
$lang->integration->sendExec = 'Send execute request success.';
$lang->integration->buildTypeList['build'] = 'Only Build';
$lang->integration->buildTypeList['buildAndDeploy'] = 'Build And Deploy';
$lang->integration->buildTypeList['buildAndTest'] = 'Build And Test';
$lang->integration->triggerTypeList['tag'] = 'Tag';
$lang->integration->triggerTypeList['commit'] = 'Code Commit';
$lang->integration->triggerTypeList['schedule'] = 'Schedule';
+33
View File
@@ -0,0 +1,33 @@
<?php
$lang->integration->common = '构建任务';
$lang->integration->browse = '浏览构建任务';
$lang->integration->create = '创建构建任务';
$lang->integration->start = '执行构建';
$lang->integration->edit = '编辑构建任务';
$lang->integration->execNow = '立即执行';
$lang->integration->delete = '删除构建任务';
$lang->integration->confirmDelete = '确认删除该构建任务吗?';
$lang->integration->id = 'ID';
$lang->integration->name = '名称';
$lang->integration->repo = '代码库';
$lang->integration->svnFolder = 'SVN Tag监控路径';
$lang->integration->jenkins = 'Jenkins服务';
$lang->integration->buildType = '构建类型';
$lang->integration->jenkinsJob = 'Jenkins任务名';
$lang->integration->triggerType = '触发方式';
$lang->integration->scheduleDay = '自定义天数';
$lang->integration->lastExec = '最后执行';
$lang->integration->example = '举例';
$lang->integration->commitEx = '%build% %integration% %id%15,其中15为Jenkins任务编号';
$lang->integration->cronSample = '如 0 0 2 * * 2-6/1 表示每个工作日凌晨2点';
$lang->integration->sendExec = '发送执行请求成功!';
$lang->integration->buildTypeList['build'] = '仅构建';
$lang->integration->buildTypeList['buildAndDeploy'] = '构建部署';
$lang->integration->buildTypeList['buildAndTest'] = '构建测试';
$lang->integration->triggerTypeList['tag'] = '打标签';
$lang->integration->triggerTypeList['commit'] = '代码提交注释';
$lang->integration->triggerTypeList['schedule'] = '定时计划';
+141
View File
@@ -0,0 +1,141 @@
<?php
/**
* The model file of integration module of ZenTaoCMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package integration
* @version $Id$
* @link http://www.zentao.net
*/
class integrationModel extends model
{
/**
* Get by id.
*
* @param int $id
* @access public
* @return object
*/
public function getByID($id)
{
return $this->dao->select('*')->from(TABLE_INTEGRATION)->where('id')->eq($id)->fetch();
}
/**
* Get integration list.
*
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getList($orderBy = 'id_desc', $pager = null)
{
return $this->dao->select('t1.*, t2.name as repoName, t3.name as jenkinsName')->from(TABLE_INTEGRATION)->alias('t1')
->leftJoin(TABLE_REPO)->alias('t2')->on('t1.repo=t2.id')
->leftJoin(TABLE_JENKINS)->alias('t3')->on('t1.jenkins=t3.id')
->where('t1.deleted')->eq('0')
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
}
/**
* Get list by triggerType field
*
* @param string $triggerType
* @access public
* @return array
*/
public function getListByTriggerType($triggerType)
{
return $this->dao->select('*')->from(TABLE_INTEGRATION)
->where('deleted')->eq('0')
->andWhere('triggerType')->eq($triggerType)
->fetchAll('id');
}
/**
* Create integration
*
* @access public
* @return void
*/
public function create()
{
$integration = fixer::input('post')
->add('createdBy', $this->app->user->account)
->add('createdDate', helper::now())
->remove('repoType')
->get();
if($integration->triggerType == 'schedule')
{
if(!isset($integration->scheduleDay)) $integration->scheduleDay = array();
$integration->scheduleDay = join(',', $integration->scheduleDay);
}
else
{
$integration->scheduleDay = '';
}
$this->dao->insert(TABLE_INTEGRATION)->data($integration)
->batchCheck($this->config->integration->create->requiredFields, 'notempty')
->batchCheckIF($integration->triggerType === 'schedule', "scheduleDay", 'notempty')
->batchCheckIF($this->post->repoType == 'Subversion', "svnFolder", 'notempty')
->autoCheck()
->exec();
if($integration->triggerType == 'schedule' and strpos($integration->scheduleDay, date('w')) !== false) $this->loadModel('compile')->createByIntegration($integration->id);
return true;
}
/**
* Update integration
*
* @param int $id
* @access public
* @return void
*/
public function update($id)
{
$oldIntegration = $this->getById($id);
$integration = fixer::input('post')
->add('editedBy', $this->app->user->account)
->add('editedDate', helper::now())
->remove('repoType')
->get();
if($integration->triggerType == 'schedule')
{
if(!isset($integration->scheduleDay)) $integration->scheduleDay = array();
$integration->scheduleDay = join(',', $integration->scheduleDay);
}
else
{
$integration->scheduleDay = '';
}
$this->dao->update(TABLE_INTEGRATION)->data($integration)
->batchCheck($this->config->integration->edit->requiredFields, 'notempty')
->batchCheckIF($integration->triggerType === 'schedule', "scheduleDay", 'notempty')
->batchCheckIF($this->post->repoType == 'Subversion', "svnFolder", 'notempty')
->autoCheck()
->where('id')->eq($id)
->exec();
if($integration->triggerType == 'schedule')
{
$week = date('w');
if($integration->triggerType != $oldIntegration->triggerType or strpos($oldIntegration->scheduleDay, $week) === false)
{
if(strpos($integration->scheduleDay, $week) !== false) $this->loadModel('compile')->createByIntegration($integration->id);
}
}
return true;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
/**
* The browse view file of ci job module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package ci
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<?php echo html::a($this->createLink('integration', 'browse'), "<span class='text'>{$lang->ci->task}</span>", '', "class='btn btn-link btn-active-text'");?>
</div>
<div class="btn-toolbar pull-right">
<?php if(common::hasPriv('integration', 'create')) common::printLink('integration', 'create', "", "<i class='icon icon-plus'></i> " . $lang->integration->create, '', "class='btn btn-primary'");?>
</div>
</div>
<div id='mainContent'>
<form class='main-table' id='ajaxForm' method='post'>
<table id='jobList' class='table has-sort-head table-fixed'>
<thead>
<tr>
<?php $vars = "orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?>
<th class='w-60px'><?php common::printOrderLink('id', $orderBy, $vars, $lang->integration->id);?></th>
<th class='w-200px text-left'><?php common::printOrderLink('name', $orderBy, $vars, $lang->integration->name);?></th>
<th class='w-200px text-left'><?php common::printOrderLink('repo', $orderBy, $vars, $lang->integration->repo);?></th>
<th class='w-150px text-left'><?php echo $lang->integration->triggerType;?></th>
<th class='w-200px text-left'><?php common::printOrderLink('jenkins', $orderBy, $vars, $lang->integration->jenkins);?></th>
<th class='w-200px text-left'><?php echo $lang->integration->jenkinsJob;?></th>
<th class='text-left'><?php echo $lang->integration->lastExec;?></th>
<th class='w-120px c-actions-4'><?php echo $lang->actions;?></th>
</tr>
</thead>
<tbody>
<?php foreach($jobList as $id => $job):?>
<tr>
<td class='text-center'><?php echo $id; ?></td>
<td class='text' title='<?php echo $job->name; ?>'><?php echo $job->name; ?></td>
<td class='text' title='<?php echo $job->repoName; ?>'><?php echo $job->repoName; ?></td>
<?php $triggerType = zget($lang->integration->triggerTypeList, $job->triggerType);?>
<td class='text' title='<?php echo $triggerType;?>'><?php echo $triggerType;?></td>
<td class='text' title='<?php echo $job->jenkinsName; ?>'><?php echo $job->jenkinsName; ?></td>
<td class='text' title='<?php echo $job->jenkinsJob; ?>'><?php echo urldecode($job->jenkinsJob);?></td>
<td class='text'><?php if($job->lastStatus) echo zget($lang->compile->statusList, $job->lastStatus) . $lang->ci->at . $job->lastExec;?></td>
<td class='c-actions text-center'>
<?php
common::printIcon('compile', 'browse', "jobID=$id", '', 'list', 'file-text');
common::printIcon('integration', 'edit', "jobID=$id", '', 'list', 'edit');
if(common::hasPriv('integration', 'delete')) echo html::a($this->createLink('integration', 'delete', "jobID=$id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->integration->delete}' class='btn'");
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php if($jobList):?>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
<?php endif;?>
</form>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+70
View File
@@ -0,0 +1,70 @@
<?php
/**
* The create view file of integration module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package integration
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<?php js::set('repoTypes', $repoTypes)?>
<?php js::set('triggerType', 'tag')?>
<div id='mainContent' class='main-row'>
<div class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->integration->create; ?></h2>
</div>
<form id='jobForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->integration->name; ?></th>
<td class='required'><?php echo html::input('name', '', "class='form-control'"); ?></td>
<td colspan="2" ></td>
</tr>
<tr>
<th><?php echo $lang->integration->repo; ?></th>
<td><?php echo html::select('repo', $repoPairs, '', "class='form-control chosen'"); ?></td>
<th class="svn-fields hidden"><?php echo $lang->integration->svnFolder; ?></th>
<td class="svn-fields hidden" id='svnFolderBox'></td>
</tr>
<tr>
<th><?php echo $lang->integration->jenkins; ?></th>
<td><?php echo html::select('jenkins', $jenkinsList, '', "class='form-control chosen'"); ?></td>
<th><?php echo $lang->integration->jenkinsJob; ?></th>
<td id='jenkinsJobBox'><?php echo html::select('jenkinsJob', array('' => ''), '', "class='form-control chosen'"); ?></td>
</tr>
<tr>
<th><?php echo $lang->integration->triggerType; ?></th>
<td><?php echo html::select('triggerType', $lang->integration->triggerTypeList, '', "class='form-control chosen'");?></td>
<td colspan="2"></td>
</tr>
<tr class="comment-fields" class="comment-fields">
<th><?php echo $lang->integration->example; ?></th>
<?php if(is_string($config->repo->matchComment)) $config->repo->matchComment = json_decode($config->repo->matchComment, true);?>
<td colspan="3"><?php echo str_replace(array('%build%', '%integration%', '%id%'), array($config->repo->matchComment['integration']['start'], $config->repo->matchComment['module']['integration'], $config->repo->matchComment['id']['mark']), $lang->integration->commitEx);?></td>
</tr>
<tr class="custom-fields">
<th><?php echo $lang->integration->scheduleDay; ?></th>
<td colspan="3"><?php echo html::checkbox('scheduleDay', $lang->datepicker->dayNames, '', '', 'inline');?></td>
</tr>
<tr>
<th></th>
<td class='text-center form-actions'>
<?php echo html::submitButton(); ?>
<?php echo html::backButton(); ?>
<?php echo html::hidden('repoType');?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+76
View File
@@ -0,0 +1,76 @@
<?php
/**
* The edit view file of integration module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package integration
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<?php js::set('repoTypes', $repoTypes)?>
<?php js::set('triggerType', $job->triggerType);?>
<?php js::set('jobRepo', $job->repo);?>
<?php js::set('svnFolder', $job->svnFolder);?>
<?php js::set('encodeSVNFolder', $this->loadModel('repo')->encodePath($job->svnFolder));?>
<?php js::set('jenkinsJob', $job->jenkinsJob);?>
<div id='mainContent' class='main-row'>
<div class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->integration->edit; ?></h2>
</div>
<form id='jobForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->integration->name; ?></th>
<td class='required'><?php echo html::input('name', $job->name, "class='form-control'"); ?></td>
<td colspan="2" ></td>
</tr>
<tr>
<th><?php echo $lang->integration->repo; ?></th>
<td><?php echo html::select('repo', $repoPairs, $job->repo, "class='form-control chosen'"); ?></td>
<th class="svn-fields hidden"><?php echo $lang->integration->svnFolder; ?></th>
<td class="svn-fields hidden" id='svnFolderBox'></td>
</tr>
<tr>
<th><?php echo $lang->integration->jenkins; ?></th>
<td><?php echo html::select('jenkins', $jenkinsList, $job->jenkins, "class='form-control chosen'"); ?></td>
<th><?php echo $lang->integration->jenkinsJob; ?></th>
<td id='jenkinsJobBox'><?php echo html::select('jenkinsJob', $jenkinsJobs, $job->jenkinsJob, "class='form-control chosen'");?></td>
</tr>
<tr>
<th><?php echo $lang->integration->triggerType; ?></th>
<td><?php echo html::select('triggerType', $lang->integration->triggerTypeList, $job->triggerType, "class='form-control chosen'"); ?></td>
<td colspan="2"></td>
</tr>
<tr class="comment-fields">
<th><?php echo $lang->integration->example; ?></th>
<?php if(is_string($config->repo->matchComment)) $config->repo->matchComment = json_decode($config->repo->matchComment, true);?>
<td colspan="3"><?php echo str_replace(array('%build%', '%integration%', '%id%'), array($config->repo->matchComment['integration']['start'], $config->repo->matchComment['module']['integration'], $config->repo->matchComment['id']['mark']), $lang->integration->commitEx);?></td>
</tr>
<tr class="custom-fields">
<th><?php echo $lang->integration->scheduleDay;?></th>
<td colspan="3"><?php echo html::checkbox('scheduleDay', $lang->datepicker->dayNames, $job->scheduleDay, '', 'inline');?></td>
</tr>
<tr>
<th></th>
<td colspan="2" class='text-center form-actions'>
<?php echo html::submitButton(); ?>
<?php echo html::backButton(); ?>
<?php echo html::hidden('repoType', zget($repoTypes, $job->repo, 'Git'));?>
<?php echo html::commonButton($lang->integration->execNow, "onclick=execJob($job->id) data-tip-class='tooltip-success'", "btn btn-info exe-job-button");?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php js::set('sendExec', $lang->integration->sendExec);?>
<?php include '../../common/view/footer.html.php';?>
+5
View File
@@ -0,0 +1,5 @@
<?php
$config->jenkins->create = new stdclass();
$config->jenkins->edit = new stdclass();
$config->jenkins->create->requiredFields = 'name,serviceUrl,credentials';
$config->jenkins->edit->requiredFields = 'name,serviceUrl,credentials';
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* The control file of jenkins module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package product
* @version $Id: ${FILE_NAME} 5144 2020/1/8 8:10 下午 chenqi@cnezsoft.com $
* @link http://www.zentao.net
*/
class jenkins extends control
{
/**
* jenkins constructor.
* @param string $moduleName
* @param string $methodName
*/
public function __construct($moduleName = '', $methodName = '')
{
parent::__construct($moduleName, $methodName);
$this->loadModel('ci')->setMenu();
}
/**
* Browse jenkinss.
*
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
if(common::hasPriv('jenkins', 'create')) $this->lang->modulePageActions = html::a(helper::createLink('jenkins', 'create'), "<i class='icon icon-plus'></i> " . $this->lang->jenkins->create, '', "class='btn btn-primary'");
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->view->title = $this->lang->jenkins->common . $this->lang->colon . $this->lang->jenkins->browse;
$this->view->position[] = $this->lang->jenkins->common;
$this->view->position[] = $this->lang->jenkins->browse;
$this->view->jenkinsList = $this->jenkins->getList($orderBy, $pager);
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* Create a jenkins.
*
* @access public
* @return void
*/
public function create()
{
if($_POST)
{
$this->jenkins->create();
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->app->loadLang('action');
$this->view->title = $this->lang->jenkins->common . $this->lang->colon . $this->lang->jenkins->create;
$this->view->position[] = html::a(inlink('browse'), $this->lang->jenkins->common);
$this->view->position[] = $this->lang->jenkins->create;
$this->display();
}
/**
* Edit a jenkins.
*
* @param int $id
* @access public
* @return void
*/
public function edit($id)
{
$jenkins = $this->jenkins->getByID($id);
if($_POST)
{
$this->jenkins->update($id);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->app->loadLang('action');
$this->view->title = $this->lang->jenkins->common . $this->lang->colon . $this->lang->jenkins->edit;
$this->view->position[] = html::a(inlink('browse'), $this->lang->jenkins->common);
$this->view->position[] = $this->lang->jenkins->edit;
$this->view->jenkins = $jenkins;
$this->display();
}
/**
* Delete a jenkins.
*
* @param int $id
* @access public
* @return void
*/
public function delete($id, $confim = 'no')
{
if($confim != 'yes') die(js::confirm($this->lang->jenkins->confirmDelete, inlink('delete', "id=$id&confirm=yes")));
$this->jenkins->delete(TABLE_JENKINS, $id);
die(js::reload('parent'));
}
/**
* Ajax get tasks.
*
* @param int $id
* @access public
* @return void
*/
public function ajaxGetTasks($id)
{
$tasks = $this->jenkins->getTasks($id);
die(json_encode($tasks));
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
$lang->jenkins->common = 'Jenkins';
$lang->jenkins->browse = 'View';
$lang->jenkins->create = 'Create';
$lang->jenkins->edit = 'Edit';
$lang->jenkins->delete = 'Delete';
$lang->jenkins->confirmDelete = 'Do you want to delete this Jenkins server?';
$lang->jenkins->id = 'ID';
$lang->jenkins->name = 'Name';
$lang->jenkins->serviceUrl = 'Service URL';
$lang->jenkins->token = 'Token';
$lang->jenkins->account = 'UserName';
$lang->jenkins->password = 'Password';
$lang->jenkins->desc = 'Description';
$lang->jenkins->tokenFirst = 'Use token if not empty.';
$lang->jenkins->tips = 'Cancel "Prevent Cross Site Request Forgery exploits" when using password.';
+18
View File
@@ -0,0 +1,18 @@
<?php
$lang->jenkins->common = 'Jenkins';
$lang->jenkins->browse = '浏览';
$lang->jenkins->create = '创建';
$lang->jenkins->edit = '编辑';
$lang->jenkins->delete = '删除';
$lang->jenkins->confirmDelete = '确认删除该Jenkins吗?';
$lang->jenkins->id = 'ID';
$lang->jenkins->name = '名称';
$lang->jenkins->serviceUrl = '服务地址';
$lang->jenkins->token = 'Token';
$lang->jenkins->account = '用户名';
$lang->jenkins->password = '密码';
$lang->jenkins->desc = '描述';
$lang->jenkins->tokenFirst = 'Token不为空时,优先使用Token。';
$lang->jenkins->tips = '使用密码时,请在Jenkins全局安全设置中禁用"防止跨站点请求伪造"选项。';
+138
View File
@@ -0,0 +1,138 @@
<?php
/**
* The model file of jenkins module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chenqi <chenqi@cnezsoft.com>
* @package product
* @version $Id: $
* @link http://www.zentao.net
*/
class jenkinsModel extends model
{
/**
* Get a jenkins by id.
*
* @param int $id
* @access public
* @return object
*/
public function getByID($id)
{
$jenkins = $this->dao->select('*')->from(TABLE_JENKINS)->where('id')->eq($id)->fetch();
$jenkins->password = base64_decode($jenkins->password);
return $jenkins;
}
/**
* Get jenkins list.
*
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getList($orderBy = 'id_desc', $pager = null)
{
return $this->dao->select('*')->from(TABLE_JENKINS)
->where('deleted')->eq('0')
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
}
/**
* Create a jenkins.
*
* @access public
* @return bool
*/
public function create()
{
$jenkins = fixer::input('post')
->add('createdBy', $this->app->user->account)
->add('createdDate', helper::now())
->skipSpecial('serviceUrl,token,account,password')
->get();
$jenkins->password = base64_encode($jenkins->password);
$this->dao->insert(TABLE_JENKINS)->data($jenkins)
->batchCheck($this->config->jenkins->create->requiredFields, 'notempty')
->batchCheck("serviceUrl", 'URL')
->autoCheck()
->exec();
return !dao::isError();
}
/**
* Update a jenkins.
*
* @param int $id
* @access public
* @return bool
*/
public function update($id)
{
$jenkins = fixer::input('post')
->add('editedBy', $this->app->user->account)
->add('editedDate', helper::now())
->skipSpecial('serviceUrl,token,account,password')
->get();
$jenkins->password = base64_encode($jenkins->password);
$this->dao->update(TABLE_JENKINS)->data($jenkins)
->batchCheck($this->config->jenkins->edit->requiredFields, 'notempty')
->batchCheck("serviceUrl", 'URL')
->autoCheck()
->where('id')->eq($id)
->exec();
return !dao::isError();
}
/**
* list jenkins for ci task edit
*
* @return array
*/
public function getPairs()
{
$jenkins = $this->dao->select('id,name')->from(TABLE_JENKINS)->where('deleted')->eq('0')->orderBy('id')->fetchPairs('id', 'name');
$jenkins = array('' => '') + $jenkins;
return $jenkins;
}
/**
* Get jenkins tasks.
*
* @param int $id
* @access public
* @return array
*/
public function getTasks($id)
{
$jenkins = $this->getById($id);
$jenkinsServer = $jenkins->serviceUrl;
$jenkinsUser = $jenkins->account;
$jenkinsPassword = $jenkins->token ? $jenkins->token : $jenkins->password;
$jenkinsAuth = '://' . $jenkinsUser . ':' . $jenkinsPassword . '@';
$jenkinsServer = str_replace('://', $jenkinsAuth, $jenkinsServer);
$response = common::http($jenkinsServer . '/api/json/items/list');
$response = json_decode($response);
$tasks = array();
if(isset($response->jobs))
{
foreach($response->jobs as $job) $tasks[basename($job->url)] = $job->name;
}
return $tasks;
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
/**
* The browse view file of jenkins module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package jenkins
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<div id='mainContent' class='main-row'>
<form class='main-table' id='ajaxForm' method='post'>
<table id='jenkinsList' class='table has-sort-head table-fixed'>
<thead>
<tr>
<?php $vars = "orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"; ?>
<th class='w-60px'><?php common::printOrderLink('id', $orderBy, $vars, $lang->jenkins->id); ?></th>
<th class='w-200px text-left'><?php common::printOrderLink('name', $orderBy, $vars, $lang->jenkins->name); ?></th>
<th class='text-left'><?php common::printOrderLink('serviceUrl', $orderBy, $vars, $lang->jenkins->serviceUrl); ?></th>
<th class='w-100px c-actions-4'><?php echo $lang->actions; ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($jenkinsList as $id => $jenkins): ?>
<tr>
<td class='text-center'><?php echo $id; ?></td>
<td class='text' title='<?php echo $jenkins->name; ?>'><?php echo $jenkins->name; ?></td>
<td class='text' title='<?php echo $jenkins->serviceUrl; ?>'><?php echo $jenkins->serviceUrl; ?></td>
<td class='c-actions text-left'>
<?php
common::printIcon('jenkins', 'edit', "jenkinsID=$id", '', 'list', 'edit');
if(common::hasPriv('jenkins', 'delete')) echo html::a($this->createLink('jenkins', 'delete', "jenkinsID=$id"), '<i class="icon-trash"></i>', '', "title='{$lang->jenkins->delete}' class='btn'");
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if($jenkinsList):?>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
<?php endif; ?>
</form>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* The create view file of jenkins module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package jenkins
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<div id='mainContent' class='main-row'>
<div class='main-col main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->jenkins->create; ?></h2>
</div>
<form id='jenkinsForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->jenkins->name; ?></th>
<td class='required'><?php echo html::input('name', '', "class='form-control'"); ?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->serviceUrl; ?></th>
<td class='required'><?php echo html::input('serviceUrl', '', "class='form-control'"); ?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->account;?></th>
<td><?php echo html::input('account', '', "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->token;?></th>
<td><?php echo html::input('token', '', "class='form-control'");?></td>
<td><?php echo $lang->jenkins->tokenFirst;?></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->password;?></th>
<td><?php echo html::password('password', '', "class='form-control'");?></td>
<td><?php echo $lang->jenkins->tips;?></td>
</tr>
<tr>
<th></th>
<td class='text-center form-actions'>
<?php echo html::submitButton(); ?>
<?php echo html::backButton(); ?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+62
View File
@@ -0,0 +1,62 @@
<?php
/**
* The edit view file of jenkins module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package jenkins
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php'; ?>
<div id='mainContent' class='main-row'>
<div class='main-col main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->jenkins->edit; ?></h2>
</div>
<form id='jenkinsForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th class='thWidth'></th>
<td colspan="2"><?php echo $lang->jenkins->tips; ?></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->name; ?></th>
<td class='required'><?php echo html::input('name', $jenkins->name, "class='form-control'"); ?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->serviceUrl; ?></th>
<td class='required'><?php echo html::input('serviceUrl', $jenkins->serviceUrl, "class='form-control'"); ?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->account;?></th>
<td><?php echo html::input('account', $jenkins->account, "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->token;?></th>
<td><?php echo html::input('token', $jenkins->token, "class='form-control'");?></td>
<td><?php echo $lang->jenkins->tokenFirst;?></td>
</tr>
<tr>
<th><?php echo $lang->jenkins->password;?></th>
<td><?php echo html::password('password', $jenkins->password, "class='form-control'");?></td>
<td><?php echo $lang->jenkins->tips;?></td>
</tr>
<tr>
<th></th>
<td class='text-center form-actions'>
<?php echo html::submitButton(); ?>
<?php echo html::backButton() ?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+1 -1
View File
@@ -17,7 +17,7 @@
$recTotalLabel = " <span class='label label-light label-badge'>{$pager->recTotal}</span>";
echo html::a(inlink('bug', "type=assignedTo"), "<span class='text'>{$lang->bug->assignedTo}</span>" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'");
echo html::a(inlink('bug', "type=openedBy"), "<span class='text'>{$lang->bug->openedByMe}</span>" . ($type == 'openedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'openedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink('bug', "type=resolvedBy"), "<span class='text'>{$lang->bug->resolvedBy}</span>" . ($type == 'resolvedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'resolvedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink('bug', "type=resolvedBy"), "<span class='text'>{$lang->bug->resolvedByMe}</span>" . ($type == 'resolvedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'resolvedBy' ? ' btn-active-text' : '') . "'");
echo html::a(inlink('bug', "type=closedBy"), "<span class='text'>{$lang->bug->closedByMe}</span>" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'");
?>
</div>
+4 -6
View File
@@ -48,12 +48,11 @@
<th class='c-project'> <?php common::printOrderLink('project', $orderBy, $vars, $lang->task->project);?></th>
<th class='c-name'> <?php common::printOrderLink('name', $orderBy, $vars, $lang->task->name);?></th>
<th class='c-user w-90px'><?php common::printOrderLink('openedBy', $orderBy, $vars, $lang->openedByAB);?></th>
<th class='w-120px c-assignedTo'><?php common::printOrderLink('assignedTo', $orderBy, $vars, $lang->task->assignedTo);?></th>
<th class='w-90px c-assignedTo'><?php common::printOrderLink('assignedTo', $orderBy, $vars, $lang->task->assignedTo);?></th>
<th class='c-user w-100px'><?php common::printOrderLink('finishedBy', $orderBy, $vars, $lang->task->finishedBy);?></th>
<th class='c-hours w-70px'><?php common::printOrderLink('estimate', $orderBy, $vars, $lang->task->estimateAB);?></th>
<th class='c-hours'> <?php common::printOrderLink('consumed', $orderBy, $vars, $lang->task->consumedAB);?></th>
<th class='c-hours'> <?php common::printOrderLink('left', $orderBy, $vars, $lang->task->leftAB);?></th>
<th class='c-date'> <?php common::printOrderLink('deadline', $orderBy, $vars, $lang->task->deadlineAB);?></th>
<th class='c-hours w-50px'><?php common::printOrderLink('estimate', $orderBy, $vars, $lang->task->estimateAB);?></th>
<th class='c-hours w-50px'> <?php common::printOrderLink('consumed', $orderBy, $vars, $lang->task->consumedAB);?></th>
<th class='c-hours w-50px'> <?php common::printOrderLink('left', $orderBy, $vars, $lang->task->leftAB);?></th>
<th class='c-status'> <?php common::printOrderLink('status', $orderBy, $vars, $lang->statusAB);?></th>
<th class='c-actions-6'><?php echo $lang->actions;?></th>
</tr>
@@ -83,7 +82,6 @@
<td class='c-hours'><?php echo $task->estimate;?></td>
<td class='c-hours'><?php echo $task->consumed;?></td>
<td class='c-hours'><?php echo $task->left;?></td>
<td class='c-date <?php if(isset($task->delay)) echo 'text-red';?>'><?php if(substr($task->deadline, 0, 4) > 0) echo $task->deadline;?></td>
<td class='c-status'>
<?php $storyChanged = (!empty($task->storyStatus) and $task->storyStatus == 'active' and $task->latestStoryVersion > $task->storyVersion and !in_array($task->status, array('cancel', 'closed')));?>
<?php !empty($storyChanged) ? print("<span class='status-story status-changed'>{$this->lang->story->changed}</span>") : print("<span class='status-task status-{$task->status}'> " . $this->processStatus('task', $task) . "</span>");?>
+31
View File
@@ -26,3 +26,34 @@ $config->repo->syncTime = 10;
$config->repo->batchNum = 100;
$config->repo->images = '|png|gif|jpg|ico|jpeg|bmp|';
$config->repo->binary = '|pdf|';
$config->repo->editor = new stdclass();
$config->repo->editor->create = array('id' => 'desc', 'tools' => 'simpleTools');
$config->repo->editor->edit = array('id' => 'desc', 'tools' => 'simpleTools');
$config->repo->editor->view = array('id' => 'commentText', 'tools' => 'simpleTools');
$config->repo->editor->diff = array('id' => 'commentText', 'tools' => 'simpleTools');
$config->repo->create = new stdclass();
$config->repo->create->requiredFields = 'SCM,name,path,encoding,client';
$config->repo->edit = new stdclass();
$config->repo->edit->requiredFields = 'SCM,name,path,encoding,client';
$config->repo->svn = new stdclass();
$config->repo->svn->requiredFields = 'account,password';
$config->repo->matchComment['module']['story'] = 'Story';
$config->repo->matchComment['module']['task'] = 'Task';
$config->repo->matchComment['module']['bug'] = 'Bug';
$config->repo->matchComment['module']['integration'] = 'Build';
$config->repo->matchComment['task']['start'] = 'Start';
$config->repo->matchComment['task']['finish'] = 'Finish';
$config->repo->matchComment['task']['cancel'] = 'Cancel';
$config->repo->matchComment['task']['consumed'] = 'Cost';
$config->repo->matchComment['task']['left'] = 'Left';
$config->repo->matchComment['bug']['resolve'] = 'Fix';
$config->repo->matchComment['bug']['resolvedBuild'] = 'Build';
$config->repo->matchComment['integration']['start'] = 'Start';
$config->repo->matchComment['id']['mark'] = '#';
$config->repo->matchComment['id']['split'] = ',';
$config->repo->matchComment['mark']['consumed'] = ':';
$config->repo->matchComment['mark']['left'] = ':';
$config->repo->matchComment['mark']['resolvedBuild'] = '#';
+226 -28
View File
@@ -29,7 +29,6 @@ class repo extends control
$this->scm = $this->app->loadClass('scm');
$this->repos = $this->repo->getRepoPairs();
if(common::hasPriv('repo', 'create')) $this->lang->modulePageActions = html::a(helper::createLink('repo', 'create'), "<i class='icon icon-plus'></i> " . $this->lang->repo->create, '', "class='btn btn-primary'");
if(empty($this->repos) and $this->methodName != 'create') die(js::locate($this->repo->createLink('create')));
/* Unlock session for wait to get data of repo. */
@@ -37,54 +36,104 @@ class repo extends control
}
/**
* Create repo.
*
* List all repo.
*
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function maintain($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$repoID = $this->session->repoID;
$this->repo->setMenu($this->repos, $repoID, false);
if(common::hasPriv('repo', 'create')) $this->lang->modulePageActions = html::a(helper::createLink('repo', 'create'), "<i class='icon icon-plus'></i> " . $this->lang->repo->create, '', "class='btn btn-primary'");
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$this->view->repoList = $this->repo->getList($orderBy, $pager);
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->browse;
$this->view->position[] = $this->lang->repo->common;
$this->view->position[] = $this->lang->repo->browse;
$this->view->repoID = $repoID;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* Create a repo.
*
* @access public
* @return void
*/
public function create()
{
$this->repo->setMenu($this->repos);
if(!empty($_POST))
if($_POST)
{
$repoID = $this->repo->create();
if(dao::isError()) die(js::error(dao::getError()));
die(js::locate($this->repo->createLink('showSyncComment', "repoID=$repoID"), 'parent'));
$link = $this->repo->createLink('showSyncComment', "repoID=$repoID");
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link));
}
$this->view->title = $this->lang->repo->create;
$this->repo->setMenu($this->repos, '', false);
$this->app->loadLang('action');
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->users = $this->loadModel('user')->getPairs('noletter|noempty|nodeleted');
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->create;
$this->view->position[] = html::a(inlink('maintain'), $this->lang->repo->common);
$this->view->position[] = $this->lang->repo->create;
$this->display();
}
/**
* Set repo.
*
* @param int $repoID
* Edit a repo.
*
* @param int $repoID
* @access public
* @return void
*/
public function settings($repoID = 0)
public function edit($repoID)
{
$this->repo->setMenu($this->repos, $repoID);
if($repoID == 0) $repoID = $this->session->repoID;
if(!empty($_POST))
$repo = $this->repo->getRepoByID($repoID);
if($_POST)
{
$needSync = $this->repo->saveSettings($repoID);
$noNeedSync = $this->repo->update($repoID);
if(dao::isError()) die(js::error(dao::getError()));
if(!$needSync)
if(!$noNeedSync)
{
die(js::locate($this->repo->createLink('showSyncComment', "repoID=$repoID"), 'parent'));
$link = $this->repo->createLink('showSyncComment', "repoID=$repoID");
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link));
}
die(js::locate($this->repo->createLink('log', "repoID=$repoID"), 'parent'));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('maintain')));
}
$this->view->title = $this->lang->repo->settings;
$this->view->repo = $this->repo->getRepoByID($repoID);
$this->repo->setMenu($this->repos, $repo->id, false);
$this->app->loadLang('action');
$repo->repoType = $repo->id . '-' . $repo->SCM;
$this->view->repo = $repo;
$this->view->repoID = $repoID;
$this->view->groups = $this->loadModel('group')->getPairs();
$this->view->users = $this->loadModel('user')->getPairs('noletter|noempty|nodeleted', !empty($repo->acl->users) ? $repo->acl->users : '');
$this->display();
$this->view->users = $this->loadModel('user')->getPairs('noletter|noempty|nodeleted');
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->edit;
$this->view->position[] = html::a(inlink('maintain'), $this->lang->repo->common);
$this->view->position[] = $this->lang->repo->edit;
$this->display();
}
/**
@@ -101,12 +150,14 @@ class repo extends control
{
die(js::confirm($this->lang->repo->notice->delete, $this->repo->createLink('delete', "repoID=$repoID&confirm=yes")));
}
$this->dao->delete()->from(TABLE_REPO)->where('id')->eq($repoID)->exec();
$this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->exec();
$this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($repoID)->exec();
$this->dao->delete()->from(TABLE_REPOBRANCH)->where('repo')->eq($repoID)->exec();
echo js::alert($this->lang->repo->notice->successDelete);
die(js::locate($this->repo->createLink('log'), 'parent'));
if(dao::isError()) die(js::error(dao::getError()));
die(js::reload('parent'));
}
/**
@@ -193,6 +244,9 @@ class repo extends control
$this->view->logType = $logType;
$this->view->info = $info;
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->view;
$this->view->position[] = $this->lang->repo->common;
$this->view->position[] = $this->lang->repo->view;
$this->display();
}
@@ -302,7 +356,7 @@ class repo extends control
}
else
{
$change['view'] = $viewPriv ? html::a($this->repo->createLink('log', "repoID=$repoID&entry=&revision=$revision", "entry=$encodePath"), $this->lang->repo->log) : '';
$change['view'] = $viewPriv ? html::a($this->repo->createLink('browse', "repoID=$repoID&path=&revision=$revision", "path=$encodePath"), $this->lang->repo->browse) : '';
if($change['action'] == 'M') $change['diff'] = $diffPriv ? html::a($this->repo->createLink('diff', "repoID=$repoID&entry=&oldRevision=$oldRevision&newRevision=$revision", "entry=$encodePath"), $this->lang->repo->diffAB) : '';
}
$changes[$path] = $change;
@@ -330,6 +384,55 @@ class repo extends control
$this->view->oldRevision = $oldRevision;
$this->view->preAndNext = $this->repo->getPreAndNext($repo, $root, $revision, $type, 'revision');
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->viewRevision;
$this->view->position[] = $this->lang->repo->common;
$this->view->position[] = $this->lang->repo->viewRevision;
$this->display();
}
/**
* Blame repo file.
*
* @param int $repoID
* @param string $entry
* @param string $revision
* @param string $encoding
* @access public
* @return void
*/
public function blame($repoID, $entry, $revision = 'HEAD', $encoding = '')
{
if($this->get->entry) $entry = $this->get->entry;
$this->repo->setMenu($this->repos, $repoID);
if($repoID == 0) $repoID = $this->session->repoID;
$repo = $this->repo->getRepoByID($repoID);
$file = $entry;
$entry = $this->repo->decodePath($entry);
$this->scm->setEngine($repo);
$encoding = empty($encoding) ? $repo->encoding : $encoding;
$encoding = strtolower(str_replace('_', '-', $encoding));
$blames = $this->scm->blame($entry, $revision);
$revisions = array();
foreach($blames as $i => $blame)
{
if(isset($blame['revision'])) $revisions[$blame['revision']] = $blame['revision'];
if($encoding != 'utf-8') $blames[$i]['content'] = helper::convertEncoding($blame['content'], $encoding);
}
$log = $repo->SCM == 'Git' ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->eq($revision)->andWhere('repo')->eq($repo->id)->fetch() : '';
$this->view->title = $this->lang->repo->common;
$this->view->repoID = $repoID;
$this->view->repo = $repo;
$this->view->revision = $revision;
$this->view->entry = $entry;
$this->view->file = $file;
$this->view->encoding = str_replace('-', '_', $encoding);
$this->view->historys = $repo->SCM == 'Git' ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in($revisions)->andWhere('repo')->eq($repo->id)->fetchPairs() : '';
$this->view->revisionName = ($log and $repo->SCM == 'Git') ? $this->repo->getGitRevisionName($log->revision, $log->commit) : $revision;
$this->view->blames = $blames;
$this->display();
}
@@ -420,9 +523,6 @@ class repo extends control
}
}
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->diff;
$this->view->position[] = $this->lang->repo->diff;
$this->view->type = 'diff';
$this->view->showBug = $showBug;
$this->view->entry = urldecode($entry);
@@ -439,6 +539,10 @@ class repo extends control
$this->view->historys = $repo->SCM == 'Git' ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in("$oldRevision,$newRevision")->andWhere('repo')->eq($repo->id)->fetchPairs() : '';
$this->view->info = $info;
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->diff;
$this->view->position[] = $this->lang->repo->common;
$this->view->position[] = $this->lang->repo->diff;
$this->display();
}
@@ -466,6 +570,37 @@ class repo extends control
$this->fetch('file', 'sendDownHeader', array("fileName" => $fileName, "fileType" => $extension, "content" => $content));
}
/**
* Set matchComment.
*
* @access public
* @return void
*/
public function setMatchComment($module = '')
{
if($_POST)
{
$module = $this->post->selectModule;
unset($_POST['selectModule']);
$this->loadModel('setting')->setItem('system.repo.matchComment', json_encode($this->post->matchComment));
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('setMatchComment', "module={$module}")));
}
$this->repo->setMenu($this->repos, $this->session->repoID, false);
$this->app->loadLang('task');
$this->app->loadLang('bug');
$this->app->loadLang('story');
$this->app->loadLang('integration');
if(is_string($this->config->repo->matchComment)) $this->config->repo->matchComment = json_decode($this->config->repo->matchComment, true);
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->setMatchComment;
$this->view->position[] = $this->lang->repo->setMatchComment;
$this->view->selectModule = $module;
$this->display();
}
/**
* Show sync comment.
*
@@ -628,4 +763,67 @@ class repo extends control
die('norecords');
}
/**
* Ajax get svn tags
*
* @param int $repoID
* @param string $path
* @param string $revision
* @access public
* @return void
*/
public function ajaxGetSVNTags($repoID, $path = '', $revision = 'HEAD')
{
if($this->get->path) $entry = $this->get->path;
$repo = $this->repo->getRepoByID($repoID);
if($repo->SCM != 'Subversion') die(json_encode(array()));
$this->scm->setEngine($repo);
$path = $this->repo->decodePath($path);
$tags = $this->scm->tags($path, $revision);
$parentInfos = array();
$parentPath = '';
$info = array();
$info['path'] = '/';
$info['url'] = $repo->path . $info['path'];
$info['encodePath'] = $this->repo->encodePath($info['path']);
$parentInfos['/'] = $info;
foreach(explode('/', $path) as $parent)
{
if(empty($parent)) continue;
$parentPath .= '/' . $parent;
$info = array();
$info['path'] = $parentPath;
$info['url'] = $repo->path . $info['path'];
$info['encodePath'] = $this->repo->encodePath($info['path']);
$parentInfos[$parent] = $info;
}
$tagInfos = array();
foreach($tags as $tag)
{
$info = array();
$info['path'] = '/';
if($path) $info['path'] .= $path . '/';
$info['path'] .= $tag;
$info['url'] = $repo->path . $info['path'];
$info['encodePath'] = $this->repo->encodePath($info['path']);
$tagInfos[$tag] = $info;
}
$svnTags = array();
$svnTags['parent'] = $parentInfos;
$svnTags['tags'] = $tagInfos;
die(json_encode($svnTags));
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
a{color:#169;}
a:hover, a:active{ text-decoration:underline; color:#C61A1A;}
h2,h3 {font-size:20px; padding:0 0 10px; margin: 0; clear: both;}
h2,h3 {font-size:20px; margin: 0; clear: both;}
h3 {font-size:16px;}
.revision{font-size:12px; line-height:20px; text-align:right; padding-right:8px;}
.directory{ background-image:url('theme/default/images/repo/dir.png')}
@@ -76,11 +76,11 @@ h3 {font-size:16px;}
.repoCode tr.commented .comment-btn {display: block;}
.repoCode tr.commented .comment-btn .icon-wrapper {background: none; border: none; width: 24px; line-height: 18px; height: 18px; left: -6px; color: #4183c4}
.repoCode tr.commented .comment-btn .icon-wrapper:hover {border-color: #169; color: #169}
.repoCode tr.commented .comment-btn .icon-wrapper > i:before {content: '\e750'; font-size: 18px; transform: scale(-1, 1); display: inline-block;}
.repoCode tr.commented .comment-btn .icon-wrapper > i:before {font-size: 18px; transform: scale(-1, 1); display: inline-block;}
.repoCode tr.commented .comment-btn .icon-wrapper:before {display: none}
.repoCode tr.over.commented .comment-btn .icon-wrapper, .repoCode tr.selected.commented .comment-btn .icon-wrapper {line-height: 20px; height: 20px; background: #4183C4; color: #fff; left: -6px}
.repoCode tr.over.commented .comment-btn .icon-wrapper > i:before {content: "\e661"; font-size: 14px;}
.repoCode tr.over.commented .comment-btn .icon-wrapper > i:before {font-size: 14px;}
.repoCode tr.selected.commented .comment-btn .icon-wrapper > i:before {font-size: 14px;}
.repoCode tr.over.commented .comment-btn .icon-wrapper:before, .repoCode tr.selected.commented .comment-btn .icon-wrapper:before {display: block;}
+20
View File
@@ -0,0 +1,20 @@
$(function()
{
scmChanged(scm);
});
function scmChanged(scm) {
if(scm == 'Git')
{
$('.account-fields').addClass('hidden');
$('.tips-git').removeClass('hidden');
$('.tips-svn').addClass('hidden');
} else
{
$('.account-fields').removeClass('hidden');
$('.tips-git').addClass('hidden');
$('.tips-svn').removeClass('hidden');
}
}
+20
View File
@@ -0,0 +1,20 @@
$(function()
{
scmChanged(scm);
});
function scmChanged(scm) {
if(scm == 'Git')
{
$('.account-fields').addClass('hidden');
$('.tips-git').removeClass('hidden');
$('.tips-svn').addClass('hidden');
} else
{
$('.account-fields').removeClass('hidden');
$('.tips-git').addClass('hidden');
$('.tips-svn').removeClass('hidden');
}
}
-150
View File
@@ -1,150 +0,0 @@
<?php
$lang->repo->common = 'Repo';
$lang->repo->create = 'Create Repo';
$lang->repo->settings = 'Settings';
$lang->repo->browse = 'View Repo';
$lang->repo->delete = 'Delete Repo';
$lang->repo->showSyncComment = 'Display Synchronization';
$lang->repo->ajaxSyncComment = 'Interface: Ajax Sync Note';
$lang->repo->download = 'Download File';
$lang->repo->downloadDiff = 'Download Diff';
$lang->repo->diffAction = 'Revision Diff';
$lang->repo->revisionAction = 'Revision Detail';
$lang->repo->blameAction = 'Repo Blame';
$lang->repo->addBug = 'Add Review';
$lang->repo->editBug = 'Edit Bug';
$lang->repo->deleteBug = 'Delete Bug';
$lang->repo->addComment = 'Add Comment';
$lang->repo->editComment = 'Edit Comment';
$lang->repo->deleteComment = 'Delete Comment';
$lang->repo->submit = 'Submit';
$lang->repo->cancel = 'Cancel';
$lang->repo->addComment = 'Add Comment';
$lang->repo->product = $lang->productCommon;
$lang->repo->module = 'Module';
$lang->repo->project = $lang->projectCommon;
$lang->repo->type = 'Type';
$lang->repo->assign = 'AssignTo';
$lang->repo->title = 'Title';
$lang->repo->detile = 'Detail';
$lang->repo->lines = 'Lines';
$lang->repo->line = 'Line';
$lang->repo->expand = 'Unfold';
$lang->repo->collapse = 'Fold';
$lang->repo->id = 'ID';
$lang->repo->SCM = 'Type';
$lang->repo->name = 'Name';
$lang->repo->path = 'Path';
$lang->repo->prefix = 'Prefix';
$lang->repo->config = 'Config';
$lang->repo->account = 'Username';
$lang->repo->password = 'Password';
$lang->repo->encoding = 'Encoding';
$lang->repo->client = 'Client Path';
$lang->repo->size = 'Size';
$lang->repo->revision = 'Revision';
$lang->repo->revisionA = 'Revision';
$lang->repo->revisions = 'Revision';
$lang->repo->time = 'Date';
$lang->repo->committer = 'Committer';
$lang->repo->commits = 'Commits';
$lang->repo->synced = 'Initialize Sync';
$lang->repo->lastSync = 'Last Sync';
$lang->repo->deleted = 'Deleted';
$lang->repo->commit = 'Commit';
$lang->repo->comment = 'Comment';
$lang->repo->view = 'View File';
$lang->repo->viewA = 'View';
$lang->repo->log = 'Revision Log';
$lang->repo->blame = 'Blame';
$lang->repo->date = 'Date';
$lang->repo->diff = 'Diff';
$lang->repo->diffAB = 'Diff';
$lang->repo->diffAll = 'Diff All';
$lang->repo->viewDiff = 'View diff';
$lang->repo->allLog = 'All Revisions';
$lang->repo->location = 'Location';
$lang->repo->file = 'File';
$lang->repo->action = 'Action';
$lang->repo->code = 'Code';
$lang->repo->review = 'Repo Review';
$lang->repo->acl = 'Privilege';
$lang->repo->group = 'Group';
$lang->repo->user = 'User';
$lang->repo->info = 'Version Info';
$lang->repo->title = 'Title';
$lang->repo->status = 'Status';
$lang->repo->openedBy = 'CreatedBy';
$lang->repo->assignedTo = 'AssignedTo';
$lang->repo->openedDate = 'CreatedDate';
$lang->repo->latestRevision = 'Latest Revision';
$lang->repo->actionInfo = "Add by %s in %s";
$lang->repo->changes = "Change Log";
$lang->repo->reviewLocation = "File: %s@%s, line:%s - %s";
$lang->repo->commentEdit = '<i class="icon-pencil"></i>';
$lang->repo->commentDelete = '<i class="icon-remove"></i>';
$lang->repo->allChanges = "Other Changes";
$lang->repo->commitTitle = "The %sth Commit";
$lang->repo->viewDiffList['inline'] = 'Inline';
$lang->repo->viewDiffList['appose'] = 'Parallel';
$lang->repo->encryptList['plain'] = 'No encryption';
$lang->repo->encryptList['base64'] = 'BASE64';
$lang->repo->logStyles['A'] = 'Add';
$lang->repo->logStyles['M'] = 'Modification';
$lang->repo->logStyles['D'] = 'Delete';
$lang->repo->encodingList['utf_8'] = 'UTF-8';
$lang->repo->encodingList['gbk'] = 'GBK';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->scmList['Git'] = 'Git';
$lang->repo->notice = new stdclass();
$lang->repo->notice->syncing = 'Synchronizing. Please wait ...';
$lang->repo->notice->syncComplete = 'Synchronized. Now redirecting ...';
$lang->repo->notice->syncedCount = 'The number of records synchronized is ';
$lang->repo->notice->delete = 'Are you sure delete this repo?';
$lang->repo->notice->successDelete = 'Repository is removed.';
$lang->repo->notice->commentContent = 'Comment';
$lang->repo->notice->deleteBug = 'Are you sure to delete this bug?';
$lang->repo->notice->deleteComment = 'Are you sure to delete this comment?';
$lang->repo->notice->lastSyncTime = 'Last Sync:';
$lang->repo->error = new stdclass();
$lang->repo->error->useless = 'Your server disabled exec and shell_exec, so it cannot be applied.';
$lang->repo->error->connect = 'Connection to the repo failed. Please enter username, password and repo address correctly!';
$lang->repo->error->version = 'Version 1.8+ of https and svn protocol is required. Please update to latest version! Go to http://subversion.apache.org/';
$lang->repo->error->path = 'Repo address is the file path, e.g. /home/test.';
$lang->repo->error->cmd = 'Client Error!';
$lang->repo->error->diff = 'Two versions must be selected.';
$lang->repo->error->product = "Please select {$lang->productCommon}!";
$lang->repo->error->commentText = 'Please enter content for review!';
$lang->repo->error->comment = 'Please enter content!';
$lang->repo->error->title = 'Please enter title!';
$lang->repo->error->accessDenied = 'You do not have the privilege to access the repository.';
$lang->repo->error->noFound = 'The repo is not found.';
$lang->repo->error->noFile = '%s does not exist.';
$lang->repo->error->noPriv = 'The program does not have the privilege to switch to %s';
$lang->repo->error->output = "The command is: %s\nThe error is(%s): %s\n";
$lang->repo->error->clientVersion = "Client version is too low, please upgrade or change SVN client";
$lang->repo->error->encoding = "The encoding maybe wrong. Please change the encoding and try again.";
$lang->repo->example = new stdclass();
$lang->repo->example->client = "For example, /usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git";
$lang->repo->example->path = "For example, SVN: http://example.googlecode.com/svn/, GIT: /home/test";
$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir.";
$lang->repo->example->encoding = "input encoding of files";
$lang->repo->typeList['standard'] = 'Standard';
$lang->repo->typeList['performance'] = 'Performance';
$lang->repo->typeList['security'] = 'Security';
$lang->repo->typeList['redundancy'] = 'Redundancy';
$lang->repo->typeList['logicError'] = 'Logic Error';
+30 -9
View File
@@ -1,11 +1,15 @@
<?php
$lang->repo->common = 'Repo';
$lang->repo->create = 'Create Repo';
$lang->repo->settings = 'Settings';
$lang->repo->browse = 'View Repo';
$lang->repo->browse = 'View';
$lang->repo->viewRevision = 'View Revision';
$lang->repo->create = 'Create';
$lang->repo->createAction = 'Create Repo';
$lang->repo->edit = 'Edit';
$lang->repo->editAction = 'Edit Repo';
$lang->repo->delete = 'Delete Repo';
$lang->repo->showSyncComment = 'Display Synchronization';
$lang->repo->ajaxSyncComment = 'Interface: Ajax Sync Note';
$lang->repo->setMatchComment = 'Set match comment';
$lang->repo->download = 'Download File';
$lang->repo->downloadDiff = 'Download Diff';
$lang->repo->diffAction = 'Revision Diff';
@@ -17,6 +21,7 @@ $lang->repo->deleteBug = 'Delete Bug';
$lang->repo->addComment = 'Add Comment';
$lang->repo->editComment = 'Edit Comment';
$lang->repo->deleteComment = 'Delete Comment';
$lang->repo->selectModule = 'Select Module';
$lang->repo->submit = 'Submit';
$lang->repo->cancel = 'Cancel';
@@ -40,6 +45,7 @@ $lang->repo->name = 'Name';
$lang->repo->path = 'Path';
$lang->repo->prefix = 'Prefix';
$lang->repo->config = 'Config';
$lang->repo->desc = 'Describe';
$lang->repo->account = 'Username';
$lang->repo->password = 'Password';
$lang->repo->encoding = 'Encoding';
@@ -90,6 +96,8 @@ $lang->repo->commentEdit = '<i class="icon-pencil"></i>';
$lang->repo->commentDelete = '<i class="icon-remove"></i>';
$lang->repo->allChanges = "Other Changes";
$lang->repo->commitTitle = "The %sth Commit";
$lang->repo->mark = "Mark Tag";
$lang->repo->split = "Split Mark";
$lang->repo->viewDiffList['inline'] = 'Inline';
$lang->repo->viewDiffList['appose'] = 'Parallel';
@@ -104,8 +112,8 @@ $lang->repo->logStyles['D'] = 'Delete';
$lang->repo->encodingList['utf_8'] = 'UTF-8';
$lang->repo->encodingList['gbk'] = 'GBK';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->scmList['Git'] = 'Git';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->notice = new stdclass();
$lang->repo->notice->syncing = 'Synchronizing. Please wait ...';
@@ -118,6 +126,12 @@ $lang->repo->notice->deleteBug = 'Are you sure to delete this bug?';
$lang->repo->notice->deleteComment = 'Are you sure to delete this comment?';
$lang->repo->notice->lastSyncTime = 'Last Sync:';
$lang->repo->matchComment = new stdclass();
$lang->repo->matchComment->exampleLabel = "Comment Example";
$lang->repo->matchComment->example['task']['start'] = "%start% %task% %id%1%split%2 %cost%%consumedmark%1 %left%%leftmark%3";
$lang->repo->matchComment->example['task']['finish'] = "%finish% %task% %id%1%split%2 %cost%%consumedmark%10";
$lang->repo->matchComment->example['bug']['resolve'] = "%resolve% %bug% %id%1%split%2 %resolvedBuild% %buildmark%10";
$lang->repo->error = new stdclass();
$lang->repo->error->useless = 'Your server disabled exec and shell_exec, so it cannot be applied.';
$lang->repo->error->connect = 'Connection to the repo failed. Please enter username, password and repo address correctly!';
@@ -137,11 +151,18 @@ $lang->repo->error->output = "The command is: %s\nThe error is(%s): %s\n"
$lang->repo->error->clientVersion = "Client version is too low, please upgrade or change SVN client";
$lang->repo->error->encoding = "The encoding maybe wrong. Please change the encoding and try again.";
$lang->repo->example = new stdclass();
$lang->repo->example->client = "For example, /usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git";
$lang->repo->example->path = "For example, SVN: http://example.googlecode.com/svn/, GIT: /home/test";
$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir.";
$lang->repo->example->encoding = "input encoding of files";
$lang->repo->synTips = '<strong>You may find the reference about how to set Git sync from <a target="_blank" href="https://www.zentao.pm/book/zentaomanual/free-open-source-project-management-software-git-105.html">here</a>.</strong>';
$lang->repo->encodingsTips = "The encodings of commit comments, can be comma separated values,e.g. utf-8";
$lang->repo->example = new stdclass();
$lang->repo->example->client = new stdclass();
$lang->repo->example->path = new stdclass();
$lang->repo->example->client->git = "e.g. /usr/bin/git";
$lang->repo->example->client->svn = "e.g. /usr/bin/svn";
$lang->repo->example->path->git = "e.g. /homt/user/myproject";
$lang->repo->example->path->svn = "e.g. http://example.googlecode.com/svn/trunk/myproject";
$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir.";
$lang->repo->example->encoding = "input encoding of files";
$lang->repo->typeList['standard'] = 'Standard';
$lang->repo->typeList['performance'] = 'Performance';
-150
View File
@@ -1,150 +0,0 @@
<?php
$lang->repo->common = 'Repo';
$lang->repo->create = 'Create Repo';
$lang->repo->settings = 'Settings';
$lang->repo->browse = 'View Repo';
$lang->repo->delete = 'Delete Repo';
$lang->repo->showSyncComment = 'Display Synchronization';
$lang->repo->ajaxSyncComment = 'Interface: Ajax Sync Note';
$lang->repo->download = 'Download File';
$lang->repo->downloadDiff = 'Download Diff';
$lang->repo->diffAction = 'Revision Diff';
$lang->repo->revisionAction = 'Revision Detail';
$lang->repo->blameAction = 'Repo Blame';
$lang->repo->addBug = 'Add Review';
$lang->repo->editBug = 'Edit Bug';
$lang->repo->deleteBug = 'Delete Bug';
$lang->repo->addComment = 'Add Comment';
$lang->repo->editComment = 'Edit Comment';
$lang->repo->deleteComment = 'Delete Comment';
$lang->repo->submit = 'Submit';
$lang->repo->cancel = 'Cancel';
$lang->repo->addComment = 'Add Comment';
$lang->repo->product = $lang->productCommon;
$lang->repo->module = 'Module';
$lang->repo->project = $lang->projectCommon;
$lang->repo->type = 'Type';
$lang->repo->assign = 'AssignTo';
$lang->repo->title = 'Title';
$lang->repo->detile = 'Detail';
$lang->repo->lines = 'Lines';
$lang->repo->line = 'Line';
$lang->repo->expand = 'Unfold';
$lang->repo->collapse = 'Fold';
$lang->repo->id = 'ID';
$lang->repo->SCM = 'Type';
$lang->repo->name = 'Name';
$lang->repo->path = 'Path';
$lang->repo->prefix = 'Prefix';
$lang->repo->config = 'Config';
$lang->repo->account = 'Username';
$lang->repo->password = 'Password';
$lang->repo->encoding = 'Encoding';
$lang->repo->client = 'Client Path';
$lang->repo->size = 'Size';
$lang->repo->revision = 'Revision';
$lang->repo->revisionA = 'Revision';
$lang->repo->revisions = 'Revision';
$lang->repo->time = 'Date';
$lang->repo->committer = 'Committer';
$lang->repo->commits = 'Commits';
$lang->repo->synced = 'Initialize Sync';
$lang->repo->lastSync = 'Last Sync';
$lang->repo->deleted = 'Deleted';
$lang->repo->commit = 'Commit';
$lang->repo->comment = 'Comment';
$lang->repo->view = 'View File';
$lang->repo->viewA = 'View';
$lang->repo->log = 'Revision Log';
$lang->repo->blame = 'Blame';
$lang->repo->date = 'Date';
$lang->repo->diff = 'Diff';
$lang->repo->diffAB = 'Diff';
$lang->repo->diffAll = 'Diff All';
$lang->repo->viewDiff = 'View diff';
$lang->repo->allLog = 'All Revisions';
$lang->repo->location = 'Location';
$lang->repo->file = 'File';
$lang->repo->action = 'Action';
$lang->repo->code = 'Code';
$lang->repo->review = 'Repo Review';
$lang->repo->acl = 'Privilege';
$lang->repo->group = 'Group';
$lang->repo->user = 'User';
$lang->repo->info = 'Version Info';
$lang->repo->title = 'Title';
$lang->repo->status = 'Status';
$lang->repo->openedBy = 'CreatedBy';
$lang->repo->assignedTo = 'AssignedTo';
$lang->repo->openedDate = 'CreatedDate';
$lang->repo->latestRevision = 'Latest Revision';
$lang->repo->actionInfo = "Add by %s in %s";
$lang->repo->changes = "Change Log";
$lang->repo->reviewLocation = "File: %s@%s, line:%s - %s";
$lang->repo->commentEdit = '<i class="icon-pencil"></i>';
$lang->repo->commentDelete = '<i class="icon-remove"></i>';
$lang->repo->allChanges = "Other Changes";
$lang->repo->commitTitle = "The %sth Commit";
$lang->repo->viewDiffList['inline'] = 'Inline';
$lang->repo->viewDiffList['appose'] = 'Parallel';
$lang->repo->encryptList['plain'] = 'No encryption';
$lang->repo->encryptList['base64'] = 'BASE64';
$lang->repo->logStyles['A'] = 'Add';
$lang->repo->logStyles['M'] = 'Modification';
$lang->repo->logStyles['D'] = 'Delete';
$lang->repo->encodingList['utf_8'] = 'UTF-8';
$lang->repo->encodingList['gbk'] = 'GBK';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->scmList['Git'] = 'Git';
$lang->repo->notice = new stdclass();
$lang->repo->notice->syncing = 'Synchronizing. Please wait ...';
$lang->repo->notice->syncComplete = 'Synchronized. Now redirecting ...';
$lang->repo->notice->syncedCount = 'The number of records synchronized is ';
$lang->repo->notice->delete = 'Are you sure delete this repo?';
$lang->repo->notice->successDelete = 'Repository is removed.';
$lang->repo->notice->commentContent = 'Comment';
$lang->repo->notice->deleteBug = 'Are you sure to delete this bug?';
$lang->repo->notice->deleteComment = 'Are you sure to delete this comment?';
$lang->repo->notice->lastSyncTime = 'Last Sync:';
$lang->repo->error = new stdclass();
$lang->repo->error->useless = 'Your server disabled exec and shell_exec, so it cannot be applied.';
$lang->repo->error->connect = 'Connection to the repo failed. Please enter username, password and repo address correctly!';
$lang->repo->error->version = 'Version 1.8+ of https and svn protocol is required. Please update to latest version! Go to http://subversion.apache.org/';
$lang->repo->error->path = 'Repo address is the file path, e.g. /home/test.';
$lang->repo->error->cmd = 'Client Error!';
$lang->repo->error->diff = 'Two versions must be selected.';
$lang->repo->error->product = "Please select {$lang->productCommon}!";
$lang->repo->error->commentText = 'Please enter content for review!';
$lang->repo->error->comment = 'Please enter content!';
$lang->repo->error->title = 'Please enter title!';
$lang->repo->error->accessDenied = 'You do not have the privilege to access the repository.';
$lang->repo->error->noFound = 'The repo is not found.';
$lang->repo->error->noFile = '%s does not exist.';
$lang->repo->error->noPriv = 'The program does not have the privilege to switch to %s';
$lang->repo->error->output = "The command is: %s\nThe error is(%s): %s\n";
$lang->repo->error->clientVersion = "Client version is too low, please upgrade or change SVN client";
$lang->repo->error->encoding = "The encoding maybe wrong. Please change the encoding and try again.";
$lang->repo->example = new stdclass();
$lang->repo->example->client = "For example, /usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git";
$lang->repo->example->path = "For example, SVN: http://example.googlecode.com/svn/, GIT: /home/test";
$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir.";
$lang->repo->example->encoding = "input encoding of files";
$lang->repo->typeList['standard'] = 'Standard';
$lang->repo->typeList['performance'] = 'Performance';
$lang->repo->typeList['security'] = 'Security';
$lang->repo->typeList['redundancy'] = 'Redundancy';
$lang->repo->typeList['logicError'] = 'Logic Error';
+31 -8
View File
@@ -1,11 +1,15 @@
<?php
$lang->repo->common = '代码';
$lang->repo->create = '创建版本库';
$lang->repo->settings = '版本库设置';
$lang->repo->browse = '浏览';
$lang->repo->viewRevision = '查看修订';
$lang->repo->create = '创建';
$lang->repo->createAction = '创建版本库';
$lang->repo->edit = '编辑';
$lang->repo->editAction = '编辑版本库';
$lang->repo->delete = '删除版本库';
$lang->repo->showSyncComment = '显示同步进度';
$lang->repo->ajaxSyncComment = '接口:AJAX同步注释';
$lang->repo->setMatchComment = '注释指令配置';
$lang->repo->download = '下载';
$lang->repo->downloadDiff = '下载Diff';
$lang->repo->diffAction = '版本对比';
@@ -17,6 +21,7 @@ $lang->repo->deleteBug = '删除评审';
$lang->repo->addComment = '添加备注';
$lang->repo->editComment = '编辑备注';
$lang->repo->deleteComment = '删除备注';
$lang->repo->selectModule = '选择模块';
$lang->repo->submit = '提交';
$lang->repo->cancel = '取消';
@@ -40,6 +45,7 @@ $lang->repo->name = '名称';
$lang->repo->path = '地址';
$lang->repo->prefix = '地址扩展';
$lang->repo->config = '配置目录';
$lang->repo->desc = '描述';
$lang->repo->account = '用户名';
$lang->repo->password = '密码';
$lang->repo->encoding = '编码';
@@ -90,6 +96,8 @@ $lang->repo->commentEdit = '<i class="icon-pencil"></i>';
$lang->repo->commentDelete = '<i class="icon-remove"></i>';
$lang->repo->allChanges = "其他改动";
$lang->repo->commitTitle = "第%s次提交";
$lang->repo->mark = "匹配标记";
$lang->repo->split = "分割匹配";
$lang->repo->viewDiffList['inline'] = '直列';
$lang->repo->viewDiffList['appose'] = '并排';
@@ -104,8 +112,8 @@ $lang->repo->logStyles['D'] = '删除';
$lang->repo->encodingList['utf_8'] = 'UTF-8';
$lang->repo->encodingList['gbk'] = 'GBK';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->scmList['Git'] = 'Git';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->notice = new stdclass();
$lang->repo->notice->syncing = '正在同步中, 请稍等...';
@@ -118,6 +126,14 @@ $lang->repo->notice->deleteBug = '确认删除该Bug?';
$lang->repo->notice->deleteComment = '确认删除该回复?';
$lang->repo->notice->lastSyncTime = '最后更新于:';
$lang->repo->matchComment = new stdclass();
$lang->repo->matchComment->exampleLabel = "注释示例";
$lang->repo->matchComment->example['story']['common'] = "%story% %id%1%split%2";
$lang->repo->matchComment->example['task']['start'] = "%start% %task% %id%1%split%2 %cost%%consumedmark%1 %left%%leftmark%3";
$lang->repo->matchComment->example['task']['finish'] = "%finish% %task% %id%1%split%2 %cost%%consumedmark%10";
$lang->repo->matchComment->example['bug']['resolve'] = "%resolve% %bug% %id%1%split%2 %resolvedBuild% %buildmark%10";
$lang->repo->matchComment->example['integration']['start'] = "%build% %integration% %id%1%split%2";
$lang->repo->error = new stdclass();
$lang->repo->error->useless = '你的服务器禁用了exec,shell_exec方法,无法使用该功能';
$lang->repo->error->connect = '连接版本库失败,请填写正确的用户名、密码和版本库地址!';
@@ -137,11 +153,18 @@ $lang->repo->error->output = "执行命令:%s\n错误结果(%s): %s\n
$lang->repo->error->clientVersion = "客户端版本过低,请升级或更换SVN客户端";
$lang->repo->error->encoding = "编码可能错误,请更换编码重试。";
$lang->repo->example = new stdclass();
$lang->repo->example->client = "例如:/usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git";
$lang->repo->example->path = "例如:SVN: http://example.googlecode.com/svn/, GIT: /homt/test";
$lang->repo->example->config = "https需要填写配置目录的位置,通过config-dir选项生成配置目录";
$lang->repo->example->encoding = "填写版本库中文件的编码";
$lang->repo->synTips = '请参照<a target="_blank" href="https://www.zentao.net/book/zentaopmshelp/207.html">这里</a>,设置版本库定时同步。';
$lang->repo->encodingsTips = "提交日志的编码,可以用逗号连接起来的多个,比如utf-8。";
$lang->repo->example = new stdclass();
$lang->repo->example->client = new stdclass();
$lang->repo->example->path = new stdclass();
$lang->repo->example->client->git = "例如:/usr/bin/git";
$lang->repo->example->client->svn = "例如:/usr/bin/svn";
$lang->repo->example->path->git = "例如:/homt/user/myproject";
$lang->repo->example->path->svn = "例如:http://example.googlecode.com/svn/trunk/myproject";
$lang->repo->example->config = "https需要填写配置目录的位置,通过config-dir选项生成配置目录";
$lang->repo->example->encoding = "填写版本库中文件的编码";
$lang->repo->typeList['standard'] = '规范';
$lang->repo->typeList['performance'] = '性能';
-150
View File
@@ -1,150 +0,0 @@
<?php
$lang->repo->common = '代碼';
$lang->repo->create = '創建版本庫';
$lang->repo->settings = '版本庫設置';
$lang->repo->browse = '瀏覽';
$lang->repo->delete = '刪除版本庫';
$lang->repo->showSyncComment = '顯示同步進度';
$lang->repo->ajaxSyncComment = '介面:AJAX同步註釋';
$lang->repo->download = '下載';
$lang->repo->downloadDiff = '下載Diff';
$lang->repo->diffAction = '版本對比';
$lang->repo->revisionAction = '版本詳情';
$lang->repo->blameAction = '版本追溯';
$lang->repo->addBug = '添加評審';
$lang->repo->editBug = '編輯評審';
$lang->repo->deleteBug = '刪除評審';
$lang->repo->addComment = '添加備註';
$lang->repo->editComment = '編輯備註';
$lang->repo->deleteComment = '刪除備註';
$lang->repo->submit = '提交';
$lang->repo->cancel = '取消';
$lang->repo->addComment = '添加評論';
$lang->repo->product = $lang->productCommon;
$lang->repo->module = '模組';
$lang->repo->project = $lang->projectCommon;
$lang->repo->type = '類型';
$lang->repo->assign = '指派';
$lang->repo->title = '標題';
$lang->repo->detile = '詳情';
$lang->repo->lines = '代碼行';
$lang->repo->line = '行';
$lang->repo->expand = '點擊展開';
$lang->repo->collapse = '點擊摺疊';
$lang->repo->id = '編號';
$lang->repo->SCM = '類型';
$lang->repo->name = '名稱';
$lang->repo->path = '地址';
$lang->repo->prefix = '地址擴展';
$lang->repo->config = '配置目錄';
$lang->repo->account = '用戶名';
$lang->repo->password = '密碼';
$lang->repo->encoding = '編碼';
$lang->repo->client = '客戶端';
$lang->repo->size = '大小';
$lang->repo->revision = '查看版本';
$lang->repo->revisionA = '版本';
$lang->repo->revisions = '版本';
$lang->repo->time = '提交時間';
$lang->repo->committer = '作者';
$lang->repo->commits = '提交數';
$lang->repo->synced = '初始化同步';
$lang->repo->lastSync = '最後同步時間';
$lang->repo->deleted = '已刪除';
$lang->repo->commit = '提交';
$lang->repo->comment = '註釋';
$lang->repo->view = '查看檔案';
$lang->repo->viewA = '查看';
$lang->repo->log = '版本歷史';
$lang->repo->blame = '追溯';
$lang->repo->date = '日期';
$lang->repo->diff = '比較差異';
$lang->repo->diffAB = '比較';
$lang->repo->diffAll = '全部比較';
$lang->repo->viewDiff = '查看差異';
$lang->repo->allLog = '所有版本';
$lang->repo->location = '位置';
$lang->repo->file = '檔案';
$lang->repo->action = '操作';
$lang->repo->code = '代碼';
$lang->repo->review = '評審';
$lang->repo->acl = '權限';
$lang->repo->group = '分組';
$lang->repo->user = '用戶';
$lang->repo->info = '版本信息';
$lang->repo->title = '標題';
$lang->repo->status = '狀態';
$lang->repo->openedBy = '創建者';
$lang->repo->assignedTo = '指派給';
$lang->repo->openedDate = '創建日期';
$lang->repo->latestRevision = '最近修訂版本';
$lang->repo->actionInfo = "由%s在%s添加";
$lang->repo->changes = "修改記錄";
$lang->repo->reviewLocation = "%s@%s,%s行 - %s行";
$lang->repo->commentEdit = '<i class="icon-pencil"></i>';
$lang->repo->commentDelete = '<i class="icon-remove"></i>';
$lang->repo->allChanges = "其他改動";
$lang->repo->commitTitle = "第%s次提交";
$lang->repo->viewDiffList['inline'] = '直列';
$lang->repo->viewDiffList['appose'] = '並排';
$lang->repo->encryptList['plain'] = '不加密';
$lang->repo->encryptList['base64'] = 'BASE64';
$lang->repo->logStyles['A'] = '添加';
$lang->repo->logStyles['M'] = '修改';
$lang->repo->logStyles['D'] = '刪除';
$lang->repo->encodingList['utf_8'] = 'UTF-8';
$lang->repo->encodingList['gbk'] = 'GBK';
$lang->repo->scmList['Subversion'] = 'Subversion';
$lang->repo->scmList['Git'] = 'Git';
$lang->repo->notice = new stdclass();
$lang->repo->notice->syncing = '正在同步中, 請稍等...';
$lang->repo->notice->syncComplete = '同步完成,正在跳轉...';
$lang->repo->notice->syncedCount = '已經同步記錄條數';
$lang->repo->notice->delete = '是否要刪除該版本庫?';
$lang->repo->notice->successDelete = '已經成功刪除版本庫。';
$lang->repo->notice->commentContent = '輸入回覆內容';
$lang->repo->notice->deleteBug = '確認刪除該Bug?';
$lang->repo->notice->deleteComment = '確認刪除該回覆?';
$lang->repo->notice->lastSyncTime = '最後更新于:';
$lang->repo->error = new stdclass();
$lang->repo->error->useless = '你的伺服器禁用了exec,shell_exec方法,無法使用該功能';
$lang->repo->error->connect = '連接版本庫失敗,請填寫正確的用戶名、密碼和版本庫地址!';
$lang->repo->error->version = "https和svn協議需要1.8及以上版本的客戶端,請升級到最新版本!詳情訪問:http://subversion.apache.org/";
$lang->repo->error->path = '版本庫地址直接填寫檔案路徑,如:/home/test。';
$lang->repo->error->cmd = '客戶端錯誤!';
$lang->repo->error->diff = '必須選擇兩個版本';
$lang->repo->error->product = "請選擇{$lang->productCommon}!";
$lang->repo->error->commentText = '請填寫評審內容';
$lang->repo->error->comment = '請填寫內容';
$lang->repo->error->title = '請填寫標題';
$lang->repo->error->accessDenied = '你沒有權限訪問該版本庫';
$lang->repo->error->noFound = '你訪問的版本庫不存在';
$lang->repo->error->noFile = '目錄 %s 不存在';
$lang->repo->error->noPriv = '程序沒有權限切換到目錄 %s';
$lang->repo->error->output = "執行命令:%s\n錯誤結果(%s): %s\n";
$lang->repo->error->clientVersion = "客戶端版本過低,請升級或更換SVN客戶端";
$lang->repo->error->encoding = "編碼可能錯誤,請更換編碼重試。";
$lang->repo->example = new stdclass();
$lang->repo->example->client = "例如:/usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git";
$lang->repo->example->path = "例如:SVN: http://example.googlecode.com/svn/, GIT: /homt/test";
$lang->repo->example->config = "https需要填寫配置目錄的位置,通過config-dir選項生成配置目錄";
$lang->repo->example->encoding = "填寫版本庫中檔案的編碼";
$lang->repo->typeList['standard'] = '規範';
$lang->repo->typeList['performance'] = '性能';
$lang->repo->typeList['security'] = '安全';
$lang->repo->typeList['redundancy'] = '冗餘';
$lang->repo->typeList['logicError'] = '邏輯錯誤';
+292 -76
View File
@@ -32,7 +32,7 @@ class repoModel extends model
* @access public
* @return void
*/
public function setMenu($repos, $repoID = '')
public function setMenu($repos, $repoID = '', $showRepoSeletion = true)
{
if(empty($repoID)) $repoID = $this->session->repoID ? $this->session->repoID : key($repos);
if(!isset($repos[$repoID])) $repoID = key($repos);
@@ -54,7 +54,7 @@ class repoModel extends model
}
}
if(!empty($repos))
if($showRepoSeletion && !empty($repos))
{
$repoIndex = '<div class="btn-group angle-btn"><div class="btn-group"><button data-toggle="dropdown" type="button" class="btn">' . ($repo->SCM == 'Subversion' ? '[SVN] ' : '[GIT] ') . $repo->name . ' <span class="caret"></span></button>';
$repoIndex .= $this->select($repos, $repoID);
@@ -131,14 +131,21 @@ class repoModel extends model
}
/**
* Get all repos.
*
* Get repo list.
*
* @param string $orderBy
* @param object $pager
* @param bool $decode
* @access public
* @return array
*/
public function getAllRepos()
public function getList($orderBy = 'id_desc', $pager = null)
{
$repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq(0)->fetchAll();
$repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq('0')
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
foreach($repos as $i => $repo)
{
$repo->acl = json_decode($repo->acl);
@@ -148,6 +155,109 @@ class repoModel extends model
return $repos;
}
/**
* Get list by SCM.
*
* @param strint $scm
* @param string $type
* @access public
* @return array
*/
public function getListBySCM($scm, $type = 'all')
{
$repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq('0')
->andWhere('SCM')->eq($scm)
->orderBy('id')
->fetchAll();
foreach($repos as $i => $repo)
{
if($repo->encrypt == 'base64') $repo->password = base64_decode($repo->password);
$repo->acl = json_decode($repo->acl);
if($type == 'haspriv' and !$this->checkPriv($repo)) unset($repos[$i]);
}
return $repos;
}
/**
* Create a repo.
*
* @access public
* @return bool
*/
public function create()
{
$this->checkConnection();
$data = fixer::input('post')->skipSpecial('path,client,account,password')->get();
$data->acl = empty($data->acl) ? '' : json_encode($data->acl);
if(empty($data->client)) $data->client = 'svn';
if($data->SCM == 'Subversion')
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($data);
$info = $scm->info('');
$data->prefix = empty($info->root) ? '' : trim(str_ireplace($info->root, '', str_replace('\\', '/', $data->path)), '/');
if($data->prefix) $data->prefix = '/' . $data->prefix;
}
if($data->encrypt == 'base64') $data->password = base64_encode($data->password);
$this->dao->insert(TABLE_REPO)->data($data)
->batchCheck($this->config->repo->create->requiredFields, 'notempty')
->checkIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty')
->autoCheck()
->exec();
return $this->dao->lastInsertID();
}
/**
* Update a repo.
*
* @param int $id
* @access public
* @return bool
*/
public function update($id)
{
$this->checkConnection();
$data = fixer::input('post')->skipSpecial('path,client,account,password')->get();
$data->acl = empty($data->acl) ? '' : json_encode($data->acl);
if(empty($data->client)) $data->client = 'svn';
$repo = $this->getRepoByID($id);
$data->prefix = $repo->prefix;
if($data->SCM == 'Subversion' and $data->path != $repo->path)
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($data);
$info = $scm->info('');
$data->prefix = empty($info->root) ? '' : trim(str_ireplace($info->root, '', str_replace('\\', '/', $data->path)), '/');
if($data->prefix) $data->prefix = '/' . $data->prefix;
}
elseif($data->SCM != $repo->SCM and $data->SCM == 'Git')
{
$data->prefix = '';
}
if($data->path != $repo->path) $data->synced = 0;
if($data->encrypt == 'base64') $data->password = base64_encode($data->password);
$this->dao->update(TABLE_REPO)->data($data)
->batchCheck($this->config->repo->edit->requiredFields, 'notempty')
->checkIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty')
->autoCheck()
->where('id')->eq($id)->exec();
if($repo->path != $data->path)
{
$this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($id)->exec();
$this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($id)->exec();
return false;
}
return true;
}
/**
* Get repo pairs.
*
@@ -185,6 +295,25 @@ class repoModel extends model
return $repo;
}
/**
* Get by id list.
*
* @param array $idList
* @access public
* @return array
*/
public function getByIdList($idList)
{
$repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq(0)->andWhere('id')->in($idList)->fetchAll();
foreach($repos as $i => $repo)
{
if($repo->encrypt == 'base64') $repo->password = base64_decode($repo->password);
$repo->acl = json_decode($repo->acl);
}
return $repos;
}
/**
* Get git branches.
*
@@ -296,6 +425,36 @@ class repoModel extends model
return $lastComment;
}
/**
* Get revisions from db.
*
* @param int $repoID
* @param string $limit
* @param string $maxRevision
* @param string $minRevision
* @access public
* @return array
*/
public function getRevisionsFromDB($repoID, $limit = '', $maxRevision = '', $minRevision = '')
{
$revisions = $this->dao->select('DISTINCT t1.*')->from(TABLE_REPOHISTORY)->alias('t1')
->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision')
->where('t1.repo')->eq($repoID)
->beginIF(!empty($maxRevision))->andWhere('t1.revision')->le($maxRevision)->fi()
->beginIF(!empty($minRevision))->andWhere('t1.revision')->ge($minRevision)->fi()
->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi()
->orderBy('t1.revision desc')
->beginIF(!empty($limit))->limit($limit)->fi()
->fetchAll('revision');
$commiters = $this->loadModel('user')->getCommiters();
foreach($revisions as $revision)
{
$revision->comment = $this->replaceCommentLink($revision->comment);
$revision->committer = isset($commiters[$revision->committer]) ? $commiters[$revision->committer] : $revision->committer;
}
return $revisions;
}
/**
* Get git revisionName.
*
@@ -310,75 +469,6 @@ class repoModel extends model
return substr($revision, 0, 10) . '<span title="' . sprintf($this->lang->repo->commitTitle, $commit) . '"> (' . $commit . ') </span>';
}
/**
* create
*
* @access public
* @return int
*/
public function create()
{
$this->checkConnection();
$data = fixer::input('post')->skipSpecial('path,client,account,password')->get();
$data->acl = empty($data->acl) ? '' : json_encode($data->acl);
if(empty($data->client)) $data->client = 'svn';
if($data->SCM == 'Subversion')
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($data);
$info = $scm->info('');
$data->prefix = empty($info->root) ? '' : trim(str_ireplace($info->root, '', str_replace('\\', '/', $data->path)), '/');
if($data->prefix) $data->prefix = '/' . $data->prefix;
}
if($data->encrypt == 'base64') $data->password = base64_encode($data->password);
$this->dao->insert(TABLE_REPO)->data($data)->exec();
return $this->dao->lastInsertID();
}
/**
* Save settings.
*
* @param int $repoID
* @access public
* @return bool
*/
public function saveSettings($repoID)
{
$this->checkConnection();
$data = fixer::input('post')->skipSpecial('path,client,account,password')->get();
$data->acl = empty($data->acl) ? '' : json_encode($data->acl);
if(empty($data->client)) $data->client = 'svn';
$repo = $this->getRepoByID($repoID);
$data->prefix = $repo->prefix;
if($data->SCM == 'Subversion' and $data->path != $repo->path)
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($data);
$info = $scm->info('');
$data->prefix = empty($info->root) ? '' : trim(str_ireplace($info->root, '', str_replace('\\', '/', $data->path)), '/');
if($data->prefix) $data->prefix = '/' . $data->prefix;
}
elseif($data->SCM != $repo->SCM and $data->SCM == 'Git')
{
$data->prefix = '';
}
if($data->path != $repo->path) $data->synced = 0;
if($data->encrypt == 'base64') $data->password = base64_encode($data->password);
$this->dao->update(TABLE_REPO)->data($data)->where('id')->eq($repoID)->exec();
if($repo->path != $data->path)
{
$this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->exec();
$this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($repoID)->exec();
$this->dao->delete()->from(TABLE_REPOBRANCH)->where('repo')->eq($repoID)->exec();
return false;
}
return true;
}
/**
* Save commit.
*
@@ -676,6 +766,7 @@ class repoModel extends model
*/
public function encodePath($path = '')
{
if(empty($path)) return $path;
return helper::safe64Encode(urlencode($path));
}
@@ -688,6 +779,7 @@ class repoModel extends model
*/
public function decodePath($path = '')
{
if(empty($path)) return $path;
return trim(urldecode(helper::safe64Decode($path)), '/');
}
@@ -801,7 +893,7 @@ class repoModel extends model
$stories = array();
$tasks = array();
$bugs = array();
$commonReg = "(?:\s){0,}((?:#|:|:){0,})([0-9, ]{1,})";
$commonReg = "(?:\s){0,}((?:#|:|��){0,})([0-9, ]{1,})";
$taskReg = '/task' . $commonReg . '/i';
$storyReg = '/story' . $commonReg . '/i';
$bugReg = '/bug' . $commonReg . '/i';
@@ -848,4 +940,128 @@ class repoModel extends model
}
return $replaceLines;
}
/**
* Parse the comment of git, extract object id list from it.
*
* @param string $comment
* @access public
* @return array
*/
public function parseComment($comment)
{
if(is_string($this->config->repo->matchComment)) $this->config->repo->matchComment = json_decode($this->config->repo->matchComment, true);
$matchComment = $this->config->repo->matchComment;
$matches = array();
$stories = array();
$tasks = array();
$bugs = array();
$integrations = array();
$actions = array();
while(true)
{
$found = preg_match("/{$matchComment['id']['mark']}[0-9]+({$matchComment['id']['split']}[0-9]+)*/", $comment, $matches);
if(empty($found)) break;
if($found)
{
$position = strpos($comment, $matches[0]);
$subComment = substr($comment, 0, $position + strlen($matches[0]));
$comment = substr($comment, $position + strlen($matches[0]) + 1);
$idList = explode($matchComment['id']['mark'], str_replace($matchComment['id']['split'], '', $matches[0]));
foreach($matchComment['module'] as $module => $moduleMatch)
{
if(stripos($subComment, $moduleMatch) !== false)
{
if($module == 'story')
{
foreach($idList as $id) $stories[$id] = $id;
}
elseif($module == 'task')
{
foreach($idList as $id) $tasks[$id] = $id;
foreach($matchComment['task'] as $method => $match)
{
if(strpos($subComment, $match) !== false)
{
$consumed = 0;
preg_match("/{$matchComment['task']['consumed']}{$matchComment['mark']['consumed']}([0-9]+)/", $comment, $costMatch);
if($costMatch) $consumed = $costMatch[1];
$left = 0;
preg_match("/{$matchComment['task']['left']}{$matchComment['mark']['left']}([0-9]+)/", $comment, $leftMatch);
if($leftMatch) $left = $leftMatch[1];
foreach($idList as $id)
{
$actions['task'][$id]['action'] = $method;
$actions['task'][$id]['consumed'] = $consumed;
$actions['task'][$id]['left'] = $left;
}
}
}
}
elseif($module == 'bug')
{
foreach($idList as $id) $bugs[$id] = $id;
foreach($matchComment['bug'] as $method => $match)
{
if(strpos($subComment, $match) !== false)
{
$buildID = 0;
preg_match("/{$matchComment['bug']['resolvedBuild']}{$matchComment['mark']['resolvedBuild']}([0-9]+)/", $comment, $buildMatch);
if($buildMatch) $buildID = $buildMatch[1];
foreach($idList as $id)
{
$actions['bug'][$id]['action'] = $method;
$actions['bug'][$id]['resolvedBuild'] = $buildID;
}
}
}
}
elseif($module == 'integration')
{
foreach($idList as $id) $integrations[$id] = $id;
foreach($matchComment['integration'] as $method => $match)
{
if(strpos($subComment, $match) !== false)
{
foreach($idList as $id) $actions['integration'][$id]['action'] = $method;
}
}
}
}
}
}
}
return array('stories' => $stories, 'tasks' => $tasks, 'bugs' => $bugs, 'integrations' => $integrations, 'actions' => $actions);
}
/**
* Iconv Comment.
*
* @param string $comment
* @param string $encodings
* @access public
* @return string
*/
public function iconvComment($comment, $encodings)
{
/* Get encodings. */
if($encodings == '') return $comment;
$encodings = explode(',', $encodings);
/* Try convert. */
foreach($encodings as $encoding)
{
if($encoding == 'utf-8') continue;
$result = helper::convertEncoding($comment, $encoding);
if($result) return $result;
}
return $comment;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
/**
* The create view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @package repo
* @version $Id: blame.html.php $
*/
?>
<?php
include '../../common/view/header.html.php';
js::import($jsRoot . 'misc/highlight/highlight.pack.js');
css::import($jsRoot . 'misc/highlight/styles/github.css');
?>
<?php if(!isonlybody()):?>
<div id='mainMenu' class='clearfix'>
<div class="btn-toolbar pull-left">
<?php
$backURI = $this->session->repoView ? $this->session->repoView : $this->session->repoList;
if($backURI)
{
echo html::a($backURI, "<i class='icon icon-back icon-sm'></i>" . $lang->goback, '', "class='btn btn-link'");
}
else
{
echo html::backButton("<i class='icon icon-back icon-sm'></i>" . $lang->goback, '', 'btn btn-link');
}
?>
<div class="divider"></div>
<div class="page-title">
<strong>
<?php
echo html::a($this->repo->createLink('browse', "repoID=$repoID"), $repo->name);
$paths= explode('/', $entry);
$fileName = array_pop($paths);
$postPath = '';
foreach($paths as $pathName)
{
$postPath .= $pathName . '/';
echo '/' . ' ' . html::a($this->repo->createLink('browse', "repoID=$repoID", "path=" . $this->repo->encodePath($postPath)), trim($pathName, '/'));
}
echo '/' . ' ' . $fileName;
echo " <span class='label label-info'>" . $revisionName . '</span>';
?>
</strong>
</div>
</div>
</div>
<?php endif;?>
<div class="code panel">
<div class='panel-heading'>
<div class='panel-title'><?php echo $entry;?></div>
<?php $encodePath = $this->repo->encodePath($entry);?>
<div class='panel-actions'>
<div class='btn-group'>
<?php echo html::commonButton(zget($lang->repo->encodingList, $encoding, $lang->repo->encoding) . "<span class='caret'></span>", "id='encoding' data-toggle='dropdown'", 'btn dropdown-toggle')?>
<ul class='dropdown-menu' role='menu' aria-labelledby='encoding'>
<?php foreach($lang->repo->encodingList as $key => $val):?>
<li><?php echo html::a($this->repo->createLink('blame', "repoID=$repoID&entry=&revision=$revision&encoding=$key", "entry=$encodePath"), $val)?></li>
<?php endforeach;?>
</ul>
</div>
</div>
</div>
<div class="content">
<table class="blame table table-form table-fixed">
<thead>
<tr>
<td class='w-70px'><?php echo $lang->repo->revision?></td>
<?php if($repo->SCM == 'Git'):?>
<td class='w-50px'><?php echo $lang->repo->commit?></td>
<?php endif;?>
<td class='w-100px'><?php echo $lang->repo->committer?></td>
<td class="w-40px"><?php echo $lang->repo->line?></td>
<td><?php echo $lang->repo->code?></td>
</tr>
</thead>
<tbody>
<?php foreach($blames as $blame):?>
<tr<?php if(isset($blame['lines'])) echo " class='topLine'";?>>
<?php
if(isset($blame['lines']))
{
$rowspan = $blame['lines'];
echo '<td rowspan="' . $rowspan . '" class="info" title="' . $blame['revision'] . '">';
echo $repo->SCM == 'Git' ? substr($blame['revision'], 0, 10) : $blame['revision'];
echo '</td>';
if($repo->SCM == 'Git') echo '<td rowspan="' . $rowspan . '" class="info">' . zget($historys, $blame['revision'], '') . '</td>';
echo '<td rowspan="' . $rowspan . '" class="info">' . $blame['committer'] . '</td>';
}
?>
<td class="line"><?php echo $blame['line'];?></td>
<td><pre><?php echo htmlspecialchars($blame['content']);?></pre></td>
</tr>
<?php endforeach?>
</tbody>
</table>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+78 -5
View File
@@ -9,15 +9,88 @@
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php if(common::checkNotCN()):?>
<style>
.user-addon{padding-right: 16px; padding-left: 16px;}
</style>
<?php endif;?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->repo->create;?></h2>
<?php js::set('scm', 'Git')?>
<div id='mainContent' class='main-row'>
<div class='main-col main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->repo->create; ?></h2>
</div>
<form id='repoForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th class='thWidth'><?php echo $lang->repo->type; ?></th>
<td style="width:550px"><?php echo html::select('SCM', $lang->repo->scmList, 'Git', "onchange='scmChanged(this.value)' class='form-control'"); ?></td>
<td class="tips-git"><?php echo $lang->repo->synTips; ?></td>
</tr>
<tr>
<th><?php echo $lang->repo->name; ?></th>
<td class='required'><?php echo html::input('name', '', "class='form-control'"); ?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->repo->path; ?></th>
<td class='required'><?php echo html::input('path', '', "class='form-control'"); ?></td>
<td class='muted'>
<span class="tips-git"><?php echo $lang->repo->example->path->git;?></span>
<span class="tips-svn"><?php echo $lang->repo->example->path->svn;?></span>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->encoding; ?></th>
<td class='required'><?php echo html::input('encoding', 'utf-8', "class='form-control'"); ?></td>
<td class='muted'><?php echo $lang->repo->encodingsTips; ?></td>
</tr>
<tr>
<th><?php echo $lang->repo->client;?></th>
<td class='required'><?php echo html::input('client', '', "class='form-control'")?></td>
<td class='muted'>
<span class="tips-git"><?php echo $lang->repo->example->client->git;?></span>
<span class="tips-svn"><?php echo $lang->repo->example->client->svn;?></span>
</td>
</tr>
<tr class="account-fields">
<th><?php echo $lang->repo->account;?></th>
<td><?php echo html::input('account', '', "class='form-control'");?></td>
</tr>
<tr class="account-fields">
<th><?php echo $lang->repo->password;?></th>
<td>
<?php echo html::password('password', '', "class='form-control'");?>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->acl;?></th>
<td class='acl'>
<div class='input-group mgb-10'>
<span class='input-group-addon'><?php echo $lang->repo->group?></span>
<?php echo html::select('acl[groups][]', $groups, '', "class='form-control chosen' multiple")?>
</div>
<div class='input-group'>
<span class='input-group-addon user-addon'><?php echo $lang->repo->user?></span>
<?php echo html::select('acl[users][]', $users, '', "class='form-control chosen' multiple")?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->desc; ?></th>
<td colspan='2'><?php echo html::textarea('desc', '', "rows='3' class='form-control'"); ?></td>
</tr>
<tr>
<th></th>
<td class='text-center form-actions'>
<?php echo html::submitButton(); ?>
<?php echo html::backButton(); ?>
</td>
</tr>
</table>
</form>
</div>
<form class='form-indicator main-form' method='post' target='hiddenwin' id='dataform'>
<table class='table table-form'>
@@ -80,4 +153,4 @@
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
<?php include '../../common/view/footer.html.php'; ?>
+102
View File
@@ -0,0 +1,102 @@
<?php
/**
* The edit view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package repo
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/kindeditor.html.php';?>
<?php if(common::checkNotCN()):?>
<style>
.user-addon{padding-right: 16px; padding-left: 16px;}
</style>
<?php endif;?>
<?php js::set('scm', $repo->SCM)?>
<div id='mainContent' class='main-row'>
<div class='main-col main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->repo->edit; ?></h2>
</div>
<form id='repoForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th class='thWidth'><?php echo $lang->repo->type; ?></th>
<td style="width:550px"><?php echo html::select('SCM', $lang->repo->scmList, $repo->SCM, "onchange='scmChanged(this.value)' class='form-control'"); ?></td>
<td>
<span class="tips-git"><?php echo $lang->repo->synTips; ?></span>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->name; ?></th>
<td class='required'><?php echo html::input('name', $repo->name, "class='form-control'"); ?></td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->repo->path; ?></th>
<td class='required'><?php echo html::input('path', $repo->path, "class='form-control'"); ?></td>
<td class='muted'>
<span class="tips-git"><?php echo $lang->repo->example->path->git;?></span>
<span class="tips-svn"><?php echo $lang->repo->example->path->svn;?></span>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->encoding; ?></th>
<td class='required'><?php echo html::input('encoding', $repo->encoding, "class='form-control'"); ?></td>
<td class='muted'><?php echo $lang->repo->encodingsTips; ?></td>
</tr>
<tr>
<th><?php echo $lang->repo->client;?></th>
<td class='required'><?php echo html::input('client', $repo->client, "class='form-control'")?></td>
<td class='muted'>
<span class="tips-git"><?php echo $lang->repo->example->client->git;?></span>
<span class="tips-svn"><?php echo $lang->repo->example->client->svn;?></span>
</td>
</tr>
<tr class="account-fields">
<th><?php echo $lang->repo->account;?></th>
<td><?php echo html::input('account', $repo->account, "class='form-control'");?></td>
</tr>
<tr class="account-fields">
<th><?php echo $lang->repo->password;?></th>
<td>
<?php echo html::password('password', $repo->password, "class='form-control'");?>
</td>
<td></td>
</tr>
<tr>
<th><?php echo $lang->repo->acl;?></th>
<td>
<div class='input-group mgb-10'>
<span class='input-group-addon'><?php echo $lang->repo->group?></span>
<?php echo html::select('acl[groups][]', $groups, empty($repo->acl->groups) ? '' : join(',', $repo->acl->groups), "class='form-control chosen' multiple")?>
</div>
<div class='input-group'>
<span class='input-group-addon user-addon'><?php echo $lang->repo->user?></span>
<?php echo html::select('acl[users][]', $users, empty($repo->acl->users) ? '' : join(',', $repo->acl->users), "class='form-control chosen' multiple")?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->desc; ?></th>
<td colspan='2'><?php echo html::textarea('desc', $repo->desc, "rows='3' class='form-control'"); ?></td>
</tr>
<tr>
<th></th>
<td class='text-center form-actions'>
<?php echo html::submitButton(); ?>
<?php echo html::backButton() ?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+49
View File
@@ -0,0 +1,49 @@
<?php
/**
* The browse view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2017 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Gang Liu <liugang@cnezsoft.com>
* @package repo
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent'>
<form class='main-table' id='ajaxForm' method='post'>
<table id='repoList' class='table has-sort-head table-fixed'>
<thead>
<tr>
<?php $vars = "orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"; ?>
<th class='w-60px'><?php common::printOrderLink('id', $orderBy, $vars, $lang->repo->id); ?></th>
<th class='w-120px'><?php common::printOrderLink('SCM', $orderBy, $vars, $lang->repo->type); ?></th>
<th class='w-200px text-left'><?php common::printOrderLink('name', $orderBy, $vars, $lang->repo->name); ?></th>
<th class='text-left'><?php echo $lang->repo->path; ?></th>
<th class='w-100px c-actions-4'><?php echo $lang->actions; ?></th>
</tr>
</thead>
<tbody>
<?php foreach($repoList as $id => $repo):?>
<tr>
<td class='text-center'><?php echo $id; ?></td>
<td class='text'><?php echo zget($lang->repo->scmList, $repo->SCM); ?></td>
<td class='text' title='<?php echo $repo->name; ?>'><?php echo $repo->name; ?></td>
<td class='text' title='<?php echo $repo->path; ?>'><?php echo $repo->path; ?></td>
<td class='text-left c-actions'>
<?php
common::printIcon('repo', 'edit', "repoID=$id", '', 'list', 'edit');
if(common::hasPriv('repo', 'delete')) echo html::a($this->createLink('repo', 'delete', "repoID=$id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->repo->delete}' class='btn'");
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<?php if($repoList):?>
<div class='table-footer'><?php $pager->show('rignt', 'pagerjs');?></div>
<?php endif;?>
</form>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+175
View File
@@ -0,0 +1,175 @@
<?php
/**
* The setMatchComment view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Yidong Wang <yidong@cnezsoft.com>
* @package repo
* @version $Id$
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<div class="main-header">
<h2><?php echo $lang->repo->setMatchComment;?></h2>
</div>
<form class='main-form form-ajax' method='post'>
<table class='table table-form'>
<tbody>
<tr>
<th class='w-100px'><?php echo $lang->repo->selectModule;?></th>
<td class='w-200px'>
<?php
foreach($config->repo->matchComment['module'] as $module => $match) $modules[$module] = $lang->{$module}->common;
echo html::select('selectModule', $modules, $selectModule, "class='form-control chosen'");
?>
</td>
<td></td>
</tr>
<tr>
<th class='w-100px'><?php echo $lang->repo->module;?></th>
<td>
<?php foreach($config->repo->matchComment['module'] as $module => $match):?>
<div class='input-group'>
<span class='input-group-addon hidden <?php echo $module . 'Item';?>'><?php echo $lang->{$module}->common;?></span>
<?php echo html::input("matchComment[module][{$module}]", $match, "class='form-control hidden {$module}Item'");?>
</div>
<?php endforeach;?>
</td>
</tr>
<tr class='taskItem hidden'>
<th><?php echo $lang->task->common;?></th>
<td colspan='2'>
<div class='input-group'>
<?php foreach($config->repo->matchComment['task'] as $method => $match):?>
<span class='input-group-addon'><?php echo $lang->task->$method;?></span>
<?php echo html::input("matchComment[task][{$method}]", $match, "class='form-control'");?>
<?php endforeach;?>
</div>
</td>
</tr>
<tr class='bugItem hidden'>
<th><?php echo $lang->bug->common;?></th>
<td colspan='2'>
<div class='input-group'>
<?php foreach($config->repo->matchComment['bug'] as $method => $match):?>
<span class='input-group-addon'><?php echo $lang->bug->$method;?></span>
<?php echo html::input("matchComment[bug][{$method}]", $match, "class='form-control'");?>
<?php endforeach;?>
</div>
</td>
</tr>
<tr class='integrationItem hidden'>
<th><?php echo $lang->integration->common;?></th>
<td>
<div class='input-group'>
<?php foreach($config->repo->matchComment['integration'] as $method => $match):?>
<span class='input-group-addon'><?php echo $lang->integration->$method;?></span>
<?php echo html::input("matchComment[integration][{$method}]", $match, "class='form-control'");?>
<?php endforeach;?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->idAB;?></th>
<td colspan='2'>
<div class='input-group'>
<?php foreach($config->repo->matchComment['id'] as $method => $match):?>
<span class='input-group-addon'><?php echo $lang->repo->$method;?></span>
<?php echo html::input("matchComment[id][{$method}]", $match, "class='form-control'");?>
<?php endforeach;?>
</div>
</td>
</tr>
<tr class='taskItem bugItem hidden'>
<th><?php echo $lang->repo->mark;?></th>
<td colspan='2'>
<div class='input-group'>
<?php foreach($config->repo->matchComment['mark'] as $method => $match):?>
<?php $module = isset($lang->task->$method) ? 'task' : 'bug';?>
<span class='input-group-addon hidden <?php echo $module . 'Item';?>'><?php echo $lang->{$module}->$method?></span>
<?php echo html::input("matchComment[mark][{$method}]", $match, "class='form-control hidden {$module}Item'");?>
<?php endforeach;?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->matchComment->exampleLabel;?></th>
<td colspan='2' id='example'></td>
</tr>
<tr>
<td colspan='3' class='text-center'>
<?php echo html::submitButton();?>
<?php echo html::backButton();?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
<?php js::set('matchCommentExample', $lang->repo->matchComment->example);?>
<script>
$(function()
{
$('#selectModule').change()
$('input').keyup(function(){replaceExample()});
})
$('#selectModule').change(function()
{
var module = $(this).val();
$('[class*=Item]').addClass('hidden');
$('.' + module + 'Item').removeClass('hidden');
replaceExample();
})
function replaceExample()
{
var module = $('#selectModule').val();
var html = '';
if(module == 'story')
{
html = matchCommentExample['story']['common'].replace('%story%', $('[id*=module][id*=story]').val())
.replace('%id%', $('[id*=id][id*=mark]').val())
.replace('%split%', $('[id*=id][id*=split]').val());
}
else if(module == 'task')
{
html = matchCommentExample['task']['start'].replace('%start%', $('[id*=start').val())
.replace('%task%', $('[id*=module][id*=task]').val())
.replace('%id%', $('[id*=id][id*=mark]').val())
.replace('%split%', $('[id*=id][id*=split]').val())
.replace('%cost%', $('[id*=task][id*=consumed]').val())
.replace('%consumedmark%', $('[id*=mark][id*=consumed]').val())
.replace('%left%', $('[id*=task][id*=left]').val())
.replace('%leftmark%', $('[id*=mark][id*=left]').val());
html += '<br />' + matchCommentExample['task']['finish'].replace('%finish%', $('[id*=finish]').val())
.replace('%task%', $('[id*=module][id*=task]').val())
.replace('%id%', $('[id*=id][id*=mark]').val())
.replace('%split%', $('[id*=id][id*=split]').val())
.replace('%cost%', $('[id*=task][id*=consumed]').val())
.replace('%consumedmark%', $('[id*=mark][id*=consumed]').val());
}
else if(module == 'bug')
{
html = matchCommentExample['bug']['resolve'].replace('%resolve%', $('[id*=bug][id*="resolve\]"]').val())
.replace('%bug%', $('[id*=module][id*=bug]').val())
.replace('%id%', $('[id*=id][id*=mark]').val())
.replace('%split%', $('[id*=id][id*=split]').val())
.replace('%resolvedBuild%', $('[id*=bug][id*=resolvedBuild]').val())
.replace('%buildmark%', $('[id*=mark][id*=resolvedBuild]').val());
}
else if(module == 'integration')
{
html = matchCommentExample['integration']['start'].replace('%build%', $('[id*=integration][id*=start]').val())
.replace('%integration%', $('[id*=module][id*=integration]').val())
.replace('%id%', $('[id*=id][id*=mark]').val())
.replace('%split%', $('[id*=id][id*=split]').val())
}
$('#example').html(html);
}
</script>
<?php include '../../common/view/footer.html.php';?>
-87
View File
@@ -1,87 +0,0 @@
<?php
/**
* The settings view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @package repo
* @version $Id: setting.html.php $
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php if(common::checkNotCN()):?>
<style>
.user-addon{padding-right: 16px; padding-left: 16px;}
</style>
<?php endif;?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $lang->repo->settings;?></h2>
</div>
<form class='form-indicator main-form' method='post' target='hiddenwin'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->repo->SCM;?></th>
<td><?php echo html::select('SCM', $lang->repo->scmList, $repo->SCM, "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->repo->name;?></th>
<td class='required'><?php echo html::input('name', $repo->name, "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->repo->path;?></th>
<td class='required'><?php echo html::input('path', $repo->path, "class='form-control'")?></td>
<td class='text-muted'><?php echo $lang->repo->example->path;?></td>
</tr>
<tr>
<th><?php echo $lang->repo->encoding;?></th>
<td class='required'><?php echo html::input('encoding', $repo->encoding, "class='form-control'")?></td>
<td class='text-muted'><?php echo $lang->repo->example->encoding;?></td>
</tr>
<tr>
<th><?php echo $lang->repo->client;?></th>
<td class='required'><?php echo html::input('client', $repo->client, "class='form-control'")?></td>
<td class='text-muted'><?php echo $lang->repo->example->client;?></td>
</tr>
<tr>
<th><?php echo $lang->repo->account;?></th>
<td>
<?php echo html::input('account', $repo->account, "class='form-control' autocomplete='off'");?>
<input type='text' style="display:none">
</td>
</tr>
<tr>
<th><?php echo $lang->repo->password;?></th>
<td>
<div class='input-group'>
<?php echo html::password('password', $repo->password, "class='form-control'");?>
<span class='input-group-addon fix-border fix-padding'></span>
<?php echo html::select('encrypt', $lang->repo->encryptList, $repo->encrypt, "class='form-control'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->repo->acl;?></th>
<td>
<div class='input-group mgb-10'>
<span class='input-group-addon'><?php echo $lang->repo->group?></span>
<?php echo html::select('acl[groups][]', $groups, empty($repo->acl->groups) ? '' : join(',', $repo->acl->groups), "class='form-control chosen' multiple")?>
</div>
<div class='input-group'>
<span class='input-group-addon user-addon'><?php echo $lang->repo->user?></span>
<?php echo html::select('acl[users][]', $users, empty($repo->acl->users) ? '' : join(',', $repo->acl->users), "class='form-control chosen' multiple")?>
</div>
</td>
</tr>
<tr>
<td colspan='3' class='text-center form-actions'>
<?php echo html::submitButton();?>
<?php echo html::backButton();?>
</td>
</tr>
</table>
</form>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+1 -1
View File
@@ -33,7 +33,7 @@ $(function(){
if(data == 'finish')
{
$('#caption').text('<?php echo $lang->repo->notice->syncComplete?>');
return self.location = createLink('repo', 'browse', "repoID=<?php echo $repoID?>");
return self.location = createLink('repo', 'maintain');
}
$('#commits').html(parseInt($('#commits').html()) + parseInt(data));
setTimeout(syncComments, 10);
+2 -1
View File
@@ -12,8 +12,8 @@
include '../../common/view/header.html.php';
include '../../common/view/form.html.php';
include '../../common/view/kindeditor.html.php';
js::import($jsRoot . 'misc/highlight/highlight.pack.js');
css::import($jsRoot . 'misc/highlight/styles/github.css');
js::import($jsRoot . 'misc/highlight/highlight.pack.js');
$encodePath = $this->repo->encodePath($entry);
$version = " <span class=\"label label-info\">$revisionName</span>";
?>
@@ -56,6 +56,7 @@ $version = " <span class=\"label label-info\">$revisionName</span>";
<div class='panel-actions'>
<?php if($suffix != 'binary' and strpos($config->repo->images, "|$suffix|") === false):?>
<?php
if(common::hasPriv('repo', 'blame')) echo html::a($this->repo->createLink('blame', "repoID=$repoID&entry=&revision=$revision&encoding=$encoding", "entry=$encodePath"), html::icon('random') . $lang->repo->blame, '', "class='btn btn-sm btn-primary'");
if(common::hasPriv('repo', 'download')) echo html::a($this->repo->createLink('download', "repoID=$repoID&path=&fromRevision=$revision", "path=$encodePath"), html::icon('download-alt') . $lang->repo->download, 'hiddenwin', "class='btn btn-sm btn-primary'");
?>
<?php endif;?>
+5 -4
View File
@@ -266,10 +266,11 @@ class storyModel extends model
public function batchCreate($productID = 0, $branch = 0, $type = 'story')
{
$this->loadModel('action');
$branch = (int)$branch;
$now = helper::now();
$mails = array();
$stories = fixer::input('post')->get();
$branch = (int)$branch;
$productID = (int)$productID;
$now = helper::now();
$mails = array();
$stories = fixer::input('post')->get();
$result = $this->loadModel('common')->removeDuplicate('story', $stories, "product={$productID}");
$stories = $result['data'];
+4 -1
View File
@@ -15,6 +15,10 @@
* $config->svn->repos['pms']['password'] = 'pass';
*
*/
$config->svn->tagRequiredFields = 'name,repo';
/* will use the config of repo records in db
$config->svn = new stdClass();
$config->svn->encodings = 'utf-8';
$config->svn->client = '';
@@ -25,7 +29,6 @@ $config->svn->repos[$i]['encoding'] = 'utf-8';
$config->svn->repos[$i]['username'] = '';
$config->svn->repos[$i]['password'] = '';
/*
$i ++;
$config->svn->repos[$i]['path'] = '';
$config->svn->repos[$i]['username'] = '';
+8 -7
View File
@@ -37,10 +37,9 @@ class svn extends control
$url = helper::safe64Decode($url);
if(common::hasPriv('repo', 'diff'))
{
$repos = $this->loadModel('repo')->getAllRepos();
foreach($repos as $repo)
$svnRepos = $this->loadModel('repo')->getListBySCM('Subversion', 'haspriv');
foreach($svnRepos as $repo)
{
if($repo->SCM != 'Subversion') continue;
if(strpos(strtolower($url), strtolower($repo->path)) === 0)
{
$entry = $this->repo->encodePath(str_ireplace($repo->path, '', $url));
@@ -72,10 +71,9 @@ class svn extends control
$url = helper::safe64Decode($url);
if(common::hasPriv('repo', 'view'))
{
$repos = $this->loadModel('repo')->getAllRepos();
$repos = $this->loadModel('repo')->getListBySCM('Subversion', 'haspriv');
foreach($repos as $repo)
{
if($repo->SCM != 'Subversion') continue;
if(strpos(strtolower($url), strtolower($repo->path)) === 0)
{
$entry = $this->repo->encodePath(str_ireplace(strtolower($repo->path), '', $url));
@@ -109,9 +107,11 @@ class svn extends control
$parsedLogs[] = $this->svn->convertLog($entry);
}
$parsedObjects = array('stories' => array(), 'tasks' => array(), 'bugs' => array());
$this->loadModel('repo');
foreach($parsedLogs as $log)
{
$objects = $this->svn->parseComment($log->msg);
$objects = $this->repo->parseComment($log->msg);
if($objects)
{
$this->svn->saveAction2PMS($objects, $log, $repoRoot);
@@ -170,7 +170,8 @@ class svn extends control
$parsedFiles[$action][] = $path;
}
$objects = $this->svn->parseComment($message);
$objects = $this->loadModel('repo')->parseComment($message);
if($objects)
{
$log = new stdclass();
+164 -153
View File
@@ -71,6 +71,7 @@ class svnModel extends model
{
parent::__construct();
$this->loadModel('action');
$this->loadModel('repo');
}
/**
@@ -90,47 +91,57 @@ class svnModel extends model
foreach($this->repos as $name => $repo)
{
$this->printLog("begin repo $name");
$repo = (object)$repo;
$repo->name = $name;
if(!$this->setRepo($repo)) return false;
$savedRevision = $this->getSavedRevision();
$this->printLog("start from revision $savedRevision");
$logs = $this->getRepoLogs($repo, $savedRevision);
if(empty($logs)) continue;
$this->printLog("get " . count($logs) . " logs");
$this->printLog('begin parsing logs');
foreach($logs as $log)
$logs = $this->getRepoLogs($repo, $savedRevision);
$objects = array();
if(!empty($logs))
{
$this->printLog("parsing log {$log->revision}");
if($log->revision == $savedRevision)
$this->printLog("get " . count($logs) . " logs");
$this->printLog('begin parsing logs');
foreach($logs as $log)
{
$this->printLog("{$log->revision} alread parsed, ommit it");
continue;
$this->printLog("parsing log {$log->revision}");
if($log->revision == $savedRevision)
{
$this->printLog("{$log->revision} alread parsed, commit it");
continue;
}
$this->printLog("comment is\n----------\n" . trim($log->msg) . "\n----------");
$objects = $this->repo->parseComment($log->msg);
if($objects)
{
$this->printLog('extract' .
'story:' . join(' ', $objects['stories']) .
' task:' . join(' ', $objects['tasks']) .
' bug:' . join(',', $objects['bugs']));
$this->saveAction2PMS($objects, $log, $repo->encoding);
}
else
{
$this->printLog('no objects found' . "\n");
}
if($log->revision > $savedRevision) $savedRevision = $log->revision;
}
$this->printLog("comment is\n----------\n" . trim($log->msg) . "\n----------");
$objects = $this->parseComment($log->msg);
if($objects)
{
$this->printLog('extract' .
'story:' . join(' ', $objects['stories']) .
' task:' . join(' ', $objects['tasks']) .
' bug:' . join(',', $objects['bugs']));
$this->saveAction2PMS($objects, $log);
}
else
{
$this->printLog('no objects found' . "\n");
}
if($log->revision > $savedRevision) $savedRevision = $log->revision;
$this->saveLastRevision($savedRevision);
$this->printLog("save revision $savedRevision");
$this->deleteRestartFile();
$this->printLog("\n\nrepo #" . $repo->id . ': ' . $repo->path . " finished");
}
$this->saveLastRevision($savedRevision);
$this->printLog("save revision $savedRevision");
$this->deleteRestartFile();
$this->printLog("\n\nrepo $name finished");
// exe ci jobs in log
$cijobIdList = zget($objects, 'integrations', array());
$this->loadModel('compile');
foreach($cijobIdList as $id) $this->compile->execByCompile($id);
}
}
@@ -146,6 +157,19 @@ class svnModel extends model
if(!is_dir($this->logRoot)) mkdir($this->logRoot);
}
/**
* Set the tag file of a repo.
*
* @param string $repoId
* @access public
* @return void
*/
public function setTagFile($repoId)
{
$this->setLogRoot();
$this->tagFile = $this->logRoot . $repoId . '.tag';
}
/**
* Set the restart file.
*
@@ -176,13 +200,46 @@ class svnModel extends model
*/
public function setRepos()
{
if(!$this->config->svn->repos)
$repos = $this->loadModel('repo')->getListBySCM('Subversion');
$svnRepos = array();
$paths = array();
foreach($repos as $repo)
{
echo "You must set one svn repo.\n";
return false;
if(isset($paths[$repo->path])) continue;
unset($repo->acl);
unset($repo->desc);
$svnRepos[] = $repo;
$paths[$repo->path] = $repo->path;
}
$this->repos = $this->config->svn->repos;
if(isset($this->config->svn->repos))
{
foreach($this->config->svn->repos as $i => $repo)
{
$repoPath = $repo['path'];
if(empty($repoPath)) continue;
if(isset($paths[$repoPath])) continue;
$svnRepo = new stdclass();
$svnRepo->id = "c{$i}";
$svnRepo->client = $this->config->svn->client;
$svnRepo->path = $repoPath;
$svnRepo->prefix = '';
$svnRepo->SCM = 'Subversion';
$svnRepo->account = $repo['username'];
$svnRepo->password = $repo['password'];
$svnRepo->encoding = zget($repo, 'encoding', $this->config->svn->client);
$svnRepos[] = $svnRepo;
$paths[$repoPath] = $repoPath;
}
}
if(empty($svnRepos)) echo "You must set one svn repo.\n";
$this->repos = $svnRepos;
return true;
}
@@ -217,7 +274,8 @@ class svnModel extends model
$this->setClient($repo);
if(empty($this->client)) return false;
$this->setLogFile($repo->name);
$this->setLogFile($repo->id);
$this->setTagFile($repo->id);
$this->setRepoRoot($repo);
return true;
}
@@ -231,23 +289,17 @@ class svnModel extends model
*/
public function setClient($repo)
{
if($this->config->svn->client == '')
{
echo "You must set the svn client file.\n";
return false;
}
$this->client = $this->config->svn->client . " --non-interactive";
$this->client = $repo->client . " --non-interactive";
if(stripos($repo->path, 'https') === 0 or stripos($repo->path, 'svn') === 0)
{
$cmd = $this->config->svn->client . ' --version --quiet';
$cmd = $repo->client . ' --version --quiet';
$version = `$cmd`;
if(version_compare($version, '1.6.0', '>'))
{
$this->client .= ' --trust-server-cert';
}
}
if(isset($repo->username)) $this->client .= " --username $repo->username --password $repo->password --no-auth-cache";
if(isset($repo->account)) $this->client .= " --username $repo->account --password $repo->password --no-auth-cache";
return true;
}
@@ -258,9 +310,9 @@ class svnModel extends model
* @access public
* @return void
*/
public function setLogFile($repoName)
public function setLogFile($repoId)
{
$this->logFile = $this->logRoot . $repoName;
$this->logFile = $this->logRoot . $repoId . '.log';
}
/**
@@ -272,11 +324,24 @@ class svnModel extends model
*/
public function setRepoRoot($repo)
{
$cmd = $this->client . " info --xml $repo->path";
$info = `$cmd`;
$info = simplexml_load_string($info);
$repoRoot = $info->entry->repository->root;
$this->repoRoot = $repoRoot;
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
$info = $scm->info('');
$this->repoRoot = $info->root;
}
/**
* get tags histories for repo.
*
* @param object $repo
* @access public
* @return void
*/
public function getRepoTags($repo, $path)
{
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
return $scm->tags($path);
}
/**
@@ -289,110 +354,24 @@ class svnModel extends model
*/
public function getRepoLogs($repo, $fromRevision)
{
$parsedLogs = array();
/* The svn log command. */
$cmd = $this->client . " log -r $fromRevision:HEAD -v --xml $repo->path";
$rawLogs = `$cmd`;
$logs = @simplexml_load_string($rawLogs); // Convert it to object.
if(!$logs)
{
echo "Some error occers: \nThe command is $cmd\n the svn logs is $rawLogs\n";
return false;
}
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
$logs = $scm->log('', $fromRevision);
if(empty($logs)) return false;
/* Process logs. */
foreach($logs->logentry as $entry) $parsedLogs[] = $this->convertLog($entry);
return $parsedLogs;
}
/**
* Convert log from xml format to object.
*
* @param object $log
* @access public
* @return object
*/
public function convertLog($log)
{
/* Get author, revision, msg, date attributes. */
$parsedLog = new stdClass();
$parsedLog->author = (string)$log->author;
$parsedLog->revision = (int)$log['revision'];
$parsedLog->msg = trim((string)$log->msg);
$parsedLog->date = date('Y-m-d H:i:s', strtotime($log->date));
/* Process files. */
$parsedLog->files = array();
foreach ($log->paths as $key => $paths)
foreach($logs as $log)
{
$parsedFiles = array();
foreach($paths as $path)
{
$action = (string)$path['action'];
$parsedFiles[$action][] = (string)$path;
}
$log->author = $log->committer;
$log->msg = $log->comment;
$log->date = $log->time;
/* Process files. */
$log->files = array();
foreach($log->change as $file => $info) $log->files[$info['action']][] = $file;
}
$parsedLog->files = $parsedFiles;
return $parsedLog;
}
/**
* Parse the comment of svn, extract object id list from it.
*
* @param string $comment
* @access public
* @return array
*/
public function parseComment($comment)
{
$stories = array();
$tasks = array();
$bugs = array();
// bug|story|task(case insensitive) + some space + #|:|:(Chinese) + id lists(maybe join with space or ,)
// $comment = "bug # 1,2,3,4 Bug:1 2 3 4 5 story:9999,1234566 story:456,1234566";
$commonReg = "(?:\s){0,}(?:#|:|:){0,}([0-9, ]{1,})";
$taskReg = '/task' . $commonReg . '/i';
$storyReg = '/story' . $commonReg . '/i';
$bugReg = '/bug' . $commonReg . '/i';
if(preg_match_all($storyReg, $comment, $result)) $stories = join(' ', $result[1]);
if(preg_match_all($taskReg, $comment, $result)) $tasks = join(' ', $result[1]);
if(preg_match_all($bugReg, $comment, $result)) $bugs = join(' ', $result[1]);
if($stories) $stories = array_unique(explode(' ', str_replace(',', ' ', $stories)));
if($tasks) $tasks = array_unique(explode(' ', str_replace(',', ' ', $tasks)));
if($bugs) $bugs = array_unique(explode(' ', str_replace(',', ' ', $bugs)));
if(!$stories and !$tasks and !$bugs) return array();
return array('stories' => $stories, 'tasks' => $tasks, 'bugs' => $bugs);
}
/**
* Convert the comment to uft-8.
*
* @param string $comment
* @access public
* @return string
*/
public function iconvComment($comment)
{
/* Get encodings. */
$encodings = str_replace(' ', '', isset($this->config->svn->encodings) ? $this->config->svn->encodings : '');
if($encodings == '') return $comment;
$encodings = explode(',', $encodings);
/* Try convert. */
foreach($encodings as $encoding)
{
if($encoding == 'utf-8') continue;
$result = helper::convertEncoding($comment, $encoding);
if($result) return $result;
}
return $comment;
return $logs;
}
/**
@@ -482,13 +461,14 @@ class svnModel extends model
* @access public
* @return void
*/
public function saveAction2PMS($objects, $log, $repoRoot = '')
public function saveAction2PMS($objects, $log, $repoRoot = '', $encodings = 'utf-8')
{
$action = new stdclass();
$action->actor = $log->author;
$action->action = 'svncommited';
$action->date = $log->date;
$action->comment = htmlspecialchars($this->iconvComment($log->msg));
$action->comment = htmlspecialchars($this->repo->iconvComment($log->msg, $encodings));
$action->extra = $log->revision;
$changes = $this->createActionChanges($log, $repoRoot);
@@ -732,4 +712,35 @@ class svnModel extends model
return $buildedURL;
}
/**
* Get the saved tag.
*
* @access public
* @return int
*/
public function getSavedTag($repoID = 0)
{
if($repoID) $this->setTagFile($repoID);
if(!file_exists($this->tagFile)) return array();
if(file_exists($this->restartFile)) return array();
$tags = array();
foreach(json_decode(file_get_contents($this->tagFile)) as $tag) $tags[$tag] = $tag;
return $tags;
}
/**
* Save the last revision.
*
* @param int $tag
* @access public
* @return void
*/
public function saveLastTag($tag, $repoId = 0)
{
if($repoId) $this->setTagFile($repoId);
if(is_array($tag)) $tag = json_encode($tag);
file_put_contents($this->tagFile, $tag);
}
}
+3 -2
View File
@@ -27,11 +27,12 @@ class taskModel extends model
dao::$errors[] = $this->lang->task->error->recordMinus;
return false;
}
$projectID = (int)$projectID;
$taskIdList = array();
$taskFiles = array();
$this->loadModel('file');
$task = fixer::input('post')
->setDefault('project', (int)$projectID)
->setDefault('project', $projectID)
->setDefault('estimate,left,story', 0)
->setDefault('status', 'wait')
->setIF($this->post->estimate != false, 'left', $this->post->estimate)
@@ -65,7 +66,7 @@ class taskModel extends model
/* Check duplicate task. */
if($task->type != 'affair')
{
$result = $this->loadModel('common')->removeDuplicate('task', $task, "project=$projectID and story=" . (int)$task->story);
$result = $this->loadModel('common')->removeDuplicate('task', $task, "project={$projectID} and story=" . (int)$task->story);
if($result['stop'])
{
$taskIdList[$assignedTo] = array('status' => 'exists', 'id' => $result['duplicate']);
+1
View File
@@ -242,6 +242,7 @@ class testcaseModel extends model
function batchCreate($productID, $branch, $storyID)
{
$branch = (int)$branch;
$productID = (int)$productID;
$now = helper::now();
$cases = fixer::input('post')->get();
+4 -2
View File
@@ -42,9 +42,11 @@ Copyright (c) Vladimir Gubarkov <xonixx@gmail.com>
}
function copyToBuffer(textToCopy) {
if (window.clipboardData) { // IE
if (window.clipboardData)
{ // IE
window.clipboardData.setData("Text", textToCopy);
} else if (window.netscape) { // FF
} else if (window.netscape)
{ // FF
// from http://developer.mozilla.org/en/docs/Using_the_Clipboard
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
+3 -3
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long