zen.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. <?php
  2. /**
  3. * The zen 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. * @link https://www.zentao.net
  9. */
  10. class todoZen extends todo
  11. {
  12. /**
  13. * 生成创建待办视图数据。
  14. * Build create form data.
  15. *
  16. * @param string $date
  17. * @access protected
  18. * @return void
  19. */
  20. protected function buildCreateView($date)
  21. {
  22. $this->view->title = $this->lang->todo->common . $this->lang->hyphen . $this->lang->todo->create;
  23. $this->view->date = date('Y-m-d', strtotime($date));
  24. $this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
  25. $this->view->time = date::now();
  26. $this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
  27. $this->display();
  28. }
  29. /**
  30. * 生成批量创建待办视图数据。
  31. * Build batch create form data.
  32. *
  33. * @param string $date
  34. * @access protected
  35. * @return void
  36. */
  37. protected function buildBatchCreateView($date)
  38. {
  39. /* Set Custom. */
  40. $customFields = array();
  41. foreach(explode(',', $this->config->todo->list->customBatchCreateFields) as $field) $customFields[$field] = $this->lang->todo->$field;
  42. $this->view->customFields = $customFields;
  43. $this->view->showFields = $this->config->todo->custom->batchCreateFields;
  44. $this->view->title = $this->lang->todo->common . $this->lang->hyphen . $this->lang->todo->batchCreate;
  45. $this->view->date = (int)$date == 0 ? $date : date('Y-m-d', strtotime($date));
  46. $this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
  47. $this->view->time = date::now();
  48. $this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
  49. $this->display();
  50. }
  51. /**
  52. * 生成编辑待办视图数据。
  53. * Build create form data.
  54. *
  55. * @param object $todo
  56. * @access protected
  57. * @return void
  58. */
  59. protected function buildEditView($todo)
  60. {
  61. $todo->date = date("Y-m-d", strtotime($todo->date));
  62. $this->view->title = $this->lang->todo->common . $this->lang->hyphen . $this->lang->todo->edit;
  63. $this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
  64. $this->view->todo = $todo;
  65. $this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
  66. $this->display();
  67. }
  68. /**
  69. * 生成指派待办视图数据。
  70. * Build assignTo form data.
  71. *
  72. * @param int $todoID
  73. * @access protected
  74. * @return void
  75. */
  76. protected function buildAssignToView($todoID)
  77. {
  78. $this->view->todo = $this->todo->getByID($todoID);
  79. $this->view->members = $this->loadModel('user')->getPairs('noclosed|noempty|nodeleted');
  80. $this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
  81. $this->view->actions = $this->loadModel('action')->getList('todo', $todoID);
  82. $this->view->users = $this->user->getPairs('noletter');
  83. $this->view->time = date::now();
  84. $this->display();
  85. }
  86. /**
  87. * 处理创建待办的请求数据。
  88. * Processing request data of create.
  89. *
  90. * @param form $form
  91. * @access public
  92. * @return object
  93. */
  94. public function beforeCreate($form)
  95. {
  96. $rawData = $form->data;
  97. $objectType = $rawData->type;
  98. $hasObject = in_array($objectType, $this->config->todo->moduleList);
  99. $objectID = 0;
  100. if($hasObject && $objectType) $objectID = zget($form->rawdata, $objectType, $rawData->objectID);
  101. $rawData->date = !empty($rawData->config['date']) ? $rawData->config['date'] : $rawData->date;
  102. return $form->add('account', $this->app->user->account)
  103. ->setDefault('objectID', 0)
  104. ->setDefault('vision', $this->config->vision)
  105. ->setDefault('assignedTo', $this->app->user->account)
  106. ->setDefault('assignedBy', $this->app->user->account)
  107. ->setDefault('assignedDate', helper::now())
  108. ->cleanInt('pri')
  109. ->setIF($hasObject && $objectType, 'objectID', (int)$objectID)
  110. ->setIF(empty($rawData->date) || $this->post->switchDate, 'date', FUTURE_TIME)
  111. ->setIF(empty($rawData->begin) || $this->post->switchTime, 'begin', '2400')
  112. ->setIF(empty($rawData->begin) || empty($rawData->end) || $this->post->switchTime, 'end', '2400')
  113. ->setIF($rawData->private == 'on', 'private', 1)
  114. ->setIF($rawData->status == 'done', 'finishedBy', $this->app->user->account)
  115. ->setIF($rawData->status == 'done', 'finishedDate', helper::now())
  116. ->stripTags($this->config->todo->editor->create['id'], $this->config->allowedTags)
  117. ->remove(implode(',', $this->config->todo->moduleList) . ',uid')
  118. ->get();
  119. }
  120. /**
  121. * 添加按年循环待办的配置项。
  122. * Adds a yearly cycle of configuration items.
  123. *
  124. * @param form $form
  125. * @access public
  126. * @return form
  127. */
  128. public function addCycleYearConfig($form)
  129. {
  130. /* Only handle cases where you add to the backlog by year. */
  131. if(empty($form->data->config)) return $form;
  132. if(!empty($form->data->config) && $form->data->config['type'] != 'year') return $form;
  133. $form->data->config['type'] = 'day';
  134. $form->data->config['specifiedDate'] = 1;
  135. $form->data->config['cycleYear'] = 1;
  136. return $form;
  137. }
  138. /**
  139. * 准备要创建的todo的数据。
  140. * Prepare the creation data.
  141. *
  142. * @param object $todo
  143. * @access public
  144. * @return object|false
  145. */
  146. public function prepareCreateData($todo)
  147. {
  148. if(!isset($todo->pri) && in_array($todo->type, $this->config->todo->moduleList) && !in_array($todo->type, array('review', 'feedback')))
  149. {
  150. $todo->pri = $this->todo->getPriByTodoType($this->config->objectTables[$todo->type], $todo->objectID);
  151. if($todo->pri == 'high') $todo->pri = 1;
  152. if($todo->pri == 'middle') $todo->pri = 2;
  153. if($todo->pri == 'low') $todo->pri = 3;
  154. }
  155. if($todo->type != 'custom' && !empty($todo->objectID))
  156. {
  157. $type = $todo->type;
  158. $object = $this->loadModel($type)->fetchByID($todo->objectID);
  159. if(isset($object->name)) $todo->name = $object->name;
  160. if(isset($object->title)) $todo->name = $object->title;
  161. }
  162. $isModuleType = isset($todo->type) && in_array($todo->type, $this->config->todo->moduleList);
  163. if($isModuleType && empty($todo->objectID)) dao::$errors[$todo->type] = sprintf($this->lang->error->notempty, $this->lang->todo->name);
  164. if($todo->end < $todo->begin)
  165. {
  166. dao::$errors['end'] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
  167. return false;
  168. }
  169. if(!empty($todo->cycle))
  170. {
  171. $todo = $this->setCycle($todo);
  172. if(!$todo) return false;
  173. }
  174. if(empty($todo->cycle)) unset($todo->config);
  175. if($todo->private) $todo->assignedTo = $todo->assignedBy = $this->app->user->account;
  176. $this->loadModel('file')->processImgURL($todo, $this->config->todo->editor->create['id'], (string)$this->post->uid);
  177. return $todo;
  178. }
  179. /**
  180. * 创建完成待办后数据处理。
  181. * Create a todo after data processing.
  182. *
  183. * @param object $todo
  184. * @param form $form
  185. * @access protected
  186. * @return object
  187. */
  188. protected function afterCreate($todo, $form)
  189. {
  190. if(isset($form->data->uid)) $this->loadModel('file')->updateObjectID($form->data->uid, $todo->id, 'todo');
  191. $this->loadModel('score')->create('todo', 'create', $todo->id);
  192. if(!empty($todo->cycle)) $this->todo->createByCycle(array($todo->id => $todo));
  193. $this->loadModel('action')->create('todo', $todo->id, 'opened');
  194. return $todo;
  195. }
  196. /**
  197. * 处理批量创建待办的请求数据。
  198. * Processing request data of batch create todo.
  199. *
  200. * @param form $form
  201. * @access protected
  202. * @return array|false
  203. */
  204. protected function beforeBatchCreate($form)
  205. {
  206. $todos = $form->cleanInt('pri, begin, end')->get();
  207. foreach($todos as $rawID => $todo)
  208. {
  209. if($todo->end < $todo->begin)
  210. {
  211. dao::$errors["end[{$rawID}]"] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
  212. continue;
  213. }
  214. $account = $this->app->user->account;
  215. $todo->date = $this->post->futureDate ? FUTURE_TIME : $this->post->date;
  216. $todo->account = $account;
  217. $todo->assignedBy = $account;
  218. $todo->assignedDate = helper::now();
  219. if(!empty($todo->switchTime))
  220. {
  221. $todo->begin = '2400';
  222. $todo->end = '2400';
  223. }
  224. if($todo->type != 'custom') $todo->objectID = (int)$todo->name;
  225. if($todo->type != 'custom' && !empty($todo->objectID))
  226. {
  227. $type = $todo->type;
  228. $object = $this->loadModel($type)->fetchByID($todo->objectID);
  229. if(isset($object->name)) $todo->name = $object->name;
  230. if(isset($object->title)) $todo->name = $object->title;
  231. }
  232. unset($todo->switchTime);
  233. }
  234. if(dao::isError()) return false;
  235. return $todos;
  236. }
  237. /**
  238. * 处理编辑待办的请求数据。
  239. * Processing edit request data.
  240. *
  241. * @param int $todoID
  242. * @param form $form
  243. * @access protected
  244. * @return object|false
  245. */
  246. protected function beforeEdit($todoID, $form)
  247. {
  248. $oldTodo = $this->dao->findByID($todoID)->from(TABLE_TODO)->fetch();
  249. $objectID = 0;
  250. $postData = $form->get();
  251. $objectType = !empty($postData->type) ? $postData->type : $oldTodo->type;
  252. $hasObject = in_array($objectType, $this->config->todo->moduleList);
  253. if($hasObject && $objectType) $objectID = $this->post->$objectType ? $this->post->$objectType : $this->post->objectID;
  254. /* Cycle todo date Replaces the todo date. */
  255. $postData->date = !empty($postData->config['date']) ? $postData->config['date'] : $postData->date;
  256. /* Process todo. */
  257. $todo = $form->add('account', $oldTodo->account)
  258. ->cleanInt('pri, begin, end')
  259. ->setIF(in_array($objectType, array('bug', 'task', 'story')), 'name', '')
  260. ->setIF($hasObject && $objectType, 'objectID', $objectID)
  261. ->setIF(empty($postData->date) || $this->post->switchDate || $this->post->cycle, 'date', FUTURE_TIME)
  262. ->setIF(empty($postData->begin) || $this->post->dateSwitcher, 'begin', '2400')
  263. ->setIF(empty($postData->end) || $this->post->dateSwitcher, 'end', '2400')
  264. ->setIF($postData->private == 'on', 'private', 1)
  265. ->setDefault('assignedBy', $oldTodo->assignedTo != $this->post->assignedTo ? $this->app->user->account : $oldTodo->assignedBy)
  266. ->setDefault('type', $objectType)
  267. ->stripTags($this->config->todo->editor->edit['id'], $this->config->allowedTags)
  268. ->remove(implode(',', $this->config->todo->moduleList) . ',uid')
  269. ->get();
  270. /* Non-custom type Gets the backlog name based on the type id. */
  271. if(in_array($todo->type, $this->config->todo->moduleList))
  272. {
  273. $type = $todo->type;
  274. $object = $this->loadModel($type)->fetchByID((int)$objectID);
  275. if(isset($object->name)) $todo->name = $object->name;
  276. if(isset($object->title)) $todo->name = $object->title;
  277. }
  278. $requiredFields = isset($todo->type) && in_array($todo->type, $this->config->todo->moduleList) ? str_replace(',name,', ',', ",{$this->config->todo->edit->requiredFields},") : $this->config->todo->edit->requiredFields;
  279. $requiredFields = trim($requiredFields, ',');
  280. foreach(explode(',', $requiredFields) as $field)
  281. {
  282. if(!empty($field) && empty($todo->$field)) dao::$errors[$field] = sprintf($this->lang->error->notempty, $this->lang->todo->$field);
  283. }
  284. if($hasObject && !$objectID)
  285. {
  286. dao::$errors[$todo->type] = sprintf($this->lang->error->notempty, $this->lang->todo->name);
  287. unset(dao::$errors['objectID']);
  288. }
  289. if($todo->end < $todo->begin) dao::$errors['end'] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
  290. if(dao::isError()) return false;
  291. /* Handle cycle configuration item. */
  292. if(!empty($oldTodo->cycle)) $this->handleCycleConfig($todo);
  293. if(empty($oldTodo->cycle)) $todo->config = '';
  294. if($todo->private) $todo->assignedTo = $todo->assignedBy = $this->app->user->account;
  295. return $this->loadModel('file')->processImgURL($todo, $this->config->todo->editor->edit['id'], $this->post->uid);
  296. }
  297. /**
  298. * 编辑完成待办后数据处理。
  299. * Handle data after edit todo.
  300. *
  301. * @param object $todo
  302. * @access protected
  303. * @return void
  304. * @param int $todoID
  305. * @param mixed[] $changes
  306. */
  307. protected function afterEdit($todoID, $changes)
  308. {
  309. if(empty($changes)) return;
  310. $actionID = $this->loadModel('action')->create('todo', $todoID, 'edited');
  311. $this->action->logHistory($actionID, $changes);
  312. }
  313. /**
  314. * 批量编辑页面渲染。
  315. * Batch edit view display.
  316. *
  317. * @param array|false $todoIdList
  318. * @param string $type
  319. * @param int $userID
  320. * @param string $status
  321. * @access protected
  322. * @return void
  323. */
  324. protected function batchEditFromMyTodo($todoIdList, $type, $userID, $status)
  325. {
  326. /* Initialize vars. */
  327. if(empty($todoIdList)) $todoIdList = array();
  328. $editedTodos = $objectIdList = $reviews = array();
  329. $columns = 7;
  330. unset($this->lang->todo->typeList['cycle']);
  331. if(empty($userID)) $userID = $this->app->user->id;
  332. $user = $this->loadModel('user')->getById($userID, 'id');
  333. $account = $user->account;
  334. list($editedTodos, $objectIdList) = $this->getBatchEditInitTodos($todoIdList, $type, $account, $status);
  335. $editedTodos = array_map(function($item) { $item->begin = str_replace(':', '', $item->begin); $item->end = str_replace(':', '', $item->end); return $item;}, $editedTodos);
  336. $bugs = $this->loadModel('bug')->getUserBugPairs($account, true, 0, array(), array(), isset($objectIdList['bug']) ? $objectIdList['bug'] : array());
  337. $tasks = $this->loadModel('task')->getUserTaskPairs($account, 'wait,doing', array(), isset($objectIdList['task']) ? $objectIdList['task'] : array());
  338. $stories = $this->loadModel('story')->getUserStoryPairs($account, 10, 'story', '', isset($objectIdList['story']) ? $objectIdList['story'] : array());
  339. $epics = $this->loadModel('story')->getUserStoryPairs($account, 10, 'epic', '', isset($objectIdList['epic']) ? $objectIdList['epic'] : array());
  340. $requirements = $this->loadModel('story')->getUserStoryPairs($account, 10, 'requirement', '', isset($objectIdList['requirement']) ? $objectIdList['requirement'] : array());
  341. $users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
  342. $testtasks = $this->loadModel('testtask')->getUserTestTaskPairs($account);
  343. if($this->config->edition != 'open') $feedbacks = $this->loadModel('feedback')->getUserFeedbackPairs($account, '', isset($objectIdList['feedback']) ? $objectIdList['feedback'] : '');
  344. if(in_array($this->config->edition, array('max', 'ipd')))
  345. {
  346. $issues = $this->loadModel('issue')->getUserIssuePairs($account);
  347. $risks = $this->loadmodel('risk')->getUserRiskPairs($account);
  348. $opportunities = $this->loadmodel('opportunity')->getUserOpportunityPairs($account);
  349. $reviewItems = $this->loadModel('my')->getReviewingList('all');
  350. foreach($reviewItems as $review) $reviews[$review->id] = $review->title;
  351. }
  352. /* Judge whether the edited todos is too large. */
  353. $countInputVars = count($editedTodos) * $columns;
  354. $showSuhosinInfo = common::judgeSuhosinSetting($countInputVars);
  355. if($showSuhosinInfo) $this->view->suhosinInfo = extension_loaded('suhosin') ? sprintf($this->lang->suhosinInfo, $countInputVars) : sprintf($this->lang->maxVarsInfo, $countInputVars);
  356. $this->view->bugs = $bugs;
  357. $this->view->tasks = $tasks;
  358. $this->view->stories = $stories;
  359. $this->view->epics = $epics;
  360. $this->view->requirements = $requirements;
  361. $this->view->reviews = $reviews;
  362. $this->view->testtasks = $testtasks;
  363. $this->view->editedTodos = $editedTodos;
  364. $this->view->users = $users;
  365. $this->view->type = $type;
  366. $this->view->userID = $userID;
  367. $this->view->status = $status;
  368. if($this->config->edition != 'open') $this->view->feedbacks = $feedbacks;
  369. if(in_array($this->config->edition, array('max', 'ipd')))
  370. {
  371. $this->view->issues = $issues;
  372. $this->view->risks = $risks;
  373. $this->view->opportunities = $opportunities;
  374. }
  375. $this->buildBatchEditView();
  376. }
  377. /**
  378. * 获取批量编辑页面初始化待办数据。
  379. * Get batch edit page initialization todo data.
  380. *
  381. * @param array $todoIdList
  382. * @param string $type
  383. * @param string $account
  384. * @param string $status
  385. * @access protected
  386. * @return array
  387. */
  388. private function getBatchEditInitTodos($todoIdList, $type, $account, $status)
  389. {
  390. $editedTodos = array();
  391. $objectIdList = array();
  392. $allTodos = $this->todo->getList($type, $account, $status);
  393. if($this->post->todoIdList) $todoIdList = $this->post->todoIdList;
  394. /* Initialize todos whose need to edited. */
  395. foreach($allTodos as $todo)
  396. {
  397. if(in_array($todo->id, $todoIdList))
  398. {
  399. $editedTodos[$todo->id] = $todo;
  400. if($todo->type != 'custom')
  401. {
  402. if(!isset($objectIdList[$todo->type])) $objectIdList[$todo->type] = array();
  403. $objectIdList[$todo->type][$todo->objectID] = $todo->objectID;
  404. }
  405. }
  406. }
  407. return array($editedTodos, $objectIdList);
  408. }
  409. /**
  410. * 生成批量创建待办视图数据。
  411. * Build batch edit view.
  412. *
  413. * @access private
  414. * @return void
  415. */
  416. private function buildBatchEditView()
  417. {
  418. /* Set Custom*/
  419. foreach(explode(',', $this->config->todo->list->customBatchEditFields) as $field) $customFields[$field] = $this->lang->todo->$field;
  420. $this->view->customFields = $customFields;
  421. $this->view->showFields = $this->config->todo->custom->batchEditFields;
  422. $this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
  423. $this->view->time = date::now();
  424. $this->view->title = $this->lang->todo->common . $this->lang->hyphen . $this->lang->todo->batchEdit;
  425. $this->display();
  426. }
  427. /**
  428. * 处理批量编辑待办数据。
  429. * Build batch edit view.
  430. *
  431. * @param array $todos
  432. * @access protected
  433. * @return array
  434. */
  435. protected function beforeBatchEdit($todos)
  436. {
  437. if(empty($todos)) return array();
  438. /* Initialize todos from the post data. */
  439. foreach($todos as $todoID => $todo)
  440. {
  441. if(in_array($todo->type, $this->config->todo->moduleList))
  442. {
  443. $todo->objectID = $todo->{$todo->type};
  444. $todo->name = '';
  445. if(empty($todo->objectID)) dao::$errors["{$todo->type}[{$todoID}]"] = sprintf($this->lang->error->notempty, $this->lang->todo->name);
  446. }
  447. elseif(empty($todo->name))
  448. {
  449. dao::$errors["name[{$todoID}]"] = sprintf($this->lang->error->notempty, $this->lang->todo->name);
  450. }
  451. unset($todo->story, $todo->epic, $todo->requirement, $todo->task, $todo->bug, $todo->testtask, $todo->feedback, $todo->issue, $todo->risk, $todo->opportunity, $todo->review);
  452. $todo->begin = empty($todo->begin) || $this->post->switchTime ? 2400 : $todo->begin;
  453. $todo->end = empty($todo->end) || $this->post->switchTime ? 2400 : $todo->end;
  454. if($todo->end < $todo->begin) dao::$errors["begin[{$todoID}]"] = sprintf($this->lang->error->gt, $this->lang->todo->end, $this->lang->todo->begin);
  455. }
  456. return $todos;
  457. }
  458. /**
  459. * 批量编辑完成待办后数据处理。
  460. * After Batch edit todo data.
  461. *
  462. * @param array $allChanges
  463. * @access protected
  464. * @return void
  465. */
  466. protected function afterBatchEdit($allChanges)
  467. {
  468. foreach($allChanges as $todoID => $changes)
  469. {
  470. if(empty($changes)) continue;
  471. $actionID = $this->loadModel('action')->create('todo', $todoID, 'edited');
  472. $this->action->logHistory($actionID, $changes);
  473. }
  474. }
  475. /**
  476. * 处理周期待办的配置值。
  477. * Handle cycle config.
  478. *
  479. * @param object $todo
  480. * @access private
  481. * @return void
  482. */
  483. private function handleCycleConfig($todo)
  484. {
  485. $todo->date = date('Y-m-d');
  486. $todo->config['begin'] = $todo->date;
  487. if($todo->config['type'] == 'day') unset($todo->config['week'], $todo->config['month']);
  488. if($todo->config['type'] == 'week')
  489. {
  490. unset($todo->config['day'], $todo->config['month']);
  491. if(!is_array($todo->config['week'])) $todo->config['week'] = (array)$todo->config['week'];
  492. $todo->config['week'] = implode(',', $todo->config['week']);
  493. }
  494. if($todo->config['type'] == 'month')
  495. {
  496. unset($todo->config['day'], $todo->config['week']);
  497. if(!is_array($todo->config['month'])) $todo->config['month'] = (array)$todo->config['month'];
  498. $todo->config['month'] = implode(',', $todo->config['month']);
  499. }
  500. $todo->config['beforeDays'] = !empty($todo->config['beforeDays']) ? $todo->config['beforeDays'] : 0;
  501. $todo->config = json_encode($todo->config);
  502. }
  503. /**
  504. * 设置周期待办数据。
  505. * Set cycle todo data.
  506. *
  507. * @param object $todoData
  508. * @access private
  509. * @return false|object
  510. */
  511. private function setCycle($todoData)
  512. {
  513. if(helper::isZeroDate($todoData->date)) $todoData->date = helper::today();
  514. $todoData->config['begin'] = $todoData->date;
  515. if($todoData->config['type'] == 'day')
  516. {
  517. unset($todoData->config['week'], $todoData->config['month']);
  518. if(empty($todoData->config['specifiedDate']))
  519. {
  520. if(empty($todoData->config['day']))
  521. {
  522. dao::$errors['config[day]'] = sprintf($this->lang->error->notempty, $this->lang->todo->cycleDaysLabel);
  523. return false;
  524. }
  525. if(!validater::checkInt($todoData->config['day']))
  526. {
  527. dao::$errors['config[day]'] = sprintf($this->lang->error->int[0], $this->lang->todo->cycleDaysLabel);
  528. return false;
  529. }
  530. }
  531. else
  532. {
  533. unset($todoData->config['day']);
  534. }
  535. }
  536. if($todoData->config['type'] == 'week')
  537. {
  538. unset($todoData->config['day'], $todoData->config['month']);
  539. if(!is_array($todoData->config['week'])) $todoData->config['week'] = (array)$todoData->config['week'];
  540. $todoData->config['week'] = implode(',', $todoData->config['week']);
  541. if(empty($todoData->config['week']))
  542. {
  543. dao::$errors['config[week][]'] = sprintf($this->lang->error->notempty, $this->lang->todo->weekly);
  544. return false;
  545. }
  546. }
  547. if($todoData->config['type'] == 'month')
  548. {
  549. unset($todoData->config['day'], $todoData->config['week']);
  550. if(!is_array($todoData->config['month'])) $todoData->config['month'] = (array)$todoData->config['month'];
  551. $todoData->config['month'] = implode(',', $todoData->config['month']);
  552. if(empty($todoData->config['month']))
  553. {
  554. dao::$errors['config[month][]'] = sprintf($this->lang->error->notempty, $this->lang->todo->monthly);
  555. return false;
  556. }
  557. }
  558. if(!empty($todoData->config['beforeDays']) && !validater::checkInt($todoData->config['beforeDays']))
  559. {
  560. dao::$errors['config[beforeDays]'] = sprintf($this->lang->error->int[0], $this->lang->todo->beforeDaysLabel);
  561. return false;
  562. }
  563. $todoData->config['beforeDays'] = !empty($todoData->config['beforeDays']) ? $todoData->config['beforeDays'] : 0;
  564. $todoData->config = json_encode($todoData->config);
  565. $todoData->type = 'cycle';
  566. return $todoData;
  567. }
  568. /**
  569. * 输出开启待办事项的确认弹框。
  570. * Output start todo confirm alert.
  571. *
  572. * @param object $todo
  573. * @access protected
  574. * @return int
  575. */
  576. protected function printStartConfirm($todo)
  577. {
  578. $confirmNote = 'confirm' . ucfirst($todo->type);
  579. if($this->config->vision == 'or' && $todo->type == 'task') $todo->type = 'researchtask';
  580. if($todo->type == 'bug') $app = 'qa';
  581. if($todo->type == 'task') $app = 'execution';
  582. if($todo->type == 'researchtask') $app = 'market';
  583. if($todo->type == 'story') $app = 'product';
  584. $confirmURL = $this->createLink($todo->type, 'view', "id=$todo->objectID");
  585. $message = sprintf($this->lang->todo->{$confirmNote}, $todo->objectID);
  586. return $this->send(array('result' => 'success', 'callback' => "zui.Modal.confirm({message: '{$message}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'}).then((res) => {if(res) openPage('{$confirmURL}', '{$app}'); else reloadPage();});"));
  587. }
  588. /**
  589. * 根据模型获取项目, 以键值对格式返回。
  590. * Get product pairs id=>name by model.
  591. *
  592. * @param string $model
  593. * @return array
  594. */
  595. protected function getProjectPairsByModel($model)
  596. {
  597. $model = $model == 'opportunity' ? 'waterfall' : 'all';
  598. return $this->loadModel('project')->getPairsByModel($model);
  599. }
  600. /**
  601. * 生成待办视图详情数据。
  602. * Build assign to todo view
  603. *
  604. * @param object $todo
  605. * @param int $projectID
  606. * @param array $project
  607. * @param string $account
  608. * @param string $from
  609. * @access protected
  610. * @return mixed
  611. * @param mixed[] $projects
  612. */
  613. protected function buildAssignToTodoView($todo, $projectID, $projects, $account, $from)
  614. {
  615. $this->loadModel('user');
  616. $this->loadModel('product');
  617. $executionPairs = array();
  618. $executions = $projects ? $this->loadModel('execution')->getByProject(key($projects), 'undone') : array();
  619. foreach($executions as $execution) $executionPairs[$execution->id] = $execution->name;
  620. $this->view->title = $account == $todo->account ? "{$this->lang->todo->common} #$todo->id $todo->name" : $this->lang->todo->common;
  621. $this->view->user = $this->user->getByID((string)$todo->account);
  622. $this->view->users = $this->user->getPairs('noletter');
  623. $this->view->actions = $this->loadModel('action')->getList('todo', (int)$todo->id);
  624. $this->view->todo = $todo;
  625. $this->view->times = date::buildTimeList((int)$this->config->todo->times->begin, (int)$this->config->todo->times->end, 5);
  626. $this->view->from = $from;
  627. $this->view->projects = $projects;
  628. $this->view->executions = $executionPairs;
  629. $this->view->products = $todo->type == 'opportunity' ? $this->product->getPairsByProjectModel('waterfall') : $this->product->getPairs();
  630. $this->view->projectProducts = $this->product->getProductPairsByProject($projectID);
  631. $this->display();
  632. }
  633. /**
  634. * 处理指派待办请求数据。
  635. * Process assign todo request data.
  636. *
  637. * @param object $formData
  638. * @access protected
  639. * @return object
  640. */
  641. protected function beforeAssignTo($formData)
  642. {
  643. $formData = $formData->get();
  644. $formData->assignedBy = $this->app->user->account;
  645. $formData->assignedDate = helper::now();
  646. if($this->post->future) $formData->date = '2030-01-01';
  647. if($this->post->lblDisableDate)
  648. {
  649. $formData->begin = '2400';
  650. $formData->end = '2400';
  651. }
  652. return $formData;
  653. }
  654. /**
  655. * 指派待办。
  656. * Assign a todo.
  657. *
  658. * @param object $todo
  659. * @access protected
  660. * @return bool
  661. */
  662. protected function doAssignTo($todo)
  663. {
  664. return $this->todo->assignTo($todo);
  665. }
  666. /**
  667. * 获取用户信息。
  668. * Get user info.
  669. *
  670. * @param int $userID
  671. * @access protected
  672. * @return object|false
  673. */
  674. protected function getUserById($userID)
  675. {
  676. return $this->loadModel('user')->getById($userID, 'id');
  677. }
  678. /**
  679. * 记录当前页面uri参数链接。
  680. * Set uri to session.
  681. *
  682. * @param string $uri
  683. * @access protected
  684. * @return true
  685. */
  686. protected function setSessionUri($uri)
  687. {
  688. foreach($this->config->todo->sessionUri as $key => $value) $this->session->set($key, $uri, $value);
  689. return true;
  690. }
  691. /**
  692. * 获取导出待办的字段和字段。
  693. * Get fields and info for export todo.
  694. *
  695. * @param array $todos
  696. * @param string $fields
  697. * @param object $todoLang
  698. * @access protected
  699. * @return array
  700. */
  701. protected function exportTodoInfo($todos, $fields, $todoLang)
  702. {
  703. $fields = explode(',', $fields);
  704. foreach($fields as $key => $fieldName)
  705. {
  706. $fieldName = trim($fieldName);
  707. $fields[$fieldName] = isset($todoLang->$fieldName) ? $todoLang->$fieldName : $fieldName;
  708. unset($fields[$key]);
  709. }
  710. unset($fields['objectID'], $fields['private']);
  711. if($this->config->edition != 'open') list($fields, $todos) = $this->loadModel('workflowfield')->appendDataFromFlow($fields, $todos);
  712. return array($todos, $fields);
  713. }
  714. /**
  715. * 获取待办关联的信息。
  716. * Get associated info for export todo.
  717. *
  718. * @param string $type
  719. * @param string $account
  720. * @access protected
  721. * @return array
  722. */
  723. protected function exportAssociated($type, $account)
  724. {
  725. if($type == 'max')
  726. {
  727. return array(
  728. $this->loadModel('issue')->getUserIssuePairs($account),
  729. $this->loadModel('risk')->getUserRiskPairs($account),
  730. $this->loadModel('opportunity')->getUserOpportunityPairs($account),
  731. );
  732. }
  733. elseif($type == 'qcVersion')
  734. {
  735. return $this->loadModel('review')->getUserReviewPairs($account, 0, 'wait');
  736. }
  737. else
  738. {
  739. return array(
  740. $this->loadModel('user')->getPairs('noletter'),
  741. $this->loadModel('bug')->getUserBugPairs($account),
  742. $this->loadModel('story')->getUserStoryPairs($account, 100, 'story'),
  743. $this->loadModel('story')->getUserStoryPairs($account, 100, 'epic'),
  744. $this->loadModel('story')->getUserStoryPairs($account, 100, 'requirement'),
  745. $this->loadModel('task')->getUserTaskPairs($account),
  746. $this->loadModel('testtask')->getUserTesttaskPairs($account),
  747. );
  748. }
  749. }
  750. /**
  751. * 处理导出数据。
  752. * Deal with export data.
  753. *
  754. * @param array $todos
  755. * @param object $assemble
  756. * @param object $todoLang
  757. * @param array $times
  758. * @access protected
  759. * @return array
  760. */
  761. protected function assembleExportData($todos, $assemble, $todoLang, $times)
  762. {
  763. foreach($todos as $todo)
  764. {
  765. $begin = isset($times[$todo->begin]) ? $times[$todo->begin] : $todo->begin;
  766. $end = isset($times[$todo->end]) ? $times[$todo->end] : $todo->end;
  767. /* fill some field with useful value. */
  768. $todo->begin = $todo->begin == '2400' ? '' : $begin;
  769. $todo->end = $todo->end == '2400' ? '' : $end;
  770. $todo->assignedTo = zget($assemble->users, $todo->assignedTo);
  771. if($todo->date == '2030-01-01') $todo->date = $this->lang->todo->future;
  772. $type = $todo->type;
  773. if(isset($assemble->users[$todo->account])) $todo->account = $assemble->users[$todo->account];
  774. if($type == 'bug') $todo->name = isset($assemble->bugs[$todo->objectID]) ? $assemble->bugs[$todo->objectID] . "(#$todo->objectID)" : '';
  775. if($type == 'task') $todo->name = isset($assemble->tasks[$todo->objectID]) ? $assemble->tasks[$todo->objectID] . "(#$todo->objectID)" : '';
  776. if($type == 'story') $todo->name = isset($assemble->stories[$todo->objectID]) ? $assemble->stories[$todo->objectID] . "(#$todo->objectID)" : '';
  777. if($type == 'epic') $todo->name = isset($assemble->epics[$todo->objectID]) ? $assemble->epics[$todo->objectID] . "(#$todo->objectID)" : '';
  778. if($type == 'requirement') $todo->name = isset($assemble->requirements[$todo->objectID]) ? $assemble->requirements[$todo->objectID] . "(#$todo->objectID)" : '';
  779. if($type == 'testtask') $todo->name = isset($assemble->testTasks[$todo->objectID]) ? $assemble->testTasks[$todo->objectID] . "(#$todo->objectID)" : '';
  780. if(in_array($this->config->edition, array('max', 'ipd')))
  781. {
  782. if($type == 'issue') $todo->name = isset($assemble->issues[$todo->objectID]) ? $assemble->issues[$todo->objectID] . "(#$todo->objectID)" : '';
  783. if($type == 'risk') $todo->name = isset($assemble->risks[$todo->objectID]) ? $assemble->risks[$todo->objectID] . "(#$todo->objectID)" : '';
  784. if($type == 'opportunity') $todo->name = isset($assemble->opportunities[$todo->objectID]) ? $assemble->opportunities[$todo->objectID] . "(#$todo->objectID)" : '';
  785. }
  786. if(isset($todoLang->typeList[$type])) $todo->type = $todoLang->typeList[$type];
  787. if(isset($todoLang->priList[$todo->pri])) $todo->pri = $todoLang->priList[$todo->pri];
  788. if(isset($todoLang->statusList[$todo->status])) $todo->status = $todoLang->statusList[$todo->status];
  789. if($todo->private == 1) $todo->desc = $this->lang->todo->thisIsPrivate;
  790. /* drop some field that is not needed. */
  791. unset($todo->objectID, $todo->private);
  792. }
  793. return $todos;
  794. }
  795. }