aboutsummaryrefslogtreecommitdiff
path: root/lib/resources/ResourceManager.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/resources/ResourceManager.class.php')
-rw-r--r--lib/resources/ResourceManager.class.php1472
1 files changed, 0 insertions, 1472 deletions
diff --git a/lib/resources/ResourceManager.class.php b/lib/resources/ResourceManager.class.php
deleted file mode 100644
index a30c470..0000000
--- a/lib/resources/ResourceManager.class.php
+++ /dev/null
@@ -1,1472 +0,0 @@
-<?php
-
-/**
- * ResourceManager.class.php
- *
- * 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 Moritz Strohm <strohm@data-quest.de>
- * @copyright 2017-2018
- * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
- * @category Stud.IP
- */
-
-
-require_once 'lib/dates.inc.php';
-
-
-/**
- * The ResourceManager class contains methods that simplify the use of
- * Resources.
- */
-class ResourceManager
-{
- // Factory methods
-
- /**
- * Simplifies the creation of a resource category.
- *
- * @param string $name The name of the new resource category.
- * @param string $description The description of the new resource category.
- * @param string $class_name The class (derived from Resource) which
- * shall be used for resources created with the new category.
- * @param bool $is_system_category True, if the category shall be a system
- * category, false otherwise.
- * @param string $iconnr The number of the icon for the resource category.
- * @param mixed[] $properties A two-dimensional array with the
- * names and requestable, protected and system flags.
- * The second dimension of the array must have the following structure:
- * [
- * property name (string),
- * requestable flag (boolean),
- * protected flag (boolean),
- * system-flag (boolean)
- * ].
- *
- * @throws ResourceCategoryException In case if no name is set or a resource
- * property doesn't exist, if the category's name is ambigous
- * or if the new category cannot be stored, a ResourceCategoryException
- * is thrown.
- *
- * @return ResourceCategory A new ResourceCategory object.
- */
- public static function createCategory(
- $name = null,
- $description = null,
- $class_name = 'Resource',
- $is_system_category = false,
- $iconnr = '1',
- $properties = []
- )
- {
- if (!$name) {
- //A name must be set!
- throw new InvalidResourceCategoryException(
- _('Es wurde kein Name für die neue Ressourcenkategorie angegeben!')
- );
- }
-
- $property_data = [];
-
- //We must check, if all the properties exist:
- if ($properties && is_array($properties)) {
- foreach ($properties as $property) {
- $property_object = ResourcePropertyDefinition::findByName(
- $property[0]
- );
-
- if (!$property_object) {
- throw new ResourcePropertyException(
- sprintf(
- _('Die Ressourceneigenschaft %s ist nicht definiert!'),
- $property[0]
- )
- );
- } elseif (count($property_object) > 1) {
- throw new ResourcePropertyException(
- sprintf(
- _('Es gibt mehrere Ressourceneigenschaften mit dem Namen %s!'),
- $property[0]
- )
- );
- }
-
- //$property_object is an array of ResourcePropertyDefinition objects:
- $property_data[] = [
- 'object' => $property_object[0],
- 'config' => $property
- ];
- }
- }
-
- //Ok, all properties exist: We can create the category:
-
- $new_category = new ResourceCategory();
- $new_category->name = $name;
- if ($description != null) {
- $new_category->description = $description;
- }
- $new_category->class_name = $class_name;
- $new_category->system = ($is_system_category ? '1' : '0');
- $new_category->iconnr = $iconnr;
-
- if ($new_category->store()) {
- //Add the properties:
- foreach ($property_data as $p) {
- $rcp = new ResourceCategoryProperty();
- $rcp->category_id = $new_category->id;
- $rcp->property_id = $p['object']->id;
- $rcp->requestable = $p['config'][1];
- $rcp->protected = $p['config'][2];
- $rcp->system = $p['config'][3];
- $rcp->store();
- }
- //die();
- } else {
- throw new InvalidResourceCategoryException(
- sprintf(
- _('Die Ressourcenkategorie %s konnte nicht gespeichert werden!'),
- $name
- )
- );
- }
-
- return $new_category;
- }
-
- /**
- * Simplifies the creation of a location resource category.
- *
- * @param string $name The name of the new resource category.
- * @param string $description The description of the new resource category.
- * @param string[] $additional_properties A two-dimensional array with the
- * names and requestable, protected and system flags. See
- * ResourceManager::createCategory for a description of the format
- * of the second array dimension.
- *
- * @throws ResourceCategoryException In case if no name is set or a resource
- * property doesn't exist, if the category's name is ambigous
- * or if the new category cannot be stored, a ResourceCategoryException
- * is thrown.
- *
- * @return ResourceCategory A new ResourceCategory object.
- */
- public static function createLocationCategory(
- $name = null,
- $description = null,
- $additional_properties = []
- )
- {
- $property_names = array_merge(
- [
- [
- 'geo_coordinates',
- true,
- true,
- true
- ]
- ],
- $additional_properties
- );
-
- return self::createCategory(
- $name,
- $description,
- 'Location',
- false,
- '1',
- $property_names
- );
- }
-
-
- /**
- * Simplifies the creation of a building resource category.
- *
- * @param string $name The name of the new resource category.
- * @param string $description The description of the new resource category.
- * @param string[] $additional_properties A two-dimensional array with the
- * names and requestable, protected and system flags. See
- * ResourceManager::createCategory for a description of the format
- * of the second array dimension.
- *
- * @throws ResourceCategoryException In case if no name is set or a resource
- * property doesn't exist, if the category's name is ambigous
- * or if the new category cannot be stored, a ResourceCategoryException
- * is thrown.
- *
- * @return ResourceCategory A new ResourceCategory object.
- */
- public static function createBuildingCategory(
- $name = null,
- $description = null,
- $additional_properties = []
- )
- {
- $property_names = array_merge(
- [
- [
- 'address',
- false,
- true,
- true
- ],
- [
- 'accessible',
- false,
- true,
- true
- ],
- [
- 'number',
- false,
- true,
- true
- ],
- [
- 'geo_coordinates',
- true,
- true,
- true
- ]
- ],
- $additional_properties
- );
-
- return self::createCategory(
- $name,
- $description,
- 'Building',
- false,
- '2',
- $property_names
- );
- }
-
-
- /**
- * Simplifies the creation of a room resource category.
- *
- * @param string $name The name of the new resource category.
- * @param string $description The description of the new resource category.
- * @param string[] $additional_properties A two-dimensional array with the
- * names and requestable, protected and system flags. See
- * ResourceManager::createCategory for a description of the format
- * of the second array dimension.
- *
- * @throws ResourceCategoryException In case if no name is set or a resource
- * property doesn't exist, if the category's name is ambigous
- * or if the new category cannot be stored, a ResourceCategoryException
- * is thrown.
- *
- * @return ResourceCategory A new ResourceCategory object.
- */
- public static function createRoomCategory(
- $name = null,
- $description = null,
- $additional_properties = []
- )
- {
- $property_names = array_merge(
- [
- [
- 'room_type',
- true,
- true,
- true
- ],
- [
- 'seats',
- true,
- true,
- true
- ],
- [
- 'booking_plan_is_public',
- true,
- true,
- true
- ]
- ],
- $additional_properties
- );
-
- return self::createCategory(
- $name,
- $description,
- 'Room',
- false,
- '3',
- $property_names
- );
- }
-
-
- // Resource methods:
-
- /**
- * Creates a copy of a resource and stores the copy in the database.
- *
- * @param Resource $resource The resource which shall be copied.
- * @param bool $copy_hierarchy True, if the resource's children shall also
- * be copied (default). False otherwise.
- * @param string $new_parent_id If this is set the original parent_id will
- * be overwritten with the ID in $new_parent_id.
- *
- * @throws ResourceException If the copy cannot be stored.
- *
- * @returns Resource A copy of the resource.
- */
- public static function copyResource(
- Resource $resource,
- $copy_hierarchy = true,
- $new_parent_id = null
- )
- {
- //We can clone all the data but we must explicitly
- //create a new ID and set the new flag of the copy
- //to prevent updating the original object.
- $copy = clone $resource;
- $copy->id = $copy->getNewId();
- $copy->setNew(true);
- if ($new_parent_id) {
- $copy->parent_id = $new_parent_id;
- }
- if (!$copy->store()) {
- throw new ResourceException();
- }
- if ($copy_hierarchy) {
- //get all children of the original resource and clone them, too.
- //If $copy_hierarchy is set all children and their descendants etc.
- //are cloned recursively.
- $children = $resource->children;
- foreach ($children as $child) {
- $copied_child = self::copyResource(
- $child,
- $copy_hierarchy,
- $copy->id //$copy is the new parent node.
- );
- }
- }
- return $copy;
- }
-
- /**
- * Moves a resource below another resource and does checks to prevent
- * resource hierarchies with misplaced resource objects.
- * This is just a convenience method which calls the addChild method
- * of the destination resource.
- *
- * @param Resource $target The resource which shall be moved.
- * @param Resource $destination The resource where $target shall be a new child.
- *
- * @throws InvalidArgumentException If $target cannot be placed below $destination.
- *
- * @returns bool True, if $target was successful placed below $destination.
- */
- public static function moveResource(Resource $target, Resource $destination)
- {
- return $destination->addChild($target);
- }
-
-
- //Resource retrieval methods:
-
-
- /**
- * Helper method that creates the identical SQL query for the
- * countUserResources and findUserResources methods.
- */
- protected static function getUserResourcesSqlData(
- User $user,
- $level = 'user',
- $time = null,
- $class_names = []
- )
- {
- $used_time = time();
- if ($time instanceof DateTime) {
- $used_time = $time->getTimestamp();
- } elseif ($time) {
- $used_time = $time;
- }
-
- $sql = '';
-
- if (count($class_names)) {
- //Make sure that all class names specify names
- //of classes derived from the Resource class.
- $valid_class_names = [];
- foreach ($class_names as $class_name) {
- if (is_a($class_name, 'Resource', true)) {
- $valid_class_names[] = $class_name;
- }
- }
- $class_names = $valid_class_names;
- }
- if (count($class_names)) {
- $sql .= 'INNER JOIN resource_categories rc
- ON resources.category_id = rc.id
- WHERE ';
- }
-
- $user_is_resource_admin = self::userHasGlobalPermission(
- $user,
- 'admin'
- ) || $GLOBALS['perm']->have_perm('root', $user->id);
-
- if (!$user_is_resource_admin) {
- $sql .= "resources.id IN (
- SELECT resource_id FROM resource_permissions
- WHERE user_id = :user_id
- AND perms IN ( :perms )
- UNION
- SELECT resource_id FROM resource_temporary_permissions
- WHERE user_id = :user_id
- AND perms IN ( :perms )
- AND (begin <= :time)
- AND (end >= :time)
- ) ";
- $data = [
- 'user_id' => $user->id,
- 'time' => $used_time
- ];
- }
-
- if (count($class_names) && !$user_is_resource_admin) {
- $sql .= 'AND ';
- }
-
- if (count($class_names)) {
- $sql .= "rc.class_name IN ( :class_names ) ";
- $data['class_names'] = $class_names;
- }
- $sql .= "GROUP BY resources.id
- ORDER BY sort_position DESC, resources.name ASC";
-
- $perms = self::getHigherPermissionLevels($level);
- array_push($perms, $level);
- $data['perms'] = $perms;
-
- return [
- 'query' => $sql,
- 'data' => $data
- ];
- }
-
-
- /**
- * Counts all resources for which the specified user has permanent or
- * temporary permissions.
- *
- * @param User $user The user whose resources shall be retrieved.
- *
- * @param string $level The minimum permission level the user must have
- * on a resource so that it will be included in the result set.
- *
- * @param DateTime|int|null $time The timestamp for the check on
- * temporary permissions. If this parameter is not set
- * the current timestamp will be used.
- *
- * @param string[] $class_names A list of resource classes that will
- * be used to filter the result set so that only resources being
- * a member of one of the specified resource classes will be retrieved.
- *
- */
- public static function countUserResources(
- User $user,
- $level = 'user',
- $time = null,
- $class_names = []
- )
- {
- $sql = self::getUserResourcesSqlData($user, $level, $time, $class_names);
- return Resource::countBySql($sql['query'], $sql['data']);
- }
-
-
- /**
- * Retrieves all resources for which the specified user has permanent or
- * temporary permissions.
- *
- * @param User $user The user whose resources shall be retrieved.
- *
- * @param string $level The minimum permission level the user must have
- * on a resource so that it will be included in the result set.
- *
- * @param DateTime|int|null $time The timestamp for the check on
- * temporary permissions. If this parameter is not set
- * the current timestamp will be used.
- *
- * @param string[] $class_names A list of resource classes that will
- * be used to filter the result set so that only resources being
- * a member of one of the specified resource classes will be retrieved.
- *
- * @param bool $convert_objects If the resource objects
- * in the result set shall be converted to objects of the derived
- * resource classes set this to true, otherwise false.
- * Defaults to true.
- *
- * @returns Resource[] An array of Resource objects
- * or objects of derived resource classes.
- */
- public static function getUserResources(
- User $user,
- $level = 'user',
- $time = null,
- $class_names = [],
- $convert_objects = true
- )
- {
- $sql = self::getUserResourcesSqlData($user, $level, $time, $class_names);
- $resources = Resource::findBySql($sql['query'], $sql['data']);
- if ($convert_objects) {
- $result = [];
- foreach ($resources as $resource) {
- $result[] = $resource->getDerivedClassInstance();
- }
- return $result;
- } else {
- return $resources;
- }
- }
-
- /**
- * Check if the coordinate are in appropriate CRSWGS_84 format.
- *
- * - latitude: up to 2 digits, decimal point, 1 to 10 digits for fraction
- * - longitude: up to 3 digits, decimal point, 1 to 10 digits for fraction
- * - altitude: up to 5 digits, decimal point, 1 to 10 digits for fraction
- *
- * @param string $coordinate_string
- * @return bool
- */
- public static function validateCoordinates(string $coordinate_string): bool
- {
- return preg_match(
- ResourcePropertyDefinition::CRSWGS84_REGEX,
- $coordinate_string
- );
- }
-
-
- // Static methods for position properties:
-
- public static function getPositionArray(ResourceProperty $property)
- {
- if (!$property->definition) {
- //An orphaned Resource property: we cannot generate an array for it!
- throw new ResourcePropertyDefinitionException(
- _('Die Positionsangabe kann nicht umgewandelt werden, da die angegebene Ressourceneigenschaft verwaist (ohne zugehörige Definition) ist!')
- );
- }
-
- if ($property->definition->type != 'position') {
- //We cannot generate an array for attributes other than the position type!
- throw new ResourcePropertyException(
- _("Die Positionsangabe kann nicht umgewandelt werden, da die angegebene Ressourceneigenschaft nicht vom Typ 'position' ist!")
- );
- }
-
- //Parse the ISO-6709 coordinates from $property->value:
- $coordinate_string = $property->state;
-
-
- // Show error message when coordinates are invalid
- if (!self::validateCoordinates($coordinate_string)) {
- PageLayout::postError(_('Die Positionsangabe kann nicht umgewandelt werden, da sie ungültige Daten enthält!'));
- }
-
- //With the first split we separate the numbers in the coordinate string.
- //The second split lets us retrieve the sign for each number.
-
- $coordinate_parts = preg_split(
- '/([+-]|(CRSWGS_84\/))+/',
- $coordinate_string,
- -1,
- PREG_SPLIT_NO_EMPTY
- );
-
- //We can simply split the coordinate string by each dot,
- //since there has to be a dot in every three coordinates!
- //This is because the coordinates are always stored
- //with decimal separators.
-
- $coordinate_signs = preg_split(
- '/\.\d{1,10}/',
- $coordinate_string,
- -1,
- PREG_SPLIT_NO_EMPTY
- );
-
- //The array position of $coordinate_parts and $coordinate_signs
- //are the same! We can directly use the indexes.
- //If a sign index is less than zero we must invert the value
- //of the corresponding coordinate part.
-
- for ($i = 0; $i < 3; $i++) {
- if ($coordinate_signs[$i] < 0) {
- $coordinate_parts[$i] *= -1;
- }
- }
-
- return $coordinate_parts;
- }
-
-
- /**
- * This method allows locking the resource management globally.
- * The user who creates the lock must have admin permissions
- * and the time interval must not lie in another global resource lock
- * interval.
- *
- * @param User $user The user who wishes to lock the room and resource
- * management globally.
- * @param DateTime $begin The begin timestamp of the lock.
- * @param DateTime $end The end timestamp of the lock.
- *
- * @throws InvalidArgumentException If $begin lies after $end or if
- * $begin is equal to $end.
- * @throws ResourcePermissionException If the specified user does not
- * have sufficient permissions to lock the resource management globally.
- * @throws GlobalResourceLockException If the resource lock could not be stored.
- *
- * @returns GlobalResourceLock object.
- */
- public function createGlobalLock(
- User $user,
- DateTime $begin,
- DateTime $end,
- $ignore_bookings = false
- )
- {
- if ($begin > $end) {
- throw new InvalidArgumentException(
- _('Der Startzeitpunkt darf nicht hinter dem Endzeitpunkt liegen!')
- );
- }
- if ($begin == $end) {
- throw new InvalidArgumentException(
- _('Startzeitpunkt und Endzeitpunkt dürfen nicht identisch sein!')
- );
- }
-
- if (!self::userHasGlobalPermission($user, 'admin')) {
- throw new ResourcePermissionException(
- _('Unzureichende Berechtigungen zum globalen Sperren der Raumverwaltung!')
- );
- }
-
- if (GlobalResourceLock::existsInTimeRange($begin, $end)) {
- throw new GlobalResourceLockOverlapException(
- sprintf(
- _('Im Zeitbereich vom %1$s bis %2$s gibt es bereits eine globale Sperrung der Raumverwaltung!'),
- $begin->format('d.m.Y H:i'),
- $end->format('d.m.Y H:i')
- )
- );
- }
-
- $lock = new GlobalResourceLock();
- $lock->begin = $begin->getTimestamp();
- $lock->end = $end->getTimestamp();
- $lock->user_id = $user->id;
- $lock->type = '0';
- if (!$lock->store()) {
- throw new GlobalResourceLockException(
- sprintf(
- _('Fehler beim Speichern der globalen Sperre der Raumverwaltung im Zeitbereich vom %1$s bis %2$s!'),
- $begin->format('d.m.Y H:i'),
- $end->format('d.m.Y H:i')
- )
- );
- }
- return $lock;
- }
-
-
- //Special methods for attributes of type position:
-
- public static function getPositionString(
- ResourceProperty $property,
- $with_altitude = false
- )
- {
- $coordinate_parts = [];
- $string = '';
- try {
- $coordinate_parts = self::getPositionArray($property);
- } catch (ResourcePropertyDefinitionException $e) {
- //An orphaned Resource property: we cannot generate a string for it!
- throw new ResourcePropertyDefinitionException(
- _('Die Positionsangabe kann nicht formatiert werden, da die angegebene Ressourceneigenschaft verwaist (ohne zugehörige Definition) ist!')
- );
- } catch (ResourcePropertyException $e) {
- //We cannot generate a string for attributes other than the position type!
- throw new ResourcePropertyException(
- _('Die Positionsangabe kann nicht formatiert werden, da die angegebene Ressourceneigenschaft nicht vom Typ "position" ist!')
- );
- } catch (ResourcePropertyStateException $e) {
- //We cannot generate a string from invalid data!
- return '';
- }
-
- $locale = localeconv();
-
- if ($coordinate_parts[0] < 0) {
- $string .= sprintf(
- _('%s°S'),
- number_format(
- abs($coordinate_parts[0]),
- 7,
- $locale['decimal_point'],
- $locale['thousands_sep']
- )
- ) . ' ';
- } else {
- $string .= sprintf(
- _('%s°N'),
- number_format(
- abs($coordinate_parts[0]),
- 7,
- $locale['decimal_point'],
- $locale['thousands_sep']
- )
- ) . ' ';
- }
-
- if ($coordinate_parts[1] < 0) {
- $string .= sprintf(
- _('%s°O'),
- number_format(
- abs($coordinate_parts[1]),
- 7,
- $locale['decimal_point'],
- $locale['thousands_sep']
- )
- ) . ' ';
- } else {
- $string .= sprintf(
- _('%s°W'),
- number_format(
- abs($coordinate_parts[1]),
- 7,
- $locale['decimal_point'],
- $locale['thousands_sep']
- )
- );
- }
-
- if ($with_altitude) {
- $string .= ' ';
- if ($coordinate_parts[2] < 0) {
- $string .= sprintf(
- _('%s m unter NHN'),
- number_format(
- abs($coordinate_parts[2]),
- 1,
- $locale['decimal_point'],
- $locale['thousands_sep']
- )
- );
- } else {
- $string .= sprintf(
- _('%s m über NHN'),
- number_format(
- abs($coordinate_parts[2]),
- 1,
- $locale['decimal_point'],
- $locale['thousands_sep']
- )
- );
- }
- }
-
- return $string;
- }
-
-
- public static function getMapUrlForResourcePosition(
- ResourceProperty $property
- )
- {
- $coordinate_parts = [];
- try {
- $coordinate_parts = self::getPositionArray($property);
- } catch (ResourcePropertyDefinitionException $e) {
- //An orphaned Resource property: we cannot generate an URL for it!
- throw new InvalidArgumentException(
- _('Eine URL zur Straßenkarte kann nicht erzeugt werden, da die angegebene Ressourceneigenschaft verwaist (ohne zugehörige Definition) ist!')
- );
- } catch (ResourcePropertyException $e) {
- //We cannot generate an URL for attributes other than the position type!
- throw new InvalidArgumentException(
- _("Eine URL zur Straßenkarte kann nicht erzeugt werden, da die angegebene Ressourceneigenschaft nicht vom Typ 'position' ist!")
- );
- } catch (ResourcePropertyStateException $e) {
- //We cannot generate an URL from invalid data!
- return '';
- }
-
- $map_service_url = Config::get()->RESOURCES_MAP_SERVICE_URL;
- if ($map_service_url) {
- //Replace the strings LATITUDE and LONGITUDE in the URL
- //with the coordinates.
- return str_replace(
- [
- 'LATITUDE',
- 'LONGITUDE'
- ],
- [
- $coordinate_parts[0],
- $coordinate_parts[1]
- ],
- $map_service_url
- );
- } else {
- //Default to OpenStreepMap:
- return sprintf(
- 'https://www.openstreetmap.org/#map=17/%1$s/%2$s',
- $coordinate_parts[0],
- $coordinate_parts[1]
- );
- }
- }
-
-
- // User permission methods:
-
- /**
- * Returns the resource management global permissions for a user,
- * determined by the assigned roles and by the user's global permissions.
- * This method does the mapping from the old resource management permissions
- * to the new resource management permissions.
- */
- public static function getGlobalResourcePermission(User $user = null)
- {
- if (!$user) {
- return '';
- }
-
- global $perm;
- //First we check if the user is a root user:
-
- if ($perm->get_perm($user->id) === 'root') {
- return 'admin';
- }
-
- //The user is not a root user:
- //We must check if he has special permissions
- //for the virtual resource with id "global":
-
- $permission = ResourcePermission::findOneBySql(
- "user_id = :user_id AND resource_id = 'global'",
- [
- 'user_id' => $user->id
- ]
- );
-
- if (!$permission) {
- //No global permissions in the resource management:
- return '';
- }
-
- return $permission->perms;
- }
-
-
- /**
- * Determines if the specified user has the specified permission level set
- * for at least one resource.
- *
- * @param User $user The users whose resource permissions shall be retrieved.
- * @param string $level The permission level the user should have
- * on at least one resource.
- * @param string|int|DateTime|null $time The timestamp
- * for the temporary permission level check.
- * If this is not set the current timestamp will be used.
- */
- public static function userHasResourcePermissions(
- User $user = null,
- $level = 'admin',
- $time = null
- )
- {
- if (!$user) {
- return false;
- }
-
- //Get all permissions and temporary permissions of the user:
-
- $permissions = ResourcePermission::findBySQL(
- "user_id = :user_id AND resource_id <> 'global'",
- [
- 'user_id' => $user->id
- ]
- );
-
- if ($permissions) {
- foreach ($permissions as $permission) {
- if (self::comparePermissionLevels($permission->perms, $level) >= 0) {
- //We have found a permission which is higher or equal
- //to the requested permission level and can therefore
- //return true:
- return true;
- }
- }
- }
-
- //No (sufficient) permanent permissions exist for the user
- //for at least one resource. We must check the temporary permissions:
-
- $used_time = time();
- if ($time instanceof DateTime) {
- $used_time = $time->getTimestamp();
- } elseif ($time) {
- $used_time = $time;
- }
-
- $temp_permissions = ResourceTemporaryPermission::findBySql(
- "user_id = :user_id AND begin <= :time AND end >= :time
- AND resource_id != 'global'",
- [
- 'user_id' => $user->id,
- 'time' => $used_time
- ]
- );
-
- if ($temp_permissions) {
- foreach ($temp_permissions as $permission) {
- if (self::comparePermissionLevels($permission->perms, $level) >= 0) {
- //We have found a temporary permission which is higher or
- //equal to the requested permission level and can therefore
- //return true:
- return true;
- }
- }
- }
-
- //We haven't found any permanent or temporary permission for the user
- //that match the requested permission level on the specified timestamp.
- return false;
- }
-
-
- //Helper methods:
-
-
- /**
- * Returns all permission levels lower than the specified level.
- */
- public static function getLowerPermissionLevels($level = 'user')
- {
- $defined_levels = ['user', 'autor', 'tutor', 'admin'];
- if (!in_array($level, $defined_levels)) {
- return [];
- }
-
- if ($level == 'admin') {
- return array_slice($defined_levels, 0, 3);
- } elseif ($level == 'tutor') {
- return array_slice($defined_levels, 0, 2);
- } elseif ($level == 'autor') {
- return array_slice($defined_levels, 0, 1);
- } else {
- //There is no lower authority than user:
- return [];
- }
- }
-
-
- /**
- * Returns all permission levels higher than the specified level.
- */
- public static function getHigherPermissionLevels($level = 'user')
- {
- $defined_levels = ['user', 'autor', 'tutor', 'admin'];
- if (!in_array($level, $defined_levels)) {
- return [];
- }
-
- if ($level == 'admin') {
- //There is no higher authority than admin:
- return [];
- } elseif ($level == 'tutor') {
- return array_slice($defined_levels, -1, 1);
- } elseif ($level == 'autor') {
- return array_slice($defined_levels, -2, 2);
- } elseif ($level == 'user') {
- return array_slice($defined_levels, -3, 3);
- } else {
- //We haven't found what you're looking for:
- return [];
- }
- }
-
-
- /**
- * Compares two resource permission levels and returns an integer telling if
- * the first level is less than (-1), equal (0) or greater than (1) the second level.
- *
- * @param string $level The first permission level.
- * @param string $other_level The second permission level.
- *
- * @throws InvalidArgumentException if $level or $other_level are either not set
- * or if they contain invalid permission level strings.
- * @returns integer -1 if $level is less than $other_level,
- * 0 if both are equal,
- * 1 if $level is greater than $other_level.
- */
- public static function comparePermissionLevels(
- $level = 'user',
- $other_level = 'user'
- )
- {
- if (!$level or !$other_level) {
- throw new InvalidArgumentException(
- _('Mindestens eine Rechtestufe fehlt zum Vergleich!')
- );
- }
-
- //The level list starts with the lowest permission level
- //and ends with the highest permission level.
- //The levels are compared by comparing the index values
- //of the list.
- $defined_levels = ['user', 'autor', 'tutor', 'admin'];
-
- if (!in_array($level, $defined_levels)) {
- throw new InvalidArgumentException(
- _('Die angegebene Rechtestufe ist ungültig!')
- );
- }
- if (!in_array($other_level, $defined_levels)) {
- throw new InvalidArgumentException(
- _('Die angegebene Rechtestufe ist ungültig!')
- );
- }
-
- $level_index = array_search($level, $defined_levels);
- $other_level_index = array_search($other_level, $defined_levels);
-
- $diff = $level_index - $other_level_index;
-
- if ($diff > 0) {
- //$level is a higher permission level than $other_level.
- return 1;
- } elseif ($diff < 0) {
- //$level is a lower permission level than $other_level.
- return -1;
- } else {
- //$level and $other_level are the same permission level.
- return 0;
- }
- }
-
-
- /**
- * Checks if the specified user has the specified permission level
- * for the resource management system.
- *
- * @param User|null $user The user whose global resource permissions shall be checked.
- * @param string $requested_permission The required permission level for the user.
- *
- * @returns bool True, if the user has the required permission level,
- * false otherwise.
- */
- public static function userHasGlobalPermission(
- User $user = null,
- $requested_permission = 'user'
- )
- {
- //ResourceManager::getGlobalResourcePermission also checks
- //for global resource locks and returns 'admin' if the
- //user is 'admin' user or 'user' in all other cases
- //where the user has permissions on the resource management system.
- $existing_permission = self::getGlobalResourcePermission($user);
-
- if (!$existing_permission) {
- //No permissions in the resource management:
- return false;
- }
-
- return self::comparePermissionLevels(
- $existing_permission,
- $requested_permission
- ) > -1;
- }
-
-
- /**
- * Counts the resources where the specified user has explicit
- * permissions set, optionally limiting the result to permanent
- * permissions.
- *
- * @param User $user The user whose resource permissions
- * shall be checked.
- * @param string $requested_permission The required minimum permission
- * level for the user.
- *
- * @return int The amount of resources where the specified user has
- * explicit permissions for.
- */
- public static function countResourcesWithPermissions(
- User $user,
- $requested_permission = 'user',
- $exclude_temporary_permissions = false
- )
- {
- $perms = self::getHigherPermissionLevels($requested_permission);
- array_push($perms, $requested_permission);
- $total = Resource::countBySql(
- "INNER JOIN resource_permissions rp
- USING (resource_id)
- WHERE
- rp.perms IN ( :perms )
- AND
- rp.user_id = :user_id",
- [
- 'perms' => $perms,
- 'user_id' => $user->id
- ]
- );
-
- if (!$exclude_temporary_permissions) {
- $now = time();
- $total += Resource::countBySql(
- "INNER JOIN resource_temporary_permissions rtp
- USING (resource_id)
- WHERE
- rtp.perms IN ( :perms )
- AND
- rtp.user_id = :user_id
- AND
- rtp.begin <= :now
- AND
- rtp.end >= :now",
- [
- 'perms' => $perms,
- 'user_id' => $user->id,
- 'now' => $now
- ]
- );
- }
-
- return $total;
- }
-
-
- /**
- * Checks if the specified user has the specified permission level
- * on at least one specific resource.
- *
- * @param User $user The user whose resource permissions
- * shall be checked.
- * @param string $requested_permission The required permission level
- * for the user.
- *
- * @returns bool True, if the user has the required permission level
- * for at least one resource, false otherwise.
- */
- public static function userHasSpecialPermissions(
- User $user,
- $requested_permission = 'user'
- )
- {
- return self::countResourcesWithPermissions(
- $user,
- $requested_permission
- ) > 0;
- }
-
-
- /**
- * Get time ranges by looking at the object specified by its ID.
- * This method works with CourseDate, SeminarCycleDate
- * and Course objects.
- *
- * @param string $range_id The ID of a Stud.IP object.
- *
- * @returns Array An Array consisting of arrays of DateTime objects.
- * The structure of the array is as follows:
- * [
- * [
- * begin timestamp DateTime object
- * end timestamp DateTime object
- * ],
- * ...
- * ]
- */
- public static function getTimeRangesFromRangeId($range_id = null)
- {
- if (!$range_id) {
- return [];
- }
-
- //We try a course date first, then a cycle date and finally
- //a course as this is the standard order to check for dates.
-
- $time_ranges = [];
-
- $course_date = CourseDate::find($range_id);
-
- if ($course_date) {
- $begin = new DateTime();
- $begin->setTimestamp($course_date->date);
- $end = new DateTime();
- $end->setTimestamp($course_date->end_time);
- $time_ranges[] = [
- $begin,
- $end
- ];
- } else {
- //No course date, but maybe a cycle date?
- $cycle_date = SeminarCycleDate::find($range_id);
-
- if ($cycle_date) {
- if ($cycle_date->dates) {
- foreach ($cycle_date->dates as $date) {
- $begin = new DateTime();
- $begin->setTimestamp($date->date);
- $end = new DateTime();
- $end->setTimestamp($date->end_time);
- $time_ranges[] = [
- $begin,
- $end
- ];
- }
- }
- } else {
- //No cycle date. It must be a course then!
- $course = Course::find($range_id);
-
- if ($course) {
- if ($course->dates) {
- foreach ($course->dates as $date) {
- $begin = new DateTime();
- $begin->setTimestamp($date->date);
- $end = new DateTime();
- $end->setTimestamp($date->end_time);
- $time_ranges[] = [
- $begin,
- $end
- ];
- }
- }
- if ($course->cycles) {
- foreach ($course->cycles as $cycle) {
- if ($cycle->dates) {
- foreach ($cycle->dates as $date) {
- $begin = new DateTime();
- $begin->setTimestamp($date->date);
- $end = new DateTime();
- $end->setTimestamp($date->end_time);
- $time_ranges[] = [
- $begin,
- $end
- ];
- }
- }
- }
- }
- }
- //No else here: Enough is enough.
- }
- }
-
- return $time_ranges;
- }
-
-
- /**
- * Determines the last booking of the user and calculates the timespan
- * from the last booking until now.
- *
- * @returns DateInterval|null|false Either a date interval from the last
- * activity to the provided DateTime object or null in case the user
- * has never been active. False is returned in case the timestamp
- * comparison fails. @see DateTime::diff in the PHP Documentation.
- */
- public static function getUserInactivityInterval(User $user, DateTime $time)
- {
- $db = DBManager::get();
-
- $stmt = $db->prepare(
- "SELECT MAX(mkdate) FROM resource_bookings
- WHERE range_id = :user_id"
- );
-
- $stmt->execute(['user_id' => $user->id]);
-
- $last_activity_timestamp = $stmt->fetchColumn();
-
- if (($last_activity_timestamp === false) or ($last_activity_timestamp === null)) {
- //No activity found
- return null;
- }
-
- $last_activity = new DateTime();
- $last_activity->setTimestamp($last_activity_timestamp);
-
- //Calculate the difference between the last activity and $time,
- //if $time is greater or equal than $last_activity.
- //If $time is less than $last_activity return null since the user
- //has not been active at the timestamp $time.
-
- if ($time < $last_activity) {
- //The user has not been active at $time.
- return null;
- }
-
- return $time->diff($last_activity);
- }
-
-
- /**
- * Retrieves booking plan objects like resource bookings and requests.
- *
- * @param string|null $included_request_types If this parameter is a string,
- * it can have the values 'all' for retrieving all requests or the
- * ID of a user so that only requests of that user are retrieved.
- *
- */
- public static function getBookingPlanObjects(
- Resource $resource,
- $time_ranges = [],
- $allowed_booking_types = [],
- $included_requests = null
- )
- {
- if (!count($time_ranges)) {
- return [];
- }
-
- $objects = [];
-
- $bookings = \ResourceBooking::findByResourceAndTimeRanges(
- $resource,
- $time_ranges,
- $allowed_booking_types
- );
- $objects = array_merge($objects, $bookings);
-
- if ($included_requests == 'all') {
- $requests = \ResourceRequest::findByResourceAndTimeRanges(
- $resource,
- $time_ranges,
- 0
- );
- $objects = array_merge($objects, $requests);
- } elseif ($included_requests) {
- $requests = \ResourceRequest::findByResourceAndTimeRanges(
- $resource,
- $time_ranges,
- 0,
- [],
- 'user_id = :user_id',
- ['user_id' => $included_requests]
- );
- $objects = array_merge($objects, $requests);
- }
-
- return $objects;
- }
-
-
- public static function getAllResourceClassNames($excluded_classes = [])
- {
- $class_names = [];
- //We have to make the autoloader load all resource model classes,
- //otherwise the get_declared_classes() statement below won't find
- //any class derived from Resource!
- foreach (
- scandir($GLOBALS['STUDIP_BASE_PATH'] . '/lib/models/resources')
- as $resource_model_file) {
- $path = pathinfo($resource_model_file);
-
- if ($path['extension'] == 'php') {
- $class_name = explode('.class', $path['filename'])[0];
- class_exists($class_name);
- }
- }
-
- foreach (get_declared_classes() as $class_name) {
- if (is_a($class_name, 'Resource', true)) {
- foreach ($excluded_classes as $excl_class) {
- //For the resource base class, we must not check
- //derived classes.
- if ($excl_class == 'Resource') {
- if ($class_name == 'Resource') {
- continue 2;
- }
- } else {
- if (is_a($class_name, $excl_class, true)) {
- //The class belongs to one of the
- //excluded resource classes.
- continue 2;
- }
- }
- }
- $class_names[] = $class_name;
- }
- }
- sort($class_names);
-
- return $class_names;
- }
-
-
- /**
- * Returns the names of all hierarchy elements from the root to the
- * specified resource.
- *
- * @param Resource $room The resource to start with.
- *
- * @returns string[] An array with the names of the hierarchy elements,
- * starting with the top resource's name.
- */
- public static function getHierarchyNames(Resource $resource)
- {
- $names = [$resource->name];
- $current_node = $resource->parent;
- while ($current_node instanceof Resource) {
- $names[] = $current_node->name;
- $current_node = $current_node->parent;
- }
- return array_reverse($names);
- }
-
-
- /**
- * Returns the hierarchy elements from the root to the
- * specified resource.
- *
- * @param Resource $room The resource to start with.
- *
- * @returns Resource[] An array with the hierarchy elements,
- * starting with the top resource.
- */
- public static function getHierarchy(Resource $resource)
- {
- $hierarchy_ids = [$resource->id];
- $items = [$resource];
- $current_node = $resource->parent;
- while ($current_node instanceof Resource) {
- if (in_array($current_node->id, $hierarchy_ids)) {
- //Circular hierarchy. That's a big error!
- throw new InvalidResourceException(
- sprintf(
- _('Zirkuläre Hierarchie: Die Ressource %1$s ist ein Elternknoten von sich selbst!'),
- $current_node->name
- )
- );
- }
- $items[] = $current_node;
- $hierarchy_ids[] = $current_node->id;
- $current_node = $current_node->parent;
- }
- return array_reverse($items);
- }
-}