]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Entity/Group.php
4f648ec4847d40d2d96de3fa65a4a7e4df130611
[galette.git] / galette / lib / Galette / Entity / Group.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Group entity
7 *
8 * PHP version 5
9 *
10 * Copyright © 2012-2014 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Entity
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2012-2014 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.7dev - 2012-01-17
35 */
36
37 namespace Galette\Entity;
38
39 use Galette\Core\Login;
40 use Analog\Analog;
41 use Laminas\Db\Sql\Expression;
42
43 /**
44 * Group entity
45 *
46 * @category Entity
47 * @name Group
48 * @package Galette
49 * @author Johan Cwiklinski <johan@x-tnd.be>
50 * @copyright 2012-2014 The Galette Team
51 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
52 * @link http://galette.tuxfamily.org
53 * @since Available since 0.7dev - 2012-01-17
54 */
55 class Group
56 {
57 const TABLE = 'groups';
58 const PK = 'id_group';
59 //relations tables
60 const GROUPSUSERS_TABLE = 'groups_members';
61 const GROUPSMANAGERS_TABLE = 'groups_managers';
62
63 const MEMBER_TYPE = 0;
64 const MANAGER_TYPE = 1;
65
66 private $id;
67 private $group_name;
68 private $parent_group;
69 private $managers;
70 private $members;
71 private $groups;
72 private $creation_date;
73 private $count_members;
74 private $isempty;
75
76 /**
77 * Default constructor
78 *
79 * @param null|int|ResultSet $args Either a ResultSet row or its id for to load
80 * a specific group, or null to just
81 * instanciate object
82 */
83 public function __construct($args = null)
84 {
85 if ($args == null || is_int($args)) {
86 if (is_int($args) && $args > 0) {
87 $this->load($args);
88 }
89 } elseif (is_object($args)) {
90 $this->loadFromRS($args);
91 }
92 }
93
94 /**
95 * Loads a group from its id
96 *
97 * @param int $id the identifiant for the group to load
98 *
99 * @return bool true if query succeed, false otherwise
100 */
101 public function load($id)
102 {
103 global $zdb;
104
105 try {
106 $select = $zdb->select(self::TABLE);
107 $select->where(array(self::PK => $id));
108
109 $results = $zdb->execute($select);
110
111 if ($results->count() > 0) {
112 $this->loadFromRS($results->current());
113 return true;
114 } else {
115 return false;
116 }
117 } catch (\Exception $e) {
118 Analog::log(
119 'Cannot load group form id `' . $id . '` | ' . $e->getMessage(),
120 Analog::WARNING
121 );
122 return false;
123 }
124 }
125
126 /**
127 * Populate object from a resultset row
128 *
129 * @param ResultSet $r the resultset row
130 *
131 * @return void
132 */
133 private function loadFromRS($r)
134 {
135 $this->id = $r->id_group;
136 $this->group_name = $r->group_name;
137 $this->creation_date = $r->creation_date;
138 if ($r->parent_group) {
139 $this->parent_group = new Group((int)$r->parent_group);
140 }
141 $adhpk = Adherent::PK;
142 if (isset($r->members)) {
143 //we're from a list, we just want members count
144 $this->count_members = $r->members;
145 } else {
146 //we're probably from a single group, let's load sub entities
147 //$this->loadPersons(self::MEMBER_TYPE);
148 //$this->loadPersons(self::MANAGER_TYPE);
149 //$this->loadSubGroups();
150 }
151 }
152
153 /**
154 * Loads members for the current group
155 *
156 * @param int $type Either self::MEMBER_TYPE or self::MANAGER_TYPE
157 *
158 * @return void
159 */
160 private function loadPersons($type)
161 {
162 global $zdb;
163
164 if ($this->id) {
165 try {
166 $join = null;
167 switch ($type) {
168 case self::MEMBER_TYPE:
169 $join = PREFIX_DB . self::GROUPSUSERS_TABLE;
170 break;
171 case self::MANAGER_TYPE:
172 $join = PREFIX_DB . self::GROUPSMANAGERS_TABLE;
173 break;
174 }
175
176 $select = $zdb->select(Adherent::TABLE, 'a');
177 $select->join(
178 array('g' => $join),
179 'g.' . Adherent::PK . '=a.' . Adherent::PK,
180 array()
181 )->where(
182 'g.' . self::PK . ' = ' . $this->id
183 )->order(
184 'nom_adh ASC',
185 'prenom_adh ASC'
186 );
187
188 $results = $zdb->execute($select);
189 $members = array();
190 $adhpk = Adherent::PK;
191
192 $deps = array(
193 'picture' => false,
194 'groups' => false,
195 'dues' => false
196 );
197
198 foreach ($results as $m) {
199 $members[] = new Adherent($zdb, $m, $deps);
200 }
201
202 if ($type === self::MEMBER_TYPE) {
203 $this->members = $members;
204 } else {
205 $this->managers = $members;
206 }
207 } catch (\Exception $e) {
208 Analog::log(
209 'Cannot get group persons | ' . $e->getMessage(),
210 Analog::WARNING
211 );
212 }
213 }
214 }
215
216 /**
217 * Load sub-groups
218 *
219 * @return void
220 */
221 private function loadSubGroups()
222 {
223 global $zdb;
224
225 try {
226 $select = $zdb->select(self::TABLE, 'a');
227
228 if (!$this->login->isAdmin() && !$this->login->isStaff()) {
229 $select->join(
230 array('b' => PREFIX_DB . self::GROUPSMANAGERS_TABLE),
231 'a.' . self::PK . '=b.' . self::PK,
232 array()
233 )->where('b.' . Adherent::PK . ' = ' . $this->login->id);
234 }
235
236 $select->where('parent_group = ' . $this->id)
237 ->order('group_name ASC');
238
239 $results = $zdb->execute($select);
240 $groups = array();
241 $grppk = self::PK;
242 foreach ($results as $m) {
243 $group = new Group((int)$m->$grppk);
244 $group->setLogin($this->login);
245 $groups[] = $group;
246 }
247 $this->groups = $groups;
248 } catch (\Exception $e) {
249 Analog::log(
250 'Cannot get subgroup for group ' . $this->group_name .
251 ' (' . $this->id . ')| ' . $e->getMessage(),
252 Analog::WARNING
253 );
254 }
255 }
256
257 /**
258 * Remove specified group
259 *
260 * @param boolean $cascade Also remove members and managers
261 *
262 * @return boolean
263 */
264 public function remove($cascade = false)
265 {
266 global $zdb;
267 $transaction = false;
268
269 try {
270 if (!$zdb->connection->inTransaction()) {
271 $zdb->connection->beginTransaction();
272 $transaction = true;
273 }
274
275 if ($cascade === true) {
276 $subgroups = $this->getGroups();
277 if (count($subgroups) > 0) {
278 Analog::log(
279 'Cascading remove ' . $this->group_name .
280 '. Subgroups, their members and managers will be detached.',
281 Analog::INFO
282 );
283 foreach ($subgroups as $subgroup) {
284 $subgroup->remove(true);
285 }
286 }
287
288 Analog::log(
289 'Cascading remove ' . $this->group_name .
290 '. Members and managers will be detached.',
291 Analog::INFO
292 );
293
294 //delete members
295 $delete = $zdb->delete(self::GROUPSUSERS_TABLE);
296 $delete->where(
297 self::PK . ' = ' . $this->id
298 );
299 $zdb->execute($delete);
300
301 //delete managers
302 $delete = $zdb->delete(self::GROUPSMANAGERS_TABLE);
303 $delete->where(
304 self::PK . ' = ' . $this->id
305 );
306 $zdb->execute($delete);
307 }
308
309 //delete group itself
310 $delete = $zdb->delete(self::TABLE);
311 $delete->where(
312 self::PK . ' = ' . $this->id
313 );
314 $zdb->execute($delete);
315
316 //commit all changes
317 if ($transaction) {
318 $zdb->connection->commit();
319 }
320
321 return true;
322 } catch (\Exception $e) {
323 if ($transaction) {
324 $zdb->connection->rollBack();
325 }
326 if ($e->getCode() == 23000) {
327 Analog::log(
328 str_replace(
329 '%group',
330 $this->group_name,
331 'Group "%group" still have members!'
332 ),
333 Analog::WARNING
334 );
335 $this->isempty = false;
336 } else {
337 Analog::log(
338 'Unable to delete group ' . $this->group_name .
339 ' (' . $this->id . ') |' . $e->getMessage(),
340 Analog::ERROR
341 );
342 }
343 return false;
344 }
345 }
346
347 /**
348 * Is group empty? (after first deletion try)
349 *
350 * @return boolean
351 */
352 public function isEmpty()
353 {
354 return $this->isempty;
355 }
356
357 /**
358 * Detach a group from its parent
359 *
360 * @return boolean
361 */
362 public function detach()
363 {
364 global $zdb, $hist;
365
366 try {
367 $update = $zdb->update(self::TABLE);
368 $update->set(
369 array('parent_group' => new Expression('NULL'))
370 )->where(
371 self::PK . ' = ' . $this->id
372 );
373
374 $edit = $zdb->execute($update);
375
376 //edit == 0 does not mean there were an error, but that there
377 //were nothing to change
378 if ($edit->count() > 0) {
379 $this->parent_group = null;
380 $hist->add(
381 _T("Group has been detached from its parent"),
382 $this->group_name
383 );
384 }
385
386 return true;
387 } catch (\Exception $e) {
388 Analog::log(
389 'Something went wrong detaching group `' . $this->group_name .
390 '` (' . $this->id . ') from its parent:\'( | ' .
391 $e->getMessage() . "\n" .
392 $e->getTraceAsString(),
393 Analog::ERROR
394 );
395 throw new \Exception(_T("Unable to detach group :("));
396 }
397 }
398
399 /**
400 * Store the group
401 *
402 * @return boolean
403 */
404 public function store()
405 {
406 global $zdb, $hist;
407
408 try {
409 $values = array(
410 self::PK => $this->id,
411 'group_name' => $this->group_name
412 );
413
414 if ($this->parent_group) {
415 $values['parent_group'] = $this->parent_group->getId();
416 }
417
418 if (!isset($this->id) || $this->id == '') {
419 //we're inserting a new group
420 unset($values[self::PK]);
421 $this->creation_date = date("Y-m-d H:i:s");
422 $values['creation_date'] = $this->creation_date;
423
424 $insert = $zdb->insert(self::TABLE);
425 $insert->values($values);
426 $add = $zdb->execute($insert);
427 if ($add->count() > 0) {
428 if ($zdb->isPostgres()) {
429 $this->id = (int)$zdb->driver->getLastGeneratedValue(
430 PREFIX_DB . 'groups_id_seq'
431 );
432 } else {
433 $this->id = (int)$zdb->driver->getLastGeneratedValue();
434 }
435
436 // logging
437 $hist->add(
438 _T("Group added"),
439 $this->group_name
440 );
441 return true;
442 } else {
443 $hist->add(_T("Fail to add new group."));
444 throw new \Exception(
445 'An error occurred inserting new group!'
446 );
447 }
448 } else {
449 //we're editing an existing group
450 $update = $zdb->update(self::TABLE);
451 $update
452 ->set($values)
453 ->where(self::PK . '=' . $this->id);
454
455 $edit = $zdb->execute($update);
456
457 //edit == 0 does not mean there were an error, but that there
458 //were nothing to change
459 if ($edit->count() > 0) {
460 $hist->add(
461 _T("Group updated"),
462 $this->group_name
463 );
464 }
465 return true;
466 }
467 /** FIXME: also store members and managers? */
468 } catch (\Exception $e) {
469 Analog::log(
470 'Something went wrong :\'( | ' . $e->getMessage() . "\n" .
471 $e->getTraceAsString(),
472 Analog::ERROR
473 );
474 return false;
475 }
476 }
477
478 /**
479 * Is current loggedin user manager of the group?
480 *
481 * @param Login $login Login instance
482 *
483 * @return boolean
484 */
485 public function isManager(Login $login)
486 {
487 if ($login->isAdmin() || $login->isStaff()) {
488 //admins as well as staff members are managers for all groups!
489 return true;
490 } else {
491 //let's check if current loggedin user is part of group managers
492 foreach ($this->managers as $manager) {
493 if ($login->login == $manager->login) {
494 return true;
495 break;
496 }
497 }
498 return false;
499 }
500 }
501
502 /**
503 * Get group id
504 *
505 * @return integer
506 */
507 public function getId()
508 {
509 return $this->id;
510 }
511
512 /**
513 * Get Level of the group
514 *
515 * @return integer
516 */
517 public function getLevel()
518 {
519 if ($this->parent_group) {
520 return $this->parent_group->getLevel() + 1;
521 }
522 return 0;
523 }
524
525 /**
526 * Get the full name of the group "foo / bar"
527 *
528 * @return string
529 */
530 public function getFullName()
531 {
532 if ($this->parent_group) {
533 return $this->parent_group->getFullName() . ' / ' . $this->group_name;
534 }
535 return $this->group_name;
536 }
537
538 /**
539 * Get the indented short name of the group " >> bar"
540 *
541 * @return string
542 */
543 public function getIndentName()
544 {
545 if (($level = $this->getLevel())) {
546 return str_repeat("&nbsp;", 3 * $level) . '&raquo; ' . $this->group_name;
547 }
548 return $this->group_name;
549 }
550
551 /**
552 * Get group name
553 *
554 * @return string
555 */
556 public function getName()
557 {
558 return $this->group_name;
559 }
560
561 /**
562 * Get group members
563 *
564 * @return Adherent[]
565 */
566 public function getMembers()
567 {
568 if (!is_array($this->members)) {
569 $this->loadPersons(self::MEMBER_TYPE);
570 }
571 return $this->members;
572 }
573
574 /**
575 * Get groups managers
576 *
577 * @return Adherent[]
578 */
579 public function getManagers()
580 {
581 if (!is_array($this->managers)) {
582 $this->loadPersons(self::MANAGER_TYPE);
583 }
584 return $this->managers;
585 }
586
587 /**
588 * Get subgroups
589 *
590 * @return Group[]
591 */
592 public function getGroups()
593 {
594 if (!is_array($this->groups)) {
595 $this->loadSubGroups();
596 }
597 return $this->groups;
598 }
599
600 /**
601 * Get parent group
602 *
603 * @return Group
604 */
605 public function getParentGroup()
606 {
607 return $this->parent_group;
608 }
609
610 /**
611 * Get group creation date
612 *
613 * @param boolean $formatted Return date formatted, raw if false
614 *
615 * @return string
616 */
617 public function getCreationDate($formatted = true)
618 {
619 if ($formatted === true) {
620 $date = new \DateTime($this->creation_date);
621 return $date->format(__("Y-m-d"));
622 } else {
623 return $this->creation_date;
624 }
625 }
626
627 /**
628 * Get member count
629 *
630 * @param boolean $force Force members load, defaults to false
631 *
632 * @return int
633 */
634 public function getMemberCount($force = false)
635 {
636 if (isset($this->members) && is_array($this->members)) {
637 return count($this->members);
638 } elseif (isset($this->count_members)) {
639 return $this->count_members;
640 } else {
641 if ($force === true) {
642 return count($this->getMembers());
643 } else {
644 return 0;
645 }
646 }
647 }
648
649 /**
650 * Set name
651 *
652 * @param string $name Group name
653 *
654 * @return Group
655 */
656 public function setName($name)
657 {
658 $this->group_name = $name;
659 return $this;
660 }
661
662 /**
663 * Set all subgroups
664 *
665 * @param array $groups Groups id
666 *
667 * @return Group
668 */
669 public function setSubgroups($groups)
670 {
671 $this->groups = $groups;
672 return $this;
673 }
674
675 /**
676 * check if can Set parent group
677 *
678 * @param Group $group Parent group
679 *
680 * @return boolean
681 */
682 public function canSetParentGroup(Group $group)
683 {
684 do {
685 if ($group->getId() == $this->getId()) {
686 return false;
687 }
688 } while ($group = $group->getParentGroup());
689
690 return true;
691 }
692
693 /**
694 * Set parent group
695 *
696 * @param int $id Parent group identifier
697 *
698 * @return Group
699 */
700 public function setParentGroup($id)
701 {
702 $group = new Group((int)$id);
703
704 if (!$this->canSetParentGroup($group)) {
705 //does not seem to work :/
706 throw new \Exception(
707 sprintf(
708 _T('Group `%1$s` cannot be set as parent!'),
709 $group->getName()
710 )
711 );
712 }
713
714 $this->parent_group = $group;
715 return $this;
716 }
717
718 /**
719 * Set members
720 *
721 * @param Adherent[] $members Members list
722 *
723 * @return void
724 */
725 public function setMembers($members)
726 {
727 global $zdb;
728
729 try {
730 $zdb->connection->beginTransaction();
731
732 //first, remove current groups members
733 $delete = $zdb->delete(self::GROUPSUSERS_TABLE);
734 $delete->where(
735 self::PK . ' = ' . $this->id
736 );
737 $zdb->execute($delete);
738
739 Analog::log(
740 'Group members has been removed for `' . $this->group_name .
741 '`, we can now store new ones.',
742 Analog::INFO
743 );
744
745 $insert = $zdb->insert(self::GROUPSUSERS_TABLE);
746 $insert->values(
747 array(
748 self::PK => ':group',
749 Adherent::PK => ':adh'
750 )
751 );
752
753 $stmt = $zdb->sql->prepareStatementForSqlObject($insert);
754
755 if (is_array($members)) {
756 foreach ($members as $m) {
757 $result = $stmt->execute(
758 array(
759 self::PK => $this->id,
760 Adherent::PK => $m->id
761 )
762 );
763
764 if ($result) {
765 Analog::log(
766 'Member `' . $m->sname . '` attached to group `' .
767 $this->group_name . '`.',
768 Analog::DEBUG
769 );
770 } else {
771 Analog::log(
772 'An error occurred trying to attach member `' .
773 $m->sname . '` to group `' . $this->group_name .
774 '` (' . $this->id . ').',
775 Analog::ERROR
776 );
777 throw new \Exception(
778 'Unable to attach `' . $m->sname . '` ' .
779 'to ' . $this->group_name . '(' . $this->id . ')'
780 );
781 }
782 }
783 }
784 //commit all changes
785 $zdb->connection->commit();
786
787 Analog::log(
788 'Group members updated successfully.',
789 Analog::INFO
790 );
791
792 return true;
793 } catch (\Exception $e) {
794 $zdb->connection->rollBack();
795 $messages = array();
796 do {
797 $messages[] = $e->getMessage();
798 } while ($e = $e->getPrevious());
799 Analog::log(
800 'Unable to attach members to group `' . $this->group_name .
801 '` (' . $this->id . ')|' . implode("\n", $messages),
802 Analog::ERROR
803 );
804 return false;
805 }
806 }
807
808 /**
809 * Set managers
810 *
811 * @param Adherent[] $members Managers list
812 *
813 * @return boolean
814 */
815 public function setManagers($members)
816 {
817 global $zdb;
818
819 try {
820 $zdb->connection->beginTransaction();
821
822 //first, remove current groups managers
823 $delete = $zdb->delete(self::GROUPSMANAGERS_TABLE);
824 $delete->where(
825 self::PK . ' = ' . $this->id
826 );
827 $zdb->execute($delete);
828
829 Analog::log(
830 'Group managers has been removed for `' . $this->group_name .
831 '`, we can now store new ones.',
832 Analog::INFO
833 );
834
835 $insert = $zdb->insert(self::GROUPSMANAGERS_TABLE);
836 $insert->values(
837 array(
838 self::PK => ':group',
839 Adherent::PK => ':adh'
840 )
841 );
842
843 $stmt = $zdb->sql->prepareStatementForSqlObject($insert);
844
845 if (is_array($members)) {
846 foreach ($members as $m) {
847 $result = $stmt->execute(
848 array(
849 Group::PK => $this->id,
850 Adherent::PK => $m->id
851 )
852 );
853
854 if ($result) {
855 Analog::log(
856 'Manager `' . $m->sname . '` attached to group `' .
857 $this->group_name . '`.',
858 Analog::DEBUG
859 );
860 } else {
861 Analog::log(
862 'An error occurred trying to attach manager `' .
863 $m->sname . '` to group `' . $this->group_name .
864 '` (' . $this->id . ').',
865 Analog::ERROR
866 );
867 throw new \Exception(
868 'Unable to attach `' . $m->sname . '` ' .
869 'to ' . $this->group_name . '(' . $this->id . ')'
870 );
871 }
872 }
873 }
874 //commit all changes
875 $zdb->connection->commit();
876
877 Analog::log(
878 'Groups managers updated successfully.',
879 Analog::INFO
880 );
881
882 return true;
883 } catch (\Exception $e) {
884 $zdb->connection->rollBack();
885 $messages = array();
886 do {
887 $messages[] = $e->getMessage();
888 } while ($e = $e->getPrevious());
889 Analog::log(
890 'Unable to attach managers to group `' . $this->group_name .
891 '` (' . $this->id . ')|' . implode("\n", $messages),
892 Analog::ERROR
893 );
894 return false;
895 }
896 }
897
898 /**
899 * Set login instance
900 *
901 * @param Login $login Login instance
902 *
903 * @return Group
904 */
905 public function setLogin(Login $login)
906 {
907 $this->login = $login;
908 return $this;
909 }
910 }