zen.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <?php
  2. /**
  3. * The zen file of mr module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Yanyi Cao <caoyanyi@easycorp.ltd>
  8. * @package mr
  9. * @link https://www.zentao.net
  10. */
  11. class mrZen extends mr
  12. {
  13. /**
  14. * 获取合并请求的代码库项目信息。
  15. * Get the code base project information of the merge request.
  16. *
  17. * @param object $repo
  18. * @access protected
  19. * @return array
  20. */
  21. protected function getAllProjects($repo)
  22. {
  23. $methodName = 'get' . ucfirst($repo->SCM) . 'Projects';
  24. return $this->mr->{$methodName}((int)$repo->serviceHost, array($repo->serviceProject => $repo->serviceProject));
  25. }
  26. /**
  27. * 向编辑合并请求页面添加数据。
  28. * Add data to the edit merge request page.
  29. *
  30. * @param object $MR
  31. * @param string $scm
  32. * @access protected
  33. * @return void
  34. */
  35. protected function assignEditData($MR, $scm)
  36. {
  37. $this->app->loadConfig('pipeline');
  38. $MR->canDeleteBranch = true;
  39. if(in_array($scm, $this->config->pipeline->formatTypeService)) $MR->sourceProject = (int)$MR->sourceProject;
  40. $branchPrivs = $this->loadModel($scm)->apiGetBranchPrivs($MR->hostID, $MR->sourceProject);
  41. foreach($branchPrivs as $priv)
  42. {
  43. if($MR->canDeleteBranch && $priv->name == $MR->sourceBranch) $MR->canDeleteBranch = false;
  44. }
  45. $sourceProject = $targetProject = $MR->sourceProject;
  46. if($MR->sourceProject != $MR->targetProject) $targetProject = $MR->targetProject;
  47. if(in_array($scm, $this->config->pipeline->formatTypeService))
  48. {
  49. $project = $this->loadModel($scm)->apiGetSingleProject($MR->hostID, (int)$MR->sourceProject, false);
  50. $targetProject = $sourceProject = zget($project, 'name_with_namespace', '');
  51. if($MR->sourceProject != $MR->targetProject)
  52. {
  53. $project = $this->loadModel($scm)->apiGetSingleProject($MR->hostID, (int)$MR->targetProject, false);
  54. $targetProject = zget($project, 'name_with_namespace', '');
  55. }
  56. }
  57. $branches = array();
  58. $jobList = array();
  59. if($MR->repoID)
  60. {
  61. $rawJobList = $this->loadModel('job')->getListByRepoID($MR->repoID);
  62. foreach($rawJobList as $rawJob) $jobList[$rawJob->id] = "[$rawJob->id] $rawJob->name";
  63. $repo = $this->loadModel('repo')->getByID($MR->repoID);
  64. $scm = $this->app->loadClass('scm');
  65. $scm->setEngine($repo);
  66. $branches = $scm->branch();
  67. $this->view->repo = $repo;
  68. }
  69. $this->view->title = $this->lang->mr->edit;
  70. $this->view->MR = $MR;
  71. $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed');
  72. $this->view->jobList = $jobList;
  73. $this->view->branches = $branches;
  74. $this->view->sourceProject = $sourceProject;
  75. $this->view->targetProject = $targetProject;
  76. $this->display();
  77. }
  78. /**
  79. * 构造关联需求的搜索表单。
  80. * Build the search form of the associated story.
  81. *
  82. * @param int $MRID
  83. * @param int $repoID
  84. * @param string $orderBy
  85. * @param int $queryID
  86. * @access protected
  87. * @return void
  88. */
  89. protected function buildLinkStorySearchForm($MRID, $repoID, $orderBy, $queryID = 0)
  90. {
  91. if(empty($this->product)) $this->loadModel('product');
  92. if(empty($this->lang->story)) $this->app->loadLang('story');
  93. $storyStatusList = $this->lang->story->statusList;
  94. unset($storyStatusList['closed']);
  95. $this->config->product->search['actionURL'] = $this->createLink($this->app->rawModule, 'linkStory', "MRID={$MRID}&repoID={$repoID}&browseType=bySearch&param=myQueryID&orderBy={$orderBy}");
  96. $this->config->product->search['queryID'] = $queryID;
  97. $this->config->product->search['style'] = 'simple';
  98. $this->config->product->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $storyStatusList);
  99. unset($this->config->product->search['fields']['plan']);
  100. unset($this->config->product->search['params']['plan']);
  101. unset($this->config->product->search['fields']['module']);
  102. unset($this->config->product->search['params']['module']);
  103. unset($this->config->product->search['fields']['product']);
  104. unset($this->config->product->search['params']['product']);
  105. unset($this->config->product->search['fields']['branch']);
  106. unset($this->config->product->search['params']['branch']);
  107. unset($this->config->product->search['fields']['grade']);
  108. unset($this->config->product->search['params']['grade']);
  109. $this->loadModel('search')->setSearchParams($this->config->product->search);
  110. }
  111. /**
  112. * 构造关联bug的搜索表单。
  113. * Build the search form of the associated bug.
  114. *
  115. * @param int $MRID
  116. * @param int $repoID
  117. * @param string $orderBy
  118. * @param int $queryID
  119. * @access protected
  120. * @return void
  121. */
  122. protected function buildLinkBugSearchForm($MRID, $repoID, $orderBy, $queryID = 0)
  123. {
  124. if(empty($this->product)) $this->loadModel('product');
  125. $this->config->bug->search['actionURL'] = $this->createLink($this->app->rawModule, 'linkBug', "MRID={$MRID}&repoID={$repoID}&browseType=bySearch&param=myQueryID&orderBy={$orderBy}");
  126. $this->config->bug->search['queryID'] = $queryID;
  127. $this->config->bug->search['style'] = 'simple';
  128. unset($this->config->bug->search['fields']['product']);
  129. unset($this->config->bug->search['params']['product']);
  130. unset($this->config->bug->search['fields']['plan']);
  131. unset($this->config->bug->search['params']['plan']);
  132. unset($this->config->bug->search['fields']['module']);
  133. unset($this->config->bug->search['params']['module']);
  134. unset($this->config->bug->search['fields']['execution']);
  135. unset($this->config->bug->search['params']['execution']);
  136. unset($this->config->bug->search['fields']['openedBuild']);
  137. unset($this->config->bug->search['params']['openedBuild']);
  138. unset($this->config->bug->search['fields']['resolvedBuild']);
  139. unset($this->config->bug->search['params']['resolvedBuild']);
  140. unset($this->config->bug->search['fields']['branch']);
  141. unset($this->config->bug->search['params']['branch']);
  142. $this->loadModel('search')->setSearchParams($this->config->bug->search);
  143. }
  144. /**
  145. * 构造关联任务的搜索表单。
  146. * Build the search form of the associated task.
  147. *
  148. * @param int $MRID
  149. * @param int $repoID
  150. * @param string $orderBy
  151. * @param int $queryID
  152. * @param array $productExecutions
  153. * @access protected
  154. * @return void
  155. */
  156. protected function buildLinkTaskSearchForm($MRID, $repoID, $orderBy, $queryID, $productExecutions)
  157. {
  158. $this->config->execution->search['module'] = 'mrTask';
  159. $this->config->execution->search['actionURL'] = $this->createLink($this->app->rawModule, 'linkTask', "MRID={$MRID}&repoID={$repoID}&browseType=bySearch&param=myQueryID&orderBy={$orderBy}");
  160. $this->config->execution->search['queryID'] = $queryID;
  161. $this->config->execution->search['params']['execution']['values'] = array_filter($productExecutions);
  162. unset($this->config->execution->search['fields']['module']);
  163. unset($this->config->execution->search['params']['module']);
  164. $this->loadModel('search')->setSearchParams($this->config->execution->search);
  165. }
  166. /**
  167. * 处理关联任务页面分页数据。
  168. * Process the pagination data of the associated task page.
  169. *
  170. * @param int $recTotal
  171. * @param int $recPerPage
  172. * @param int $pageID
  173. * @param array $allTasks
  174. * @access protected
  175. * @return void
  176. */
  177. protected function processLinkTaskPager($recTotal, $recPerPage, $pageID, $allTasks)
  178. {
  179. $this->app->loadClass('pager', true);
  180. $pager = new pager($recTotal, $recPerPage, $pageID);
  181. $pager->setRecTotal(count($allTasks));
  182. $pager->setPageTotal();
  183. if($pager->pageID > $pager->pageTotal) $pager->setPageID($pager->pageTotal);
  184. $count = 1;
  185. $limitMin = ($pager->pageID - 1) * $pager->recPerPage;
  186. $limitMax = $pager->pageID * $pager->recPerPage;
  187. foreach($allTasks as $key => $task)
  188. {
  189. if($count <= $limitMin || $count > $limitMax) unset($allTasks[$key]);
  190. $count ++;
  191. }
  192. $this->view->allTasks = $allTasks;
  193. $this->view->pager = $pager;
  194. }
  195. /**
  196. * 检查是否有权限编辑项目。
  197. * Check if you have permission to edit the project.
  198. *
  199. * @param string $hostType
  200. * @param object $sourceProject
  201. * @param object $MR
  202. * @access protected
  203. * @return bool
  204. */
  205. protected function checkProjectEdit($hostType, $sourceProject, $MR)
  206. {
  207. if($hostType == 'gitlab')
  208. {
  209. $groupIDList = array(0 => 0);
  210. $groups = $this->loadModel('gitlab')->apiGetGroups($MR->hostID, 'name_asc', 'developer');
  211. foreach($groups as $group) $groupIDList[] = $group->id;
  212. $isDeveloper = $this->gitlab->checkUserAccess($MR->hostID, 0, $sourceProject, $groupIDList, 'developer');
  213. $gitUsers = $this->loadModel('pipeline')->getUserBindedPairs($MR->hostID, 'gitlab');
  214. if(isset($gitUsers[$this->app->user->account]) && $isDeveloper) return true;
  215. }
  216. elseif($hostType == 'gitea')
  217. {
  218. return (isset($sourceProject->allow_merge_commits) && $sourceProject->allow_merge_commits == true);
  219. }
  220. elseif($hostType == 'gogs')
  221. {
  222. return (isset($sourceProject->permissions->push) && $sourceProject->permissions->push);
  223. }
  224. return false;
  225. }
  226. /**
  227. * 获取代码分支的访问地址。
  228. * Get repo branch url.
  229. *
  230. * @param object $host
  231. * @param int|string $projectID $projectID is an int in gitlab and a string in gitea or gogs.
  232. * @param string $branch
  233. * @access protected
  234. * @return string
  235. */
  236. protected function getBranchUrl($host, $projectID, $branch)
  237. {
  238. $branch = $this->loadModel($host->type)->apiGetSingleBranch($host->id, $projectID, $branch);
  239. return $branch ? zget($branch, 'web_url', '') : '';
  240. }
  241. /**
  242. * 检查是否有新的提交。
  243. * Check if there are new commits.
  244. *
  245. * @param string $hostType
  246. * @param int $hostID
  247. * @param string $projectID
  248. * @param int $mriid
  249. * @param string $lastTime
  250. * @access protected
  251. * @return bool
  252. */
  253. protected function checkNewCommit($hostType, $hostID, $projectID, $mriid, $lastTime)
  254. {
  255. $commitLogs = $this->mr->apiGetMRCommits($hostID, $projectID, $mriid);
  256. if($commitLogs)
  257. {
  258. $lastCommit = zget($commitLogs[0], 'committed_date', '');
  259. if(in_array($hostType, array('gitea', 'gogs'))) $lastCommit = $commitLogs[0]->author->committer->date;
  260. if($lastCommit > $lastTime) return true;
  261. }
  262. return false;
  263. }
  264. /**
  265. * 保存合并请求数据.
  266. * Save merge request data.
  267. *
  268. * @param object $repo
  269. * @param array $rawMRList
  270. * @access protected
  271. * @return bool
  272. * @param mixed[] $rawMrList
  273. */
  274. protected function saveMrData($repo, $rawMrList)
  275. {
  276. $this->loadModel('action');
  277. foreach($rawMrList as $rawMR)
  278. {
  279. $MR = new stdclass();
  280. $MR->hostID = $repo->serviceHost;
  281. $MR->mriid = $rawMR->iid;
  282. $MR->sourceProject = $rawMR->source_project_id;
  283. $MR->sourceBranch = $rawMR->source_branch;
  284. $MR->targetProject = $rawMR->target_project_id;
  285. $MR->targetBranch = $rawMR->target_branch;
  286. $MR->title = $rawMR->title;
  287. $MR->repoID = $repo->id;
  288. $MR->createdBy = 'system';
  289. $MR->createdDate = isset($rawMR->created) ? date('Y-m-d H:i:s', intval($rawMR->created / 1000)) : date('Y-m-d H:i:s', strtotime($rawMR->created_at));
  290. $MR->editedDate = isset($rawMR->updated) ? date('Y-m-d H:i:s', intval($rawMR->updated / 1000)) : date('Y-m-d H:i:s', strtotime($rawMR->updated_at));
  291. $MR->mergeStatus = $rawMR->merge_status ?: '';
  292. $MR->status = $rawMR->state ?: '';
  293. $MR->isFlow = empty($rawMR->flow) ? 0 : 1;
  294. if($MR->status == 'open') $MR->status = 'opened';
  295. $mrID = $this->mr->insertMr($MR);
  296. if($mrID) $this->action->create(empty($rawMR->flow) ? 'mr' : 'pullreq', $mrID, 'opened', '', '', 'system');
  297. if(dao::isError()) return false;
  298. }
  299. return true;
  300. }
  301. }