model.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <?php
  2. /**
  3. * The model file of todo 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 Lanzongjun <lanzongjun@easycorp.ltd>
  8. * @package todo
  9. * @link https://www.zentao.net
  10. */
  11. class todoModel extends model
  12. {
  13. /**
  14. * 创建待办。
  15. * Create todo data.
  16. *
  17. * @param object $todo
  18. * @access public
  19. * @return int|false
  20. */
  21. public function create($todo)
  22. {
  23. $todoID = $this->todoTao->insert($todo);
  24. if(dao::isError()) return false;
  25. return $todoID;
  26. }
  27. /**
  28. * 批量创建待办。
  29. * Batch create todo.
  30. *
  31. * @param mixed[] $todos
  32. * @access public
  33. * @return array|false
  34. */
  35. public function batchCreate($todos)
  36. {
  37. $this->loadModel('action');
  38. $this->loadModel('score');
  39. $todoIdList = array();
  40. foreach($todos as $todo)
  41. {
  42. $todoID = $this->todoTao->insert($todo);
  43. if(!$todoID) return false;
  44. $todoIdList[] = $todoID;
  45. $this->score->create('todo', 'create', $todoID);
  46. $this->action->create('todo', $todoID, 'opened');
  47. }
  48. return $todoIdList;
  49. }
  50. /**
  51. * 更新待办数据。
  52. * update a todo.
  53. *
  54. * @param int $todoID
  55. * @param object $todo
  56. * @access public
  57. * @return array|false
  58. */
  59. public function update($todoID, $todo)
  60. {
  61. $oldTodo = $this->dao->findByID($todoID)->from(TABLE_TODO)->fetch();
  62. if(!$this->todoTao->updateRow($todoID, $todo)) return false;
  63. if(!empty($todo->uid)) $this->loadModel('file')->updateObjectID($todo->uid, $todoID, 'todo');
  64. if(!empty($oldTodo->cycle)) $this->createByCycle(array($todoID => $todo));
  65. if($this->config->edition != 'open' && $todo->type == 'feedback' && $todo->objectID) $this->loadModel('feedback')->updateStatus('todo', $todo->objectID, $todo->status, '', $todoID);
  66. return common::createChanges($oldTodo, (array)$todo);
  67. }
  68. /**
  69. * 更新批量编辑待办数据。
  70. * Update batch edit todo data.
  71. *
  72. * @param array $todos
  73. * @param array $todoIdList
  74. * @access public
  75. * @return array|int
  76. */
  77. public function batchUpdate($todos, $todoIdList)
  78. {
  79. if(empty($todos)) return $todos;
  80. $this->loadModel('action');
  81. if($this->config->edition != 'open') $this->loadModel('feedback');
  82. /* Initialize todos from the post data. */
  83. $oldTodos = $this->getTodosByIdList($todoIdList);
  84. $allChanges = array();
  85. foreach($todos as $todoID => $todo)
  86. {
  87. $oldTodo = $oldTodos[$todoID];
  88. if(in_array($todo->type, $this->config->todo->moduleList))
  89. {
  90. $table = $this->config->objectTables[$todo->type];
  91. $nameField = $this->config->action->objectNameFields[$todo->type];
  92. $todo->name = $this->dao->select($nameField)->from($table)->where('id')->eq($todo->objectID)->fetch($nameField);
  93. }
  94. if(!empty($oldTodo->private) && !isset($todo->private)) $todo->assignedTo = $oldTodo->assignedTo;
  95. $this->todoTao->updateRow($todoID, $todo);
  96. if($oldTodo->status != 'done' and $todo->status == 'done') $this->loadModel('action')->create('todo', $todoID, 'finished', '', 'done');
  97. if(!dao::isError())
  98. {
  99. if($this->config->edition != 'open' && $todo->type == 'feedback' && $todo->objectID && !isset($feedbacks[$todo->objectID]))
  100. {
  101. $feedbacks[$todo->objectID] = $todo->objectID;
  102. $this->feedback->updateStatus('todo', $todo->objectID, $todo->status, '', $todoID);
  103. }
  104. /* Create changes of one object. */
  105. $allChanges[$todoID] = common::createChanges($oldTodo, $todo);
  106. }
  107. else
  108. {
  109. dao::$errors[] = 'todo#' . $todoID . dao::getError(true);
  110. }
  111. }
  112. return $allChanges;
  113. }
  114. /**
  115. * 开始一个待办事项。
  116. * Start a todo.
  117. *
  118. * @param int $todoID
  119. * @access public
  120. * @return bool
  121. */
  122. public function start($todoID)
  123. {
  124. $this->dao->update(TABLE_TODO)->set('status')->eq('doing')->where('id')->eq($todoID)->exec();
  125. $this->loadModel('action')->create('todo', $todoID, 'started');
  126. return !dao::isError();
  127. }
  128. /**
  129. * 完成一个待办。
  130. * Finish one todo.
  131. *
  132. * @param int $todoID
  133. * @access public
  134. * @return bool
  135. */
  136. public function finish($todoID)
  137. {
  138. $todo = new stdclass();
  139. $todo->id = $todoID;
  140. $todo->status = 'done';
  141. $todo->finishedBy = $this->app->user->account;
  142. $todo->finishedDate = helper::now();
  143. $this->todoTao->updateRow($todoID, $todo);
  144. if(dao::isError()) return false;
  145. $this->loadModel('action')->create('todo', $todoID, 'finished', '', 'done');
  146. if($this->config->edition != 'open')
  147. {
  148. $todo = $this->todoTao->fetch($todoID);
  149. $feedbackID = $todo->objectID ? $todo->objectID : '' ;
  150. if($feedbackID) $this->loadModel('feedback')->updateStatus('todo', $feedbackID, 'done', '', $todoID);
  151. }
  152. return true;
  153. }
  154. /**
  155. * 批量完成待办。
  156. * Batch finish todos.
  157. *
  158. * @param int[] $todoIdList
  159. * @access public
  160. * @return bool
  161. */
  162. public function batchFinish($todoIdList)
  163. {
  164. foreach($todoIdList as $todoID)
  165. {
  166. $isFinished = $this->finish($todoID);
  167. if(!$isFinished) return $isFinished;
  168. }
  169. return true;
  170. }
  171. /**
  172. * 获取待办事项详情数据。
  173. * Get info of a todo.
  174. *
  175. * @param int $todoID
  176. * @param bool $setImgSize true|false
  177. * @access public
  178. * @return object|false
  179. */
  180. public function getByID($todoID, $setImgSize = false)
  181. {
  182. $todo = $this->dao->findByID($todoID)->from(TABLE_TODO)->fetch();
  183. if(!$todo) return false;
  184. $todo = $this->loadModel('file')->replaceImgURL((object)$todo, 'desc');
  185. if($setImgSize) $todo->desc = $this->file->setImgSize($todo->desc);
  186. $todo->date = str_replace('-', '', $todo->date);
  187. return $this->todoTao->setTodoNameByType($todo);
  188. }
  189. /**
  190. * 获取用户的待办事项列表。
  191. * Get todo list of a user.
  192. *
  193. * @param string $type
  194. * @param string $account
  195. * @param string|array $status all|today|thisweek|lastweek|before, or a date.
  196. * @param int $limit
  197. * @param object $pager
  198. * @param string $orderBy
  199. * @access public
  200. * @return array
  201. */
  202. public function getList($type = 'today', $account = '', $status = 'all', $limit = 0, $pager = null, $orderBy="date,status,begin")
  203. {
  204. $this->app->loadClass('date', true);
  205. $type = strtolower($type);
  206. $dateRange = $this->dateRange($type);
  207. if(empty($account)) $account = $this->app->user->account;
  208. $todos = $this->todoTao->getListBy($type, $account, $status, (string)$dateRange['begin'], (string)$dateRange['end'], $limit, $orderBy, $pager);
  209. /* Set session. */
  210. $sql = explode('WHERE', $this->dao->get());
  211. $sql = explode('ORDER', $sql[1]);
  212. $this->session->set('todoReportCondition', $sql[0]);
  213. foreach($todos as $todo)
  214. {
  215. $todo = $this->todoTao->setTodoNameByType($todo);
  216. $todo->begin = date::formatTime($todo->begin);
  217. $todo->end = date::formatTime($todo->end);
  218. if($todo->private and $this->app->user->account != $todo->account) $todo->name = $this->lang->todo->thisIsPrivate;
  219. }
  220. return $todos;
  221. }
  222. /**
  223. * 根据类型获取时间范围。
  224. * Date range.
  225. *
  226. * @param string $type
  227. * @access protected
  228. * @return array
  229. */
  230. protected function dateRange($type)
  231. {
  232. if($type == 'future') return array('begin' => '2030-01-01', 'end' => '2030-01-01');
  233. if(is_numeric($type))
  234. {
  235. $date = date('Y-m-d', strtotime($type));
  236. return array('begin' => $date . ' 00:00:00', 'end' => $date . ' 23:59:59');
  237. }
  238. if(strpos(',before,today,yesterday,thisweek,lastweek,thismonth,lastmonth,thisseason,thisyear,', ",$type,") === false) return array('begin' => '', 'end' => '');
  239. if($type == 'before') return array('begin' => '', 'end' => date::yesterday());
  240. $funcName = ($type == 'today' || $type == 'yesterday') ? $type : 'get' . ucfirst($type);
  241. $dateRange = date::$funcName();
  242. if($type == 'today' || $type == 'yesterday') return array('begin' => $dateRange, 'end' => $dateRange);
  243. return $dateRange;
  244. }
  245. /**
  246. * 通过包含一个或多个待办ID的列表获取待办列表,这个列表是以todoID为key、以todo对象为value的数组。
  247. * 如果待办ID列表为空则返回所有待办。
  248. * Get a array with todos which todoID as key, todo object as value by todo id list.
  249. * Return all todos if the todo id list is empty.
  250. *
  251. * @param array|string $todoIdList
  252. * @access public
  253. * @return array
  254. */
  255. public function getByList($todoIdList = '')
  256. {
  257. return $this->dao->select('*')->from(TABLE_TODO)
  258. ->beginIF($todoIdList)->where('id')->in($todoIdList)->fi()
  259. ->fetchAll('id', false);
  260. }
  261. /**
  262. * 判断当前动作是否可以点击。
  263. * Judge an action is clickable or not.
  264. *
  265. * @param object $todo
  266. * @param string $action
  267. * @access public
  268. * @return bool
  269. */
  270. public static function isClickable($todo, $action)
  271. {
  272. global $app;
  273. $action = strtolower($action);
  274. if($todo->private && $app->user->account != $todo->account) return false;
  275. if($action == 'start') return $todo->status == 'wait' && !$todo->cycle;
  276. if($action == 'activate') return $todo->status == 'done' or $todo->status == 'closed';
  277. if($action == 'close') return $todo->status == 'done';
  278. if($action == 'assignto') return !$todo->private && $todo->status != 'done' && $todo->status != 'closed';
  279. if($action == 'finish') return $todo->status != 'done' && $todo->status != 'closed' && !$todo->cycle;
  280. return true;
  281. }
  282. /**
  283. * 根据周期待办创建待办。
  284. * Create todo by cycle.
  285. *
  286. * @param array $todoList
  287. * @access public
  288. * @return void
  289. */
  290. public function createByCycle($todoList)
  291. {
  292. if(empty($todoList)) return;
  293. $this->loadModel('action');
  294. $today = helper::today();
  295. $now = helper::now();
  296. $cycleList = $this->todoTao->getCycleList($todoList);
  297. $validUsers = $this->dao->select('account')->from(TABLE_USER)->where('deleted')->eq(0)->fetchPairs('account', 'account');
  298. foreach($todoList as $todoID => $todo)
  299. {
  300. if(!isset($validUsers[$todo->account])) continue;
  301. $todo->config = json_decode($todo->config);
  302. $begin = $todo->config->begin;
  303. $end = zget($todo->config, 'end', '');
  304. $beforeDays = (int)$todo->config->beforeDays;
  305. if(!empty($beforeDays) && $beforeDays > 0) $begin = date('Y-m-d', strtotime($begin) - $beforeDays * 24 * 3600);
  306. if($today < $begin || (!empty($end) && $today > $end)) continue;
  307. if(empty($todo->id)) $todo->id = $todoID;
  308. $newTodo = $this->todoTao->buildCycleTodo($todo);
  309. if(isset($todo->assignedTo) && $todo->assignedTo) $newTodo->assignedDate = $now;
  310. $start = strtotime($begin);
  311. $finish = strtotime("$today +{$beforeDays} days");
  312. foreach(range($start, $finish, 86400) as $today)
  313. {
  314. $today = date('Y-m-d', $today);
  315. $lastCycle = zget($cycleList, $todoID, '');
  316. $date = $this->todoTao->getCycleTodoDate($todo, $lastCycle, $today);
  317. if($date === false) continue;
  318. if(!$date) continue;
  319. if($date < $todo->config->begin) continue;
  320. if($date < date('Y-m-d')) continue;
  321. if($date > date('Y-m-d', $finish)) continue;
  322. if(!empty($end) && $date > $end) continue;
  323. if($lastCycle and ($date == $lastCycle->date)) continue;
  324. $newTodo->date = $date;
  325. $todoID = $this->todoTao->insert($newTodo);
  326. if($todoID) $this->action->create('todo', $todoID, 'opened', '', '', $newTodo->account);
  327. }
  328. }
  329. }
  330. /**
  331. * 激活待办事项。
  332. * Activate a todo.
  333. *
  334. * @param int $todoID
  335. * @access public
  336. * @return bool
  337. */
  338. public function activate($todoID)
  339. {
  340. $this->dao->update(TABLE_TODO)->set('status')->eq('wait')->where('id')->eq($todoID)->exec();
  341. $todo = $this->fetchByID($todoID);
  342. $this->loadModel('action')->create('todo', $todoID, 'activated', '', 'wait');
  343. if($this->config->edition != 'open' && $todo->type == 'feedback' && $todo->objectID) $this->loadModel('feedback')->updateStatus('todo', $todo->objectID, $todo->status, '', $todoID);
  344. return !dao::isError();
  345. }
  346. /**
  347. * 关闭待办。如果是企业版或旗舰版则更新关联的反馈。
  348. * Close todo. Update related feedback if edition is biz or max.
  349. *
  350. * @param int $todoID
  351. * @access public
  352. * @return bool
  353. */
  354. public function close($todoID)
  355. {
  356. $isClosed = $this->todoTao->closeTodo($todoID);
  357. if(!$isClosed) return false;
  358. $this->loadModel('action')->create('todo', $todoID, 'closed', '', 'closed');
  359. /* Update status of feedback if the closed todo type is feedback. */
  360. if($this->config->edition != 'open')
  361. {
  362. $feedbackID = $this->dao->select('objectID')->from(TABLE_TODO)->where('id')->eq($todoID)->andWhere('type')->eq('feedback')->fetch('objectID');
  363. if($feedbackID) $this->loadModel('feedback')->updateStatus('todo', $feedbackID, 'closed', '', $todoID);
  364. }
  365. return true;
  366. }
  367. /**
  368. * 指派待办。
  369. * Assign todo.
  370. *
  371. * @param object $todo
  372. * @access public
  373. * @return bool
  374. */
  375. public function assignTo($todo)
  376. {
  377. $todoID = (int)$todo->id;
  378. $result = $this->todoTao->updateRow($todoID, $todo);
  379. if(!$result) return false;
  380. $this->loadModel('action')->create('todo', $todoID, 'assigned', '', $todo->assignedTo);
  381. return !dao::isError();
  382. }
  383. /**
  384. * 获取待办事项的数量。
  385. * Get todo count.
  386. *
  387. * @param string $account
  388. * @access public
  389. * @return int
  390. */
  391. public function getCount($account = '')
  392. {
  393. if(empty($account)) $account = $this->app->user->account;
  394. $count = $this->todoTao->getCountByAccount($account, $this->config->vision);
  395. if(dao::isError()) return 0;
  396. return $count;
  397. }
  398. /**
  399. * 根据类型获取各个模块的列表。
  400. * Gets the project ID of the to-do object.
  401. *
  402. * @param array $todoList
  403. * @access public
  404. * @return array
  405. */
  406. public function getTodoProjects($todoList)
  407. {
  408. $projectIDList = array();
  409. $projectModules = $this->config->todo->project;
  410. foreach($todoList as $type => $todos)
  411. {
  412. $todoIdList = array_keys($todos);
  413. if(empty($todoIdList))
  414. {
  415. $projectIDList[$type] = array();
  416. continue;
  417. }
  418. $todoIdList = array_unique($todoIdList);
  419. if(isset($projectModules[$type])) $projectIDList[$type] = $this->todoTao->getProjectList($projectModules[$type], $todoIdList);
  420. if(dao::isError()) return array();
  421. }
  422. return $projectIDList;
  423. }
  424. /**
  425. * 修改待办事项的时间。
  426. * Edit the date of todo.
  427. *
  428. * @param array $todoIdList
  429. * @param string $date
  430. * @access public
  431. * @return bool
  432. */
  433. public function editDate($todoIdList, $date)
  434. {
  435. return $this->todoTao->updateDate($todoIdList, $date);
  436. }
  437. /**
  438. * 获取导出的待办数据。
  439. * Get data for export todo.
  440. *
  441. * @param string $orderBy
  442. * @param string $queryCondition
  443. * @param string $checkedItem
  444. * @access public
  445. * @return array
  446. */
  447. public function getByExportList($orderBy, $queryCondition, $checkedItem)
  448. {
  449. return $this->dao->select('*')->from(TABLE_TODO)
  450. ->where($queryCondition)
  451. ->beginIF($checkedItem)->andWhere('id')->in($checkedItem)->fi()
  452. ->orderBy($orderBy)->fetchAll('id', false);
  453. }
  454. /**
  455. * 根据待办ID获取多条待办。
  456. * Get todo data by id list.
  457. *
  458. * @param array $todoIdList
  459. * @access public
  460. * @return array
  461. */
  462. public function getTodosByIdList($todoIdList)
  463. {
  464. return $this->dao->select('*')->from(TABLE_TODO)->where('id')->in(array_values($todoIdList))->fetchAll('id', false);
  465. }
  466. /**
  467. * 获取所有有效的周期待办列表。
  468. * Get valid cycle todo list.
  469. *
  470. * @access public
  471. * @return array
  472. */
  473. public function getValidCycleList()
  474. {
  475. return $this->dao->select('*')->from(TABLE_TODO)->where('cycle')->eq(1)->andWhere('deleted')->eq(0)->fetchAll('id', false);
  476. }
  477. /**
  478. * 根据待办类型获取优先级。
  479. * Get pri by todo type.
  480. *
  481. * @param string $todoType
  482. * @param int $todoObjectID
  483. * @access public
  484. * @return int
  485. */
  486. public function getPriByTodoType($todoType, $todoObjectID)
  487. {
  488. if(!isset($this->config->objectTables[$todoType])) return $this->config->todo->defaultPri;
  489. $pri = $this->dao->select('pri')->from($this->config->objectTables[$todoType])->where('id')->eq($todoObjectID)->fetch('pri');
  490. return $pri ?: $this->config->todo->defaultPri;
  491. }
  492. }