3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
10 * Copyright © 2012-2023 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
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.
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.
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/>.
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2012-2023 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
37 namespace Galette\Entity
;
40 use Galette\Repository\Groups
;
42 use Galette\Core\Login
;
44 use Laminas\Db\Sql\Expression
;
52 * @author Johan Cwiklinski <johan@x-tnd.be>
53 * @copyright 2012-2023 The Galette Team
54 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
55 * @link http://galette.tuxfamily.org
56 * @since Available since 0.7dev - 2012-01-17
60 public const TABLE
= 'groups';
61 public const PK
= 'id_group';
63 public const GROUPSUSERS_TABLE
= 'groups_members';
64 public const GROUPSMANAGERS_TABLE
= 'groups_managers';
66 public const MEMBER_TYPE
= 0;
67 public const MANAGER_TYPE
= 1;
71 private $parent_group;
75 private $creation_date;
76 private $count_members;
83 * @param null|int|ArrayObject $args Either a ResultSet row or its id for to load
84 * a specific group, or null to just
87 public function __construct($args = null)
89 if ($args === null ||
is_int($args)) {
90 if (is_int($args) && $args > 0) {
93 } elseif (is_object($args)) {
94 $this->loadFromRS($args);
99 * Loads a group from its id
101 * @param int $id the identifiant for the group to load
103 * @return bool true if query succeed, false otherwise
105 public function load($id)
110 $select = $zdb->select(self
::TABLE
);
111 $select->where(array(self
::PK
=> $id));
113 $results = $zdb->execute($select);
115 if ($results->count() > 0) {
116 $this->loadFromRS($results->current());
121 } catch (Throwable
$e) {
123 'Cannot load group form id `' . $id . '` | ' . $e->getMessage(),
131 * Populate object from a resultset row
133 * @param ArrayObject $r the resultset row
137 private function loadFromRS(ArrayObject
$r)
139 $this->id
= (int)$r->id_group
;
140 $this->group_name
= $r->group_name
;
141 $this->creation_date
= $r->creation_date
;
142 if ($r->parent_group
) {
143 $this->parent_group
= new Group((int)$r->parent_group
);
145 $adhpk = Adherent
::PK
;
146 if (isset($r->members
)) {
147 //we're from a list, we just want members count
148 $this->count_members
= $r->members
;
150 //we're probably from a single group, let's load sub entities
151 //$this->loadPersons(self::MEMBER_TYPE);
152 //$this->loadPersons(self::MANAGER_TYPE);
153 //$this->loadSubGroups();
158 * Loads members for the current group
160 * @param int $type Either self::MEMBER_TYPE or self::MANAGER_TYPE
164 private function loadPersons($type)
172 case self
::MEMBER_TYPE
:
173 $join = PREFIX_DB
. self
::GROUPSUSERS_TABLE
;
175 case self
::MANAGER_TYPE
:
176 $join = PREFIX_DB
. self
::GROUPSMANAGERS_TABLE
;
180 $select = $zdb->select(Adherent
::TABLE
, 'a');
183 'g.' . Adherent
::PK
. '=a.' . Adherent
::PK
,
186 'g.' . self
::PK
=> $this->id
192 $results = $zdb->execute($select);
201 foreach ($results as $m) {
202 $members[] = new Adherent($zdb, $m, $deps);
205 if ($type === self
::MEMBER_TYPE
) {
206 $this->members
= $members;
208 $this->managers
= $members;
210 } catch (Throwable
$e) {
212 'Cannot get group persons | ' . $e->getMessage(),
225 private function loadSubGroups()
229 if (!isset($this->login
) ||
!$this->login
->isLogged()) {
235 $select = $zdb->select(self
::TABLE
, 'a');
237 if (!$this->login
->isAdmin() && !$this->login
->isStaff()) {
239 array('b' => PREFIX_DB
. self
::GROUPSMANAGERS_TABLE
),
240 'a.' . self
::PK
. '=b.' . self
::PK
,
242 )->where(['b.' . Adherent
::PK
=> $this->login
->id
]);
245 $select->where(['parent_group' => $this->id
])
246 ->order('group_name ASC');
248 $results = $zdb->execute($select);
251 foreach ($results as $m) {
252 $group = new Group((int)$m->$grppk);
253 $group->setLogin($this->login
);
256 $this->groups
= $groups;
257 } catch (Throwable
$e) {
259 'Cannot get subgroup for group ' . $this->group_name
.
260 ' (' . $this->id
. ')| ' . $e->getMessage(),
268 * Remove specified group
270 * @param boolean $cascade Also remove members and managers
274 public function remove($cascade = false)
277 $transaction = false;
280 if (!$zdb->connection
->inTransaction()) {
281 $zdb->connection
->beginTransaction();
285 if ($cascade === true) {
286 $subgroups = $this->getGroups();
287 if (count($subgroups) > 0) {
289 'Cascading remove ' . $this->group_name
.
290 '. Subgroups, their members and managers will be detached.',
293 foreach ($subgroups as $subgroup) {
294 $subgroup->remove(true);
299 'Cascading remove ' . $this->group_name
.
300 '. Members and managers will be detached.',
305 $delete = $zdb->delete(self
::GROUPSUSERS_TABLE
);
306 $delete->where([self
::PK
=> $this->id
]);
307 $zdb->execute($delete);
310 $delete = $zdb->delete(self
::GROUPSMANAGERS_TABLE
);
311 $delete->where([self
::PK
=> $this->id
]);
312 $zdb->execute($delete);
315 //delete group itself
316 $delete = $zdb->delete(self
::TABLE
);
317 $delete->where([self
::PK
=> $this->id
]);
318 $zdb->execute($delete);
322 $zdb->connection
->commit();
326 } catch (Throwable
$e) {
328 $zdb->connection
->rollBack();
330 if (!$zdb->isPostgres() && $e->getCode() == 23000 ||
$zdb->isPostgres() && $e->getCode() == 23503) {
335 'Group "%group" still have members!'
339 $this->isempty
= false;
342 'Unable to delete group ' . $this->group_name
.
343 ' (' . $this->id
. ') |' . $e->getMessage(),
353 * Is group empty? (after first deletion try)
357 public function isEmpty()
359 return $this->isempty
;
363 * Detach a group from its parent
367 public function detach()
372 $update = $zdb->update(self
::TABLE
);
374 array('parent_group' => new Expression('NULL'))
376 [self
::PK
=> $this->id
]
379 $edit = $zdb->execute($update);
381 //edit == 0 does not mean there were an error, but that there
382 //were nothing to change
383 if ($edit->count() > 0) {
384 $this->parent_group
= null;
386 _T("Group has been detached from its parent"),
392 } catch (Throwable
$e) {
394 'Something went wrong detaching group `' . $this->group_name
.
395 '` (' . $this->id
. ') from its parent:\'( | ' .
396 $e->getMessage() . "\n" .
397 $e->getTraceAsString(),
409 public function store()
413 $parent_group = null;
414 if ($this->parent_group
) {
415 $parent_group = $this->parent_group
->getId();
417 if (!Groups
::isUnique($zdb, $this->getName(), $parent_group, $this->getId())) {
419 'Group name is not unique at requested level',
422 throw new \
RuntimeException(
423 _T("The group name you have requested already exists in the database.")
429 self
::PK
=> $this->id
,
430 'group_name' => $this->group_name
433 if ($this->parent_group
) {
434 $values['parent_group'] = $parent_group;
437 if (!isset($this->id
) ||
$this->id
== '') {
438 //we're inserting a new group
439 unset($values[self
::PK
]);
440 $this->creation_date
= date("Y-m-d H:i:s");
441 $values['creation_date'] = $this->creation_date
;
443 $insert = $zdb->insert(self
::TABLE
);
444 $insert->values($values);
445 $add = $zdb->execute($insert);
446 if ($add->count() > 0) {
447 $this->id
= $zdb->getLastGeneratedValue($this);
456 $hist->add(_T("Fail to add new group."));
457 throw new \
Exception(
458 'An error occurred inserting new group!'
462 //we're editing an existing group
463 $update = $zdb->update(self
::TABLE
);
466 ->where([self
::PK
=> $this->id
]);
468 $edit = $zdb->execute($update);
470 //edit == 0 does not mean there were an error, but that there
471 //were nothing to change
472 if ($edit->count() > 0) {
480 /** FIXME: also store members and managers? */
481 } catch (Throwable
$e) {
483 'Something went wrong :\'( | ' . $e->getMessage() . "\n" .
484 $e->getTraceAsString(),
492 * Is current logged-in user manager of the group?
494 * @param Login $login Login instance
498 public function isManager(Login
$login)
500 if ($login->isAdmin() ||
$login->isStaff()) {
501 //admins as well as staff members are managers for all groups!
504 //let's check if current logged-in user is part of group managers
505 if (!is_array($this->managers
)) {
506 $this->loadPersons(self
::MANAGER_TYPE
);
509 foreach ($this->managers
as $manager) {
510 if ($login->login
== $manager->login
) {
523 public function getId()
529 * Get Level of the group
533 public function getLevel()
535 if ($this->parent_group
) {
536 return $this->parent_group
->getLevel() +
1;
542 * Get the full name of the group "foo / bar"
546 public function getFullName()
548 if ($this->parent_group
) {
549 return $this->parent_group
->getFullName() . ' / ' . $this->group_name
;
551 return $this->group_name
;
555 * Get the indented short name of the group " >> bar"
559 public function getIndentName()
561 if (($level = $this->getLevel())) {
562 return str_repeat(" ", 3 * $level) . '» ' . $this->group_name
;
564 return $this->group_name
;
572 public function getName()
574 return $this->group_name
;
582 public function getMembers()
584 if (!is_array($this->members
)) {
585 $this->loadPersons(self
::MEMBER_TYPE
);
587 return $this->members
;
591 * Get groups managers
595 public function getManagers()
597 if (!is_array($this->managers
)) {
598 $this->loadPersons(self
::MANAGER_TYPE
);
600 return $this->managers
;
608 public function getGroups()
610 if (!is_array($this->groups
)) {
611 $this->loadSubGroups();
613 return $this->groups
;
621 public function getParentGroup()
623 return $this->parent_group
;
627 * Get group creation date
629 * @param boolean $formatted Return date formatted, raw if false
633 public function getCreationDate($formatted = true)
635 if ($formatted === true) {
636 $date = new \
DateTime($this->creation_date
);
637 return $date->format(__("Y-m-d"));
639 return $this->creation_date
;
646 * @param boolean $force Force members load, defaults to false
650 public function getMemberCount($force = false)
652 if (isset($this->members
) && is_array($this->members
)) {
653 return count($this->members
);
654 } elseif (isset($this->count_members
)) {
655 return $this->count_members
;
657 if ($force === true) {
658 return count($this->getMembers());
668 * @param string $name Group name
672 public function setName($name)
674 $this->group_name
= $name;
679 * check if can Set parent group
681 * @param Group $group Parent group
685 public function canSetParentGroup(Group
$group)
688 if ($group->getId() == $this->getId()) {
691 } while ($group = $group->getParentGroup());
699 * @param int $id Parent group identifier
703 public function setParentGroup($id)
705 $group = new Group((int)$id);
707 if (!$this->canSetParentGroup($group)) {
708 //does not seem to work :/
709 throw new \
Exception(
711 _T('Group `%1$s` cannot be set as parent!'),
717 $this->parent_group
= $group;
724 * @param Adherent[] $members Members list
729 public function setMembers(array $members = []): bool
734 $zdb->connection
->beginTransaction();
736 //first, remove current groups members
737 $delete = $zdb->delete(self
::GROUPSUSERS_TABLE
);
738 $delete->where([self
::PK
=> $this->id
]);
739 $zdb->execute($delete);
742 'Group members has been removed for `' . $this->group_name
.
743 '`, we can now store new ones.',
747 $insert = $zdb->insert(self
::GROUPSUSERS_TABLE
);
750 self
::PK
=> ':group',
751 Adherent
::PK
=> ':adh'
755 $stmt = $zdb->sql
->prepareStatementForSqlObject($insert);
757 foreach ($members as $m) {
758 $result = $stmt->execute(
760 'group' => $this->id
,
767 'Member `' . $m->sname
. '` attached to group `' .
768 $this->group_name
. '`.',
773 'An error occurred trying to attach member `' .
774 $m->sname
. '` to group `' . $this->group_name
.
775 '` (' . $this->id
. ').',
778 throw new \
Exception(
779 'Unable to attach `' . $m->sname
. '` ' .
780 'to ' . $this->group_name
. '(' . $this->id
. ')'
786 $zdb->connection
->commit();
789 'Group members updated successfully.',
794 } catch (Throwable
$e) {
796 $zdb->connection
->rollBack();
799 $messages[] = $e->getMessage();
800 } while ($e = $e->getPrevious());
802 'Unable to attach members to group `' . $this->group_name
.
803 '` (' . $this->id
. ')|' . implode("\n", $messages),
813 * @param Adherent[] $members Managers list
818 public function setManagers(array $members = []): bool
823 $zdb->connection
->beginTransaction();
825 //first, remove current groups managers
826 $delete = $zdb->delete(self
::GROUPSMANAGERS_TABLE
);
827 $delete->where([self
::PK
=> $this->id
]);
828 $zdb->execute($delete);
831 'Group managers has been removed for `' . $this->group_name
.
832 '`, we can now store new ones.',
836 $insert = $zdb->insert(self
::GROUPSMANAGERS_TABLE
);
839 self
::PK
=> ':group',
840 Adherent
::PK
=> ':adh'
844 $stmt = $zdb->sql
->prepareStatementForSqlObject($insert);
846 foreach ($members as $m) {
847 $result = $stmt->execute(
849 'group' => $this->id
,
856 'Manager `' . $m->sname
. '` attached to group `' .
857 $this->group_name
. '`.',
862 'An error occurred trying to attach manager `' .
863 $m->sname
. '` to group `' . $this->group_name
.
864 '` (' . $this->id
. ').',
867 throw new \
Exception(
868 'Unable to attach `' . $m->sname
. '` ' .
869 'to ' . $this->group_name
. '(' . $this->id
. ')'
875 $zdb->connection
->commit();
878 'Groups managers updated successfully.',
883 } catch (Throwable
$e) {
885 $zdb->connection
->rollBack();
888 $messages[] = $e->getMessage();
889 } while ($e = $e->getPrevious());
891 'Unable to attach managers to group `' . $this->group_name
.
892 '` (' . $this->id
. ')|' . implode("\n", $messages),
902 * @param Login $login Login instance
906 public function setLogin(Login
$login)
908 $this->login
= $login;
913 * Can current logged-in user edit group
915 * @param Login $login Login instance
919 public function canEdit(Login
$login): bool
923 //admin and staff users can edit
924 if ($login->isAdmin() ||
$login->isStaff()) {
928 //group managers can edit groups they manage when pref is on
929 if ($preferences->pref_bool_groupsmanagers_edit_member
&& $this->isManager($login)) {