model.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. <?php
  2. /**
  3. * The control file of transfer module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Tang Hucheng <tanghucheng@cnezsoft.com>
  8. * @package transfer
  9. * @link https://www.zentao.net
  10. */
  11. class transferModel extends model
  12. {
  13. /* 最大导入数量。*/
  14. /* The max import number. */
  15. public $maxImport;
  16. /* Transfer模块的配置。*/
  17. /* Transfer module config. */
  18. public $transferConfig;
  19. /* 导入字段。*/
  20. /* Import fields. */
  21. public $templateFields;
  22. /* 导出字段。*/
  23. /* Export fields. */
  24. public $exportFields;
  25. /* 模块配置。*/
  26. /* Module config. */
  27. public $moduleConfig;
  28. /* 模块语言项。*/
  29. /* Module language. */
  30. public $moduleLang;
  31. /* 模块字段列表。*/
  32. /* Module field list. */
  33. public $moduleFieldList;
  34. /* 模块下拉字段列表。*/
  35. /* Module list fields. */
  36. public $moduleListFields;
  37. /**
  38. * The construc method, to do some auto things.
  39. *
  40. * @access public
  41. * @return void
  42. */
  43. public function __construct()
  44. {
  45. parent::__construct();
  46. $this->maxImport = isset($_COOKIE['maxImport']) ? (int)$_COOKIE['maxImport'] : 0;
  47. $this->transferConfig = $this->config->transfer;
  48. }
  49. /**
  50. * Transfer 公共方法(格式化模块语言项,Config,字段)。
  51. * Common actions.
  52. *
  53. * @param string $module
  54. * @access public
  55. * @return void
  56. */
  57. public function commonActions($module = '')
  58. {
  59. if($module)
  60. {
  61. if(in_array($module, array('epic', 'requirement'))) $module = 'story';
  62. if($module == 'caselib') $module = 'testcase';
  63. $this->loadModel($module);
  64. $this->moduleConfig = $this->config->$module;
  65. $this->moduleLang = $this->lang->$module;
  66. $this->moduleFieldList = $this->config->$module->dtable->fieldList ?? array();
  67. $this->moduleListFields = explode(',', $this->config->$module->listFields ?? '');
  68. }
  69. }
  70. /**
  71. * 生成导出数据
  72. * Export module data.
  73. *
  74. * @param string $module
  75. * @access public
  76. * @return void
  77. */
  78. public function export($module = '')
  79. {
  80. /* 设置PHP最大运行内存和最大执行时间。 */
  81. /* Set PHP max running memory and max execution time. */
  82. ini_set('memory_limit', '-1');
  83. ini_set('max_execution_time','100');
  84. $fields = $this->post->exportFields;
  85. /* Init config fieldList. */
  86. $fieldList = $this->initFieldList($module, $fields);
  87. /* 生成该模块的导出数据。 */
  88. /* Generate export datas. */
  89. $rows = $this->getRows($module, $fieldList);
  90. if($module == 'story')
  91. {
  92. $parentList = array_unique(array_filter(array_column($rows, 'parent')));
  93. $parentPairs = $this->dao->select('id,title')->from(TABLE_STORY)->where('id')->in($parentList)->fetchPairs();
  94. $product = $this->loadModel('product')->fetchByID((int)$this->session->storyTransferParams['productID']);
  95. $storyIdList = array_unique(array_column($rows, 'id'));
  96. $storyTasks = $this->loadModel('task')->getStoryTaskCounts($storyIdList);
  97. $storyBugs = $this->loadModel('bug')->getStoryBugCounts($storyIdList);
  98. $storyCases = $this->loadModel('testcase')->getStoryCaseCounts($storyIdList);
  99. foreach($rows as $row)
  100. {
  101. $row->taskCountAB = zget($storyTasks, $row->id, 0);
  102. $row->bugCountAB = zget($storyBugs, $row->id, 0);
  103. $row->caseCountAB = zget($storyCases, $row->id, 0);
  104. $row->parent = $row->parent ? '#' . $row->parent . ' ' . $parentPairs[$row->parent] : '';
  105. if(!empty($product->shadow)) $row->product = '';
  106. }
  107. }
  108. /* 设置Excel下拉数据。 */
  109. /* Set Excel dropdown data. */
  110. $list = $this->setListValue($module, $fieldList);
  111. if($list) foreach($list as $listName => $listValue) $this->post->set($listName, $listValue);
  112. /* Get export rows and fields datas. */
  113. $exportDatas = $this->generateExportDatas($fieldList, $rows);
  114. $fields = $exportDatas['fields'];
  115. $rows = !empty($exportDatas['rows']) ? $exportDatas['rows'] : array();
  116. if($this->config->edition != 'open') list($fields, $rows) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $rows, $module);
  117. $this->post->set('rows', $rows);
  118. $this->post->set('fields', $fields);
  119. $this->post->set('kind', $module);
  120. }
  121. /**
  122. * 将字段格式化成数组。
  123. * Init FieldList.
  124. *
  125. * @param string $module
  126. * @param string $fields
  127. * @param bool $withKey
  128. * @access public
  129. * @return array
  130. */
  131. public function initFieldList($module, $fields, $withKey = true)
  132. {
  133. if(!is_array($fields)) $fields = explode(',', $fields);
  134. $this->commonActions($module);
  135. $this->mergeConfig($module);
  136. $this->transferConfig->sysDataList = $this->initSysDataFields();
  137. $transferFieldList = $this->transferConfig->fieldList; //生成一个完整的fieldList结构。
  138. if(is_string($this->moduleConfig->dateFields)) $this->moduleConfig->dateFields = explode(',', $this->moduleConfig->dateFields);
  139. if(is_string($this->moduleConfig->datetimeFields)) $this->moduleConfig->datetimeFields = explode(',', $this->moduleConfig->datetimeFields);
  140. if(is_string($this->moduleConfig->textareaFields)) $this->moduleConfig->textareaFields = explode(',', $this->moduleConfig->textareaFields);
  141. $fieldList = array();
  142. /* build module fieldList. */
  143. foreach($fields as $field)
  144. {
  145. $field = trim($field);
  146. $moduleFieldList = isset($this->moduleFieldList[$field]) ? $this->moduleFieldList[$field] : array();
  147. /* 根据fieldList结构生成每个字段的结构。*/
  148. /* Generate each field structure by fieldList structure. */
  149. foreach($transferFieldList as $transferField => $value)
  150. {
  151. if((!isset($moduleFieldList[$transferField])) or $transferField == 'title')
  152. {
  153. $moduleFieldList[$transferField] = $this->transferConfig->fieldList[$transferField];
  154. if(strpos($this->transferConfig->initFunction, $transferField) !== false) //调用初始化方法
  155. {
  156. $funcName = 'init' . ucfirst($transferField);
  157. $moduleFieldList[$transferField] = $this->$funcName($module, $field);
  158. if($transferField == 'title') $moduleFieldList['label'] = $moduleFieldList[$transferField];
  159. }
  160. }
  161. }
  162. if(in_array($field, $this->moduleConfig->dateFields)) $moduleFieldList['control'] = 'datePicker';
  163. if(in_array($field, $this->moduleConfig->datetimeFields)) $moduleFieldList['control'] = 'datetimePicker';
  164. if(in_array($field, $this->moduleConfig->textareaFields)) $moduleFieldList['control'] = 'textarea';
  165. $moduleFieldList['multiple'] = $moduleFieldList['control'] == 'multiple';
  166. if($moduleFieldList['control'] == 'select' || $moduleFieldList['control'] == 'multiple') $moduleFieldList['control'] = 'picker';
  167. $moduleFieldList['width'] = isset($this->moduleConfig->dtable->fieldList[$field]['width']) ? $this->moduleConfig->dtable->fieldList[$field]['width'] : '136px';
  168. if(is_numeric($moduleFieldList['width'])) $moduleFieldList['width'] .= 'px';
  169. $moduleFieldList['name'] = $field;
  170. $moduleFieldList['items'] = $this->initItems($module, $field, $moduleFieldList, $withKey);
  171. $fieldList[$field] = $moduleFieldList;
  172. }
  173. /* 抄送给默认多选。*/
  174. /* Copy to default multiple. */
  175. if(!empty($fieldList['mailto']))
  176. {
  177. $fieldList['mailto']['control'] = 'picker';
  178. $fieldList['mailto']['multiple'] = true;
  179. $fieldList['mailto']['items'] = $this->transferConfig->sysDataList['user'];
  180. }
  181. if($this->config->edition != 'open') $fieldList = $this->initWorkflowFieldList($module, $fieldList);
  182. return $fieldList;
  183. }
  184. /**
  185. * 初始化工作流字段。
  186. * Init workflow fieldList.
  187. *
  188. * @param string $module
  189. * @param array $fieldList
  190. * @access protected
  191. * @return array
  192. */
  193. protected function initWorkflowFieldList($module, $fieldList)
  194. {
  195. $this->loadModel($module);
  196. $moduleName = $this->app->rawModule;
  197. $methodName = $this->app->rawMethod;
  198. $groupID = $this->loadModel('workflowgroup')->getGroupIDByData($moduleName, null);
  199. $action = $this->loadModel('workflowaction')->getByModuleAndAction($moduleName, $methodName, $groupID);
  200. if(empty($action)) return $fieldList;
  201. if($action->extensionType == 'none' and $action->buildin == 1) return $fieldList;
  202. $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
  203. $workflowFields = $this->workflowaction->getPageFields($moduleName, $methodName, true, null, 0, $groupID);
  204. foreach($workflowFields as $field)
  205. {
  206. if(empty($fieldList[$field->field])) continue;
  207. if(!empty($field->buildin)) continue;
  208. if(empty($field->show)) continue;
  209. if($field->control == 'file')
  210. {
  211. unset($fieldList[$field->field]);
  212. continue;
  213. }
  214. $fieldList[$field->field]['name'] = $field->field;
  215. $fieldList[$field->field]['label'] = $field->name;
  216. $fieldList[$field->field]['title'] = $field->name;
  217. if($notEmptyRule && strpos(",{$field->rules},", ",{$notEmptyRule->id},") !== false) $fieldList[$field->field]['required'] = true;
  218. $fieldList[$field->field]['control'] = $this->loadModel('flow')->buildFormControl($field);
  219. if(in_array($field->control, array('select', 'radio', 'multi-select', 'checkbox')))
  220. {
  221. if(empty($field->options)) continue;
  222. $field = $this->loadModel('workflowfield')->processFieldOptions($field);
  223. $options = $this->workflowfield->getFieldOptions($field, true);
  224. if($options)
  225. {
  226. $fieldList[$field->field]['items'] = $options;
  227. if($field->control == 'multi-select') $fieldList[$field->field]['multiple'] = true;
  228. $this->moduleListFields[] = $field->field;
  229. $this->config->$module->listFields .= ',' . $field->field;
  230. }
  231. }
  232. }
  233. return $fieldList;
  234. }
  235. /**
  236. * 初始化导出字段下拉列表的数据。
  237. * Init field items.
  238. *
  239. * @param string $model
  240. * @param string $field
  241. * @param array $object
  242. * @param bool $withKey 是否需要将键值拼接到Value中
  243. * @access public
  244. * @return array
  245. */
  246. public function initItems($model, $field, $object, $withKey = true)
  247. {
  248. $items = $object['items'];
  249. if(!$object['dataSource']) return $items;
  250. /* 解析dataSource。*/
  251. /* Parse dataSource. */
  252. extract($object['dataSource']); // $module, $method, $params, $pairs, $sql, $lang
  253. /* 如果配置了系统字段,使用系统数据。*/
  254. /* If empty items put system datas. */
  255. if(empty($items))
  256. {
  257. if(strpos($this->moduleConfig->sysLangFields, $field) !== false && !empty($this->moduleLang->{$field.'List'}))
  258. {
  259. if($field == 'pri' && isset($this->moduleLang->priList[0])) unset($this->moduleLang->priList[0]);
  260. $items = $this->moduleLang->{$field.'List'};
  261. }
  262. if(strpos($this->moduleConfig->sysDataFields, $field) !== false && !empty($this->transferConfig->sysDataList[$field])) $items = $this->transferConfig->sysDataList[$field];
  263. }
  264. /* 如果配置了来源方法,则调用该方法。*/
  265. /* If config the source method, call the method. */
  266. if(!empty($module) && !empty($method))
  267. {
  268. $params = !empty($params) ? $params : '';
  269. $pairs = !empty($pairs) ? $pairs : '';
  270. $items = $this->transferTao->getSourceByModuleMethod($model, $module, $method, $params, $pairs);
  271. }
  272. elseif(!empty($lang))
  273. {
  274. /* 如果配置了语言字段,返回语言数据。*/
  275. /* If config the language field, return language data. */
  276. $items = isset($this->moduleLang->$lang) ? $this->moduleLang->$lang : '';
  277. }
  278. if(is_array($items) && $withKey)
  279. {
  280. unset($items['']);
  281. foreach($items as $key => $value) $items[$key] = $value . "(#$key)";
  282. }
  283. return $items;
  284. }
  285. /**
  286. * 初始化名称字段。
  287. * Init Title.
  288. *
  289. * @param string $module
  290. * @param string $field
  291. * @access public
  292. * @return string
  293. */
  294. public function initTitle($module, $field = '')
  295. {
  296. if(!$field) return '';
  297. $this->commonActions($module);
  298. /* 如果存在config->dtable->titles,则使用config->dtable->titles。 */
  299. /* If exists config->dtable->titles, use config->dtable->titles. */
  300. if(!empty($this->moduleConfig->fieldList[$field]['title'])) return $this->moduleLang->{$this->moduleConfig->fieldList[$field]['title']};
  301. /* 如果存在该字段的语言项,则使用该语言项。 */
  302. /* If exists the field's language item, use it. */
  303. if(isset($this->moduleLang->$field)) return $this->moduleLang->$field;
  304. /* 如果存在该字段的语言项别名,则使用该语言项别名。 */
  305. /* If exists the field's language item alias, use it. */
  306. if(isset($this->moduleLang->{$field . 'AB'})) return $this->moduleLang->{$field . 'AB'};
  307. return $field;
  308. }
  309. /**
  310. * 设置字段的控件类型(是否是下拉菜单)。
  311. * Init Control.
  312. *
  313. * @param string $module
  314. * @param string $field
  315. * @access public
  316. * @return void
  317. */
  318. public function initControl($module, $field)
  319. {
  320. if(isset($this->moduleFieldList[$field]['control'])) return $this->moduleFieldList[$field]['control'];
  321. if(isset($this->moduleLang->{$field.'List'})) return 'select'; //如果存在该字段的语言项列表,则控件为select
  322. if(isset($this->moduleFieldList[$field]['dataSource'])) return 'select'; //如果定义了dataSource,则控件为select
  323. /* 如果Transfer系统字段中存在该字段,则control为下拉。 */
  324. /* If Transfer system field exists the field, control is select. */
  325. if(strpos($this->transferConfig->sysDataFields, $field) !== false) return 'select';
  326. return $this->transferConfig->fieldList['control'];
  327. }
  328. /**
  329. * 设置字段是否必填。
  330. * Init Required.
  331. *
  332. * @param string $module
  333. * @param string $field
  334. * @access public
  335. * @return void
  336. */
  337. public function initRequired($module, $field)
  338. {
  339. if(!$field) return false;
  340. $this->commonActions($module);
  341. /* 检查必填字段中是否存在该字段,如果存在返回yes,否则返回no。 */
  342. /* Check whether the required field contains the field. If yes, return true. Otherwise, return false. */
  343. if(empty($this->moduleConfig->create->requiredFields)) return false;
  344. $requiredFields = "," . $this->moduleConfig->create->requiredFields . ",";
  345. if(strpos($requiredFields, $field) !== false) return true;
  346. return false;
  347. }
  348. /**
  349. * 初始化系统数据字段列表(project,execution,product,user)。
  350. * Init system datafields list.
  351. *
  352. * @access public
  353. * @return array
  354. */
  355. public function initSysDataFields()
  356. {
  357. $this->commonActions();
  358. $dataList = array();
  359. $sysDataFields = explode(',', $this->transferConfig->sysDataFields);
  360. foreach($sysDataFields as $field)
  361. {
  362. /* 调用对应模块getPairs方法获取id => name 关联数据。 */
  363. /* Call getPairs method of corresponding module to get id => name related data. */
  364. $dataList[$field] = $this->loadModel($field)->getPairs();
  365. ksort($dataList[$field]);
  366. if($field == 'user')
  367. {
  368. $dataList[$field] = $this->loadModel($field)->getPairs('noclosed|nodeleted|noletter');
  369. unset($dataList[$field]['']);
  370. }
  371. if(!in_array(strtolower($this->app->methodName), array('ajaxgettbody', 'ajaxgetoptions', 'showimport'))) foreach($dataList[$field] as $key => $value) $dataList[$field][$key] = $value . "(#$key)";
  372. }
  373. return $dataList;
  374. }
  375. /**
  376. * 将被操作模块与Transfer模块的配置合并。
  377. * Merge config.
  378. *
  379. * @param string $module
  380. * @access public
  381. * @return void
  382. */
  383. public function mergeConfig($module)
  384. {
  385. $this->commonActions($module);
  386. $transferConfig = $this->transferConfig;
  387. $moduleConfig = $this->moduleConfig;
  388. if(!isset($moduleConfig->export)) $moduleConfig->export = new stdClass();
  389. if(!isset($moduleConfig->import)) $moduleConfig->export = new stdClass();
  390. $this->moduleConfig->dateFields = isset($moduleConfig->dateFields) ? $moduleConfig->dateFields : $transferConfig->dateFields;
  391. $this->moduleConfig->listFields = isset($moduleConfig->listFields) ? $moduleConfig->listFields : $transferConfig->listFields;
  392. $this->moduleConfig->sysLangFields = isset($moduleConfig->sysLangFields) ? $moduleConfig->sysLangFields : $transferConfig->sysLangFields;
  393. $this->moduleConfig->sysDataFields = isset($moduleConfig->sysDataFields) ? $moduleConfig->sysDataFields : $transferConfig->sysDataFields;
  394. $this->moduleConfig->datetimeFields = isset($moduleConfig->datetimeFields) ? $moduleConfig->datetimeFields : $transferConfig->datetimeFields;
  395. $this->moduleConfig->textareaFields = isset($moduleConfig->textareaFields) ? $moduleConfig->textareaFields : $transferConfig->textareaFields;
  396. }
  397. /**
  398. * 生成导出数据。
  399. * Generate export datas.
  400. *
  401. * @param array $fieldList
  402. * @param array $rows
  403. * @access public
  404. * @return array
  405. */
  406. public function generateExportDatas($fieldList, $rows = array())
  407. {
  408. $exportDatas = array();
  409. $dataSourceList = array();
  410. foreach($fieldList as $key => $field)
  411. {
  412. $exportDatas['fields'][$key] = $field['title'];
  413. if($field['values'] || $field['items'])
  414. {
  415. $exportDatas[$key] = $field['items'] ? $field['items'] : $field['values'];
  416. $dataSourceList[] = $key;
  417. }
  418. }
  419. if(empty($rows)) return $exportDatas;
  420. $exportDatas['user'] = $this->loadModel('user')->getPairs('noclosed|nodeleted|noletter');
  421. foreach($rows as $id => $row)
  422. {
  423. foreach($row as $field => $value)
  424. {
  425. if(isset($fieldList[$field]['from']) && $fieldList[$field]['from'] == 'workflow') continue;
  426. if(in_array($field, $dataSourceList))
  427. {
  428. /* 处理下拉框数据。*/
  429. /* Deal dropdown values. */
  430. if($fieldList[$field]['control'] == 'select' || $fieldList[$field]['control'] == 'picker') $rows[$id]->$field = isset($exportDatas[$field][$value]) ? $exportDatas[$field][$value] : $value; // 单选下拉
  431. if($fieldList[$field]['control'] == 'multiple' || $fieldList[$field]['multiple'] == true) // 多选下拉
  432. {
  433. $separator = $field == 'mailto' ? ',' : "\n";
  434. $multipleLsit = explode(',', (string) $value);
  435. foreach($multipleLsit as $key => $tmpValue) $multipleLsit[$key] = zget($exportDatas[$field], $tmpValue);
  436. $multiple = implode($separator, $multipleLsit);
  437. $rows[$id]->$field = $multiple;
  438. }
  439. }
  440. elseif(strpos($this->config->transfer->userFields, $field) !== false)
  441. {
  442. /* 处理用户字段。*/
  443. /* if user deleted when export set userFields is itself. */
  444. $rows[$id]->$field = zget($exportDatas['user'], $value);
  445. }
  446. /* 处理为空字段的情况。*/
  447. /* if value = 0 or value = 0000:00:00 set value = ''. */
  448. if(is_string($rows[$id]->$field) && ($rows[$id]->$field == '0' || substr($rows[$id]->$field, 0, 10) == '0000:00:00')) $rows[$id]->$field = '';
  449. if(isset($fieldList[$field]['control']) && in_array($fieldList[$field]['control'], array('select', 'picker', 'multiple')) && $rows[$id]->$field == '0') $rows[$id]->$field = '';
  450. }
  451. }
  452. $exportDatas['rows'] = array_values($rows);
  453. return $exportDatas;
  454. }
  455. /**
  456. * 获取当前选中数据的所有附件。
  457. * Get related files.
  458. *
  459. * @param string $module
  460. * @param array $rows
  461. * @access public
  462. * @return array
  463. */
  464. public function getFiles($module, $rows)
  465. {
  466. if(empty($rows)) return array();
  467. /* 获取附件分组。*/
  468. /* Get file groups. */
  469. $fileGroups = $this->transferTao->getFileGroups($module, array_keys($rows));
  470. if(empty($fileGroups)) return $rows;
  471. /* 将附件追加到数组中并设置下载链接。*/
  472. /* Set related files. */
  473. foreach($rows as $row)
  474. {
  475. $row->files = '';
  476. if(!isset($fileGroups[$row->id])) continue;
  477. foreach($fileGroups[$row->id] as $file)
  478. {
  479. /* 设置下载链接。*/
  480. /* Set download link. */
  481. $fileURL = common::getSysURL() . helper::createLink('file', 'download', "fileID={$file->id}");
  482. $row->files .= html::a($fileURL, $file->title, '_blank') . '<br />';
  483. }
  484. }
  485. return $rows;
  486. }
  487. /**
  488. * 设置下拉字段数据。
  489. * Set list value.
  490. *
  491. * @param string $module
  492. * @param array $fieldList
  493. * @access public
  494. * @return array
  495. */
  496. public function setListValue($module, $fieldList)
  497. {
  498. $lists = array();
  499. if(!empty($this->moduleListFields))
  500. {
  501. $listFields = $this->moduleListFields;
  502. /* 从fieldList和sysLangFields中为下拉字段赋值。*/
  503. /* Set value from fieldList and sysLangFields. */
  504. foreach($listFields as $field)
  505. {
  506. if(empty($field)) continue;
  507. $listName = $field . 'List'; // 下拉字段以字段名 + List命名。
  508. if(!empty($_POST[$listName]))
  509. {
  510. if(isset($this->config->excel->sysDataField)) $this->config->excel->sysDataField[] = $field;
  511. continue;
  512. }
  513. $lists[$listName] = array();
  514. if(!empty($fieldList[$field]))
  515. {
  516. $lists[$listName] = !empty($fieldList[$field]['items']) ? $fieldList[$field]['items'] : $fieldList[$field]['values'];
  517. /* 从语言项里赋值。*/
  518. /* Set value from lang. */
  519. if(strpos($this->moduleConfig->sysLangFields, $field) !== false && is_array($fieldList[$field]['values'])) $lists[$listName] = implode(',', $fieldList[$field]['values']);
  520. }
  521. /* 将下拉字段赋值给excel->sysDataField。*/
  522. /* Set value to excel->sysDataField. */
  523. if(isset($this->config->excel->sysDataField) && is_array($lists[$listName])) $this->config->excel->sysDataField[] = $field;
  524. }
  525. $lists['listStyle'] = $listFields;
  526. }
  527. /* 如果有级联字段,则加入级联字段列表(如用例的模块和相关需求)。*/
  528. /* If has cascade field, add cascade field list. */
  529. if(!empty($this->moduleConfig->cascade))
  530. {
  531. $lists = $this->transferTao->getCascadeList($module, $lists);
  532. $lists['cascade'] = $this->moduleConfig->cascade;
  533. }
  534. return $lists;
  535. }
  536. /**
  537. * 获取导出数据。
  538. * Get Rows.
  539. *
  540. * @param string $module
  541. * @param object|array $fieldList
  542. * @access public
  543. * @return array
  544. */
  545. public function getRows($module, $fieldList)
  546. {
  547. $moduleDatas = $this->getQueryDatas($module); // 根据SESSION中的条件查询数据。
  548. if(is_object($fieldList)) $fieldList = (array) $fieldList;
  549. if(isset($fieldList['files'])) $moduleDatas = $this->getFiles($module, $moduleDatas); // 如果有附件字段则获取附件。
  550. /* 如果存在rows则用rows中的数据覆盖查询的数据。*/
  551. /* If has rows, use rows data to cover query data. */
  552. $rows = !empty($_POST['rows']) ? $_POST['rows'] : array();
  553. foreach($rows as $id => $row) $moduleDatas[$id] = (object) array_merge((array)$moduleDatas[$id], (array)$row);
  554. /* 如果要导出描述或验收标准字段,则追加。*/
  555. /* If export spec or verify field, append. */
  556. if(isset($fieldList['spec']) || isset($fieldList['verify']))
  557. {
  558. if($module == 'demand')
  559. {
  560. $editorDataList = $this->dao->select('demand,spec,verify')->from(TABLE_DEMANDSPEC)->where('demand')->in(array_keys($moduleDatas))->fetchAll('demand');
  561. }
  562. else
  563. {
  564. $editorDataList = $this->dao->select('story,spec,verify')->from(TABLE_STORYSPEC)->where('story')->in(array_keys($moduleDatas))->fetchAll('story');
  565. }
  566. foreach($moduleDatas as $moduleData)
  567. {
  568. if(isset($fieldList['spec'])) $moduleData->spec = !empty($editorDataList[$moduleData->id]) ? $editorDataList[$moduleData->id]->spec : $editorDataList[$moduleData->id]->spec;
  569. if(isset($fieldList['verify'])) $moduleData->verify = !empty($editorDataList[$moduleData->id]) ? $editorDataList[$moduleData->id]->verify : $editorDataList[$moduleData->id]->verify;
  570. }
  571. }
  572. /* 设置子数据。*/
  573. /* Deal children datas and multiple tasks. */
  574. if($moduleDatas) $moduleDatas = $this->transferTao->updateChildDatas($moduleDatas);
  575. return $moduleDatas;
  576. }
  577. /**
  578. * 获取查询数据。
  579. * Get query datas.
  580. *
  581. * @param string $module
  582. * @access public
  583. * @return array
  584. */
  585. public function getQueryDatas($module = '')
  586. {
  587. $moduleDatas = array();
  588. $checkedItem = $this->post->checkedItem ? $this->post->checkedItem : $this->cookie->checkedItem;
  589. $onlyCondition = $this->session->{$module . 'OnlyCondition'};
  590. $queryCondition = $this->session->{$module . 'QueryCondition'};
  591. $orderBy = $this->session->{$module . 'OrderBy'};
  592. /* 插入用例场景数据。*/
  593. /* Fetch the scene's cases. */
  594. if($module == 'testcase') $queryCondition = preg_replace("/AND\s+t[0-9]\.\"?scene\"?\s+=\s+'0'/i", '', $queryCondition);
  595. /* 根据SESSION中的条件查询数据。*/
  596. /* Fetch datas by session condition. */
  597. if($onlyCondition && $queryCondition)
  598. {
  599. if($module == 'story') $queryCondition = str_replace('`story`', '`id`', $queryCondition);
  600. /* 业需用需列表也可能选中软需进行导出。 */
  601. if(in_array($module, array('epic', 'requirement')) && $this->post->exportType == 'selected')
  602. {
  603. $queryCondition = str_replace("`type` = '$module'", '1 = 1', $queryCondition);
  604. }
  605. $selectedFields = '*';
  606. if($orderBy && strpos($orderBy, 'priOrder') !== false) $selectedFields .= ",IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) AS priOrder";
  607. if($orderBy && strpos($orderBy, 'severityOrder') !== false) $selectedFields .= ",IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) AS severityOrder";
  608. if($module == 'task' && $orderBy && strpos($orderBy, 'statusOrder') !== false) $selectedFields .= ",INSTR('wait,doing,done,pause,cancel,closed,', status) AS statusOrder";
  609. $table = zget($this->config->objectTables, $module); //获取对应的表
  610. $moduleDatas = $this->dao->select($selectedFields)->from($table)
  611. ->where($queryCondition)
  612. ->beginIF($this->post->exportType == 'selected')->andWhere('id')->in($checkedItem)->fi()
  613. ->beginIF($orderBy)->orderBy($orderBy)->fi()
  614. ->fetchAll('id', false);
  615. }
  616. elseif($queryCondition)
  617. {
  618. $selectKey = 'id';
  619. if($module == 'testcase') $module = 'case';
  620. /* 字段前加入表别名。*/
  621. /* Add table alias to field. */
  622. preg_match_all('/[`"]' . $this->config->db->prefix . $module .'[`"] AS ([\w]+) /', $queryCondition, $matches);
  623. if(isset($matches[1][0])) $selectKey = "{$matches[1][0]}.id";
  624. $stmt = $this->dbh->query($queryCondition . ($this->post->exportType == 'selected' ? " AND $selectKey IN(" . ($checkedItem ? $checkedItem : '0') . ")" : '') . ($orderBy ? " ORDER BY $orderBy" : ''));
  625. while($row = $stmt->fetch())
  626. {
  627. if($selectKey !== 't1.id' and isset($row->$module) and isset($row->id)) $row->id = $row->$module;
  628. $moduleDatas[$row->id] = $row;
  629. }
  630. }
  631. return $moduleDatas;
  632. }
  633. /**
  634. * 处理Excel表中下拉框数据。
  635. * Deal excel dropdown values.
  636. *
  637. * @param string $module 模块
  638. * @param array $rows Excel数据行
  639. * @param string $filter
  640. * @param array $fields
  641. * @access public
  642. * @return array
  643. */
  644. public function parseExcelDropdownValues($module, $rows, $filter = '', $fields = array())
  645. {
  646. $this->commonActions($module);
  647. $fieldList = $this->initFieldList($module, array_keys($fields), false);
  648. if(is_string($this->moduleConfig->dateFields)) $this->moduleConfig->dateFields = explode(',', $this->moduleConfig->dateFields);
  649. if(is_string($this->moduleConfig->datetimeFields)) $this->moduleConfig->datetimeFields = explode(',', $this->moduleConfig->datetimeFields);
  650. if(is_string($this->transferConfig->dateFields)) $this->transferConfig->dateFields = explode(',', $this->transferConfig->dateFields);
  651. foreach($rows as $key => $data)
  652. {
  653. /* 每行的数据。*/
  654. /* Row data. */
  655. foreach($data as $field => $cellValue)
  656. {
  657. if(empty($cellValue) || is_array($cellValue)) continue;
  658. if(in_array($field, $this->transferConfig->dateFields) and helper::isZeroDate($cellValue)) $rows[$key]->$field = ''; // 如果是日期,并且为 0000-00-00,则转换为空
  659. if(in_array($field, $this->moduleConfig->dateFields) or in_array($field, $this->moduleConfig->datetimeFields)) $rows[$key]->$field = $this->loadModel('common')->formatDate((string)$cellValue); // 如果是时间类型字段,则转换为时间
  660. /* 获取字段的控件类型。*/
  661. /* Get field control type. */
  662. $control = isset($fieldList[$field]['control']) ? $fieldList[$field]['control'] : '';
  663. if(isset($control['control'])) $control = $control['control'];
  664. /* 如果字段是下拉字段并且在excel里不是下拉框的形式时,根据fieldList->value查找value。*/
  665. /* If the field is a dropdown field and the value in excel is not a dropdown box, the value is found by fieldList->value. */
  666. if(!in_array($control, array('select', 'multiple', 'picker', 'radioList', 'checkList'))) continue;
  667. $rows[$key]->$field = $this->transferTao->extractElements((string) $cellValue, $field, $fieldList[$field]['items']);
  668. }
  669. }
  670. return $rows;
  671. }
  672. /**
  673. * 处理导入分页数据。
  674. * Get pagelist for datas.
  675. *
  676. * @param array $datas
  677. * @param int $pagerID
  678. * @access protected
  679. * @return object
  680. */
  681. protected function getPageDatas($datas, $pagerID = 1)
  682. {
  683. $maxImport = $this->maxImport; //每页最大导入数
  684. $result = new stdClass();
  685. $result->allCount = count($datas); //所有数据
  686. $result->allPager = 1; //起始页
  687. $result->pagerID = $pagerID; //当前页
  688. /* 如果总数大于最大导入数,则分页。*/
  689. /* If the total number is greater than the maximum import number, then paging. */
  690. if($result->allCount > $this->config->file->maxImport && empty($maxImport))
  691. {
  692. $result->datas = $datas;
  693. $result->maxImport = $maxImport;
  694. return $result;
  695. }
  696. /* 计算总页数。*/
  697. /* Calculate total pages. */
  698. if($maxImport)
  699. {
  700. $result->allPager = ceil($result->allCount / $maxImport); //总页数
  701. $datas = array_slice($datas, ($pagerID - 1) * $maxImport, $maxImport, true); //获取当前页的数据
  702. }
  703. if(!$maxImport) $this->maxImport = $result->allCount; //设置每页最大导入数
  704. $result->maxImport = $maxImport;
  705. $result->isEndPage = $pagerID >= $result->allPager; //是否是最后一页
  706. $result->datas = $datas;
  707. $this->session->set('insert', !empty($datas) && empty(reset($datas)->id)); //如果存在ID列则在SESSION中标记insert用来判断是否是插入/更新
  708. if(empty($datas)) return print(js::locate('back'));
  709. return $result;
  710. }
  711. /**
  712. * 格式化导入导出标准数据格式。
  713. * Format standard data format.
  714. *
  715. * @param string $module
  716. * @param string $filter
  717. * @access protected
  718. * @return array
  719. */
  720. protected function format($module = '', $filter = '')
  721. {
  722. /* Bulid import paris (field => name). */
  723. $fields = $this->transferTao->getImportFields($module);
  724. /* 检查临时文件是否存在并返回完成路径。 */
  725. /* Check tmpfile. */
  726. $tmpFile = $this->checkTmpFile();
  727. /* 如果临时文件存在,则读取临时文件i,否则就创建临时文件。 */
  728. /* If tmp file exists, read tmp file, otherwise create tmp file. */
  729. if(!$tmpFile)
  730. {
  731. $rows = $this->getRowsFromExcel(); // 从Excel中获取数据
  732. if(dao::isError()) return false;
  733. $moduleData = $this->processRows4Fields($rows, $fields); // 处理Excel中的数据过滤无效字段
  734. if(dao::isError()) return false;
  735. $moduleData = $this->parseExcelDropdownValues($module, $moduleData, $filter, $fields); // 解析Excel中下拉字段的数据,转换成具体value
  736. $this->createTmpFile($moduleData); //将格式化后的数据写入临时文件中
  737. }
  738. else
  739. {
  740. $moduleData = unserialize(file_get_contents($tmpFile));
  741. }
  742. if(isset($fields['id'])) unset($fields['id']);
  743. $this->session->set($module . 'TemplateFields', implode(',', array_keys($fields))); // 将模板字段到SESSION中
  744. return $moduleData;
  745. }
  746. /**
  747. * 检查临时文件是否存在。
  748. * Check tmp file.
  749. *
  750. * @access protected
  751. * @return string|false
  752. */
  753. protected function checkTmpFile()
  754. {
  755. /* 从session中获取临时文件。*/
  756. /* Get tmp file from session. */
  757. $file = $this->session->fileImportFileName;
  758. $tmpPath = $this->loadModel('file')->getPathOfImportedFile();
  759. $tmpFile = $tmpPath . DS . md5(basename($file));
  760. if($this->maxImport and file_exists($tmpFile)) return $tmpFile;
  761. return false;
  762. }
  763. /**
  764. * 获取Excel中的数据。
  765. * Get rows from excel.
  766. *
  767. * @access protected
  768. * @return array|bool
  769. */
  770. protected function getRowsFromExcel()
  771. {
  772. $rows = $this->file->getRowsFromExcel($this->session->fileImportFileName);
  773. if(is_string($rows))
  774. {
  775. if($this->session->fileImportFileName) unlink($this->session->fileImportFileName);
  776. unset($_SESSION['fileImportFileName']);
  777. unset($_SESSION['fileImportExtension']);
  778. dao::$errors['message'] = $rows;
  779. return false;
  780. }
  781. return $rows;
  782. }
  783. /**
  784. * 处理行数据。
  785. * Process rows for fields.
  786. *
  787. * @param array $rows
  788. * @param array $fields
  789. * @access protected
  790. * @return array
  791. */
  792. protected function processRows4Fields($rows = array(), $fields = array())
  793. {
  794. $objectDatas = array();
  795. foreach($rows as $currentRow => $row)
  796. {
  797. $tmpArray = new stdClass();
  798. foreach($row as $currentColumn => $cellValue)
  799. {
  800. $cellValue = trim((string)$cellValue);
  801. /* 第一行是标题字段。*/
  802. /* First row is title field. */
  803. if($currentRow == 1)
  804. {
  805. $field = array_search($cellValue, $fields); //找出要导入的字段
  806. $columnKey[$currentColumn] = $field ? $field : '';
  807. continue;
  808. }
  809. if(empty($columnKey[$currentColumn])) continue;
  810. $field = $columnKey[$currentColumn];
  811. /* Check empty data. */
  812. $tmpArray->$field = empty($cellValue) ? '' : $cellValue;
  813. $currentColumn ++;
  814. }
  815. if(!empty($tmpArray->title) || !empty($tmpArray->name)) $objectDatas[$currentRow] = $tmpArray;
  816. unset($tmpArray);
  817. }
  818. if(empty($objectDatas))
  819. {
  820. /* 删除临时文件和SESSION记录。*/
  821. /* Delete tmp file and session record. */
  822. if(file_exists($this->session->fileImportFileName)) unlink($this->session->fileImportFileName);
  823. unset($_SESSION['fileImportFileName']);
  824. unset($_SESSION['fileImportExtension']);
  825. dao::$errors['message'] = $this->lang->excel->noData;
  826. }
  827. return $objectDatas;
  828. }
  829. /**
  830. * 创建临时文件。
  831. * Create tmpFile.
  832. *
  833. * @param array $objectDatas
  834. * @access protected
  835. * @return void
  836. */
  837. protected function createTmpFile($objectDatas)
  838. {
  839. $file = $this->session->fileImportFileName;
  840. $tmpPath = $this->loadModel('file')->getPathOfImportedFile();
  841. $tmpFile = $tmpPath . DS . md5(basename($file));
  842. if(file_exists($tmpFile)) unlink($tmpFile);
  843. file_put_contents($tmpFile, serialize($objectDatas));
  844. $this->session->set('tmpFile', $tmpFile);
  845. }
  846. }