model.php 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579
  1. <?php
  2. /**
  3. * The model file of convert module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package convert
  9. * @version $Id: model.php 4129 2013-01-18 01:58:14Z wwccss $
  10. * @link https://www.zentao.net
  11. */
  12. ?>
  13. <?php
  14. class convertModel extends model
  15. {
  16. /**
  17. * 数据库连接句柄。
  18. * Database link handle.
  19. *
  20. * @var object
  21. * @access public
  22. */
  23. public $sourceDBH;
  24. /**
  25. * 连接数据库。
  26. * Connect to db.
  27. *
  28. * @param string $dbName
  29. * @access public
  30. * @return object|string
  31. */
  32. public function connectDB($dbName = '')
  33. {
  34. try
  35. {
  36. $params = clone $this->config->db;
  37. $params->name = $dbName;
  38. $dbh = new dbh($params);
  39. $dbh->exec("SET NAMES {$params->encoding}");
  40. $dbh->setAttribute(PDO::ATTR_CASE , PDO::CASE_LOWER);
  41. $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
  42. $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  43. $this->sourceDBH = $dbh;
  44. return $dbh;
  45. }
  46. catch (PDOException $exception)
  47. {
  48. return $exception->getMessage();
  49. }
  50. }
  51. /**
  52. * 检查数据库是否存在。
  53. * Check database exits or not.
  54. *
  55. * @param string $dbName
  56. * @access public
  57. * @return object|false
  58. */
  59. public function dbExists($dbName = '')
  60. {
  61. if(!$this->checkDBName($dbName)) return false;
  62. return $this->dbh->dbExists($dbName);
  63. }
  64. /**
  65. * 检查数据表是否存在。
  66. * Check table exits or not.
  67. *
  68. * @param string $table
  69. * @access public
  70. * @return object|false
  71. */
  72. public function tableExists($table)
  73. {
  74. $sql = "SHOW tables like '$table'";
  75. return $this->dbh->query($sql)->fetch();
  76. }
  77. /**
  78. * 检查jira数据库表是否存在。
  79. * Check table of jira databases exits or not.
  80. *
  81. * @param string $dbName
  82. * @param string $table
  83. * @access public
  84. * @return object|false
  85. */
  86. public function tableExistsOfJira($dbName, $table)
  87. {
  88. $this->connectDB($dbName);
  89. $sql = "SHOW tables like '$table'";
  90. return $this->dao->dbh($this->sourceDBH)->query($sql)->fetch();
  91. }
  92. /**
  93. * 保存每个表的最大ID。
  94. * Save the max id of every table. Thus when we convert again, when can delete id larger then the saved max id.
  95. *
  96. * @access public
  97. * @return void
  98. */
  99. public function saveState()
  100. {
  101. /* Get user defined tables. */
  102. $constants = get_defined_constants(true);
  103. $userConstants = $constants['user'];
  104. /* These tables needn't save. */
  105. unset($userConstants['TABLE_BURN']);
  106. unset($userConstants['TABLE_GROUPPRIV']);
  107. unset($userConstants['TABLE_PROJECTPRODUCT']);
  108. unset($userConstants['TABLE_PROJECTSTORY']);
  109. unset($userConstants['TABLE_STORYSPEC']);
  110. unset($userConstants['TABLE_TEAM']);
  111. unset($userConstants['TABLE_USERGROUP']);
  112. unset($userConstants['TABLE_STORYSTAGE']);
  113. unset($userConstants['TABLE_SEARCHDICT']);
  114. /* Get max id of every table. */
  115. foreach($userConstants as $key => $value)
  116. {
  117. if(strpos($key, 'TABLE') === false) continue;
  118. if($key == 'TABLE_COMPANY') continue;
  119. $state[$value] = (int)$this->dao->select('MAX(id) AS id')->from($value)->fetch('id');
  120. }
  121. $this->session->set('state', $state);
  122. }
  123. /**
  124. * 获取jira的数据。
  125. * Get jira data.
  126. *
  127. * @param string $method
  128. * @param string $module
  129. * @param int $lastID
  130. * @param int $limit
  131. * @access public
  132. * @return array
  133. */
  134. public function getJiraData($method, $module, $lastID = 0, $limit = 0)
  135. {
  136. if($method == 'db')
  137. {
  138. $originDBH = $this->dbh;
  139. $this->connectDB($this->session->jiraDB);
  140. $result = $this->getJiraDataFromDB($module, $lastID, $limit);
  141. $this->dao->dbh($originDBH);
  142. return $result;
  143. }
  144. elseif($method == 'file')
  145. {
  146. return $this->getJiraDataFromFile($module, $lastID, $limit);
  147. }
  148. else
  149. {
  150. return $this->getJiraDataFromAPI($module, $lastID, $limit);
  151. }
  152. }
  153. /**
  154. * 从接口获取jira数据。
  155. * Get jira data from jiraAPI.
  156. *
  157. * @param string $module
  158. * @param int $lastID
  159. * @param int $limit
  160. * @access public
  161. * @return array
  162. */
  163. public function getJiraDataFromAPI($module = '', $lastID = 0, $limit = 0)
  164. {
  165. if(empty($_SESSION['jiraApi'])) return array();
  166. $jiraApi = json_decode($this->session->jiraApi, true);
  167. if(empty($jiraApi['domain'])) return array();
  168. $this->app->loadClass('jira', true);
  169. $jiraApi = new jira($jiraApi['domain'], $jiraApi['admin'], $jiraApi['token']);
  170. $functionMap = array(
  171. 'issue' => 'getIssues',
  172. 'issuelinktype' => 'getIssueLinkTypes',
  173. 'issuetype' => 'getIssueTypes',
  174. 'user' => 'getUsers',
  175. 'project' => 'getProjects',
  176. 'resolution' => 'getResolutions',
  177. 'priority' => 'getPriority',
  178. 'status' => 'getStatus',
  179. 'customfield' => 'getCustomFields',
  180. 'customfieldoption' => 'getCustomFieldOption',
  181. 'workflow' => 'getWorkflowActions'
  182. );
  183. if(isset($functionMap[$module]))
  184. {
  185. $function = $functionMap[$module];
  186. $dataList = $jiraApi->$function($lastID, $limit);
  187. }
  188. else
  189. {
  190. return array();
  191. }
  192. if(in_array($module, array_keys($this->config->convert->objectTables)) && $module != 'workflow')
  193. {
  194. if($module == 'customfield')
  195. {
  196. foreach($dataList as $issueType => $datas)
  197. {
  198. foreach($datas as $key => $data)
  199. {
  200. $buildfunction = 'build' . ucfirst($module) . 'data';
  201. $dataList[$issueType][$key] = $this->$buildfunction($data);
  202. }
  203. }
  204. }
  205. else
  206. {
  207. foreach($dataList as $key => $data)
  208. {
  209. $buildfunction = 'build' . ucfirst($module) . 'data';
  210. $dataList[$key] = $this->$buildfunction($data);
  211. }
  212. }
  213. }
  214. return $dataList;
  215. }
  216. /**
  217. * 从数据库获取jira数据。
  218. * Get jira data from db.
  219. *
  220. * @param string $module
  221. * @param int $lastID
  222. * @param int $limit
  223. * @access public
  224. * @return array
  225. */
  226. public function getJiraDataFromDB($module = '', $lastID = 0, $limit = 0)
  227. {
  228. $dataList = array();
  229. $table = zget($this->config->convert->objectTables, $module, '');
  230. if($module == 'user')
  231. {
  232. $dataList = $this->dao->dbh($this->sourceDBH)->select('t1.`ID`, t1.`lower_user_name` as account, t1.`lower_display_name` as realname, t1.`lower_email_address` as email, t1.created_date as `join`, t2.user_key as userCode')->from(JIRA_USERINFO)->alias('t1')
  233. ->leftJoin(JIRA_USER)->alias('t2')->on('t1.`lower_user_name` = t2.`lower_user_name`')
  234. ->where('1 = 1')
  235. ->beginIF($lastID)->andWhere('t1.ID')->gt($lastID)->fi()
  236. ->orderBy('t1.ID asc')->limit($limit)
  237. ->fetchAll('id');
  238. }
  239. elseif($module == 'nodeassociation')
  240. {
  241. $dataList = $this->dao->dbh($this->sourceDBH)->select('*')->from($table)->limit($lastID, $limit)->fetchAll();
  242. }
  243. elseif($module == 'fixversion' || $module == 'affectsversion')
  244. {
  245. $dataList = array();
  246. }
  247. elseif(!empty($table))
  248. {
  249. $dataList = $this->dao->dbh($this->sourceDBH)->select('*')->from($table)
  250. ->where('1 = 1')
  251. ->beginIF($lastID)->andWhere('ID')->gt($lastID)->fi()
  252. ->orderBy('ID asc')->limit($limit)
  253. ->fetchAll('id', false);
  254. }
  255. return $dataList;
  256. }
  257. /**
  258. * 从文件中获取jira数据。
  259. * Get jira data from file.
  260. *
  261. * @param string $module
  262. * @param int $lastID
  263. * @param int $limit
  264. * @access public
  265. * @return array
  266. */
  267. public function getJiraDataFromFile($module, $lastID = 0, $limit = 0)
  268. {
  269. $fileName = $module;
  270. if($module == 'build') $fileName = 'version';
  271. if($module == 'file') $fileName = 'fileattachment';
  272. $filePath = $this->app->getTmpRoot() . 'jirafile/' . $fileName . '.xml';
  273. if(!file_exists($filePath)) return array();
  274. $xmlContent = file_get_contents($filePath);
  275. $xmlContent = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $xmlContent);
  276. $reader = new XMLReader();
  277. $dom = new DOMDocument();
  278. $reader->XML($xmlContent);
  279. $reader->read();
  280. $domNode = $reader->expand($dom);
  281. $reader->close();
  282. $parsedXML = simplexml_import_dom($domNode);
  283. $parsedXML = $this->object2Array($parsedXML);
  284. $dataList = array();
  285. foreach($parsedXML as $key => $xmlArray)
  286. {
  287. if(strtolower($key) != strtolower($fileName)) continue;
  288. foreach($xmlArray as $key => $attributes)
  289. {
  290. if(is_array($attributes) && isset($attributes['status']) && $attributes['status'] == 'deleted') break;
  291. if(is_numeric($key))
  292. {
  293. $desc = isset($attributes['description']) ? $attributes['description'] : '';
  294. $summary = isset($attributes['summary']) ? $attributes['summary'] : '';
  295. $body = isset($attributes['body']) ? $attributes['body'] : '';
  296. $data = array();
  297. $dataID = 0;
  298. foreach($attributes as $k => $value)
  299. {
  300. if(empty($value)) continue;
  301. if(is_array($value))
  302. {
  303. if(isset($value['status']) && $value['status'] == 'deleted') break;
  304. if(!empty($desc)) $value['description'] = $desc;
  305. if(!empty($summary)) $value['summary'] = $summary;
  306. if(!empty($body)) $value['body'] = $body;
  307. $dataID = !empty($value['id']) ? $value['id'] : ($key + 1);
  308. $data = array_merge($data, $value);
  309. }
  310. else
  311. {
  312. $data = array_merge($data, array($k => $value));
  313. }
  314. }
  315. if(!empty($dataID)) $dataList[$dataID] = $data;
  316. }
  317. else
  318. {
  319. if(is_array($attributes))
  320. {
  321. if(isset($attributes['status']) && $attributes['status'] == 'deleted') continue;
  322. $dataID = !empty($attributes['id']) ? $attributes['id'] : $key;
  323. $dataList[$dataID] = $attributes;
  324. }
  325. else
  326. {
  327. if(!empty($dataID)) $dataList[$dataID][$key] = $attributes;
  328. }
  329. }
  330. }
  331. }
  332. if($limit)
  333. {
  334. $dataList = array_slice($dataList, $lastID, $limit, true);
  335. if(empty($dataList)) return array();
  336. }
  337. if(in_array($module, array_keys($this->config->convert->objectTables)))
  338. {
  339. foreach($dataList as $key => $data)
  340. {
  341. $buildFunction = 'build' . ucfirst($module) . 'Data';
  342. $dataList[$key] = $this->$buildFunction($data);
  343. }
  344. }
  345. return $dataList;
  346. }
  347. /**
  348. * 分割jira文件。
  349. * Split jira file.
  350. *
  351. * @access public
  352. * @return void
  353. */
  354. public function splitFile()
  355. {
  356. $filePath = $this->app->getTmpRoot() . 'jirafile/';
  357. $fileName = 'entities.xml';
  358. $file = $filePath . $fileName;
  359. $handle = fopen($file, "r");
  360. $tagList = array('<Action' => '</Action>', '<Project' => '</Project>', '<Status' => '</Status>', '<Resolution' => '</Resolution>', '<User' => '</User>', '<Issue' => '</Issue>', '<ChangeGroup' => '</ChangeGroup>', '<ChangeItem' => '</ChangeItem>', '<IssueLink' => '</IssueLink>', '<IssueLinkType' => '</IssueLinkType>', '<FileAttachment' => '</FileattAchment>', '<Version' => '</Version>', '<IssueType' => '</IssueType>', '<NodeAssociation' => '</NodeAssociation>', '<ApplicationUser' => '</ApplicationUser>', '<FieldScreenLayoutItem' => '<FieldScreenLayoutItem>', '<Workflow' => '</Workflow>', '<WorkflowScheme' => '</WorkflowScheme>', '<FieldConfigSchemeIssueType' => '</FieldConfigSchemeIssueType>', '<FieldConfigScheme' => '</FieldConfigScheme>', '<CustomField' => '</CustomField>', '<CustomFieldOption' => '</CustomFieldOption>', '<CustomFieldValue' => '</CustomFieldValue>', '<OSPropertyEntry' => '</OSPropertyEntry>', '<Worklog' => '</Worklog>', '<AuditLog' => '</AuditLog>', '<Group' => '</Group>', '<Membership' => '</Membership>', '<ProjectRoleActor' => '</ProjectRoleActor>', '<Priority' => '</Priority>', '<ConfigurationContext' => '</ConfigurationContext>', '<OptionConfiguration' => '</OptionConfiguration>', '<FixVersion' => '</FixVersion>', '<AffectsVersion' => '</AffectsVersion>');
  361. while(!feof($handle))
  362. {
  363. $itemStr = fgets($handle);
  364. if(empty($itemStr)) continue;
  365. foreach($tagList as $startName => $endName)
  366. {
  367. $startName .= ' ';
  368. if(strpos($itemStr, $startName) === false) continue;
  369. if(strpos($itemStr, '/>') === false)
  370. {
  371. while(true)
  372. {
  373. $followItemStr = fgets($handle);
  374. $itemStr .= $followItemStr;
  375. if(strpos($itemStr, $endName) !== false) break;
  376. }
  377. }
  378. $object = str_replace('<', '', $startName);
  379. $object = trim(strtolower($object));
  380. $data = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $itemStr);
  381. if(!file_exists($filePath . $object . '.xml')) $data = "<?xml version='1.0' encoding='UTF-8'?>\n <entity-engine-xml>\n" . $data;
  382. file_put_contents($filePath . $object . '.xml', $data, FILE_APPEND);
  383. }
  384. }
  385. foreach($tagList as $startName => $endName)
  386. {
  387. $object = str_replace('<', '', $startName);
  388. $object = strtolower($object);
  389. $filename = $filePath . $object . '.xml';
  390. if(file_exists($filename)) file_put_contents($filename, '</entity-engine-xml>', FILE_APPEND);
  391. }
  392. fclose($handle);
  393. }
  394. /**
  395. * 创建jira数据表。
  396. * Create tmp table for import jira.
  397. *
  398. * @access public
  399. * @return void
  400. */
  401. public function createTmpTable4Jira()
  402. {
  403. $sql = <<<EOT
  404. CREATE TABLE `jiratmprelation`(
  405. `id` int(8) NOT NULL AUTO_INCREMENT,
  406. `AType` char(30) NOT NULL,
  407. `AID` char(100) NOT NULL,
  408. `BType` char(30) NOT NULL,
  409. `BID` char(100) NOT NULL,
  410. `extra` char(100) NULL,
  411. PRIMARY KEY (`id`),
  412. UNIQUE KEY `relation` (`AType`,`BType`,`AID`,`BID`)
  413. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  414. EOT;
  415. try
  416. {
  417. $this->dbh->exec($sql);
  418. }
  419. catch(Exception $e){}
  420. }
  421. /**
  422. * 使用接口导入Jira数据。
  423. * Import jira from REST API.
  424. *
  425. * @param string $type user|project|issue|build|issuelink|action|file
  426. * @param int $lastID
  427. * @param bool $createTable
  428. * @access public
  429. * @return array
  430. */
  431. public function importJiraData($type = '', $lastID = 0, $createTable = false)
  432. {
  433. if($createTable) $this->createTmpTable4Jira();
  434. if($this->session->jiraMethod == 'db') $this->connectDB($this->session->jiraDB);
  435. $limit = 1000;
  436. $nextObject = false;
  437. if(empty($type)) $type = key($this->lang->convert->jira->objectList);
  438. foreach(array_keys($this->lang->convert->jira->objectList) as $module)
  439. {
  440. if($module != $type && !$nextObject) continue;
  441. if($module == $type) $nextObject = true;
  442. $this->convertTao->sourceDBH = $this->sourceDBH;
  443. while(true)
  444. {
  445. $dataList = $this->getJiraData($this->session->jiraMethod, $module, $lastID, $limit);
  446. if(empty($dataList))
  447. {
  448. $lastID = 0;
  449. break;
  450. }
  451. if($module == 'user') $this->convertTao->importJiraUser($dataList);
  452. if($module == 'project') $this->convertTao->importJiraProject($dataList);
  453. if($module == 'issue') $this->convertTao->importJiraIssue($dataList);
  454. if($module == 'build') $this->convertTao->importJiraBuild($dataList);
  455. if($module == 'issuelink') $this->convertTao->importJiraIssueLink($dataList);
  456. if($module == 'worklog') $this->convertTao->importJiraWorkLog($dataList);
  457. if($module == 'action') $this->convertTao->importJiraAction($dataList);
  458. if($module == 'changeitem') $this->convertTao->importJiraChangeItem($dataList);
  459. if($module == 'file') $this->convertTao->importJiraFile($dataList);
  460. $offset = $lastID + $limit;
  461. return array('type' => $module, 'count' => count($dataList), 'lastID' => $this->session->jiraMethod == 'db' ? max(array_keys($dataList)) : $offset);
  462. }
  463. }
  464. if($this->session->jiraMethod == 'file') $this->deleteJiraFile();
  465. /* 更新各项目的统计数据。 */
  466. $projectList = $this->dao->dbh($this->dbh)->select('BID')->from(JIRA_TMPRELATION)->where('BType')->in('zproject,zexecution')->fetchPairs();
  467. $this->loadModel('program')->updateStats($projectList);
  468. unset($_SESSION['jiraDB']);
  469. unset($_SESSION['jiraMethod']);
  470. unset($_SESSION['jiraRelation']);
  471. unset($_SESSION['stepStatus']);
  472. unset($_SESSION['jiraUser']);
  473. return array('finished' => true);
  474. }
  475. /**
  476. * 删除jira文件。
  477. * Delete jira backip file.
  478. *
  479. * @access public
  480. * @return void
  481. */
  482. public function deleteJiraFile()
  483. {
  484. $fileList = array('action', 'project', 'status', 'resolution', 'user', 'issue', 'changegroup', 'changeitem', 'issuelink', 'issuelinktype', 'fileattachment', 'version', 'issuetype', 'nodeassociation', 'applicationuser', 'fieldscreenlayoutitem', 'workflow', 'workflowscheme', 'fieldconfigscheme', 'fieldconfigschemeissuetype', 'customfield', 'customfieldoption', 'customfieldvalue', 'ospropertyentry', 'worklog', 'auditlog', 'group', 'membership', 'projectroleactor', 'priority', 'configurationcontext', 'optionconfiguration', 'fixversion', 'affectsversion');
  485. foreach($fileList as $fileName)
  486. {
  487. $filePath = $this->app->getTmpRoot() . 'jirafile/' . $fileName . '.xml';
  488. if(file_exists($filePath)) @unlink($filePath);
  489. }
  490. }
  491. /**
  492. * 检查数据库名称。
  493. * Check dbName is valide.
  494. *
  495. * @param string $dbName
  496. * @access public
  497. * @return bool
  498. */
  499. public function checkDBName($dbName)
  500. {
  501. if(preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $dbName)) return true;
  502. return false;
  503. }
  504. /**
  505. * 获取禅道对象列表。
  506. * Get zentao object list.
  507. *
  508. * @access public
  509. * @return array
  510. */
  511. public function getZentaoObjectList()
  512. {
  513. $objectList = $this->lang->convert->jira->zentaoObjectList;
  514. if(!$this->config->enableER) unset($objectList['epic']);
  515. if(!$this->config->URAndSR) unset($objectList['requirement']);
  516. return $objectList;
  517. }
  518. /**
  519. * 获取禅道关联关系列表。
  520. * Get zentao relation list.
  521. *
  522. * @access public
  523. * @return array
  524. */
  525. public function getZentaoRelationList()
  526. {
  527. return $this->lang->convert->jira->zentaoLinkTypeList;
  528. }
  529. /**
  530. * 获取禅道字段列表。
  531. * Get zentao fields.
  532. *
  533. * @param string $module
  534. * @access public
  535. * @return array
  536. */
  537. public function getZentaoFields($module)
  538. {
  539. $this->app->loadLang($module);
  540. $fields = array();
  541. if(isset($this->config->convert->objectFields[$module]))
  542. {
  543. foreach($this->config->convert->objectFields[$module] as $field)
  544. {
  545. $fields[$field] = $this->lang->{$module}->$field;
  546. }
  547. }
  548. return $fields;
  549. }
  550. /**
  551. * 获取禅道状态列表。
  552. * Get zentao status.
  553. *
  554. * @param string $module
  555. * @access public
  556. * @return array
  557. */
  558. public function getZentaoStatus($module)
  559. {
  560. $this->loadModel($module);
  561. $statusList = $this->lang->{$module}->statusList;
  562. if($module == 'testcase') $statusList = array_merge($statusList, array('add_case_status' => $this->lang->convert->add));
  563. return $statusList;
  564. }
  565. /**
  566. * 获取导入jira的步骤列表。
  567. * Get import jira data steps.
  568. *
  569. * @param array $jiraData
  570. * @param array $issueTypeList
  571. * @access public
  572. * @return array
  573. */
  574. public function getJiraStepList($jiraData, $issueTypeList = array())
  575. {
  576. $stepList = array();
  577. $objectSteps = array();
  578. if(empty($issueTypeList)) $issueTypeList = $this->getJiraData($this->session->jiraMethod, 'issuetype');
  579. if(!empty($jiraData['jiraObject']))
  580. {
  581. foreach($jiraData['jiraObject'] as $objectID)
  582. {
  583. if($jiraData['zentaoObject'][$objectID] == 'add_custom') continue;
  584. if($objectID) $objectSteps[$objectID] = zget($issueTypeList[$objectID], 'pname', '') . $this->lang->convert->jira->steps['objectData'];
  585. }
  586. }
  587. foreach($this->lang->convert->jira->steps as $currentStep => $stepLabel)
  588. {
  589. if($currentStep == 'objectData')
  590. {
  591. if(!empty($objectSteps))
  592. {
  593. $stepList = $stepList + $objectSteps;
  594. continue;
  595. }
  596. }
  597. $stepList[$currentStep] = $stepLabel;
  598. }
  599. return $stepList;
  600. }
  601. /**
  602. * 校验导入jira数据时配置信息完整性。
  603. * Check import jira.
  604. *
  605. * @param string $step
  606. * @access public
  607. * @return bool
  608. */
  609. public function checkImportJira($step)
  610. {
  611. $data = fixer::input('post')->get();
  612. if($step == 'object')
  613. {
  614. $jiraObject = $data->jiraObject;
  615. $zentaoObject = $data->zentaoObject;
  616. foreach($jiraObject as $typeID)
  617. {
  618. if(empty($zentaoObject[$typeID])) dao::$errors['message'] = sprintf($this->lang->error->notempty, $this->lang->convert->jira->zentaoObject);
  619. }
  620. }
  621. return !dao::isError();
  622. }
  623. /**
  624. * 获取导入Jira时禅道的对象属性默认值。
  625. * Get object default value.
  626. *
  627. * @param string $step
  628. * @access public
  629. * @return array
  630. */
  631. public function getObjectDefaultValue($step)
  632. {
  633. if($step == 'object') return $this->config->convert->importDeafaultValue;
  634. $jiraRelation = $this->session->jiraRelation;
  635. $jiraRelation = $jiraRelation ? json_decode($jiraRelation, true) : array();
  636. if(empty($jiraRelation['zentaoObject'][$step])) return array();
  637. $zentaoObject = $jiraRelation['zentaoObject'][$step];
  638. $defaultValue = array();
  639. $importDeafaultValue = $this->config->convert->importDeafaultValue;
  640. $defaultValue["zentaoField$step"] = !empty($importDeafaultValue[$zentaoObject]['field']) ? $importDeafaultValue[$zentaoObject]['field'] : array();
  641. $defaultValue["zentaoStatus$step"] = !empty($importDeafaultValue[$zentaoObject]['status']) ? $importDeafaultValue[$zentaoObject]['status'] : array();
  642. $defaultValue["zentaoAction$step"] = !empty($importDeafaultValue[$zentaoObject]['action']) ? $importDeafaultValue[$zentaoObject]['action'] : array();
  643. $defaultValue["zentaoResolution$step"] = !empty($importDeafaultValue[$zentaoObject]['resolution']) ? $importDeafaultValue[$zentaoObject]['resolution'] : array();
  644. $defaultValue["zentaoReason$step"] = !empty($importDeafaultValue[$zentaoObject]['reason']) ? $importDeafaultValue[$zentaoObject]['reason'] : array();
  645. return $defaultValue;
  646. }
  647. /**
  648. * 获取Jira事务类型列表。
  649. * Get jira type list.
  650. *
  651. * @access public
  652. * @return array
  653. */
  654. public function getJiraTypeList()
  655. {
  656. return $this->getJiraData($this->session->jiraMethod, 'issuetype');
  657. }
  658. /**
  659. * 获取Jira状态列表。
  660. * Get jira status list.
  661. *
  662. * @param string $step
  663. * @access public
  664. * @return array
  665. */
  666. public function getJiraStatusList($step = '')
  667. {
  668. if($this->session->jiraMethod == 'api')
  669. {
  670. $jql = 'created<=' . date('Y-m-d', strtotime('+1 day'));
  671. if($step) $jql .= " AND issuetype = {$step}";
  672. $issues = $this->callJiraAPI('/rest/api/3/search/jql?jql=' . urlencode($jql) . '&fields=status,issuetype&maxResults=5000');
  673. foreach($issues as $issue)
  674. {
  675. if(!empty($issue->fields))
  676. {
  677. foreach($issue->fields as $field => $value) $issue->$field = $value;
  678. }
  679. if(!empty($issue->status->id)) $issue->issuestatus = $issue->status->id;
  680. if(!empty($issue->issuetype->id)) $issue->issuetype = $issue->issuetype->id;
  681. }
  682. }
  683. else
  684. {
  685. $issues = $this->getJiraData($this->session->jiraMethod, 'issue');
  686. }
  687. $statusList = $this->getJiraData($this->session->jiraMethod, 'status');
  688. $jiraStatusList = array();
  689. foreach($issues as $issue)
  690. {
  691. if(empty($issue->issuestatus)) continue;
  692. if(empty($statusList[$issue->issuestatus])) continue;
  693. $status = $statusList[$issue->issuestatus];
  694. $jiraStatusList[$issue->issuetype][$issue->issuestatus] = $status->pname;
  695. }
  696. return $step ? zget($jiraStatusList, $step, array()) : $jiraStatusList;
  697. }
  698. /**
  699. * 获取Jira自定义字段列表。
  700. * Get jira custom fields.
  701. *
  702. * @access public
  703. * @return array
  704. */
  705. public function getJiraCustomField()
  706. {
  707. if($this->config->edition == 'open') return array();
  708. $jiraFields = array();
  709. $fields = $this->getJiraData($this->session->jiraMethod, 'customfield');
  710. if($this->session->jiraMethod == 'api')
  711. {
  712. foreach($fields as $issueType => $fieldList)
  713. {
  714. foreach($fieldList as $fieldID => $field) $fields[$issueType][$fieldID] = $field->cfname;
  715. }
  716. return $fields;
  717. }
  718. $issues = $this->getJiraData($this->session->jiraMethod, 'issue');
  719. $fieldValue = $this->getJiraData($this->session->jiraMethod, 'customfieldvalue');
  720. foreach($fieldValue as $value)
  721. {
  722. if(empty($issues[$value->issue]) || empty($fields[$value->customfield])) continue;
  723. $issue = $issues[$value->issue];
  724. $field = $fields[$value->customfield];
  725. if(in_array($field->customfieldtypekey, array('com.pyxis.greenhopper.jira:gh-sprint', 'com.pyxis.greenhopper.jira:gh-epic-label', 'com.pyxis.greenhopper.jira:gh-epic-status', 'com.pyxis.greenhopper.jira:gh-epic-color'))) continue;
  726. $jiraFields[$issue->issuetype][$value->customfield] = $field->cfname;
  727. }
  728. return $jiraFields;
  729. }
  730. /**
  731. * 获取Jira按照项目分组的自定义字段。
  732. * Get jira custom fields group by project.
  733. *
  734. * @param array $relations
  735. * @access public
  736. * @return array
  737. */
  738. public function getJiraFieldGroupByProject($relations)
  739. {
  740. $fieldList = $this->dao->dbh($this->dbh)->select('AID, extra, BID AS field')->from(JIRA_TMPRELATION)->where('AType')->eq('jcustomfield')->andWhere('BType')->eq('zworkflowfield')->fetchGroup('AID', 'extra');
  741. if($this->session->jiraMethod == 'api')
  742. {
  743. $projectList = $this->callJiraAPI('/rest/api/3/issue/createmeta?expand=projects.issuetypes.fields&maxResults=1000');
  744. $jiraFields = array();
  745. foreach($projectList as $project)
  746. {
  747. if(empty($project->issuetypes)) continue;
  748. foreach($project->issuetypes as $issueType)
  749. {
  750. if(empty($issueType->fields)) continue;
  751. $zentaoObject = $relations['zentaoObject'][$issueType->id];
  752. foreach($issueType->fields as $key => $field)
  753. {
  754. if(strpos($key, 'customfield_') === false) continue;
  755. $fieldKey = str_replace('customfield_', '', $key);
  756. if(empty($fieldList[$fieldKey])) continue;
  757. $zentaoField = $fieldList[$fieldKey][$zentaoObject];
  758. $jiraFields[$project->id][$zentaoObject][$zentaoField->field] = $zentaoField;
  759. }
  760. }
  761. }
  762. return $jiraFields;
  763. }
  764. $issues = $this->getJiraData($this->session->jiraMethod, 'issue');
  765. $fieldValue = $this->getJiraData($this->session->jiraMethod, 'customfieldvalue');
  766. $jiraFields = array();
  767. foreach($fieldValue as $value)
  768. {
  769. if(empty($issues[$value->issue])) continue;
  770. $issue = $issues[$value->issue];
  771. $zentaoObject = $relations['zentaoObject'][$issue->issuetype];
  772. if(!empty($fieldList[$value->customfield][$zentaoObject]))
  773. {
  774. $field = $fieldList[$value->customfield][$zentaoObject];
  775. $jiraFields[$issue->project][$zentaoObject][$field->field] = $field;
  776. }
  777. }
  778. return $jiraFields;
  779. }
  780. /**
  781. * 获取Jira工作流里的动作列表。
  782. * Get jira workflow actions.
  783. *
  784. * @access public
  785. * @return array
  786. */
  787. public function getJiraWorkflowActions()
  788. {
  789. if($this->config->edition == 'open') return array();
  790. if($this->session->jiraMethod == 'api')
  791. {
  792. $schemeList = $this->callJiraAPI('/rest/api/3/issuetypescheme?expand=projects,issuetypes&maxResults=1000');
  793. $workflowList = $this->callJiraAPI('/rest/api/3/workflow/search?expand=transitions,projects&maxResults=1000');
  794. $projectGroup = array();
  795. foreach($schemeList as $scheme)
  796. {
  797. if(empty($scheme->issueTypes->values) || empty($scheme->projects->values)) continue;
  798. foreach($scheme->issueTypes->values as $issueType)
  799. {
  800. foreach($scheme->projects->values as $project) $projectGroup[$issueType->id][$project->id] = $project->id;
  801. }
  802. }
  803. $workflowActions = array();
  804. foreach($workflowList as $workflow)
  805. {
  806. if(empty($workflow->transitions) || empty($workflow->projects)) continue;
  807. foreach($workflow->projects as $project)
  808. {
  809. foreach($projectGroup as $issueType => $projects)
  810. {
  811. if(!in_array($project->id, $projects)) continue;
  812. foreach($workflow->transitions as $action) $workflowActions[$issueType]['actions'][$action->id] = (array)$action;
  813. }
  814. }
  815. }
  816. return $workflowActions;
  817. }
  818. $workflows = $this->getJiraData($this->session->jiraMethod, 'workflow');
  819. $workflowActions = array();
  820. $actionNameList = array();
  821. foreach($workflows as $workflowID => $workflow)
  822. {
  823. $descriptor = simplexml_load_string($workflow->descriptor);
  824. $descriptor = $this->object2Array($descriptor);
  825. foreach($descriptor as $id => $actions)
  826. {
  827. if(!empty($actions['action']))
  828. {
  829. $actionInfo = array();
  830. foreach($actions['action'] as $key => $actionList)
  831. {
  832. if(is_numeric($key))
  833. {
  834. foreach($actionList as $k => $action)
  835. {
  836. $actionInfo = array_merge($actionInfo, $k == '@attributes' ? $action : array($k => $action));
  837. if(empty($actionNameList[$actionInfo['name']]) && strpos($actionInfo['name'], 'Issue') === false) $workflowActions['actions'][] = $actionInfo;
  838. $actionNameList[$actionInfo['name']] = $actionInfo['name'];
  839. }
  840. }
  841. else
  842. {
  843. $actionInfo = array_merge($actionInfo, $key == '@attributes' ? $actionList : array($key => $actionList));
  844. if(empty($actionNameList[$actionInfo['name']]) && strpos($actionInfo['name'], 'Issue') === false) $workflowActions['actions'][] = $actionInfo;
  845. $actionNameList[$actionInfo['name']] = $actionInfo['name'];
  846. }
  847. }
  848. }
  849. elseif(!empty($actions['step']))
  850. {
  851. foreach($actions['step'] as $step)
  852. {
  853. if(empty($step['meta'])) continue;
  854. $workflowActions['steps'][$step['@attributes']['id']] = $step['meta'];
  855. }
  856. }
  857. }
  858. }
  859. return $workflowActions;
  860. }
  861. /**
  862. * 验证Jira api接口能否访问。
  863. * Check jira api.
  864. *
  865. * @access public
  866. * @return bool
  867. */
  868. public function checkJiraApi()
  869. {
  870. $jiraApi = json_decode($this->session->jiraApi, true);
  871. if(empty($jiraApi['domain'])) return false;
  872. $token = base64_encode("{$jiraApi['admin']}:{$jiraApi['token']}");
  873. $url = $jiraApi['domain'] . '/rest/agile/1.0/board';
  874. $boardList = json_decode(commonModel::http($url, array(), array(), array("Authorization: Basic $token"), 'json', 'GET', 10));
  875. if(empty($boardList->values))
  876. {
  877. dao::$errors['message'] = $this->lang->convert->jira->apiError;
  878. return false;
  879. }
  880. return true;
  881. }
  882. /**
  883. * 调用Jira api接口。
  884. * Call Jira API.
  885. *
  886. * @param string $url
  887. * @param int $start
  888. * @param bool $rawData
  889. * @access public
  890. * @return array
  891. */
  892. public function callJiraAPI($url, $start = 0, $rawData = false)
  893. {
  894. if(empty($_SESSION['jiraApi'])) return array();
  895. $jiraApi = json_decode($this->session->jiraApi, true);
  896. if(empty($jiraApi['domain'])) return array();
  897. if(strpos($url, $jiraApi['domain']) === 0) $url = str_replace($jiraApi['domain'], '', $url);
  898. $token = base64_encode("{$jiraApi['admin']}:{$jiraApi['token']}");
  899. $httpURL = $jiraApi['domain'] . $url . "&startAt=$start";
  900. $result = json_decode(commonModel::http($httpURL, array(), array(), array("Authorization: Basic $token"), 'json', 'GET', 10));
  901. if(!empty($result->values) || !empty($result->issues))
  902. {
  903. $dataList = !empty($result->values) ? $result->values : $result->issues;
  904. if(empty($result->isLast))
  905. {
  906. if(!empty($result->nextPageToken))
  907. {
  908. $dataList = array_merge($dataList, $this->callJiraAPI($url . "&nextPageToken={$result->nextPageToken}"));
  909. }
  910. else
  911. {
  912. $dataList = array_merge($dataList, $this->callJiraAPI($url, $start + $result->maxResults));
  913. }
  914. }
  915. return $dataList;
  916. }
  917. if(!empty($result->projects)) return $result->projects;
  918. return $rawData ? $result : array();
  919. }
  920. /**
  921. * 获取jira用户。
  922. * Get jira account.
  923. *
  924. * @param string $userKey
  925. * @access public
  926. * @return string
  927. */
  928. public function getJiraAccount($userKey)
  929. {
  930. if(empty($userKey)) return '';
  931. $users = $this->getJiraUser();
  932. return zget($users, $userKey, '');
  933. }
  934. /**
  935. * 处理Jira用户名为禅道格式。
  936. * Process jira user.
  937. *
  938. * @param string $jiraAccount
  939. * @param string $jiraEmail
  940. * @access public
  941. * @return string
  942. */
  943. public function processJiraUser($jiraAccount, $jiraEmail)
  944. {
  945. $userConfig = $this->session->jiraUser;
  946. $account = substr($jiraAccount, 0, 30);
  947. if($userConfig['mode'] == 'email' && $jiraEmail)
  948. {
  949. if(strpos($jiraEmail, '@') !== false)
  950. {
  951. $account = substr(substr($jiraEmail, 0, strpos($jiraEmail, '@')), 0, 30);
  952. }
  953. else
  954. {
  955. $account = substr($jiraEmail, 0, 30);
  956. }
  957. }
  958. return preg_replace("/[^a-zA-Z0-9]/", "", $account);
  959. }
  960. /**
  961. * 从jira文件中获取版本信息。
  962. * Get version group from jira file.
  963. *
  964. * @access public
  965. * @return array
  966. */
  967. public function getVersionGroup()
  968. {
  969. $filePath = $this->app->getTmpRoot() . 'jirafile/nodeassociation.xml';
  970. if(!file_exists($filePath)) return array();
  971. $xmlContent = file_get_contents($this->app->getTmpRoot() . 'jirafile/nodeassociation.xml');
  972. $xmlContent = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $xmlContent);
  973. $parsedXML = simplexml_load_string($xmlContent, 'SimpleXMLElement', LIBXML_NOCDATA);
  974. if(empty($parsedXML)) return array();
  975. $dataList = array();
  976. $parsedXML = $this->object2Array($parsedXML);
  977. foreach($parsedXML as $key => $xmlArray)
  978. {
  979. if(strtolower($key) != 'nodeassociation') continue;
  980. foreach($xmlArray as $key => $attributes)
  981. {
  982. foreach($attributes as $value)
  983. {
  984. if(!is_array($value)) continue;
  985. if($value['sinkNodeEntity'] != 'Version') continue;
  986. $data = new stdclass();
  987. $data->versionid = $value['sinkNodeId'];
  988. $data->issueid = $value['sourceNodeId'];
  989. $data->relation = $value['associationType'];
  990. $dataList[$value['sinkNodeId']][] = $data;
  991. }
  992. }
  993. }
  994. return $dataList;
  995. }
  996. /**
  997. * 将对象转换为数组。
  998. * Convert object to array.
  999. *
  1000. * @param object|array $parsedXML
  1001. * @access public
  1002. * @return array
  1003. */
  1004. public function object2Array($parsedXML)
  1005. {
  1006. if(is_object($parsedXML))
  1007. {
  1008. $parsedXML = (array)$parsedXML;
  1009. }
  1010. if(is_array($parsedXML))
  1011. {
  1012. foreach($parsedXML as $key => $value)
  1013. {
  1014. if(is_object($value) && empty($value))
  1015. {
  1016. $parsedXML[$key] = (string)$value;
  1017. continue;
  1018. }
  1019. if(!is_object($value) && !is_array($value)) continue;
  1020. $parsedXML[$key] = $this->object2Array($value);
  1021. }
  1022. }
  1023. return $parsedXML;
  1024. }
  1025. /**
  1026. * 转换需求阶段。
  1027. * Convert stage.
  1028. *
  1029. * @param string $jiraStatus
  1030. * @param string $issueType
  1031. * @param array $relations
  1032. * @access public
  1033. * @return string
  1034. */
  1035. public function convertStage($jiraStatus, $issueType, $relations = array())
  1036. {
  1037. if(empty($relations))
  1038. {
  1039. $jiraRelation = $this->session->jiraRelation;
  1040. $relations = $jiraRelation ? json_decode($jiraRelation, true) : array();
  1041. }
  1042. $stage = 'wait';
  1043. if(!empty($relations["zentaoStage$issueType"][$jiraStatus])) $stage = $relations["zentaoStage$issueType"][$jiraStatus];
  1044. return $stage;
  1045. }
  1046. /**
  1047. * 转换状态。
  1048. * Convert jira status.
  1049. *
  1050. * @param string $objectType
  1051. * @param string $jiraStatus
  1052. * @param string $issueType
  1053. * @param array $relations
  1054. * @access public
  1055. * @return string
  1056. */
  1057. public function convertStatus($objectType, $jiraStatus, $issueType, $relations = array())
  1058. {
  1059. if(empty($relations))
  1060. {
  1061. $jiraRelation = $this->session->jiraRelation;
  1062. $relations = $jiraRelation ? json_decode($jiraRelation, true) : array();
  1063. }
  1064. if(!empty($relations["zentaoStatus{$issueType}"][$jiraStatus])) return (string)$relations["zentaoStatus{$issueType}"][$jiraStatus];
  1065. if($objectType == 'testcase' && empty($this->config->testcase->needReview)) return 'normal';
  1066. if($objectType == 'feedback' && empty($this->config->feedback->needReview)) return 'normal';
  1067. if($objectType == 'ticket' && empty($this->config->feedback->ticket)) return 'normal';
  1068. return in_array($objectType, array('task', 'testcase', 'feedback', 'ticket', 'flow')) ? 'wait' : 'active';
  1069. }
  1070. /**
  1071. * 获取Jira的所有冲刺。
  1072. * Get jira sprint.
  1073. *
  1074. * @param array $projectList
  1075. * @access public
  1076. * @return array
  1077. */
  1078. public function getJiraSprint($projectList)
  1079. {
  1080. $sprintGroup = array();
  1081. if($this->session->jiraMethod == 'file' || $this->session->jiraMethod == 'api')
  1082. {
  1083. foreach($projectList as $projectID)
  1084. {
  1085. $boardList = $this->callJiraAPI("/rest/agile/1.0/board?projectKeyOrId={$projectID}&maxResults=1000");
  1086. foreach($boardList as $board)
  1087. {
  1088. $sprintList = $this->callJiraAPI("/rest/agile/1.0/board/$board->id/sprint?maxResults=1000");
  1089. foreach($sprintList as $sprint)
  1090. {
  1091. $sprintGroup[$projectID][$sprint->id] = $sprint;
  1092. }
  1093. }
  1094. }
  1095. }
  1096. else
  1097. {
  1098. $sprintGroup = $this->dao->dbh($this->sourceDBH)->select('project.id as pid, sprint.*')->from('ao_60db71_sprint')->alias('sprint')
  1099. ->leftJoin('ao_60db71_rapidview')->alias('rapview')->on('rapview.id=sprint.RAPID_VIEW_ID')
  1100. ->leftJoin('searchrequest')->alias('search')->on('search.ID=rapview.SAVED_FILTER_ID')
  1101. ->leftJoin('project')->on("search.reqcontent like concat('%project = ', project.pkey, ' O%')")
  1102. ->where('project.id')->notNULL()
  1103. ->fetchGroup('pid', 'id');
  1104. }
  1105. return $sprintGroup;
  1106. }
  1107. /**
  1108. * 获取Jira issue的所属sprint。
  1109. * Get jira sprint issue.
  1110. *
  1111. * @access public
  1112. * @return array
  1113. */
  1114. public function getJiraSprintIssue()
  1115. {
  1116. $issueGroup = array();
  1117. $sprintRelation = $this->dao->dbh($this->dbh)->select('AID,BID')->from(JIRA_TMPRELATION)->where('AType')->eq('jsprint')->andWhere('BType')->eq('zexecution')->fetchPairs();
  1118. if($this->session->jiraMethod != 'db')
  1119. {
  1120. foreach($sprintRelation as $sprintID => $executionID)
  1121. {
  1122. $issueList = $this->callJiraAPI("/rest/agile/1.0/sprint/{$sprintID}/issue?maxResults=1000");
  1123. foreach($issueList as $issue)
  1124. {
  1125. $issueGroup[$issue->id] = $executionID;
  1126. if(!empty($issue->fields->subtasks))
  1127. {
  1128. foreach($issue->fields->subtasks as $subtask) $issueGroup[$subtask->id] = $executionID;
  1129. }
  1130. }
  1131. }
  1132. }
  1133. return $issueGroup;
  1134. }
  1135. /**
  1136. * 获取Jira已归档的项目。
  1137. * Get jira archived project.
  1138. *
  1139. * @param array $dataList
  1140. * @access public
  1141. * @return array
  1142. */
  1143. public function getJiraArchivedProject($dataList)
  1144. {
  1145. if($this->session->jiraMethod == 'api') return array();
  1146. $archivedProject = array();
  1147. $auditLog = $this->getJiraData($this->session->jiraMethod, 'auditlog');
  1148. if($auditLog)
  1149. {
  1150. foreach($auditLog as $log)
  1151. {
  1152. if($log->summary == 'Project archived' && $log->object_type == 'project') $archivedProject[$log->object_id] = $log->object_id;
  1153. }
  1154. }
  1155. if($this->session->jiraMethod == 'file')
  1156. {
  1157. if(empty($_SESSION['jiraApi'])) return $archivedProject;
  1158. $jiraApi = json_decode($this->session->jiraApi, true);
  1159. if(empty($jiraApi['domain'])) return $archivedProject;
  1160. $token = base64_encode("{$jiraApi['admin']}:{$jiraApi['token']}");
  1161. $url = $jiraApi['domain'] . '/rest/api/2/project/';
  1162. $result = json_decode(commonModel::http($url, array(), array(), array("Authorization: Basic $token"), 'json', 'GET', 10));
  1163. $projectList = array();
  1164. if(!empty($result))
  1165. {
  1166. foreach($result as $project)
  1167. {
  1168. if(!empty($project->id) && empty($project->archived)) $projectList[$project->id] = $project->id; // 没有被归档的项目。
  1169. }
  1170. }
  1171. /* 过滤掉没被归档的项目,剩下的都是被归档的项目。 */
  1172. foreach($dataList as $project)
  1173. {
  1174. if(empty($projectList[$project->id])) $archivedProject[$project->id] = $project->id;
  1175. }
  1176. }
  1177. else
  1178. {
  1179. $sql = "SHOW tables like 'ao_c77861_audit_entity';";
  1180. if($this->dao->dbh($this->sourceDBH)->query($sql)->fetch())
  1181. {
  1182. $auditEntity = $this->dao->dbh($this->sourceDBH)->select('PRIMARY_RESOURCE_ID')->from('ao_c77861_audit_entity')
  1183. ->where('ACTION_T_KEY')->eq('jira.auditing.project.archived')
  1184. ->andWhere('PRIMARY_RESOURCE_TYPE')->eq('PROJECT')
  1185. ->fetchPairs();
  1186. $archivedProject = array_merge($archivedProject, $auditEntity);
  1187. }
  1188. }
  1189. return $archivedProject;
  1190. }
  1191. /**
  1192. * 获取Jira项目角色与成员。
  1193. * Get jira project role actor.
  1194. *
  1195. * @param array $projectList
  1196. * @access public
  1197. * @return array
  1198. */
  1199. public function getJiraProjectRoleActor($projectList)
  1200. {
  1201. if($this->session->jiraMethod == 'api')
  1202. {
  1203. $projectMember = array();
  1204. foreach($projectList as $project)
  1205. {
  1206. $projectRoles = $this->callJiraAPI("/rest/api/2/project/{$project->id}/role?maxResults=1000", 0, true);
  1207. foreach($projectRoles as $role)
  1208. {
  1209. if(!is_string($role)) continue;
  1210. $users = $this->callJiraAPI($role . '?maxResults=1000', 0 , true);
  1211. if(empty($users->actors)) continue;
  1212. foreach($users->actors as $user) $projectMember[$project->id][$user->actorUser->accountId] = $user->actorUser->accountId;
  1213. }
  1214. }
  1215. return $projectMember;
  1216. }
  1217. $projectRoleActor = $this->getJiraData($this->session->jiraMethod, 'projectroleactor');
  1218. $memberShip = $this->getJiraData($this->session->jiraMethod, 'membership');
  1219. $projectMember = array();
  1220. foreach($projectRoleActor as $role)
  1221. {
  1222. if(empty($role->pid)) continue;
  1223. if($role->roletype == 'atlassian-user-role-actor')
  1224. {
  1225. $projectMember[$role->pid][$role->roletypeparameter] = $role->roletypeparameter;
  1226. }
  1227. if($role->roletype == 'atlassian-group-role-actor')
  1228. {
  1229. foreach($memberShip as $member)
  1230. {
  1231. if($member->parent_name == $role->roletypeparameter) $projectMember[$role->pid]["JIRAUSER{$member->child_id}"] = 'JIRAUSER' . $member->child_id;
  1232. }
  1233. }
  1234. }
  1235. return $projectMember;
  1236. }
  1237. /**
  1238. * 获取按照项目分组的Jira事务类型。
  1239. * get Jira issue type group by project.
  1240. *
  1241. * @param array $relations
  1242. * @access public
  1243. * @return array
  1244. */
  1245. public function getIssueTypeList($relations)
  1246. {
  1247. if($this->session->jiraMethod == 'api')
  1248. {
  1249. $projectList = $this->callJiraAPI('/rest/api/3/issue/createmeta?expand=projects.issuetypes&maxResults=1000');
  1250. $projectGroup = array();
  1251. foreach($projectList as $project)
  1252. {
  1253. if(empty($project->issuetypes)) continue;
  1254. foreach($project->issuetypes as $issueType)
  1255. {
  1256. $projectGroup[$project->id][] = $relations['zentaoObject'][$issueType->id];
  1257. }
  1258. }
  1259. return $projectGroup;
  1260. }
  1261. $schemeproject = $this->getJiraData($this->session->jiraMethod, 'configurationcontext');
  1262. $schemeissuetype = $this->getJiraData($this->session->jiraMethod, 'optionconfiguration');
  1263. $projectIssueTypeList = array();
  1264. foreach($schemeproject as $projectRelation)
  1265. {
  1266. if(!empty($projectRelation->project) && $projectRelation->customfield == 'issuetype')
  1267. {
  1268. foreach($schemeissuetype as $issueTypeRelation)
  1269. {
  1270. if($issueTypeRelation->fieldconfig == $projectRelation->fieldconfigscheme && $issueTypeRelation->fieldid == 'issuetype' && !empty($issueTypeRelation->optionid))
  1271. {
  1272. if(!empty($relations['zentaoObject'][$issueTypeRelation->optionid])) $projectIssueTypeList[$projectRelation->project][] = $relations['zentaoObject'][$issueTypeRelation->optionid];
  1273. }
  1274. }
  1275. }
  1276. }
  1277. return $projectIssueTypeList;
  1278. }
  1279. /**
  1280. * 快速导入jira数据。
  1281. * Quick import Jira data.
  1282. *
  1283. * @access public
  1284. * @return void
  1285. */
  1286. public function quickImportJiraData()
  1287. {
  1288. $jiraApi = array();
  1289. $jiraApi['domain'] = $this->post->domain;
  1290. $jiraApi['admin'] = $this->post->account;
  1291. $jiraApi['token'] = $this->post->token;
  1292. $this->session->set('jiraApi', json_encode($jiraApi));
  1293. $this->session->set('jiraDB', '');
  1294. $this->session->set('jiraMethod', 'api');
  1295. $this->checkJiraApi();
  1296. if(dao::isError()) return false;
  1297. $issueTypeList = $this->getJiraTypeList();
  1298. $defaultValue = $this->getObjectDefaultValue('object');
  1299. $jiraFields = $this->getJiraCustomField();
  1300. $jiraStatus = $this->getJiraStatusList();
  1301. $jiraActions = $this->getJiraWorkflowActions();
  1302. $jiraResolution = $this->getJiraData('api', 'resolution');
  1303. $jiraLinkTypes = $this->getJiraData('api', 'issuelinktype');
  1304. $jiraRelation = array();
  1305. foreach($issueTypeList as $issueID => $issueType)
  1306. {
  1307. $jiraRelation['jiraObject'][] = $issueID;
  1308. $jiraRelation['zentaoObject'][$issueID] = !empty($defaultValue['zentaoObject'][$issueType->pname]) ? $defaultValue['zentaoObject'][$issueType->pname] : 'add_custom';
  1309. if($jiraRelation['zentaoObject'][$issueID] == 'add_custom') continue;
  1310. if(!empty($jiraFields[$issueID]))
  1311. {
  1312. foreach($jiraFields[$issueID] as $fieldID => $field)
  1313. {
  1314. $jiraRelation["jiraField{$issueID}"][] = $fieldID;
  1315. $jiraRelation["zentaoField{$issueID}"][$fieldID] = 'add_field';
  1316. }
  1317. }
  1318. if(!empty($jiraStatus[$issueID]))
  1319. {
  1320. foreach($jiraStatus[$issueID] as $statusID => $status)
  1321. {
  1322. $jiraRelation["jiraStatus{$issueID}"][] = $statusID;
  1323. $jiraRelation["zentaoStatus{$issueID}"][$statusID] = !empty($this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['status'][$status]) ? $this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['status'][$status] : '';
  1324. if(in_array($jiraRelation['zentaoObject'][$issueID], array('requirement', 'story', 'epic'))) $jiraRelation["zentaoStage{$issueID}"][$statusID] = '';
  1325. }
  1326. }
  1327. if(!empty($jiraActions[$issueID]))
  1328. {
  1329. foreach($jiraActions[$issueID]['actions'] as $actionID => $action)
  1330. {
  1331. $jiraRelation["jiraAction{$issueID}"][] = $actionID;
  1332. $jiraRelation["zentaoAction{$issueID}"][$actionID] = !empty($this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['action'][$action['name']]) ? $this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['action'][$action['name']] : 'add_action';
  1333. }
  1334. }
  1335. if(!empty($jiraResolution))
  1336. {
  1337. foreach($jiraResolution as $resolutionID => $resolution)
  1338. {
  1339. $jiraRelation["jiraResolution{$issueID}"][] = $resolutionID;
  1340. if($jiraRelation['zentaoObject'][$issueID] == 'bug')
  1341. {
  1342. $jiraRelation["zentaoResolution{$issueID}"][$resolutionID] = !empty($this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['resolution'][$resolution->pname]) ? $this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['resolution'][$resolution->pname] : '';
  1343. }
  1344. else
  1345. {
  1346. $jiraRelation["zentaoReason{$issueID}"][$resolutionID] = !empty($this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['reason'][$resolution->pname]) ? $this->config->convert->importDeafaultValue[$jiraRelation['zentaoObject'][$issueID]]['reason'][$resolution->pname] : '';
  1347. }
  1348. }
  1349. }
  1350. }
  1351. if(!empty($jiraLinkTypes))
  1352. {
  1353. foreach($jiraLinkTypes as $linkID => $linkType)
  1354. {
  1355. $jiraRelation['jiraLinkType'][] = $linkID;
  1356. $jiraRelation['zentaoLinkType'][$linkID] = 'add_relation';
  1357. }
  1358. }
  1359. $jiraUser['password'] = md5('123456');
  1360. $jiraUser['group'] = '';
  1361. $jiraUser['mode'] = 'email';
  1362. $this->session->set('jiraUser', $jiraUser);
  1363. $this->session->set('jiraRelation', json_encode($jiraRelation));
  1364. $this->batchImportJiraData();
  1365. return true;
  1366. }
  1367. /**
  1368. * 分批次的导入Jira数据。
  1369. * Batch import Jira data.
  1370. *
  1371. * @param string $type
  1372. * @param int $lastID
  1373. * @param bool $createTable
  1374. * @access public
  1375. * @return void
  1376. */
  1377. public function batchImportJiraData($type = '', $lastID = 0, $createTable = true)
  1378. {
  1379. $result = $this->importJiraData($type, $lastID, $createTable);
  1380. if(!empty($result['finished'])) return true;
  1381. return $this->batchImportJiraData($result['type'], $result['lastID'], false);
  1382. }
  1383. /**
  1384. * 将日期转成对应时区的日期。
  1385. * Format date.
  1386. *
  1387. * @param string $date
  1388. * @access public
  1389. * @return string
  1390. */
  1391. public function formatDate($date)
  1392. {
  1393. if($this->session->jiraMethod == 'api') return $date;
  1394. $dateUTC = new DateTime($date, new DateTimeZone('UTC'));
  1395. $dateUTC->setTimezone(new DateTimeZone($this->config->timezone));
  1396. return $dateUTC->format('Y-m-d');
  1397. }
  1398. /**
  1399. * 将日期转成对应时区的日期。
  1400. * Format date.
  1401. *
  1402. * @param string $date
  1403. * @access public
  1404. * @return string
  1405. * @param string $datetime
  1406. */
  1407. public function formatDatetime($datetime)
  1408. {
  1409. if($this->session->jiraMethod == 'api') return $datetime;
  1410. $dateUTC = new DateTime($datetime, new DateTimeZone('UTC'));
  1411. $dateUTC->setTimezone(new DateTimeZone($this->config->timezone));
  1412. return $dateUTC->format('Y-m-d H:i:s');
  1413. }
  1414. }