aboutsummaryrefslogtreecommitdiff
path: root/lib/models/Statusgruppen.php
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+github@gmail.com>2021-07-22 16:07:19 +0200
committerJan-Hendrik Willms <tleilax+github@gmail.com>2021-07-22 16:19:12 +0200
commita3da1483a9e689846179159355badfec8073dbec (patch)
tree770dcca6bdf5f6f2a11b0e7fcbbeda6919a3fc52 /lib/models/Statusgruppen.php
current code from svn, revision 62608
Diffstat (limited to 'lib/models/Statusgruppen.php')
-rw-r--r--lib/models/Statusgruppen.php705
1 files changed, 705 insertions, 0 deletions
diff --git a/lib/models/Statusgruppen.php b/lib/models/Statusgruppen.php
new file mode 100644
index 0000000..30d2149
--- /dev/null
+++ b/lib/models/Statusgruppen.php
@@ -0,0 +1,705 @@
+<?php
+
+/**
+ * Statusgruppen.php
+ * model class for statusgroups.
+ * The statusgrouphierarchy is represented by the attributes
+ * children and parent
+ *
+ * Statusgroupmembers are saved as in <code>$this->members</code>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * @author Florian Bieringer <florian.bieringer@uni-passau.de>
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
+ * @category Stud.IP
+ *
+ * @property string statusgruppe_id database column
+ * @property string id alias column for statusgruppe_id
+ * @property string name database column
+ * @property string range_id database column
+ * @property string position database column
+ * @property string size database column
+ * @property string selfassign database column
+ * @property string selfassign_start database column
+ * @property string mkdate database column
+ * @property string chdate database column
+ * @property string calendar_group database column
+ * @property string name_w database column
+ * @property string name_m database column
+ * @property string children computed column
+ * @property SimpleORMapCollection members has_many StatusgruppeUser
+ * @property Statusgruppen parent belongs_to Statusgruppen
+ */
+class Statusgruppen extends SimpleORMap implements PrivacyObject
+{
+ public $keep_children = false;
+
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'statusgruppen';
+ $config['has_many']['members'] = [
+ 'class_name' => 'StatusgruppeUser',
+ 'assoc_foreign_key' => 'statusgruppe_id',
+ 'on_delete' => 'delete',
+ 'order_by' => 'ORDER BY position ASC',
+ ];
+ $config['has_and_belongs_to_many']['dates'] = [
+ 'class_name' => 'CourseDate',
+ 'thru_table' => 'termin_related_groups',
+ 'order_by' => 'ORDER BY date',
+ 'on_delete' => 'delete', // TODO: This might cause trouble
+ 'on_store' => 'store'
+ ];
+ $config['belongs_to']['parent'] = [
+ 'class_name' => 'Statusgruppen',
+ 'foreign_key' => 'range_id',
+ ];
+ $config['belongs_to']['course'] = [
+ 'class_name' => 'Course',
+ 'foreign_key' => 'range_id',
+ ];
+ $config['belongs_to']['institute'] = [
+ 'class_name' => 'Institute',
+ 'foreign_key' => 'range_id',
+ ];
+ $config['belongs_to']['user'] = [
+ 'class_name' => 'User',
+ 'foreign_key' => 'range_id',
+ ];
+ $config['has_one']['blubberthread'] = [
+ 'class_name' => 'BlubberStatusgruppeThread',
+ 'on_store' => 'store',
+ 'on_delete' => 'delete'
+ ];
+ $config['additional_fields']['children'] = true;
+
+ $config['default_values']['position'] = null;
+
+ $config['registered_callbacks']['before_store'][] = 'cbAddPosition';
+ $config['registered_callbacks']['after_delete'][] = 'cbReorderPositions';
+
+ $config['i18n_fields']['name'] = true;
+ $config['i18n_fields']['name_w'] = true;
+ $config['i18n_fields']['name_m'] = true;
+
+ parent::configure($config);
+ }
+
+ public static function findAllByRangeId($range_id, $as_collection = false)
+ {
+ $groups = self::findBySQL('range_id IN (?)', [$range_id]);
+ if (count($groups) > 0) {
+ $ids = array_map(function ($group) { return $group->id; }, $groups);
+ $groups = array_merge($groups, self::findAllByRangeId($ids, false));
+ }
+
+ return $as_collection
+ ? SimpleCollection::createFromArray($groups)
+ : $groups;
+ }
+
+ /**
+ * Creates or updates a statusgroup.
+ *
+ * @param string $id ID of an existing group or empty if new group
+ * @param string $name group name
+ * @param int $position position or null if automatic position after other groups
+ * @param string $range_id ID of the object this group belongs to
+ * @param int $size max number of members or 0 if unlimited
+ * @param int $selfassign may users join this group by themselves?
+ * @param int $selfassign_start group joining is possible starting at ...
+ * @param int $makefolder create a document folder assigned to this group?
+ * @param array $dates dates assigned to this group
+ * @return Statusgruppen The saved statusgroup.
+ * @throws Exception
+ */
+ public static function createOrUpdate(
+ $id,
+ $name,
+ $position,
+ $range_id,
+ $size,
+ $selfassign,
+ $selfassign_start,
+ $selfassign_end,
+ $makefolder,
+ $dates = []
+ )
+ {
+ $group = new Statusgruppen($id);
+
+ $group->name = $name;
+ $group->position = $position;
+ $group->range_id = $range_id;
+ $group->size = $size;
+ $group->selfassign = $selfassign;
+ $group->selfassign_start = $selfassign ? $selfassign_start : 0;
+ $group->selfassign_end = $selfassign ? $selfassign_end : 0;
+
+ // Set assigned dates.
+ if ($dates) {
+ $group->dates = CourseDate::findMany($dates);
+ }
+
+ $group->store();
+
+ /*
+ * Create document folder if requested (ID is needed here,
+ * so we do that after store()).
+ */
+ if (!$group->hasFolder() && $makefolder) {
+ $group->updateFolder(true);
+ }
+
+ return $group;
+ }
+
+ public function getChildren()
+ {
+ $result = Statusgruppen::findBySQL('range_id = ? ORDER BY position', [$this->id]);
+ return $result ?: [];
+ }
+
+ public function getDatafields()
+ {
+ return DataFieldEntry::getDataFieldEntries([$this->range_id, $this->statusgruppe_id], 'roleinstdata');
+ }
+
+ public function setDatafields($data)
+ {
+ foreach ($this->getDatafields() as $field) {
+ $field->setValueFromSubmit($data[$field->getId()]);
+ $field->store();
+ }
+ }
+
+ /**
+ * Finds all statusgroups by a course id
+ *
+ * @param string The course id
+ * @return array Statusgroups
+ */
+ public static function findBySeminar_id($course_id)
+ {
+ return self::findByRange_id($course_id, 'ORDER BY position asc, name asc');
+ }
+
+ public static function findByTermin_id($termin_id)
+ {
+ return self::findBySQL('INNER JOIN termin_related_groups USING (statusgruppe_id) WHERE termin_id = ?', [$termin_id]);
+ }
+
+ public static function findContactGroups($user_id = null)
+ {
+ return self::findByRange_id($user_id ?: $GLOBALS['user']->id);
+ }
+
+ /**
+ * Find all groups belonging to the given range_id that may be joined
+ * by the given user.
+ *
+ * @param String $range_id range_id the groups shall belong to
+ * @param String $user_id user to check
+ * @return array
+ */
+ public static function findJoinableGroups($range_id, $user_id)
+ {
+ $groups = self::findByRange_id($range_id);
+ return array_filter($groups, function ($g) use ($user_id) { return $g->userMayJoin($user_id); });
+ }
+
+ /**
+ * Reorders the positions in numeric order without gaps (e.g. after a delete).
+ *
+ * @param string $range_id Id of range
+ */
+ public static function reorderPositionsForRange($range_id)
+ {
+ return self::findEachBySQL(
+ function ($group, $index) {
+ $group->position = $index;
+ $group->store();
+ },
+ 'range_id = ? ORDER BY position ASC, name ASC',
+ [$range_id]
+ );
+ }
+
+ /**
+ * Produces an array of all statusgroups a user is in
+ *
+ * @param string $user_id The user_id
+ * @param string $seperator The sign between the full paths
+ * @param string $pre Preface of the outputted string (used for recursion)
+ * @return array Stringarray of full gendered paths
+ */
+ public function getFullGenderedPaths($user_id, $seperator = " > ", $pre = "")
+ {
+ $result = [];
+ $name = $pre
+ ? $pre . $seperator . $this->getGenderedName($user_id)
+ : $this->getGenderedName($user_id);
+ if ($this->isMember($user_id)) {
+ $result[] = $name;
+ }
+ if ($this->children) {
+ foreach ($this->children as $child) {
+ $result = array_merge($result, $child->getFullGenderedPaths($user_id, $seperator, $name));
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Produces string of all statusgroups a user is in (upwards from the
+ * current group)
+ *
+ * @param string $user_id The user_id
+ * @param string $seperator The sign between the full paths
+ * @return array String of full gendered paths separated by given separator
+ */
+ public function getFullGenderedName($user_id, $seperator = ' > ')
+ {
+ $result = [$this->getGenderedName($user_id)];
+
+ $item = $this;
+ while ($item = $item->parent) {
+ array_unshift($result, $item->getGenderedName($user_id));
+ }
+
+ return implode($seperator, $result);
+ }
+
+ /**
+ * Returns the gendered name of a statusgroup
+ *
+ * @param string|User $user_id The user_id
+ * @return string The gendered name
+ */
+ public function getGenderedName($user_or_id)
+ {
+ // We have to have at least 1 name gendered
+ if ((string) $this->name_m || (string) $this->name_w) {
+ $user = User::toObject($user_or_id);
+ switch ($user->geschlecht) {
+ case UserInfo::GENDER_FEMALE:
+ return (string) $this->name_w ?: $this->name;
+ case UserInfo::GENDER_MALE:
+ return (string) $this->name_m ?: $this->name;
+ }
+ }
+ return $this->name;
+ }
+
+ public function getName()
+ {
+ return $this->content['name'];
+ }
+
+ /**
+ * Puts out an array of all gendered userroles for a user in a certain
+ * context
+ *
+ * @param string $context The context
+ * @param string $user The user id
+ * @return array All roles
+ */
+ public static function getUserRoles($context, $user)
+ {
+ $roles = [];
+ $groups = self::findByRange_id($context);
+ foreach ($groups as $group) {
+ $roles = array_merge($roles, $group->getFullGenderedPaths($user));
+ }
+ return $roles;
+ }
+
+ /**
+ * Checks if a statusgroup has a folder.
+ *
+ * @return boolean <b>true</> if the statusgroup has a folder, else
+ * <b>false</b>
+ */
+ public function hasFolder()
+ {
+ $query = "SELECT id FROM folders WHERE folder_type = 'CourseGroupFolder' AND range_id = ? AND data_content LIKE ? LIMIT 1";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->range_id, '%"group":"' . $this->id. '"%']);
+ return $statement->fetchColumn();
+ }
+
+ /**
+ * Gets the folder assigned to this statusgroup.
+ *
+ * @return CourseGroupFolder|null
+ */
+ public function getFolder()
+ {
+ $folder_id = $this->hasFolder();
+ return $folder_id ? FileManager::getTypedFolder($folder_id) : null;
+ }
+
+ /**
+ * Delete or create a folder
+ * @param boolean $set <b>true</b> Create a folder
+ * <b>false</b> Unlink the existing folder from the group
+ */
+ public function updateFolder($set)
+ {
+ // Keep existing folder, but disconnect it from group.
+ if ($this->hasFolder() && !$set) {
+ $folder = $this->getFolder();
+ $folder->type = 'StandardFolder';
+ unset($folder->data_content['group']);
+ return $folder->store();
+ }
+
+ // Create new CourseGroupFolder under top folder.
+ if (!$this->hasFolder() && $set) {
+ $topFolder = Folder::findTopFolder($this->range_id);
+ if ($topFolder) {
+ $folderdata = [
+ 'user_id' => $GLOBALS['user']->id,
+ 'parent_id' => $topFolder->id,
+ 'range_id' => $this->range_id,
+ 'range_type' => 'course',
+ 'folder_type' => 'CourseGroupFolder',
+ 'name' => _('Dateiordner der Gruppe:') . ' ' . $this->name,
+ 'data_content' => ['group' => $this->id],
+ 'description' => _('Ablage für Ordner und Dokumente dieser Gruppe')
+ ];
+ $groupFolder = new CourseGroupFolder($folderdata);
+ return $groupFolder->store();
+ }
+ }
+ }
+
+ /**
+ * Finds CourseTopics assigned to this group via course dates.
+ * @return array
+ */
+ public function findTopics()
+ {
+ $topics = [];
+ foreach ($this->dates as $d) {
+ foreach ($d->topics as $t) {
+ // Assign topics with ID as key so we get unique entries.
+ $topics[$t->id] = $t;
+ }
+ }
+ return $topics;
+ }
+
+ /**
+ * Finds Lecturers assigned to this group via course dates.
+ * @return array
+ */
+ public function findLecturers()
+ {
+ $lecturers = [];
+ foreach ($this->dates as $dates) {
+ foreach ($dates->dozenten as $d) {
+ // Assign topics with ID as key so we get unique entries.
+ $lecturers[$d->id] = $d;
+ }
+ }
+ return $lecturers;
+ }
+
+ /**
+ * Checks if a user is a member of this group
+ *
+ * @param string $user_id The user id
+ * @return boolean <b>true</b> if user is a member of this group
+ */
+ public function isMember($user_id = null)
+ {
+ if ($user_id == null) {
+ $user_id = $GLOBALS['user']->id;
+ }
+ foreach ($this->members as $member) {
+ if ($member->user_id == $user_id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Displayfunction to show the places left in this group
+ *
+ * @return string displaystring
+ */
+ public function getPlaces()
+ {
+ return $this->size ? "( " . min(count($this->members), $this->size) . " / {$this->size} )" : "";
+ }
+
+ /**
+ * Remove all users of this group
+ */
+ public function removeAllUsers()
+ {
+ StatusgruppeUser::deleteBySQL('statusgruppe_id = ?', [$this->id]);
+ }
+
+ /**
+ * Remove one user from this group
+ *
+ * @param string $user_id The user id
+ * @param bool $deep Remove user from children as well?
+ * @return bool
+ */
+ public function removeUser($user_id, $deep = false)
+ {
+ // Delete user from statusgruppe
+ $member = StatusgruppeUser::find([$this->id, $user_id]);
+ $result = $member !== null && $member->delete();
+
+ if ($deep) {
+ foreach ($this->children as $child) {
+ $child->removeUser($user_id, true);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Adds a user to a group
+ *
+ * @param string $user_id The user id
+ * @param boolean $check if <b>true</b> checks if there is space left in
+ * this group
+ * @return boolean <b>true</b> if user was added
+ */
+ public function addUser($user_id, $check = false)
+ {
+ if ($check && !$this->userMayJoin($user_id)) {
+ return false;
+ }
+ $user = new StatusgruppeUser([$this->id, $user_id]);
+
+ // set up default datafield values for institute groups
+ if ($user->isNew() && !Course::find($this->range_id)) {
+ $user->datafields->each(function ($datafield) {
+ // note: $datafield->content does not work here
+ $datafield['content'] = 'default_value';
+ $datafield->store();
+ });
+ }
+ return $user->store();
+ }
+
+ /**
+ * Checks if a user could join this group
+ *
+ * @param string $user_id The user id
+ * @return boolean <b>true</b> if user is allowed to join
+ */
+ public function userMayJoin($user_id)
+ {
+ return !$this->isMember($user_id)
+ && $this->hasSpace()
+ && ($this->selfassign != 2 || !$this->userHasExclusiveGroup($user_id));
+ }
+
+ /**
+ * Checks if a user could leave this group
+ *
+ * @param string $user_id The user id
+ * @return boolean <b>true</b> if user is allowed to leave
+ */
+ public function userMayLeave($user_id)
+ {
+ return $this->isMember($user_id)
+ && ($this->selfassign && (!$this->selfassign_end || $this->selfassign_end > time()));
+ }
+
+ /**
+ * Checks if the user is already in an exclusive group of this range
+ *
+ * @param string $user_id The user id
+ * @return boolean <b>true</b> if user has already an exclusive group
+ */
+ public function userHasExclusiveGroup($user_id)
+ {
+ $sql = "SELECT 1 FROM statusgruppe_user JOIN statusgruppen USING (statusgruppe_id) WHERE selfassign = 2 AND range_id = ? AND user_id = ?";
+ $stmt = DBManager::get()->prepare($sql);
+ $stmt->execute([$this->range_id, $user_id]);
+ return $stmt->fetchColumn();
+ }
+
+ /**
+ * Sorts the member of a group alphabetic
+ */
+ public function sortMembersAlphabetic()
+ {
+ foreach ($this->members as $member) {
+ $assoc[$member->id] = $member->user->nachname."_".$member->user->vorname;
+ }
+ asort($assoc);
+
+ $i = 0;
+ foreach ($assoc as $key => $value) {
+ $statusgruppenuser = new StatusgruppeUser(explode('_', $key));
+ $statusgruppenuser->position = $i++;
+ $statusgruppenuser->store();
+ }
+ }
+ /**
+ * Sorts subgroups alphabetical
+ */
+ public function sortSubGroupsAlphabetic()
+ {
+ $groups = self::findBySQL('range_id = ? ORDER BY name', [$this->id]);
+
+ foreach ($groups as $position => $group) {
+ $group->position = $position;
+ $group->store();
+ }
+ }
+
+ /**
+ * Checks if there is free space in this group
+ *
+ * @return <b>true</b> if there is free space
+ */
+ public function hasSpace()
+ {
+ return $this->selfassign &&
+ ($this->selfassign_start <= time()) &&
+ ($this->selfassign_end == 0 || $this->selfassign_end >= time()) &&
+ ($this->size == 0 || count($this->members) < $this->size);
+ }
+
+ /**
+ * Move a user to a position of a group
+ *
+ * @param string $user
+ * @param type $pos
+ */
+ public function moveUser($user_id, $pos)
+ {
+ $statususer = new StatusgruppeUser([$this->id, $user_id]);
+ if ($pos > $statususer->position) {
+ $sql = "UPDATE statusgruppe_user SET position = position - 1 WHERE statusgruppe_id = ? AND position > ? AND position <= ?";
+ } else {
+ $sql = "UPDATE statusgruppe_user SET position = position + 1 WHERE statusgruppe_id = ? AND position < ? AND position >= ?";
+ }
+ $db = DBManager::get();
+ $stmt = $db->prepare($sql);
+ $stmt->execute([$this->id, $statususer->position, $pos]);
+
+ $sql2 = "UPDATE statusgruppe_user SET position = ? WHERE statusgruppe_id = ? AND user_id = ?";
+ $stmt2 = $db->prepare($sql2);
+ $stmt2->execute([$pos, $this->id, $statususer->user_id]);
+ }
+
+ /**
+ * Deletes a status group. Any associated child group will move upwards
+ * in the tree.
+ */
+ public function remove()
+ {
+ // get all child-statusgroups and put them as a child of the father, so they don't hang around without a parent
+ $children = $this->children->pluck('statusgruppe_id');
+ if (!empty($children)) {
+ $query = "UPDATE statusgruppen
+ SET range_id = ?
+ WHERE statusgruppe_id IN (?)";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->range_id, $children]);
+ }
+
+ $old = $this->keep_children;
+ $this->keep_children = true;
+
+ $result = $this->delete();
+
+ $this->keep_children = $old;
+
+ return $result;
+ }
+
+ /**
+ * Deletes a status group and all it's child groups.
+ *
+ * @return int number of deleted groups
+ */
+ public function delete()
+ {
+ $result = 0;
+ if (!$this->keep_children) {
+ foreach($this->children as $child) {
+ $result += $child->delete();
+ }
+
+ }
+
+ // Remove datafields
+ $query = "DELETE FROM datafields_entries
+ WHERE range_id = ?";
+ $statement = DBManager::get()->prepare($query);
+ $statement->execute([$this->id]);
+
+ $result += parent::delete();
+
+ return $result;
+ }
+
+ /**
+ * Adds the next free position if position is null.
+ */
+ public function cbAddPosition()
+ {
+ if ($this->position === null) {
+ $sql = "SELECT MAX(position) FROM statusgruppen WHERE range_id = ?";
+ $stmt = DBManager::get()->prepare($sql);
+ $stmt->execute([$this->range_id]);
+ $max_position = $stmt->fetchColumn();
+ $this->position = $max_position === null ? 0 : $max_position + 1;
+ }
+ }
+
+ /**
+ * Reorders position after delete or for the assoicated range_id.
+ */
+ public function cbReorderPositions()
+ {
+ if (self::$performs_batch_operation) {
+ return;
+ }
+
+ self::reorderPositionsForRange($this->range_id);
+ }
+
+ /**
+ * Export available data of a given user into a storage object
+ * (an instance of the StoredUserData class) for that user.
+ *
+ * @param StoredUserData $storage object to store data into
+ */
+ public static function exportUserData(StoredUserData $storage)
+ {
+ $sorm = self::findThru($storage->user_id, [
+ 'thru_table' => 'statusgruppe_user',
+ 'thru_key' => 'user_id',
+ 'thru_assoc_key' => 'statusgruppe_id',
+ 'assoc_foreign_key' => 'statusgruppe_id',
+ ]);
+ if ($sorm) {
+ $field_data = [];
+ foreach ($sorm as $row) {
+ $field_data[] = $row->toRawArray();
+ }
+ if ($field_data) {
+ $storage->addTabularData(_('Statusgruppen'), 'statusgruppen', $field_data);
+ }
+ }
+ }
+}