tao.php 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. <?php
  2. class upgradeTao extends upgradeModel
  3. {
  4. /**
  5. * 获取中间表分组ID。
  6. * Get dataview group ID.
  7. *
  8. * @param string $name
  9. * @access protected
  10. * @return int
  11. */
  12. protected function getDataviewGroupID($name)
  13. {
  14. $groupID = $this->dao->select('id')->from(TABLE_MODULE)->where('type')->eq('dataview')->andWhere('name')->eq($name)->fetch('id');
  15. if(empty($groupID))
  16. {
  17. $group = new stdclass();
  18. $group->root = 0;
  19. $group->name = $name;
  20. $group->parent = 0;
  21. $group->grade = 1;
  22. $group->order = 10;
  23. $group->type = 'dataview';
  24. $this->dao->insert(TABLE_MODULE)->data($group)->exec();
  25. $groupID = $this->dao->lastInsertID();
  26. $this->dao->update(TABLE_MODULE)->set("`path` = CONCAT(',', `id`, ',')")->where('id')->eq($groupID)->exec();
  27. }
  28. return $groupID;
  29. }
  30. /**
  31. * 将内置透视表转化为中间表。
  32. * Convert built-in dataset to dataview.
  33. *
  34. * @access protected
  35. * @return void
  36. */
  37. protected function convertBuiltInDataSet()
  38. {
  39. $this->loadModel('dataset');
  40. $this->loadModel('dataview');
  41. $dataview = new stdclass();
  42. $dataview->group = $this->getDataviewGroupID($this->lang->dataview->builtIn);
  43. $dataview->createdBy = 'system';
  44. $dataview->createdDate = helper::now();
  45. foreach($this->lang->dataset->tables as $code => $dataset)
  46. {
  47. $sameCodeList = $this->dao->select('*')->from(TABLE_DATAVIEW)->where('code')->eq($code)->fetchAll();
  48. if(!empty($sameCodeList)) continue;
  49. $dataview->name = $dataset['name'];
  50. $dataview->code = $code;
  51. $dataview->view = 'ztv_' . $code;
  52. $table = $this->dataset->getTableInfo($code);
  53. $dataview->sql = $this->dataset->getTableData($table->schema, 'id_desc', 100, true);
  54. $fields = array();
  55. foreach($table->schema->fields as $key => $field)
  56. {
  57. $relatedField = '';
  58. if($field['type'] == 'object' && isset($field['show']))
  59. {
  60. $key = str_replace('.', '_', $field['show']);
  61. $relatedField = substr($field['show'], strpos($field['show'], '.') + 1);
  62. $relatedObject = isset($field['object']) ? $field['object'] : '';
  63. if(!empty($relatedObject) && isset($table->schema->objects[$relatedObject]))
  64. {
  65. foreach($table->schema->objects[$relatedObject] as $fieldID => $fieldName)
  66. {
  67. if($fieldID == $relatedField) continue;
  68. $addField = "{$relatedObject}_{$fieldID}";
  69. $fields[$addField] = array();
  70. $fields[$addField]['name'] = isset($fieldName['name']) ? $fieldName['name'] : $addField;
  71. $fields[$addField]['field'] = $fieldID;
  72. $fields[$addField]['object'] = $relatedObject;
  73. $fields[$addField]['type'] = 'object';
  74. }
  75. }
  76. }
  77. if(!isset($fields[$key])) $fields[$key] = array();
  78. $fields[$key]['name'] = $field['name'];
  79. $fields[$key]['field'] = empty($relatedField) ? $key : $relatedField;
  80. $fields[$key]['object'] = isset($field['object']) ? $field['object'] : $code;
  81. $fields[$key]['type'] = $field['type'];
  82. }
  83. $dataview->fields = json_encode($fields);
  84. $this->dao->insert(TABLE_DATAVIEW)->data($dataview)->exec();
  85. $dataviewID = $this->dao->lastInsertID();
  86. if(!empty($dataview->view) && !empty($dataview->sql)) $this->dataview->createViewInDB($dataviewID, $dataview->view, $dataview->sql);
  87. }
  88. }
  89. /**
  90. * 将自定义透视表转化为中间表。
  91. * Convert custom dataset to dataview.
  92. *
  93. * @param array $customDataset
  94. * @access protected
  95. * @return void
  96. */
  97. protected function convertCustomDataSet($customDataset)
  98. {
  99. $dataview = new stdclass();
  100. $dataview->group = $this->getDataviewGroupID($this->lang->dataview->default);
  101. foreach($customDataset as $datasetID => $dataset)
  102. {
  103. $dataview->name = $dataset->name;
  104. $dataview->code = 'custom_' . $datasetID;
  105. $dataview->view = 'ztv_custom_' . $datasetID;
  106. $dataview->sql = $dataset->sql;
  107. $dataview->fields = $dataset->fields;
  108. $dataview->createdBy = $dataset->createdBy;
  109. $dataview->createdDate = $dataset->createdDate;
  110. $dataview->deleted = $dataset->deleted;
  111. $this->dao->insert(TABLE_DATAVIEW)->data($dataview)->exec();
  112. $dataviewID = $this->dao->lastInsertID();
  113. if(!empty($dataview->view) && !empty($dataview->sql)) $this->dataview->createViewInDB($dataviewID, $dataview->view, $dataview->sql);
  114. }
  115. }
  116. /**
  117. * 获取标准的版本 sql 语句。
  118. * Get standard sqls by version.
  119. *
  120. * @param string $version
  121. * @access protected
  122. * @return string
  123. */
  124. protected function getStandardSQLs($version)
  125. {
  126. if(empty($version)) $version = $this->config->installedVersion;
  127. $version = str_replace('.', '_', $version);
  128. $openVersion = $this->getOpenVersion($version);
  129. $openVersion = str_replace('_', '.', $openVersion);
  130. $checkVersion = version_compare($openVersion, '16.5', '<') ? str_replace('_', '.', $version) : $openVersion;
  131. $standardSQL = $this->app->getAppRoot() . 'db' . DS . 'standard' . DS . 'zentao' . $checkVersion . '.sql';
  132. if(!file_exists($standardSQL)) return '';
  133. $sqls = file_get_contents($standardSQL);
  134. if(empty($this->config->isINT))
  135. {
  136. $xStandardSQL = $this->app->getAppRoot() . 'db' . DS . 'standard' . DS . 'xuanxuan' . $openVersion . '.sql';
  137. if(file_exists($xStandardSQL)) $sqls .= "\n" . file_get_contents($xStandardSQL);
  138. }
  139. return $sqls;
  140. }
  141. /**
  142. * 根据 sql 语句获取数据库中的字段。
  143. * Get fields in database by sql.
  144. *
  145. * @param string $sql
  146. * @access protected
  147. * @return array
  148. */
  149. protected function getFieldsBySQL($sql)
  150. {
  151. $return = array('fields' => array(), 'lines' => array(), 'table' => '', 'tableExists' => true, 'changeEngineSQL' => '');
  152. $lines = explode("\n", $sql);
  153. /* If table name isn't exist, skip it. . */
  154. $createHead = array_shift($lines);
  155. preg_match_all('/CREATE TABLE `([^`]*)`/', $createHead, $out);
  156. if(!isset($out[1][0])) return array_values($return);
  157. $return['table'] = str_replace('zt_', $this->config->db->prefix, $out[1][0]);
  158. try
  159. {
  160. $dbCreateSQL = $this->dbh->query("SHOW CREATE TABLE `{$return['table']}`")->fetch(PDO::FETCH_ASSOC);
  161. $dbSQLLines = explode("\n", $dbCreateSQL['Create Table']);
  162. $dbSQLFoot = array_pop($dbSQLLines);
  163. preg_match_all('/ENGINE=(\w+) /', $dbSQLFoot, $out);
  164. $dbEngine = isset($out[1][0]) ? $out[1][0] : 'InnoDB';
  165. array_shift($dbSQLLines);
  166. foreach($dbSQLLines as $dbSQLLine)
  167. {
  168. $dbSQLLine = trim($dbSQLLine);
  169. if(!preg_match('/^`([^`]*)` /', $dbSQLLine)) continue; // Skip no describe field line.
  170. $dbSQLLine = rtrim($dbSQLLine, ',');
  171. $dbSQLLine = str_replace('utf8 COLLATE utf8_general_ci', 'utf8', $dbSQLLine);
  172. $dbSQLLine = preg_replace('/ DEFAULT (\-?\d+\.?\d*)$/', " DEFAULT '$1'", $dbSQLLine);
  173. list($field) = explode(' ', $dbSQLLine);
  174. $return['fields'][$field] = rtrim($dbSQLLine, ',');
  175. }
  176. }
  177. catch(PDOException $e)
  178. {
  179. $errorInfo = $e->errorInfo;
  180. $errorCode = $errorInfo[1];
  181. if($errorCode == '1146') $return['tableExists'] = false; // If table is not extists, try create this table by create sql.
  182. }
  183. /* If the table engine in the database isn't the standard engine, change the table engine in the database to the standard engine. */
  184. $createFoot = array_pop($lines);
  185. preg_match_all('/ENGINE=(\w+) /', $createFoot, $out);
  186. $stdEngine = isset($out[1][0]) ? $out[1][0] : 'InnoDB';
  187. if($stdEngine != $dbEngine) $return['changeEngineSQL'] = "ALTER TABLE `{$return['table']}` ENGINE='{$stdEngine}'";
  188. $return['lines'] = $lines;
  189. return array_values($return);
  190. }
  191. /**
  192. * 根据标准 sql 语句修改数据库中的字段。
  193. * Change fields in database by standard sql.
  194. *
  195. * @param string $stdLine
  196. * @param string $dbLine
  197. * @access protected
  198. * @return string
  199. */
  200. protected function changeField($stdLine, $dbLine)
  201. {
  202. /* Get configs. */
  203. $stdConfigs = explode(' ', $stdLine);
  204. $dbConfigs = explode(' ', $dbLine);
  205. if($stdConfigs[1] != $dbConfigs[1])
  206. {
  207. /* Get field type. */
  208. $stdType = $stdConfigs[1];
  209. $dbType = $dbConfigs[1];
  210. $stdLength = 0;
  211. $dbLength = 0;
  212. /* Get field type and length. */
  213. preg_match_all('/^(\w+)(\((\d+)\))?$/', $stdConfigs[1], $stdOutput);
  214. if(!empty($stdOutput[1][0])) $stdType = $stdOutput[1][0];
  215. if(!empty($stdOutput[3][0])) $stdLength = $stdOutput[3][0];
  216. preg_match_all('/^(\w+)(\((\d+)\))?$/', $dbConfigs[1], $dbOutput);
  217. if(!empty($dbOutput[1][0])) $dbType = $dbOutput[1][0];
  218. if(!empty($dbOutput[3][0])) $dbLength = $dbOutput[3][0];
  219. $stdIsInt = stripos($stdType, 'int') !== false;
  220. $stdIsVarchar = stripos($stdType, 'varchar') !== false;
  221. $stdIsText = stripos($stdType, 'text') !== false;
  222. $dbIsInt = stripos($dbType, 'int') !== false;
  223. $dbIsText = stripos($dbType, 'text') !== false;
  224. /* If the type in database is int and the length of type is empty, get the length from the config. */
  225. if($dbIsInt && $dbLength == 0)
  226. {
  227. $intFieldLengths = zget($this->config->upgrade->dbFieldLengths, $dbConfigs[2] == 'unsigned' ? 'unsigned' : 'int', array());
  228. $dbLength = zget($intFieldLengths, $dbType, 0);
  229. }
  230. if($dbLength > $stdLength) $stdConfigs[1] = $dbConfigs[1]; // If the length in database is longer than the length in standard, use the length in database.
  231. if($stdIsInt && $dbIsText) $stdConfigs[1] = $dbConfigs[1]; // If the type in standard is int and the type in database is text, use text as the type.
  232. if($stdIsVarchar && $dbIsText) $stdConfigs[1] = $dbConfigs[1]; // If the type in standard is varchar and the type in database is text, use text as the type.
  233. /* If both of the type in standard and in database are text, get the length from the config. */
  234. if($stdIsText && $dbIsText)
  235. {
  236. $textFieldLengths = $this->config->upgrade->dbFieldLengths['text'];
  237. if($textFieldLengths[$stdType] < $textFieldLengths[$dbType]) $stdConfigs[1] = $dbConfigs[1];
  238. }
  239. if(stripos($stdConfigs[1], 'int') === false && $stdConfigs[2] == 'unsigned') unset($stdConfigs[2]);
  240. $stdLine = implode(' ', $stdConfigs);
  241. if($stdLine == $dbLine) return ''; // If the sql is same, won't change it.
  242. }
  243. return $stdLine;
  244. }
  245. /**
  246. * 获取查询关联到发布的动作的sql。
  247. * Get sql of query action linked to release.
  248. *
  249. * @access protected
  250. * @return string
  251. */
  252. protected function getLinked2releaseActionsSql()
  253. {
  254. return $this->dao->select('objectID, extra, max(`date`) as date, action')
  255. ->from(TABLE_ACTION)
  256. ->where('objectType')->eq('story')
  257. ->andWhere('action')->eq('linked2release')
  258. ->groupBy('objectID,extra,action')
  259. ->get();
  260. }
  261. /**
  262. * 获取关联分支记录相关的数据。
  263. * Get data linked to release.
  264. *
  265. * @access protected
  266. * @return array
  267. */
  268. protected function getReleasedStorys()
  269. {
  270. $linked2releaseActions = $this->getLinked2releaseActionsSql();
  271. return $this->dao->select('t2.id, t1.date')
  272. ->from("($linked2releaseActions)")->alias('t1')
  273. ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.objectID = t2.id')
  274. ->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t2.product = t3.id')
  275. ->leftJoin(TABLE_RELEASE)->alias('t4')->on('t1.extra = t4.id')
  276. ->where('t2.deleted')->eq('0')
  277. ->andWhere('t3.deleted')->eq('0')
  278. ->andWhere('t2.stage', true)->eq('released')
  279. ->orWhere('t2.closedReason')->eq('done')
  280. ->markRight(1)
  281. ->fetchAll();
  282. }
  283. /**
  284. * 在工作流表中插入内置模块数据。
  285. * Create buildin modules to TABLE_WORKFLOW.
  286. *
  287. * @access protected
  288. * @return void
  289. */
  290. protected function createModules2Workflow()
  291. {
  292. $data = new stdclass();
  293. $data->vision = 'rnd';
  294. $data->buildin = 1;
  295. $data->createdBy = isset($this->app->user) ? $this->app->user->account : 'admin';
  296. $data->createdDate = helper::now();
  297. $data->status = 'normal';
  298. foreach($this->config->workflow->buildin->modules as $app => $appModules)
  299. {
  300. $data->app = $app;
  301. foreach($appModules as $module => $options)
  302. {
  303. $this->app->loadLang($module);
  304. $data->module = $module;
  305. $data->name = isset($this->lang->$module->common) ? $this->lang->$module->common : $module;
  306. $data->table = str_replace('`', '', zget($options, 'table', ''));
  307. $data->navigator = zget($options, 'navigator', 'secondary');
  308. if($module == 'story') $data->name = $this->lang->searchObjects['story'];
  309. if($module == 'execution') $data->name = $this->lang->workflow->execution;
  310. $modelField = $this->dao->select('id')->from(TABLE_WORKFLOW)->where('app')->eq($app)->andWhere('module')->eq($module)->andWhere('vision')->eq('rnd')->fetch('id');
  311. if(!$modelField) $this->dao->insert(TABLE_WORKFLOW)->data($data)->exec();
  312. }
  313. }
  314. }
  315. /**
  316. * 在工作流操作表中插入内置模块操作数据。
  317. * Create buildin modules actions to TABLE_WORKFLOWACTION.
  318. *
  319. * @access protected
  320. * @return void
  321. */
  322. protected function createActions2Workflow()
  323. {
  324. $actions = $this->config->workflowaction->buildin->actions;
  325. $actionTypes = $this->config->workflowaction->buildin->types;
  326. $actionMethods = $this->config->workflowaction->buildin->methods;
  327. $actionOpens = $this->config->workflowaction->buildin->opens;
  328. $actionLayouts = $this->config->workflowaction->buildin->layouts;
  329. $actionPositions = $this->config->workflowaction->buildin->positions;
  330. $actionShows = $this->config->workflowaction->buildin->shows;
  331. $data = new stdclass();
  332. $data->buildin = 1;
  333. $data->role = 'buildin';
  334. $data->extensionType = 'none';
  335. $data->createdBy = isset($this->app->user) ? $this->app->user->account : 'admin';
  336. $data->createdDate = helper::now();
  337. foreach($actions as $module => $moduleActions)
  338. {
  339. $data->module = $module;
  340. foreach($moduleActions as $action)
  341. {
  342. $data->action = $action;
  343. /* Use default action name if not set flow action name. */
  344. $name = '';
  345. if(isset($this->lang->$module->$action)) $name = $this->lang->$module->$action;
  346. if(empty($name) && in_array($action, array_keys($this->config->flowAction)))
  347. {
  348. $methodName = $this->config->flowAction[$action];
  349. if(isset($this->lang->$module->$methodName)) $name = $this->lang->$module->$methodName;
  350. }
  351. if(empty($name) && isset($this->lang->workflowaction->default->actions[$action])) $name = $this->lang->workflowaction->default->actions[$action];
  352. if(empty($name)) $name = $action;
  353. $data->name = $name;
  354. $data->method = $data->action;
  355. $data->open = 'normal';
  356. $data->layout = 'normal';
  357. $data->type = 'single';
  358. $data->position = 'browseandview';
  359. $data->show = 'direct';
  360. if(isset($actionMethods[$module][$action])) $data->method = $actionMethods[$module][$action];
  361. if(isset($actionOpens[$module][$action])) $data->open = $actionOpens[$module][$action];
  362. if(isset($actionLayouts[$module][$action])) $data->layout = $actionLayouts[$module][$action];
  363. if(isset($actionTypes[$module][$action])) $data->type = $actionTypes[$module][$action];
  364. if(isset($actionPositions[$module][$action])) $data->position = $actionPositions[$module][$action];
  365. if(isset($actionShows[$module][$action])) $data->show = $actionShows[$module][$action];
  366. $actionField = $this->dao->select('id')->from(TABLE_WORKFLOWACTION)->where('module')->eq($module)->andWhere('action')->eq($action)->andWhere('vision')->eq('rnd')->fetch('id');
  367. if(!$actionField) $this->dao->insert(TABLE_WORKFLOWACTION)->data($data)->exec();
  368. }
  369. }
  370. }
  371. /**
  372. * 在工作流布局表中插入内置模块布局数据。
  373. * Insert layouts of buildin modules to TABLE_WORKFLOWLAYOUT.
  374. *
  375. * @access protected
  376. * @return void
  377. */
  378. protected function createLayouts2Workflow()
  379. {
  380. $data = new stdclass();
  381. foreach($this->config->workflowlayout->buildin->layouts as $module => $moduleLayouts)
  382. {
  383. $data->module = $module;
  384. foreach($moduleLayouts as $action => $layoutFields)
  385. {
  386. $order = 1;
  387. $data->action = $action;
  388. foreach($layoutFields as $field => $options)
  389. {
  390. $data->field = $field;
  391. $data->width = zget($options, 'width', 0);
  392. $data->mobileShow = zget($options, 'mobileShow', 0);
  393. $data->order = $order++;
  394. if($data->width == 'auto') $data->width = 0;
  395. $layoutField = $this->dao->select('id')->from(TABLE_WORKFLOWLAYOUT)->where('module')->eq($module)->andWhere('action')->eq($action)->andWhere('field')->eq($field)->andWhere('vision')->eq('rnd')->fetch('id');
  396. if(!$layoutField) $this->dao->insert(TABLE_WORKFLOWLAYOUT)->data($data)->exec();
  397. }
  398. }
  399. }
  400. }
  401. /**
  402. * 为项目创建项目主库。
  403. * Create doc lib for project.
  404. *
  405. * @param object $project
  406. * @access protected
  407. * @return void
  408. */
  409. protected function createProjectDocLib($project)
  410. {
  411. $this->app->loadLang('doc');
  412. $lib = new stdclass();
  413. $lib->project = $project->id;
  414. $lib->name = $this->lang->doclib->main['project'];
  415. $lib->type = 'project';
  416. $lib->main = '1';
  417. $lib->acl = $project->acl != 'program' ? $project->acl : 'custom';
  418. $this->dao->insert(TABLE_DOCLIB)->data($lib)->exec();
  419. }
  420. /**
  421. * 获取没有合并的冲刺。
  422. * Get sprints has not been merged.
  423. *
  424. * @access protected
  425. * @return array
  426. */
  427. protected function getNoMergedSprints()
  428. {
  429. return $this->dao->select('*')->from(TABLE_PROJECT)
  430. ->where('project')->eq(0)
  431. ->andWhere('vision')->eq('rnd')
  432. ->andWhere('type')->eq('sprint')
  433. ->andWhere('deleted')->eq(0)
  434. ->fetchAll('id', false);
  435. }
  436. /**
  437. * 更新文件对象。
  438. * Update file objects.
  439. *
  440. * @param string $type
  441. * @param int $lastID
  442. * @param int $limit
  443. * @access protected
  444. * @return array
  445. */
  446. protected function updateFileObjects($type, $lastID, $limit)
  447. {
  448. /* Get type editor, fields, and query conditions. */
  449. $cond = array();
  450. $editorFields = array();
  451. $editor = $this->config->upgrade->editors[$type];
  452. $fields = explode(',', $editor['fields']);
  453. foreach($fields as $field)
  454. {
  455. if(strpos($field, '`') !== false)
  456. {
  457. $cond[] = $field . " like '%data/upload/%'";
  458. $editorFields[] = $field;
  459. }
  460. else
  461. {
  462. $idField = $field;
  463. }
  464. }
  465. /* Get objects. */
  466. $objects = $this->dao->select($editor['fields'])->from($editor['table'])
  467. ->where($idField)->gt($lastID)
  468. ->beginIF($cond)->andWhere('(' . join(' OR ', $cond) . ')')->fi()
  469. ->orderBy($idField)
  470. ->limit($limit)
  471. ->fetchAll($idField);
  472. foreach($objects as $object)
  473. {
  474. $files = array();
  475. /* Get files in the editor fields. */
  476. foreach($editorFields as $field)
  477. {
  478. $field = trim($field, '`');
  479. preg_match_all('/"\/?data\/upload\/.*1\/([0-9]{6}\/[^"]+)"/', $object->{$field}, $output);
  480. foreach($output[1] as $path) $files[$path] = $path;
  481. }
  482. /* If the file exists, update the file. */
  483. if($files) $this->dao->update(TABLE_FILE)->set('objectType')->eq($type)->set('objectID')->eq($object->{$idField})->set('extra')->eq('editor')->where('pathname')->in($files)->exec();
  484. }
  485. return array(count($objects), isset($object) ? $object->{$idField} : 0);
  486. }
  487. /**
  488. * 通过已有的产品获取未关联项目的相关数据,并且关联到项目。
  489. * Get data from product and relate to project.
  490. *
  491. * @param int $projectID
  492. * @param array $productIdList
  493. * @access protected
  494. * @return void
  495. */
  496. protected function updateProjectByProduct($projectID, $productIdList)
  497. {
  498. if(empty($productIdList)) return;
  499. $this->dao->update(TABLE_BUG)->set('project')->eq($projectID)->where('product')->in($productIdList)->andWhere('project')->eq(0)->exec();
  500. $this->dao->update(TABLE_TESTREPORT)->set('project')->eq($projectID)->where('product')->in($productIdList)->andWhere('project')->eq(0)->exec();
  501. $this->dao->update(TABLE_TESTSUITE)->set('project')->eq($projectID)->where('product')->in($productIdList)->andWhere('project')->eq(0)->exec();
  502. $this->dao->update(TABLE_RELEASE)->set('project')->eq($projectID)->where('product')->in($productIdList)->exec();
  503. }
  504. /**
  505. * 通过已有的执行获取为关联项目的相关数据,并且关联到项目。
  506. * Get data from product and relate to project.
  507. *
  508. * @param int $projectID
  509. * @param array $productIdList
  510. * @access protected
  511. * @return void
  512. * @param mixed[] $sprintIdList
  513. */
  514. protected function updateProjectByExecution($projectID, $sprintIdList)
  515. {
  516. $this->dao->update(TABLE_TASK)->set('project')->eq($projectID)->where('execution')->in($sprintIdList)->andWhere('project')->eq(0)->exec();
  517. $this->dao->update(TABLE_BUILD)->set('project')->eq($projectID)->where('execution')->in($sprintIdList)->andWhere('project')->eq(0)->exec();
  518. $this->dao->update(TABLE_BUG)->set('project')->eq($projectID)->where('execution')->in($sprintIdList)->exec();
  519. $this->dao->update(TABLE_DOC)->set('project')->eq($projectID)->where("lib IN(SELECT id from " . TABLE_DOCLIB . " WHERE type = 'execution' and execution " . helper::dbIN($sprintIdList) . ')')->andWhere('project')->eq(0)->exec();
  520. $this->dao->update(TABLE_DOCLIB)->set('project')->eq($projectID)->where('type')->eq('execution')->andWhere('execution')->in($sprintIdList)->andWhere('project')->eq(0)->exec();
  521. $this->dao->update(TABLE_TESTTASK)->set('project')->eq($projectID)->where('execution')->in($sprintIdList)->andWhere('project')->eq(0)->exec();
  522. $this->dao->update(TABLE_EFFORT)->set('project')->eq($projectID)->where('execution')->in($sprintIdList)->andWhere('objectType')->eq('task')->exec();
  523. }
  524. /**
  525. * 将执行下的所有需求相关转移到项目下。
  526. * Move all stories under execution to project.
  527. *
  528. * @param int $projectID
  529. * @param array $sprintIdList
  530. * @access protected
  531. * @return void
  532. */
  533. protected function moveSprintStoryToProject($projectID, $sprintIdList)
  534. {
  535. $sprintStories = $this->dao->select('*')->from(TABLE_PROJECTSTORY)
  536. ->where('project')->in($sprintIdList)
  537. ->fetchAll();
  538. foreach($sprintStories as $sprintStory)
  539. {
  540. $projectStory = $sprintStory;
  541. $projectStory->project = $projectID;
  542. $this->dao->replace(TABLE_PROJECTSTORY)->data($projectStory)->exec();
  543. }
  544. }
  545. /**
  546. * 将执行下的所有用例关联关系转移到项目下。
  547. * Move all cases under execution to project.
  548. *
  549. * @param int $projectID
  550. * @param array $sprintIdList
  551. * @access protected
  552. * @return void
  553. */
  554. protected function moveSprintCaseToProject($projectID, $sprintIdList)
  555. {
  556. $projectCases = $this->dao->select('`case`,product,project,count,version')->from(TABLE_PROJECTCASE)
  557. ->where('project')->in($sprintIdList)
  558. ->fetchAll();
  559. foreach($projectCases as $projectCase)
  560. {
  561. $projectCase->project = $projectID;
  562. $projectCase->order = $projectCase->case * 5;
  563. $this->dao->replace(TABLE_PROJECTCASE)->data($projectCase)->exec();
  564. }
  565. }
  566. /**
  567. * 将执行下虽有的测试用例相关都转移到项目下。
  568. *
  569. * @param int $projectID
  570. * @param array $sprintIdList
  571. * @access protected
  572. * @return void
  573. */
  574. protected function moveAllTestTaskToProject($projectID, $sprintIdList)
  575. {
  576. $sprintCases = $this->dao->select('t2.case,t2.version,t1.product,t1.execution as project')
  577. ->from(TABLE_TESTTASK)->alias('t1')
  578. ->leftJoin(TABLE_TESTRUN)->alias('t2')->on('t1.id = t2.task')
  579. ->where('t1.execution')->in($sprintIdList)
  580. ->fetchAll();
  581. foreach($sprintCases as $sprintCase)
  582. {
  583. $sprintCase->case = (int)$sprintCase->case;
  584. $sprintCase->version = (int)$sprintCase->version;
  585. $this->dao->replace(TABLE_PROJECTCASE)->data($sprintCase)->exec();
  586. $sprintCase->order = $sprintCase->case * 5;
  587. $sprintCase->project = $projectID;
  588. $this->dao->replace(TABLE_PROJECTCASE)->data($sprintCase)->exec();
  589. }
  590. }
  591. /**
  592. * 将项目的信息同步给所有的执行。
  593. * Sync project info to all executions.
  594. *
  595. * @param int $projectID
  596. * @param array $sprintIdList
  597. * @access protected
  598. * @return void
  599. * @param int $programID
  600. * @param bool $isProjectType
  601. */
  602. protected function syncProjectInfoForSprints($projectID, $sprintIdList, $programID, $isProjectType = false)
  603. {
  604. $project = $this->dao->findById($projectID)->from(TABLE_PROJECT)->fetch();
  605. $sprints = $this->dao->select('id, type, acl, begin, end')->from(TABLE_PROJECT)->where('id')->in($sprintIdList)->fetchAll();
  606. $minBeginDate = $project->begin;
  607. $maxEndDate = $project->end;
  608. foreach($sprints as $sprint)
  609. {
  610. $data = new stdclass();
  611. $data->project = $projectID;
  612. $data->parent = $projectID;
  613. $data->grade = 1;
  614. $data->path = ",{$projectID},{$sprint->id},";
  615. $data->type = 'sprint';
  616. $data->acl = $sprint->acl == 'custom' ? 'private' : $sprint->acl;
  617. $this->dao->update(TABLE_PROJECT)->data($data)->where('id')->eq($sprint->id)->exec();
  618. $minBeginDate = ($sprint->begin < $minBeginDate) ? $sprint->begin : $minBeginDate;
  619. $maxEndDate = $sprint->end > $maxEndDate ? $sprint->end : $maxEndDate;
  620. }
  621. if($isProjectType)
  622. {
  623. list($minRealBegan, $maxRealEnd) = $this->getMaxAndMinDateByAction($projectID, $sprintIdList);
  624. $this->updateProjectTime($project, $minRealBegan, $maxRealEnd, $minBeginDate, $maxEndDate, $projectID, $programID);
  625. }
  626. }
  627. /**
  628. * 根据历史记录获取项目的最小开始时间和最大结束时间。
  629. * Get min begin date and max end date by action.
  630. *
  631. * @param int $projectID
  632. * @param array $sprintIdList
  633. * @access protected
  634. * @return array
  635. */
  636. protected function getMaxAndMinDateByAction($projectID, $sprintIdList)
  637. {
  638. $linkedSprintIdList = $this->dao->select('id')->from(TABLE_PROJECT)->where('project')->eq($projectID)->fetchPairs();
  639. $linkedSprintIdList += $sprintIdList;
  640. $minRealBegan = $this->dao->select('date')->from(TABLE_ACTION)->where('objectID')->in($linkedSprintIdList)->andWhere('objectType')->eq('project')->andWhere('action')->eq('started')->orderBy('date_asc')->fetch('date');
  641. $maxRealEnd = $this->dao->select('date')->from(TABLE_ACTION)->where('objectID')->in($linkedSprintIdList)->andWhere('objectType')->eq('project')->andWhere('action')->eq('closed')->orderBy('date_desc')->fetch('date');
  642. return array($minRealBegan, $maxRealEnd);
  643. }
  644. /**
  645. * 更新项目以及项目所属项目集的时间相关数据。
  646. * Update project and program time.
  647. *
  648. * @param object $project
  649. * @param string $minRealBegan
  650. * @param string $maxRealEnd
  651. * @param string $minBeginDate
  652. * @param string $maxEndDate
  653. * @param int $projectID
  654. * @param int $programID
  655. * @access protected
  656. * @return void
  657. */
  658. protected function updateProjectTime($project, $minRealBegan, $maxRealEnd, $minBeginDate, $maxEndDate, $projectID, $programID)
  659. {
  660. $data = new stdClass();
  661. $data->realBegan = $minRealBegan ? substr($minRealBegan, 0, 10) : '0000-00-00';
  662. $projectStatus = $this->dao->select('status')->from(TABLE_PROJECT)->where('id')->eq($projectID)->fetch('status');
  663. if($projectStatus == 'closed')
  664. {
  665. $data->realEnd = substr($maxRealEnd, 0, 10);
  666. $data->closedDate = $maxRealEnd;
  667. }
  668. if($minBeginDate != $project->begin || $maxEndDate != $project->end)
  669. {
  670. $data->begin = $minBeginDate;
  671. $data->end = $maxEndDate;
  672. $data->days = $this->computeDaysDelta($data->begin, $data->end);
  673. }
  674. if(helper::isZeroDate($data->realBegan)) unset($data->realBegan);
  675. if(isset($data->realEnd) and helper::isZeroDate($data->realEnd)) unset($data->realEnd);
  676. if(isset($data->closedDate) and helper::isZeroDate($data->closedDate)) unset($data->closedDate);
  677. if(isset($data->begin) and helper::isZeroDate($data->begin)) unset($data->begin);
  678. if(isset($data->end) and helper::isZeroDate($data->end)) unset($data->end);
  679. if(empty(get_object_vars($data))) return;
  680. $this->dao->update(TABLE_PROJECT)->data($data)->where('id')->eq($projectID)->exec();
  681. $this->dao->update(TABLE_PROGRAM)->data($data)->where('id')->eq($programID)->exec();
  682. }
  683. /**
  684. * 设置项目和产品的关联关系。
  685. * Set relation between project and product.
  686. *
  687. * @param int $projectID
  688. * @param array $productIdList
  689. * @param array $sprintIdList
  690. * @access protected
  691. * @return void
  692. */
  693. protected function setProjectProductsRelation($projectID, $productIdList, $sprintIdList)
  694. {
  695. $sprintProducts = $this->dao->select('product,branch,plan')->from(TABLE_PROJECTPRODUCT)->where('project')->in($sprintIdList)->andWhere('product')->in($productIdList)->fetchGroup('product', 'branch');
  696. $projectProducts = $this->dao->select('product,branch,plan')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->andWhere('product')->in($productIdList)->fetchGroup('product', 'branch');
  697. foreach($productIdList as $productID)
  698. {
  699. $data = new stdclass();
  700. $data->project = $projectID;
  701. $data->product = $productID;
  702. if(isset($sprintProducts[$productID]))
  703. {
  704. foreach($sprintProducts[$productID] as $branchID => $sprintProduct)
  705. {
  706. $data->plan = $sprintProduct->plan;
  707. $data->branch = $branchID;
  708. if(isset($projectProducts[$productID][$branchID]) and !empty($projectProducts[$productID][$branchID]->plan))
  709. {
  710. $data->plan .= ',' . $projectProducts[$productID][$branchID]->plan;
  711. $data->plan = implode(',', array_unique(array_filter(explode(',', $data->plan))));
  712. }
  713. $this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec();
  714. }
  715. }
  716. else
  717. {
  718. $data->plan = 0;
  719. $data->branch = 0;
  720. $this->dao->replace(TABLE_PROJECTPRODUCT)->data($data)->exec();
  721. }
  722. }
  723. }
  724. /**
  725. * 为作为项目升级构建项目数据。
  726. * Build project data when auto upgrading.
  727. *
  728. * @param object $sprint
  729. * @param int $programID
  730. * @param string $fromMode
  731. * @param string $account
  732. * @param string $now
  733. * @access protected
  734. * @return object
  735. */
  736. protected function buildProjectInAutoUpgrade($sprint, $programID, $fromMode, $account, $now)
  737. {
  738. $project = new stdclass();
  739. $project->name = $sprint->name;
  740. $project->desc = $sprint->desc;
  741. $project->type = 'project';
  742. $project->model = 'scrum';
  743. $project->parent = $programID;
  744. $project->status = $sprint->status;
  745. $project->begin = $sprint->begin;
  746. $project->end = isset($sprint->end) ? $sprint->end : LONG_TIME;
  747. $project->realBegan = zget($sprint, 'realBegan', '');
  748. $project->realEnd = zget($sprint, 'realEnd', '');
  749. $project->PM = $sprint->PM;
  750. $project->auth = 'extend';
  751. $project->openedBy = $account;
  752. $project->openedDate = $now;
  753. $project->openedVersion = $this->config->version;
  754. $project->lastEditedBy = $account;
  755. $project->lastEditedDate = $now;
  756. $project->grade = 2;
  757. $project->acl = $sprint->acl == 'open' ? 'open' : 'private';
  758. $project->storyType = 'story';
  759. if($fromMode == 'classic')
  760. {
  761. $project->multiple = '0';
  762. $project->code = $sprint->code;
  763. $project->team = $sprint->team;
  764. $project->openedBy = $sprint->openedBy;
  765. $project->openedDate = $sprint->openedDate;
  766. }
  767. if(helper::isZeroDate($project->realBegan)) unset($project->realBegan);
  768. if(helper::isZeroDate($project->realEnd)) unset($project->realEnd);
  769. return $project;
  770. }
  771. /**
  772. * 检查创建项目时的必填字段。
  773. * Check fields required when create program.
  774. *
  775. * @param object $data
  776. * @param string $projectType
  777. * @access protected
  778. * @return bool
  779. */
  780. protected function checkProgramRequired($data, $projectType)
  781. {
  782. if(empty($data->programName))
  783. {
  784. dao::$errors['programName'][] = sprintf($this->lang->error->notempty, $this->lang->upgrade->programName);
  785. return false;
  786. }
  787. if(empty($data->longTime) && empty($data->end) && isset($data->begin))
  788. {
  789. dao::$errors['end'][] = sprintf($this->lang->error->notempty, $this->lang->upgrade->end);
  790. return false;
  791. }
  792. if(isset($data->projectName) && $projectType == 'execution' && empty($data->projectName))
  793. {
  794. dao::$errors['projectName'][] = sprintf($this->lang->error->notempty, $this->lang->upgrade->projectName);
  795. return false;
  796. }
  797. return true;
  798. }
  799. /**
  800. * 获取重复的项目名称。
  801. * Get duplicate project name.
  802. *
  803. * @param array $projectIdList
  804. * @access protected
  805. * @return string
  806. */
  807. protected function getDuplicateProjectName($projectIdList)
  808. {
  809. $projectPairs = $this->dao->select('id, name')->from(TABLE_EXECUTION)->where('deleted')->eq('0')->andWhere('id')->in($projectIdList)->fetchPairs();
  810. $projectNames = array();
  811. $duplicateList = '';
  812. foreach($projectPairs as $projectID => $projectName)
  813. {
  814. if(isset($projectNames[$projectName]))
  815. {
  816. $duplicateList .= "$projectID,";
  817. $duplicateList .= "{$projectNames[$projectName]},";
  818. continue;
  819. }
  820. $projectNames[$projectName] = $projectID;
  821. }
  822. return $duplicateList;
  823. }
  824. /**
  825. * 获取工作流组列表
  826. * Get workflow group list for process.
  827. *
  828. * @return array
  829. */
  830. protected function getWorkflowGroupForProcess()
  831. {
  832. return $this->dao->select('id,projectModel,projectType,main')->from(TABLE_WORKFLOWGROUP)
  833. ->where('projectModel')->in('scrum,waterfall,agileplus,waterfallplus')
  834. ->andWhere('deleted')->eq('0')
  835. ->fetchAll('id');
  836. }
  837. /**
  838. * 处理使用内置数据的流程。
  839. * Handle buildin workflow group.
  840. *
  841. * @param object $group 流程对象
  842. * @param int $groupID 流程ID
  843. * @param array $classifyModule 分类模块数组
  844. * @return array
  845. */
  846. protected function handleBuildinWorkflowGroup($group, $groupID, $classifyModule)
  847. {
  848. $this->dao->update(TABLE_PROCESS)->set('workflowGroup')->eq($groupID)->where('model')->eq($group->projectModel)->exec();
  849. /* 更新活动的流程。 */
  850. $processList = $this->dao->select('id')->from(TABLE_PROCESS)->where('workflowGroup')->eq($groupID)->andWhere('deleted')->eq('0')->fetchPairs();
  851. $this->dao->update(TABLE_ACTIVITY)->set('workflowGroup')->eq($groupID)->where('process')->in(array_keys($processList))->exec();
  852. $this->migrateOutputToDeliverable($group);
  853. $classifyModule[$groupID] = $this->migrateClassifyToModule($group, $groupID);
  854. return $classifyModule;
  855. }
  856. /**
  857. * 将过程分类从语言表迁移到模块表
  858. * Migrate process classify from language table to module table.
  859. *
  860. * @param object $group 流程对象
  861. * @param int $groupID 流程ID
  862. * @return array
  863. */
  864. protected function migrateClassifyToModule($group, $groupID)
  865. {
  866. $this->app->loadLang('process');
  867. $classifyKey = $group->projectModel == 'waterfall' ? 'classify' : $group->projectModel . 'Classify';
  868. $classifyList = $this->dao->select('`key`,`value`')->from(TABLE_LANG)->where('module')->eq('process')->andWhere('section')->eq($classifyKey)->fetchPairs();
  869. if(empty($classifyList)) $classifyList = $this->lang->process->classifyList;
  870. $orderList = array('project' => 10, 'engineering' => 20, 'support' => 30);
  871. $classifyModule = array();
  872. $customOrder = 40;
  873. foreach($classifyList as $key => $value)
  874. {
  875. $order = isset($orderList[$key]) ? $orderList[$key] : $customOrder;
  876. $moduleID = $this->createProcessModule($groupID, $key, $value, $order);
  877. $classifyModule[$key] = $moduleID;
  878. if(!isset($orderList[$key])) $customOrder += 10;
  879. }
  880. return $classifyModule;
  881. }
  882. /**
  883. * 创建过程模块。
  884. * Create process module.
  885. *
  886. * @param int $groupID 流程ID
  887. * @param string $key 模块标识
  888. * @param string $name 模块名称
  889. * @param int $order 排序
  890. * @return int
  891. */
  892. protected function createProcessModule($groupID, $key, $name, $order)
  893. {
  894. $module = new stdclass();
  895. $module->root = $groupID;
  896. $module->branch = '0';
  897. $module->name = $name;
  898. $module->grade = '1';
  899. $module->order = $order;
  900. $module->type = 'process';
  901. $module->extra = $key;
  902. $this->dao->insert(TABLE_MODULE)->data($module)->exec();
  903. $moduleID = $this->dao->lastInsertID();
  904. $this->dao->update(TABLE_MODULE)->set('path')->eq(",$moduleID,")->where('id')->eq($moduleID)->exec();
  905. return $moduleID;
  906. }
  907. /**
  908. * 处理需要复制过程和活动的流程。
  909. * Handle need copy workflow group.
  910. *
  911. * @param object $group 流程对象
  912. * @param int $groupID 流程ID
  913. * @param array $classifyModule 分类模块数组
  914. */
  915. protected function handleNeedCopyWorkflowGroup($group, $groupID, $classifyModule)
  916. {
  917. $projectIdList = $this->dao->select('id')->from(TABLE_PROJECT)->where('workflowGroup')->eq($group->id)->fetchPairs();
  918. $processList = $this->dao->select('*')->from(TABLE_PROCESS)->where('model')->eq($group->projectModel)->andWhere('deleted')->eq('0')->fetchAll('id');
  919. $activityGroup = $this->dao->select('*')->from(TABLE_ACTIVITY)->where('process')->in(array_keys($processList))->andWhere('deleted')->eq('0')->fetchGroup('process', 'id');
  920. $outputGroup = $this->dao->select('t1.*')->from(TABLE_ZOUTPUT)
  921. ->alias('t1')->leftJoin(TABLE_ACTIVITY)
  922. ->alias('t2')->on('t1.activity = t2.id')
  923. ->where('t2.process')->in(array_keys($processList))
  924. ->andWhere('t1.deleted')->eq('0')
  925. ->fetchGroup('activity', 'id');
  926. foreach($processList as $process)
  927. {
  928. $this->copyProcessWithActivities($process, $groupID, $activityGroup, $outputGroup, $projectIdList);
  929. }
  930. $classifyModule[$groupID] = $this->migrateClassifyToModule($group, $groupID);
  931. return $classifyModule;
  932. }
  933. /**
  934. * 复制过程及其活动。
  935. * Copy process and activities.
  936. *
  937. * @param object $process 过程对象
  938. * @param int $groupID 流程ID
  939. * @param array $activityGroup 活动分组
  940. * @param array $outputGroup 文档分组
  941. * @param array $projectIdList 项目ID列表
  942. */
  943. protected function copyProcessWithActivities($process, $groupID, $activityGroup, $outputGroup, $projectIdList)
  944. {
  945. $activityList = zget($activityGroup, $process->id, array());
  946. unset($process->id);
  947. $process->workflowGroup = $groupID;
  948. $process->model = '';
  949. $process->editedBy = '';
  950. $process->editedDate = null;
  951. $process->assignedDate = null;
  952. $this->dao->insert(TABLE_PROCESS)->data($process)->exec();
  953. $processID = $this->dao->lastInsertID();
  954. foreach($activityList as $activity)
  955. {
  956. $this->copyActivity($activity, $processID, $groupID, $outputGroup, $projectIdList);
  957. }
  958. }
  959. /**
  960. * 复制活动。
  961. * Copy activity.
  962. *
  963. * @param object $activity 活动对象
  964. * @param int $processID 过程ID
  965. * @param int $groupID 流程ID
  966. * @param array $outputGroup 文档分组
  967. * @param array $projectIdList 项目ID列表
  968. */
  969. protected function copyActivity($activity, $processID, $groupID, $outputGroup, $projectIdList)
  970. {
  971. $outputList = zget($outputGroup, $activity->id, array());
  972. $oldActivityID = $activity->id;
  973. unset($activity->id);
  974. $activity->process = $processID;
  975. $activity->optional = !empty($activity->optional) ? $activity->optional : 'no';
  976. $activity->workflowGroup = $groupID;
  977. $activity->editedBy = '';
  978. $activity->editedDate = null;
  979. $activity->assignedDate = null;
  980. $this->dao->insert(TABLE_ACTIVITY)->data($activity)->exec();
  981. $newActivityID = $this->dao->lastInsertID();
  982. $this->dao->update(TABLE_AUDITPLAN)->set('objectID')->eq($newActivityID)->where('objectID')->eq($oldActivityID)->andWhere('objectType')->eq('activity')->andWhere('project')->in($projectIdList)->exec();
  983. $this->dao->update(TABLE_AUDITCL)->set('objectID')->eq($newActivityID)->where('objectID')->eq($oldActivityID)->andWhere('workflowGroup')->eq($groupID)->andWhere('objectType')->eq('activity')->exec();
  984. $this->dao->update(TABLE_PROGRAMACTIVITY)->set('activity')->eq($newActivityID)->where('activity')->eq($oldActivityID)->andWhere('project')->in($projectIdList)->exec();
  985. $idMap = array();
  986. foreach($outputList as $output)
  987. {
  988. $oldID = $output->id;
  989. unset($output->id);
  990. $output->activity = $newActivityID;
  991. $output->editedBy = '';
  992. $output->editedDate = null;
  993. $this->dao->insert(TABLE_ZOUTPUT)->data($output)->exec();
  994. $newOutputID = $this->dao->lastInsertID();
  995. $idMap[$oldID] = $newOutputID;
  996. }
  997. foreach($idMap as $oldID => $newOutputID)
  998. {
  999. $this->dao->update(TABLE_AUDITCL)->set('objectID')->eq($newOutputID)->where('objectID')->eq($oldID)->andWhere('workflowGroup')->eq($groupID)->andWhere('objectType')->eq('zoutput')->exec();
  1000. $this->dao->update(TABLE_AUDITPLAN)->set('objectID')->eq($newOutputID)->where('objectID')->eq($oldID)->andWhere('objectType')->eq('zoutput')->andWhere('project')->in($projectIdList)->exec();
  1001. }
  1002. }
  1003. /**
  1004. * 将过程文档迁移到交付物。
  1005. * Migrate process output to deliverable.
  1006. *
  1007. * @param object $group
  1008. * @return void
  1009. */
  1010. public function migrateOutputToDeliverable($group)
  1011. {
  1012. $outputList = $this->dao->select('t1.*')->from(TABLE_ZOUTPUT)
  1013. ->alias('t1')->leftJoin(TABLE_ACTIVITY)
  1014. ->alias('t2')->on('t1.activity = t2.id')
  1015. ->where('t2.workflowGroup')->eq($group->id)
  1016. ->andWhere('t1.deleted')->eq('0')
  1017. ->fetchAll('id');
  1018. if(empty($outputList)) return;
  1019. $otherModule = $this->dao->select('id')->from(TABLE_MODULE)->where('root')->eq($group->id)->andWhere('type')->eq('deliverable')->andWhere('deleted')->eq('0')->andWhere('extra')->eq('other')->fetch('id');
  1020. $deliverable = new stdclass();
  1021. $deliverable->template = '[]';
  1022. $deliverable->status = 'hidden';
  1023. $deliverable->workflowGroup = $group->id;
  1024. $deliverable->module = $otherModule;
  1025. $deliverable->builtin = 1;
  1026. $deliverable->deleted = '1';
  1027. $idMap = array();
  1028. foreach($outputList as $output)
  1029. {
  1030. $deliverable->name = $output->name;
  1031. $deliverable->activity = $output->activity;
  1032. $deliverable->trimmable = $output->optional == 'yes' ? '1' : '0';
  1033. $this->dao->insert(TABLE_DELIVERABLE)->data($deliverable)->exec();
  1034. $deliverableID = $this->dao->lastInsertID();
  1035. $idMap[$output->id] = $deliverableID;
  1036. }
  1037. $projectIdList = $this->dao->select('id')->from(TABLE_PROJECT)->where('workflowGroup')->eq($group->id)->fetchPairs();
  1038. $projectOutputList = $this->dao->select('id,output')->from(TABLE_PROGRAMOUTPUT)->where('project')->in($projectIdList)->andWhere('output')->in(array_keys($idMap))->fetchPairs();
  1039. foreach($projectOutputList as $id => $outputID)
  1040. {
  1041. $newID = $idMap[$outputID];
  1042. $this->dao->update(TABLE_PROGRAMOUTPUT)->set('output')->eq($newID)->where('id')->eq($id)->exec();
  1043. }
  1044. $auditplans = $this->dao->select('id,objectID')->from(TABLE_AUDITPLAN)
  1045. ->where('objectType')->eq('zoutput')
  1046. ->andWhere('objectID')->in(array_keys($idMap))
  1047. ->andWhere('project')->in($projectIdList)
  1048. ->fetchPairs();
  1049. foreach($auditplans as $id => $objectID)
  1050. {
  1051. $newID = $idMap[$objectID];
  1052. $this->dao->update(TABLE_AUDITPLAN)->set('objectID')->eq($newID)->where('id')->eq($id)->exec();
  1053. }
  1054. $auditcls = $this->dao->select('id, objectID')->from(TABLE_AUDITCL)
  1055. ->where('objectType')->eq('zoutput')
  1056. ->andWhere('workflowGroup')->eq($group->id)
  1057. ->andWhere('objectID')->in(array_keys($outputList))
  1058. ->andWhere('deleted')->eq('0')
  1059. ->fetchPairs();
  1060. foreach($auditcls as $id => $objectID)
  1061. {
  1062. $newID = $idMap[$objectID];
  1063. $this->dao->update(TABLE_AUDITCL)->set('objectID')->eq($newID)->where('id')->eq($id)->exec();
  1064. }
  1065. }
  1066. /**
  1067. * 更新过程的模块分类。
  1068. * Update process modules.
  1069. *
  1070. * @param array $classifyModule
  1071. */
  1072. protected function updateProcessModules($classifyModule)
  1073. {
  1074. foreach($classifyModule as $groupID => $modules)
  1075. {
  1076. foreach($modules as $key => $moduleID)
  1077. {
  1078. $this->dao->update(TABLE_PROCESS)->set('module')->eq($moduleID)->where('workflowGroup')->eq($groupID)->andWhere('type')->eq($key)->exec();
  1079. }
  1080. }
  1081. }
  1082. }