/app/Model/Swimlane.php

https://gitlab.com/x33n/kanboard · PHP · 561 lines · 281 code · 76 blank · 204 comment · 16 complexity · 7d4add41ad70726f962800fb05c863f0 MD5 · raw file

  1. <?php
  2. namespace Model;
  3. use SimpleValidator\Validator;
  4. use SimpleValidator\Validators;
  5. /**
  6. * Swimlanes
  7. *
  8. * @package model
  9. * @author Frederic Guillot
  10. */
  11. class Swimlane extends Base
  12. {
  13. /**
  14. * SQL table name
  15. *
  16. * @var string
  17. */
  18. const TABLE = 'swimlanes';
  19. /**
  20. * Value for active swimlanes
  21. *
  22. * @var integer
  23. */
  24. const ACTIVE = 1;
  25. /**
  26. * Value for inactive swimlanes
  27. *
  28. * @var integer
  29. */
  30. const INACTIVE = 0;
  31. /**
  32. * Get a swimlane by the id
  33. *
  34. * @access public
  35. * @param integer $swimlane_id Swimlane id
  36. * @return array
  37. */
  38. public function getById($swimlane_id)
  39. {
  40. return $this->db->table(self::TABLE)->eq('id', $swimlane_id)->findOne();
  41. }
  42. /**
  43. * Get the swimlane name by the id
  44. *
  45. * @access public
  46. * @param integer $swimlane_id Swimlane id
  47. * @return string
  48. */
  49. public function getNameById($swimlane_id)
  50. {
  51. return $this->db->table(self::TABLE)->eq('id', $swimlane_id)->findOneColumn('name') ?: '';
  52. }
  53. /**
  54. * Get a swimlane id by the project and the name
  55. *
  56. * @access public
  57. * @param integer $project_id Project id
  58. * @param string $name Name
  59. * @return integer
  60. */
  61. public function getIdByName($project_id, $name)
  62. {
  63. return (int) $this->db->table(self::TABLE)
  64. ->eq('project_id', $project_id)
  65. ->eq('name', $name)
  66. ->findOneColumn('id');
  67. }
  68. /**
  69. * Get a swimlane by the project and the name
  70. *
  71. * @access public
  72. * @param integer $project_id Project id
  73. * @param string $name Swimlane name
  74. * @return array
  75. */
  76. public function getByName($project_id, $name)
  77. {
  78. return $this->db->table(self::TABLE)
  79. ->eq('project_id', $project_id)
  80. ->eq('name', $name)
  81. ->findOne();
  82. }
  83. /**
  84. * Get default swimlane properties
  85. *
  86. * @access public
  87. * @param integer $project_id Project id
  88. * @return array
  89. */
  90. public function getDefault($project_id)
  91. {
  92. $result = $this->db->table(Project::TABLE)
  93. ->eq('id', $project_id)
  94. ->columns('id', 'default_swimlane', 'show_default_swimlane')
  95. ->findOne();
  96. if ($result['default_swimlane'] === 'Default swimlane') {
  97. $result['default_swimlane'] = t($result['default_swimlane']);
  98. }
  99. return $result;
  100. }
  101. /**
  102. * Get all swimlanes for a given project
  103. *
  104. * @access public
  105. * @param integer $project_id Project id
  106. * @return array
  107. */
  108. public function getAll($project_id)
  109. {
  110. return $this->db->table(self::TABLE)
  111. ->eq('project_id', $project_id)
  112. ->orderBy('position', 'asc')
  113. ->findAll();
  114. }
  115. /**
  116. * Get the list of swimlanes by status
  117. *
  118. * @access public
  119. * @param integer $project_id Project id
  120. * @param integer $status Status
  121. * @return array
  122. */
  123. public function getAllByStatus($project_id, $status = self::ACTIVE)
  124. {
  125. $query = $this->db->table(self::TABLE)
  126. ->eq('project_id', $project_id)
  127. ->eq('is_active', $status);
  128. if ($status == self::ACTIVE) {
  129. $query->asc('position');
  130. }
  131. else {
  132. $query->asc('name');
  133. }
  134. return $query->findAll();
  135. }
  136. /**
  137. * Get active swimlanes
  138. *
  139. * @access public
  140. * @param integer $project_id Project id
  141. * @return array
  142. */
  143. public function getSwimlanes($project_id)
  144. {
  145. $swimlanes = $this->db->table(self::TABLE)
  146. ->columns('id', 'name')
  147. ->eq('project_id', $project_id)
  148. ->eq('is_active', self::ACTIVE)
  149. ->orderBy('position', 'asc')
  150. ->findAll();
  151. $default_swimlane = $this->db->table(Project::TABLE)
  152. ->eq('id', $project_id)
  153. ->eq('show_default_swimlane', 1)
  154. ->findOneColumn('default_swimlane');
  155. if ($default_swimlane) {
  156. if ($default_swimlane === 'Default swimlane') {
  157. $default_swimlane = t($default_swimlane);
  158. }
  159. array_unshift($swimlanes, array('id' => 0, 'name' => $default_swimlane));
  160. }
  161. return $swimlanes;
  162. }
  163. /**
  164. * Get list of all swimlanes
  165. *
  166. * @access public
  167. * @param integer $project_id Project id
  168. * @param boolean $prepend Prepend default value
  169. * @param boolean $only_active Return only active swimlanes
  170. * @return array
  171. */
  172. public function getList($project_id, $prepend = false, $only_active = false)
  173. {
  174. $swimlanes = array();
  175. $default = $this->db->table(Project::TABLE)->eq('id', $project_id)->eq('show_default_swimlane', 1)->findOneColumn('default_swimlane');
  176. if ($prepend) {
  177. $swimlanes[-1] = t('All swimlanes');
  178. }
  179. if (! empty($default)) {
  180. $swimlanes[0] = $default === 'Default swimlane' ? t($default) : $default;
  181. }
  182. return $swimlanes + $this->db->hashtable(self::TABLE)
  183. ->eq('project_id', $project_id)
  184. ->in('is_active', $only_active ? array(self::ACTIVE) : array(self::ACTIVE, self::INACTIVE))
  185. ->orderBy('position', 'asc')
  186. ->getAll('id', 'name');
  187. }
  188. /**
  189. * Add a new swimlane
  190. *
  191. * @access public
  192. * @param integer $project_id
  193. * @param string $name
  194. * @return integer|boolean
  195. */
  196. public function create($project_id, $name)
  197. {
  198. return $this->persist(self::TABLE, array(
  199. 'project_id' => $project_id,
  200. 'name' => $name,
  201. 'position' => $this->getLastPosition($project_id),
  202. ));
  203. }
  204. /**
  205. * Rename a swimlane
  206. *
  207. * @access public
  208. * @param integer $swimlane_id Swimlane id
  209. * @param string $name Swimlane name
  210. * @return bool
  211. */
  212. public function rename($swimlane_id, $name)
  213. {
  214. return $this->db->table(self::TABLE)
  215. ->eq('id', $swimlane_id)
  216. ->update(array('name' => $name));
  217. }
  218. /**
  219. * Update the default swimlane
  220. *
  221. * @access public
  222. * @param array $values Form values
  223. * @return bool
  224. */
  225. public function updateDefault(array $values)
  226. {
  227. return $this->db
  228. ->table(Project::TABLE)
  229. ->eq('id', $values['id'])
  230. ->update(array(
  231. 'default_swimlane' => $values['default_swimlane'],
  232. 'show_default_swimlane' => $values['show_default_swimlane'],
  233. ));
  234. }
  235. /**
  236. * Get the last position of a swimlane
  237. *
  238. * @access public
  239. * @param integer $project_id
  240. * @return integer
  241. */
  242. public function getLastPosition($project_id)
  243. {
  244. return $this->db->table(self::TABLE)
  245. ->eq('project_id', $project_id)
  246. ->eq('is_active', 1)
  247. ->count() + 1;
  248. }
  249. /**
  250. * Disable a swimlane
  251. *
  252. * @access public
  253. * @param integer $project_id Project id
  254. * @param integer $swimlane_id Swimlane id
  255. * @return bool
  256. */
  257. public function disable($project_id, $swimlane_id)
  258. {
  259. $result = $this->db
  260. ->table(self::TABLE)
  261. ->eq('id', $swimlane_id)
  262. ->update(array(
  263. 'is_active' => self::INACTIVE,
  264. 'position' => 0,
  265. ));
  266. if ($result) {
  267. // Re-order positions
  268. $this->updatePositions($project_id);
  269. }
  270. return $result;
  271. }
  272. /**
  273. * Enable a swimlane
  274. *
  275. * @access public
  276. * @param integer $project_id Project id
  277. * @param integer $swimlane_id Swimlane id
  278. * @return bool
  279. */
  280. public function enable($project_id, $swimlane_id)
  281. {
  282. return $this->db
  283. ->table(self::TABLE)
  284. ->eq('id', $swimlane_id)
  285. ->update(array(
  286. 'is_active' => self::ACTIVE,
  287. 'position' => $this->getLastPosition($project_id),
  288. ));
  289. }
  290. /**
  291. * Remove a swimlane
  292. *
  293. * @access public
  294. * @param integer $project_id Project id
  295. * @param integer $swimlane_id Swimlane id
  296. * @return bool
  297. */
  298. public function remove($project_id, $swimlane_id)
  299. {
  300. $this->db->startTransaction();
  301. // Tasks should not be assigned anymore to this swimlane
  302. $this->db->table(Task::TABLE)->eq('swimlane_id', $swimlane_id)->update(array('swimlane_id' => 0));
  303. if (! $this->db->table(self::TABLE)->eq('id', $swimlane_id)->remove()) {
  304. $this->db->cancelTransaction();
  305. return false;
  306. }
  307. // Re-order positions
  308. $this->updatePositions($project_id);
  309. $this->db->closeTransaction();
  310. return true;
  311. }
  312. /**
  313. * Update swimlane positions after disabling or removing a swimlane
  314. *
  315. * @access public
  316. * @param integer $project_id Project id
  317. * @return boolean
  318. */
  319. public function updatePositions($project_id)
  320. {
  321. $position = 0;
  322. $swimlanes = $this->db->table(self::TABLE)
  323. ->eq('project_id', $project_id)
  324. ->eq('is_active', 1)
  325. ->asc('position')
  326. ->findAllByColumn('id');
  327. if (! $swimlanes) {
  328. return false;
  329. }
  330. foreach ($swimlanes as $swimlane_id) {
  331. $this->db->table(self::TABLE)
  332. ->eq('id', $swimlane_id)
  333. ->update(array('position' => ++$position));
  334. }
  335. return true;
  336. }
  337. /**
  338. * Move a swimlane down, increment the position value
  339. *
  340. * @access public
  341. * @param integer $project_id Project id
  342. * @param integer $swimlane_id Swimlane id
  343. * @return boolean
  344. */
  345. public function moveDown($project_id, $swimlane_id)
  346. {
  347. $swimlanes = $this->db->hashtable(self::TABLE)
  348. ->eq('project_id', $project_id)
  349. ->eq('is_active', self::ACTIVE)
  350. ->asc('position')
  351. ->getAll('id', 'position');
  352. $positions = array_flip($swimlanes);
  353. if (isset($swimlanes[$swimlane_id]) && $swimlanes[$swimlane_id] < count($swimlanes)) {
  354. $position = ++$swimlanes[$swimlane_id];
  355. $swimlanes[$positions[$position]]--;
  356. $this->db->startTransaction();
  357. $this->db->table(self::TABLE)->eq('id', $swimlane_id)->update(array('position' => $position));
  358. $this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $swimlanes[$positions[$position]]));
  359. $this->db->closeTransaction();
  360. return true;
  361. }
  362. return false;
  363. }
  364. /**
  365. * Move a swimlane up, decrement the position value
  366. *
  367. * @access public
  368. * @param integer $project_id Project id
  369. * @param integer $swimlane_id Swimlane id
  370. * @return boolean
  371. */
  372. public function moveUp($project_id, $swimlane_id)
  373. {
  374. $swimlanes = $this->db->hashtable(self::TABLE)
  375. ->eq('project_id', $project_id)
  376. ->eq('is_active', self::ACTIVE)
  377. ->asc('position')
  378. ->getAll('id', 'position');
  379. $positions = array_flip($swimlanes);
  380. if (isset($swimlanes[$swimlane_id]) && $swimlanes[$swimlane_id] > 1) {
  381. $position = --$swimlanes[$swimlane_id];
  382. $swimlanes[$positions[$position]]++;
  383. $this->db->startTransaction();
  384. $this->db->table(self::TABLE)->eq('id', $swimlane_id)->update(array('position' => $position));
  385. $this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $swimlanes[$positions[$position]]));
  386. $this->db->closeTransaction();
  387. return true;
  388. }
  389. return false;
  390. }
  391. /**
  392. * Duplicate Swimlane to project
  393. *
  394. * @access public
  395. * @param integer $project_from Project Template
  396. * @param integer $project_to Project that receives the copy
  397. * @return integer|boolean
  398. */
  399. public function duplicate($project_from, $project_to)
  400. {
  401. $swimlanes = $this->getAll($project_from);
  402. foreach ($swimlanes as $swimlane) {
  403. unset($swimlane['id']);
  404. $swimlane['project_id'] = $project_to;
  405. if (! $this->db->table(self::TABLE)->save($swimlane)) {
  406. return false;
  407. }
  408. }
  409. $default_swimlane = $this->getDefault($project_from);
  410. $default_swimlane['id'] = $project_to;
  411. $this->updateDefault($default_swimlane);
  412. return true;
  413. }
  414. /**
  415. * Validate creation
  416. *
  417. * @access public
  418. * @param array $values Form values
  419. * @return array $valid, $errors [0] = Success or not, [1] = List of errors
  420. */
  421. public function validateCreation(array $values)
  422. {
  423. $rules = array(
  424. new Validators\Required('project_id', t('The project id is required')),
  425. new Validators\Required('name', t('The name is required')),
  426. );
  427. $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
  428. return array(
  429. $v->execute(),
  430. $v->getErrors()
  431. );
  432. }
  433. /**
  434. * Validate modification
  435. *
  436. * @access public
  437. * @param array $values Form values
  438. * @return array $valid, $errors [0] = Success or not, [1] = List of errors
  439. */
  440. public function validateModification(array $values)
  441. {
  442. $rules = array(
  443. new Validators\Required('id', t('The id is required')),
  444. new Validators\Required('name', t('The name is required')),
  445. );
  446. $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
  447. return array(
  448. $v->execute(),
  449. $v->getErrors()
  450. );
  451. }
  452. /**
  453. * Validate default swimlane modification
  454. *
  455. * @access public
  456. * @param array $values Form values
  457. * @return array $valid, $errors [0] = Success or not, [1] = List of errors
  458. */
  459. public function validateDefaultModification(array $values)
  460. {
  461. $rules = array(
  462. new Validators\Required('id', t('The id is required')),
  463. new Validators\Required('default_swimlane', t('The name is required')),
  464. );
  465. $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
  466. return array(
  467. $v->execute(),
  468. $v->getErrors()
  469. );
  470. }
  471. /**
  472. * Common validation rules
  473. *
  474. * @access private
  475. * @return array
  476. */
  477. private function commonValidationRules()
  478. {
  479. return array(
  480. new Validators\Integer('id', t('The id must be an integer')),
  481. new Validators\Integer('project_id', t('The project id must be an integer')),
  482. new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
  483. );
  484. }
  485. }