tao.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. <?php
  2. /**
  3. * The tao file of common 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 Yidong Wang<yidong@easycorp.ltd>
  8. * @package common
  9. * @link https://www.zentao.net
  10. */
  11. class commonTao extends commonModel
  12. {
  13. /**
  14. * Get SQL for preAndNext.
  15. *
  16. * @param string $type
  17. * @access protected
  18. * @return string
  19. */
  20. protected function getPreAndNextSQL($type)
  21. {
  22. $queryCondition = $type . 'QueryCondition';
  23. $typeOnlyCondition = $type . 'OnlyCondition';
  24. $queryCondition = $this->session->$queryCondition;
  25. $table = zget($this->config->objectTables, $type, '');
  26. if($this->config->edition != 'open' && empty($table))
  27. {
  28. $flow = $this->loadModel('workflow')->getByModule($type);
  29. if(empty($flow->table)) return '';
  30. $table = $flow->table;
  31. }
  32. $orderBy = $type . 'OrderBy';
  33. $orderBy = $this->session->$orderBy;
  34. $select = '';
  35. if($this->session->$typeOnlyCondition)
  36. {
  37. if($orderBy and strpos($orderBy, 'priOrder') !== false) $select .= ", IF(`pri` = 0, {$this->config->maxPriValue}, `pri`) as priOrder";
  38. if($orderBy and strpos($orderBy, 'severityOrder') !== false) $select .= ", IF(`severity` = 0, {$this->config->maxPriValue}, `severity`) as severityOrder";
  39. if($type == 'task' && strpos($orderBy, 'statusOrder') !== false) $select .= ", INSTR('wait,doing,done,pause,cancel,closed,', status) as statusOrder";
  40. $queryCondition = str_replace('t4.status', 'status', $queryCondition);
  41. $sql = $this->dao->select("*$select")->from($table)
  42. ->where($queryCondition)
  43. ->beginIF($orderBy != false)->orderBy($orderBy)->fi()
  44. ->get();
  45. }
  46. else
  47. {
  48. $sql = $queryCondition . (empty($orderBy) ? '' : " ORDER BY $orderBy");
  49. }
  50. return $sql;
  51. }
  52. /**
  53. * Query list for preAndNext.
  54. *
  55. * @param string $type
  56. * @param string $sql
  57. * @access protected
  58. * @return array
  59. */
  60. protected function queryListForPreAndNext($type, $sql)
  61. {
  62. $objectIdListKey = $type . 'BrowseList';
  63. $existsObjectList = $this->session->$objectIdListKey;
  64. $typeOnlyCondition = $type . 'OnlyCondition';
  65. if(empty($existsObjectList) or trim($existsObjectList['sql']) != trim($sql))
  66. {
  67. $queryObjects = $this->dao->query($sql);
  68. $objectList = array();
  69. $key = 'id';
  70. if($queryObjects)
  71. {
  72. while($object = $queryObjects->fetch())
  73. {
  74. if(!$this->session->$typeOnlyCondition and $type == 'testcase' and isset($object->case)) $key = 'case';
  75. $id = $object->$key;
  76. $objectList[$id] = $id;
  77. }
  78. }
  79. $this->session->set($objectIdListKey, array('sql' => $sql, 'idkey' => $key, 'objectList' => $objectList), $this->app->tab);
  80. $existsObjectList = $this->session->$objectIdListKey;
  81. }
  82. return $existsObjectList;
  83. }
  84. /**
  85. * Search preAndNext from list.
  86. *
  87. * @param int $objectID
  88. * @param array $objectList
  89. * @access protected
  90. * @return object
  91. */
  92. protected function searchPreAndNextFromList($objectID, $objectList)
  93. {
  94. $preAndNextObject = new stdClass();
  95. $preAndNextObject->pre = '';
  96. $preAndNextObject->next = '';
  97. if(!isset($objectList['objectList'])) return $preAndNextObject;
  98. $preObj = false;
  99. foreach($objectList['objectList'] as $id)
  100. {
  101. /* Get next object. */
  102. if($preObj === true)
  103. {
  104. $preAndNextObject->next = $id;
  105. break;
  106. }
  107. /* Get pre object. */
  108. if($id == $objectID)
  109. {
  110. if($preObj) $preAndNextObject->pre = $preObj;
  111. $preObj = true;
  112. }
  113. if($preObj !== true) $preObj = $id;
  114. }
  115. return $preAndNextObject;
  116. }
  117. /**
  118. * Fetch preAndNextObject.
  119. *
  120. * @param string $type
  121. * @param int $objectID
  122. * @param object $preAndNextObject
  123. * @access protected
  124. * @return object
  125. */
  126. protected function fetchPreAndNextObject($type, $objectID, $preAndNextObject)
  127. {
  128. $queryCondition = $type . 'QueryCondition';
  129. $typeOnlyCondition = $type . 'OnlyCondition';
  130. $objectIdListKey = $type . 'BrowseList';
  131. $queryCondition = $this->session->$queryCondition;
  132. $existsObjectList = $this->session->$objectIdListKey;
  133. $table = zget($this->config->objectTables, $type, '');
  134. if($this->config->edition != 'open' && empty($table))
  135. {
  136. $flow = $this->loadModel('workflow')->getByModule($type);
  137. if(empty($flow->table)) return $preAndNextObject;
  138. $table = $flow->table;
  139. }
  140. if(empty($preAndNextObject->pre) and empty($preAndNextObject->next)) return $preAndNextObject;
  141. if(empty($queryCondition) or $this->session->$typeOnlyCondition)
  142. {
  143. if(!empty($preAndNextObject->pre)) $preAndNextObject->pre = $this->dao->select('*')->from($table)->where('id')->eq($preAndNextObject->pre)->fetch();
  144. if(!empty($preAndNextObject->next)) $preAndNextObject->next = $this->dao->select('*')->from($table)->where('id')->eq($preAndNextObject->next)->fetch();
  145. return $preAndNextObject;
  146. }
  147. $searched = false;
  148. $objects = array();
  149. $key = $existsObjectList['idkey'];
  150. $queryObjects = $this->dao->query($existsObjectList['sql']);
  151. while($object = $queryObjects->fetch())
  152. {
  153. $objects[$object->$key] = $object;
  154. if(!empty($preAndNextObject->pre) and is_numeric($preAndNextObject->pre) and $object->$key == $preAndNextObject->pre) $preAndNextObject->pre = $object;
  155. if(!empty($preAndNextObject->next) and is_numeric($preAndNextObject->next) and $object->$key == $preAndNextObject->next) $preAndNextObject->next = $object;
  156. if((empty($preAndNextObject->pre) or is_object($preAndNextObject->pre)) and (empty($preAndNextObject->next) or is_object($preAndNextObject->next)))
  157. {
  158. $searched = true;
  159. break;
  160. }
  161. }
  162. /* If the pre object or next object is number type, then continue to find the pre or next. */
  163. if(!$searched)
  164. {
  165. $objectIdList = array_keys($objects);
  166. $objectIdIndex = (int)array_search($objectID, $objectIdList);
  167. if(is_numeric($preAndNextObject->pre)) $preAndNextObject->pre = $objectIdIndex - 1 >= 0 ? $objects[$objectIdList[$objectIdIndex - 1]] : '';
  168. if(is_numeric($preAndNextObject->next)) $preAndNextObject->next = $objectIdIndex + 1 < count($objectIdList) ? $objects[$objectIdList[$objectIdIndex + 1]] : '';
  169. }
  170. $preAndNextObject->idKey = $key;
  171. return $preAndNextObject;
  172. }
  173. /**
  174. * 查看当前用户是否有资产库下其他方法的权限。
  175. * Check if current user has other methods permissions under the asset library.
  176. *
  177. * @param bool $display
  178. * @param string $currentModule
  179. * @param string $currentMethod
  180. * @access protected
  181. * @return array
  182. */
  183. protected static function setAssetLibMenu($display, $currentModule, $currentMethod)
  184. {
  185. $methodList = array('caselib', 'issuelib', 'risklib', 'opportunitylib', 'practicelib', 'componentlib');
  186. foreach($methodList as $method)
  187. {
  188. if(common::hasPriv($currentModule, $method))
  189. {
  190. $display = true;
  191. $currentMethod = $method;
  192. break;
  193. }
  194. }
  195. return array($display, $currentMethod);
  196. }
  197. /**
  198. * 根据后台维护分组的视图设置,判断用户是否有权限。
  199. * According to the view maintained by the background, determine whether the user has permission.
  200. *
  201. * @param string $module
  202. * @param string $method
  203. * @param array $acls
  204. * @param mixed $object
  205. * @access protected
  206. * @return bool
  207. */
  208. protected static function checkPrivByRights($module, $method, $acls, $object)
  209. {
  210. global $lang, $app;
  211. if(!commonModel::hasDBPriv($object, $module, $method)) return false;
  212. if(empty($acls['views'])) return true;
  213. $menu = isset($lang->navGroup->$module) ? $lang->navGroup->$module : $module;
  214. if($module == 'my' and $method == 'team') $menu = 'system'; // Fix bug #18642.
  215. $menu = strtolower($menu);
  216. if($menu == 'product' && $app->tab != 'product' && in_array($module, array('story', 'requirement', 'epic'))) return true;
  217. if($menu != 'qa' and $menu != 'project' and !isset($lang->$menu->menu)) return true;
  218. if(($menu == 'my' and $method != 'team') or $menu == 'index' or $module == 'tree') return true;
  219. if($module == 'company' and $method == 'dynamic') return true;
  220. if($module == 'action' and $method == 'editcomment') return true;
  221. if($module == 'action' and $method == 'comment') return true;
  222. if($module == 'report' and $method == 'export') return true;
  223. if(!isset($acls['views'][$menu])) return false;
  224. return true;
  225. }
  226. /**
  227. * 查看当前用户是否有其他个性化设置导航的权限。
  228. * Check if current user has other methods permissions under the preference menu.
  229. *
  230. * @param bool $display
  231. * @param string $currentModule
  232. * @param string $currentMethod
  233. * @access protected
  234. * @return array
  235. */
  236. protected static function setPreferenceMenu($display, $currentModule, $currentMethod)
  237. {
  238. global $app;
  239. global $lang;
  240. $app->loadLang('my');
  241. $moduleLinkList = $currentModule . 'LinkList';
  242. foreach($lang->my->$moduleLinkList as $key => $linkList)
  243. {
  244. $moduleMethodList = explode('-', $key);
  245. $method = $moduleMethodList[1];
  246. if(common::hasPriv($currentModule, $method))
  247. {
  248. $display = true;
  249. $currentMethod = $method;
  250. break;
  251. }
  252. }
  253. return array($display, $currentMethod);
  254. }
  255. /**
  256. * 非个性化设置的导航,查看当前用户是否有该应用下其他方法的权限。
  257. * For non-personalized settings navigation, check if current user has other methods permissions under the application.
  258. *
  259. * @param bool $display
  260. * @param string $currentModule
  261. * @param string $currentMethod
  262. * @access protected
  263. * @return array
  264. */
  265. protected static function setOtherMenu($display, $currentModule, $currentMethod)
  266. {
  267. global $lang;
  268. foreach($lang->$currentModule->menu as $menu)
  269. {
  270. if(!isset($menu['link'])) continue;
  271. $linkPart = explode('|', $menu['link']);
  272. if(!isset($linkPart[2])) continue;
  273. $method = $linkPart[2];
  274. /* Skip some pages that do not require permissions.*/
  275. if($currentModule == 'report' and $method == 'annualData') continue;
  276. if($currentModule == 'my' and $currentMethod == 'team') continue;
  277. if(common::hasPriv($currentModule, $method))
  278. {
  279. $display = true;
  280. $currentMethod = $method;
  281. if(!isset($menu['target'])) break; // Try to jump to the method without opening a new window.
  282. }
  283. }
  284. return array($display, $currentMethod);
  285. }
  286. /**
  287. * 基于导航的所属应用,查看当前用户是否有该应用下其他方法的权限。
  288. * Based on the navigation of the application, check if current user has other methods permissions under the application.
  289. *
  290. * @param string $group
  291. * @param bool $display
  292. * @param string $currentModule
  293. * @param string $currentMethod
  294. * @access protected
  295. * @return array
  296. */
  297. protected static function setMenuByGroup($group, $display, $currentModule, $currentMethod)
  298. {
  299. global $lang;
  300. foreach($lang->$group->menu as $menu)
  301. {
  302. if(!isset($menu['link'])) continue;
  303. $linkPart = explode('|', $menu['link']);
  304. if(count($linkPart) < 3) continue;
  305. list(, $module, $method) = $linkPart;
  306. if(common::hasPriv($module, $method))
  307. {
  308. $display = true;
  309. $currentModule = $module;
  310. $currentMethod = $method;
  311. if(!isset($menu['target'])) break; // Try to jump to the method without opening a new window.
  312. }
  313. }
  314. return array($display, $currentModule, $currentMethod);
  315. }
  316. /**
  317. * 判断用户是否在项目、产品、计划、执行的管理员中。
  318. * Check if the user is in the administrator of the project, product, plan, and execution.
  319. *
  320. * @param string $module
  321. * @param mixed $object
  322. * @access protected
  323. * @return bool
  324. */
  325. protected static function isProjectAdmin($module, $object)
  326. {
  327. global $app, $lang;
  328. $inProject = ((isset($lang->navGroup->$module) and $lang->navGroup->$module == 'project') || (!empty($object->type) and $object->type == 'project'));
  329. $projectID = $app->session->project;
  330. if(!empty($object->type) && $object->type == 'project') $projectID = $object->id;
  331. if($inProject and $projectID and (strpos(",{$app->user->rights['projects']},", ",{$projectID},") !== false or strpos(",{$app->user->rights['projects']},", ',all,') !== false)) return true;
  332. $inProduct = (isset($lang->navGroup->$module) and $lang->navGroup->$module == 'product');
  333. $productID = empty($object->id) ? $app->session->product : $object->id;
  334. if($inProduct and $productID and (strpos(",{$app->user->rights['products']},", ",{$productID},") !== false or strpos(",{$app->user->rights['products']},", ',all,') !== false)) return true;
  335. $inProgram = (isset($lang->navGroup->$module) and $lang->navGroup->$module == 'program');
  336. $programID = empty($object->id) ? $app->session->program : $object->id;
  337. if($inProgram and $programID and (strpos(",{$app->user->rights['programs']},", ",{$programID},") !== false or strpos(",{$app->user->rights['programs']},", ',all,') !== false)) return true;
  338. $inExecution = (isset($lang->navGroup->$module) and $lang->navGroup->$module == 'execution');
  339. $executionID = empty($object->id) ? $app->session->execution : $object->id;
  340. if($inExecution and $executionID and (strpos(",{$app->user->rights['executions']},", ",{$executionID},") !== false or strpos(",{$app->user->rights['executions']},", ',all,') !== false)) return true;
  341. return false;
  342. }
  343. /**
  344. * 获取需求模块下,真实请求的模块和方法。
  345. * Get the real module and method of the request under the requirement module.
  346. *
  347. * @param string $module
  348. * @param string $method
  349. * @param array $params
  350. * @access protected
  351. * @return array
  352. */
  353. protected static function getStoryModuleAndMethod($module, $method, $params)
  354. {
  355. if($module == 'story' && $method == 'processstorychange') return array($module, $method);
  356. global $app;
  357. if(empty($params['storyType']) and $module == 'story' and !empty($app->params['storyType']) and strpos(",story,requirement,", ",{$app->params['storyType']},") !== false) $module = $app->params['storyType'];
  358. if($module == 'story' and !empty($params['storyType']) and strpos(",story,requirement,", ",{$params['storyType']},") !== false) $module = $params['storyType'];
  359. if($module == 'product' and $method == 'browse' and !empty($app->params['storyType']) and $app->params['storyType'] == 'requirement') $method = 'requirement';
  360. if($module == 'product' and $method == 'browse' and !empty($params['storyType']) and $params['storyType'] == 'requirement') $method = 'requirement';
  361. if($module == 'story' and $method == 'linkrequirements') $module = 'requirement';
  362. return array($module, $method);
  363. }
  364. /**
  365. * 获取白板模块下,真实请求的模块和方法。
  366. * Get the real module and method of the request under the board module.
  367. *
  368. * @param string $module
  369. * @param string $method
  370. * @param array $params
  371. * @access protected
  372. * @return string[]
  373. */
  374. protected static function getBoardModuleAndMethod($module, $method, $params)
  375. {
  376. if($module != 'board') return array($module, $method);
  377. if(strpos(',view,open,bind,canvas,autoupdate,quit,importstory,', ",{$method},") !== false) $method = 'creation';
  378. if($method == 'createbytemplate') $method = 'createboard';
  379. return array($module, $method);
  380. }
  381. /**
  382. * Check and update webRoot config in DB.
  383. *
  384. * @param object $dbConfig
  385. * @access public
  386. * @return void
  387. */
  388. public function updateDBWebRoot($dbConfig)
  389. {
  390. if(PHP_SAPI == 'cli') return;
  391. if(!empty($this->app->installing) || !empty($this->app->upgrading)) return;
  392. global $config;
  393. /* Check config webRoot right or not. */
  394. if($config->webRoot[0] != '/') return;
  395. if($config->webRoot[strlen($config->webRoot) - 1] != '/') return;
  396. /* Get webRoot config in db. */
  397. $webRootConfig = null;
  398. foreach($dbConfig->common as $commonConfig)
  399. {
  400. if($commonConfig->key == 'webRoot')
  401. {
  402. $webRootConfig = $commonConfig;
  403. break;
  404. }
  405. }
  406. /* Init db webRoot config. */
  407. if(empty($webRootConfig)) return $this->loadModel('setting')->setItem('system.common.webRoot', $config->webRoot);
  408. if($config->webRoot == $webRootConfig->value) return;
  409. /* Update db webRoot. */
  410. $webRootConfig->value = $config->webRoot;
  411. $this->loadModel('setting')->updateItem('system.common.webRoot', $config->webRoot);
  412. }
  413. }