]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Entity/Group.php
6e500412b6a2fc152d29cd00d80cc3049447f45b
[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 * @version SVN: $Id$
34 * @link http://galette.tuxfamily.org
35 * @since Available since 0.7dev - 2012-01-17
36 */
37
38 namespace Galette\Entity;
39
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 const TABLE = 'groups';
59 const PK = 'id_group';
60 //relations tables
61 const GROUPSUSERS_TABLE = 'groups_members';
62 const GROUPSMANAGERS_TABLE = 'groups_managers';
63
64 const MEMBER_TYPE = 0;
65 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 (\Exception $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 $adhpk = Adherent::PK;
192
193 $deps = array(
194 'picture' => false,
195 'groups' => false,
196 'dues' => false
197 );
198
199 foreach ($results as $m) {
200 $members[] = new Adherent($zdb, $m, $deps);
201 }
202
203 if ($type === self::MEMBER_TYPE) {
204 $this->members = $members;
205 } else {
206 $this->managers = $members;
207 }
208 } catch (\Exception $e) {
209 Analog::log(
210 'Cannot get group persons | ' . $e->getMessage(),
211 Analog::WARNING
212 );
213 }
214 }
215 }
216
217 /**
218 * Load sub-groups
219 *
220 * @return void
221 */
222 private function loadSubGroups()
223 {
224 global $zdb;
225
226 try {
227 $select = $zdb->select(self::TABLE, 'a');
228
229 if (!$this->login->isAdmin() && !$this->login->isStaff()) {
230 $select->join(
231 array('b' => PREFIX_DB . self::GROUPSMANAGERS_TABLE),
232 'a.' . self::PK . '=b.' . self::PK,
233 array()
234 )->where('b.' . Adherent::PK . ' = ' . $this->login->id);
235 }
236
237 $select->where('parent_group = ' . $this->id)
238 ->order('group_name ASC');
239
240 $results = $zdb->execute($select);
241 $groups = array();
242 $grppk = self::PK;
243 foreach ($results as $m) {
244 $group = new Group((int)$m->$grppk);
245 $group->setLogin($this->login);
246 $groups[] = $group;
247 }
248 $this->groups = $groups;
249 } catch (\Exception $e) {
250 Analog::log(
251 'Cannot get subgroup for group ' . $this->group_name .
252 ' (' . $this->id . ')| ' . $e->getMessage(),
253 Analog::WARNING
254 );
255 }
256 }
257
258 /**
259 * Remove specified group
260 *
261 * @param boolean $cascade Also remove members and managers
262 *
263 * @return boolean
264 */
265 public function remove($cascade = false)
266 {
267 global $zdb;
268 $transaction = false;
269
270 try {
271 if (!$zdb->connection->inTransaction()) {
272 $zdb->connection->beginTransaction();
273 $transaction = true;
274 }
275
276 if ($cascade === true) {
277 $subgroups = $this->getGroups();
278 if (count($subgroups) > 0) {
279 Analog::log(
280 'Cascading remove ' . $this->group_name .
281 '. Subgroups, their members and managers will be detached.',
282 Analog::INFO
283 );
284 foreach ($subgroups as $subgroup) {
285 $subgroup->remove(true);
286 }
287 }
288
289 Analog::log(
290 'Cascading remove ' . $this->group_name .
291 '. Members and managers will be detached.',
292 Analog::INFO
293 );
294
295 //delete members
296 $delete = $zdb->delete(self::GROUPSUSERS_TABLE);
297 $delete->where(
298 self::PK . ' = ' . $this->id
299 );
300 $zdb->execute($delete);
301
302 //delete managers
303 $delete = $zdb->delete(self::GROUPSMANAGERS_TABLE);
304 $delete->where(
305 self::PK . ' = ' . $this->id
306 );
307 $zdb->execute($delete);
308 }
309
310 //delete group itself
311 $delete = $zdb->delete(self::TABLE);
312 $delete->where(
313 self::PK . ' = ' . $this->id
314 );
315 $zdb->execute($delete);
316
317 //commit all changes
318 if ($transaction) {
319 $zdb->connection->commit();
320 }
321
322 return true;
323 } catch (\Exception $e) {
324 if ($transaction) {
325 $zdb->connection->rollBack();
326 }
327 if ($e->getCode() == 23000) {
328 Analog::log(
329 str_replace(
330 '%group',
331 $this->group_name,
332 'Group "%group" still have members!'
333 ),
334 Analog::WARNING
335 );
336 $this->isempty = false;
337 } else {
338 Analog::log(
339 'Unable to delete group ' . $this->group_name .
340 ' (' . $this->id . ') |' . $e->getMessage(),
341 Analog::ERROR
342 );
343 }
344 return false;
345 }
346 }
347
348 /**
349 * Is group empty? (after first deletion try)
350 *
351 * @return boolean
352 */
353 public function isEmpty()
354 {
355 return $this->isempty;
356 }
357
358 /**
359 * Detach a group from its parent
360 *
361 * @return boolean
362 */
363 public function detach()
364 {
365 global $zdb, $hist;
366
367 try {
368 $update = $zdb->update(self::TABLE);
369 $update->set(
370 array('parent_group' => new Expression('NULL'))
371 )->where(
372 self::PK . ' = ' . $this->id
373 );
374
375 $edit = $zdb->execute($update);
376
377 //edit == 0 does not mean there were an error, but that there
378 //were nothing to change
379 if ($edit->count() > 0) {
380 $this->parent_group = null;
381 $hist->add(
382 _T("Group has been detached from its parent"),
383 $this->group_name
384 );
385 }
386
387 return true;
388 } catch (\Exception $e) {
389 Analog::log(
390 'Something went wrong detaching group `' . $this->group_name .
391 '` (' . $this->id . ') from its parent:\'( | ' .
392 $e->getMessage() . "\n" .
393 $e->getTraceAsString(),
394 Analog::ERROR
395 );
396 throw new \Exception(_T("Unable to detach group :("));
397 }
398 }
399
400 /**
401 * Store the group
402 *
403 * @return boolean
404 */
405 public function store()
406 {
407 global $zdb, $hist;
408
409 try {
410 $values = array(
411 self::PK => $this->id,
412 'group_name' => $this->group_name
413 );
414
415 if ($this->parent_group) {
416 $values['parent_group'] = $this->parent_group->getId();
417 }
418
419 if (!isset($this->id) || $this->id == '') {
420 //we're inserting a new group
421 unset($values[self::PK]);
422 $this->creation_date = date("Y-m-d H:i:s");
423 $values['creation_date'] = $this->creation_date;
424
425 $insert = $zdb->insert(self::TABLE);
426 $insert->values($values);
427 $add = $zdb->execute($insert);
428 if ($add->count() > 0) {
429 if ($zdb->isPostgres()) {
430 $this->id = (int)$zdb->driver->getLastGeneratedValue(
431 PREFIX_DB . 'groups_id_seq'
432 );
433 } else {
434 $this->id = (int)$zdb->driver->getLastGeneratedValue();
435 }
436
437 // logging
438 $hist->add(
439 _T("Group added"),
440 $this->group_name
441 );
442 return true;
443 } else {
444 $hist->add(_T("Fail to add new group."));
445 throw new \Exception(
446 'An error occurred inserting new group!'
447 );
448 }
449 } else {
450 //we're editing an existing group
451 $update = $zdb->update(self::TABLE);
452 $update
453 ->set($values)
454 ->where(self::PK . '=' . $this->id);
455
456 $edit = $zdb->execute($update);
457
458 //edit == 0 does not mean there were an error, but that there
459 //were nothing to change
460 if ($edit->count() > 0) {
461 $hist->add(
462 _T("Group updated"),
463 $this->group_name
464 );
465 }
466 return true;
467 }
468 /** FIXME: also store members and managers? */
469 } catch (\Exception $e) {
470 Analog::log(
471 'Something went wrong :\'( | ' . $e->getMessage() . "\n" .
472 $e->getTraceAsString(),
473 Analog::ERROR
474 );
475 return false;
476 }
477 }
478
479 /**
480 * Is current loggedin user manager of the group?
481 *
482 * @param Login $login Login instance
483 *
484 * @return boolean
485 */
486 public function isManager(Login $login)
487 {
488 if ($login->isAdmin() || $login->isStaff()) {
489 //admins as well as staff members are managers for all groups!
490 return true;
491 } else {
492 //let's check if current loggedin user is part of group managers
493 foreach ($this->managers as $manager) {
494 if ($login->login == $manager->login) {
495 return true;
496 break;
497 }
498 }
499 return false;
500 }
501 }
502
503 /**
504 * Get group id
505 *
506 * @return integer
507 */
508 public function getId()
509 {
510 return $this->id;
511 }
512
513 /**
514 * Get Level of the group
515 *
516 * @return integer
517 */
518 public function getLevel()
519 {
520 if ($this->parent_group) {
521 return $this->parent_group->getLevel()+1;
522 }
523 return 0;
524 }
525
526 /**
527 * Get the full name of the group "foo / bar"
528 *
529 * @return string
530 */
531 public function getFullName()
532 {
533 if ($this->parent_group) {
534 return $this->parent_group->getFullName().' / '.$this->group_name;
535 }
536 return $this->group_name;
537 }
538
539 /**
540 * Get the indented short name of the group " >> bar"
541 *
542 * @return string
543 */
544 public function getIndentName()
545 {
546 if (($level = $this->getLevel())) {
547 return str_repeat("&nbsp;", 3*$level).'&raquo; '.$this->group_name;
548 }
549 return $this->group_name;
550 }
551
552 /**
553 * Get group name
554 *
555 * @return string
556 */
557 public function getName()
558 {
559 return $this->group_name;
560 }
561
562 /**
563 * Get group members
564 *
565 * @return Adherent[]
566 */
567 public function getMembers()
568 {
569 if (!is_array($this->members)) {
570 $this->loadPersons(self::MEMBER_TYPE);
571 }
572 return $this->members;
573 }
574
575 /**
576 * Get groups managers
577 *
578 * @return Adherent[]
579 */
580 public function getManagers()
581 {
582 if (!is_array($this->managers)) {
583 $this->loadPersons(self::MANAGER_TYPE);
584 }
585 return $this->managers;
586 }
587
588 /**
589 * Get subgroups
590 *
591 * @return Group[]
592 */
593 public function getGroups()
594 {
595 if (!is_array($this->groups)) {
596 $this->loadSubGroups();
597 }
598 return $this->groups;
599 }
600
601 /**
602 * Get parent group
603 *
604 * @return Group
605 */
606 public function getParentGroup()
607 {
608 return $this->parent_group;
609 }
610
611 /**
612 * Get group creation date
613 *
614 * @param boolean $formatted Return date formatted, raw if false
615 *
616 * @return string
617 */
618 public function getCreationDate($formatted = true)
619 {
620 if ($formatted === true) {
621 $date = new \DateTime($this->creation_date);
622 return $date->format(__("Y-m-d"));
623 } else {
624 return $this->creation_date;
625 }
626 }
627
628 /**
629 * Get member count
630 *
631 * @param boolean $force Force members load, defaults to false
632 *
633 * @return int
634 */
635 public function getMemberCount($force = false)
636 {
637 if (isset($this->members) && is_array($this->members)) {
638 return count($this->members);
639 } elseif (isset($this->count_members)) {
640 return $this->count_members;
641 } else {
642 if ($force === true) {
643 return count($this->getMembers());
644 } else {
645 return 0;
646 }
647 }
648 }
649
650 /**
651 * Set name
652 *
653 * @param string $name Group name
654 *
655 * @return Group
656 */
657 public function setName($name)
658 {
659 $this->group_name = $name;
660 return $this;
661 }
662
663 /**
664 * Set all subgroups
665 *
666 * @param array $groups Groups id
667 *
668 * @return Group
669 */
670 public function setSubgroups($groups)
671 {
672 $this->groups = $groups;
673 return $this;
674 }
675
676 /**
677 * check if can Set parent group
678 *
679 * @param Group $group Parent group
680 *
681 * @return boolean
682 */
683 public function canSetParentGroup(Group $group)
684 {
685 do {
686 if ($group->getId() == $this->getId()) {
687 return false;
688 }
689 } while ($group = $group->getParentGroup());
690
691 return true;
692 }
693
694 /**
695 * Set parent group
696 *
697 * @param int $id Parent group identifier
698 *
699 * @return Group
700 */
701 public function setParentGroup($id)
702 {
703 $group = new Group((int)$id);
704
705 if (!$this->canSetParentGroup($group)) {
706 //does not seem to work :/
707 throw new \Exception(
708 sprintf(
709 _T('Group `%1$s` cannot be set as parent!'),
710 $group->getName()
711 )
712 );
713 }
714
715 $this->parent_group = $group;
716 return $this;
717 }
718
719 /**
720 * Set members
721 *
722 * @param Adherent[] $members Members list
723 *
724 * @return void
725 */
726 public function setMembers($members)
727 {
728 global $zdb;
729
730 try {
731 $zdb->connection->beginTransaction();
732
733 //first, remove current groups members
734 $delete = $zdb->delete(self::GROUPSUSERS_TABLE);
735 $delete->where(
736 self::PK . ' = ' . $this->id
737 );
738 $zdb->execute($delete);
739
740 Analog::log(
741 'Group members has been removed for `' . $this->group_name .
742 '`, we can now store new ones.',
743 Analog::INFO
744 );
745
746 $insert = $zdb->insert(self::GROUPSUSERS_TABLE);
747 $insert->values(
748 array(
749 self::PK => ':group',
750 Adherent::PK => ':adh'
751 )
752 );
753
754 $stmt = $zdb->sql->prepareStatementForSqlObject($insert);
755
756 if (is_array($members)) {
757 foreach ($members as $m) {
758 $result = $stmt->execute(
759 array(
760 self::PK => $this->id,
761 Adherent::PK => $m->id
762 )
763 );
764
765 if ($result) {
766 Analog::log(
767 'Member `' . $m->sname . '` attached to group `' .
768 $this->group_name . '`.',
769 Analog::DEBUG
770 );
771 } else {
772 Analog::log(
773 'An error occurred trying to attach member `' .
774 $m->sname . '` to group `' . $this->group_name .
775 '` (' . $this->id . ').',
776 Analog::ERROR
777 );
778 throw new \Exception(
779 'Unable to attach `' . $m->sname . '` ' .
780 'to ' . $this->group_name . '(' . $this->id . ')'
781 );
782 }
783 }
784 }
785 //commit all changes
786 $zdb->connection->commit();
787
788 Analog::log(
789 'Group members updated successfully.',
790 Analog::INFO
791 );
792
793 return true;
794 } catch (\Exception $e) {
795 $zdb->connection->rollBack();
796 $messages = array();
797 do {
798 $messages[] = $e->getMessage();
799 } while ($e = $e->getPrevious());
800 Analog::log(
801 'Unable to attach members to group `' . $this->group_name .
802 '` (' . $this->id . ')|' . implode("\n", $messages),
803 Analog::ERROR
804 );
805 return false;
806 }
807 }
808
809 /**
810 * Set managers
811 *
812 * @param Adherent[] $members Managers list
813 *
814 * @return boolean
815 */
816 public function setManagers($members)
817 {
818 global $zdb;
819
820 try {
821 $zdb->connection->beginTransaction();
822
823 //first, remove current groups managers
824 $delete = $zdb->delete(self::GROUPSMANAGERS_TABLE);
825 $delete->where(
826 self::PK . ' = ' . $this->id
827 );
828 $zdb->execute($delete);
829
830 Analog::log(
831 'Group managers has been removed for `' . $this->group_name .
832 '`, we can now store new ones.',
833 Analog::INFO
834 );
835
836 $insert = $zdb->insert(self::GROUPSMANAGERS_TABLE);
837 $insert->values(
838 array(
839 self::PK => ':group',
840 Adherent::PK => ':adh'
841 )
842 );
843
844 $stmt = $zdb->sql->prepareStatementForSqlObject($insert);
845
846 if (is_array($members)) {
847 foreach ($members as $m) {
848 $result = $stmt->execute(
849 array(
850 Group::PK => $this->id,
851 Adherent::PK => $m->id
852 )
853 );
854
855 if ($result) {
856 Analog::log(
857 'Manager `' . $m->sname . '` attached to group `' .
858 $this->group_name . '`.',
859 Analog::DEBUG
860 );
861 } else {
862 Analog::log(
863 'An error occurred trying to attach manager `' .
864 $m->sname . '` to group `' . $this->group_name .
865 '` (' . $this->id . ').',
866 Analog::ERROR
867 );
868 throw new \Exception(
869 'Unable to attach `' . $m->sname . '` ' .
870 'to ' . $this->group_name . '(' . $this->id . ')'
871 );
872 }
873 }
874 }
875 //commit all changes
876 $zdb->connection->commit();
877
878 Analog::log(
879 'Groups managers updated successfully.',
880 Analog::INFO
881 );
882
883 return true;
884 } catch (\Exception $e) {
885 $zdb->connection->rollBack();
886 $messages = array();
887 do {
888 $messages[] = $e->getMessage();
889 } while ($e = $e->getPrevious());
890 Analog::log(
891 'Unable to attach managers to group `' . $this->group_name .
892 '` (' . $this->id . ')|' . implode("\n", $messages),
893 Analog::ERROR
894 );
895 return false;
896 }
897 }
898
899 /**
900 * Set login instance
901 *
902 * @param Login $login Login instance
903 *
904 * @return Group
905 */
906 public function setLogin(Login $login)
907 {
908 $this->login = $login;
909 return $this;
910 }
911 }