diff --git a/module/gitlab/control.php b/module/gitlab/control.php index 91816b3dae..18a3b4c083 100644 --- a/module/gitlab/control.php +++ b/module/gitlab/control.php @@ -23,7 +23,7 @@ class gitlab extends control if(stripos($this->methodName, 'ajax') === false) { if(!commonModel::hasPriv('space', 'browse')) $this->loadModel('common')->deny('space', 'browse', false); - + if(!in_array(strtolower(strtolower($this->methodName)), array('browseproject', 'browsegroup', 'browseuser', 'browsebranch', 'browsetag'))) { if(!commonModel::hasPriv('instance', 'manage')) $this->loadModel('common')->deny('instance', 'manage', false); @@ -174,7 +174,7 @@ class gitlab extends control $accountList[$user] = $openID; } - if(count($repeatUsers)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->gitlab->bindUserError, join(',', $repeatUsers)))); + if(count($repeatUsers)) return $this->send(array('result' => 'fail', 'message' => sprintf($this->lang->gitlab->bindUserError, join(',', array_unique($repeatUsers))))); $user = new stdclass; $user->providerID = $gitlabID; @@ -423,10 +423,10 @@ class gitlab extends control if(!$this->app->user->admin) { $openID = $this->gitlab->getUserIDByZentaoAccount($gitlabID, $this->app->user->account); - if(!$openID) return print(js::alert($this->lang->gitlab->mustBindUser) . js::locate($this->createLink('gitlab', 'browse'))); + if(!$openID) return print(js::alert($this->lang->gitlab->mustBindUser) . js::locate($this->createLink('space', 'browse'))); $members = $this->gitlab->apiGetGroupMembers($gitlabID, $groupID, $openID); - if(empty($members) or $members[0]->access_level < $this->config->gitlab->accessLevel['owner']) return print(js::alert($this->lang->gitlab->noAccess) . js::locate($this->createLink('gitlab', 'browse'))); + if(empty($members) or $members[0]->access_level < $this->config->gitlab->accessLevel['owner']) return print(js::alert($this->lang->gitlab->noAccess) . js::locate($this->createLink('space', 'browse'))); } if($_POST) diff --git a/module/gitlab/model.php b/module/gitlab/model.php index 23d2bfa039..76023e20e1 100644 --- a/module/gitlab/model.php +++ b/module/gitlab/model.php @@ -655,7 +655,7 @@ class gitlabModel extends model */ public function apiGetGroups($gitlabID, $orderBy = 'id_desc', $minRole = '', $keyword = '') { - $apiRoot = $this->getApiRoot($gitlabID, false); + $apiRoot = $this->getApiRoot($gitlabID, $minRole == 'owner' ? true : false); $url = sprintf($apiRoot, "/groups"); if($minRole == 'owner') { @@ -1270,7 +1270,7 @@ class gitlabModel extends model public function addPushWebhook($repo, $token = '') { $hook = new stdClass; - $hook->url = (defined('RUN_MODE') && RUN_MODE == 'test' ? 'http://unittest' : common::getSysURL()) . '/api.php/v1/gitlab/webhook?repoID='. $repo->id; + $hook->url = common::getSysURL() . '/api.php/v1/gitlab/webhook?repoID='. $repo->id; $hook->push_events = true; $hook->merge_requests_events = true; if($token) $hook->token = $token; diff --git a/module/instance/model.php b/module/instance/model.php index f60221069d..b33cbdc6c0 100644 --- a/module/instance/model.php +++ b/module/instance/model.php @@ -928,7 +928,9 @@ class InstanceModel extends model */ public function createInstance($app, $space, $thirdDomain, $name = '', $k8name = '', $channel = 'stable', $snippets = array()) { + $createdBy = $this->app->user->account; if(empty($k8name)) $k8name = "{$app->chart}-" . date('YmdHis'); //name rule: chartName-userAccount-YmdHis; + if(defined('IN_INSTALL') && IN_INSTALL && empty($this->app->user->account)) $createdBy = trim($this->app->company->admins, ','); //Set createdBy if in login progress; $instanceData = new stdclass; $instanceData->appId = $app->id; diff --git a/module/mr/config.php b/module/mr/config.php index 113e298280..1dd193d229 100644 --- a/module/mr/config.php +++ b/module/mr/config.php @@ -1,5 +1,5 @@ mr = new stdclass(); $config->mr->create = new stdclass(); @@ -56,7 +56,7 @@ $config->mr->actionList['delete']['icon'] = 'trash'; $config->mr->actionList['delete']['hint'] = $lang->mr->delete; $config->mr->actionList['delete']['url'] = helper::createLink('mr', 'delete', "MRID={id}&confirm=yes"); $config->mr->actionList['delete']['data-confirm'] = $lang->mr->confirmDelete; -$config->mr->actionList['delete']['class'] = 'ajax-submit'; +$config->mr->actionList['delete']['className'] = 'ajax-submit'; $config->mr->actionList['accept']['icon'] = 'flow'; $config->mr->actionList['accept']['text'] = $lang->mr->acceptMR; @@ -72,15 +72,28 @@ $config->mr->actionList['reject']['text'] = $lang->mr->reject; $config->mr->actionList['reject']['url'] = helper::createLink('mr', 'approval', "MRID={id}&action=reject"); $config->mr->actionList['reject']['data-toggle'] = 'modal'; -$config->mr->actionList['close']['icon'] = 'off'; -$config->mr->actionList['close']['text'] = $lang->mr->close; -$config->mr->actionList['close']['url'] = helper::createLink('mr', 'close', "MRID={id}"); -$config->mr->actionList['close']['class'] = 'ajax-submit'; +$config->mr->actionList['close']['icon'] = 'off'; +$config->mr->actionList['close']['text'] = $lang->mr->close; +$config->mr->actionList['close']['url'] = helper::createLink('mr', 'close', "MRID={id}"); +$config->mr->actionList['close']['className'] = 'ajax-submit'; -$config->mr->actionList['reopen']['icon'] = 'restart'; -$config->mr->actionList['reopen']['text'] = $lang->mr->reopen; -$config->mr->actionList['reopen']['url'] = helper::createLink('mr', 'reopen', "MRID={id}"); -$config->mr->actionList['reopen']['class'] = 'ajax-submit'; +$config->mr->actionList['reopen']['icon'] = 'restart'; +$config->mr->actionList['reopen']['text'] = $lang->mr->reopen; +$config->mr->actionList['reopen']['url'] = helper::createLink('mr', 'reopen', "MRID={id}"); +$config->mr->actionList['reopen']['className'] = 'ajax-submit'; + +$app->loadLang('release'); +$app->loadLang('story'); +$app->loadLang('bug'); +$app->loadLang('build'); +$app->loadLang('task'); +$config->mr->actionList['unlinkStory']['icon'] = 'unlink'; +$config->mr->actionList['unlinkStory']['hint'] = $lang->release->unlinkStory; +$config->mr->actionList['unlinkStory']['url'] = 'javascript: unlinkObject("story", "{id}")'; + +$config->mr->actionList['unlinkBug']['icon'] = 'unlink'; +$config->mr->actionList['unlinkBug']['hint'] = $lang->release->unlinkBug; +$config->mr->actionList['unlinkBug']['url'] = 'javascript: unlinkObject("bug", "{id}")'; $config->mr->view = new stdclass(); $config->mr->view->operateList = array('accept', 'approval', 'reject', 'close', 'edit', 'reopen', 'delete'); diff --git a/module/mr/config/dtable.php b/module/mr/config/dtable.php index f5cd747e49..64626e1a85 100644 --- a/module/mr/config/dtable.php +++ b/module/mr/config/dtable.php @@ -1,7 +1,7 @@ mr->dtable = new stdclass; +$config->mr->dtable = new stdclass(); $config->mr->dtable->fieldList['id']['name'] = 'id'; $config->mr->dtable->fieldList['id']['title'] = $lang->idAB; @@ -40,7 +40,7 @@ $config->mr->dtable->fieldList['createdBy']['sortType'] = true; $config->mr->dtable->fieldList['createdDate']['name'] = 'createdDate'; $config->mr->dtable->fieldList['createdDate']['title'] = $lang->mr->createdDate; -$config->mr->dtable->fieldList['createdDate']['type'] = 'date'; +$config->mr->dtable->fieldList['createdDate']['type'] = 'datetime'; $config->mr->dtable->fieldList['createdDate']['sortType'] = true; $config->mr->dtable->fieldList['actions']['name'] = 'actions'; @@ -49,3 +49,40 @@ $config->mr->dtable->fieldList['actions']['type'] = 'actions'; $config->mr->dtable->fieldList['actions']['menu'] = array('view', 'edit', 'diff', 'link', 'delete'); $config->mr->dtable->fieldList['actions']['list'] = $config->mr->actionList; +$config->mr->taskDtable = new stdclass(); +$config->mr->taskDtable->fieldList['id']['title'] = $lang->idAB; +$config->mr->taskDtable->fieldList['id']['type'] = 'checkID'; +$config->mr->taskDtable->fieldList['id']['sortType'] = 'desc'; +$config->mr->taskDtable->fieldList['id']['checkbox'] = false; +$config->mr->taskDtable->fieldList['id']['required'] = true; + +$config->mr->taskDtable->fieldList['name']['fixed'] = 'left'; +$config->mr->taskDtable->fieldList['name']['title'] = $lang->task->name; +$config->mr->taskDtable->fieldList['name']['flex'] = ''; +$config->mr->taskDtable->fieldList['name']['type'] = 'nestedTitle'; +$config->mr->taskDtable->fieldList['name']['nestedToggle'] = true; +$config->mr->taskDtable->fieldList['name']['sortType'] = true; +$config->mr->taskDtable->fieldList['name']['link'] = array('module' => 'task', 'method' => 'view', 'params' => 'taskID={id}', 'target' => '_blank'); +$config->mr->taskDtable->fieldList['name']['required'] = true; + +$config->mr->taskDtable->fieldList['pri']['title'] = $lang->priAB; +$config->mr->taskDtable->fieldList['pri']['type'] = 'pri'; +$config->mr->taskDtable->fieldList['pri']['sortType'] = true; +$config->mr->taskDtable->fieldList['pri']['show'] = true; + +$config->mr->taskDtable->fieldList['assignedTo']['type'] = 'desc'; +$config->mr->taskDtable->fieldList['assignedTo']['title'] = $lang->task->assignedTo; +$config->mr->taskDtable->fieldList['assignedTo']['currentUser'] = $app->user->account; +$config->mr->taskDtable->fieldList['assignedTo']['sortType'] = true; +$config->mr->taskDtable->fieldList['assignedTo']['show'] = true; + +$config->mr->taskDtable->fieldList['finishedBy']['title'] = $lang->task->finishedByAB; +$config->mr->taskDtable->fieldList['finishedBy']['type'] = 'user'; +$config->mr->taskDtable->fieldList['finishedBy']['sortType'] = true; +$config->mr->taskDtable->fieldList['finishedBy']['show'] = true; + +$config->mr->taskDtable->fieldList['status']['title'] = $lang->statusAB; +$config->mr->taskDtable->fieldList['status']['type'] = 'status'; +$config->mr->taskDtable->fieldList['status']['statusMap'] = $lang->task->statusList; +$config->mr->taskDtable->fieldList['status']['sortType'] = true; +$config->mr->taskDtable->fieldList['status']['show'] = true; diff --git a/module/mr/control.php b/module/mr/control.php index d7d7867c5a..acc883aae9 100644 --- a/module/mr/control.php +++ b/module/mr/control.php @@ -57,6 +57,7 @@ class mr extends control } $filterProjects = empty($repo->serviceProject) ? array() : array($repo->serviceHost => array($repo->serviceProject => $repo->serviceProject)); $MRList = $this->mr->getList($mode, $param, $orderBy, $pager, $filterProjects, $repoID); + if($repo->SCM == 'Gitlab') { $projectIds = array(); @@ -83,7 +84,6 @@ class mr extends control { $product = $this->mr->getMRProduct($MR); $MR->linkButton = empty($product) ? false : true; - $MR->createdDate = date('m-d H:i', strtotime($MR->createdDate)); } /* Load lang from compile module */ @@ -118,6 +118,7 @@ class mr extends control $this->view->orderBy = $orderBy; $this->view->openIDList = $openIDList; $this->view->users = $this->loadModel('user')->getPairs('noletter'); + $this->view->sortLink = $this->createLink('mr', 'browse', "repoID={$repoID}&mode={$mode}¶m={$param}&objectID={$objectID}&orderBy={orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}"); $this->display(); } @@ -258,7 +259,7 @@ class mr extends control $this->loadModel('action')->create('mr', $MRID, 'deleted', '', $MR->title); $this->mr->createMRLinkedAction($MRID, 'removemr'); - return $this->send(array('result' => 'success', 'reload' => inlink('browse'))); + return $this->send(array('result' => 'success', 'load' => true)); } /** @@ -271,7 +272,7 @@ class mr extends control public function view($MRID) { $MR = $this->mr->getByID($MRID); - if(!$MR) return print(js::error($this->lang->notFound) . js::locate($this->createLink('mr', 'browse'))); + if(!$MR) return $this->locate($this->createLink('mr', 'browse')); if(isset($MR->hostID)) $rawMR = $this->mr->apiGetSingleMR($MR->hostID, $MR->targetProject, $MR->mriid); if($MR->synced and (!isset($rawMR->id) or (isset($rawMR->message) and $rawMR->message == '404 Not found') or empty($rawMR))) return $this->display(); @@ -287,13 +288,32 @@ class mr extends control $sourceBranch = $this->$scm->apiGetSingleBranch($MR->hostID, $MR->sourceProject, $MR->sourceBranch); $targetBranch = $this->$scm->apiGetSingleBranch($MR->hostID, $MR->targetProject, $MR->targetBranch); - $projectOwner = true; + $projectOwner = $projectEdit = false; if(isset($MR->hostID) and !$this->app->user->admin) { $openID = $this->$scm->getUserIDByZentaoAccount($MR->hostID, $this->app->user->account); if(!$projectOwner and isset($sourceProject->owner->id) and $sourceProject->owner->id == $openID) $projectOwner = true; } + if($scm == 'gitlab') + { + $gitUsers = $this->gitlab->getUserAccountIdPairs($MR->hostID); + $groupIDList = array(0 => 0); + $groups = $this->gitlab->apiGetGroups($MR->hostID, 'name_asc', 'developer'); + foreach($groups as $group) $groupIDList[] = $group->id; + $isDeveloper = $this->gitlab->checkUserAccess($MR->hostID, 0, $sourceProject, $groupIDList, 'developer'); + + if(isset($gitUsers[$this->app->user->account]) && $isDeveloper) $projectEdit = true; + } + elseif($scm == 'gitea') + { + $projectEdit = (isset($sourceProject->allow_merge_commits) and $sourceProject->allow_merge_commits == true) ? true : false; + } + elseif($scm == 'gogs') + { + $projectEdit = (isset($sourceProject->permissions->push) and $sourceProject->permissions->push) ? true : false; + } + $this->view->sourceProjectName = $sourceProject->name_with_namespace; $this->view->targetProjectName = $targetProject->name_with_namespace; $this->view->sourceProjectURL = isset($sourceBranch->web_url) ? $sourceBranch->web_url : ''; @@ -310,6 +330,7 @@ class mr extends control $this->view->compile = $this->loadModel('compile')->getById($MR->compileID); $this->view->compileJob = $MR->jobID ? $this->job->getById($MR->jobID) : false; $this->view->projectOwner = $projectOwner; + $this->view->projectEdit = $projectEdit; $this->view->title = $this->lang->mr->view; $this->view->MR = $MR; @@ -545,6 +566,7 @@ class mr extends control $this->app->loadModuleConfig('release'); $this->app->loadModuleConfig('task'); + $this->app->loadModuleConfig('repo'); $MR = $this->mr->getByID($MRID); $product = $this->mr->getMRProduct($MR); @@ -650,7 +672,7 @@ class mr extends control $linkedStories = $this->mr->getLinkList($MRID, $product->id, 'story'); if($browseType == 'bySearch') { - $allStories = $this->story->getBySearch($productID, 0, $queryID, 'id', '', 'story', array_keys($linkedStories), $pager); + $allStories = $this->story->getBySearch($productID, 0, $queryID, $orderBy, '', 'story', array_keys($linkedStories), '', $pager); } else { @@ -716,7 +738,7 @@ class mr extends control $this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getForProducts(array($productID => $productID)); $this->config->bug->search['params']['module']['values'] = $modules; $this->config->bug->search['params']['execution']['values'] = $this->product->getExecutionPairsByProduct($productID); - $this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs(array($productID), 'all', 'releasetag'); + $this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getBuildPairs($productID, 'all', 'releasetag'); $this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values']; unset($this->config->bug->search['fields']['product']); @@ -739,17 +761,13 @@ class mr extends control $linkedBugs = $this->mr->getLinkList($MRID, $product->id, 'bug'); if($browseType == 'bySearch') { - $allBugs = $this->bug->getBySearch($productID, 0, $queryID, 'id_desc', array_keys($linkedBugs), $pager); + $allBugs = $this->bug->getBySearch($productID, 0, $queryID, $orderBy, array_keys($linkedBugs), $pager); } else { - $allBugs = $this->bug->getActiveBugs($productID, 0, '0', array_keys($linkedBugs), $pager); + $allBugs = $this->bug->getActiveBugs($productID, 0, '0', array_keys($linkedBugs), $pager, $orderBy); } - list($order, $sort) = explode('_', $orderBy); - $sortCol = array_column($allBugs, $order); - array_multisort($sortCol, $sort == 'asc' ? SORT_ASC : SORT_DESC, $allBugs); - $this->view->modules = $modules; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->allBugs = $allBugs; @@ -792,6 +810,7 @@ class mr extends control $this->loadModel('execution'); $this->loadModel('product'); $this->app->loadLang('task'); + $this->app->loadModuleConfig('repo'); /* Set browse type. */ $browseType = strtolower($browseType); diff --git a/module/mr/css/common.ui.css b/module/mr/css/common.ui.css index db027f7627..cf0c538b1e 100644 --- a/module/mr/css/common.ui.css +++ b/module/mr/css/common.ui.css @@ -1,2 +1,3 @@ .mr-linkstory-title {background-color: #e6f0ff; color: #2e7fff; padding: 12px 6px 12px;} -#log .action-btn .caret {display: none; color: red} \ No newline at end of file +#log .action-btn .caret {display: none; color: red} +.dropmenu-btn>.text {max-width: 280px;} \ No newline at end of file diff --git a/module/mr/css/diff.ui.css b/module/mr/css/diff.ui.css index 6a22df286a..03b155291a 100644 --- a/module/mr/css/diff.ui.css +++ b/module/mr/css/diff.ui.css @@ -71,4 +71,5 @@ .panel-body .detail-content{margin-top: 10px;} #log .action-btn .caret {display: none;} #fileTabs .monaco-dropmenu .caret {display: none;} -#monacoTabs .gap-x-5 {-moz-column-gap: 0; column-gap: 0;} \ No newline at end of file +#monacoTabs .gap-x-5 {-moz-column-gap: 0; column-gap: 0;} +#log .action-btn .btn {padding-left: .25rem; padding-right: .25rem;} \ No newline at end of file diff --git a/module/mr/js/browse.ui.js b/module/mr/js/browse.ui.js new file mode 100644 index 0000000000..6403cb11fd --- /dev/null +++ b/module/mr/js/browse.ui.js @@ -0,0 +1,6 @@ +window.createSortLink = function(col) +{ + var sort = col.name + '_asc'; + if(sort == orderBy) sort = col.name + '_desc'; + return sortLink.replace('{orderBy}', sort); +} diff --git a/module/mr/js/create.ui.js b/module/mr/js/create.ui.js index c155d896a8..d3b3c880b9 100644 --- a/module/mr/js/create.ui.js +++ b/module/mr/js/create.ui.js @@ -21,7 +21,8 @@ function urlencode(param) */ function getBranchPriv(project) { - var branchUrl = $.createLink('mr', 'ajaxGetBranchPivs', "hostID=" + hostID + "&project=" + project); + var sourceProject = projectNamespace ? urlencode(projectNamespace) : project; + var branchUrl = $.createLink('mr', 'ajaxGetBranchPivs', "hostID=" + hostID + "&project=" + sourceProject); $.get(branchUrl, function(response) { branchPrivs = eval('(' + response + ')'); @@ -30,7 +31,8 @@ function getBranchPriv(project) function onProjectChange() { - var branchUrl = $.createLink(hostType, 'ajaxGetProjectBranches', hostType + "ID=" + hostID + "&projectID=" + projectID); + var sourceProject = projectNamespace ? urlencode(projectNamespace) : projectID; + var branchUrl = $.createLink(hostType, 'ajaxGetProjectBranches', hostType + "ID=" + hostID + "&projectID=" + sourceProject); $.ajaxSubmit( { url: branchUrl, diff --git a/module/mr/js/diff.js b/module/mr/js/diff.js index 37a53877c1..16e4768f9a 100644 --- a/module/mr/js/diff.js +++ b/module/mr/js/diff.js @@ -70,11 +70,12 @@ function changeEncoding(encoding) * @access public * @return string */ -function htmlspecialchars_decode(str){ +function htmlspecialchars_decode(str) +{ str = str.replace(/&/g, '&'); str = str.replace(/</g, '<'); str = str.replace(/>/g, '>'); - str = str.replace(/"/g, "''"); + str = str.replace(/"/g, '"'); str = str.replace(/'/g, "'"); return str; } @@ -98,27 +99,38 @@ function getDiffs(fileName) { if(diff.fileName == fileName) { - if(typeof diff.contents[0].lines != 'object') return result; - - var lines = diff.contents[0].lines; - $.each(lines, function(l, code) + $.each(diff.contents, function(c, content) { - if(code.type == 'all' || code.type == 'new') - { - result.code.new += htmlspecialchars_decode(code.line.substring(2)) + "\n"; - result.line.new.push(code.newlc); - } + if(typeof content.lines != 'object') return result; - if(code.type == 'all' || code.type == 'old') + var lines = content.lines; + $.each(lines, function(l, code) { - result.code.old += htmlspecialchars_decode(code.line.substring(2)) + "\n"; - result.line.old.push(code.oldlc); - } - }) + var subIndex = code.type == 'all' ? 1 : 2; + var lineCode = htmlspecialchars_decode(code.line.substring(subIndex)) + "\n"; + + if(code.type == 'new') + { + result.code.new += lineCode; + result.line.new.push(code.newlc); + } + else if(code.type == 'old') + { + result.code.old += lineCode; + result.line.old.push(code.oldlc); + } + else + { + result.code.new += lineCode; + result.code.old += lineCode; + result.line.new.push(code.newlc); + result.line.old.push(code.oldlc); + } + }); + }); return result; } }); - return result; } diff --git a/module/mr/js/diff.ui.js b/module/mr/js/diff.ui.js index 88c3680a32..c27a150b4e 100644 --- a/module/mr/js/diff.ui.js +++ b/module/mr/js/diff.ui.js @@ -211,9 +211,7 @@ window.afterPageUpdate = function() /* Select default tree item. */ const currentElement = findItemInTreeItems(tree, fileAsId, 0); - setTimeout(() => { - if(currentElement != undefined) $('#' + currentElement.id).parent().addClass('selected'); - }, 200); + if(currentElement != undefined) $('#' + currentElement.id).parent().addClass('selected'); expandTree(); $('.btn-left').on('click', function() {arrowTabs('monacoTabs', 1);}); diff --git a/module/mr/js/link.ui.js b/module/mr/js/link.ui.js index 0e25f73d28..44c152fc6a 100644 --- a/module/mr/js/link.ui.js +++ b/module/mr/js/link.ui.js @@ -35,16 +35,7 @@ window.createTaskSortLink = function(col) window.showLink = function(obj) { - let link = $(obj).data('url'); - let $tabContent = $(obj); - - if($(obj).hasClass('link')) - { - $tabContent = $(obj).closest('.tab-pane'); - } - else - { - link = $(obj).find('.link').data('url'); - } + const $tabContent = $(obj).closest('.tab-pane'); + const link = $tabContent.find('.link').data('url'); $tabContent.load(link); }; diff --git a/module/mr/js/linkbug.ui.js b/module/mr/js/linkbug.ui.js index f64dd9a1ad..a832c51e5b 100644 --- a/module/mr/js/linkbug.ui.js +++ b/module/mr/js/linkbug.ui.js @@ -3,7 +3,7 @@ window.createSortLink = function(col) var sort = col.name + '_asc'; if(sort == orderBy) sort = col.name + '_desc'; - return sortLink.replace('{orderBy}', sort); + return "javascript:loadTarget('" + sortLink.replace('{orderBy}', sort) + "', 'mr-bug')"; } $(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer .batch-btn', function(e) @@ -20,6 +20,7 @@ $(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer url: $(this).data('url'), data: postData }); + $(this).prop('disabled', true); }); window.onSearchFormResult = function(formName, response) @@ -28,4 +29,4 @@ window.onSearchFormResult = function(formName, response) .then(json => { loadTarget(json.load, 'mr-bug'); }).catch(console.error); -} \ No newline at end of file +} diff --git a/module/mr/js/linkstory.ui.js b/module/mr/js/linkstory.ui.js index 23596b2a6d..b2a50cddf0 100644 --- a/module/mr/js/linkstory.ui.js +++ b/module/mr/js/linkstory.ui.js @@ -3,7 +3,7 @@ window.createSortLink = function(col) var sort = col.name + '_asc'; if(sort == orderBy) sort = col.name + '_desc'; - return sortLink.replace('{orderBy}', sort); + return "javascript:loadTarget('" + sortLink.replace('{orderBy}', sort) + "', 'mr-story')"; } $(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer .batch-btn', function(e) @@ -20,6 +20,7 @@ $(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer url: $(this).data('url'), data: postData }); + $(this).prop('disabled', true); }); window.onSearchFormResult = function(formName, response) diff --git a/module/mr/js/linktask.ui.js b/module/mr/js/linktask.ui.js index 012ec34b27..a06251e3cd 100644 --- a/module/mr/js/linktask.ui.js +++ b/module/mr/js/linktask.ui.js @@ -3,7 +3,7 @@ window.createSortLink = function(col) var sort = col.name + '_asc'; if(sort == orderBy) sort = col.name + '_desc'; - return sortLink.replace('{orderBy}', sort); + return "javascript:loadTarget('" + sortLink.replace('{orderBy}', sort) + "', 'mr-task')"; } $(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer .batch-btn', function(e) @@ -20,6 +20,7 @@ $(document).off('click','.dtable-footer .batch-btn').on('click', '.dtable-footer url: $(this).data('url'), data: postData }); + $(this).prop('disabled', true); }); window.onSearchFormResult = function(formName, response) @@ -28,4 +29,4 @@ window.onSearchFormResult = function(formName, response) .then(json => { loadTarget(json.load, 'mr-task'); }).catch(console.error); -} \ No newline at end of file +} diff --git a/module/mr/lang/de.php b/module/mr/lang/de.php index 817d887729..fb8ded06e0 100644 --- a/module/mr/lang/de.php +++ b/module/mr/lang/de.php @@ -21,6 +21,7 @@ $lang->mr->link = 'Link of stories,Bugs,tasks'; $lang->mr->createAction = '%s, %s submitted a Merge Request.'; $lang->mr->editAction = '%s, %s edited Merge Request。'; $lang->mr->removeAction = '%s, %s deleted Merge Request。'; +$lang->mr->submitType = 'Submit type'; $lang->mr->linkList = 'Link List of stories,Bugs,tasks'; $lang->mr->linkStory = 'Link Stories'; @@ -42,7 +43,7 @@ $lang->mr->commits = 'commits'; $lang->mr->changes = 'changes'; $lang->mr->gitlabID = 'GitLab'; $lang->mr->repoID = 'Repo'; -$lang->mr->jobID = 'Compile job'; +$lang->mr->jobID = 'Pipeline job'; $lang->mr->canMerge = "Can be merged"; $lang->mr->cantMerge = "Can not be merged"; @@ -64,7 +65,7 @@ $lang->mr->approvalResultList['approve'] = 'Approve'; $lang->mr->approvalResultList['reject'] = 'Reject'; $lang->mr->needApproved = 'This MR should be approved before merge'; -$lang->mr->needCI = 'Merge only after passing CI'; +$lang->mr->needCI = 'Merge only after passing pipeline'; $lang->mr->removeSourceBranch = 'Delete source branch after merge'; $lang->mr->squash = 'Squash commits'; @@ -99,11 +100,11 @@ $lang->mr->sourceProject = 'Source repository'; $lang->mr->sourceBranch = 'Source branch'; $lang->mr->targetProject = 'Target repository'; $lang->mr->targetBranch = 'Target branch'; -$lang->mr->noCompileJob = 'No Compile Job'; +$lang->mr->noCompileJob = 'No Pipeline Job'; $lang->mr->compileUnexecuted = 'Compile Unexecuted'; $lang->mr->notFound = "Merge Request does not exist!"; -$lang->mr->toCreatedMessage = "The merge request you submitted:%s, the build task succeeded."; +$lang->mr->toCreatedMessage = "The merge request you submitted:%s, the pipeline task succeeded."; $lang->mr->toReviewerMessage = "You have one merge request %s waiting."; $lang->mr->failMessage = "Your merge request %s failed. Please check its execution result. "; $lang->mr->storySummary = "Total %s {$lang->SRCommon} on this page."; diff --git a/module/mr/lang/en.php b/module/mr/lang/en.php index a6ddb5a34e..fb8ded06e0 100644 --- a/module/mr/lang/en.php +++ b/module/mr/lang/en.php @@ -21,7 +21,7 @@ $lang->mr->link = 'Link of stories,Bugs,tasks'; $lang->mr->createAction = '%s, %s submitted a Merge Request.'; $lang->mr->editAction = '%s, %s edited Merge Request。'; $lang->mr->removeAction = '%s, %s deleted Merge Request。'; -$lang->mr->submitType = 'Submission method'; +$lang->mr->submitType = 'Submit type'; $lang->mr->linkList = 'Link List of stories,Bugs,tasks'; $lang->mr->linkStory = 'Link Stories'; @@ -43,7 +43,7 @@ $lang->mr->commits = 'commits'; $lang->mr->changes = 'changes'; $lang->mr->gitlabID = 'GitLab'; $lang->mr->repoID = 'Repo'; -$lang->mr->jobID = 'Compile job'; +$lang->mr->jobID = 'Pipeline job'; $lang->mr->canMerge = "Can be merged"; $lang->mr->cantMerge = "Can not be merged"; @@ -65,7 +65,7 @@ $lang->mr->approvalResultList['approve'] = 'Approve'; $lang->mr->approvalResultList['reject'] = 'Reject'; $lang->mr->needApproved = 'This MR should be approved before merge'; -$lang->mr->needCI = 'Merge only after passing CI'; +$lang->mr->needCI = 'Merge only after passing pipeline'; $lang->mr->removeSourceBranch = 'Delete source branch after merge'; $lang->mr->squash = 'Squash commits'; @@ -100,11 +100,11 @@ $lang->mr->sourceProject = 'Source repository'; $lang->mr->sourceBranch = 'Source branch'; $lang->mr->targetProject = 'Target repository'; $lang->mr->targetBranch = 'Target branch'; -$lang->mr->noCompileJob = 'No Compile Job'; +$lang->mr->noCompileJob = 'No Pipeline Job'; $lang->mr->compileUnexecuted = 'Compile Unexecuted'; $lang->mr->notFound = "Merge Request does not exist!"; -$lang->mr->toCreatedMessage = "The merge request you submitted:%s, the build task succeeded."; +$lang->mr->toCreatedMessage = "The merge request you submitted:%s, the pipeline task succeeded."; $lang->mr->toReviewerMessage = "You have one merge request %s waiting."; $lang->mr->failMessage = "Your merge request %s failed. Please check its execution result. "; $lang->mr->storySummary = "Total %s {$lang->SRCommon} on this page."; diff --git a/module/mr/lang/fr.php b/module/mr/lang/fr.php index 7268fae084..632b60d0f7 100644 --- a/module/mr/lang/fr.php +++ b/module/mr/lang/fr.php @@ -21,6 +21,7 @@ $lang->mr->link = 'Link of stories,Bugs,tasks'; $lang->mr->createAction = '%s, %s submitted a Merge Request.'; $lang->mr->editAction = '%s, %s edited Merge Request。'; $lang->mr->removeAction = '%s, %s deleted Merge Request。'; +$lang->mr->submitType = 'Submit type'; $lang->mr->linkList = 'Link List of stories,Bugs,tasks'; $lang->mr->linkStory = 'Link Stories'; @@ -42,7 +43,7 @@ $lang->mr->commits = 'commits'; $lang->mr->changes = 'changes'; $lang->mr->gitlabID = 'GitLab'; $lang->mr->repoID = 'Repo'; -$lang->mr->jobID = 'Compile job'; +$lang->mr->jobID = 'Pipeline job'; $lang->mr->canMerge = "Can be merged"; $lang->mr->cantMerge = "Can not be merged"; @@ -64,7 +65,7 @@ $lang->mr->approvalResultList['approve'] = 'Approve'; $lang->mr->approvalResultList['reject'] = 'Reject'; $lang->mr->needApproved = 'This MR should be approved before merge'; -$lang->mr->needCI = 'Merge only after passing CI'; +$lang->mr->needCI = 'Merge only after passing pipeline'; $lang->mr->removeSourceBranch = 'Delete source branch after merge'; $lang->mr->squash = 'Squash commits'; @@ -99,11 +100,11 @@ $lang->mr->sourceProject = 'Source project'; $lang->mr->sourceBranch = 'Source branch'; $lang->mr->targetProject = 'Target project'; $lang->mr->targetBranch = 'Target branch'; -$lang->mr->noCompileJob = 'No Compile Job'; +$lang->mr->noCompileJob = 'No Pipeline Job'; $lang->mr->compileUnexecuted = 'Compile Unexecuted'; $lang->mr->notFound = "Merge Request does not exist!"; -$lang->mr->toCreatedMessage = "The merge request you submitted:%s, the build task succeeded."; +$lang->mr->toCreatedMessage = "The merge request you submitted:%s, the pipeline task succeeded."; $lang->mr->toReviewerMessage = "You have one merge request %s waiting."; $lang->mr->failMessage = "Your merge request %s failed. Please check its execution result. "; $lang->mr->storySummary = "Total %s {$lang->SRCommon} on this page."; diff --git a/module/mr/lang/zh-cn.php b/module/mr/lang/zh-cn.php index 31cde8ee47..92528e4179 100644 --- a/module/mr/lang/zh-cn.php +++ b/module/mr/lang/zh-cn.php @@ -4,7 +4,7 @@ $lang->mr->common = "合并请求"; $lang->mr->server = "服务器"; $lang->mr->view = "概况"; $lang->mr->viewAction = "{$lang->mr->common}详情"; -$lang->mr->create = "提{$lang->mr->common}"; +$lang->mr->create = "创建{$lang->mr->common}"; $lang->mr->apiCreate = "接口:创建{$lang->mr->common}"; $lang->mr->browse = "浏览{$lang->mr->common}"; $lang->mr->browseAction = "{$lang->mr->common}列表"; @@ -43,7 +43,7 @@ $lang->mr->commits = '提交数'; $lang->mr->changes = '更改数'; $lang->mr->gitlabID = 'GitLab'; $lang->mr->repoID = '版本库'; -$lang->mr->jobID = '构建任务'; +$lang->mr->jobID = '流水线任务'; $lang->mr->canMerge = "可合并"; $lang->mr->cantMerge = "不可合并"; @@ -65,7 +65,7 @@ $lang->mr->approvalResultList['approve'] = '通过'; $lang->mr->approvalResultList['reject'] = '拒绝'; $lang->mr->needApproved = '需要通过评审才能合并'; -$lang->mr->needCI = '需要通过构建才能合并'; +$lang->mr->needCI = '需要通过流水线才能合并'; $lang->mr->removeSourceBranch = '合并后删除源分支'; $lang->mr->squash = '合并提交记录'; @@ -100,13 +100,13 @@ $lang->mr->sourceProject = '源仓库'; $lang->mr->sourceBranch = '源分支'; $lang->mr->targetProject = '目标仓库'; $lang->mr->targetBranch = '目标分支'; -$lang->mr->noCompileJob = '没有构建任务'; +$lang->mr->noCompileJob = '没有流水线任务'; $lang->mr->compileUnexecuted = '还未执行'; $lang->mr->notFound = "此{$lang->mr->common}不存在。"; -$lang->mr->toCreatedMessage = "您提交的合并请求:%s 构建任务执行通过。"; +$lang->mr->toCreatedMessage = "您提交的合并请求:%s 流水线任务执行通过。"; $lang->mr->toReviewerMessage = "有一个合并请求:%s 待审核。"; -$lang->mr->failMessage = "您提交的合并请求:%s 构建任务执行失败,查看执行结果。"; +$lang->mr->failMessage = "您提交的合并请求:%s 流水线任务执行失败,查看执行结果。"; $lang->mr->storySummary = "本页共 %s 个" . $lang->SRCommon; $lang->mr->apiError = new stdclass; diff --git a/module/mr/model.php b/module/mr/model.php index 146221cb89..e48f70adf7 100644 --- a/module/mr/model.php +++ b/module/mr/model.php @@ -123,7 +123,7 @@ class mrModel extends model public function getGiteaProjects($hostID = 0) { $projects = $this->loadModel('gitea')->apiGetProjects($hostID); - return array($hostID => array_column($projects, null, 'full_name')); + return array($hostID => helper::arrayColumn($projects, null, 'full_name')); } /** @@ -136,7 +136,7 @@ class mrModel extends model public function getGogsProjects($hostID = 0) { $projects = $this->loadModel('gogs')->apiGetProjects($hostID); - return array($hostID => array_column($projects, null, 'full_name')); + return array($hostID => helper::arrayColumn($projects, null, 'full_name')); } /** @@ -219,6 +219,7 @@ class mrModel extends model ->setDefault('removeSourceBranch','0') ->setDefault('needCI', 0) ->setDefault('squash', 0) + ->setIF($this->post->needCI == 0, 'jobID', 0) ->add('createdBy', $this->app->user->account) ->add('createdDate', helper::now()) ->get(); @@ -427,8 +428,9 @@ class mrModel extends model $MR = $this->getByID($MRID); $this->linkObjects($MR); - - $this->loadModel('action')->create('mr', $MRID, 'edited'); + $changes = common::createChanges($oldMR, $MR); + $actionID = $this->loadModel('action')->create('mr', $MRID, 'edited'); + if(!empty($changes)) $this->action->logHistory($actionID, $changes); $this->createMRLinkedAction($MRID, 'editmr', $MR->editedDate); if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError()); @@ -635,7 +637,7 @@ class mrModel extends model { $todoDesc = $this->dao->select('*') ->from(TABLE_TODO) - ->where('objectID')->eq($rawTodo->id) + ->where('idvalue')->eq($rawTodo->id) ->fetch(); if(empty($todoDesc)) { @@ -651,7 +653,7 @@ class mrModel extends model $todo->begin = '2400'; /* 2400 means begin is 'undefined'. */ $todo->end = '2400'; /* 2400 means end is 'undefined'. */ $todo->type = 'custom'; - $todo->objectID = $rawTodo->id; + $todo->idvalue = $rawTodo->id; $todo->pri = 3; $todo->name = $this->lang->mr->common . ": " . $rawTodo->target->title; $todo->desc = $author . ' ' . $this->lang->mr->at . ' ' . '' . $rawTodo->project->path .'' . ' ' . $this->lang->mr->todomessage . '' . ' ' . $this->lang->mr->common .'' . '。'; @@ -720,7 +722,7 @@ class mrModel extends model $MRObject->head = $MR->sourceBranch; $MRObject->base = $MR->targetBranch; $MRObject->body = $MR->description; - if($MR->assignee) + if(!$MR->assignee) { $assignee = $this->{$host->type}->getUserIDByZentaoAccount($this->post->hostID, $MR->assignee); if($assignee) $MRObject->assignee = $assignee; @@ -761,11 +763,12 @@ class mrModel extends model } $response = json_decode(commonModel::http($url, $data = null, $options = array(), $headers = array(), $dataType = 'data', $method = 'POST', $timeout = 30, $httpCode = false, $log = false)); - if(empty($response)) $response = array(); + if(empty($response) || isset($response->message)) $response = array(); if($scm == 'Gitea') { foreach($response as $MR) { + if(empty($MR)) continue; $MR->iid = $MR->number; $MR->state = $MR->state == 'open' ? 'opened' : $MR->state; if($MR->merged) $MR->state = 'merged'; @@ -842,7 +845,7 @@ class mrModel extends model $host = $this->loadModel('pipeline')->getByID($hostID); if($host->type == 'gitlab') { - $url = sprintf($this->gitlab->getApiRoot($hostID), "/projects/$projectID/merge_requests/$MRID"); + $url = sprintf($this->gitlab->getApiRoot($hostID, false), "/projects/$projectID/merge_requests/$MRID"); $MR = json_decode(commonModel::http($url)); } elseif($host->type == 'gitea') @@ -945,11 +948,11 @@ class mrModel extends model $newMR->squash = $MR->squash == '1' ? 1 : 0; if($MR->assignee) { - $gitlabAssignee = $this->gitlab->getUserIDByZentaoAccount($MR->hostID, $MR->assignee); + $gitlabAssignee = $this->gitlab->getUserIDByZentaoAccount($hostID, $MR->assignee); if($gitlabAssignee) $newMR->assignee_ids = $gitlabAssignee; } $url = sprintf($this->gitlab->getApiRoot($hostID), "/projects/$projectID/merge_requests/$MRID"); - return json_decode(commonModel::http($url, $MR, $options = array(CURLOPT_CUSTOMREQUEST => 'PUT'))); + return json_decode(commonModel::http($url, $newMR, $options = array(CURLOPT_CUSTOMREQUEST => 'PUT'))); } else { @@ -962,7 +965,6 @@ class mrModel extends model $assignee = $this->{$host->type}->getUserIDByZentaoAccount($this->post->hostID, $MR->assignee); if($assignee) $newMR->assignee = $assignee; } - $mergeResult = json_decode(commonModel::http($url, $newMR, array(), array(), 'json', 'PATCH')); if(isset($mergeResult->number)) $mergeResult->iid = $host->type == 'gitea' ? $mergeResult->number : $mergeResult->id; if(isset($mergeResult->mergeable)) @@ -1973,7 +1975,7 @@ class mrModel extends model { $product = array(); - if($MR->repoID) + if(is_object($MR) && $MR->repoID) { $productID = $this->dao->select('product')->from(TABLE_REPO)->where('id')->eq($MR->repoID)->fetch('product'); } diff --git a/module/mr/ui/approval.html.php b/module/mr/ui/approval.html.php index 889def6ae8..a04c44c2b1 100644 --- a/module/mr/ui/approval.html.php +++ b/module/mr/ui/approval.html.php @@ -12,6 +12,7 @@ namespace zin; modalHeader ( + set::title(''), set::entityText($MR->title . ' - ' . zget($lang->mr->approvalResultList, $action)), set::entityID($MR->id), ); @@ -30,6 +31,7 @@ formPanel ) : null, formGroup ( + set::width('1/2'), set::label($lang->mr->assignee), set::name('assignedTo'), set::items($users), diff --git a/module/mr/ui/browse.html.php b/module/mr/ui/browse.html.php index de152a2ce1..5038d3cb58 100644 --- a/module/mr/ui/browse.html.php +++ b/module/mr/ui/browse.html.php @@ -10,6 +10,9 @@ declare(strict_types=1); */ namespace zin; +jsVar('orderBy', $orderBy); +jsVar('sortLink', $sortLink); + dropmenu(set::objectID($repo->id), set::text($repo->name), set::tab('repo')); foreach($MRList as $MR) @@ -72,8 +75,10 @@ toolBar dtable ( + set::userMap($users), set::cols($config->mr->dtable->fieldList), set::data($MRs), + set::sortLink(jsRaw('createSortLink')), set::footPager(usePager()), ); diff --git a/module/mr/ui/create.html.php b/module/mr/ui/create.html.php index a9d1b4099f..1d4e4ba8da 100644 --- a/module/mr/ui/create.html.php +++ b/module/mr/ui/create.html.php @@ -17,9 +17,19 @@ jsVar('repo', $repo); jsVar('projectID', $project->id); jsVar('mrLang', $lang->mr); jsVar('branchPrivs', array()); +jsVar('projectNamespace', in_array($repo->SCM, array('Gitea', 'Gogs')) ? $project->name_with_namespace : ''); dropmenu(set::objectID($repo->id), set::text($repo->name), set::tab('repo')); +if(in_array($repo->SCM, array('Gitea', 'Gogs'))) +{ + $projectItem = array($project->name_with_namespace => $project->name_with_namespace); +} +else +{ + $projectItem = array($project->id => $project->name_with_namespace); +} + formPanel ( set::title($lang->mr->create), @@ -39,7 +49,7 @@ formPanel set::label($lang->mr->sourceProject), set::name('sourceProject'), set::id('sourceProject'), - set::items(array($project->id => $project->name_with_namespace)), + set::items($projectItem), set::disabled(true), ), formGroup @@ -60,7 +70,7 @@ formPanel set::label($lang->mr->targetProject), set::id('targetProject'), set::name('targetProject'), - set::items(array($project->id => $project->name_with_namespace)), + set::items($projectItem), set::disabled(true), ), formGroup @@ -125,7 +135,7 @@ formPanel ( set::name('description'), set::label($lang->mr->description), - set::control('editor'), + set::control('textarea'), ), formRow ( diff --git a/module/mr/ui/diff.html.php b/module/mr/ui/diff.html.php index 02dfd01b34..992314fe49 100644 --- a/module/mr/ui/diff.html.php +++ b/module/mr/ui/diff.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +dropmenu(set::objectID($MR->repoID), set::tab('repo')); + detailHeader ( to::title @@ -34,7 +36,7 @@ $fileInfo = $entry ? pathinfo($entry) : array(); $showBug = isset($showBug) ? $showBug : true; $objectID = isset($objectID) ? $objectID : 0; $tree = $this->repo->getFileTree($repo, '', $diffs); -$diffLink = $this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=" . $file . "&oldrevision={oldRevision}&newRevision={newRevision}"); +$diffLink = $this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=&oldrevision={oldRevision}&newRevision={newRevision}"); jsVar('diffs', $diffs); jsVar('tree', $tree); diff --git a/module/mr/ui/link.html.php b/module/mr/ui/link.html.php index c441e7c256..b852b9af06 100644 --- a/module/mr/ui/link.html.php +++ b/module/mr/ui/link.html.php @@ -10,9 +10,14 @@ declare(strict_types=1); */ namespace zin; +$repoName = $this->dao->select('name')->from(TABLE_REPO)->where('id')->eq($MR->repoID)->fetch('name'); +dropmenu(set::objectID($MR->repoID), set::text($repoName), set::tab('repo')); + jsVar('type', $type); jsVar('orderBy', $orderBy); -jsVar('sortLink', createLink('mr', 'link', "MRID={$MR->id}&type={type}&orderBy={orderBy}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}")); +jsVar('sortLink', createLink('mr', 'link', "MRID={$MR->id}&type={type}&orderBy={orderBy}&recTotal=&recPerPage=&pageID=")); + +dropmenu(set::objectID($MR->repoID), set::tab('repo')); $actionMenu = array(); $actionMenu['title'] = $lang->actions; @@ -23,37 +28,32 @@ $actionMenu['list']['unlink']['hint'] = $lang->productplan->unlinkStory; $actionMenu['list']['unlink']['className'] = 'ajax-submit'; $storyCols = $config->release->dtable->story->fieldList; -$storyCols['actions'] = $actionMenu; +$storyCols['actions'] = $actionMenu; +$storyCols['id']['checkbox'] = false; +$storyCols['title']['data-toggle'] = ''; +$storyCols['title']['nestedToggle'] = false; +$storyCols['title']['link'] = array('module' => 'story', 'method' => 'view', 'params' => 'storyID={id}', 'target' => '_blank'); + $storyCols['actions']['list']['unlink']['data-confirm'] = $lang->productplan->confirmUnlinkStory; $storyCols['actions']['list']['unlink']['url'] = $this->createLink('mr', 'unlink', "MRID=$MR->id&productID=$product->id&type=story&linkID={id}&confirm=yes"); -$storyCols['id']['checkbox'] = false; + $stories = initTableData($stories, $storyCols); $bugCols = $config->release->dtable->bug->fieldList; $bugCols['actions'] = $actionMenu; $bugCols['actions']['list']['unlink']['data-confirm'] = $lang->productplan->confirmUnlinkBug; $bugCols['actions']['list']['unlink']['url'] = $this->createLink('mr', 'unlink', "MRID=$MR->id&productID=$product->id&type=bug&linkID={id}&confirm=yes"); -$bugCols['id']['checkbox'] = false; + +$bugCols['id']['checkbox'] = false; +$bugCols['title']['data-toggle'] = ''; +$bugCols['title']['link'] = array('module' => 'bug', 'method' => 'view', 'params' => 'bugID={id}', 'target' => '_blank'); $bugs = initTableData($bugs, $bugCols); -$taskCols = array(); -$taskCols['id'] = $config->task->dtable->fieldList['id']; -$taskCols['name'] = $config->task->dtable->fieldList['name']; -$taskCols['pri'] = $config->task->dtable->fieldList['pri']; -$taskCols['finishedBy'] = $config->task->dtable->fieldList['finishedBy']; -$taskCols['assignedTo'] = $config->task->dtable->fieldList['assignedTo']; -$taskCols['status'] = $config->task->dtable->fieldList['status']; +$taskCols = $config->mr->taskDtable->fieldList; $taskCols['actions'] = $actionMenu; $taskCols['actions']['list']['unlink']['data-confirm'] = $lang->mr->confirmUnlinkTask; $taskCols['actions']['list']['unlink']['url'] = $this->createLink('mr', 'unlink', "MRID=$MR->id&productID=$product->id&type=task&linkID={id}&confirm=yes"); -$taskCols['id']['checkbox'] = false; -$taskCols['name']['title'] = $lang->task->name; -$taskCols['name']['fixed'] = ''; -$taskCols['assignedTo']['title'] = $lang->task->assignedTo; -foreach($taskCols as $col => $colConfig) -{ - if(isset($colConfig['group'])) unset($taskCols[$col]['group']); -} + $tasks = initTableData($tasks, $taskCols); if(common::hasPriv('mr', 'linkStory')) diff --git a/module/mr/ui/linkbug.html.php b/module/mr/ui/linkbug.html.php index 5c7b28bd9f..ee34512231 100644 --- a/module/mr/ui/linkbug.html.php +++ b/module/mr/ui/linkbug.html.php @@ -33,7 +33,8 @@ div $cols = array(); foreach($config->release->dtable->defaultFields['linkBug'] as $field) $cols[$field] = zget($config->bug->dtable->fieldList, $field, array()); $data = array_values($allBugs); -$cols['title']['link'] = array('module' => 'bug', 'method' => 'view', 'params' => 'bugID={id}', 'target' => '_blank'); +$cols['title']['data-toggle'] = ''; +$cols['title']['link'] = array('module' => 'bug', 'method' => 'view', 'params' => 'bugID={id}', 'target' => '_blank'); dtable ( set::userMap($users), diff --git a/module/mr/ui/linkstory.html.php b/module/mr/ui/linkstory.html.php index ffd983eee5..9c6def03ef 100644 --- a/module/mr/ui/linkstory.html.php +++ b/module/mr/ui/linkstory.html.php @@ -33,9 +33,9 @@ div $cols = array(); foreach($config->release->dtable->defaultFields['linkStory'] as $field) $cols[$field] = zget($config->release->dtable->story->fieldList, $field, array()); $cols = array_map(function($col){$col['show'] = true; return $col;}, $cols); -$cols['title']['link'] = array('module' => 'story', 'method' => 'view', 'params' => 'storyID={id}', 'target' => '_blank'); -$cols['title']['nestedToggle'] = false; $cols['title']['data-toggle'] = ''; +$cols['title']['nestedToggle'] = false; +$cols['title']['link'] = array('module' => 'story', 'method' => 'view', 'params' => 'storyID={id}', 'target' => '_blank'); $data = array_values($allStories); dtable diff --git a/module/mr/ui/view.html.php b/module/mr/ui/view.html.php index 34b443be03..d487e59ec2 100644 --- a/module/mr/ui/view.html.php +++ b/module/mr/ui/view.html.php @@ -10,14 +10,14 @@ declare(strict_types=1); */ namespace zin; -$repoName = $this->dao->select('name')->from(TABLE_REPO)->where('id')->eq($MR->repoID)->fetch('name'); -dropmenu(set::objectID($MR->repoID), set::text($repoName), set::tab('repo')); +dropmenu(set::objectID($MR->repoID), set::tab('repo')); $hasNoConflict = $MR->synced === '1' ? $rawMR->has_conflicts : (bool)$MR->hasNoConflict; $sourceDisabled = ($MR->status == 'merged' && $MR->removeSourceBranch == '1') ? 'disabled' : ''; $compileNotSuccess = !empty($compile->id) && $compile->status != 'success'; -$mainActions = array(); +$mainActions = array(); +$suffixActions = array(); foreach($config->mr->view->operateList as $operate) { if(!common::hasPriv('mr', $operate == 'reject' ? 'approval' : $operate)) continue; @@ -29,7 +29,7 @@ foreach($config->mr->view->operateList as $operate) if(in_array($operate, array('approval', 'reject', 'close', 'edit'))) { if(!$MR->synced || $rawMR->state != 'opened') continue; - if($operate == 'reject' && $MR->approvalStatus == 'rejected') continue; + if($operate == 'reject' && $MR->approvalStatus == 'rejected') $action['disabled'] = true; if($operate == 'approval') { @@ -38,6 +38,14 @@ foreach($config->mr->view->operateList as $operate) } if($operate == 'reopen' && (!$MR->synced || $rawMR->state != 'closed')) continue; + if($operate == 'delete' && !$projectOwner && !$this->app->user->admin) $action['disabled'] = true; + if($operate == 'edit' && !$projectEdit && !$this->app->user->admin) $action['disabled'] = true; + + if($operate === 'edit' || $operate === 'delete') + { + $suffixActions[] = $action; + continue; + } $mainActions[] = $action; } @@ -209,7 +217,7 @@ panel item ( set::name($lang->mr->description), - !empty($MR->description) ? $MR->description : $lang->noData, + !empty($MR->description) ? html(nl2br($MR->description)) : $lang->noData, ), ), ), @@ -235,7 +243,7 @@ panel div ( setClass('article-h1'), - $lang->compile->job, + $lang->mr->jobID, ), $job ), @@ -250,6 +258,7 @@ div set::object($MR), isAjaxRequest('modal') ? null : to::prefix(backBtn(set::icon('back'), $lang->goback)), set::main($mainActions), + set::suffix($suffixActions) ), ); diff --git a/module/mr/view/header.review.html.php b/module/mr/view/header.review.html.php index a674196299..cc8a6db864 100644 --- a/module/mr/view/header.review.html.php +++ b/module/mr/view/header.review.html.php @@ -32,7 +32,7 @@ $taskRepoModule = 0; $taskMembers = array('' => ''); if($repoExecution) { - $taskModules = $this->loadModel('tree')->getTaskOptionMenu($repoExecution); + $taskModules = $this->loadModel('tree')->getTaskOptionMenu($repoExecution, 0, 0, ''); $taskRepoModule = (!empty($lastReview->task) && $lastReview->task->execution == $repoExecution) ? $lastReview->task->module : ''; $taskMembers = $this->loadModel('user')->getTeamMemberPairs($repoExecution, 'execution', 'nodeleted'); } diff --git a/module/mr/view/link.html.php b/module/mr/view/link.html.php index 68fd0575a6..108e66d89f 100644 --- a/module/mr/view/link.html.php +++ b/module/mr/view/link.html.php @@ -156,7 +156,7 @@ pri;?>> priAB);?> bug->title);?> openedByAB);?> - bug->abbr->assignedTo);?> + bug->assignedToAB);?> bug->status);?> actions?> diff --git a/module/mr/view/linkbug.html.php b/module/mr/view/linkbug.html.php index 15df1063d2..8a2f0457d7 100644 --- a/module/mr/view/linkbug.html.php +++ b/module/mr/view/linkbug.html.php @@ -30,7 +30,7 @@ pri;?>>priAB;?> bug->title;?> openedByAB;?> - bug->abbr->assignedTo;?> + bug->assignedToAB;?> bug->status;?> diff --git a/module/repo/control.php b/module/repo/control.php index 5e4d8c0883..f26c3bbd2c 100644 --- a/module/repo/control.php +++ b/module/repo/control.php @@ -43,7 +43,7 @@ class repo extends control * @access public * @return void */ - public function commonAction($repoID = 0, $objectID = 0) + public function commonAction(int $repoID = 0, int $objectID = 0) { $tab = $this->app->tab; $this->repos = $this->repo->getRepoPairs($tab, $objectID); @@ -88,9 +88,7 @@ class repo extends control $repoID = $this->repo->saveState(0, $objectID); if($this->viewType !== 'json') $this->commonAction($repoID, $objectID); - $repoList = $this->repo->getList(0, '', $orderBy, null, false, true, $type, $param); - $sonarRepoList = $this->loadModel('job')->getSonarqubeByRepo(array_keys($repoList)); - + $repoList = $this->repo->getList(0, '', $orderBy, null, false, true, $type, $param); /* Pager. */ $this->app->loadClass('pager', true); $recTotal = count($repoList); @@ -99,23 +97,13 @@ class repo extends control $repoList = empty($repoList) ? array() : $repoList[$pageID - 1]; /* Get success jobs of sonarqube.*/ - $jobIDList = array(); - foreach($repoList as $repo) - { - if(isset($sonarRepoList[$repo->id])) $jobIDList[] = $sonarRepoList[$repo->id]->id; - } - $successJobs = $this->loadModel('compile')->getSuccessJobs($jobIDList); + $sonarRepoList = $this->loadModel('job')->getSonarqubeByRepo(array_keys($repoList)); + $successJobs = $this->loadModel('compile')->getSuccessJobs(helper::arrayColumn($sonarRepoList, 'id')); $products = $this->loadModel('product')->getPairs('all', 0, '', 'all'); $projects = $this->loadModel('project')->getPairs(); - session_start(); - $this->config->repo->search['params']['product']['values'] = $products; - $this->config->repo->search['params']['projects']['values'] = $projects; - $this->config->repo->search['actionURL'] = $this->createLink('repo', 'maintain', "objectID={$objectID}&orderBy={$orderBy}&recPerPage={$recPerPage}&pageID={$pageID}&type=bySearch¶m=myQueryID"); - $this->config->repo->search['queryID'] = $param; - $this->config->repo->search['onMenuBar'] = 'yes'; - $this->loadModel('search')->setSearchParams($this->config->repo->search); + $this->repoZen->buildRepoSearchForm($products, $projects, $objectID, $orderBy, $recPerPage, $pageID, $param); $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->browse; $this->view->type = $type; @@ -199,6 +187,7 @@ class repo extends control $actionID = $this->loadModel('action')->create('repo', $repoID, 'edited'); $changes = common::createChanges($repo, $newRepo); $this->action->logHistory($actionID, $changes); + if(!$noNeedSync) { $link = $this->repo->createLink('showSyncCommit', "repoID=$repoID"); @@ -219,20 +208,11 @@ class repo extends control * @access public * @return void */ - public function delete($repoID, $objectID = 0, $confirm = 'no') + public function delete(int $repoID, int $objectID = 0, string $confirm = 'no') { if($confirm == 'no') return print(js::confirm($this->lang->repo->notice->delete, $this->repo->createLink('delete', "repoID=$repoID&objectID=$objectID&confirm=yes"))); - $relationID = $this->dao->select('id')->from(TABLE_RELATION) - ->where('extra')->eq($repoID) - ->andWhere('AType')->eq('design') - ->fetch(); - - $error = $relationID ? $this->lang->repo->error->deleted : ''; - - $jobs = $this->dao->select('*')->from(TABLE_JOB)->where('repo')->eq($repoID)->andWhere('deleted')->eq('0')->fetchAll(); - if($jobs) $error .= ($error ? '\n' : '') . $this->lang->repo->error->linkedJob; - + $error = $this->repoZen->checkDeleteError($repoID); if($error) return $this->send(array('result' => 'fail', 'message' => $error)); $this->dao->delete()->from(TABLE_REPO)->where('id')->eq($repoID)->exec(); @@ -406,7 +386,6 @@ class repo extends control public function view($repoID, $objectID = 0, $entry = '', $revision = 'HEAD', $showBug = 'false', $encoding = '') { set_time_limit(0); - $browser = helper::getBrowser(); if($this->get->repoPath) $entry = $this->get->repoPath; $this->repo->setBackSession('view', true); if($repoID == 0) $repoID = $this->session->repoID; @@ -421,6 +400,8 @@ class repo extends control session_start(); $this->session->set('storyList', inlink('view', "repoID=$repoID&objectID=$objectID&entry=$entry&revision=$revision&showBug=$showBug&encoding=$encoding"), 'product'); session_write_close(); + + $browser = helper::getBrowser(); if($browser['name'] != 'ie') return print($this->fetch('repo', 'monaco', "repoID=$repoID&objectID=$objectID&entry=$entry&revision=$revision&showBug=$showBug&encoding=$encoding")); if($_POST) @@ -524,15 +505,19 @@ class repo extends control /* Get path. */ if($this->get->repoPath) $path = $this->get->repoPath; + $path = $this->repo->decodePath($path); + + if($_POST) + { + $oldRevision = isset($this->post->revision[1]) ? $this->post->revision[1] : ''; + $newRevision = isset($this->post->revision[0]) ? $this->post->revision[0] : ''; + + $this->locate($this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=" . $this->repo->encodePath($path) . "&oldrevision=$oldRevision&newRevision=$newRevision")); + } /* Set menu and session. */ $this->commonAction($repoID, $objectID); - $this->repo->setBackSession('list', true); - - session_start(); - $this->session->set('revisionList', $this->app->getURI(true)); - $this->session->set('gitlabBranchList', $this->app->getURI(true)); - session_write_close(); + $this->repoZen->setBrowseSession(); /* Get repo and synchronous commit. */ $repo = $this->repo->getByID($repoID); @@ -543,98 +528,41 @@ class repo extends control } if(!$repo->synced) $this->locate($this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID")); - if($repo->SCM == 'Gitlab') list($branchInfo, $tagInfo) = $this->repoZen->getBrowseInfo($repo); - /* Set branch or tag for git. */ - $branches = $tags = $branchesAndTags = array(); - if(in_array($repo->SCM, $this->config->repo->gitTypeList)) - { - $scm = $this->app->loadClass('scm'); - $scm->setEngine($repo); - $branches = isset($branchInfo) && $branchInfo !== false ? $branchInfo : $scm->branch(); - $initTags = isset($tagInfo) && $tagInfo !== false ? $tagInfo : $scm->tags(''); - foreach($initTags as $tag) $tags[$tag] = $tag; - $branchesAndTags = $branches + $tags; - - if(empty($branchID) and $this->cookie->repoBranch and $this->session->repoID == $repoID) $branchID = $this->cookie->repoBranch; - if($branchID) $this->repo->setRepoBranch($branchID); - if(!isset($branchesAndTags[$branchID])) - { - $branchID = (string)key($branches); - $this->repo->setRepoBranch($branchID); - } - } - else - { - $this->repo->setRepoBranch(''); - } - - /* Decrypt path. */ - $path = $this->repo->decodePath($path); + $branchInfo = $tagInfo = false; + if($repo->SCM == 'Gitlab') list($branchInfo, $tagInfo) = $this->repoZen->getBrowseInfo($repo); + list($branchID, $branches, $tags, $branchesAndTags) = $this->repoZen->setBranchTag($repo, $branchID, $branchInfo, $tagInfo); /* Load pager. */ $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); - if($_POST) - { - $oldRevision = isset($this->post->revision[1]) ? $this->post->revision[1] : ''; - $newRevision = isset($this->post->revision[0]) ? $this->post->revision[0] : ''; - - $this->locate($this->repo->createLink('diff', "repoID=$repoID&objectID=$objectID&entry=" . $this->repo->encodePath($path) . "&oldrevision=$oldRevision&newRevision=$newRevision")); - } - /* Refresh repo. */ if(empty($refresh) and $this->cookie->repoRefresh) $refresh = $this->cookie->repoRefresh; if($refresh) { $this->repo->updateCommit($repoID, $objectID, $originBranchID); - if($repo->SCM == 'Gitlab') $this->repo->checkDeletedBranches($repoID, $branches); } + if($this->cookie->repoRefresh) helper::setcookie('repoRefresh', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, true); - /* Set logType and revisions. */ - $logType = $type; - $revisions = $this->repo->getCommits($repo, $path, $revision, $logType, $pager); + /* Get revisions. */ + $revisions = $this->repoZen->getCommits($repo, $path, $revision, $type, $pager, $objectID); $lastRevision = current($revisions); $lastRevision = empty($lastRevision) ? new stdclass() : $lastRevision; - $pathInfo = '&root=' . $this->repo->encodePath(empty($path) ? '/' : $path); - foreach($revisions as $item) - { - $item->link = $this->repo->createLink('revision', "repoID=$repoID&objectID=$objectID&revision={$item->revision}" . $pathInfo); - $item->revision = $repo->SCM != 'Subversion' ? substr($item->revision, 0, 10) : $item->revision; - } if($path == '') $this->repoZen->updateLastCommit($repo, $lastRevision); /* Get files info. */ - $infos = $this->repoZen->getFilesInfo($repo, $path, $branchID, $refresh, $revision, $lastRevision); - $base64BranchID = helper::safe64Encode(base64_encode($branchID)); - foreach($infos as $info) - { - $infoPath = trim(urldecode($path) . '/' . $info->name, '/'); - $info->revision = $repo->SCM == 'Subversion' ? $info->revision : substr($info->revision, 0, 10); - if($info->kind == 'dir') - { - $info->link = $this->repo->createLink('browse', "repoID=$repoID&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($infoPath)); - } - else - { - $info->link = $this->repo->createLink('view', "repoID=$repoID&objectID=$objectID&entry=" . $this->repo->encodePath($infoPath)); - } - } - - if($this->cookie->repoRefresh) helper::setcookie('repoRefresh', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, true); + $infos = $this->repoZen->getFilesInfo($repo, $path, $branchID, $refresh, $revision, $lastRevision, $base64BranchID, $objectID); /* Synchronous commit only in root path. */ if(in_array($repo->SCM, $this->config->repo->gitTypeList) and empty($path) and $infos and empty($revisions)) $this->locate($this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID&branch=" . helper::safe64Encode(base64_encode($this->cookie->repoBranch)))); $this->view->title = $this->lang->repo->common; $this->view->repo = $repo; - $this->view->repos = $this->repos; $this->view->revisions = $revisions; - $this->view->repoGroup = $this->repo->getRepoGroup($this->app->tab, $objectID); $this->view->revision = $revision; $this->view->infos = $infos; $this->view->repoID = $repoID; @@ -646,7 +574,7 @@ class repo extends control $this->view->currentProject = $this->app->tab == 'project' ? $this->loadModel('project')->getByID($objectID) : null; $this->view->pager = $pager; $this->view->path = urldecode($path); - $this->view->logType = $logType; + $this->view->logType = $type; $this->view->cloneUrl = $this->repo->getCloneUrl($repo); $this->view->cacheTime = isset($lastRevision->time) ? date('m-d H:i', strtotime($lastRevision->time)) : date('m-d H:i'); $this->view->branchOrTag = $branchOrTag; @@ -1467,7 +1395,8 @@ class repo extends control } } - $logs = array(); + $logs = array(); + $version = 1; if($repo->SCM != 'Gitlab') { $latestInDB = $this->dao->select('t1.*')->from(TABLE_REPOHISTORY)->alias('t1') diff --git a/module/repo/model.php b/module/repo/model.php index 4825e4b79f..18392e54f6 100644 --- a/module/repo/model.php +++ b/module/repo/model.php @@ -167,16 +167,6 @@ class repoModel extends model ->page($pager) ->fetchAll('id'); - if($getCodePath) - { - $gitlabRepos = array(); - foreach($repos as $repo) - { - if($repo->SCM == 'Gitlab') $gitlabRepos[$repo->id] = $repo; - } - $this->loadModel('gitlab')->apiMultiGetProjects($gitlabRepos); - } - /* Get products. */ $productIdList = $this->loadModel('product')->getProductIDByProject($projectID, false); foreach($repos as $i => $repo) @@ -259,20 +249,6 @@ class repoModel extends model */ public function create(object $repo, bool $isPipelineServer): int|false { - if($isPipelineServer) - { - $serviceProject = $this->dao->select('*')->from(TABLE_REPO) - ->where('`SCM`')->eq($repo->SCM) - ->andWhere('`serviceHost`')->eq($repo->serviceHost) - ->andWhere('`serviceProject`')->eq($repo->serviceProject) - ->fetch(); - if($serviceProject) - { - dao::$errors['serviceProject'][] = $this->lang->repo->error->projectUnique; - return false; - } - } - $this->dao->insert(TABLE_REPO)->data($repo, 'serviceToken') ->batchCheck($this->config->repo->create->requiredFields, 'notempty') ->batchCheckIF($repo->SCM != 'Gitlab', 'path,client', 'notempty') @@ -409,9 +385,7 @@ class repoModel extends model if(($repo->serviceHost != $data->serviceHost || $repo->serviceProject != $data->serviceProject) && $repo->path != $data->path) { - $this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($id)->exec(); - $this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($id)->exec(); - $this->dao->delete()->from(TABLE_REPOBRANCH)->where('repo')->eq($id)->exec(); + $this->repoTao->deleteInfoByID($id); return false; } @@ -1437,7 +1411,6 @@ class repoModel extends model */ public function setRepoBranch($branch) { - if(empty($branch)) return; helper::setcookie("repoBranch", $branch, 0, $this->config->webRoot, '', $this->config->cookieSecure, false); $_COOKIE['repoBranch'] = $branch; } @@ -2192,6 +2165,9 @@ class repoModel extends model $commentGroup = $this->loadModel('job')->getTriggerGroup('commit', array($repo->id)); if($repo->SCM == 'Gitlab') { + $scm = $this->app->loadClass('scm'); + $scm->setEngine($repo); + $this->loadModel('repo'); $jobs = zget($commentGroup, $repo->id, array()); @@ -2207,6 +2183,13 @@ class repoModel extends model $log->msg = $commit->message; $log->author = $commit->author->name; $log->date = date("Y-m-d H:i:s", strtotime($commit->timestamp)); + $log->files = array(); + + $diffs = $scm->engine->getFilesByCommit($log->revision); + if(!empty($diffs)) + { + foreach($diffs as $diff) $log->files[$diff->action][] = $diff->path; + } $objects = $this->repo->parseComment($log->msg); $this->repo->saveAction2PMS($objects, $log, $repo->path, $repo->encoding, 'git', $accountPairs); diff --git a/module/repo/tao.php b/module/repo/tao.php index 5a2c84a56b..7b27e31b37 100644 --- a/module/repo/tao.php +++ b/module/repo/tao.php @@ -24,5 +24,20 @@ class repoTao extends repoModel { return $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->orderBy('time_desc')->fetch(); } + + /** + * 根据id删除版本库信息。 + * Delete repo info by id. + * + * @param int $repoID + * @access protected + * @return void + */ + protected function deleteInfoByID(int $repoID): void + { + $this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->exec(); + $this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($repoID)->exec(); + $this->dao->delete()->from(TABLE_REPOBRANCH)->where('repo')->eq($repoID)->exec(); + } } diff --git a/module/repo/test/model/getcommits.php b/module/repo/test/model/getcommits.php index fa625c44f9..144d4bad0f 100755 --- a/module/repo/test/model/getcommits.php +++ b/module/repo/test/model/getcommits.php @@ -5,18 +5,18 @@ include dirname(__FILE__, 5) . '/test/lib/init.php'; /** title=测试 repoModel::getCommits(); +timeout=0 cid=1 -pid=1 -通过repo,path,获取commit列表 >> 1 -通过repo,path,获取commit数量 >> 1 +- 通过repo,path,获取commit列表 @1 +- 通过repo,path,获取commit数量 @1 */ $repoModel = $tester->loadModel('repo'); $repoID = 1; -$repo = $repoModel->getRepoByID($repoID); +$repo = $repoModel->getByID($repoID); $path = ''; $result = $repoModel->getCommits($repo, $path); diff --git a/module/repo/ui/browse.html.php b/module/repo/ui/browse.html.php index 5c45732faa..81dcee973a 100644 --- a/module/repo/ui/browse.html.php +++ b/module/repo/ui/browse.html.php @@ -83,7 +83,7 @@ if($fileName) $breadcrumbItems[] = h::span($fileName); set::text($repo->name), set::url(createLink('repo', 'ajaxGetDropMenu', "repoID={$repo->id}&module=repo&method=browse&projectID={$objectID}")) ) : null, - $repo->SCM != 'Subversion' ? dropmenu + ($repo->SCM != 'Subversion' && ($branches || $tags)) ? dropmenu ( setID('repoBranchDropMenu'), set::objectID($selected), diff --git a/module/repo/ui/monaco.html.php b/module/repo/ui/monaco.html.php index f46a5f80a4..d03eda2fcf 100644 --- a/module/repo/ui/monaco.html.php +++ b/module/repo/ui/monaco.html.php @@ -67,7 +67,7 @@ div( ), div(set::id('tab-' . $file)), ), - dropdown + empty($monacoDropMenus) ? null : dropdown ( set::arrow(false), set::staticMenu(true), diff --git a/module/repo/zen.php b/module/repo/zen.php index fb3bfaa6a6..f7ff6f9c09 100644 --- a/module/repo/zen.php +++ b/module/repo/zen.php @@ -60,6 +60,20 @@ class repoZen extends repo $repo->prefix = empty($infoRoot) ? '' : trim(str_ireplace($infoRoot, '', str_replace('\\', '/', $repo->path)), '/'); if($repo->prefix) $repo->prefix = '/' . $repo->prefix; } + + if($isPipelineServer) + { + $serviceProject = $this->dao->select('*')->from(TABLE_REPO) + ->where('`SCM`')->eq($repo->SCM) + ->andWhere('`serviceHost`')->eq($repo->serviceHost) + ->andWhere('`serviceProject`')->eq($repo->serviceProject) + ->fetch(); + if($serviceProject) + { + dao::$errors['serviceProject'][] = $this->lang->repo->error->projectUnique; + return false; + } + } return $repo; } @@ -467,46 +481,58 @@ class repoZen extends repo * @param int $refresh * @param string $revision * @param object $lastRevision + * @param string $base64BranchID + * @param int $objectID * @access protected * @return array */ - protected function getFilesInfo(object $repo, string $path, string $branchID, int $refresh, string $revision, object $lastRevision): array + protected function getFilesInfo(object $repo, string $path, string $branchID, int $refresh, string $revision, object $lastRevision, string $base64BranchID, int $objectID): array { - $cacheFile = $this->repo->getCacheFile($repo->id, $path, $branchID); - $cacheRefreshTime = isset($lastRevision->time) ? date('Y-m-d H:i', strtotime($lastRevision->time)) : date('Y-m-d H:i'); - $this->scm->setEngine($repo); - if($refresh or !$cacheFile or !file_exists($cacheFile) or filemtime($cacheFile) < strtotime($cacheRefreshTime)) + if($repo->SCM == 'Gitlab') { - if($repo->SCM == 'Gitlab') + $cacheFile = $this->repo->getCacheFile($repo->id, $path, $branchID); + $cacheRefreshTime = isset($lastRevision->time) ? date('Y-m-d H:i', strtotime($lastRevision->time)) : date('Y-m-d H:i'); + $this->scm->setEngine($repo); + if($refresh or !$cacheFile or !file_exists($cacheFile) or filemtime($cacheFile) < strtotime($cacheRefreshTime)) { $infos = $this->repo->getFileList($repo, $branchID, $path); - } - else - { - $infos = $this->scm->ls($path, $revision); - $revisionList = helper::arrayColumn($infos, 'revision', 'revision'); - $comments = $this->repo->getHistory($repo->id, $revisionList); - foreach($infos as $info) + + if($cacheFile && !empty($infos)) { - if(isset($comments[$info->revision])) + if(!file_exists($cacheFile . '.lock')) { - $comment = $comments[$info->revision]; - $info->comment = $comment->comment; + touch($cacheFile . '.lock'); + file_put_contents($cacheFile, serialize($infos)); + unlink($cacheFile . '.lock'); } } } - if($cacheFile && !empty($infos)) file_put_contents($cacheFile, serialize($infos), LOCK_EX); + else + { + $infos = unserialize(file_get_contents($cacheFile)); + if(empty($infos)) unlink($cacheFile); + } } else { - $infos = unserialize(file_get_contents($cacheFile)); - if(empty($infos)) unlink($cacheFile); + $infos = $this->repo->getFileCommits($repo, $branchID, $path); } foreach($infos as $info) { $info->originalComment = $info->comment; $info->comment = $this->repo->replaceCommentLink($info->comment); + $info->revision = $repo->SCM == 'Subversion' ? $info->revision : substr($info->revision, 0, 10); + + $infoPath = trim(urldecode($path) . '/' . $info->name, '/'); + if($info->kind == 'dir') + { + $info->link = $this->repo->createLink('browse', "repoID={$repo->id}&branchID=$base64BranchID&objectID=$objectID&path=" . $this->repo->encodePath($infoPath)); + } + else + { + $info->link = $this->repo->createLink('view', "repoID={$repo->id}&objectID=$objectID&entry=" . $this->repo->encodePath($infoPath)); + } } return $infos; @@ -666,5 +692,136 @@ class repoZen extends repo return array(isset($branches) ? $branches : false, isset($tags) ? $tags : false); } } -} + /** + * 为git类型版本库设置分支和tag。 + * Set branch or tag for git. + * + * @param object $repo + * @param string $branchID + * @param array|bool $branchInfo + * @param array|bool $tagInfo + * @access protected + * @return array + */ + protected function setBranchTag(object $repo, string $branchID, array|bool $branchInfo = false, array|bool $tagInfo = false): array + { + $repoID = $repo->id; + $branches = $tags = $branchesAndTags = array(); + if(in_array($repo->SCM, $this->config->repo->gitTypeList)) + { + $scm = $this->app->loadClass('scm'); + $scm->setEngine($repo); + $branches = isset($branchInfo) && $branchInfo !== false ? $branchInfo : $scm->branch(); + $initTags = isset($tagInfo) && $tagInfo !== false ? $tagInfo : $scm->tags(''); + foreach($initTags as $tag) $tags[$tag] = $tag; + $branchesAndTags = $branches + $tags; + + if(empty($branchID) and $this->cookie->repoBranch and $this->session->repoID == $repoID) $branchID = $this->cookie->repoBranch; + if($branchID) $this->repo->setRepoBranch($branchID); + if(!isset($branchesAndTags[$branchID])) + { + $branchID = (string)key($branches); + $this->repo->setRepoBranch($branchID); + } + + return array($branchID, $branches, $tags, $branchesAndTags); + } + else + { + $this->repo->setRepoBranch(''); + return array('', array(), array(), array()); + } + } + + /** + * 获取commits列表 + * Get commits. + * + * @param object $repo + * @param string $path + * @param string $revision + * @param string $type + * @param object $pager + * @param int $objectID + * @access protected + * @return array + */ + protected function getCommits(object $repo, string $path, string $revision, string $type, object $pager, int $objectID): array + { + $revisions = $this->repo->getCommits($repo, $path, $revision, $type, $pager); + $pathInfo = '&root=' . $this->repo->encodePath(empty($path) ? '/' : $path); + foreach($revisions as $item) + { + $item->link = $this->repo->createLink('revision', "repoID={$repo->id}&objectID=$objectID&revision={$item->revision}" . $pathInfo); + $item->revision = $repo->SCM != 'Subversion' ? substr($item->revision, 0, 10) : $item->revision; + } + + return $revisions; + } + + /** + * 设置session信息。 + * Set session. + * + * @access protected + * @return void + */ + protected function setBrowseSession(): void + { + $this->repo->setBackSession('list', true); + + session_start(); + $this->session->set('revisionList', $this->app->getURI(true)); + $this->session->set('gitlabBranchList', $this->app->getURI(true)); + session_write_close(); + } + + /** + * 构建版本库搜索框。 + * Build repo search form. + * + * @param array $products + * @param array $projects + * @param int $objectID + * @param string $orderBy + * @param int $recPerPage + * @param int $pageID + * @param int $param + * @access protected + * @return void + */ + protected function buildRepoSearchForm(array $products, array $projects, int $objectID, string $orderBy, int $recPerPage, int $pageID, int $param): void + { + session_start(); + $this->config->repo->search['params']['product']['values'] = $products; + $this->config->repo->search['params']['projects']['values'] = $projects; + $this->config->repo->search['actionURL'] = $this->createLink('repo', 'maintain', "objectID={$objectID}&orderBy={$orderBy}&recPerPage={$recPerPage}&pageID={$pageID}&type=bySearch¶m=myQueryID"); + $this->config->repo->search['queryID'] = $param; + $this->config->repo->search['onMenuBar'] = 'yes'; + $this->loadModel('search')->setSearchParams($this->config->repo->search); + session_write_close(); + } + + /** + * 检查删除版本库的错误。 + * Check repo delete error. + * + * @param int $repoID + * @access protected + * @return string + */ + protected function checkDeleteError(int $repoID): string + { + $relationID = $this->dao->select('id')->from(TABLE_RELATION) + ->where('extra')->eq($repoID) + ->andWhere('AType')->eq('design') + ->fetch(); + $error = $relationID ? $this->lang->repo->error->deleted : ''; + + $jobs = $this->dao->select('*')->from(TABLE_JOB)->where('repo')->eq($repoID)->andWhere('deleted')->eq('0')->fetchAll(); + if($jobs) $error .= ($error ? '\n' : '') . $this->lang->repo->error->linkedJob; + + return $error; + } +} diff --git a/module/space/ui/browse.html.php b/module/space/ui/browse.html.php index b0355f6e91..4463e2a3cd 100644 --- a/module/space/ui/browse.html.php +++ b/module/space/ui/browse.html.php @@ -14,7 +14,7 @@ jsVar('orderBy', $orderBy); jsVar('sortLink', $sortLink); $statusMap = array(); -$canInstall = hasPriv('instance', 'mange'); +$canInstall = hasPriv('instance', 'manage'); foreach($instances as $instance) if('store' === $instance->type) $statusMap[$instance->id] = $instance->status; jsVar('statusMap', $statusMap);