zen.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. <?php
  2. /**
  3. * The zen file of custom 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 Shujie Tian <tianshujie@easycorp.ltd>
  8. * @package custom
  9. * @link https://www.zentao.net
  10. */
  11. class customZen extends custom
  12. {
  13. /**
  14. * 设置自定义字段列表。
  15. * Set the list of custom fields.
  16. *
  17. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  18. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  19. * @param string $lang all|zh-cn|zh-tw|en|de|fr
  20. * @param string $currentLang all|zh-cn|zh-tw|en|de|fr
  21. * @access protected
  22. * @return void
  23. */
  24. protected function assignFieldListForSet($module = 'story', $field = 'priList', $lang = '', $currentLang = '')
  25. {
  26. $this->app->loadLang($module);
  27. if($lang == 'all')
  28. {
  29. $fieldList = array();
  30. $items = $this->custom->getItems("lang=all&module={$module}&section={$field}&vision={$this->config->vision}");
  31. foreach($items as $key => $item)
  32. {
  33. $fieldList[$key] = $item->value;
  34. }
  35. }
  36. else
  37. {
  38. $fieldList = zget($this->lang->$module, $field, array());
  39. }
  40. /* Check whether the current language has been customized. */
  41. $lang = str_replace('_', '-', $lang);
  42. $dbFields = $this->custom->getItems("lang={$lang}&module={$module}&section={$field}&vision={$this->config->vision}");
  43. if(empty($dbFields)) $dbFields = $this->custom->getItems("lang=" . ($lang == $currentLang ? 'all' : $currentLang) . "&module={$module}&section={$field}");
  44. if($dbFields)
  45. {
  46. $dbField = reset($dbFields);
  47. if($lang != $dbField->lang)
  48. {
  49. $lang = str_replace('-', "_", $dbField->lang);
  50. foreach($fieldList as $key => $value)
  51. {
  52. if(isset($dbFields[$key]) && $value != $dbFields[$key]->value) $fieldList[$key] = $dbFields[$key]->value;
  53. }
  54. }
  55. }
  56. $this->view->fieldList = $fieldList;
  57. $this->view->dbFields = $dbFields;
  58. $this->view->lang2Set = str_replace('_', '-', $lang);
  59. }
  60. /**
  61. * 设置自定义列表变量。
  62. * Set the list of custom variables.
  63. *
  64. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  65. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  66. * @param string $lang all|zh-cn|zh-tw|en|de|fr
  67. * @param string $currentLang all|zh-cn|zh-tw|en|de|fr
  68. * @access protected
  69. * @return void
  70. */
  71. protected function assignVarsForSet($module = 'story', $field = 'priList', $lang = '', $currentLang = '')
  72. {
  73. $this->assignFieldListForSet($module, $field, $lang, $currentLang);
  74. if($module == 'project' && $field == 'unitList')
  75. {
  76. $this->app->loadConfig($module);
  77. $unitList = zget($this->config->$module, 'unitList', '');
  78. $this->view->unitList = explode(',', $unitList);
  79. $this->view->defaultCurrency = zget($this->config->$module, 'defaultCurrency', 'CNY');
  80. }
  81. if(in_array($module, array('story', 'demand', 'requirement', 'epic')) && $field == 'reviewRules')
  82. {
  83. $this->app->loadConfig($module);
  84. $this->view->reviewRule = zget($this->config->$module, 'reviewRules', 'allpass');
  85. $this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted');
  86. $this->view->superReviewers = zget($this->config->$module, 'superReviewers', '');
  87. }
  88. if(in_array($module, array('story', 'requirement', 'epic')) && $field == 'gradeRule')
  89. {
  90. $this->app->loadConfig($module);
  91. $this->view->gradeRule = zget($this->config->$module, 'gradeRule', '');
  92. }
  93. if(in_array($module, array('story', 'requirement', 'epic')) && $field == 'grade')
  94. {
  95. $this->view->storyGrades = $this->loadModel('story')->getGradeList($module);
  96. }
  97. if(in_array($module, array('story', 'testcase', 'demand', 'requirement', 'epic')) && $field == 'review')
  98. {
  99. $this->app->loadConfig($module);
  100. $this->loadModel('user');
  101. if(in_array($module, array('story', 'requirement', 'demand', 'epic')))
  102. {
  103. $this->view->depts = $this->loadModel('dept')->getDeptPairs();
  104. $this->view->forceReviewRoles = zget($this->config->$module, 'forceReviewRoles', '');
  105. $this->view->forceReviewDepts = zget($this->config->$module, 'forceReviewDepts', '');
  106. $this->view->forceNotReviewRoles = zget($this->config->$module, 'forceNotReviewRoles', '');
  107. $this->view->forceNotReviewDepts = zget($this->config->$module, 'forceNotReviewDepts', '');
  108. }
  109. $this->view->users = $module == 'story' ? $this->user->getCanCreateStoryUsers() : $this->user->getPairs('noclosed|nodeleted');
  110. $this->view->needReview = zget($this->config->$module, 'needReview', 1);
  111. $this->view->forceReview = zget($this->config->$module, 'forceReview', '');
  112. $this->view->forceNotReview = zget($this->config->$module, 'forceNotReview', '');
  113. }
  114. if($module == 'bug' && $field == 'longlife')
  115. {
  116. $this->app->loadConfig('bug');
  117. $this->view->longlife = $this->config->bug->longlife;
  118. }
  119. if($module == 'block' && $field == 'closed')
  120. {
  121. $this->loadModel('block');
  122. $closedBlock = isset($this->config->block->closed) ? $this->config->block->closed : '';
  123. $this->view->blockPairs = $this->block->getClosedBlockPairs($closedBlock);
  124. $this->view->closedBlock = $closedBlock;
  125. }
  126. if($module == 'user' && $field == 'deleted')
  127. {
  128. $this->app->loadConfig('user');
  129. $this->view->showDeleted = isset($this->config->user->showDeleted) ? $this->config->user->showDeleted : '0';
  130. }
  131. }
  132. /**
  133. * 设置自定义字段的值。
  134. * Set the value of the custom field.
  135. *
  136. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  137. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  138. * @access protected
  139. * @return string|bool
  140. */
  141. protected function setFieldListForSet($module = 'story', $field = 'priList')
  142. {
  143. $data = $_POST;
  144. if($module == 'project' && $field == 'unitList')
  145. {
  146. if(empty($data['unitList'])) return dao::$errors['message'] = $this->lang->custom->currencyNotEmpty;
  147. if(empty($data['defaultCurrency'])) return dao::$errors['message'] = $this->lang->custom->defaultNotEmpty;
  148. $data['unitList'] = implode(',', $data['unitList']);
  149. $this->loadModel('setting')->setItems("system.$module", $data);
  150. }
  151. elseif(in_array($module, array('story', 'demand', 'requirement', 'epic')) && $field == 'review')
  152. {
  153. $this->setStoryReview($module, $data);
  154. }
  155. elseif(in_array($module, array('story', 'requirement', 'epic')) && $field == 'gradeRule')
  156. {
  157. $this->setGradeRule($module, $data);
  158. }
  159. elseif(in_array($module, array('story', 'requirement', 'epic')) && $field == 'grade')
  160. {
  161. $this->custom->setStoryGrade($module, $data);
  162. }
  163. elseif(in_array($module, array('story', 'demand', 'requirement', 'epic')) && $field == 'reviewRules')
  164. {
  165. if(!isset($data['superReviewers'])) $data['superReviewers'] = array();
  166. $data['superReviewers'] = implode(',', $data['superReviewers']);
  167. $this->loadModel('setting')->setItems("system.$module@{$this->config->vision}", $data);
  168. }
  169. elseif($module == 'testcase' && $field == 'review')
  170. {
  171. $this->setTestcaseReview($data);
  172. }
  173. elseif($module == 'bug' && $field == 'longlife')
  174. {
  175. $this->loadModel('setting')->setItems('system.bug', $data);
  176. }
  177. elseif($module == 'block' && $field == 'closed')
  178. {
  179. $data['closed'] = implode(',', $data['closed']);
  180. $this->loadModel('setting')->setItem('system.block.closed', zget($data, 'closed', ''));
  181. }
  182. elseif($module == 'user' && $field == 'contactField')
  183. {
  184. if(!isset($data['contactField'])) $data['contactField']= array();
  185. $data['contactField'] = implode(',', $data['contactField']);
  186. $this->loadModel('setting')->setItem('system.user.contactField', $data['contactField']);
  187. }
  188. elseif($module == 'user' && $field == 'deleted')
  189. {
  190. $this->loadModel('setting')->setItem('system.user.showDeleted', $data['showDeleted']);
  191. }
  192. else
  193. {
  194. if(!$this->post->keys) return dao::$errors['message'] = sprintf($this->lang->error->notempty, $this->lang->custom->key);
  195. $this->checkKeysForSet($module, $field);
  196. }
  197. return !dao::isError();
  198. }
  199. /**
  200. * 检查自定义key值是否合法。
  201. * Check whether the key of custom is valid.
  202. *
  203. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  204. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  205. * @access protected
  206. * @return bool
  207. */
  208. protected function checkKeysForSet($module = 'story', $field = 'priList')
  209. {
  210. $this->checkDuplicateKeys($module, $field);
  211. if(dao::$errors) return false;
  212. $oldCustoms = $this->checkInvalidKeys($module, $field);
  213. if(dao::$errors) return false;
  214. $lang = $_POST['lang'];
  215. $currentLang = $this->app->getClientLang();
  216. $this->custom->deleteItems("lang={$lang}&module={$module}&section={$field}&vision={$this->config->vision}");
  217. if($lang == 'all') $this->custom->deleteItems("lang={$currentLang}&module={$module}&section={$field}&vision={$this->config->vision}");
  218. $this->checkEmptyKeys($module, $field);
  219. if($module == 'testcase' && $field == 'stageList')
  220. {
  221. $oldItems = array();
  222. foreach($oldCustoms as $key => $item) $oldItems[$key] = $item->value;
  223. $oldItems = arrayUnion($oldItems, zget($this->lang->$module, $field, array()));
  224. $newItems = array();
  225. $newCustoms = $this->custom->getItems("lang={$lang}&module={$module}&section={$field}");
  226. foreach($newCustoms as $key => $item) $newItems[$key] = $item->value;
  227. $addItems = array_diff_key($newItems, $oldItems);
  228. $delItems = array_diff_key($oldItems, $newItems);
  229. $changeItems = array();
  230. foreach($oldItems as $key => $item)
  231. {
  232. if(!empty($newItems[$key]) && $newItems[$key] != $item) $changeItems[$key] = $newItems[$key];
  233. }
  234. $this->loadModel('upgrade');
  235. if($addItems) $this->upgrade->createTestcaseDeliverable($addItems);
  236. if($delItems) $this->upgrade->deleteTestcaseDeliverable($delItems);
  237. if($changeItems) $this->upgrade->changeTestcaseDeliverable($changeItems);
  238. }
  239. return !dao::isError();
  240. }
  241. /**
  242. * 检查自定义key值是否重复。
  243. * Check whether the key of custom is duplicate.
  244. *
  245. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  246. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  247. * @access protected
  248. * @return bool|string
  249. */
  250. protected function checkDuplicateKeys($module = 'story', $field = 'priList')
  251. {
  252. $keys = array();
  253. if(isset($_POST['keys']))
  254. {
  255. foreach($_POST['keys'] as $key)
  256. {
  257. if($module == 'testtask' && $field == 'typeList' && empty($key)) continue;
  258. if($key && in_array($key, $keys)) return dao::$errors['message'] = sprintf($this->lang->custom->notice->repeatKey, $key);
  259. $keys[] = $key;
  260. }
  261. }
  262. return true;
  263. }
  264. /**
  265. * 检查自定义key值是否正确。
  266. * Check whether the key of custom is valid.
  267. *
  268. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  269. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  270. * @access protected
  271. * @return array|string
  272. */
  273. protected function checkInvalidKeys($module = 'story', $field = 'priList')
  274. {
  275. $lang = $_POST['lang'];
  276. $oldCustoms = $this->custom->getItems("lang={$lang}&module={$module}&section={$field}");
  277. foreach($_POST['keys'] as $index => $key)
  278. {
  279. if(!empty($key)) $key = trim($key);
  280. /* Invalid key. It should be numbers. (It includes severityList in bug module && priList in story, task, bug, testcasea, testtask && todo module.) */
  281. if(($field == 'priList' || $field == 'severityList') && (!is_numeric($key) || $key > 255)) return dao::$errors['message'] = $this->lang->custom->notice->invalidNumberKey;
  282. if(!empty($key) && !isset($oldCustoms[$key]) && $key != 'n/a' && !validater::checkREG($key, '/^[a-z_A-Z_0-9]+$/')) return dao::$errors['message'] = $this->lang->custom->notice->invalidStringKey;
  283. /* The length of roleList in user module is less than 10. check it when saved. */
  284. if($module == 'user' && $field == 'roleList' && strlen($key) > 10) return dao::$errors['message'] = $this->lang->custom->notice->invalidStrlen['ten'];
  285. /* The length of typeList in todo module is less than 15. check it when saved. */
  286. if($module == 'todo' && $field == 'typeList' && strlen($key) > 15) return dao::$errors['message'] = $this->lang->custom->notice->invalidStrlen['fifteen'];
  287. /* The length of sourceList in story module && typeList in task module is less than 20, check it when saved. */
  288. if((($module == 'story' && $field == 'sourceList') || ($module == 'task' && $field == 'typeList')) && strlen($key) > 20) return dao::$errors['message'] = $this->lang->custom->notice->invalidStrlen['twenty'];
  289. /* The length of field that in bug && testcase module && reasonList in story && task module is less than 30, check it when saved. */
  290. if((in_array($module, array('bug', 'testcase')) || (in_array($module, array('story', 'task')) && $field == 'reasonList')) && strlen($key) > 30) return dao::$errors['message'] = $this->lang->custom->notice->invalidStrlen['thirty'];
  291. }
  292. return $oldCustoms;
  293. }
  294. /**
  295. * 检查自定义key值是否为空。
  296. * Check whether the key of custom is empty.
  297. *
  298. * @param string $module todo|story|task|bug|testcase|testtask|user|project
  299. * @param string $field priList|typeList|statusList|sourceList|reasonList|stageList|reviewRules|reviewResultList|review|severityList|osList|browserList|resolutionList|longlife|resultList|roleList|contactField|deleted|unitList
  300. * @access protected
  301. * @return bool|string
  302. */
  303. protected function checkEmptyKeys($module = 'story', $field = 'priList')
  304. {
  305. $lang = $_POST['lang'];
  306. $emptyKey = false;
  307. $keys = array();
  308. foreach($_POST['keys'] as $index => $key)
  309. {
  310. if(!$key && $emptyKey) continue;
  311. $value = $_POST['values'][$index];
  312. $system = $_POST['systems'][$index];
  313. if($key && trim($value) === '') return dao::$errors['message'] = $this->lang->custom->notice->valueEmpty; // Fix bug #23538.
  314. $this->custom->setItem("{$lang}.{$module}.{$field}.{$key}.{$system}", $value);
  315. if(!$key) $emptyKey = true;
  316. }
  317. return true;
  318. }
  319. /**
  320. * 设置需求评审规则。
  321. * Set the review rules of story.
  322. *
  323. * @param string $module
  324. * @param array $data
  325. * @access protected
  326. * @return bool
  327. */
  328. protected function setStoryReview($module, $data)
  329. {
  330. $forceFields = array('forceReview', 'forceNotReview', 'forceReviewRoles', 'forceNotReviewRoles', 'forceReviewDepts', 'forceNotReviewDepts');
  331. foreach($forceFields as $forceField)
  332. {
  333. if(!isset($data[$forceField])) $data[$forceField] = array();
  334. $data[$forceField] = implode(',', $data[$forceField]);
  335. }
  336. foreach($data as $key => $value)
  337. {
  338. if($key == 'needReview') continue;
  339. if(strpos($key, 'Not') && $data['needReview'] == 0) $data[$key] = '';
  340. if(!strpos($key, 'Not') && $data['needReview'] == 1) $data[$key] = '';
  341. }
  342. $this->loadModel('setting')->setItems("system.{$module}@{$this->config->vision}", $data);
  343. return !dao::isError();
  344. }
  345. /**
  346. * 设置需求细分流程。
  347. * Set the rule of story grade.
  348. *
  349. * @param string $module
  350. * @param array $data
  351. * @access protected
  352. * @return bool
  353. */
  354. public function setGradeRule($module, $data)
  355. {
  356. $this->loadModel('setting')->setItems("system.{$module}", $data);
  357. return !dao::isError();
  358. }
  359. /**
  360. * 设置用例评审规则。
  361. * Set testcase review rules.
  362. *
  363. * @param array $data
  364. * @access protected
  365. * @return bool
  366. */
  367. protected function setTestcaseReview($data)
  368. {
  369. if($data['needReview'])
  370. {
  371. unset($data['forceReview']);
  372. if(!isset($data['forceNotReview'])) $data['forceNotReview'] = array();
  373. $data['forceNotReview'] = implode(',', $data['forceNotReview']);
  374. }
  375. else
  376. {
  377. unset($data['forceNotReview']);
  378. if(!isset($data['forceReview'])) $data['forceReview'] = array();
  379. $data['forceReview'] = implode(',', $data['forceReview']);
  380. }
  381. $this->loadModel('setting')->setItems("system.testcase", $data);
  382. return !dao::isError();
  383. }
  384. }