]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Entity/Group.php
Add methods to manage dependencies to load on members
[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 Throwable;
40 use Galette\Core\Login;
41 use Analog\Analog;
42 use Laminas\Db\Sql\Expression;
43
44 /**
45 * Group entity
46 *
47 * @category Entity
48 * @name Group
49 * @package Galette
50 * @author Johan Cwiklinski <johan@x-tnd.be>
51 * @copyright 2012-2014 The Galette Team
52 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
53 * @link http://galette.tuxfamily.org
54 * @since Available since 0.7dev - 2012-01-17
55 */
56 class Group
57 {
58 public const TABLE = 'groups';
59 public const PK = 'id_group';
60 //relations tables
61 public const GROUPSUSERS_TABLE = 'groups_members';
62 public const GROUPSMANAGERS_TABLE = 'groups_managers';
63
64 public const MEMBER_TYPE = 0;
65 public const MANAGER_TYPE = 1;
66
67 private $id;
68 private $group_name;
69 private $parent_group;
70 private $managers;
71 private $members;
72 private $groups;
73 private $creation_date;
74 private $count_members;
75 private $isempty;
76
77 /**
78 * Default constructor
79 *
80 * @param null|int|ResultSet $args Either a ResultSet row or its id for to load
81 * a specific group, or null to just
82 * instanciate object
83 */
84 public function __construct($args = null)
85 {
86 if ($args == null || is_int($args)) {
87 if (is_int($args) && $args > 0) {
88 $this->load($args);
89 }
90 } elseif (is_object($args)) {
91 $this->loadFromRS($args);
92 }
93 }
94
95 /**
96 * Loads a group from its id
97 *
98 * @param int $id the identifiant for the group to load
99 *
100 * @return bool true if query succeed, false otherwise
101 */
102 public function load($id)
103 {
104 global $zdb;
105
106 try {
107 $select = $zdb->select(self::TABLE);
108 $select->where(array(self::PK => $id));
109
110 $results = $zdb->execute($select);
111
112 if ($results->count() > 0) {
113 $this->loadFromRS($results->current());
114 return true;
115 } else {
116 return false;
117 }
118 } catch (Throwable $e) {
119 Analog::log(
120 'Cannot load group form id `' . $id . '` | ' . $e->getMessage(),
121 Analog::WARNING
122 );
123 return false;
124 }
125 }
126
127 /**
128 * Populate object from a resultset row
129 *
130 * @param ResultSet $r the resultset row
131 *
132 * @return void
133 */
134 private function loadFromRS($r)
135 {
136 $this->id = $r->id_group;
137 $this->group_name = $r->group_name;
138 $this->creation_date = $r->creation_date;
139 if ($r->parent_group) {
140 $this->parent_group = new Group((int)$r->parent_group);
141 }
142 $adhpk = Adherent::PK;
143 if (isset($r->members)) {
144 //we're from a list, we just want members count
145 $this->count_members = $r->members;
146 } else {
147 //we're probably from a single group, let's load sub entities
148 //$this->loadPersons(self::MEMBER_TYPE);
149 //$this->loadPersons(self::MANAGER_TYPE);
150 //$this->loadSubGroups();
151 }
152 }
153
154 /**
155 * Loads members for the current group
156 *
157 * @param int $type Either self::MEMBER_TYPE or self::MANAGER_TYPE
158 *
159 * @return void
160 */
161 private function loadPersons($type)
162 {
163 global $zdb;
164
165 if ($this->id) {
166 try {
167 $join = null;
168 switch ($type) {
169 case self::MEMBER_TYPE:
170 $join = PREFIX_DB . self::GROUPSUSERS_TABLE;
171 break;
172 case self::MANAGER_TYPE:
173 $join = PREFIX_DB . self::GROUPSMANAGERS_TABLE;
174 break;
175 }
176
177 $select = $zdb->select(Adherent::TABLE, 'a');
178 $select->join(
179 array('g' => $join),
180 'g.' . Adherent::PK . '=a.' . Adherent::PK,
181 array()
182 )->where(
183 'g.' . self::PK . ' = ' . $this->id
184 )->order(
185 'nom_adh ASC',
186 'prenom_adh ASC'
187 );
188
189 $results = $zdb->execute($select);
190 $members = array();
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 (Throwable $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 (Throwable $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 (Throwable $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 (Throwable $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 $this->id = $zdb->getLastGeneratedValue($this);
429
430 // logging
431 $hist->add(
432 _T("Group added"),
433 $this->group_name
434 );
435 return true;
436 } else {
437 $hist->add(_T("Fail to add new group."));
438 throw new \Exception(
439 'An error occurred inserting new group!'
440 );
441 }
442 } else {
443 //we're editing an existing group
444 $update = $zdb->update(self::TABLE);
445 $update
446 ->set($values)
447 ->where(self::PK . '=' . $this->id);
448
449 $edit = $zdb->execute($update);
450
451 //edit == 0 does not mean there were an error, but that there
452 //were nothing to change
453 if ($edit->count() > 0) {
454 $hist->add(
455 _T("Group updated"),
456 $this->group_name
457 );
458 }
459 return true;
460 }
461 /** FIXME: also store members and managers? */
462 } catch (Throwable $e) {
463 Analog::log(
464 'Something went wrong :\'( | ' . $e->getMessage() . "\n" .
465 $e->getTraceAsString(),
466 Analog::ERROR
467 );
468 return false;
469 }
470 }
471
472 /**
473 * Is current logged-in user manager of the group?
474 *
475 * @param Login $login Login instance
476 *
477 * @return boolean
478 */
479 public function isManager(Login $login)
480 {
481 if ($login->isAdmin() || $login->isStaff()) {
482 //admins as well as staff members are managers for all groups!
483 return true;
484 } else {
485 //let's check if current logged-in user is part of group managers
486 foreach ($this->managers as $manager) {
487 if ($login->login == $manager->login) {
488 return true;
489 break;
490 }
491 }
492 return false;
493 }
494 }
495
496 /**
497 * Get group id
498 *
499 * @return integer
500 */
501 public function getId()
502 {
503 return $this->id;
504 }
505
506 /**
507 * Get Level of the group
508 *
509 * @return integer
510 */
511 public function getLevel()
512 {
513 if ($this->parent_group) {
514 return $this->parent_group->getLevel() + 1;
515 }
516 return 0;
517 }
518
519 /**
520 * Get the full name of the group "foo / bar"
521 *
522 * @return string
523 */
524 public function getFullName()
525 {
526 if ($this->parent_group) {
527 return $this->parent_group->getFullName() . ' / ' . $this->group_name;
528 }
529 return $this->group_name;
530 }
531
532 /**
533 * Get the indented short name of the group " >> bar"
534 *
535 * @return string
536 */
537 public function getIndentName()
538 {
539 if (($level = $this->getLevel())) {
540 return str_repeat("&nbsp;", 3 * $level) . '&raquo; ' . $this->group_name;
541 }
542 return $this->group_name;
543 }
544
545 /**
546 * Get group name
547 *
548 * @return string
549 */
550 public function getName()
551 {
552 return $this->group_name;
553 }
554
555 /**
556 * Get group members
557 *
558 * @return Adherent[]
559 */
560 public function getMembers()
561 {
562 if (!is_array($this->members)) {
563 $this->loadPersons(self::MEMBER_TYPE);
564 }
565 return $this->members;
566 }
567
568 /**
569 * Get groups managers
570 *
571 * @return Adherent[]
572 */
573 public function getManagers()
574 {
575 if (!is_array($this->managers)) {
576 $this->loadPersons(self::MANAGER_TYPE);
577 }
578 return $this->managers;
579 }
580
581 /**
582 * Get subgroups
583 *
584 * @return Group[]
585 */
586 public function getGroups()
587 {
588 if (!is_array($this->groups)) {
589 $this->loadSubGroups();
590 }
591 return $this->groups;
592 }
593
594 /**
595 * Get parent group
596 *
597 * @return Group
598 */
599 public function getParentGroup()
600 {
601 return $this->parent_group;
602 }
603
604 /**
605 * Get group creation date
606 *
607 * @param boolean $formatted Return date formatted, raw if false
608 *
609 * @return string
610 */
611 public function getCreationDate($formatted = true)
612 {
613 if ($formatted === true) {
614 $date = new \DateTime($this->creation_date);
615 return $date->format(__("Y-m-d"));
616 } else {
617 return $this->creation_date;
618 }
619 }
620
621 /**
622 * Get member count
623 *
624 * @param boolean $force Force members load, defaults to false
625 *
626 * @return int
627 */
628 public function getMemberCount($force = false)
629 {
630 if (isset($this->members) && is_array($this->members)) {
631 return count($this->members);
632 } elseif (isset($this->count_members)) {
633 return $this->count_members;
634 } else {
635 if ($force === true) {
636 return count($this->getMembers());
637 } else {
638 return 0;
639 }
640 }
641 }
642
643 /**
644 * Set name
645 *
646 * @param string $name Group name
647 *
648 * @return Group
649 */
650 public function setName($name)
651 {
652 $this->group_name = $name;
653 return $this;
654 }
655
656 /**
657 * Set all subgroups
658 *
659 * @param array $groups Groups id
660 *
661 * @return Group
662 */
663 public function setSubgroups($groups)
664 {
665 $this->groups = $groups;
666 return $this;
667 }
668
669 /**
670 * check if can Set parent group
671 *
672 * @param Group $group Parent group
673 *
674 * @return boolean
675 */
676 public function canSetParentGroup(Group $group)
677 {
678 do {
679 if ($group->getId() == $this->getId()) {
680 return false;
681 }
682 } while ($group = $group->getParentGroup());
683
684 return true;
685 }
686
687 /**
688 * Set parent group
689 *
690 * @param int $id Parent group identifier
691 *
692 * @return Group
693 */
694 public function setParentGroup($id)
695 {
696 $group = new Group((int)$id);
697
698 if (!$this->canSetParentGroup($group)) {
699 //does not seem to work :/
700 throw new \Exception(
701 sprintf(
702 _T('Group `%1$s` cannot be set as parent!'),
703 $group->getName()
704 )
705 );
706 }
707
708 $this->parent_group = $group;
709 return $this;
710 }
711
712 /**
713 * Set members
714 *
715 * @param Adherent[] $members Members list
716 *
717 * @return void
718 */
719 public function setMembers($members)
720 {
721 global $zdb;
722
723 try {
724 $zdb->connection->beginTransaction();
725
726 //first, remove current groups members
727 $delete = $zdb->delete(self::GROUPSUSERS_TABLE);
728 $delete->where(
729 self::PK . ' = ' . $this->id
730 );
731 $zdb->execute($delete);
732
733 Analog::log(
734 'Group members has been removed for `' . $this->group_name .
735 '`, we can now store new ones.',
736 Analog::INFO
737 );
738
739 $insert = $zdb->insert(self::GROUPSUSERS_TABLE);
740 $insert->values(
741 array(
742 self::PK => ':group',
743 Adherent::PK => ':adh'
744 )
745 );
746
747 $stmt = $zdb->sql->prepareStatementForSqlObject($insert);
748
749 if (is_array($members)) {
750 foreach ($members as $m) {
751 $result = $stmt->execute(
752 array(
753 'group' => $this->id,
754 'adh' => $m->id
755 )
756 );
757
758 if ($result) {
759 Analog::log(
760 'Member `' . $m->sname . '` attached to group `' .
761 $this->group_name . '`.',
762 Analog::DEBUG
763 );
764 } else {
765 Analog::log(
766 'An error occurred trying to attach member `' .
767 $m->sname . '` to group `' . $this->group_name .
768 '` (' . $this->id . ').',
769 Analog::ERROR
770 );
771 throw new \Exception(
772 'Unable to attach `' . $m->sname . '` ' .
773 'to ' . $this->group_name . '(' . $this->id . ')'
774 );
775 }
776 }
777 }
778 //commit all changes
779 $zdb->connection->commit();
780
781 Analog::log(
782 'Group members updated successfully.',
783 Analog::INFO
784 );
785
786 return true;
787 } catch (Throwable $e) {
788 $zdb->connection->rollBack();
789 $messages = array();
790 do {
791 $messages[] = $e->getMessage();
792 } while ($e = $e->getPrevious());
793 Analog::log(
794 'Unable to attach members to group `' . $this->group_name .
795 '` (' . $this->id . ')|' . implode("\n", $messages),
796 Analog::ERROR
797 );
798 return false;
799 }
800 }
801
802 /**
803 * Set managers
804 *
805 * @param Adherent[] $members Managers list
806 *
807 * @return boolean
808 */
809 public function setManagers($members)
810 {
811 global $zdb;
812
813 try {
814 $zdb->connection->beginTransaction();
815
816 //first, remove current groups managers
817 $delete = $zdb->delete(self::GROUPSMANAGERS_TABLE);
818 $delete->where(
819 self::PK . ' = ' . $this->id
820 );
821 $zdb->execute($delete);
822
823 Analog::log(
824 'Group managers has been removed for `' . $this->group_name .
825 '`, we can now store new ones.',
826 Analog::INFO
827 );
828
829 $insert = $zdb->insert(self::GROUPSMANAGERS_TABLE);
830 $insert->values(
831 array(
832 self::PK => ':group',
833 Adherent::PK => ':adh'
834 )
835 );
836
837 $stmt = $zdb->sql->prepareStatementForSqlObject($insert);
838
839 if (is_array($members)) {
840 foreach ($members as $m) {
841 $result = $stmt->execute(
842 array(
843 'group' => $this->id,
844 'adh' => $m->id
845 )
846 );
847
848 if ($result) {
849 Analog::log(
850 'Manager `' . $m->sname . '` attached to group `' .
851 $this->group_name . '`.',
852 Analog::DEBUG
853 );
854 } else {
855 Analog::log(
856 'An error occurred trying to attach manager `' .
857 $m->sname . '` to group `' . $this->group_name .
858 '` (' . $this->id . ').',
859 Analog::ERROR
860 );
861 throw new \Exception(
862 'Unable to attach `' . $m->sname . '` ' .
863 'to ' . $this->group_name . '(' . $this->id . ')'
864 );
865 }
866 }
867 }
868 //commit all changes
869 $zdb->connection->commit();
870
871 Analog::log(
872 'Groups managers updated successfully.',
873 Analog::INFO
874 );
875
876 return true;
877 } catch (Throwable $e) {
878 $zdb->connection->rollBack();
879 $messages = array();
880 do {
881 $messages[] = $e->getMessage();
882 } while ($e = $e->getPrevious());
883 Analog::log(
884 'Unable to attach managers to group `' . $this->group_name .
885 '` (' . $this->id . ')|' . implode("\n", $messages),
886 Analog::ERROR
887 );
888 return false;
889 }
890 }
891
892 /**
893 * Set login instance
894 *
895 * @param Login $login Login instance
896 *
897 * @return Group
898 */
899 public function setLogin(Login $login)
900 {
901 $this->login = $login;
902 return $this;
903 }
904 }