Merge branch 'jihuMR'

This commit is contained in:
dingguodong
2021-09-07 20:47:10 +08:00
48 changed files with 2303 additions and 261 deletions
+4
View File
@@ -60,6 +60,7 @@ $filter->git = new stdclass();
$filter->svn = new stdclass();
$filter->search = new stdclass();
$filter->gitlab = new stdclass();
$filter->mr = new stdclass();
$filter->ci = new stdclass();
$filter->block->default = new stdclass();
@@ -140,6 +141,7 @@ $filter->repo->ajaxsynccommit = new stdclass();
$filter->search->index = new stdclass();
$filter->gitlab->webhook = new stdclass();
$filter->gitlab->importissue = new stdclass();
$filter->mr->diff = new stdclass();
$filter->ci->checkCompileStatus = new stdclass();
$filter->execution->export = new stdclass();
@@ -356,4 +358,6 @@ $filter->gitlab->importissue->get['product'] = 'string';
$filter->gitlab->importissue->get['project'] = 'int';
$filter->gitlab->importissue->get['repo'] = 'int';
$filter->mr->diff->cookie['arrange'] = 'reg::word';
$filter->ci->checkCompileStatus->get['gitlabOnly'] = 'string';
+3 -1
View File
@@ -212,9 +212,10 @@ 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_PIPELINE', '`' . $config->db->prefix . 'pipeline`');
define('TABLE_PIPELINE', '`' . $config->db->prefix . 'pipeline`');
define('TABLE_JOB', '`' . $config->db->prefix . 'job`');
define('TABLE_COMPILE', '`' . $config->db->prefix . 'compile`');
define('TABLE_MR', '`' . $config->db->prefix . 'mr`');
define('TABLE_REPO', '`' . $config->db->prefix . 'repo`');
define('TABLE_RELATION', '`' . $config->db->prefix . 'relation`');
@@ -257,6 +258,7 @@ $config->objectTables['stakeholder'] = TABLE_STAKEHOLDER;
$config->objectTables['job'] = TABLE_JOB;
$config->objectTables['team'] = TABLE_TEAM;
$config->objectTables['pipeline'] = TABLE_PIPELINE;
$config->objectTables['mr'] = TABLE_MR;
/* Program privs.*/
$config->programPriv = new stdclass();
+3
View File
@@ -2,3 +2,6 @@ ALTER TABLE `zt_testtask` ADD `realFinishedDate` datetime NOT NULL AFTER `end`;
ALTER TABLE `zt_doc` ADD `draft` longtext NOT NULL AFTER `views`;
ALTER TABLE `zt_release` ADD `mailto` text AFTER `desc`;
ALTER TABLE `zt_release` ADD `notify` varchar(255) AFTER `mailto`;
INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`, `lastTime`) VALUES
('*/5', '*', '*', '*', '*', 'moduleName=mr&methodName=syncMR', '定时同步GitLab合并数据到禅道数据库', 'zentao', 1, 'normal', '0000-00-00 00:00:00');
+22
View File
@@ -2,3 +2,25 @@ UPDATE `zt_action` SET `action` = 'reviewpassed' WHERE `action` = 'passreviewed'
UPDATE `zt_action` SET `action` = 'reviewrejected' WHERE `action` = 'reviewclosed';
UPDATE `zt_action` SET `action` = 'reviewclarified' WHERE `action` = 'clarifyreviewed';
DELETE FROM `zt_score` WHERE `module` = 'tutorial' AND `method` = 'finish';
CREATE TABLE `zt_mr` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`gitlabID` mediumint(8) unsigned NOT NULL,
`sourceProject` int unsigned NOT NULL,
`sourceBranch` varchar(100) NOT NULL,
`targetProject` int unsigned NOT NULL,
`targetBranch` varchar(100) NOT NULL,
`mriid` int unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`assignee` varchar(255) NOT NULL,
`reviewer` varchar(255) NOT NULL,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
`deleted` tinyint(1) NOT NULL,
`status` char(30) NOT NULL,
`mergeStatus` char(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+22
View File
@@ -545,6 +545,28 @@ CREATE TABLE IF NOT EXISTS `zt_module` (
KEY `type` (`type`),
KEY `path` (`path`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_mr`;
CREATE TABLE `zt_mr` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`gitlabID` mediumint(8) unsigned NOT NULL,
`sourceProject` int unsigned NOT NULL,
`sourceBranch` varchar(100) NOT NULL,
`targetProject` int unsigned NOT NULL,
`targetBranch` varchar(100) NOT NULL,
`mriid` int unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`assignee` varchar(255) NOT NULL,
`reviewer` varchar(255) NOT NULL,
`createdBy` varchar(30) NOT NULL,
`createdDate` datetime NOT NULL,
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
`deleted` tinyint(1) NOT NULL,
`status` char(30) NOT NULL,
`mergeStatus` char(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_notify`;
CREATE TABLE IF NOT EXISTS `zt_notify` (
`id` mediumint unsigned NOT NULL AUTO_INCREMENT,
+5 -4
View File
@@ -278,13 +278,15 @@ class gitlab
* @access public
* @return array
*/
public function diff($path, $fromRevision, $toRevision)
public function diff($path, $fromRevision, $toRevision, $fromProject = '')
{
if(!scm::checkRevision($fromRevision)) return array();
if(!scm::checkRevision($toRevision)) return array();
$api = "compare";
$params = array('from' => $fromRevision, 'to' => $toRevision, 'straight' => 1);
$api = "compare";
$params = array('from' => $fromRevision, 'to' => $toRevision);
if($fromProject) $params['from_project_id'] = $fromProject;
if($toRevision == 'HEAD' and $this->branch) $params['to'] = $this->branch;
$results = $this->fetch($api, $params);
foreach($results->diffs as $key => $diff)
@@ -303,7 +305,6 @@ class gitlab
foreach($diffLines as $diffLine) $lines[] = $diffLine;
}
return $lines;
//return $results->diffs;
}
/**
+64 -63
View File
@@ -5,8 +5,8 @@ class scm
/**
* Set engine.
*
* @param object $repo
*
* @param object $repo
* @access public
* @return void
*/
@@ -19,10 +19,10 @@ class scm
}
/**
* List files.
*
* @param string $path
* @param string $revision
* List files.
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
@@ -34,10 +34,10 @@ class scm
/**
* Get tags.
*
* @param string $path
* @param string $revision
* @param bool $onlyDir
*
* @param string $path
* @param string $revision
* @param bool $onlyDir
* @access public
* @return array
*/
@@ -49,7 +49,7 @@ class scm
/**
* Get branch.
*
*
* @access public
* @return array
*/
@@ -60,11 +60,11 @@ class scm
/**
* Get log.
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @param int $count
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @param int $count
* @access public
* @return array
*/
@@ -78,9 +78,9 @@ class scm
/**
* Blame file.
*
* @param string $path
* @param string $revision
*
* @param string $path
* @param string $revision
* @access public
* @return array
*/
@@ -92,9 +92,9 @@ class scm
/**
* Get last log.
*
* @param string $path
* @param int $count
*
* @param string $path
* @param int $count
* @access public
* @return array
*/
@@ -105,20 +105,21 @@ class scm
/**
* Diff file.
*
* @param string $path
* @param string $fromRevision
* @param string $toRevision
* @param string $parse
*
* @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')
public function diff($path, $fromRevision = 0, $toRevision = 'HEAD', $parse = 'yes', $extra = '')
{
if(!scm::checkRevision($fromRevision)) return array();
if(!scm::checkRevision($toRevision)) return array();
$diffs = $this->engine->diff($path, $fromRevision, $toRevision);
if(!$extra) $diffs = $this->engine->diff($path, $fromRevision, $toRevision);
if($extra) $diffs = $this->engine->diff($path, $fromRevision, $toRevision, $extra);
if($parse != 'yes') return implode("\n", $diffs);
return $this->engine->parseDiff($diffs);
@@ -126,23 +127,23 @@ class scm
/**
* Cat file.
*
* @param string $entry
* @param string $revision
*
* @param string $entry
* @param string $revision
* @access public
* @return string
*/
public function cat($entry, $revision = 'HEAD')
{
if(!scm::checkRevision($revision)) return false;
return $this->engine->cat($entry, $revision);
return $this->engine->cat($entry, $revision);
}
/**
* Get info.
*
* @param string $entry
* @param string $revision
*
* @param string $entry
* @param string $revision
* @access public
* @return object
*/
@@ -154,8 +155,8 @@ class scm
/**
* Exec scm cmd.
*
* @param string $cmd
*
* @param string $cmd
* @access public
* @return array
*/
@@ -165,47 +166,47 @@ class scm
}
/**
* Get commit count
*
* @param int $commits
* @param string $lastVersion
* Get commit count
*
* @param int $commits
* @param string $lastVersion
* @access public
* @return int
*/
public function getCommitCount($commits = 0, $lastVersion = 0)
{
if(!scm::checkRevision($lastVersion)) return false;
return $this->engine->getCommitCount($commits, $lastVersion);
return $this->engine->getCommitCount($commits, $lastVersion);
}
/**
* Get latest revision.
*
*
* @access public
* @return string
*/
public function getLatestRevision()
{
return $this->engine->getLatestRevision();
return $this->engine->getLatestRevision();
}
/**
* Get first revision.
*
*
* @access public
* @return string
*/
public function getFirstRevision()
{
return $this->engine->getFirstRevision();
return $this->engine->getFirstRevision();
}
/**
* Get commits.
*
* @param string $version
* @param int $count
* @param string $branch
*
* @param string $version
* @param int $count
* @param string $branch
* @access public
* @return array
*/
@@ -216,9 +217,9 @@ class scm
}
/**
* Check revision
*
* @param int|string $revision
* Check revision
*
* @param int|string $revision
* @static
* @access public
* @return bool
@@ -231,9 +232,9 @@ class scm
}
/**
* Escape command.
*
* @param string $cmd
* Escape command.
*
* @param string $cmd
* @access public
* @return string
*/
@@ -246,12 +247,12 @@ function escapeCmd($cmd)
}
/**
* Execute command.
*
* @param string $cmd
* @param string $return
* @param int $result
* @param string $type
* Execute command.
*
* @param string $cmd
* @param string $return
* @param int $result
* @param string $type
* @access public
* @return array|string
*/
+1
View File
@@ -242,6 +242,7 @@ $lang->testcase->testsuite = 'Test Suite';
$lang->testcase->caselib = 'Case Library';
$lang->devops->compile = 'Compile';
$lang->devops->mr = 'Merge Request';
$lang->devops->repo = 'Repo';
$lang->devops->rules = 'Rule';
+8 -5
View File
@@ -238,6 +238,7 @@ $lang->scrum->menu->settings['subMenu']->whitelist = array('link' => "{$lang->
$lang->scrum->menu->settings['subMenu']->stakeholder = array('link' => "{$lang->stakeholder->common}|stakeholder|browse|project=%s", 'subModule' => 'stakeholder');
$lang->scrum->menu->settings['subMenu']->group = array('link' => "{$lang->priv}|project|group|project=%s", 'alias' => 'group,manageview,managepriv');
/* Execution menu. */
$lang->execution->homeMenu = new stdclass();
$lang->execution->homeMenu->all = array('link' => "{$lang->execution->all}|execution|all|", 'alias' => 'batchedit');
@@ -333,6 +334,7 @@ $lang->qa->dividerMenu = ',bug,testtask,caselib,';
$lang->devops->menu = new stdclass();
$lang->devops->menu->code = array('link' => "{$lang->repo->common}|repo|browse|repoID=%s", 'alias' => 'diff,view,revision,log,blame,showsynccommit');
$lang->devops->menu->compile = array('link' => "{$lang->devops->compile}|job|browse", 'subModule' => 'compile,job');
$lang->devops->menu->mr = array('link' => "{$lang->devops->mr}|mr|browse");
$lang->devops->menu->gitlab = array('link' => "GitLab|gitlab|browse", 'alias' => 'create,edit');
$lang->devops->menu->jenkins = array('link' => "Jenkins|jenkins|browse", 'alias' => 'create,edit');
$lang->devops->menu->maintain = array('link' => "{$lang->devops->repo}|repo|maintain", 'alias' => 'create,edit');
@@ -340,11 +342,11 @@ $lang->devops->menu->rules = array('link' => "{$lang->devops->rules}|repo|set
$lang->devops->menuOrder[5] = 'code';
$lang->devops->menuOrder[10] = 'compile';
$lang->devops->menuOrder[15] = 'gitlab';
$lang->devops->menuOrder[20] = 'jenkins';
$lang->devops->menuOrder[25] = 'maintain';
$lang->devops->menuOrder[30] = 'rules';
$lang->devops->menuOrder[15] = 'mr';
$lang->devops->menuOrder[20] = 'gitlab';
$lang->devops->menuOrder[25] = 'jenkins';
$lang->devops->menuOrder[30] = 'maintain';
$lang->devops->menuOrder[35] = 'rules';
/* Doc menu. */
$lang->doc->menu = new stdclass();
$lang->doc->menu->dashboard = array('link' => "{$lang->dashboard}|doc|index");
@@ -538,6 +540,7 @@ $lang->navGroup->devops = 'devops';
$lang->navGroup->repo = 'devops';
$lang->navGroup->job = 'devops';
$lang->navGroup->jenkins = 'devops';
$lang->navGroup->mr = 'devops';
$lang->navGroup->gitlab = 'devops';
$lang->navGroup->compile = 'devops';
$lang->navGroup->ci = 'devops';
+5 -3
View File
@@ -241,9 +241,11 @@ $lang->testcase->case = '用例';
$lang->testcase->testsuite = '套件';
$lang->testcase->caselib = '用例库';
$lang->devops->compile = '构建';
$lang->devops->repo = '版本库';
$lang->devops->rules = '指令';
$lang->devops->compile = '构建';
$lang->devops->mr = '合并请求';
$lang->devops->repo = '版本库';
$lang->devops->rules = '指令';
$lang->devops->settings = '合并请求设置';
$lang->admin->system = '系统';
$lang->admin->entry = '应用';
+23 -10
View File
@@ -129,17 +129,30 @@ class compileModel extends model
if(!$job) return false;
$data = new stdclass();
$data->PARAM_TAG = $compile->tag;
$data->ZENTAO_DATA = "compile={$compile->id}";
$compileID = $compile->id;
$repo = $this->loadModel('repo')->getRepoById($job->repo);
$url = $this->getBuildUrl($job);
$build = new stdclass();
$build->queue = $this->loadModel('ci')->sendRequest($url->url, $data, $url->userPWD);
$build->status = $build->queue ? 'created' : 'create_fail';
$build->updateDate = helper::now();
$this->dao->update(TABLE_COMPILE)->data($build)->where('id')->eq($compile->id)->exec();
$this->dao->update(TABLE_JOB)->set('lastStatus')->eq($build->status)->set('lastExec')->eq($build->updateDate)->where('id')->eq($compile->job)->exec();
if($job->triggerType == 'tag')
{
$lastTag = $this->getLastTagByRepo($repo);
if($lastTag)
{
$job->lastTag = $lastTag;
$this->dao->update(TABLE_JOB)->set('lastTag')->eq($lastTag)->where('id')->eq($job->id)->exec();
}
$this->dao->update(TABLE_COMPILE)->set('tag')->eq($lastTag)->where('id')->eq($compile->id)->exec();
}
if($job->engine == 'gitlab') $compile = $this->loadModel('job')->execGitlabPipeline($job, $compileID);
if($job->engine == 'jenkins') $compile = $this->job->execJenkinsPipeline($job, $repo, $compile->id);
$this->dao->update(TABLE_COMPILE)->data($compile)->where('id')->eq($compileID)->exec();
$this->dao->update(TABLE_JOB)
->set('lastStatus')->eq($build->status)
->set('lastExec')->eq($build->updateDate)
->where('id')->eq($job->id)
->exec();
return !dao::isError();
}
+86 -2
View File
@@ -58,6 +58,8 @@ class gitlab extends control
$gitlabID = $this->gitlab->create();
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action');
$actionID = $this->action->create('gitlab', $gitlabID, 'create');
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
@@ -66,6 +68,24 @@ class gitlab extends control
$this->display();
}
/**
* view a gitlab.
* @param int $id
* @access public
* @return void
*/
public function view($id)
{
$gitlab = $this->gitlab->getByID($id);
$this->view->title = $this->lang->gitlab->common . $this->lang->colon . $this->lang->gitlab->view;
$this->view->gitlab = $gitlab;
$this->view->users = $this->loadModel('user')->getPairs('noclosed');
$this->view->actions = $this->loadModel('action')->getList('gitlab', $id);
$this->view->preAndNext = $this->loadModel('common')->getPreAndNextObject('pipeline', $id);
$this->display();
}
/**
* Edit a gitlab.
*
@@ -75,17 +95,24 @@ class gitlab extends control
*/
public function edit($id)
{
$gitlab = $this->gitlab->getByID($id);
$oldGitLab = $this->gitlab->getByID($id);
if($_POST)
{
$this->checkToken();
$this->gitlab->update($id);
$gitLab = $this->gitlab->getByID($id);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('action');
$actionID = $this->action->create('gitlab', $id, 'edit');
$changes = common::createChanges($oldGitLab, $gitLab);
$this->action->logHistory($actionID, $changes);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse')));
}
$this->view->title = $this->lang->gitlab->common . $this->lang->colon . $this->lang->gitlab->edit;
$this->view->gitlab = $gitlab;
$this->view->gitlab = $oldGitLab;
$this->display();
}
@@ -178,7 +205,14 @@ class gitlab extends control
{
if($confim != 'yes') die(js::confirm($this->lang->gitlab->confirmDelete, inlink('delete', "id=$id&confirm=yes")));
$oldGitLab = $this->gitlab->getByID($id);
$this->loadModel('action');
$this->gitlab->delete(TABLE_PIPELINE, $id);
$gitLab = $this->gitlab->getByID($id);
$actionID = $this->action->create('gitlab', $id, 'delete');
$changes = common::createChanges($oldGitLab, $gitLab);
$this->action->logHistory($actionID, $changes);
die(js::reload('parent'));
}
@@ -367,4 +401,54 @@ class gitlab extends control
}
return $this->send($options);
}
/**
* AJAX: Get project branches.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return void
*/
public function ajaxGetProjectBranches($gitlabID, $projectID)
{
if(!$gitlabID or !$projectID) return $this->send(array('message' => array()));
$branches = $this->gitlab->apiGetBranches($gitlabID, $projectID);
$options = "<option value=''></option>";
foreach($branches as $branch)
{
$options .= "<option value='{$branch->name}'>{$branch->name}</option>";
}
$this->send($options);
}
/**
* AJAX: Get MR user pairs to select assignee_ids and reviewer_ids.
* Attention: The user must be a member of the GitLab project.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return void
*/
public function ajaxGetMRUserPairs($gitlabID, $projectID)
{
if(!$gitlabID) return $this->send(array('message' => array()));
$bindedUsers = $this->gitlab->getUserIdRealnamePairs($gitlabID);
$rawProjectUsers = $this->gitlab->apiGetProjectUsers($gitlabID, $projectID);
$users = array();
foreach($rawProjectUsers as $rawProjectUser)
{
if(!empty($bindedUsers[$rawProjectUser->id])) $users[$rawProjectUser->id] = $bindedUsers[$rawProjectUser->id];
}
$options = "<option value=''></option>";
foreach($users as $index => $user)
{
$options .= "<option value='{$index}'>{$user}</option>";
}
$this->send($options);
}
}
+1
View File
@@ -4,6 +4,7 @@ $lang->gitlab->common = 'GitLab';
$lang->gitlab->browse = '浏览GitLab';
$lang->gitlab->create = '添加GitLab';
$lang->gitlab->edit = '编辑GitLab';
$lang->gitlab->view = '查看GitLab';
$lang->gitlab->bindUser = '绑定用户';
$lang->gitlab->webhook = 'webhook';
$lang->gitlab->bindProduct = '关联产品';
+178 -35
View File
@@ -34,12 +34,15 @@ class gitlabModel extends model
*/
public function getList($orderBy = 'id_desc', $pager = null)
{
return $this->loadModel('pipeline')->getList('gitlab', $orderBy, $pager);
$gitlabList = $this->loadModel('pipeline')->getList('gitlab', $orderBy, $pager);
return $gitlabList;
}
/**
* Get gitlab pairs.
*
* @access public
* @return array
*/
@@ -63,7 +66,25 @@ class gitlabModel extends model
}
/**
* Get gitlab user id zentao account pairs of one gitlab.
* Get gitlab user id and realname pairs of one gitlab.
*
* @param int $gitlabID
* @access public
* @return void
*/
public function getUserIdRealnamePairs($gitlabID)
{
return $this->dao->select('oauth.openID as openID,user.realname as realname')
->from(TABLE_OAUTH)->alias('oauth')
->leftJoin(TABLE_USER)->alias('user')
->on("oauth.account = user.account")
->where('providerType')->eq('gitlab')
->andWhere('providerID')->eq($gitlabID)
->fetchPairs();
}
/**
* Get gitlab user id and zentao account pairs of one gitlab.
*
* @param int $gitlab
* @access public
@@ -255,42 +276,60 @@ class gitlabModel extends model
/**
* Get gitlab project name of one gitlab project.
*
* @param int $jobID
* @param int $gitlabID
* @param int $projectID
* @access public
* @return string|false
*/
public function getObjectNameForJob($gitlabID, $projectID)
public function getProjectName($gitlabID, $projectID)
{
$project = $this->apiGetSingleProject($gitlabID, $projectID);
if(isset($project->name)) return $project->name;
if(is_object($project) and isset($project->name)) return $project->name;
return false;
}
/**
* Get ref option menus.
* Get reference option menus.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return array
*/
public function getRefOptions($gitlabID, $projectID)
public function getReferenceOptions($gitlabID, $projectID)
{
$refList = array();
$branches = $this->loadModel('gitlab')->apiGetBranches($gitlabID, $projectID);
$tags = $this->loadModel('gitlab')->apiGetTags($gitlabID, $projectID);
$refList = array();
/* fix bug 14612*/
if(isset($branches->message)) return array();
$branches = $this->apiGetBranches($gitlabID, $projectID);
foreach($branches as $branch) $refList[$branch->name] = "Branch::" . $branch->name;
if(isset($branches->error)) return array();
$tags = $this->apiGetTags($gitlabID, $projectID);
foreach($tags as $tag) $refList[$tag->name] = "Tag::" . $tag->name;
foreach($branches as $branch) $refList[] = "Branch::" . $branch->name;
foreach($tags as $tag) $refList[] = "Tag::" . $tag->name;
return $refList;
}
/**
* Get branches.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return array
*/
public function getBranches($gitlabID, $projectID)
{
$rawBranches = $this->apiGetBranches($gitlabID, $projectID);
$branches = array();
foreach($rawBranches as $branch)
{
$branches[] = $branch->name;
}
return $branches;
}
/**
* Create a gitlab.
*
@@ -352,6 +391,23 @@ class gitlabModel extends model
return json_decode(commonModel::http($url, $data, $options));
}
/**
* Get a list of to-do items.
*
* @see https://docs.gitlab.com/ee/api/todos.html
* @param int $gitlabID
* @param int $projectID
* @access public
* @return object
*/
public function apiTodoList($gitlabID, $projectID)
{
$gitlab = $this->loadModel('gitlab')->getByID($gitlabID);
if(!$gitlab) return '';
$url = rtrim($gitlab->url, '/')."/api/v4/todos?project_id=$projectID&type=MergeRequest&private_token={$gitlab->token}";
return json_decode(commonModel::http($url));
}
/**
* Get current user.
*
@@ -413,10 +469,8 @@ class gitlabModel extends model
$allResults = array();
for($page = 1; true; $page ++)
{
$results = json_decode(commonModel::http($host . "?private_token={$gitlab->token}&simple=true&membership=true&page={$page}&per_page=100"));
if(isset($results->error)) break;
$results = json_decode(commonModel::http($host . "?private_token={$gitlab->token}&simple=true&page={$page}&per_page=100"));
if(empty($results) or $page > 10) break;
$allResults = array_merge($allResults, $results);
}
@@ -437,6 +491,94 @@ class gitlabModel extends model
return json_decode(commonModel::http($url));
}
/**
* Get project users.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return object
*/
public function apiGetProjectUsers($gitlabID, $projectID)
{
$url = sprintf($this->getApiRoot($gitlabID), "/projects/$projectID/users");
return json_decode(commonModel::http($url));
}
/**
* Get project all members(users and users in groups).
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return object
*/
public function apiGetProjectMembers($gitlabID, $projectID)
{
$url = sprintf($this->getApiRoot($gitlabID), "/projects/$projectID/members/all");
return json_decode(commonModel::http($url));
}
/**
* Get the member detail in project.
*
* @param int $gitlabID
* @param int $projectID
* @param int $userID
* @access public
* @return object
*/
public function apiGetProjectMember($gitlabID, $projectID, $userID)
{
$url = sprintf($this->getApiRoot($gitlabID), "/projects/$projectID/members/all/$userID");
return json_decode(commonModel::http($url));
}
/**
* Get single branch by API.
*
* @param int $gitlabID
* @param int $projectID
* @param string $branch
* @access public
* @return object
*/
public function apiGetSingleBranch($gitlabID, $projectID, $branch)
{
$url = sprintf($this->getApiRoot($gitlabID), "/projects/$projectID/repository/branches/$branch");
return json_decode(commonModel::http($url));
}
/**
* Get Forks of a project by API.
*
* @docs https://docs.gitlab.com/ee/api/projects.html#list-forks-of-a-project
* @param int $gitlabID
* @param int $projectID
* @access public
* @return object
*/
public function apiGetForks($gitlabID, $projectID)
{
$url = sprintf($this->getApiRoot($gitlabID), "/projects/$projectID/forks");
return json_decode(commonModel::http($url));
}
/**
* Get upstream project by API.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return void
*/
public function apiGetUpstream($gitlabID,$projectID)
{
$currentProject = $this->apiGetSingleProject($gitlabID, $projectID);
if(isset($currentProject->forked_from_project)) return $currentProject->forked_from_project;
return array();
}
/**
* Get hooks.
*
@@ -726,25 +868,26 @@ class gitlabModel extends model
/**
* Create a new pipeline by api.
*
* @param integer $gitlabID
* @param integer $projectID
* @param string $reference
* @param int $gitlabID
* @param int $projectID
* @param object $params
* @access public
* @return object
* @docment https://docs.gitlab.com/ee/api/pipelines.html#create-a-new-pipeline
*/
public function apiCreatePipeline($gitlabID, $projectID, $reference)
public function apiCreatePipeline($gitlabID, $projectID, $params)
{
if(!is_string($params)) $params = json_encode($params);
$url = sprintf($this->getApiRoot($gitlabID), "/projects/{$projectID}/pipeline");
return json_decode(commonModel::http($url, $reference, null, array("Content-Type: application/json")));
return json_decode(commonModel::http($url, $params, null, array("Content-Type: application/json")));
}
/**
* Get single pipline by api.
*
* @param integer $gitlabID
* @param integer $projectID
* @param integer $pipelineID
* @param int $gitlabID
* @param int $projectID
* @param int $pipelineID
* @access public
* @return object
* @docment https://docs.gitlab.com/ee/api/pipelines.html#get-a-single-pipeline
@@ -758,9 +901,9 @@ class gitlabModel extends model
/**
* List pipeline jobs by api.
*
* @param integer $gitlabID
* @param integer $projectID
* @param integer $pipelineID
* @param int $gitlabID
* @param int $projectID
* @param int $pipelineID
* @return object
* @docment https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-jobs
*/
@@ -773,9 +916,9 @@ class gitlabModel extends model
/**
* Get a single job by api.
*
* @param integer $gitlabID
* @param integer $projectID
* @param integer $jobID
* @param int $gitlabID
* @param int $projectID
* @param int $jobID
* @return object
* @docment https://docs.gitlab.com/ee/api/jobs.html#get-a-single-job
*/
@@ -788,9 +931,9 @@ class gitlabModel extends model
/**
* Get a log file by api.
*
* @param integer $gitlabID
* @param integer $projectID
* @param integer $jobID
* @param int $gitlabID
* @param int $projectID
* @param int $jobID
* @return string
* @docment https://docs.gitlab.com/ee/api/jobs.html#get-a-log-file
*/
+1 -1
View File
@@ -42,7 +42,7 @@
<?php foreach ($gitlabList as $id => $gitlab): ?>
<tr class='text' title='<?php if(!$gitlab->isAdminToken) echo $lang->gitlab->tokenLimit;?>'>
<td class='text-center'><?php echo $id;?></td>
<td class='text c-name' title='<?php echo $gitlab->name;?>'><?php echo $gitlab->name;?></td>
<td class='text-c-name' title='<?php echo $gitlab->name;?>'><a class="iframe" data-width="90%" href="<?php echo $this->createLink('gitlab', 'view', "id=$id", '', true); ?>"><?php echo $gitlab->name;?></a></td>
<td class='text' title='<?php echo $gitlab->url;?>'><?php echo $gitlab->url;?></td>
<td class='c-actions text-left'>
<?php
+30
View File
@@ -0,0 +1,30 @@
<?php
/**
* The view file of GitLab 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 dave.li <lichengjun@cnezsoft.com>
* @package GitLab
* @version $Id: view.html.php 4728 2013-05-03 06:14:34Z david18810279601@gmail.com $
* @link http://www.zentao.net
* */
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('sysurl', common::getSysUrl());?>
<div id="mainMenu" class="clearfix">
<div class="btn-toolbar pull-left">
<div class="page-title">
<span class="label label-id"><?php echo $gitlab->id?></span>
<span class="text" title="<?php echo $gitlab->name;?>" style='color: #3c4354'><?php echo $gitlab->name;?></span>
</div>
</div>
</div>
<div id="mainContent" class="main-row">
<div class="main-col col-8">
<div class='cell'><?php include '../../common/view/action.html.php';?></div>
</div>
</div>
<?php include '../../common/view/footer.html.php';?>
+2 -2
View File
@@ -5,8 +5,8 @@ function showPriv(value)
/**
* Control the actions select control for a module.
*
* @param string $module
*
* @param string $module
* @access public
* @return void
*/
+16 -1
View File
@@ -63,6 +63,7 @@ $lang->moduleOrder[205] = 'cron';
$lang->moduleOrder[210] = 'dev';
$lang->moduleOrder[215] = 'message';
$lang->moduleOrder[220] = 'gitlab';
$lang->moduleOrder[225] = 'mr';
$lang->resource = new stdclass();
@@ -1146,6 +1147,7 @@ $lang->resource->gitlab->importIssue = 'importIssue';
$lang->resource->gitlab->delete = 'delete';
$lang->resource->gitlab->bindUser = 'bindUser';
$lang->resource->gitlab->bindProduct = 'bindProduct';
//$lang->resource->gitlab->webhook = 'webhook';
$lang->gitlab->methodOrder[5] = 'browse';
@@ -1154,9 +1156,22 @@ $lang->gitlab->methodOrder[15] = 'edit';
$lang->gitlab->methodOrder[20] = 'importIssue';
$lang->gitlab->methodOrder[30] = 'delete';
$lang->gitlab->methodOrder[35] = 'bindUser';
$lang->gitlab->methodOrder[40] = 'bindProduct';
//$lang->gitlab->methodOrder[45] = 'webhook';
/* merge request. */
$lang->resource->mr = new stdclass();
$lang->resource->mr->create = 'create';
$lang->resource->mr->browse = 'browse';
$lang->resource->mr->edit = 'edit';
$lang->resource->mr->delete = 'delete';
$lang->resource->mr->settings = 'settings';
$lang->mr->methodOrder[10] = 'create';
$lang->mr->methodOrder[15] = 'browse';
$lang->mr->methodOrder[20] = 'edit';
$lang->mr->methodOrder[25] = 'delete';
$lang->mr->methodOrder[35] = 'settings';
/* Git. */
$lang->resource->git = new stdclass();
$lang->resource->git->diff = 'diff';
+1 -1
View File
@@ -10,7 +10,7 @@
* @link http://www.zentao.net
*/
?>
<?php
<?php
include '../../common/view/header.html.php';
if($type == 'byGroup') include 'privbygroup.html.php';
if($type == 'byModule') include 'privbymodule.html.php';
+1
View File
@@ -200,6 +200,7 @@ $lang->install->cronList['moduleName=todo&methodName=createCycle'] = '生
$lang->install->cronList['moduleName=ci&methodName=initQueue'] = '创建周期性任务';
$lang->install->cronList['moduleName=ci&methodName=checkCompileStatus'] = '同步Jenkins任务状态';
$lang->install->cronList['moduleName=ci&methodName=exec'] = '执行Jenkins任务';
$lang->install->cronList['moduleName=mr&methodName=syncMR'] = '定时同步GitLabMR信息';
$lang->install->success = "安装成功";
$lang->install->login = '登录禅道管理系统';
+24 -34
View File
@@ -168,6 +168,8 @@ class job extends control
$repo = $this->loadModel('repo')->getRepoByID($job->repo);
$this->view->repo = $this->loadModel('repo')->getRepoByID($job->repo);
if($repo->SCM == 'Gitlab') $this->view->refList = $this->loadModel('gitlab')->getReferenceOptions($repo->gitlab, $repo->project);
$repoList = $this->repo->getList($this->projectID);
$repoPairs = array(0 => '', $repo->id => $repo->name);
$gitlabRepos = array(0 => '');
@@ -306,49 +308,22 @@ class job extends control
* @access public
* @return void
*/
public function exec($id, $showForm = 'no')
public function exec($id)
{
if($showForm == 'yes' or $_POST)
$job = $this->job->getByID($id);
if(strtolower($job->engine) == 'gitlab')
{
$job = $this->job->getByID($id);
$refList = $this->loadModel('gitlab')->getRefOptions($job->server, $job->pipeline);
if($_POST)
if(!isset($job->reference) or !$job->reference)
{
$reference = new stdclass;
$explodedRefs = explode("::", $refList[$this->post->ref]);
$reference->ref = $explodedRefs[1];
$variables = array();
foreach($this->post->keys as $key => $field)
{
$variable = array();
if(!trim($this->post->values[$key])) continue;
$variable['key'] = $field;
$variable['value'] = $this->post->values[$key];
$variable['variable_type'] = "env_var";
$variables[] = $variable;
}
$reference->variables = $variables;
$compile = $this->job->exec($id, json_encode($reference));
return $this->send(array('result' => 'success', 'message' => $this->lang->job->execSuccess, 'locate' => $this->createLink('compile', 'logs', "buildID={$compile->id}")));
return $this->send(array('result' => 'fail', 'message' => $this->lang->job->setReferenceTips, 'locate' => inlink('edit', "id=$id")));
}
$this->view->title = $this->lang->job->runPipeline;
$this->view->refList = $refList;
$this->view->job = $job;
$this->view->pipelineTips = $this->lang->job->pipelineTips;
return $this->display();
}
$compile = $this->job->exec($id);
if(dao::isError()) die(js::error(dao::getError()));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->app->loadLang('compile');
echo js::alert(sprintf($this->lang->job->sendExec, zget($this->lang->compile->statusList, $compile->status)));
die(js::reload('parent'));
return $this->send(array('result' => 'success', 'message' => sprintf($this->lang->job->sendExec, zget($this->lang->compile->statusList, $compile->status))));
}
/**
@@ -383,4 +358,19 @@ class job extends control
$productName = $this->loadModel('product')->getByID($repo->product)->name;
die(json_encode(array($productName => $repo->product)));
}
/**
* Ajax get reference list function.
*
* @param int $repoID
* @access public
* @return void
*/
public function ajaxGetRefList($repoID)
{
$repo = $this->loadModel('repo')->getRepoByID($repoID);
if($repo->SCM != 'Gitlab') $this->send(array('result' => 'fail'));
$refList = $this->loadModel('gitlab')->getReferenceOptions($repo->gitlab, $repo->project);
$this->send(array('result' => 'success', 'refList' => $refList));
}
}
+16
View File
@@ -0,0 +1,16 @@
$(document).ready(function()
{
$('.icon-job-exec').parent().click(function()
{
link = $(this).attr('href');
$.getJSON(link, function(response)
{
if(response.result == 'success') bootbox.alert(response.message);
if(response.result != 'success') bootbox.alert(response.message, function()
{
if(typeof(response.locate) == 'string') location.href = response.locate;
});
});
return false;
});
});
+20 -2
View File
@@ -129,19 +129,37 @@ $(document).ready(function()
if($(this).val() == 'gitlab')
{
$('#triggerType').find('[value=schedule]').remove();
$('tr.gitlabRepo').show();
$('tr.commonRepo').hide();
}
else if($('#triggerType').find('[value=schedule]').size() == 0 )
{
$('#triggerType').append(scheduleOption);
$('tr.gitlabRepo').hide();
$('tr.commonRepo').show();
}
});
$('#engine').change();
$('#gitlabRepo').change(function()
{
$('#reference option').remove();
var repoID = $(this).val();
if(repoID > 0)
{
$.getJSON(createLink('job', 'ajaxGetRefList', "repoID=" + repoID), function(response)
{
if(response.result == 'success')
{
$.each(response.refList, function(reference, name)
{
$('#reference').append("<option value='" + reference + "'>" + name + "</option>");
});
}
$('#reference').trigger('chosen:updated');
});
}
});
$('#triggerType').change();
});
+21
View File
@@ -146,6 +146,27 @@ $(document).ready(function()
}
});
$('#gitlabRepo').change(function()
{
$('#reference option').remove();
var repoID = $(this).val();
if(repoID > 0)
{
$.getJSON(createLink('job', 'ajaxGetRefList', "repoID=" + repoID), function(response)
{
if(response.result == 'success')
{
$.each(response.refList, function(reference, name)
{
$('#reference').append("<option value='" + reference + "'>" + name + "</option>");
});
}
$('#reference').trigger('chosen:updated');
});
}
});
$('#engine').change();
$('#jkServer').change();
+1
View File
@@ -86,3 +86,4 @@ $lang->job->pipelineVariables = "变量";
$lang->job->pipelineVariablesKeyPlaceHolder = "输入变量的名称";
$lang->job->pipelineVariablesValuePlaceHolder = "输入变量的值";
$lang->job->pipelineVariablesTips = "指定要在此次运行中使用的变量值。CI/CD设置中指定的值将用作默认值。";
$lang->job->setReferenceTips = "在执行构建前,请先设置代码库的分支信息。";
+153 -78
View File
@@ -20,7 +20,14 @@ class jobModel extends model
*/
public function getByID($id)
{
return $this->dao->select('*')->from(TABLE_JOB)->where('id')->eq($id)->fetch();
$job = $this->dao->select('*')->from(TABLE_JOB)->where('id')->eq($id)->fetch();
if(strtolower($job->engine) == 'gitlab')
{
$pipeline = json_decode($job->pipeline);
$job->project = $pipeline->project;
$job->reference = $pipeline->reference;
}
return $job;
}
/**
@@ -117,7 +124,7 @@ class jobModel extends model
->setDefault('atDay', '')
->add('createdBy', $this->app->user->account)
->add('createdDate', helper::now())
->remove('repoType')
->remove('repoType,reference')
->get();
if($job->engine == 'jenkins')
@@ -128,10 +135,12 @@ class jobModel extends model
if(strtolower($job->engine) == 'gitlab')
{
$repo = $this->loadModel('repo')->getRepoByID($job->gitlabRepo);
$project = zget($repo, 'project');
$job->repo = $job->gitlabRepo;
$repo = $this->loadModel('repo')->getRepoByID($job->repo);
$job->server = (int)zget($repo, 'gitlab', 0);
$job->pipeline = zget($repo, 'project', '');
$job->pipeline = json_encode(array('project' => $project, 'reference' => $this->post->reference));
}
unset($job->jkServer);
@@ -203,7 +212,7 @@ class jobModel extends model
->setIF($this->post->triggerType != 'tag', 'lastTag', '')
->add('editedBy', $this->app->user->account)
->add('editedDate', helper::now())
->remove('repoType')
->remove('repoType,reference')
->get();
if($job->engine == 'jenkins')
@@ -214,10 +223,12 @@ class jobModel extends model
if(strtolower($job->engine) == 'gitlab')
{
$repo = $this->loadModel('repo')->getRepoByID($job->gitlabRepo);
$project = zget($repo, 'project');
$job->repo = $job->gitlabRepo;
$repo = $this->loadModel('repo')->getRepoByID($job->repo);
$job->server = (int)zget($repo, 'gitlab', 0);
$job->pipeline = zget($repo, 'project', '');
$job->pipeline = json_encode(array('project' => $project, 'reference' => $this->post->reference));
}
unset($job->jkServer);
@@ -252,6 +263,7 @@ class jobModel extends model
if(!empty($paramName)) $customParam[$paramName] = $paramValue;
}
unset($job->paramName);
unset($job->paramValue);
unset($job->custom);
@@ -326,102 +338,165 @@ class jobModel extends model
* @access public
* @return string|bool
*/
public function exec($id, $reference = null)
public function exec($id)
{
$job = $this->dao->select('t1.id,t1.name,t1.product,t1.repo,t1.server,t1.pipeline,t1.triggerType,t1.atTime,t1.customParam,t1.engine,t2.name as jenkinsName,t2.url,t2.account,t2.token,t2.password')
->from(TABLE_JOB)->alias('t1')
->leftJoin(TABLE_PIPELINE)->alias('t2')->on('t1.server=t2.id')
->where('t1.id')->eq($id)
->fetch();
if(!$job) return false;
$build = new stdclass();
$build->job = $job->id;
$build->name = $job->name;
$now = helper::now();
$data = new stdclass();
$repo = $this->loadModel('repo')->getRepoById($job->repo);
$data->PARAM_TAG = '';
if($job->triggerType == 'tag')
{
$lastTag = '';
if($repo->SCM == 'Subversion')
{
$dirs = $this->loadModel('svn')->getRepoTags($repo, $job->svnDir);
if($dirs)
{
end($dirs);
$lastTag = current($dirs);
$lastTag = rtrim($repo->path , '/') . '/' . trim($job->svnDir, '/') . '/' . $lastTag;
}
}
else
{
$tags = $this->loadModel('git')->getRepoTags($repo);
if($tags)
{
end($tags);
$lastTag = current($tags);
}
}
if($lastTag)
{
$build->tag = $lastTag;
$this->dao->update(TABLE_JOB)->set('lastTag')->eq($lastTag)->where('id')->eq($job->id)->exec();
$data->PARAM_TAG = $lastTag;
}
}
elseif($job->triggerType == 'schedule')
{
$build->atTime = $job->atTime;
}
$now = helper::now();
/* Save compile data. */
$build = new stdclass();
$build->job = $job->id;
$build->name = $job->name;
$build->createdBy = $this->app->user->account;
$build->createdDate = $now;
$build->updateDate = $now;
if($job->triggerType == 'schedule') $build->atTime = $job->atTime;
if($job->triggerType == 'tag')
{
$lastTag = $this->getLastTagByRepo($repo);
if($lastTag)
{
$build->tag = $lastTag;
$job->lastTag = $lastTag;
$this->dao->update(TABLE_JOB)->set('lastTag')->eq($lastTag)->where('id')->eq($job->id)->exec();
}
}
$this->dao->insert(TABLE_COMPILE)->data($build)->exec();
$compileID = $this->dao->lastInsertId();
$data->ZENTAO_DATA = "compile={$compileID}";
if($job->engine == 'jenkins') $compile = $this->execJenkinsPipeline($job, $repo, $compileID);
if($job->engine == 'gitlab') $compile = $this->execGitlabPipeline($job);
$this->dao->update(TABLE_COMPILE)->data($compile)->where('id')->eq($compileID)->exec();
$this->dao->update(TABLE_JOB)
->set('lastExec')->eq($now)
->set('lastStatus')->eq($compile->status)
->where('id')->eq($job->id)
->exec();
return $compile;
}
/**
* Exec jenkins pipeline.
*
* @param object $job
* @param object $repo
* @param int $compileID
* @access public
* @return object
*/
public function execJenkinsPipeline($job, $repo, $compileID)
{
$pipeline = new stdclass();
$pipeline->PARAM_TAG = '';
$pipeline->ZENTAO_DATA = "compile={$compileID}";
if($job->triggerType == 'tag') $pipeline->PARAM_TAG = $job->lastTag;
/* Add custom parameters to the data. */
foreach(json_decode($job->customParam) as $paramName => $paramValue)
{
$paramValue = str_replace('$zentao_version', $this->config->version, $paramValue);
$paramValue = str_replace('$zentao_account', $this->app->user->account, $paramValue);
$paramValue = str_replace('$zentao_product', $job->product, $paramValue);
$paramValue = str_replace('$zentao_version', $this->config->version, $paramValue);
$paramValue = str_replace('$zentao_account', $this->app->user->account, $paramValue);
$paramValue = str_replace('$zentao_product', $job->product, $paramValue);
$paramValue = str_replace('$zentao_repopath', $repo->path, $paramValue);
$data->$paramName = $paramValue;
$pipeline->$paramName = $paramValue;
}
$url = $this->loadModel('compile')->getBuildUrl($job);
$compile = new stdclass();
$compile->id = $compileID;
if($job->engine == 'jenkins')
{
$url = $this->loadModel('compile')->getBuildUrl($job);
$compile->queue = $this->loadModel('ci')->sendRequest($url->url, $data, $url->userPWD);
$compile->status = $compile->queue ? 'created' : 'create_fail';
}
elseif($job->engine == 'gitlab' and $reference)
{
$pipeline = $this->loadModel('gitlab')->apiCreatePipeline($job->server, $job->pipeline, $reference);
if(empty($pipeline->id))
{
$compile->status = 'create_fail';
}
else
{
$compile->queue = $pipeline->id;
$compile->status = zget($pipeline, 'status', 'create_fail');
}
}
$this->dao->update(TABLE_COMPILE)->data($compile)->where('id')->eq($compileID)->exec();
$this->dao->update(TABLE_JOB)->set('lastExec')->eq($now)->set('lastStatus')->eq($compile->status)->where('id')->eq($job->id)->exec();
$compile->id = $compileID;
$compile->queue = $this->loadModel('ci')->sendRequest($url->url, $pipeline, $url->userPWD);
$compile->status = $compile->queue ? 'created' : 'create_fail';
return $compile;
}
/**
* Exec gitlab pipeline.
*
* @param int $job
* @access public
* @return void
*/
public function execGitlabPipeline($job)
{
$pipeline = json_decode($job->pipeline);
$pipelineParams = new stdclass;
$pipelineParams->ref = $pipeline->reference;
$customParams = json_decode($job->customParam);
$variables = array();
foreach($customParams as $paramName => $paramValue)
{
$variable = array();
$variable['key'] = $paramName;
$variable['value'] = $paramValue;
$variable['variable_type'] = "env_var";
$variables[] = $variable;
}
if(!empty($variables)) $pipelineParams->variables = $variables;
$compile = new stdclass;
$pipeline = $this->loadModel('gitlab')->apiCreatePipeline($job->server, $pipeline->project, $pipelineParams);
if(empty($pipeline->id)) $compile->status = 'create_fail';
if(!empty($pipeline->id))
{
$compile->queue = $pipeline->id;
$compile->status = zget($pipeline, 'status', 'create_fail');
}
return $compile;
}
/**
* Get last tag of one repo.
*
* @param object $repo
* @access public
* @return void
*/
public function getLastTagByRepo($repo)
{
if($repo->SCM == 'Subversion')
{
$dirs = $this->loadModel('svn')->getRepoTags($repo, $job->svnDir);
if($dirs)
{
end($dirs);
$lastTag = current($dirs);
return rtrim($repo->path , '/') . '/' . trim($job->svnDir, '/') . '/' . $lastTag;
}
}
else
{
$tags = $this->loadModel('git')->getRepoTags($repo);
if($tags)
{
end($tags);
return current($tags);
}
}
return '';
}
}
+9 -3
View File
@@ -50,13 +50,20 @@
</thead>
<tbody>
<?php foreach($jobList as $id => $job):?>
I <?php
if(strtolower($job->engine) == 'gitlab')
{
$pipeline = json_decode($job->pipeline);
if(is_numeric($job->pipeline)) $job->pipeline = $this->loadModel('gitlab')->getProjectName($job->server, $job->pipeline);
if(isset($pipeline->reference)) $job->pipeline = $this->loadModel('gitlab')->getProjectName($job->server, $pipeline->project);
}
?>
<tr class='text-left'>
<td class='text-center'><?php echo $id;?></td>
<td class='text-left c-name' title='<?php echo $job->name;?>'><?php echo common::hasPriv('job', 'view') ? html::a($this->createLink('job', 'view', "jobID={$job->id}", 'html', true), $job->name, '', "class='iframe' data-width='90%'") : $job->name;?></td>
<td title='<?php echo $job->repoName;?>'><?php echo $job->repoName;?></td>
<td><?php echo zget($lang->job->engineList, $job->engine);?></td>
<td><?php echo zget($lang->job->frameList, $job->frame);?></td>
<?php if(strtolower($job->engine) == 'gitlab') $job->pipeline = $this->loadModel('gitlab')->getObjectNameForJob($job->server, $job->pipeline);?>
<?php $jenkins = urldecode($job->pipeline) . '@' . $job->jenkinsName;?>
<td class='c-name' title='<?php echo $jenkins;?>'><?php echo $jenkins;?></td>
<?php $triggerConfig = $this->job->getTriggerConfig($job);?>
@@ -67,8 +74,7 @@
<?php
common::printIcon('compile', 'browse', "jobID=$id", '', 'list', 'history');
common::printIcon('job', 'edit', "jobID=$id", '', 'list', 'edit');
if(strtolower($job->engine) == 'jenkins') common::printIcon('job', 'exec', "jobID=$id", '', 'list', 'play', 'hiddenwin');
if(strtolower($job->engine) == 'gitlab') common::printIcon('job', 'exec', "jobID=$id&showForm=yes", '', 'list', 'play', '', '', false, "data-toggle='modal' data-type='ajax'");
common::printIcon('job', 'exec', "jobID=$id", '', 'list', 'play');
if(common::hasPriv('job', 'delete')) echo html::a($this->createLink('job', 'delete', "jobID=$id"), '<i class="icon-trash"></i>', 'hiddenwin', "title='{$lang->job->delete}' class='btn'");
?>
</td>
+3 -2
View File
@@ -48,7 +48,8 @@
</tr>
<tr class='gitlabRepo hide'>
<th><?php echo $lang->job->repo; ?></th>
<td><?php echo html::select('gitlabRepo', $gitlabRepos, '', "class='form-control'"); ?></td>
<td> <?php echo html::select('gitlabRepo', $gitlabRepos, '', "class='chosen form-control'");?> </td>
<td> <?php echo html::select('reference', array(), '', "class='chosen form-control'");?> </td>
</tr>
<tr>
<th><?php echo $lang->job->product; ?></th>
@@ -95,7 +96,7 @@
<div class='table-col'><?php echo html::select('jkServer', $jenkinsServerList, '', "class='form-control chosen'"); ?></div>
<div class='table-col'>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->job->pipeline; ?></span>
<span class='input-group-addon'><?php echo $lang->job->pipeline;?></span>
<?php echo html::select('jkTask', array('' => ''), '', "class='form-control chosen'"); ?>
</div>
</div>
+3 -2
View File
@@ -12,7 +12,7 @@
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('repoTypes', $repoTypes)?>
<?php js::set('repoTypes', $repoTypes);?>
<?php js::set('triggerType', $job->triggerType);?>
<?php js::set('pipeline', $job->pipeline);?>
<?php js::set('dirChange', $lang->job->dirChange);?>
@@ -43,7 +43,8 @@
</tr>
<tr class='gitlabRepo hide'>
<th><?php echo $lang->job->repo; ?></th>
<td><?php echo html::select('gitlabRepo', $gitlabRepos, $job->repo, "class='form-control'"); ?></td>
<td> <?php echo html::select('gitlabRepo', $gitlabRepos, $job->repo, "class='chosen form-control'");?> </td>
<td> <?php echo html::select('reference', $refList, $job->reference, "class='chosen form-control'");?> </td>
</tr>
<tr>
<th><?php echo $lang->job->product;?></th>
+1 -1
View File
@@ -55,7 +55,7 @@
</tr>
<tr>
<th><?php echo $lang->job->server;?></th>
<?php if(strtolower($job->engine) == 'gitlab') $job->pipeline = $this->loadModel('gitlab')->getObjectNameForJob($job->server, $job->pipeline);?>
<?php if(strtolower($job->engine) == 'gitlab') $job->pipeline = $this->loadModel('gitlab')->getProjectName($job->server, $job->pipeline);?>
<td><?php echo urldecode($job->pipeline) . '@' . $jenkins->name;?></td>
</tr>
<tr>
+19
View File
@@ -0,0 +1,19 @@
<?php
$config->MR = new stdclass();
$config->MR->create = new stdclass();
$config->MR->create->requiredFields = 'gitlabID,sourceProject,sourceBranch,targetProject,targetBranch,title';
$config->MR->create->skippedFields = 'projectID';
$config->MR->maps = new stdclass;
$config->MR->maps->sync = array();
$config->MR->maps->sync['title'] = 'title|field|';
$config->MR->maps->sync['description'] = 'description|field|';
$config->MR->maps->sync['assignee'] = 'assignees|userPairs|id';
$config->MR->maps->sync['reviewer'] = 'reviewers|userPairs|id';
$config->MR->maps->sync['targetBranch'] = 'target_branch|field|';
$config->MR->maps->sync['sourceBranch'] = 'source_branch|field|';
$config->MR->maps->sync['sourceProject'] = 'source_project_id|field|';
$config->MR->maps->sync['targetProject'] = 'target_project_id|field|';
$config->MR->maps->sync['status'] = 'state|field|';
$config->MR->maps->sync['mergeStatus'] = 'merge_status|field|';
+287
View File
@@ -0,0 +1,287 @@
<?php
class mr extends control
{
/**
* Browse mr.
*
* @param int $objectID
* @param string $orderBy
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function browse($objectID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$MRList = $this->mr->getList($orderBy, $pager);
/* Save current URI to session. */
$this->session->set('mrList', $this->app->getURI(true), 'repo');
/* Sync GitLab MR to ZenTao Database. */
$MRList = $this->mr->batchSyncMR($MRList);
$this->view->title = $this->lang->mr->common . $this->lang->colon . $this->lang->mr->browse;
$this->view->MRList = $MRList;
$this->view->orderBy = $orderBy;
$this->view->objectID = $objectID;
$this->view->pager = $pager;
$this->display();
}
/**
* Create MR function.
*
* @access public
* @return void
*/
public function create()
{
if($_POST)
{
$result = $this->mr->create();
return $this->send($result);
}
$this->view->title = $this->lang->mr->create;
$this->view->gitlabHosts = $this->loadModel('gitlab')->getPairs();
$this->display();
}
/**
* Edit MR function.
*
* @access public
* @return void
*/
public function edit($MRID)
{
if($_POST)
{
$result = $this->mr->update($MRID);
return $this->send($result);
}
$MR = $this->mr->getByID($MRID);
$branchList = $this->loadModel('gitlab')->getBranches($MR->gitlabID, $MR->targetProject);
$targetBranchList = array();
foreach($branchList as $branch) $targetBranchList[$branch] = $branch;
/* Fetch user list both in Zentao and current GitLab project. */
$bindedUsers = $this->gitlab->getUserIdRealnamePairs($MR->gitlabID);
$rawProjectUsers = $this->gitlab->apiGetProjectUsers($MR->gitlabID, $MR->targetProject);
$users = array();
foreach($rawProjectUsers as $rawProjectUser)
{
if(!empty($bindedUsers[$rawProjectUser->id])) $users[$rawProjectUser->id] = $bindedUsers[$rawProjectUser->id];
}
$gitlabUsers = $this->gitlab->getUserAccountIdPairs($MR->gitlabID);
$this->view->title = $this->lang->mr->edit;
$this->view->MR = $MR;
$this->view->targetBranchList = $targetBranchList;
$this->view->users = array("" => "") + $users;
$this->view->assignee = zget($gitlabUsers, $MR->assignee, '');
$this->view->reviewer = zget($gitlabUsers, $MR->reviewer, '');
$this->display();
}
/**
* Delete a MR.
*
* @param int $id
* @access public
* @return void
*/
public function delete($id, $confim = 'no')
{
if($confim != 'yes') die(js::confirm($this->lang->gitlab->confirmDelete, inlink('delete', "id=$id&confirm=yes")));
$MR = $this->mr->getByID($id);
$this->dao->delete()->from(TABLE_MR)->where('id')->eq($id)->exec();
$this->mr->apiDeleteMR($MR->gitlabID, $MR->sourceProject, $MR->mriid);
die(js::locate(inlink('browse'), 'parent'));
}
/**
* View a MR.
*
* @access public
* @return void
*/
public function view($id)
{
$MR = $this->mr->getByID($id);
if(isset($MR->gitlabID)) $rawMR = $this->mr->apiGetSingleMR($MR->gitlabID, $MR->targetProject, $MR->mriid);
$this->view->title = $this->lang->mr->view;
$this->view->MR = $MR;
$this->view->rawMR = isset($rawMR) ? $rawMR : false;
$this->loadModel('gitlab');
$sourceProject = $this->gitlab->apiGetSingleProject($MR->gitlabID, $MR->sourceProject);
$targetProject = $this->gitlab->apiGetSingleProject($MR->gitlabID, $MR->targetProject);
$sourceBranch = $this->gitlab->apiGetSingleBranch($MR->gitlabID, $MR->sourceProject, $MR->sourceBranch);
$targetBranch = $this->gitlab->apiGetSingleBranch($MR->gitlabID, $MR->targetProject, $MR->targetBranch);
$this->view->sourceProjectName = $sourceProject->name_with_namespace;
$this->view->targetProjectName = $targetProject->name_with_namespace;
$this->view->sourceProjectURL = $sourceBranch ->web_url;
$this->view->targetProjectURL = $targetBranch ->web_url;
/* Those variables are used to render $lang->mr->commandDocument. */
$this->view->httpRepoURL = $sourceProject->http_url_to_repo;
$this->view->branchPath = $sourceProject->path_with_namespace . '-' . $rawMR->source_branch;
$this->display();
}
/**
* Crontab sync MR from GitLab API to Zentao database, default time 5 minutes to execute once.
*
* @access public
* @return void
*/
public function syncMR()
{
$MRList = $this->mr->getList();
$this->mr->batchSyncMR($MRList);
if(dao::isError())
{
echo json_encode(dao::getError());
return true;
}
echo 'success';
}
/**
* Accept a MR.
*
* @param int $MRID
* @access public
* @return void
*/
public function accept($MRID)
{
$MR = $this->mr->getByID($MRID);
/* Accept MR by using the mapped user in GitLab. */
$sudoUser = $this->mr->getSudoUsername($MR->gitlabID, $MR->targetProject);
if(isset($MR->gitlabID))
{
if(!empty($sudoUser)) $rawMR = $this->mr->apiAcceptMR($MR->gitlabID, $MR->targetProject, $MR->mriid, $sudo = $sudoUser);
if(empty($sudoUser)) $rawMR = $this->mr->apiAcceptMR($MR->gitlabID, $MR->targetProject, $MR->mriid);
}
if(isset($rawMR->state) and $rawMR->state == 'merged')
{
/* Force reload when locate to the url. */
$random = uniqid();
return $this->send(array('result' => 'success', 'message' => $this->lang->mr->mergeSuccess, 'locate' => helper::createLink('mr', 'browse', "random={$random}")));
}
/* The type of variable `$rawMR->message` is string. This is different with apiCreateMR. */
if(isset($rawMR->message)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->mr->apiError->sudo, $rawMR->message), 'locate' => helper::createLink('mr', 'view', "mr={$MRID}")));
return $this->send(array('result' => 'fail', 'message' => $this->lang->mr->mergeFailed, 'locate' => helper::createLink('mr', 'view', "mr={$MRID}")));
}
/**
* AJAX: Get MR target projects.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return void
*/
public function ajaxGetMRTargetProjects($gitlabID, $projectID)
{
$this->loadModel('gitlab');
/* First step: get forks. Only get first level forks(not recursively). */
$projects = $this->gitlab->apiGetForks($gitlabID, $projectID);
/* Second step: get project itself. */
$projects[] = $this->gitlab->apiGetSingleProject($gitlabID, $projectID);
/* Last step: find its upstream recursively. */
$project = $this->gitlab->apiGetUpstream($gitlabID, $projectID);
if(!empty($project)) $projects[] = $project;
while(!empty($project) and isset($project->id))
{
$project = $this->gitlab->apiGetUpstream($gitlabID, $project->id);
if(empty($project)) break;
$projects[] = $project;
}
if(!$projects) return $this->send(array('message' => array()));
$options = "<option value=''></option>";
foreach($projects as $project)
{
$options .= "<option value='{$project->id}' data-name='{$project->name}'>{$project->name_with_namespace}</option>";
}
$this->send($options);
}
/**
* View diff between MR source and target branches.
*
* @param int $MRID
* @access public
* @return void
*/
public function diff($MRID)
{
$MR = $this->mr->getByID($MRID);
$diffs = $this->mr->getDiffs($MR);
$arrange = $this->cookie->arrange ? $this->cookie->arrange : 'inline';
if($this->server->request_method == 'POST')
{
if($this->post->arrange)
{
$arrange = $this->post->arrange;
setcookie('arrange', $arrange);
}
if($this->post->encoding) $encoding = $this->post->encoding;
}
if($arrange == 'appose')
{
foreach($diffs as $diffFile)
{
if(empty($diffFile->contents)) continue;
foreach($diffFile->contents as $content)
{
$old = array();
$new = array();
foreach($content->lines as $line)
{
if($line->type != 'new') $old[$line->oldlc] = $line->line;
if($line->type != 'old') $new[$line->newlc] = $line->line;
}
$content->old = $old;
$content->new = $new;
}
}
}
$this->view->title = $this->lang->mr->viewDiff;
$this->view->diffs = $diffs;
$this->view->arrange = $arrange;
$this->display();
}
}
+22
View File
@@ -0,0 +1,22 @@
body {padding-bottom: 0px;}
.w-code {width: 48%; word-break: break-all;}
td.code {color: #484848; padding: 0px 3px; white-space: pre-wrap;}
.none {background: #EAF2F5;}
table.diff {margin-bottom: 0px;}
.diff caption {border: 1px solid #e4e4e4; background: #edf3fe; margin: 0; padding: 6px 2px 6px 10px; text-align: left; font-weight: bold; font-size: 13px;}
.diff th, .diff td {border: none;}
.diff th {padding-top: 2px; padding-bottom: 2px;}
.diff .line-new, .diff .line-new {background: #CFC;}
.diff .line-old, .diff .line-old {background: #FCC;}
.diff .line-all, .diff .line-all {background: #FFF;}
.diff .w-num {width: 25px; border-right: 1px solid #E4E4E4; color: #999; border-left: 1px solid #E4E4E4; color: #999; font-weight: normal;}
.repoCode .diff tr .comment-btn .icon-wrapper {left: -23px;}
.repoCode .diff tr.over td.line-all, .repoCode .diff tr.over td.line-all {background: #f8eec7;}
.repoCode .diff tr.over td.line-new, .repoCode .diff tr.over td.line-new {background: #8eff8e;}
.repoCode .diff tr.over td.line-old, .repoCode .diff tr.over td.line-old {background: #f6b2b2;}
.repoCode form > .btn {margin-right: 10px;}
.label-exchange {background-color: #566F7C; cursor: pointer;}
.label-exchange i {padding: 0;}
.btn-download {border-right: none;}
+56
View File
@@ -0,0 +1,56 @@
$(function()
{
$('#gitlabID').change(function()
{
gitlabID = $('#gitlabID').val();
if(gitlabID == '') return false;
url = createLink('repo', 'ajaxgetgitlabprojects', "gitlabID=" + gitlabID);
$.get(url, function(response)
{
$('#sourceProject').html('').append(response);
$('#sourceProject').chosen().trigger("chosen:updated");;
});
});
$('#sourceProject,#targetProject').change(function()
{
sourceProject = $(this).val();
var branchSelect = $(this).parents('td').find('select[name*=Branch]');
branchUrl = createLink('gitlab', 'ajaxgetprojectbranches', "gitlabID=" + gitlabID + "&projectID=" + sourceProject);
$.get(branchUrl, function(response)
{
branchSelect.html('').append(response);
branchSelect.chosen().trigger("chosen:updated");;
});
});
$('#sourceProject').change(function()
{
sourceProject = $(this).val();
projectUrl = createLink('mr', 'ajaxGetMRTargetProjects', "gitlabID=" + gitlabID + "&projectID=" + sourceProject);
$.get(projectUrl, function(response)
{
$('#targetProject').html('').append(response);
$('#targetProject').chosen().trigger("chosen:updated");;
});
});
$('#targetProject').change(function()
{
targetProject = $(this).val();
var assignee = $("#assignee").parents('td').find('select[name*=assignee]');
var reviewer = $("#reviewer").parents('td').find('select[name*=reviewer]');
usersUrl = createLink('gitlab', 'ajaxgetmruserpairs', "gitlabID=" + gitlabID + "&projectID=" + targetProject);
$.get(usersUrl, function(response)
{
assignee.html('').append(response);
assignee.chosen().trigger("chosen:updated");;
reviewer.html('').append(response);
reviewer.chosen().trigger("chosen:updated");;
});
});
});
+12
View File
@@ -0,0 +1,12 @@
$(document).ready(function()
{
$("#inline").click(function(){$('#arrange').val('inline');this.form.submit();});
$("#appose").click(function(){$('#arrange').val('appose');this.form.submit();});
$(".label-exchange").click(function(){ $('#exchange').submit();});
});
function changeEncoding(encoding)
{
$('#encoding').val(encoding);
$('#encoding').parents('form').submit();
}
+17
View File
@@ -0,0 +1,17 @@
$(document).ready(function()
{
$('#mergeButton').click(function()
{
link = $(this).attr('href');
$.getJSON(link, function(response)
{
if(response.result == 'success')
{
$.zui.messager.success(response.message);
setTimeout(function(){ location.href=response.locate }, 2500);
}
if(response.result == 'fail') $.zui.messager.danger(response.message);
});
return false;
});
});
+111
View File
@@ -0,0 +1,111 @@
<?php
$lang->mr->common = "Merge Request";
$lang->mr->create = "create";
$lang->mr->browse = "browse";
$lang->mr->list = "list";
$lang->mr->edit = "edit";
$lang->mr->delete = "delete";
$lang->mr->view = "view";
$lang->mr->source = 'source';
$lang->mr->target = 'target';
$lang->mr->viewDiff = 'view diff';
$lang->mr->viewInGitlab = 'view in GitLab';
$lang->mr->id = 'ID';
$lang->mr->mriid = "raw MR ID";
$lang->mr->name = 'Name';
$lang->mr->status = 'Status';
$lang->mr->author = 'Author';
$lang->mr->assignee = 'Assignee';
$lang->mr->reviewer = 'Reviewer';
$lang->mr->mergeStatus = 'Merge status';
$lang->mr->commits = 'commits';
$lang->mr->changes = 'changes';
$lang->mr->statusList = array();
$lang->mr->statusList['opened'] = 'opened';
$lang->mr->statusList['closed'] = 'closed';
$lang->mr->statusList['merged'] = 'merged';
$lang->mr->mergeStatusList = array();
$lang->mr->mergeStatusList['checking'] = 'checking';
$lang->mr->mergeStatusList['can_be_merged'] = 'can be merged';
$lang->mr->mergeStatusList['cannot_be_merged'] = 'cannot be merged';
$lang->mr->description = 'Description';
$lang->mr->confirmDelete = 'Are you sure to delete this merge request?';
$lang->mr->sourceProject = 'Source project';
$lang->mr->sourceBranch = 'Source branch';
$lang->mr->targetProject = 'Target project';
$lang->mr->targetBranch = 'Target branch';
$lang->mr->usersTips = 'Tip: If you cannot choose the assignee and reviewer, please go to the GitLab page to bind the user first.';
$lang->mr->notFound = "Merge Request does not exist!";
$lang->mr->apiError = new stdclass;
$lang->mr->apiError->createMR = "Failed to create a merge request through API. Reason: %s";
$lang->mr->apiError->sudo = "Unable to operate with the GitLab account bound to the current user. Reason: %s";
$lang->mr->createFailedFromAPI = "Failed to create Merge Request.";
$lang->mr->accessGitlabFailed = "Unable to connect to the GitLab server.";
$lang->mr->from = "from";
$lang->mr->to = "to";
$lang->mr->at = "at";
$lang->mr->pipeline = "Pipeline";
$lang->mr->pipelineSuccess = "Success";
$lang->mr->pipelineFailed = "Failed";
$lang->mr->pipelineCancled = "Canceled";
$lang->mr->pipelineUnknown = "Unknown";
$lang->mr->pipelineStatus = array();
$lang->mr->pipelineStatus['success'] = "success";
$lang->mr->pipelineStatus['failed'] = "failed";
$lang->mr->pipelineStatus['canceled'] = "canceled";
$lang->mr->MRHasConflicts = "Merge Request has a conflict";
$lang->mr->hasConflicts = "There are merge conflicts or wait for push";
$lang->mr->hasNoConflict = "Can merge";
$lang->mr->mergeByManual = "This merge request can be merged manually, please refer to";
$lang->mr->commandLine = "Merge Request command";
$lang->mr->acceptMR = "Accept Merge request ";
$lang->mr->mergeFailed = "Unable to merge request, please check the merge request status";
$lang->mr->mergeSuccess = "Merge Request Successfully";
/**
* Merge Command Document.
*
* %s source_project::http_url_to_repo
* %s mr::source_branch
* %s source_project::path_with_namespace . '-' . mr::source_branch
* %s mr::target_branch
* %s source_project::path_with_namespace . '-' . mr::source_branch
* %s mr::target_branch
*/
$lang->mr->commandDocument = <<< EOD
<div class='detail-title'>Check out, review and merge locally</div>
<div class='detail-content'>
<p><strong>Note: This merge request status will be changed after you merge locally and you will need to delete this merge request or submit new code.</strong></p>
<p>
step 1. Fetch and check out the branch for this merge request
<pre>
git fetch "%s" %s
git checkout -b "%s" FETCH_HEAD</pre>
</p>
<p>
step 2. Review the changes locally
</p>
<p>
step 3. Merge the branch and fix any conflicts that come up
<pre>
git fetch origin
git checkout "%s"
git merge --no-ff "%s"</pre>
</p>
<p>
step 4. Push the result of the merge to GitLab
<pre> git push origin "%s" </pre>
</p>
</div>
EOD;
+112
View File
@@ -0,0 +1,112 @@
<?php
$lang->mr->common = "合并请求";
$lang->mr->create = "创建{$lang->mr->common}";
$lang->mr->browse = "浏览{$lang->mr->common}";
$lang->mr->list = $lang->mr->browse;
$lang->mr->edit = "编辑{$lang->mr->common}";
$lang->mr->delete = "删除{$lang->mr->common}";
$lang->mr->view = "{$lang->mr->common}详情";
$lang->mr->source = '源项目分支';
$lang->mr->target = '目标项目分支';
$lang->mr->viewDiff = '比对代码';
$lang->mr->viewInGitlab = '在GitLab查看';
$lang->mr->id = 'ID';
$lang->mr->mriid = "MR原始ID";
$lang->mr->name = '名称';
$lang->mr->status = '状态';
$lang->mr->author = '创建人';
$lang->mr->assignee = '指派给';
$lang->mr->reviewer = '评审人';
$lang->mr->mergeStatus = '是否可合并';
$lang->mr->commits = '提交数';
$lang->mr->changes = '更改数';
$lang->mr->settings = '合并代码设置';
$lang->mr->statusList = array();
$lang->mr->statusList['opened'] = '开放中';
$lang->mr->statusList['closed'] = '已关闭';
$lang->mr->statusList['merged'] = '已合并';
$lang->mr->mergeStatusList = array();
$lang->mr->mergeStatusList['checking'] = '检查中';
$lang->mr->mergeStatusList['can_be_merged'] = '可合并';
$lang->mr->mergeStatusList['cannot_be_merged'] = '不可自动合并';
$lang->mr->description = '描述';
$lang->mr->confirmDelete = '确认删除该合并请求吗?';
$lang->mr->sourceProject = '源项目';
$lang->mr->sourceBranch = '源分支';
$lang->mr->targetProject = '目标项目';
$lang->mr->targetBranch = '目标分支';
$lang->mr->usersTips = '提示:如果无法选择指派人和评审人,请先前往GitLab页面绑定用户。';
$lang->mr->notFound = "此{$lang->mr->common}不存在。";
$lang->mr->apiError = new stdclass;
$lang->mr->apiError->createMR = "通过API创建合并请求失败,失败原因:%s";
$lang->mr->apiError->sudo = "无法以当前用户绑定的GitLab账户进行操作,失败原因:%s";
$lang->mr->createFailedFromAPI = "创建合并请求失败。";
$lang->mr->accessGitlabFailed = "当前无法连接到GitLab服务器。";
$lang->mr->from = "从";
$lang->mr->to = "合并到";
$lang->mr->at = "于";
$lang->mr->pipeline = "流水线";
$lang->mr->pipelineSuccess = "已通过";
$lang->mr->pipelineFailed = "未通过";
$lang->mr->pipelineCancled = "已取消";
$lang->mr->pipelineUnknown = "未知";
$lang->mr->pipelineStatus = array();
$lang->mr->pipelineStatus['success'] = "已通过";
$lang->mr->pipelineStatus['failed'] = "未通过";
$lang->mr->pipelineStatus['canceled'] = "已取消";
$lang->mr->MRHasConflicts = "是否存在冲突";
$lang->mr->hasConflicts = "存在冲突或等待提交";
$lang->mr->hasNoConflict = "可以合并";
$lang->mr->mergeByManual = "此合并请求可以手动合并,请使用以下";
$lang->mr->commandLine = "合并命令";
$lang->mr->acceptMR = "合并";
$lang->mr->mergeFailed = "无法合并,请核对合并请求状态";
$lang->mr->mergeSuccess = "已成功合并";
/**
* Merge Command Document.
*
* %s source_project::http_url_to_repo
* %s mr::source_branch
* %s source_project::path_with_namespace . '-' . mr::source_branch
* %s mr::target_branch
* %s source_project::path_with_namespace . '-' . mr::source_branch
* %s mr::target_branch
*/
$lang->mr->commandDocument = <<< EOD
<div class='detail-title'>在本地检出、审核和手动合并</div>
<div class='detail-content'>
<p><strong>注意:您在本地合并后此合并请求将变为不可合并状态,需要删除此合并请求或者提交新的代码。</strong></p>
<p>
第 1 步. 获取并查看此合并请求的分支
<pre>
git fetch "%s" %s
git checkout -b "%s" FETCH_HEAD</pre>
</p>
<p>
第 2 步. 在本地查看更改
</p>
<p>
第 3 步. 合并分支并解决出现的任何冲突
<pre>
git fetch origin
git checkout "%s"
git merge --no-ff "%s"</pre>
</p>
<p>
第 4 步. 将合并结果推送到GitLab
<pre> git push origin "%s" </pre>
</p>
</div>
EOD;
+490
View File
@@ -0,0 +1,490 @@
<?php
/**
* The model file of mr module of ZenTaoPMS.
*
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author dingguodong <dingguodong@easycorp.ltd>
* @package mr
* @version $Id$
* @link http://www.zentao.net
*/
class mrModel extends model
{
/**
* The construct method, to do some auto things.
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
$this->loadModel('gitlab');
}
/**
* Get a MR by id.
*
* @param int $id
* @access public
* @return object
*/
public function getByID($id)
{
return $this->dao->findByID($id)->from(TABLE_MR)->fetch();
}
/**
* Get MR list of gitlab project.
*
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getList($orderBy = 'id_desc', $pager = null)
{
$MRList = $this->dao->select('*')
->from(TABLE_MR)
->where('deleted')->eq('0')
->orderBy($orderBy)
->page($pager)
->fetchAll('id');
return $MRList;
}
/**
* Get gitlab pairs.
*
* @access public
* @return array
*/
public function getPairs($repoID)
{
$MR = $this->dao->select('id,title')
->from(TABLE_MR)
->where('deleted')->eq('0')
->AndWhere('repoID')->eq($repoID)
->orderBy('id')->fetchPairs('id', 'title');
return array('' => '') + $MR;
}
/**
* Create MR function.
*
* @access public
* @return int|bool|object
*/
public function create()
{
$MR = fixer::input('post')
->add('createdBy', $this->app->user->account)
->add('createdDate', helper::now())
->get();
$this->dao->insert(TABLE_MR)->data($MR, $this->config->MR->create->skippedFields)
->batchCheck($this->config->MR->create->requiredFields, 'notempty')
->autoCheck()
->exec();
if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
$MRID = $this->dao->lastInsertId();
$MRObject = new stdclass;
$MRObject->target_project_id = $MR->targetProject;
$MRObject->source_branch = $MR->sourceBranch;
$MRObject->target_branch = $MR->targetBranch;
$MRObject->title = $MR->title;
$MRObject->description = $MR->description;
$MRObject->assignee_ids = $MR->assignee;
$MRObject->reviewer_ids = $MR->reviewer;
$rawMR = $this->apiCreateMR($this->post->gitlabID, $this->post->sourceProject, $MRObject);
/**
* Another open merge request already exists for this source branch.
* The type of variable `$rawMR->message` is array.
*/
if(isset($rawMR->message) and !isset($rawMR->iid))
{
$this->dao->delete()->from(TABLE_MR)->where('id')->eq($MRID)->exec();
return array('result' => 'fail', 'message' => sprintf($this->lang->mr->apiError->createMR, $rawMR->message[0]));
}
/* Create MR failed. */
if(!isset($rawMR->iid))
{
$this->dao->delete()->from(TABLE_MR)->where('id')->eq($MRID)->exec();
return array('result' => 'fail', 'message' => $this->lang->mr->createFailedFromAPI);
}
$newMR = new stdclass;
$newMR->mriid = $rawMR->iid;
$newMR->status = $rawMR->state;
$newMR->mergeStatus = $rawMR->merge_status;
/* Change gitlab user ID to zentao account. */
$gitlabUsers = $this->gitlab->getUserIdAccountPairs($MR->gitlabID);
$newMR->assignee = zget($gitlabUsers, $MR->assignee, '');
$newMR->reviewer = zget($gitlabUsers, $MR->reviewer, '');
/* Update MR in Zentao database. */
$this->dao->update(TABLE_MR)->data($newMR)
->where('id')->eq($MRID)
->autoCheck()
->exec();
if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => helper::createLink('mr', 'browse'));
}
/**
* Edit MR function.
*
* @access public
* @return void
*/
public function update($MRID)
{
$MR = fixer::input('post')
->setDefault('editedBy', $this->app->user->account)
->setDefault('editedDate', helper::now())
->get();
/* Update MR in GitLab. */
$newMR = new stdclass;
$newMR->title = $MR->title;
$newMR->description = $MR->description;
$newMR->assignee_ids = $MR->assignee;
$newMR->reviewer_ids = $MR->reviewer;
$newMR->target_branch = $MR->targetBranch;
$oldMR = $this->getByID($MRID);
/* Known issue: `reviewer_ids` takes no effect. */
$rawMR = $this->apiUpdateMR($oldMR->gitlabID, $oldMR->targetProject, $oldMR->mriid, $newMR);
/* Change gitlab user ID to zentao account. */
$gitlabUsers = $this->gitlab->getUserIdAccountPairs($oldMR->gitlabID);
$MR->assignee = zget($gitlabUsers, $MR->assignee, '');
$MR->reviewer = zget($gitlabUsers, $MR->reviewer, '');
/* Update MR in Zentao database. */
$this->dao->update(TABLE_MR)->data($MR)
->where('id')->eq($MRID)
->autoCheck()
->exec();
$MR = $this->getByID($MRID);
if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError());
return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => helper::createLink('mr', 'browse'));
}
/**
* sync MR from GitLab API to Zentao database.
*
* @param object $MR
* @access public
* @return void
*/
public function apiSyncMR($MR)
{
$rawMR = $this->apiGetSingleMR($MR->gitlabID, $MR->targetProject, $MR->mriid);
/* Sync MR in ZenTao database whatever status of MR in GitLab. */
if(isset($rawMR->iid))
{
$map = $this->config->MR->maps->sync;
$gitlabUsers = $this->gitlab->getUserIdAccountPairs($MR->gitlabID);
$newMR = new stdclass;
foreach($map as $syncField => $config)
{
$value = '';
list($field, $optionType, $options) = explode('|', $config);
if($optionType == 'field') $value = $rawMR->$field;
if($optionType == 'userPairs')
{
$gitlabUserID = '';
if(isset($rawMR->$field[0]))
{
$gitlabUserID = $rawMR->$field[0]->$options;
}
$value = zget($gitlabUsers, $gitlabUserID, '');
}
if($value) $newMR->$syncField = $value;
}
/* Update MR in Zentao database. */
$this->dao->update(TABLE_MR)->data($newMR)
->where('id')->eq($MR->id)
->exec();
}
return $this->dao->findByID($MR->id)->from(TABLE_MR)->fetch();
}
/**
* Batch Sync GitLab MR Database.
*
* @param object $MRList
* @access public
* @return void
*/
public function batchSyncMR($MRList)
{
if(!empty($MRList)) foreach($MRList as $key => $MR)
{
if($MR->status != 'opened') continue;
$rawMR = $this->apiGetSingleMR($MR->gitlabID, $MR->targetProject, $MR->mriid);
if(isset($rawMR->iid))
{
/* create gitlab mr todo to zentao todo */
$this->batchSyncTodo($MR->gitlabID, $MR->targetProject);
$map = $this->config->MR->maps->sync;
$gitlabUsers = $this->gitlab->getUserIdAccountPairs($MR->gitlabID);
$newMR = new stdclass;
foreach($map as $syncField => $config)
{
$value = '';
list($field, $optionType, $options) = explode('|', $config);
if($optionType == 'field') $value = $rawMR->$field;
if($optionType == 'userPairs')
{
$gitlabUserID = '';
if(isset($rawMR->$field[0]))
{
$gitlabUserID = $rawMR->$field[0]->$options;
}
$value = zget($gitlabUsers, $gitlabUserID, '');
}
if($value) $newMR->$syncField = $value;
}
/* Update MR in Zentao database. */
$this->dao->update(TABLE_MR)->data($newMR)
->where('id')->eq($MR->id)
->exec();
/* Refetch MR in Zentao database. */
$MR = $this->dao->findByID($MR->id)->from(TABLE_MR)->fetch();
$MRList[$key] = $MR;
}
}
return $MRList;
}
/**
* Sync GitLab Todo to ZenTao Todo.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return void
*/
public function batchSyncTodo($gitlabID, $projectID)
{
$todoList = $this->loadModel('gitlab')->apiTodoList($gitlabID, $projectID);
if(!empty($todoList))
{
foreach($todoList as $do)
{
$todoDesc = $this->dao->select('*')
->from(TABLE_TODO)
->where('idvalue')->eq($do->id)
->fetch();
if(empty($todoDesc))
{
$todo = new stdClass;
$todo->account = $this->app->user->account;
$todo->assignedTo = $this->app->user->account;
$todo->assignedBy = $this->app->user->account;
$todo->date = $do->target->created_at;
$todo->assignedDate = $do->target->created_at;
$todo->begin = $do->target->created_at;
$todo->type = 'mrapprove';
$todo->idvalue = $do->id;
$todo->pri = 1;
$todo->name = $do->target->title;
$todo->desc = $do->target->description . "<br>" . '<a href="' . $this->todoDescriptionLink($gitlabID, $projectID) . '"target="_blank">' . $this->todoDescriptionLink($gitlabID, $projectID) .'</a>';
$todo->finishedBy = $this->app->user->account;
$this->dao->insert(TABLE_TODO)->data($todo)->exec();
}
}
}
}
/**
* Get a list of to-do items.
*
* @param int $gitlabID
* @param int $projectID
* @access public
* @return object
*/
public function todoDescriptionLink($gitlabID, $projectID)
{
$gitlab = $this->loadModel('gitlab')->getByID($gitlabID);
if(!$gitlab) return '';
return rtrim($gitlab->url, '/')."/dashboard/todos?project_id=$projectID&type=MergeRequest";
}
/**
* Create MR by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#create-mr
* @param int $gitlabID
* @param int $projectID
* @param object $MR
* @access public
* @return object
*/
public function apiCreateMR($gitlabID, $projectID, $MR)
{
$url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests");
return json_decode(commonModel::http($url, $MR));
}
/**
* Get MR list by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#list-project-merge-requests
* @param int $gitlabID
* @param int $projectID
* @access public
* @return object
*/
public function apiGetMRList($gitlabID, $projectID)
{
$url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests");
return json_decode(commonModel::http($url));
}
/**
* Get single MR by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr
* @param int $gitlabID
* @param int $projectID targetProject
* @param int $MRID
* @access public
* @return object
*/
public function apiGetSingleMR($gitlabID, $projectID, $MRID)
{
$url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests/$MRID");
return json_decode(commonModel::http($url));
}
/**
* Update MR by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#update-mr
* @param int $gitlabID
* @param int $projectID
* @param int $MRID
* @param object $MR
* @access public
* @return object
*/
public function apiUpdateMR($gitlabID, $projectID, $MRID, $MR)
{
$url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests/$MRID");
return json_decode(commonModel::http($url, $MR, $options = array(CURLOPT_CUSTOMREQUEST => 'PUT')));
}
/**
* Delete MR by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#delete-a-merge-request
* @param int $gitlabID
* @param int $projectID
* @param int $MRID
* @access public
* @return object
*/
public function apiDeleteMR($gitlabID, $projectID, $MRID)
{
$url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests/$MRID");
return json_decode(commonModel::http($url, null, array(CURLOPT_CUSTOMREQUEST => 'DELETE')));
}
/**
* Accept MR by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#accept-mr
* @param int $gitlabID
* @param int $projectID
* @param int $MRID
* @param string $sudo
* @access public
* @return object
*/
public function apiAcceptMR($gitlabID, $projectID, $MRID, $sudo = "")
{
$url = sprintf($this->gitlab->getApiRoot($gitlabID), "/projects/$projectID/merge_requests/$MRID/merge");
if($sudo != "") return json_decode(commonModel::http($url, $data = null, $options = array(CURLOPT_CUSTOMREQUEST => 'PUT'), $headers = array("sudo: {$sudo}")));
return json_decode(commonModel::http($url, $data = null, $options = array(CURLOPT_CUSTOMREQUEST => 'PUT')));
}
/**
* Get MR diff versions by API.
*
* @docs https://docs.gitlab.com/ee/api/merge_requests.html#get-mr-diff-versions
* @param object $MR
* @access public
* @return object
*/
public function getDiffs($MR)
{
$gitlab = $this->gitlab->getByID($MR->gitlabID);
$this->loadModel('repo');
$repo = new stdclass;
$repo->SCM = 'GitLab';
$repo->gitlab = $gitlab->id;
$repo->project = $MR->targetProject;
$repo->path = sprintf($this->config->repo->gitlab->apiPath, $gitlab->url, $MR->targetProject);
$repo->client = $gitlab->url;
$repo->password = $gitlab->token;
$scm = $this->app->loadClass('scm');
$scm->setEngine($repo);
$encoding = empty($encoding) ? $repo->encoding : $encoding;
$encoding = strtolower(str_replace('_', '-', $encoding));
return $scm->diff('', $MR->sourceBranch, $MR->targetBranch, $parse = true, $MR->sourceProject);
}
/**
* Get sudo user ID in both GitLab and Project.
* Note: sudo parameter in GitLab API can be user ID or username.
* @param int $gitlabID
* @param int $projectID
* @access public
* @return int|string
*/
public function getSudoUsername($gitlabID, $projectID)
{
$zentaoUser = $this->app->user->account;
/* Fetch user list both in Zentao and current GitLab project. */
$bindedUsers = $this->gitlab->getUserAccountIdPairs($gitlabID);
$rawProjectUsers = $this->gitlab->apiGetProjectUsers($gitlabID, $projectID);
$users = array();
foreach($rawProjectUsers as $rawProjectUser)
{
if(!empty($bindedUsers[$rawProjectUser->username])) $users[$rawProjectUser->username] = $bindedUsers[$rawProjectUser->username];
}
if(!empty($users[$zentaoUser])) return $users[$zentaoUser];
return "";
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
/**
* The view file for browse page of mr module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Guodong Ding
* @package mr
* @version $Id: create.html.php $
*/
?>
<?php include '../../common/view/header.html.php';?>
<div id="mainMenu" class="clearfix">
<div class='pull-right'>
<?php common::printLink('mr', 'create', '', "<i class='icon icon-plus'></i> " . $lang->mr->create, '', "class='btn btn-primary'");?>
</div>
</div>
<div id='mainContent'>
<?php if(empty($MRList)):?>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->noData . $lang->mr->common;?></span>
<?php if(common::hasPriv('mr', 'create')):?>
<?php echo html::a($this->createLink('mr', 'create'), "<i class='icon icon-plus'></i> " . $lang->mr->create, '', "class='btn btn-info'");?>
<?php endif;?>
</p>
</div>
<?php else: ?>
<form class='main-table' id='ajaxForm' method='post'>
<table id='gitlabProjectList' class='table has-sort-head table-fixed'>
<thead>
<tr>
<?php $vars = "objectID=$objectID&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}"; ?>
<th class='w-60px text-left'><?php common::printOrderLink('id', $orderBy, $vars, $lang->mr->id); ?></th>
<th class='text-left'><?php common::printOrderLink('title', $orderBy, $vars, $lang->mr->name); ?></th>
<th class='text-left'><?php common::printOrderLink('sourceProject', $orderBy, $vars, $lang->mr->sourceProject); ?></th>
<th class='w-100px text-left'><?php common::printOrderLink('sourceBranch', $orderBy, $vars, $lang->mr->sourceBranch); ?></th>
<th class='text-left'><?php common::printOrderLink('targetProject', $orderBy, $vars, $lang->mr->targetProject); ?></th>
<th class='w-100px text-left'><?php common::printOrderLink('targetBranch', $orderBy, $vars, $lang->mr->targetBranch); ?></th>
<th class='w-100px text-left'><?php common::printOrderLink('mergeStatus', $orderBy, $vars, $lang->mr->mergeStatus); ?></th>
<th class='w-120px c-actions-4'><?php echo $lang->actions; ?></th>
</tr>
</thead>
<tbody>
<?php foreach($MRList as $MR):?>
<tr>
<td class='text'><?php echo $MR->id; ?></td>
<td class='text'><?php echo $MR->title; ?></td>
<td class='text'><?php echo $this->loadModel('gitlab')->apiGetSingleProject($MR->gitlabID, $MR->sourceProject)->name_with_namespace; ?></td>
<td class='text'><?php echo $MR->sourceBranch;?></td>
<td class='text'><?php echo $this->loadModel('gitlab')->apiGetSingleProject($MR->gitlabID, $MR->targetProject)->name_with_namespace; ?></td>
<td class='text'><?php echo $MR->targetBranch;?></td>
<td class='text'><?php echo ($MR->status == 'merged') ? zget($lang->mr->statusList, $MR->status) : zget($lang->mr->mergeStatusList, $MR->mergeStatus); ?></td>
<td class='text-left c-actions'>
<?php
common::printLink('mr', 'view', "mr={$MR->id}", '<i class="icon icon-eye"></i>', '', "title='{$lang->mr->view}' class='btn btn-info'");
common::printLink('mr', 'edit', "mr={$MR->id}", '<i class="icon icon-edit"></i>', '', "title='{$lang->mr->edit}' class='btn btn-info'");
/* Function diff is not ready yet. so comment it. */
//common::printLink('mr', 'diff', "mr={$MR->id}", '<i class="icon icon-review"></i>', '', "title='{$lang->mr->viewDiff}' class='btn btn-info'");
common::printLink('mr', 'delete', "mr={$MR->id}", '<i class="icon icon-trash"></i>', '', "title='{$lang->mr->delete}' class='btn btn-info'");
?>
</td>
</tr>
<?php endforeach;?>
</tbody>
</table>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
</form>
<?php endif;?>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+76
View File
@@ -0,0 +1,76 @@
<?php
/**
* The create view file of mr module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Guodong
* @package mr
* @version $Id: create.html.php $
*/
?>
<?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->mr->create;?></h2>
</div>
<form id='mrForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->gitlab->common;?></th>
<td class='required'><?php echo html::select('gitlabID', $gitlabHosts, '', "class='form-control'");?></td>
</tr>
<tr>
<th><?php echo $lang->mr->sourceProject;?></th>
<td class='required'>
<div class='input-group'>
<?php echo html::select('sourceProject', array(''), '', "class='form-control chosen'");?>
<span class='input-group-addon fix-border'><?php echo $lang->mr->sourceBranch ?></span>
<?php echo html::select('sourceBranch', array(''), '', "class='form-control chosen'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->mr->targetProject;?></th>
<td class='required'>
<div class='input-group'>
<?php echo html::select('targetProject', array(''), '', "class='form-control chosen'");?>
<span class='input-group-addon fix-border'><?php echo $lang->mr->targetBranch ?></span>
<?php echo html::select('targetBranch', array(''), '', "class='form-control chosen'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->mr->name;?></th>
<td class='required'><?php echo html::input('title', '', "class='form-control'"); ?></td>
</tr>
<tr>
<th><?php echo $lang->mr->description; ?></th>
<td colspan='1'><?php echo html::textarea('description', '', "rows='3' class='form-control'"); ?></td>
</tr>
<tr>
<th><?php echo $lang->mr->assignee;?></th>
<td><?php echo html::select('assignee', array(''), '', "class='form-control chosen'")?></td>
</tr>
<tr>
<th><?php echo $lang->mr->reviewer;?></th>
<td><?php echo html::select('reviewer', array(''), '', "class='form-control chosen'")?></td>
</tr>
<tr>
<th></th>
<td><?php echo $lang->mr->usersTips;?></td>
</tr>
<tr>
<th></th>
<td colspan='2' class='text-left form-actions'>
<?php echo html::submitButton(); ?>
<?php if(!isonlybody()) echo html::a(inlink('browse', ""), $lang->goback, '', 'class="btn btn-wide"');?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+147
View File
@@ -0,0 +1,147 @@
<?php
/**
* The diff view file of repo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Xiying Guan
* @package repo
* @version $Id: browse.html.php $
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php include '../../common/view/form.html.php';?>
<?php if(!isonlybody()):?>
<div id='mainMenu' class='clearfix'>
<div class='btn-toolbar pull-left'>
<?php
$backURI = $this->session->mrView ? $this->session->mrView : $this->session->mrList;
if($backURI)
{
echo html::a($backURI, "<i class='icon icon-back icon-sm'></i> " . $lang->goback, '', "class='btn btn-link' data-app='{$app->openApp}'");
}
else
{
echo html::backButton("<i class='icon icon-back icon-sm'></i> " . $lang->goback, '', "btn btn-link");
}
?>
<div class="divider"></div>
</div>
</div>
<?php endif;?>
<div class="repo panel">
<div class='panel-heading'>
<form method='post'>
<div class='btn-group pull-right'>
<?php echo html::commonButton($lang->repo->viewDiffList['inline'], "id='inline'", $arrange == 'inline' ? 'active btn btn-sm' : 'btn btn-sm')?>
<?php echo html::commonButton($lang->repo->viewDiffList['appose'], "id='appose'", $arrange == 'appose' ? 'active btn btn-sm' : 'btn btn-sm')?>
</div>
<div class='btn-toolbar'>
<div class='btn-group'>
<div class='btn-group'>
<?php echo html::commonButton(zget($lang->repo->encodingList, $encoding, $lang->repo->encoding) . "<span class='caret'></span>", "data-toggle='dropdown'", 'btn dropdown-toggle btn-sm')?>
<ul class='dropdown-menu' role='menu'>
<?php foreach($lang->repo->encodingList as $key => $val):?>
<li><?php echo html::a('javascript:changeEncoding("'. $key . '")', $val)?></li>
<?php endforeach;?>
</ul>
</div>
</div>
</div>
<?php echo html::hidden('arrange', $arrange) . html::hidden('encoding', $encoding) . html::hidden('revision[]', $newRevision) . html::hidden('revision[]', $oldRevision)?>
</form>
</div>
<?php foreach($diffs as $diffFile):?>
<div class='repoCode'>
<table class='table diff' id='diff'>
<caption><?php echo $diffFile->fileName;?></caption>
<?php if(empty($diffFile->contents)) continue;?>
<?php foreach($diffFile->contents as $content):?>
<?php
$oldCurrentLine = $content->oldStartLine;
$newCurrentLine = $content->newStartLine;
?>
<?php if(!in_array($oldCurrentLine, array('0', '1')) and !in_array($newCurrentLine, array('0', '1'))):?>
<tr data-line='<?php echo $newCurrentLine ?>' class='empty'>
<th class='w-num text-center'>...</th>
<?php if($arrange == 'appose'):?>
<td class='none code'></td>
<?php endif;?>
<th class='w-num text-center'>...</th>
<td class='none code'></td>
</tr>
<?php endif?>
<?php if($arrange == 'inline'):?>
<?php foreach($content->lines as $line):?>
<tr data-line='<?php echo $line->newlc ?>'>
<th class='w-num text-right'><?php if($line->type != 'new') echo $line->oldlc?></th>
<th class='w-num text-left'><?php if($line->type != 'old') echo $line->newlc?></th>
<td class='line-<?php echo $line->type?> code'><?php
$line->line = $repo->SCM == 'Subversion' ? htmlspecialchars($line->line) : $line->line;
echo $line->type == 'old' ? preg_replace('/^\-/', '&ndash;', $line->line) : ($line->type == 'new' ? $line->line : ' ' . $line->line);
?></td>
</tr>
<?php endforeach;?>
<?php else:?>
<?php foreach($content->lines as $line):?>
<tr data-line='<?php echo $line->newlc ?>'>
<?php
if($line->type == 'old')
{
$oldlc = $line->oldlc;
$newlc = '';
if(isset($content->new[$oldlc]))
{
$newlc = $line->oldlc;
$line->type = 'custom';
}
}
else
{
$oldlc = $line->oldlc;
$newlc = $line->newlc;
if(!isset($content->new[$newlc])) continue;
}
?>
<th class='w-num text-right'><?php echo $oldlc?></th>
<td class='w-code line-<?php if($line->type != 'new')echo $line->type?> <?php if($line->type == 'custom') echo "line-old"?> code'><?php
if(!isset($content->old[$oldlc])) $content->old[$oldlc] = '';
$content->old[$oldlc] = $repo->SCM == 'Subversion' ? htmlspecialchars($content->old[$oldlc]) : $content->old[$oldlc];
if(!empty($oldlc)) echo $line->type != 'all' ? preg_replace('/^\-/', '&ndash;', $content->old[$oldlc]) : ' ' . $content->old[$oldlc];
?></td>
<th class='w-num text-right'><?php echo $newlc?></th>
<td class='w-code line-<?php if($line->type != 'old') echo $line->type?> <?php if($line->type == 'custom') echo "line-new"?> code'><?php
if(!isset($content->new[$newlc])) $content->new[$newlc] = '';
$content->new[$newlc] = $repo->SCM == 'Subversion' ? htmlspecialchars($content->new[$newlc]) : $content->new[$newlc];
if(!empty($newlc)) echo $line->type != 'all' ? $content->new[$newlc] : ' ' . $content->new[$newlc];
?></td>
<?php
if(isset($content->old[$oldlc])) unset($content->old[$oldlc]);
if(isset($content->new[$newlc])) unset($content->new[$newlc]);
?>
</tr>
<?php endforeach;?>
<?php endif;?>
<?php endforeach;?>
</table>
</div>
<?php endforeach?>
</div>
<div class='revisions hidden'>
<?php
if(strpos($repo->SCM, 'Subversion') === false)
{
$oldRevision = $oldRevision == '^' ? "$newRevision" : $oldRevision;
echo " <span class='label label-info'>" . substr($oldRevision, 0, 10) . " : " . substr($newRevision, 0, 10) . ' (' . $historys[$oldRevision] . ' : ' . $historys[$newRevision] . ')</span>';
}
else
{
$oldRevision = $oldRevision == '^' ? $newRevision - 1 : $oldRevision;
echo " <span class='label label-info'>$oldRevision : $newRevision</span>";
}
?>
</div>
<form method="post" id="exchange" class="hidden">
<input type="hidden" name="revision[]" value="<?php echo $oldRevision;?>"/>
<input type="hidden" name="revision[]" value="<?php echo $newRevision;?>"/>
</form>
<?php include '../../common/view/footer.html.php';?>
+78
View File
@@ -0,0 +1,78 @@
<?php
/**
* The edit view file of mr module of ZenTaoPMS.
*
* @copyright Copyright 2009-2012 青岛易软天创网络科技有限公司 (QingDao Nature Easy Soft Network Technology Co,LTD www.cnezsoft.com)
* @author Wang Yidong, Zhu Jinyong
* @package mr
* @version $Id: create.html.php $
*/
?>
<?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->mr->edit;?></h2>
</div>
<form id='mrForm' method='post' class='form-ajax'>
<table class='table table-form'>
<tr>
<th><?php echo $lang->gitlab->common;?></th>
<td><?php echo $this->loadModel('gitlab')->getByID($MR->gitlabID)->name;?></td>
</tr>
<tr>
<th><?php echo $lang->mr->sourceProject;?></th>
<td>
<div>
<span class='fix-border text-left'>
<?php echo $this->loadModel('gitlab')->apiGetSingleProject($MR->gitlabID, $MR->sourceProject)->name_with_namespace; ?>:
<?php echo $MR->sourceBranch;?>
</span>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->mr->targetProject;?></th>
<td class='required'>
<div class='input-group'>
<span class='input-group-addon fix-border'>
<?php echo $this->loadModel('gitlab')->apiGetSingleProject($MR->gitlabID, $MR->targetProject)->name_with_namespace;?>
</span>
<?php echo html::select('targetBranch', $targetBranchList, $MR->targetBranch, "class='form-control chosen'");?>
</div>
</td>
</tr>
<tr>
<th><?php echo $lang->mr->name;?></th>
<td class='required'><?php echo html::input('title', $MR->title, "class='form-control'"); ?></td>
</tr>
<tr>
<th><?php echo $lang->mr->description; ?></th>
<td colspan='1'><?php echo html::textarea('description', $MR->description, "rows='3' class='form-control'"); ?></td>
</tr>
<tr>
<th><?php echo $lang->mr->assignee;?></th>
<td><?php echo html::select('assignee', $users, $assignee, "class='form-control chosen'")?></td>
</tr>
<tr>
<th><?php echo $lang->mr->reviewer;?></th>
<td><?php echo html::select('reviewer', $users, $reviewer, "class='form-control chosen'")?></td>
</tr>
<tr>
<th></th>
<td><?php echo $lang->mr->usersTips;?></td>
</tr>
<tr>
<th></th>
<td colspan='2' class='text-left form-actions'>
<?php echo html::submitButton(); ?>
<?php if(!isonlybody()) echo html::a(inlink('browse', ""), $lang->goback, '', 'class="btn btn-wide"');?>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php include '../../common/view/footer.html.php'; ?>
+67
View File
@@ -0,0 +1,67 @@
<?php include '../../common/view/header.html.php';?>
<?php if(!isset($MR->id)):?>
<div id='mainContent'>
<div class="table-empty-tip">
<p>
<span class="text-muted"><?php echo $lang->mr->notFound;?></span>
<?php if(common::hasPriv('mr', 'create')):?>
<?php echo html::a($this->createLink('mr', 'create'), "<i class='icon icon-plus'></i> " . $lang->mr->create, '', "class='btn btn-info'");?>
<?php endif;?>
</p>
</div>
</div>
<?php else:?>
<div class="btn-toolbar pull-left">
<?php echo html::a($this->createLink('mr', 'browse'), '<i class="icon icon-back icon-sm"></i> ' . $lang->goback, '', "class='btn btn-secondary'");?>
<div class="divider"></div>
<div class="page-title">
<span class="label label-id"><?php echo $MR->id?></span>
<span class="text" title='<?php echo $MR->title;?>'><?php echo $MR->title;?></span>
<span class="text" title='<?php echo $MR->title;?>' style='color: blue'><?php echo html::a($rawMR->web_url, $lang->mr->viewInGitlab, "_blank", "class='btn btn-link btn-active-text' style='color: blue'");?></span>
</div>
</div>
<div id="mainContent" class="main-row">
<div class="main-col">
<div class="cell">
<div class="detail">
<div class="detail-title">
<div><?php echo $lang->mr->from . html::a($sourceProjectURL, $sourceProjectName . ":" . $MR->sourceBranch, "_blank", "class='btn btn-link btn-active-text' style='color: blue'") . $lang->mr->to . html::a($targetProjectURL, $targetProjectName . ":" . $MR->targetBranch, "_blank", "class='btn btn-link btn-active-text' style='color: blue'");?></div>
</div>
<div class="detail-content article-content">
<strong><?php echo $lang->mr->status;?></strong>
<?php echo zget($lang->mr->statusList, $MR->status);?>
<br>
<?php if(isset($rawMR->head_pipeline->status)):?>
<div>
<strong><?php echo "{$lang->mr->pipeline}{$lang->mr->status}";?></strong>
<?php echo zget($lang->mr->pipelineStatus, $rawMR->head_pipeline->status, $lang->mr->pipelineUnknown);?>
</div>
<?php endif;?>
<strong><?php echo $lang->mr->mergeStatus;?> </strong>
<?php echo zget($lang->mr->mergeStatusList, $rawMR->merge_status);?>
<br>
<strong><?php echo $lang->mr->MRHasConflicts;?></strong>
<?php echo ($rawMR->has_conflicts ? $lang->mr->hasConflicts : $lang->mr->hasNoConflict);?>
</div>
</div>
</div>
<div class="cell">
<div class="detail">
<div class="detail-title"><?php echo $lang->mr->description;?></div>
<div class="detail-content article-content">
<?php echo !empty($MR->description) ? $MR->description : "<div class='text-center text-muted'>" . $lang->noData . '</div>';?>
</div>
</div>
</div>
<?php if($rawMR->state == 'opened'):?>
<div class="cell"><?php echo sprintf($lang->mr->commandDocument, $httpRepoURL, $MR->sourceBranch, $branchPath, $MR->targetBranch, $branchPath, $MR->targetBranch);?></div>
<?php endif;?>
</div>
</div>
<br>
<?php if($rawMR->state == 'opened' and !$rawMR->has_conflicts):?>
<?php echo html::a(inlink( 'accept', "mr=$MR->id"), '<i class="icon icon-checked"></i> ' . $lang->mr->acceptMR, '', "id='mergeButton' class='btn btn-wide btn-primary'");?>
<?php endif;?>
<?php endif;?>
<?php include '../../common/view/footer.html.php';?>
+3 -3
View File
@@ -87,9 +87,9 @@ class repo extends control
/* Pager. */
$this->app->loadClass('pager', $static = true);
$recTotal = count($repoList);
$pager = new pager($recTotal, $recPerPage, $pageID);
$repoList = array_chunk($repoList, $pager->recPerPage);
$recTotal = count($repoList);
$pager = new pager($recTotal, $recPerPage, $pageID);
$repoList = array_chunk($repoList, $pager->recPerPage);
$this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->browse;
$this->view->position[] = $this->lang->repo->common;
+1 -1
View File
@@ -62,7 +62,7 @@
</tbody>
</table>
<?php if($repoList):?>
<div class='table-footer'><?php $pager->show('rignt', 'pagerjs');?></div>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
<?php endif;?>
</form>
</div>
+7 -6
View File
@@ -96,12 +96,13 @@ $lang->todo->priList[2] = 2;
$lang->todo->priList[3] = 3;
$lang->todo->priList[4] = 4;
$lang->todo->typeList['custom'] = '自定义';
$lang->todo->typeList['cycle'] = '周期';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = '任务';
$lang->todo->typeList['story'] = $lang->SRCommon;
$lang->todo->typeList['testtask'] = '测试单';
$lang->todo->typeList['custom'] = '自定义';
$lang->todo->typeList['cycle'] = '周期';
$lang->todo->typeList['bug'] = 'Bug';
$lang->todo->typeList['task'] = '任务';
$lang->todo->typeList['story'] = $lang->SRCommon;
$lang->todo->typeList['testtask'] = '测试单';
$lang->todo->typeList['mrapprove'] = '合并请求审批';
$lang->todo->confirmDelete = "您确定要删除这条待办吗?";
$lang->todo->thisIsPrivate = '这是一条私人事务。:)';
+1 -1
View File
@@ -50,7 +50,7 @@
</tbody>
</table>
<?php if($webhooks):?>
<div class='table-footer'><?php $pager->show('rignt', 'pagerjs');?></div>
<div class='table-footer'><?php $pager->show('right', 'pagerjs');?></div>
<?php endif;?>
</form>
</div>