zentaopms/module/mr/control.php
2023-05-16 10:47:08 +08:00

1096 lines
43 KiB
PHP

<?php
class mr extends control
{
/**
* The gitlab constructor.
* @param string $moduleName
* @param string $methodName
*/
public function __construct($moduleName = '', $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();
}
/**
* 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($repoID = 0, $mode = 'status', $param = 'opened', $objectID = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$repoCount = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq('0')
->andWhere('SCM')->in(array('Gitlab', 'Gitea', 'Gogs'))
->andWhere('synced')->eq(1)
->orderBy('id')
->count();
if($repoCount == 0) $this->locate($this->loadModel('repo')->createLink('create'));
$repoID = $this->loadModel('repo')->saveState($repoID, $objectID);
$repo = $this->repo->getRepoByID($repoID);
if(!in_array(strtolower($repo->SCM), $this->config->mr->gitServiceList))
{
$repoID = $this->dao->select('id')->from(TABLE_REPO)->where('deleted')->eq('0')->andWhere('SCM')->in(array('Gitlab', 'Gitea', 'Gogs'))->andWhere('synced')->eq(1)->orderBy('id')->fetch('id');
$repo = $this->repo->getRepoByID($repoID);
}
$this->loadModel('ci')->setMenu($repo->id);
$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();
foreach($MRList as $MR)
{
$projectIds[$MR->sourceProject] = $MR->sourceProject;
$projectIds[$MR->targetProject] = $MR->targetProject;
}
$projects = $this->mr->getGitlabProjects($repo->serviceHost, $projectIds);
}
else
{
$projects = $this->mr->getAllProjects($repoID, $repo->SCM);
}
/* Save current URI to session. */
$this->session->set('mrList', $this->app->getURI(true), 'repo');
/* Sync GitLab MR to ZenTao Database. */
$MRList = $this->mr->batchSyncMR($MRList, $repo->SCM);
/* Check whether Mr is linked with the product. */
foreach($MRList as $MR)
{
$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 */
$this->app->loadLang('compile');
$openIDList = array();
if(!$this->app->user->admin)
{
if($repo->SCM == 'Gitlab')
{
$openIDList = $this->loadModel('gitlab')->getGitLabListByAccount($this->app->user->account);
}
elseif($repo->SCM == 'Gitea')
{
$openIDList = $this->loadModel('gitea')->getGiteaListByAccount($this->app->user->account);
}
elseif($repo->SCM == 'Gogs')
{
$openIDList = $this->loadModel('gogs')->getGogsListByAccount($this->app->user->account);
}
}
$this->view->title = $this->lang->mr->common . $this->lang->colon . $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->repoID = $repoID;
$this->view->objectID = $objectID;
$this->view->repo = $repo;
$this->view->orderBy = $orderBy;
$this->view->openIDList = $openIDList;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->display();
}
/**
* Create MR function.
*
* @access public
* @return void
*/
public function create()
{
if($_POST)
{
$result = $this->mr->create();
return $this->send($result);
}
$repoID = $this->loadModel('repo')->saveState(0);
$repo = $this->repo->getRepoByID($repoID);
$this->loadModel('gitlab');
$this->loadModel('gitea');
$this->loadModel('gogs');
if($repo->SCM == 'Gitea')
{
$project = $this->gitea->apiGetSingleProject($repo->gitService, $repo->project);
if(empty($project) or !$project->allow_merge_commits) $repo = array();
}
$hosts = $this->loadModel('pipeline')->getList(array('gitea', 'gitlab', 'gogs'));
if(!$this->app->user->admin)
{
$gitlabUsers = $this->gitlab->getGitLabListByAccount();
$giteaUsers = $this->gitea->getGiteaListByAccount();
$gogsUsers = $this->gogs->getGogsListByAccount();
foreach($hosts as $hostID => $host)
{
if($host->type == 'gitlab' and isset($gitlabUsers[$hostID])) continue;
if($host->type == 'gitea' and isset($giteaUsers[$hostID])) continue;
if($host->type == 'gogs' and isset($gogsUsers[$hostID])) continue;
unset($hosts[$hostID]);
}
}
$hostPairs = array();
foreach($hosts as $host) $hostPairs[$host->id] = '[' . ucfirst($host->type) . "] {$host->name}";
$this->app->loadLang('repo'); /* Import lang in repo module. */
$this->app->loadLang('compile');
$this->view->title = $this->lang->mr->create;
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
$this->view->jobList = $this->loadModel('job')->getList();
$this->view->hostPairs = $hostPairs;
$this->view->hosts = $hosts;
$this->view->repo = $repo;
$this->display();
}
/**
* Edit MR function.
*
* @access public
* @return void
*/
public function edit($MRID)
{
if($_POST)
{
$result = $this->mr->update($MRID);
return $this->send($result);
}
$MR = $this->mr->getByID($MRID);
if(isset($MR->hostID)) $rawMR = $this->mr->apiGetSingleMR($MR->hostID, $MR->targetProject, $MR->mriid);
$this->view->title = $this->lang->mr->edit;
$this->view->MR = $MR;
$this->view->rawMR = isset($rawMR) ? $rawMR : false;
if(!isset($rawMR->id) or (isset($rawMR->message) and $rawMR->message == '404 Not found') or empty($rawMR)) return $this->display();
$host = $this->loadModel('pipeline')->getByID($MR->hostID);
$scm = $host->type;
$branchList = $this->loadModel($scm)->getBranches($MR->hostID, $MR->targetProject);
$MR->canDeleteBranch = true;
$branchPrivs = $this->loadModel($scm)->apiGetBranchPrivs($MR->hostID, $MR->sourceProject);
foreach($branchPrivs as $priv)
{
if($MR->canDeleteBranch and $priv->name == $MR->sourceBranch) $MR->canDeleteBranch = false;
}
$targetBranchList = array();
foreach($branchList as $branch) $targetBranchList[$branch] = $branch;
/* Fetch user list both in Zentao and current GitLab project. */
$bindedUsers = $this->$scm->getUserIdRealnamePairs($MR->hostID);
$gitUsers = $this->$scm->getUserAccountIdPairs($MR->hostID);
/* Check permissions. */
if(!$this->app->user->admin and $scm == 'gitlab')
{
$groupIDList = array(0 => 0);
$groups = $this->$scm->apiGetGroups($MR->hostID, 'name_asc', 'developer');
foreach($groups as $group) $groupIDList[] = $group->id;
$sourceProject = $this->$scm->apiGetSingleProject($MR->hostID, $MR->sourceProject);
$isDeveloper = $this->$scm->checkUserAccess($MR->hostID, 0, $sourceProject, $groupIDList, 'developer');
if(!isset($gitUsers[$this->app->user->account]) or !$isDeveloper) return print(js::alert($this->lang->mr->errorLang[3]) . js::locate($this->createLink('mr', 'browse')));
}
/* Import lang for required modules. */
$this->loadModel('repo');
$this->loadModel('job');
$this->loadModel('compile');
$repoList = array();
$rawRepoList = $this->repo->getRepoListByClient($MR->hostID, $MR->sourceProject);
foreach($rawRepoList as $rawRepo) $repoList[$rawRepo->id] = "[$rawRepo->id] $rawRepo->name";
$jobList = array();
if($MR->repoID)
{
$rawJobList = $this->job->getListByRepoID($MR->repoID);
foreach($rawJobList as $rawJob) $jobList[$rawJob->id] = "[$rawJob->id] $rawJob->name";
}
$this->view->repoList = $repoList;
$this->view->jobList = !empty($MR->repoID) ? $jobList : array();
$this->view->title = $this->lang->mr->edit;
$this->view->MR = $MR;
$this->view->host = $host;
$this->view->targetBranchList = $targetBranchList;
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
$this->view->assignee = $MR->assignee;
$this->view->reviewer = zget($gitUsers, $MR->reviewer, '');
$this->display();
}
/**
* Delete a MR.
*
* @param int $MRID
* @access public
* @return void
*/
public function delete($MRID, $confirm = 'no')
{
if($confirm != 'yes') return print(js::confirm($this->lang->mr->confirmDelete, inlink('delete', "MRID=$MRID&confirm=yes")));
$MR = $this->mr->getByID($MRID);
if($MR->synced)
{
$res = $this->mr->apiDeleteMR($MR->hostID, $MR->targetProject, $MR->mriid);
if(isset($res->message)) return print(js::alert($this->mr->convertApiError($res->message)));
}
$this->dao->delete()->from(TABLE_MR)->where('id')->eq($MRID)->exec();
$this->loadModel('action')->create('mr', $MRID, 'deleted', '', $MR->title);
$this->mr->createMRLinkedAction($MRID, 'removemr');
echo js::locate(inlink('browse'), 'parent');
}
/**
* View a MR.
*
* @param int $MRID
* @access public
* @return void
*/
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(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();
$host = $this->loadModel('pipeline')->getByID($MR->hostID);
$scm = $host->type;
$this->loadModel($scm);
$this->loadModel('job');
/* Sync MR from GitLab to ZentaoPMS. */
$MR = $this->mr->apiSyncMR($MR);
$sourceProject = $this->$scm->apiGetSingleProject($MR->hostID, $MR->sourceProject);
$targetProject = $this->$scm->apiGetSingleProject($MR->hostID, $MR->targetProject);
$sourceBranch = $this->$scm->apiGetSingleBranch($MR->hostID, $MR->sourceProject, $MR->sourceBranch);
$targetBranch = $this->$scm->apiGetSingleBranch($MR->hostID, $MR->targetProject, $MR->targetBranch);
$projectOwner = true;
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;
}
$this->view->sourceProjectName = $sourceProject->name_with_namespace;
$this->view->targetProjectName = $targetProject->name_with_namespace;
$this->view->sourceProjectURL = isset($sourceBranch->web_url) ? $sourceBranch->web_url : '';
$this->view->targetProjectURL = isset($targetBranch->web_url) ? $targetBranch->web_url : '';
/* Those variables are used to render $lang->mr->commandDocument. */
$this->view->httpRepoURL = $sourceProject->http_url_to_repo;
$this->view->branchPath = $sourceProject->path_with_namespace . '-' . $MR->sourceBranch;
/* Get mr linked list. */
$this->app->loadLang('productplan');
$product = $this->mr->getMRProduct($MR);
$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->title = $this->lang->mr->view;
$this->view->MR = $MR;
$this->view->rawMR = isset($rawMR) ? $rawMR : false;
$this->view->product = $product;
$this->view->stories = $this->mr->getLinkList($MR->id, $product->id, 'story');
$this->view->bugs = $this->mr->getLinkList($MR->id, $product->id, 'bug');
$this->view->tasks = $this->mr->getLinkList($MR->id, $product->id, 'task');
$this->view->actions = $this->loadModel('action')->getList('mr', $MRID);
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
$this->display();
}
/**
* Crontab sync MR from GitLab API to Zentao database, default time 5 minutes to execute once.
*
* @access public
* @return void
*/
public function syncMR()
{
$MRList = $this->mr->getList();
$this->mr->batchSyncMR($MRList);
if(dao::isError())
{
echo json_encode(dao::getError());
return true;
}
echo 'success';
}
/**
* Accept a MR.
*
* @param int $MRID
* @access public
* @return void
*/
public function accept($MRID)
{
$MR = $this->mr->getByID($MRID);
/* 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->send(array('result' => 'fail', 'message' => $this->lang->mr->needCI, 'locate' => helper::createLink('mr', 'view', "mr={$MRID}")));
}
}
if(isset($MR->needApproved) and $MR->needApproved == '1')
{
if($MR->approvalStatus != 'approved')
{
return $this->send(array('result' => 'fail', 'message' => $this->lang->mr->needApproved, 'locate' => helper::createLink('mr', 'view', "mr={$MRID}")));
}
}
if(isset($MR->hostID)) $rawMR = $this->mr->apiAcceptMR($MR);
if(isset($rawMR->state) and $rawMR->state == 'merged')
{
$this->mr->logMergedAction($MR);
return $this->send(array('result' => 'success', 'message' => $this->lang->mr->mergeSuccess, 'locate' => helper::createLink('mr', 'browse')));
}
/* 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->send(array('result' => 'fail', 'message' => sprintf($this->lang->mr->apiError->sudo, $errorMessage), 'locate' => helper::createLink('mr', 'view', "mr={$MRID}")));
}
return $this->send(array('result' => 'fail', 'message' => $this->lang->mr->mergeFailed, 'locate' => helper::createLink('mr', 'view', "mr={$MRID}")));
}
/**
* View diff between MR source and target branches.
*
* @param int $MRID
* @access public
* @return void
*/
public function diff($MRID, $encoding = '')
{
$this->app->loadLang('productplan');
$this->app->loadLang('bug');
$this->app->loadLang('task');
$encoding = empty($encoding) ? 'utf-8' : $encoding;
$encoding = strtolower(str_replace('_', '-', $encoding)); /* Revert $config->requestFix in $encoding. */
$MR = $this->mr->getByID($MRID);
$this->view->title = $this->lang->mr->viewDiff;
$this->view->MR = $MR;
$rawMR = null;
if($MR->synced)
{
$rawMR = $this->mr->apiGetSingleMR($MR->hostID, $MR->targetProject, $MR->mriid);
if(!isset($rawMR->id) or (isset($rawMR->message) and $rawMR->message == '404 Not found') or empty($rawMR)) return $this->display();
}
$this->view->rawMR = $rawMR;
$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;
setcookie('arrange', $arrange);
}
if($this->post->encoding) $encoding = $this->post->encoding;
}
if($arrange == 'appose')
{
foreach($diffs as $diffFile)
{
if(empty($diffFile->contents)) continue;
foreach($diffFile->contents as $content)
{
$old = array();
$new = array();
foreach($content->lines as $line)
{
if($line->type != 'new') $old[$line->oldlc] = $line->line;
if($line->type != 'old') $new[$line->newlc] = $line->line;
}
$content->old = $old;
$content->new = $new;
}
}
}
$this->view->repo = $this->loadModel('repo')->getRepoByID($MR->repoID);
$this->view->repoID = $MR->repoID;
$this->view->diffs = $diffs;
$this->view->encoding = $encoding;
$this->view->arrange = $arrange;
$this->view->sourceBranch = $MR->sourceBranch;
$this->view->targetBranch = $MR->targetBranch;
$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($MRID, $action = 'approve')
{
$MR = $this->mr->getByID($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->app->loadLang('compile'); /* Import lang. */
$this->view->compileUrl = $this->createLink('job', 'view', "jobID={$MR->jobID}&compileID={$MR->compileID}");
}
$this->view->showCompileResult = $showCompileResult;
$this->view->MR = $MR;
$this->view->action = $action;
$this->view->actions = $this->loadModel('action')->getList('mr', $MRID);
$this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
$this->display();
}
/**
* Close this MR.
*
* @param int $MRID
* @return void
*/
public function close($MRID)
{
$MR = $this->mr->getByID($MRID);
$result = $this->mr->close($MR);
return $this->send($result);
}
/**
* Reopen this MR.
*
* @param int $MRID
* @return void
*/
public function reopen($MRID)
{
$MR = $this->mr->getByID($MRID);
return $this->send($this->mr->reopen($MR));
}
/**
* link MR list.
*
* @param int $MRID
* @param string $type
* @param string $orderBy
* @param string $link
* @param string $param
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @return void
*/
public function link($MRID, $type = 'story', $orderBy = 'id_desc', $link = 'false', $param = '', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
$this->app->loadLang('productplan');
$this->app->loadLang('bug');
$this->app->loadLang('task');
$MR = $this->mr->getByID($MRID);
$product = $this->mr->getMRProduct($MR);
/* Load pager. */
$this->app->loadClass('pager', $static = 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);
$stories = $this->mr->getLinkList($MRID, $product->id, 'story', $type == 'story' ? $orderBy : '', $storyPager);
$bugs = $this->mr->getLinkList($MRID, $product->id, 'bug', $type == 'bug' ? $orderBy : '', $bugPager);
$tasks = $this->mr->getLinkList($MRID, $product->id, 'task', $type == 'task' ? $orderBy : '', $taskPager);
$this->view->title = $this->lang->mr->common . $this->lang->colon . $this->lang->mr->link;
$this->view->MR = $MR;
$this->view->canBeChanged = true;
$this->view->modulePairs = $this->loadModel('tree')->getOptionMenu($product->id, 'story');
$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->orderBy = $orderBy;
$this->view->link = $link;
$this->view->param = $param;
$this->display();
}
/**
* Link story to mr.
*
* @param int $MRID
* @param int $productID
* @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($MRID, $productID = 0, $browseType = '', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
if(!empty($_POST['stories']))
{
$this->mr->link($MRID, $productID, 'story');
if(dao::isError()) return print(js::error(dao::getError()));
return print(js::locate(inlink('link', "MRID=$MRID&type=story&orderBy=$orderBy"), 'parent'));
}
$this->loadModel('story');
$this->app->loadLang('productplan');
$product = $this->loadModel('product')->getById($productID);
$modules = $this->loadModel('tree')->getOptionMenu($productID, 'story');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Build search form. */
$storyStatusList = $this->lang->story->statusList;
unset($storyStatusList['closed']);
$queryID = ($browseType == 'bySearch') ? (int) $param : 0;
unset($this->config->product->search['fields']['product']);
$this->config->product->search['actionURL'] = $this->createLink('mr', 'link', "$MRID=$MRID&type=story&orderBy=$orderBy&link=true&param=" . helper::safe64Encode('&browseType=bySearch&queryID=myQueryID'));
$this->config->product->search['queryID'] = $queryID;
$this->config->product->search['style'] = 'simple';
$this->config->product->search['params']['product']['values'] = array($product) + array('all' => $this->lang->product->allProductsOfProject);
$this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getForProducts(array($productID => $productID));
$this->config->product->search['params']['module']['values'] = $modules;
$this->config->product->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $storyStatusList);
if($product->type == 'normal')
{
unset($this->config->product->search['fields']['branch']);
unset($this->config->product->search['params']['branch']);
}
else
{
$this->product->setMenu($productID, 0);
$this->config->product->search['fields']['branch'] = $this->lang->product->branch;
$branches = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty');
$this->config->product->search['params']['branch']['values'] = $branches;
}
$this->loadModel('search')->setSearchParams($this->config->product->search);
$MR = $this->mr->getByID($MRID);
$relatedStories = $this->mr->getCommitedLink($MR, 'story');
$linkedStories = $this->mr->getLinkList($MRID, $product->id, 'story');
if($browseType == 'bySearch')
{
$allStories = $this->story->getBySearch($productID, 0, $queryID, 'id', '', 'story', array_keys($linkedStories), $pager);
}
else
{
$allStories = $this->story->getProductStories($productID, 0, '0', 'draft,reviewing,active,changing', 'story', 'id_desc', false, array_keys($linkedStories), $pager);
}
$this->view->modules = $modules;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->allStories = $allStories;
$this->view->relatedStories = $relatedStories;
$this->view->product = $product;
$this->view->MRID = $MRID;
$this->view->browseType = $browseType;
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* Link bug to mr.
*
* @param int $MRID
* @param int $productID
* @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($MRID, $productID = 0, $browseType = '', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
if(!empty($_POST['bugs']))
{
$this->mr->link($MRID, $productID, 'bug');
if(dao::isError()) return print(js::error(dao::getError()));
return print(js::locate(inlink('link', "MRID=$MRID&type=bug&orderBy=$orderBy"), 'parent'));
}
$this->loadModel('bug');
$this->app->loadLang('productplan');
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$product = $this->loadModel('product')->getById($productID);
$modules = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'bug');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Build search form. */
$this->config->bug->search['actionURL'] = $this->createLink('mr', 'link', "$MRID=$MRID&type=bug&orderBy=$orderBy&link=true&param=" . helper::safe64Encode('&browseType=bySearch&queryID=myQueryID'));
$this->config->bug->search['queryID'] = $queryID;
$this->config->bug->search['style'] = 'simple';
$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($productID, $branch = 'all', $params = 'releasetag');
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
unset($this->config->bug->search['fields']['product']);
if($product->type == 'normal')
{
unset($this->config->bug->search['fields']['branch']);
unset($this->config->bug->search['params']['branch']);
}
else
{
$this->product->setMenu($productID, 0);
$this->config->bug->search['fields']['branch'] = $this->lang->product->branch;
$this->config->bug->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty');
}
$this->loadModel('search')->setSearchParams($this->config->bug->search);
$MR = $this->mr->getByID($MRID);
$relatedBugs = $this->mr->getCommitedLink($MR, 'bug');
$linkedBugs = $this->mr->getLinkList($MRID, $product->id, 'bug');
if($browseType == 'bySearch')
{
$allBugs = $this->bug->getBySearch($productID, 0, $queryID, 'id_desc', array_keys($linkedBugs), $pager);
}
else
{
$allBugs = $this->bug->getActiveBugs($productID, 0, '0', array_keys($linkedBugs), $pager);
}
$this->view->modules = $modules;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->allBugs = $allBugs;
$this->view->relatedBugs = $relatedBugs;
$this->view->product = $product;
$this->view->MRID = $MRID;
$this->view->browseType = $browseType;
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* Link task to mr.
*
* @param int $MRID
* @param int $productID
* @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($MRID, $productID = 0, $browseType = 'unclosed', $param = 0, $orderBy = 'id_desc', $recTotal = 0, $recPerPage = 100, $pageID = 1)
{
if(!empty($_POST['tasks']))
{
$this->mr->link($MRID, $productID, 'task');
if(dao::isError()) return print(js::error(dao::getError()));
return print(js::locate(inlink('link', "MRID=$MRID&type=task&orderBy=$orderBy"), 'parent'));
}
$this->loadModel('execution');
$this->loadModel('product');
$this->app->loadLang('task');
/* Set browse type. */
$browseType = strtolower($browseType);
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
$product = $this->loadModel('product')->getById($productID);
$modules = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'task');
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage, $pageID);
/* Build search form. */
$this->config->execution->search['actionURL'] = $this->createLink('mr', 'link', "$MRID=$MRID&type=task&orderBy=$orderBy&link=true&param=" . helper::safe64Encode('&browseType=bySearch&queryID=myQueryID'));
$this->config->execution->search['queryID'] = $queryID;
$this->config->execution->search['params']['module']['values'] = $modules;
$this->config->execution->search['params']['execution']['values'] = $this->product->getExecutionPairsByProduct($productID);
$this->loadModel('search')->setSearchParams($this->config->execution->search);
$MR = $this->mr->getByID($MRID);
$relatedTasks = $this->mr->getCommitedLink($MR, 'task');
$linkedTasks = $this->mr->getLinkList($MRID, $product->id, 'task');
/* Get executions by product. */
$productExecutions = $this->product->getExecutionPairsByProduct($productID);
$productExecutionIDs = array_filter(array_keys($productExecutions));
$this->config->execution->search['params']['execution']['values'] = array_filter($productExecutions);
/* Get tasks by executions. */
$allTasks = array();
foreach($productExecutionIDs as $productExecutionID)
{
$tasks = $this->execution->getTasks(0, $productExecutionID, array(), $browseType, $queryID, 0, $orderBy, null);
$allTasks = array_merge($tasks, $allTasks);
}
/* Filter linked tasks. */
$linkedTaskIDs = array_keys($linkedTasks);
foreach($allTasks as $key => $task)
{
if(in_array($task->id, $linkedTaskIDs)) unset($allTasks[$key]);
}
/* Page the records. */
$pager->setRecTotal(count($allTasks));
$pager->setPageTotal();
if($pager->pageID > $pager->pageTotal) $pager->setPageID($pager->pageTotal);
$count = 1;
$limitMin = ($pager->pageID - 1) * $pager->recPerPage;
$limitMax = $pager->pageID * $pager->recPerPage;
foreach($allTasks as $key => $task)
{
if($count <= $limitMin or $count > $limitMax) unset($allTasks[$key]);
$count ++;
}
$this->view->modules = $modules;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->view->allTasks = $allTasks;
$this->view->relatedTasks = $relatedTasks;
$this->view->product = $product;
$this->view->MRID = $MRID;
$this->view->browseType = $browseType;
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->pager = $pager;
$this->display();
}
/**
* UnLink an mr link.
*
* @param int $MRID
* @param int $productID
* @param string $type
* @param int $linkID
* @param string $confirm
* @access public
* @return mix
*/
public function unlink($MRID, $productID, $type, $linkID, $confirm = 'no')
{
$this->app->loadLang('productplan');
if($confirm == 'no')
{
return print(js::confirm($this->lang->productplan->confirmUnlinkStory, $this->createLink('mr', 'unlink', "MRID=$MRID&productID=$productID&linkID=$linkID&type=$type&confirm=yes")));
}
else
{
$this->mr->unlink($MRID, $productID, $type, $linkID);
/* if ajax request, send result. */
if($this->server->ajax)
{
if(dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
}
else
{
$response['result'] = 'success';
$response['message'] = '';
}
return $this->send($response);
}
return print(js::reload('parent'));
}
}
/**
* Add a review for this review.
*
* @param int $repoID
* @param int $mr
* @param int $v1
* @param int $v2
* @access public
* @return void
*/
public function addReview($repoID, $mr, $v1, $v2)
{
/* Handle the exception that when $repoID is empty. */
if($repoID == "0") $this->send(array());
$this->loadModel('repo');
if(!empty($_POST))
{
$v1 = helper::safe64Decode($v1);
$v2 = helper::safe64Decode($v2);
if($this->post->reviewType == 'bug') $result = $this->mr->saveBug($repoID, $mr, $v1, $v2);
if($this->post->reviewType == 'task') $result = $this->mr->saveTask($repoID, $mr, $v1, $v2);
if($result['result'] == 'fail') return print(json_encode($result));
$objectID = $result['id'];
$repo = $this->repo->getRepoById($repoID);
/* Handle the exception that when $repo is empty. */
if(empty($repo) or empty($result)) $this->send(json_encode(array()));
$location = sprintf($this->lang->repo->reviewLocation, $this->post->entry ? base64_decode($this->post->entry) : '', $repo->SCM != 'Subversion' ? substr($v2, 0, 10) : $v2, $this->post->begin, $this->post->end);
$link = $this->createLink('mr', 'diff', "mr=$mr") . '#L' . $this->post->begin;
$actionID = $this->loadModel('action')->create($this->post->reviewType, $objectID, 'repoCreated', '', html::a($link, $location));
$this->loadModel('mail')->sendmail($objectID, $actionID);
echo json_encode($result);
}
}
/**
* AJAX: Get MR target projects.
*
* @param int $hostID
* @param int $projectID
* @param string $scm
* @access public
* @return void
*/
public function ajaxGetMRTargetProjects($hostID, $projectID, $scm = 'gitlab')
{
$this->loadModel($scm);
if($scm != 'gitlab') $projectID = urldecode(base64_decode($projectID));
/* First step: get forks. Only get first level forks(not recursively). */
$projects = $scm == 'gitlab' ? $this->$scm->apiGetForks($hostID, $projectID) : array();
/* Second step: get project itself. */
$projects[] = $this->$scm->apiGetSingleProject($hostID, $projectID);
/* Last step: find its upstream recursively. */
$project = $this->$scm->apiGetUpstream($hostID, $projectID);
if(!empty($project)) $projects[] = $project;
if(!empty($project) and isset($project->id))
{
$project = $this->$scm->apiGetUpstream($hostID, $project->id);
if(!empty($project)) $projects[] = $project;
}
if($scm == 'gitlab')
{
$groupIDList = array(0 => 0);
$groups = $this->$scm->apiGetGroups($hostID, 'name_asc', 'developer');
foreach($groups as $group) $groupIDList[] = $group->id;
foreach($projects as $key => $project)
{
if($this->$scm->checkUserAccess($hostID, 0, $project, $groupIDList, 'developer') == false) unset($projects[$key]);
}
if(!$projects) return $this->send(array('message' => array()));
}
$options = "<option value=''></option>";
foreach($projects as $project)
{
if($scm == 'gitlab')
{
$options .= "<option value='{$project->id}' data-name='{$project->name}'>{$project->name_with_namespace}</option>";
}
else
{
$options .= "<option value='{$project->full_name}' data-name='{$project->full_name}'>{$project->full_name}</option>";
}
}
$this->send($options);
}
/**
* AJAX: Get repo list.
*
* @param int $hostID
* @param int $projectID
* @return void
*/
public function ajaxGetRepoList($hostID, $projectID)
{
$host = $this->loadModel('pipeline')->getByID($hostID);
if($host->type != 'gitlab') $projectID = urldecode(base64_decode($projectID));
$repoList = $this->loadModel('repo')->getRepoListByClient($hostID, $projectID);
if(!$repoList) return $this->send(array('message' => array()));
$options = "<option value=''></option>";
foreach($repoList as $repo) $options .= "<option value='{$repo->id}' data-name='{$repo->name}'>[{$repo->id}] {$repo->name}</option>";
$this->send($options);
}
/**
* AJAX: Get job list.
*
* @param int $repoID
* @return void
*/
public function ajaxGetJobList($repoID)
{
$this->loadModel('job');
$jobList = $this->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: Get compile list.
*
* @param int $jobID
* @return void
*/
public function ajaxGetCompileList($jobID)
{
$this->loadModel('compile');
$compileList = $this->compile->getListByJobID($jobID);
if(!$compileList) return $this->send(array('message' => array()));
$options = "<option value=''></option>";
foreach($compileList as $compile) $options .= "<option value='{$compile->id}' data-name='{$compile->name}'>[{$compile->id}] [{$this->lang->compile->statusList[$compile->status]}] {$compile->name}</option>";
$this->send($options);
}
/**
* Ajax check same opened mr for source branch.
*
* @param int $hostID
* @access public
* @return void
*/
public function ajaxCheckSameOpened($hostID)
{
$sourceProject = $this->post->sourceProject;
$sourceBranch = $this->post->sourceBranch;
$targetProject = $this->post->targetProject;
$targetBranch = $this->post->targetBranch;
$result = $this->mr->checkSameOpened($hostID, $sourceProject, $sourceBranch, $targetProject, $targetBranch);
return print(json_encode($result));
}
/**
* Ajax get branch pivs.
*
* @param int $hostID
* @param int|string $project
* @access public
* @return void
*/
public function ajaxGetBranchPivs($hostID, $project)
{
$host = $this->loadModel('pipeline')->getByID($hostID);
$scm = $host->type;
if(in_array($scm, array('gitea', 'gogs'))) $project = urldecode(base64_decode($project));
$branchPrivs = array();
$branches = $this->loadModel($scm)->apiGetBranchPrivs($hostID, $project);
foreach($branches as $branch) $branchPrivs[$branch->name] = $branch->name;
return print(json_encode($branchPrivs));
}
}