znt/module/mr/control.php

1001 lines
36 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
/**
* The control file of mr module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Yanyi Cao <caoyanyi@easycorp.ltd>
* @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}&param={$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 = "<option value=''></option>";
foreach($jobList as $job) $options .= "<option value='{$job->id}' data-name='{$job->name}'>[{$job->id}] {$job->name}</option>";
$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));
}
}