dao.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <?php
  2. /**
  3. * ZenTaoPHP的dao和sql类。
  4. * The dao and sql class file of ZenTaoPHP framework.
  5. *
  6. * The author disclaims copyright to this source code. In place of
  7. * a legal notice, here is a blessing:
  8. *
  9. * May you do good and not evil.
  10. * May you find forgiveness for yourself and forgive others.
  11. * May you share freely, never taking more than you give.
  12. */
  13. helper::import(dirname(__FILE__, 2) . '/base/dao/dao.class.php');
  14. /**
  15. * DAO类。
  16. * DAO, data access object.
  17. *
  18. * @package framework
  19. */
  20. class dao extends baseDAO
  21. {
  22. public function exec($sql = '')
  23. {
  24. if(isset($_SESSION['tutorialMode']) and $_SESSION['tutorialMode']) return true;
  25. return parent::exec($sql);
  26. }
  27. /**
  28. * 设置需要更新或插入的数据。
  29. * Set the data to update or insert.
  30. *
  31. * @param object $data the data object or array
  32. * @access public
  33. * @return object the dao object self.
  34. */
  35. public function data($data, $skipFields = '')
  36. {
  37. if(!is_object($data)) $data = (object)$data;
  38. if(get_class($data) == 'form') $data = $data->data;
  39. if(isset($this->config->bizVersion))
  40. {
  41. $this->app->loadLang('workflow');
  42. $this->app->loadConfig('workflow');
  43. /* Check current module is buildin workflow. */
  44. if(isset($this->config->workflow->buildin->modules))
  45. {
  46. $currentModule = $this->app->fetchModule ?: $this->app->rawModule;
  47. foreach($this->config->workflow->buildin->modules as $appModules)
  48. {
  49. if(!empty($appModules->$currentModule))
  50. {
  51. $currentMainTable = zget($appModules->$currentModule, 'table', '');
  52. break;
  53. }
  54. }
  55. if(isset($currentMainTable))
  56. {
  57. if(trim($currentMainTable, '`') == trim($this->table, '`'))
  58. {
  59. $data = $this->processData($data);
  60. }
  61. else
  62. {
  63. $workflowFields = array();
  64. $stmt = $this->dbh->query("SELECT `field`,`type` FROM " . TABLE_WORKFLOWFIELD . " WHERE `module` = '{$currentModule}' AND `buildin` = '0'");
  65. while($row = $stmt->fetch())
  66. {
  67. $workflowFields[$row->field] = $row->type;
  68. }
  69. $fields = $this->getFieldsType();
  70. foreach($data as $field => $value)
  71. {
  72. if(!isset($fields[$field]) && isset($workflowFields[$field])) unset($data->$field);
  73. }
  74. }
  75. }
  76. }
  77. }
  78. $skipFields .= ',uid';
  79. return parent::data($data, $skipFields);
  80. }
  81. /**
  82. * Process workflow data
  83. *
  84. * @param object $data
  85. * @access public
  86. * @return object
  87. */
  88. public function processData($data)
  89. {
  90. if(!isset($this->config->bizVersion)) return $this;
  91. $module = $this->app->getModuleName();
  92. $method = $this->app->getMethodName();
  93. $action = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `vision` = '{$this->config->vision}' AND `extensionType` = 'extend'")->fetch(PDO::FETCH_OBJ);
  94. if(!$action) return $data;
  95. $fields = $this->dbh->query("SELECT t2.name,t2.rules,t2.control,t2.field,t2.type,t2.options,t1.layoutRules FROM " . TABLE_WORKFLOWLAYOUT . " AS t1 LEFT JOIN " . TABLE_WORKFLOWFIELD . " AS t2 ON t1.module = t2.module WHERE t1.field = t2.field AND t1.module = '{$module}' AND t1.action = '{$method}' AND `vision` = '{$this->config->vision}' AND t1.readonly = '0'")->fetchAll();
  96. if(!$fields) return $data;
  97. $this->app->loadLang('flow');
  98. $this->app->loadLang('workflowfield');
  99. $this->app->loadConfig('flow');
  100. $this->app->loadConfig('workflowfield');
  101. $this->app->loadConfig('workflowlinkage');
  102. $linkages = !empty($action->linkages) ? json_decode($action->linkages) : array();
  103. $hiddenFields = array();
  104. foreach($linkages as $key => $linkage)
  105. {
  106. $sources = zget($linkage, 'sources', array());
  107. $targets = zget($linkage, 'targets', array());
  108. if(!$linkage or !$sources or !$targets) continue;
  109. $source = reset($linkage->sources);
  110. if(isset($data->{$source->field}))
  111. {
  112. $operator = zget($this->config->workflowlinkage->operatorList, $source->operator);
  113. if(helper::checkCondition($data->{$source->field}, $source->value, $operator))
  114. {
  115. foreach($targets as $target)
  116. {
  117. if($target->status == 'show')
  118. {
  119. unset($hiddenFields[$target->field]);
  120. }
  121. else
  122. {
  123. $hiddenFields[$target->field] = $target->field;
  124. }
  125. }
  126. }
  127. }
  128. }
  129. foreach($fields as $field)
  130. {
  131. if(isset($hiddenFields[$field->field]))
  132. {
  133. unset($data->{$field->field});
  134. continue;
  135. }
  136. if(isset($data->{$field->field}))
  137. {
  138. if($field->options && is_string($field->options) && strpos(',user,dept,', ",$field->options,") !== false)
  139. {
  140. if(!is_array($data->{$field->field})) $data->{$field->field} = explode(',', (string) $data->{$field->field});
  141. foreach($data->{$field->field} as $key => $value)
  142. {
  143. $data->{$field->field}[$key] = $this->getParamRealValue($value);
  144. }
  145. }
  146. /* If data value is array, implode by comma. */
  147. if(is_array($data->{$field->field}))
  148. {
  149. $dataValue = array_values(array_unique(array_filter($data->{$field->field})));
  150. asort($dataValue);
  151. $data->{$field->field} = implode(',', $dataValue);
  152. }
  153. }
  154. else
  155. {
  156. if(strpos(',radio,checkbox,multi-select,', ",$field->control,") !== false && $this->method != 'update') $data->{$field->field} = '';
  157. }
  158. }
  159. foreach($data as $field => $value)
  160. {
  161. if(in_array($value, $this->config->flow->variables)) $data->$field = $this->getParamRealValue($value);
  162. }
  163. return $data;
  164. }
  165. /**
  166. * Get param real value
  167. *
  168. * @param string $param
  169. * @access public
  170. * @return string
  171. */
  172. public function getParamRealValue($param)
  173. {
  174. if(empty($this->deptManager))
  175. {
  176. $dept = zget($this->app->user, 'dept', '');
  177. $manager = $this->dbh->query("SELECT manager FROM " . TABLE_DEPT . " WHERE `id` = '{$dept}'")->fetch(PDO::FETCH_OBJ);
  178. $this->deptManager = $manager ? trim((string) $manager->manager, ',') : '';
  179. }
  180. switch ((string)$param) {
  181. case 'today':
  182. return date('Y-m-d');
  183. case 'now':
  184. case 'currentTime':
  185. return date('Y-m-d H:i:s');
  186. case 'actor':
  187. case 'currentUser':
  188. return $this->app->user->account;
  189. case 'currentDept':
  190. return $this->app->user->dept ?: $param;
  191. case 'deptManager':
  192. return $this->deptManager ?: $param;
  193. default:
  194. return $param;
  195. }
  196. }
  197. /**
  198. * Check workFlow field rule.
  199. *
  200. * @param bool $skip true|false
  201. * @access public
  202. * @return object the dao object self.
  203. */
  204. public function checkFlow($skip = false)
  205. {
  206. if($skip) return $this;
  207. if(!isset($this->config->bizVersion)) return $this;
  208. $module = $this->app->getModuleName();
  209. $method = $this->app->getMethodName();
  210. if($module == 'story' && $this->app->rawModule == 'requirement') $module = 'requirement';
  211. if($module == 'story' && $this->app->rawModule == 'epic') $module = 'epic';
  212. if($module == 'project' && $method == 'createtemplate') $method = 'create';
  213. $linkProductModules = array('productplan', 'release', 'story', 'requirement', 'epic', 'bug', 'testcase', 'testtask', 'feedback', 'ticket');
  214. $linkProjectModules = array('execution', 'build', 'task');
  215. $groupID = 0;
  216. if(in_array($module, array('project', 'product')))
  217. {
  218. $groupID = empty($_POST['workflowGroup']) ? 0 : $_POST['workflowGroup'];
  219. }
  220. elseif(in_array($module, $linkProductModules))
  221. {
  222. $productVar = in_array($module, array('feedback', 'ticket')) ? "{$module}Product" : 'product';
  223. if(!empty($_SESSION[$productVar]) && is_numeric($_SESSION[$productVar]))
  224. {
  225. $productID = $_SESSION[$productVar];
  226. $result = $this->dbh->query('SELECT `workflowGroup`, `shadow` FROM ' . TABLE_PRODUCT . " WHERE `id` = '" . $productID . "'")->fetch(PDO::FETCH_OBJ);
  227. $groupID = !empty($result->workflowGroup) ? $result->workflowGroup : 0;
  228. if(!empty($result->shadow))
  229. {
  230. $result = $this->dbh->query("SELECT t2.`workflowGroup` FROM " . TABLE_PROJECTPRODUCT . " AS t1 LEFT JOIN " . TABLE_PROJECT . " AS t2 ON t1.project = t2.id WHERE t1.product = '{$productID}'")->fetch(PDO::FETCH_OBJ);
  231. $groupID = !empty($result->workflowGroup) ? $result->workflowGroup : 0;
  232. }
  233. }
  234. }
  235. elseif(!empty($_SESSION['project']) && in_array($module, $linkProjectModules))
  236. {
  237. $result = $this->dbh->query('SELECT `workflowGroup` FROM ' . TABLE_PROJECT . " WHERE `id` = '" . $_SESSION['project'] . "'")->fetch(PDO::FETCH_OBJ);
  238. $groupID = !empty($result->workflowGroup) ? $result->workflowGroup : 0;
  239. }
  240. if($groupID)
  241. {
  242. $builtIn = $this->dbh->query("SELECT `main` FROM " . TABLE_WORKFLOWGROUP . " WHERE `id` = '{$groupID}'")->fetch(PDO::FETCH_OBJ);
  243. $groupID = !empty($builtIn->main) ? 0 : $groupID;
  244. }
  245. $flowAction = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '{$module}' AND `action` = '{$method}' AND `buildin` = '1' AND `extensionType` = 'extend' AND `vision` = '{$this->config->vision}' AND `group` = '{$groupID}'")->fetch(PDO::FETCH_OBJ);
  246. if(!$flowAction) return $this;
  247. $flowFields = $this->dbh->query("SELECT t2.name,t2.rules,t2.control,t2.field,t1.layoutRules FROM " . TABLE_WORKFLOWLAYOUT . " AS t1 LEFT JOIN " . TABLE_WORKFLOWFIELD . " AS t2 ON t1.module = t2.module AND t1.field = t2.field WHERE t1.module = '{$module}' AND t1.action = '{$method}' AND t1.readonly = '0' AND t1.vision = '{$this->config->vision}' AND t1.`group` = '{$groupID}' AND t2.`group` = '{$groupID}'")->fetchAll();
  248. if(!$flowFields) return $this;
  249. $rules = array();
  250. $rawRules = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWRULE)->fetchAll();
  251. foreach($rawRules as $rule) $rules[$rule->id] = $rule;
  252. foreach($flowFields as $key => $field)
  253. {
  254. if(!$field)
  255. {
  256. unset($flowFields[$key]);
  257. continue;
  258. }
  259. $ruleIDs = explode(',', trim((string) $field->rules, ',') . ',' . trim((string) $field->layoutRules, ','));
  260. $ruleIDs = array_unique($ruleIDs);
  261. $fieldRules = array();
  262. foreach($ruleIDs as $ruleID)
  263. {
  264. if(!$ruleID || !isset($rules[$ruleID])) continue;
  265. $fieldRules[] = $rules[$ruleID];
  266. }
  267. $field->ruleData = $fieldRules;
  268. $field->rules = join(',', $ruleIDs);
  269. }
  270. return $this->checkExtend($flowFields);
  271. }
  272. /**
  273. * 检查工作流扩展字段
  274. * check workflow extend field
  275. *
  276. * @param array $fields
  277. * @access public
  278. * @return object the dao object self
  279. */
  280. public function checkExtend($fields)
  281. {
  282. global $lang;
  283. if(!$fields) return $this;
  284. foreach($fields as $field)
  285. {
  286. /* If the field don't have rule, don't check it. */
  287. if(empty($field->rules)) continue;
  288. if($field->control == 'file')
  289. {
  290. foreach($field->ruleData as $rule)
  291. {
  292. if(empty($rule)) continue;
  293. if($rule->type != 'system' || $rule->rule != 'notempty') continue;
  294. if(!isset($_FILES[$field->field])) continue;
  295. $files = !empty($_FILES[$field->field]) ? $_FILES[$field->field] : '';
  296. if(empty($files['size'][0])) dao::$errors[$field->field][] = sprintf($lang->error->notempty, $field->name);
  297. break;
  298. }
  299. continue;
  300. }
  301. /* Check rules of fields. */
  302. foreach($field->ruleData as $rule)
  303. {
  304. if(empty($rule)) continue;
  305. if(!isset($this->sqlobj->data->{$field->field})) continue;
  306. if($rule->type == 'system')
  307. {
  308. if($rule->rule == 'unique')
  309. {
  310. if(empty($this->sqlobj->data->{$field->field})) continue;
  311. $condition = isset($this->sqlobj->data->id) ? '`id` != ' . $this->sqlobj->data->id : '';
  312. $this->check($field->field, $rule->rule, $condition);
  313. }
  314. else
  315. {
  316. if(is_numeric($this->sqlobj->data->{$field->field}) && $this->sqlobj->data->{$field->field} == 0 && $rule->rule == 'notempty' && !in_array($field->control, array('select', 'multi-select'))) continue;
  317. $this->check($field->field, $rule->rule);
  318. }
  319. }
  320. elseif($rule->type == 'regex')
  321. {
  322. $this->check($field->field, 'reg', $rule->rule);
  323. }
  324. }
  325. }
  326. return $this;
  327. }
  328. /**
  329. * 将记录进行分页,自动设置limit语句。
  330. * Page the records, set the limit part auto.
  331. *
  332. * @param object $pager
  333. * @param string $distinctField
  334. * @access public
  335. * @return static|sql the dao object self.
  336. */
  337. public function page($pager, $distinctField = '')
  338. {
  339. if(!is_object($pager)) return $this;
  340. /*
  341. * 重新计算分页数据,并判断是否需要返回上一页。
  342. * Calculate pagination to determine whether to return to the previous page.
  343. */
  344. $originalPageID = $pager->pageID;
  345. $recTotal = $this->count($distinctField);
  346. $pager->setRecTotal($recTotal);
  347. $pager->setPageTotal();
  348. if($originalPageID > ceil(($pager->recTotal - $pager->offset) / $pager->recPerPage)) $pager->setPageID($pager->pageTotal);
  349. $this->sqlobj->limit($pager->limit());
  350. return $this;
  351. }
  352. }
  353. /**
  354. * SQL类。
  355. * The SQL class.
  356. *
  357. * @package framework
  358. */
  359. class sql extends baseSQL
  360. {
  361. /**
  362. * 创建GROUP BY部分。
  363. * Create the groupby part.
  364. *
  365. * @param string $groupBy
  366. * @access public
  367. * @return object the sql object.
  368. */
  369. public function groupBy($groupBy)
  370. {
  371. if($this->inCondition and !$this->conditionIsTrue) return $this;
  372. //The dm database cannot use alias for group by
  373. /*
  374. if(!preg_match('/^[a-zA-Z0-9_`\.,\s\"]+$/', $groupBy))
  375. {
  376. $groupBy = htmlspecialchars($groupBy);
  377. die("Group is bad query, The group is $groupBy");
  378. }
  379. */
  380. $this->sql .= ' ' . DAO::GROUPBY . " $groupBy";
  381. return $this;
  382. }
  383. }