diff --git a/module/gitfox/model.php b/module/gitfox/model.php index f19d3a4e0a..b04b6e0d2b 100644 --- a/module/gitfox/model.php +++ b/module/gitfox/model.php @@ -11,6 +11,8 @@ declare(strict_types=1); */ class gitfoxModel extends model { + protected $repos = array(); + /** * 获取gitfox根据id。 * Get a gitfox by id. @@ -52,6 +54,46 @@ class gitfoxModel extends model return $this->loadModel('pipeline')->getPairs('gitfox'); } + /** + * 获取gitfox api 基础url 根据gitfox id。 + * Get gitfox api base url by gitfox id. + * + * @param int $gitfoxID + * @access public + * @return string|object + */ + public function getApiRoot(int $gitfoxID): string|object + { + $gitfox = $this->getByID($gitfoxID); + if(!$gitfox || $gitfox->type != 'gitfox') return ''; + + $apiRoot = new stdclass; + $apiRoot->url = rtrim($gitfox->url, '/') . '/api/v1%s'; + $apiRoot->header = array('Authorization: Bearer ' . $gitfox->token); + + return $apiRoot; + } + + /** + * 通过api获取一个代码库信息。 + * Get single repo by API. + * + * @param int $gitfoxID + * @param int $projectID + * @param bool $useUser + * @access public + * @return object|array|null + */ + public function apiGetSingleRepo(int $gitfoxID, int $repoID): object|array|null + { + if(isset($this->repos[$gitfoxID][$repoID])) return $this->repos[$gitfoxID][$repoID]; + + $apiRoot = $this->getApiRoot($gitfoxID); + $url = sprintf($apiRoot->url, "/repos/$repoID"); + $this->repos[$gitfoxID][$repoID] = json_decode(commonModel::http($url, null, array(), $apiRoot->header)); + return $this->repos[$gitfoxID][$repoID]; + } + /** * 检查token。 * Check token access. @@ -73,5 +115,198 @@ class gitfoxModel extends model return $users; } + + /** + * 获取gitfox的代码库列表。 + * Get repos of one gitfox. + * + * @param int $gitfoxID + * @param string $simple + * @param int $minID + * @param int $maxID + * @param bool $sudo + * @access public + * @return array + */ + public function apiGetRepos(int $gitfoxID, string $query = ''): array + { + $apiRoot = $this->getApiRoot($gitfoxID); + if(!$apiRoot) return array(); + + $url = sprintf($apiRoot->url, "/repos?listAllRepos"); + + $allResults = array(); + for($page = 1; true; $page++) + { + $results = json_decode(commonModel::http($url . "&query={$query}&page={$page}&limit=100")); + if(!is_array($results)) break; + if(!empty($results)) $allResults = array_merge($allResults, $results); + if(count($results) < 100) break; + } + + return $allResults; + } + + /** + * 更新版本库的代码地址。 + * Update repo code path. + * + * @param int $gitfoxID + * @param int $projectID + * @param int $repoID + * @access public + * @return bool + */ + public function updateCodePath(int $gitfoxID, int $repoID, int $id): bool + { + $project = $this->apiGetSingleRepo($gitfoxID, $repoID); + if(is_object($project) and !empty($project->git_url)) + { + $this->dao->update(TABLE_REPO)->set('path')->eq($project->git_url)->where('id')->eq($id)->exec(); + return true; + } + + return false; + } + + /** + * 通过api获取项目 hooks。 + * Get hooks. + * + * @param int $gitfoxID + * @param int $repoID + * @access public + * @link https://docs.gitfox.com/ee/api/projects.html#list-project-hooks + * @return object|array|null + */ + public function apiGetHooks(int $gitfoxID, int $repoID, int $hookID = 0): object|array|null + { + $apiRoot = $this->getApiRoot($gitfoxID); + $apiPath = "/repos/{$repoID}/webhooks" . ($hookID ? "/{$hookID}" : ''); + $url = sprintf($apiRoot->url, $apiPath); + + return json_decode(commonModel::http($url, null, array(), $apiRoot->header)); + } + + /** + * 通过api创建hook。 + * Create hook by api. + * + * @param int $gitfoxID + * @param int $repoID + * @param object $hook + * @access public + * @link https://docs.gitfox.com/ee/api/projects.html#add-project-hook + * @return object|array|null|false + */ + public function apiCreateHook(int $gitfoxID, int $repoID, object $hook): object|array|null|false + { + if(!isset($hook->url)) return false; + + $newHook = new stdclass; + $newHook->insecure = "false"; /* Disable ssl verification for every hook. */ + + foreach($hook as $index => $item) $newHook->$index= $item; + + $apiRoot = $this->getApiRoot($gitfoxID); + $url = sprintf($apiRoot->url, "/repos/{$repoID}/webhooks"); + + return json_decode(commonModel::http($url, $newHook, array(), $apiRoot->header)); + } + + /** + * 通过api删除hook。 + * Delete hook by api. + * + * @param int $gitfoxID + * @param int $repoID + * @param int $hookID + * @access public + * @link https://docs.gitfox.com/ee/api/projects.html#delete-project-hook + * @return object|array|null + */ + public function apiDeleteHook(int $gitfoxID, int $repoID, int $hookID): object|array|null + { + $apiRoot = $this->getApiRoot($gitfoxID, false); + $url = sprintf($apiRoot->url, "/repos/{$repoID}/hooks/{$hookID}"); + + return json_decode(commonModel::http($url, array(), array(CURLOPT_CUSTOMREQUEST => 'DELETE'), $apiRoot->header)); + } + + /** + * 添加一个推送和合并请求事件的webhook到gitfox项目。 + * Add webhook with push and merge request events to GitLab project. + * + * @param object $repo + * @param string $token + * @access public + * @return bool|array + */ + public function addPushWebhook(object $repo, string $token = ''): bool|array + { + $systemURL = dirname(common::getSysURL() . $_SERVER['REQUEST_URI']); + + $hook = new stdClass; + $hook->url = $systemURL . '/api.php/v1/gitfox/webhook?repoID='. $repo->id; + $hook->display_name = "zentao_{$repo->id}_" . date('Ymd'); + $hook->enabled = true; + if($token) $hook->secret = $token; + + /* Return an empty array if where is one existing webhook. */ + if($this->isWebhookExists($repo, $hook->url)) return true; + + $result = $this->apiCreateHook($repo->gitService, (int)$repo->project, $hook); + + if(!empty($result->id)) return true; + + if(!empty($result->message)) return array('result' => 'fail', 'message' => $result->message); + return false; + } + + /** + * 检查webhook是否存在。 + * Check if Webhook exists. + * + * @param object $repo + * @param string $url + * @return bool + */ + public function isWebhookExists(object $repo, string $url = ''): bool + { + $hookList = $this->apiGetHooks($repo->gitService, (int)$repo->project); + foreach($hookList as $hook) + { + if(empty($hook->url)) continue; + if($hook->url == $url) return true; + } + return false; + } + + /** + * 通过api更新hook。 + * Update hook by api. + * + * @param int $gitfoxID + * @param int $projectID + * @param int $hookID + * @param object $hook + * @access public + * @link https://docs.gitfox.com/ee/api/projects.html#edit-project-hook + * @return string|false + */ + public function apiUpdateHook(int $gitfoxID, int $projectID, int $hookID, object $hook): string|false + { + $apiRoot = $this->getApiRoot($gitfoxID, false); + + if(!isset($hook->url)) return false; + + $newHook = new stdclass; + $newHook->enable_ssl_verification = "false"; /* Disable ssl verification for every hook. */ + + foreach($hook as $index => $item) $newHook->$index= $item; + + $url = sprintf($apiRoot, "/projects/{$projectID}/hooks/{$hookID}"); + return commonModel::http($url, $newHook, array(CURLOPT_CUSTOMREQUEST => 'PUT')); + } } diff --git a/module/repo/control.php b/module/repo/control.php index 94e35b1fc7..abe8be0190 100644 --- a/module/repo/control.php +++ b/module/repo/control.php @@ -151,18 +151,18 @@ class repo extends control if($repo) $repoID = $this->repo->create($repo, $isPipelineServer); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - if($this->post->SCM == 'Gitlab') + if(in_array($this->post->SCM, $this->config->repo->notSyncSCM)) { /* Add webhook. */ $repo = $this->repo->getByID($repoID); - $this->loadModel('gitlab')->updateCodePath($repo->serviceHost, $repo->serviceProject, $repo->id); + $this->loadModel($this->post->SCM)->updateCodePath($repo->serviceHost, $repo->serviceProject, $repo->id); $this->repo->updateCommitDate($repoID); } $this->loadModel('action')->create('repo', $repoID, 'created'); if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $repoID)); - $link = $this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID", '', false) . '#app=' . $this->app->tab; + $link = $this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID", '', false); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link)); } @@ -1421,6 +1421,32 @@ class repo extends control return print(json_encode($options)); } + /** + * 获取Gitfox项目。 + * Ajax get gitfox projects. + * + * @param string $gitfoxID + * @param array $projectIdList + * @access public + * @return void + */ + public function ajaxGetGitfoxProjects(int $gitfoxID, string $projectIdList = '') + { + $projects = $this->repo->getGitfoxProjects($gitfoxID); + + if(!$projects) return print('[]'); + $projectIdList = $projectIdList ? explode(',', $projectIdList) : null; + + $options = array(); + $options[] = array('text' => '', 'value' => '');; + foreach($projects as $project) + { + if(!empty($projectIdList) and $project and !in_array($project->id, $projectIdList)) continue; + $options[] = array('text' => $project->path, 'value' => $project->id); + } + return print(json_encode($options)); + } + /** * 根据服务器ID获取分组。 * Ajax get groups by server. diff --git a/module/repo/model.php b/module/repo/model.php index 268a0ce7a7..d60c48637f 100644 --- a/module/repo/model.php +++ b/module/repo/model.php @@ -181,7 +181,7 @@ class repoModel extends model { $this->dao->insert(TABLE_REPO)->data($repo, 'serviceToken') ->batchCheck($this->config->repo->create->requiredFields, 'notempty') - ->batchCheckIF($repo->SCM != 'Gitlab', 'path,client', 'notempty') + ->batchCheckIF(!in_array($repo->SCM, $this->config->repo->notSyncSCM), 'path,client', 'notempty') ->batchCheckIF($isPipelineServer, 'serviceHost,serviceProject', 'notempty') ->batchCheckIF($repo->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty') ->check('name', 'unique', "`SCM` = " . $this->dao->sqlobj->quote($repo->SCM)) @@ -2290,8 +2290,7 @@ class repoModel extends model */ public function getGitlabProjects(int $gitlabID, string $projectFilter = ''): array { - $showAll = ($projectFilter == 'ALL' and common::hasPriv('repo', 'create')) ? true : false; - if($this->app->user->admin or $showAll) + if($this->app->user->admin || ($projectFilter == 'ALL' && common::hasPriv('repo', 'create'))) { $projects = $this->loadModel('gitlab')->apiGetProjects($gitlabID, 'true', 0, 0, false); } @@ -2316,6 +2315,21 @@ class repoModel extends model return $projects; } + /** + * 获取gitfox项目列表。 + * Get gitfox projects. + * + * @param int $gitfoxID + * @access public + * @return array + */ + public function getGitfoxProjects(int $gitfoxID): array + { + $projects = $this->loadModel('gitfox')->apiGetRepos($gitfoxID); + + return $projects; + } + /** * Get repo groups. * @@ -2393,15 +2407,15 @@ class repoModel extends model $repo = $this->getByID($repoID); if(empty($repo->id)) return; - if($repo->SCM == 'Gitlab') + if(in_array($repo->SCM, $this->config->repo->notSyncSCM)) { $scm = $this->app->loadClass('scm'); $scm->setEngine($repo); $commits = $scm->engine->getCommitsByPath('', '', '', 1, 1); - if($commits && !empty($commits[0]->committed_date)) + $commitDate = $repo->SCM == 'GitLab' ? $commits[0]->committed_date : $commits[0]->author->when; + if($commits && !empty($commitDate)) { - $commit = $commits[0]; - $lastCommitDate = date('Y-m-d H:i:s', strtotime($commit->committed_date)); + $lastCommitDate = date('Y-m-d H:i:s', strtotime($commitDate)); $this->dao->update(TABLE_REPO)->set('lastCommit')->eq($lastCommitDate)->where('id')->eq($repoID)->exec(); } }