* @package mr * @link https://www.zentao.net * @property mrModel $mr * @property mrZen $mrZen */ class mr extends control { /** * The gitlab constructor. * @param string $moduleName * @param string $methodName */ public function __construct(string $moduleName = '', string $methodName = '') { parent::__construct($moduleName, $methodName); /* This is essential when changing tab(menu) from gitlab to repo. */ /* Optional: common::setMenuVars('devops', $this->session->repoID); */ if($this->app->getMethodName() != 'browse') { $this->loadModel('ci')->setMenu(); $this->view->objectID = 0; if(in_array($this->app->tab, array('execution', 'project'))) $this->view->objectID = $this->session->{$this->app->tab}; if($this->app->tab == 'execution') { $this->view->executionID = $this->session->execution; $this->loadModel('execution')->setMenu((int)$this->session->execution); } if($this->app->tab == 'project') { $this->view->projectID = $this->session->project; $this->loadModel('project')->setMenu((int)$this->session->project); } /* Unlock session for wait to get data of repo. */ if(!in_array(strtolower($this->app->methodName), array('linkbug', 'linkstory', 'linktask'))) session_write_close(); } } /** * 获取合并请求列表. * Browse mr. * * @param int $repoID * @param string $mode * @param string $param * @param int $objectID * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function browse(int $repoID = 0, string $mode = 'status', string $param = 'opened', int $objectID = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { if($this->app->tab == 'execution') { $this->session->set('execution', $objectID); $execution = $this->loadModel('execution')->getByID($objectID); if($execution && $execution->type === 'kanban') return $this->locate($this->createLink('execution', 'kanban', "executionID=$objectID")); $features = $this->execution->getExecutionFeatures($execution); if(!$features['devops']) return $this->locate($this->createLink('execution', 'task', "objectID=$objectID")); $this->loadModel('execution')->setMenu($objectID); } elseif($this->app->tab == 'project') { $this->session->set('project', $objectID); $this->loadModel('project')->setMenu($objectID); } if(in_array($this->app->tab, array('execution', 'project')) && $objectID) return print($this->fetch('mr', 'browseByExecution', "repoID={$repoID}&mode={$mode}¶m={$param}&objectID={$objectID}&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}")); session_write_close(); $repoList = $this->loadModel('repo')->getListBySCM(implode(',', $this->config->repo->gitServiceTypeList)); if(empty($repoList)) $this->locate($this->repo->createLink('create')); if(!$repoID) $repoID = key($repoList); $repoID = $this->repo->saveState($repoID, $objectID); if(!isset($repoList[$repoID])) return $this->locate($this->createLink('repo', 'browse', "repoID=$repoID&objectID=$objectID")); $repo = $repoList[$repoID]; $this->loadModel('ci')->setMenu($repo->id); if($param == 'assignee' || $param == 'creator') { $mode = $param; $param = $this->app->user->account; } $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); $filterProjects = empty($repo->serviceProject) ? array() : array($repo->serviceHost => $repo->serviceProject); $MRList = $this->mr->getList($mode, $param, $orderBy, $filterProjects, $repoID, 0, $pager); $projects = $this->mrZen->getAllProjects($repo); $this->view->title = $this->lang->mr->common . $this->lang->hyphen . $this->lang->mr->browse; $this->view->MRList = $MRList; $this->view->projects = $projects; $this->view->pager = $pager; $this->view->mode = $mode; $this->view->param = $param; $this->view->objectID = $objectID; $this->view->repo = $repo; $this->view->orderBy = $orderBy; $this->view->openIDList = $this->app->user->admin ? array() : $this->loadModel('pipeline')->getProviderPairsByAccount($repo->SCM); $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->display(); } /** * 获取执行下合并请求列表. * Browse mr for execution. * * @param int $repoID * @param string $mode * @param string $param * @param int $projectID * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function browseByExecution(int $repoID = 0, string $mode = 'status', string $param = 'opened', int $projectID = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { if($param == 'assignee' || $param == 'creator') { $mode = $param; $param = $this->app->user->account; } $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); $MRList = $this->mr->getList($mode, $param, $orderBy, array(), $repoID, $projectID, $pager); $repoList = $this->loadModel('repo')->getList($projectID); $repoPairs = array(); foreach($repoList as $repo) { if(!in_array($repo->SCM, $this->config->repo->gitServiceTypeList)) continue; $repoPairs[$repo->id] = $repo->name; if($repoID && $repoID != $repo->id) continue; } $openIDList = array(); if(!$this->app->user->admin) { $this->loadModel('pipeline'); foreach(array('gitlab', 'gitea', 'gogs') as $service) $openIDList += $this->pipeline->getProviderPairsByAccount($service); } $objectName = $this->app->tab == 'project' ? 'projectID' : 'executionID'; $this->view->{$objectName} = $projectID; $this->view->title = $this->lang->mr->common . $this->lang->hyphen . $this->lang->mr->browse; $this->view->MRList = $MRList; $this->view->pager = $pager; $this->view->mode = $mode; $this->view->repoID = $repoID; $this->view->param = $param; $this->view->objectID = $projectID; $this->view->repoList = $repoList; $this->view->repoPairs = $repoPairs; $this->view->orderBy = $orderBy; $this->view->openIDList = $openIDList; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->display(); } /** * 创建合并请求. * Create MR function. * * @param int $repoID * @param int $objectID * @access public * @return void */ public function create(int $repoID = 0, int $objectID = 0) { if($_POST) { $MR = form::data($this->config->mr->form->create) ->setIF($this->post->needCI == 0, 'jobID', 0) ->add('createdBy', $this->app->user->account) ->skipSpecial('title,description') ->get(); $result = $this->mr->create($MR); return $this->send($result); } $repoID = $this->loadModel('repo')->saveState($repoID); $repo = $this->repo->getByID($repoID); if(!in_array($repo->SCM, $this->config->repo->gitServiceTypeList)) { $repoList = $this->repo->getListBySCM(implode(',', $this->config->repo->gitServiceTypeList)); $repoID = key($repoList); $repoID = $this->loadModel('repo')->saveState($repoID); $repo = $repoList[$repoID]; } if(in_array($repo->SCM, $this->config->repo->notSyncSCM)) $repo->serviceProject = (int)$repo->serviceProject; $project = $this->loadModel(strtolower($repo->SCM))->apiGetSingleProject($repo->gitService, $repo->serviceProject, false); $jobPairs = array(); $jobs = $this->loadModel('job')->getListByRepoID($repoID); foreach($jobs as $job) $jobPairs[$job->id] = "[{$job->id}]{$job->name}"; $repoPairs = array(); if(in_array($this->app->tab, array('execution', 'project')) && $objectID) { $repoList = $this->loadModel('repo')->getList($objectID); foreach($repoList as $repoInfo) { if(in_array($repoInfo->SCM, $this->config->repo->gitServiceTypeList)) $repoPairs[$repoInfo->id] = $repoInfo->name; } } $this->view->title = $this->lang->mr->create; $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); $this->view->repo = $repo; $this->view->repoPairs = $repoPairs; $this->view->project = $project; $this->view->executionID = $objectID; $this->view->objectID = $objectID; $this->view->jobPairs = $jobPairs; $this->display(); } /** * 编辑合并请求。 * Edit MR function. * * @param int $MRID * @access public * @return void */ public function edit(int $MRID) { if($_POST) { $MR = form::data($this->config->mr->form->edit) ->setIF($this->post->needCI == 0, 'jobID', 0) ->add('editedBy', $this->app->user->account) ->skipSpecial('title,description') ->get(); $result = $this->mr->update($MRID, $MR); return $this->send($result); } $MR = $this->mr->fetchByID($MRID); if(isset($MR->hostID)) $rawMR = $this->mr->apiGetSingleMR($MR->repoID, $MR->mriid); $this->view->title = $this->lang->mr->edit; $this->view->MR = $MR; $this->view->rawMR = isset($rawMR) ? $rawMR : false; if(!isset($rawMR->id) || empty($rawMR)) return $this->display(); /* Fetch user list both in Zentao and current GitLab project. */ $host = $this->loadModel('pipeline')->getByID($MR->hostID); $this->view->host = $host; $this->mrZen->assignEditData($MR, $host->type); } /** * 删除合并请求。 * Delete a MR. * * @param int $MRID * @access public * @return void */ public function delete(int $MRID) { $mr = $this->mr->fetchByID($MRID); $this->mr->deleteByID($MRID); if(dao::isError()) return $this->sendError(dao::getError()); return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'browse', "repoID={$mr->repoID}"))); } /** * 合并请求详情。 * View a MR. * * @param int $MRID * @access public * @return void */ public function view(int $MRID) { $oldMR = $this->mr->fetchByID($MRID); if(!$oldMR) return $this->locate($this->createLink($this->app->rawModule, 'browse')); if(isset($oldMR->hostID)) $rawMR = $this->mr->apiGetSingleMR($oldMR->repoID, $oldMR->mriid); if($oldMR->synced && (!isset($rawMR->id) || empty($rawMR))) $this->sendError($this->lang->mr->apiError->emptyResponse, true); /* Sync MR from GitLab to ZenTaoPMS. */ $MR = $this->mr->apiSyncMR($oldMR); $host = $this->loadModel('pipeline')->getByID($MR->hostID); if(in_array($host->type, $this->config->pipeline->formatTypeService)) { $MR->sourceProject = (int)$MR->sourceProject; $MR->targetProject = (int)$MR->targetProject; } $sourceProject = $this->loadModel($host->type)->apiGetSingleProject($MR->hostID, $MR->sourceProject, false); $compile = $this->loadModel('compile')->getByID($MR->compileID); $this->view->title = $this->lang->mr->view; $this->view->MR = $MR; $this->view->rawMR = isset($rawMR) ? $rawMR : false; $this->view->repo = $this->loadModel('repo')->getByID($MR->repoID); $this->view->reviewer = $this->loadModel('user')->getById($MR->assignee); $this->view->actions = $this->loadModel('action')->getList($this->app->rawModule, $MRID); $this->view->compile = $compile; $this->view->hasNewCommit = $compile ? $this->mrZen->checkNewCommit($host->type, $MR->hostID, (string)$MR->targetProject, $MR->mriid, $compile->createdDate) : false; $this->view->sourceProject = $sourceProject; $this->view->targetProject = $this->{$host->type}->apiGetSingleProject($MR->hostID, $MR->targetProject, false); $this->view->sourceBranch = $this->mrZen->getBranchUrl($host, $MR->sourceProject, $MR->sourceBranch); $this->view->targetBranch = $this->mrZen->getBranchUrl($host, $MR->targetProject, $MR->targetBranch); $this->display(); } /** * 定时任务,从GitLab API同步合并请求状态到禅道数据库,默认5分钟执行一次。 * 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(int $MRID) { $MR = $this->mr->fetchByID($MRID); /* Judge that if this MR can be accepted. */ if(isset($MR->needCI) and $MR->needCI == '1') { $compileStatus = empty($MR->compileID) ? 'fail' : $this->loadModel('compile')->getByID($MR->compileID)->status; if(isset($compileStatus) and $compileStatus != 'success') { return $this->sendError($this->lang->mr->needCI); } } if(isset($MR->needApproved) and $MR->needApproved == '1') { if($MR->approvalStatus != 'approved') { return $this->sendError($this->lang->mr->needApproved); } } if(isset($MR->hostID)) $rawMR = $this->mr->apiAcceptMR($MR); if(isset($rawMR->state) and $rawMR->state == 'merged') { $this->mr->logMergedAction($MR); return $this->sendSuccess(array('message' => $this->lang->mr->mergeSuccess, 'load' => true)); } /* The type of variable `$rawMR->message` is string. This is different with apiCreateMR. */ if(isset($rawMR->message)) { $errorMessage = $this->mr->convertApiError($rawMR->message); return $this->sendError(sprintf($this->lang->mr->apiError->sudo, $errorMessage)); } return $this->sendError($this->lang->mr->mergeFailed); } /** * 获取合并请求的对比信息。 * View diff between MR source and target branches. * * @param int $MRID * @access public * @return void */ public function diff(int $MRID, string $encoding = '') { $encoding = empty($encoding) ? 'utf-8' : $encoding; $encoding = strtolower(str_replace('_', '-', $encoding)); /* Revert $config->requestFix in $encoding. */ $MR = $this->mr->fetchByID($MRID); $this->view->title = $this->lang->mr->viewDiff; $this->view->MR = $MR; $this->view->compile = $this->loadModel('compile')->getById($MR->compileID); if($MR->synced) { $rawMR = $this->mr->apiGetSingleMR($MR->repoID, $MR->mriid); $this->view->rawMR = $rawMR; if(!isset($rawMR->id) || empty($rawMR)) return $this->display(); } $diffs = $this->mr->getDiffs($MR, $encoding); $arrange = $this->cookie->arrange ? $this->cookie->arrange : 'inline'; if($this->server->request_method == 'POST') { if($this->post->arrange) { $arrange = $this->post->arrange; helper::setcookie('arrange', $arrange); } if($this->post->encoding) $encoding = $this->post->encoding; } $this->view->repo = $this->loadModel('repo')->getByID($MR->repoID); $this->view->diffs = $arrange == 'appose' ? $this->repo->getApposeDiff($diffs) : $diffs; $this->view->encoding = $encoding; $this->view->oldRevision = $MR->targetBranch; $this->view->newRevision = $MR->sourceBranch; $this->display(); } /** * 审核合并请求。 * Approval for this MR. * * @param int $MRID * @param string $action * @return void */ public function approval(int $MRID, string $action = 'approve') { $MR = $this->mr->fetchByID($MRID); if($_POST) { $comment = $this->post->comment; $result = $this->mr->approve($MR, $action, $comment); return $this->send($result); } $showCompileResult = false; if(!empty($MR->compileStatus)) { $showCompileResult = true; $this->view->compileUrl = $this->createLink('job', 'view', "jobID={$MR->jobID}&compileID={$MR->compileID}"); } $this->view->MR = $MR; $this->view->action = $action; $this->view->actions = $this->loadModel('action')->getList($this->app->rawModule, $MRID); $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); $this->view->showCompileResult = $showCompileResult; $this->display(); } /** * 关闭合并请求。 * Close this MR. * * @param int $MRID * @access public * @return void */ public function close(int $MRID) { $MR = $this->mr->fetchByID($MRID); $result = $this->mr->close($MR); return $this->send($result); } /** * 重新打开合并请求。 * Reopen this MR. * * @param int $MRID * @access public * @return void */ public function reopen(int $MRID) { $MR = $this->mr->fetchByID($MRID); return $this->send($this->mr->reopen($MR)); } /** * 获取合并请求的关联信息。 * link MR list. * * @param int $MRID * @param string $type * @param string $orderBy * @param string $param * @param int $recPerPage * @param int $pageID * @return void */ public function link(int $MRID, string $type = 'story', string $orderBy = 'id_desc', string $param = '', int $recPerPage = 20, int $pageID = 1) { $MR = $this->mr->fetchByID($MRID); $product = $this->mr->getMRProduct($MR); /* Load pager. */ $this->app->loadClass('pager', true); $storyPager = new pager(0, $recPerPage, $type == 'story' ? $pageID : 1); $bugPager = new pager(0, $recPerPage, $type == 'bug' ? $pageID : 1); $taskPager = new pager(0, $recPerPage, $type == 'task' ? $pageID : 1); $productID = $product ? $product->id : 0; $stories = $this->mr->getLinkList($MRID, 'story', $type == 'story' ? $orderBy : '', $storyPager); $bugs = $this->mr->getLinkList($MRID, 'bug', $type == 'bug' ? $orderBy : '', $bugPager); $tasks = $this->mr->getLinkList($MRID, 'task', $type == 'task' ? $orderBy : '', $taskPager); $builds = $this->loadModel('build')->getBuildPairs($productID); $this->view->title = $this->lang->mr->common . $this->lang->hyphen . $this->lang->mr->link; $this->view->MR = $MR; $this->view->repoID = $MR->repoID; $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->stories = $stories; $this->view->bugs = $bugs; $this->view->tasks = $tasks; $this->view->product = $product; $this->view->storyPager = $storyPager; $this->view->bugPager = $bugPager; $this->view->taskPager = $taskPager; $this->view->type = $type; $this->view->builds = $builds; $this->view->orderBy = $orderBy; $this->view->param = $param; $this->display(); } /** * 获取合并请求可关联的需求列表。 * Link story to mr. * * @param int $MRID * @param int $repoID * @param string $browseType * @param int $param * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function linkStory(int $MRID, int $repoID = 0, string $browseType = '', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 10, int $pageID = 1) { if(!empty($_POST['stories'])) { $this->mr->link($MRID, 'story', $this->post->stories); if(dao::isError()) return $this->sendError(dao::getError()); $link = $this->createLink($this->app->rawModule,'link', "MRID=$MRID&type=story&orderBy=$orderBy"); return $this->sendSuccess(array('load' => $link, 'closeModal' => true)); } $this->loadModel('story'); /* Load pager. */ $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); /* Build search form. */ $queryID = ($browseType == 'bySearch') ? (int) $param : 0; $this->mrZen->buildLinkStorySearchForm($MRID, $repoID, $orderBy, $queryID); $repo = $this->loadModel('repo')->fetchByID($repoID); $productID = $repo ? $repo->product : 0; $linkedStories = $this->mr->getLinkList($MRID, 'story'); if($browseType == 'bySearch') { $this->session->set('repoID', $repoID); $allStories = $this->story->getBySearch('all', 0, $queryID, $orderBy, 0, 'story', array_keys($linkedStories), '', $pager); } else { $allStories = $this->story->getProductStories($productID, 'all', '0', 'draft,reviewing,active,changing', 'story', $orderBy, true, array_keys($linkedStories), $pager); } $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->allStories = $allStories; $this->view->MRID = $MRID; $this->view->browseType = $browseType; $this->view->param = $param; $this->view->orderBy = $orderBy; $this->view->repoID = $repoID; $this->view->pager = $pager; $this->display(); } /** * 获取合并请求可关联的Bug列表。 * Link bug to mr. * * @param int $MRID * @param int $repoID * @param string $browseType * @param int $param * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function linkBug(int $MRID, int $repoID = 0, string $browseType = '', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 10, int $pageID = 1) { if(!empty($_POST['bugs'])) { $this->mr->link($MRID, 'bug', $this->post->bugs); if(dao::isError()) return $this->sendError(dao::getError()); $link = $this->createLink($this->app->rawModule,'link', "MRID=$MRID&type=bug&orderBy=$orderBy"); return $this->sendSuccess(array('load' => $link, 'closeModal' => true)); } $this->loadModel('bug'); $queryID = ($browseType == 'bysearch') ? (int)$param : 0; $this->mrZen->buildLinkBugSearchForm($MRID, $repoID, $orderBy, $queryID); /* Load pager. */ $this->app->loadClass('pager', true); $pager = new pager($recTotal, $recPerPage, $pageID); /* Build search form. */ $repo = $this->loadModel('repo')->fetchByID($repoID); $productIdList = $repo ? explode(',', trim($repo->product, ',')) : 0; $linkedBugs = $this->mr->getLinkList($MRID, 'bug'); if($browseType == 'bySearch') { $allBugs = $this->bug->getBySearch('bug', $productIdList, 0, 0, 0, $queryID, implode(',', array_keys($linkedBugs)), $orderBy, $pager); } else { $allBugs = $this->bug->getActiveBugs($productIdList, 0, '0', array_keys($linkedBugs), $pager, $orderBy); } $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->allBugs = $allBugs; $this->view->MRID = $MRID; $this->view->browseType = $browseType; $this->view->param = $param; $this->view->orderBy = $orderBy; $this->view->pager = $pager; $this->view->repoID = $repoID; $this->display(); } /** * 获取合并请求可关联的任务列表。 * Link task to mr. * * @param int $MRID * @param int $repoID * @param string $browseType * @param int $param * @param string $orderBy * @param int $recTotal * @param int $recPerPage * @param int $pageID * @access public * @return void */ public function linkTask(int $MRID, int $repoID = 0, string $browseType = 'unclosed', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 10, int $pageID = 1) { if(!empty($_POST['tasks'])) { $this->mr->link($MRID, 'task', $this->post->tasks); if(dao::isError()) return $this->sendError(dao::getError()); $link = $this->createLink($this->app->rawModule,'link', "MRID=$MRID&type=task&orderBy=$orderBy"); return $this->sendSuccess(array('load' => $link, 'closeModal' => true)); } /* Set browse type. */ $browseType = strtolower($browseType); $queryID = ($browseType == 'bysearch') ? (int)$param : 0; /* Get executions by product. */ $this->loadModel('product'); $executions = array(); $repo = $this->loadModel('repo')->fetchByID($repoID); $productIdList = $repo ? explode(',', trim($repo->product, ',')) : array(); foreach($productIdList as $productID) { if(empty($productID)) continue; $executions = $executions + $this->product->getExecutionPairsByProduct((int)$productID); } $this->loadModel('execution'); $this->mrZen->buildLinkTaskSearchForm($MRID, $repoID, $orderBy, $queryID, $executions); $linkedTasks = $this->mr->getLinkList($MRID, 'task'); /* Get tasks by executions. */ $allTasks = array(); if($browseType == 'bysearch') { $allTasks = $this->execution->getTasks(0, 0, $executions, $browseType, $queryID, 0, $orderBy, null); } else { $this->loadModel('task'); $queryStatus = $this->lang->task->statusList; unset($queryStatus['closed']); $condition = new stdclass(); $condition->statusList = array_keys($queryStatus); $condition->executionList = array_keys($executions); $allTasks = $this->loadModel('task')->getListByCondition($condition); } /* Filter linked tasks. */ $linkedTaskIDs = array_keys($linkedTasks); foreach($allTasks as $key => $task) { if(in_array($task->id, $linkedTaskIDs)) unset($allTasks[$key]); } $this->mrZen->processLinkTaskPager($recTotal, $recPerPage, $pageID, $allTasks); $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->view->MRID = $MRID; $this->view->browseType = $browseType; $this->view->param = $param; $this->view->orderBy = $orderBy; $this->view->repoID = $repoID; $this->display(); } /** * 解除合并请求关联的对象。 * UnLink an mr link. * * @param int $MRID * @param string $type * @param int $linkID * @access public * @return void */ public function unlink(int $MRID, string $type, int $linkID) { $this->mr->unlink($MRID, $type, $linkID); if(dao::isError()) return $this->sendError(dao::getError()); return $this->sendSuccess(array('message' => '', 'load' => $this->createLink($this->app->rawModule, 'link', "MRID=$MRID&type=$type"))); } /** * 查看合并请求的提交记录。 * Show the commit logs for this Merge Request. * * @param int $MRID * @return void */ public function commitLogs(int $MRID) { $MR = $this->mr->fetchByID($MRID); $compile = $this->loadModel('compile')->getById($MR->compileID); $this->view->title = $this->lang->mr->commitLogs; $this->view->MR = $MR; $this->view->compile = $compile; if($MR->synced) { $rawMR = $this->mr->apiGetSingleMR($MR->repoID, $MR->mriid); $this->view->rawMR = $rawMR; if(!isset($rawMR->id) || empty($rawMR)) return $this->display(); } $repo = $this->loadModel('repo')->getByID($MR->repoID); $commitLogs = $this->mr->apiGetMRCommits($MR->hostID, $MR->targetProject, $MR->mriid); foreach($commitLogs as $commitLog) { $commitLog->repoID = $MR->repoID; if(in_array(strtolower($repo->SCM), array('gitea', 'gogs'))) { $commitLog->id = $commitLog->sha; $commitLog->committed_date = $commitLog->author->committer->date; $commitLog->committer_name = $commitLog->author->committer->name; $commitLog->committer_email = $commitLog->author->committer->email; $commitLog->title = $commitLog->commit->message; } $commitLog->id = substr($commitLog->id, 0, 10); } $this->view->commitLogs = $commitLogs; $this->view->repo = $repo; $this->display(); } /** * 获取构建列表。 * AJAX: Get job list. * * @param int $repoID * @return void */ public function ajaxGetJobList(int $repoID) { $jobList = $this->loadModel('job')->getListByRepoID($repoID); if(!$jobList) return $this->send(array('message' => array())); $options = ""; foreach($jobList as $job) $options .= ""; $this->send($options); } /** * 检查是否有相同的合并请求。 * Ajax check same opened mr for source branch. * * @param int $hostID * @access public * @return void */ public function ajaxCheckSameOpened(int $hostID) { $sourceProject = $this->post->sourceProject; $sourceBranch = $this->post->sourceBranch; $targetProject = $this->post->targetProject; $targetBranch = $this->post->targetBranch; $result = $this->mr->checkSameOpened($hostID, (string)$sourceProject, $sourceBranch, (string)$targetProject, $targetBranch); echo json_encode($result); } /** * 获取分支权限。 * Ajax get branch privileges. * * @param int $hostID * @param string $project * @access public * @return void */ public function ajaxGetBranchPrivs(int $hostID, string $project) { $host = $this->loadModel('pipeline')->getByID($hostID); if(in_array($host->type, array('gitea', 'gogs'))) { $project = urldecode(base64_decode($project)); } else { $project = (int)$project; } $branchPrivs = array(); $branches = $this->loadModel($host->type)->apiGetBranchPrivs($hostID, $project); foreach($branches as $branch) $branchPrivs[$branch->name] = $branch->name; echo json_encode($branchPrivs); } /** * 从代码托管服务器同步合并请求到禅道。 * AJAX: sync Merge Requests from API server. * * @param int $repoID * @return void */ public function ajaxSyncMRs(int $repoID) { if(!$repoID) $this->sendSuccess(); $repo = $this->loadModel('repo')->getByID($repoID); if($repo && in_array($repo->SCM, $this->config->repo->notSyncSCM)) $repo->serviceProject = (int)$repo->serviceProject; $rawMRs = $this->loadModel(strtolower($repo->SCM))->apiGetMergeRequests($repo->gitService, $repo->serviceProject); if(empty($rawMRs)) $this->sendSuccess(); $MRs = $this->dao->select('`id`,`sourceProject`,`sourceBranch`,`targetProject`,`targetBranch`,`mriid`')->from(TABLE_MR)->where('repoID')->eq($repoID)->fetchAll(); $existedMRs = array(); $needSyncMRs = $rawMRs; foreach($rawMRs as $index => $rawMR) { if($rawMR->state == 'merged' || $rawMR->source_project_id != $rawMR->target_project_id) { unset($needSyncMRs[$index]); continue; } foreach($MRs as $MR) { if($MR->mriid == $rawMR->iid && $MR->sourceProject == $rawMR->source_project_id && $MR->sourceBranch == $rawMR->source_branch && $MR->targetProject == $rawMR->target_project_id && $MR->targetBranch == $rawMR->target_branch) { $existedMRs[$MR->id] = $needSyncMRs[$index]; unset($needSyncMRs[$index]); } } } $this->mrZen->saveMrData($repo, $needSyncMRs); foreach($existedMRs as $mrID => $existedMR) { $MR = $this->mr->fetchByID($mrID); $status = $existedMR->state ?: ''; $mergeStatus = $existedMR->merge_status ?: ''; if($MR->title != $existedMR->title || $MR->status != $status || $MR->mergeStatus != $mergeStatus) { $newMR = new stdclass(); $newMR->title = $existedMR->title; $newMR->status = $status; $newMR->isFlow = empty($existedMR->flow) ? 0 : 1; $newMR->mergeStatus = $mergeStatus; $this->dao->update(TABLE_MR)->data($newMR)->where('id')->eq($mrID)->exec(); if(!dao::isError()) $this->loadModel('action')->create(empty($existedMR->flow) ? 'mr' : 'pullreq', $mrID, 'synced', '', '', 'system'); } } $this->sendSuccess(); } /** * 执行合并请求的构建。 * AJAX exec MR job. * * @param int $MRID * @param int $jobID * @access public * @return void */ public function ajaxExecJob(int $MRID, int $jobID) { $this->mr->execJob($MRID, $jobID); return $this->sendSuccess(array('load' => true)); } /** * 获取合并请求关联的对象。 * AJAX get MR linked objects. * * @param int $MRID * @access public * @return void */ public function ajaxLinkObjects(int $MRID) { $mr = $this->mr->fetchByID($MRID); if(!$mr) return false; return $this->mr->linkObjects($mr); } /** * 同步流水线的构建状态。 * AJAX sync compile status. * * @param int $compileID * @access public * @return void */ public function ajaxSyncCompile(int $compileID) { $this->loadModel('ci')->checkCompileStatus($compileID); return $this->sendSuccess(array('message' => $this->lang->mr->refreshSuccess, 'load' => true)); } }