Skip to content
Snippets Groups Projects
Select Git revision
  • 4ca7069678a88da56ce75038c1599d96409f2d9b
  • main default protected
  • step-3263
  • feature/plugins-cli
  • feature/vite
  • step-2484-peerreview
  • biest/issue-5051
  • tests/simplify-jsonapi-tests
  • fix/typo-in-1a70031
  • feature/broadcasting
  • database-seeders-and-factories
  • feature/peer-review-2
  • feature-feedback-jsonapi
  • feature/peerreview
  • feature/balloon-plus
  • feature/stock-images-unsplash
  • tic-2588
  • 5.0
  • 5.2
  • biest/unlock-blocks
  • biest-1514
21 results

SemClass.class.php

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    SemClass.class.php 22.99 KiB
    <?php
    
    /*
     *  Copyright (c) 2012  Rasmus Fuhse <fuhse@data-quest.de>
     *
     *  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.
     */
    
    /**
     * Class to define and manage attributes of seminar classes (or seminar categories).
     * Usually all sem-classes are stored in a global variable $SEM_CLASS which is
     * an array of SemClass objects.
     *
     * SemClass::getClasses() gets you all seminar classes in an array.
     *
     * You can access the attributes of a sem-class like an associative
     * array with $sem_class['default_read_level']. The uinderlying data is stored
     * in the database in the table sem_classes.
     *
     * If you want to have a name of a sem-class like "Lehre", please use
     * $sem_class['name'] and you will get a fully localized name and not the pure
     * database entry.
     *
     * This class manages also which modules are contained in which course-slots,
     * like "what module is used as a forum in my seminars". In the database stored
     * is the name of the module like "CoreForum" or a classname of a plugin or null
     * if the forum is completely disabled by root for this sem-class. Core-modules
     * can only be used within a standard slot. Plugins may also be used as optional
     * modules not contained in a slot.
     *
     * In the field 'modules' in the database is for each modules stored in a json-string
     * if the module is activatable by the teacher or not and if it is activated as
     * a default. Please use the methods SemClass::isSlotModule, SemClass::getSlotModule,
     * SemClass::isModuleAllowed, SemClass::isModuleMandatory, SemClass::isSlotMandatory
     * or even more simple SemClass::getNavigationForSlot (see documentation there).
     */
    class SemClass implements ArrayAccess
    {
        protected $data = [];
    
        static protected $studygroup_forbidden_modules = [
            'CoreAdmin',
            'CoreParticipants',
            'CoreSchedule'
        ];
    
        static protected $sem_classes = null;
    
        static public function getDefaultSemClass() {
            $data = [
                'name' => "Fehlerhafte Seminarklasse!",
                'modules' => '{"CoreOverview":{"activated":1,"sticky":1},"CoreAdmin":{"activated":1,"sticky":1}}',
                'visible' => 1,
                'is_group' => false
            ];
            return new SemClass($data);
        }
    
        /**
         * Generates a dummy SemClass for institutes of this type (as defined in config.inc.php).
         * @param integer $type   institute type
         * @return SemClass
         */
        static public function getDefaultInstituteClass($type)
        {
            global $INST_MODULES;
    
            // fall back to 'default' if modules are not defined
            $type = isset($INST_MODULES[$type]) ? $type : 'default';
    
            $data = [
                'name'                => 'Generierte Standardinstitutsklasse',
                'visible'             => 1,
                'overview'            => 'CoreOverview', // always available
                'admin'               => 'CoreAdmin'     // always available
            ];
            $slots = [
                'forum'               => 'CoreForum',
                'documents'           => 'CoreDocuments',
                'scm'                 => 'CoreScm',
                'wiki'                => 'CoreWiki',
                'calendar'            => 'CoreCalendar',
                'elearning_interface' => 'CoreElearningInterface',
                'personal'            => 'CorePersonal'
            ];
            $modules = [
                'CoreOverview'        => ['activated' => 1, 'sticky' => 1],
                'CoreAdmin'           => ['activated' => 1, 'sticky' => 1]
            ];
    
            foreach ($slots as $slot => $module) {
                $data[$slot] = $module;
                $modules[$module] = ['activated' => (int) ($INST_MODULES[$type][$slot] ?? 0), 'sticky' => 0];
            }
            $data['modules'] = json_encode($modules);
    
            return new SemClass($data);
        }
    
        /**
         * Constructor can be set with integer of sem_class_id or an array of
         * the old $SEM_CLASS style.
         * @param integer | array $data
         */
        public function __construct($data)
        {
            $db = DBManager::get();
            if (is_int($data)) {
                $statement = $db->prepare("SELECT * FROM sem_classes WHERE id = :id ");
                $statement->execute(['id' => $data]);
                $this->data = $statement->fetch(PDO::FETCH_ASSOC);
            } else {
                $this->data = $data;
            }
            if (!empty($this->data['modules'])) {
                $this->data['modules'] = self::object2array(json_decode($this->data['modules']));
    
            } else {
                $this->data['modules'] = [];
            }
            if (!empty($this->data['studygroup_mode'])) {
                if (!isset($this->data['modules']['CoreStudygroupAdmin'])) {
                    $this->data['modules']['CoreStudygroupAdmin'] = ['activated' => 1, 'sticky' => 1];
                }
            } else {
                if (!isset($this->data['modules']['CoreAdmin'])) {
                    $this->data['modules']['CoreAdmin'] = ['activated' => 1, 'sticky' => 1];
                }
            }
            foreach (array_keys($this->data['modules']) as $modulename) {
                if ($this->isModuleForbidden($modulename)) {
                    unset($this->data['modules'][$modulename]);
                }
            }
        }
    
    
        /**
         * @param string $module
         * @return false|int
         */
        public function activateModuleInCourses($module)
        {
            $plugin = PluginManager::getInstance()->getPlugin($module);
            if ($plugin) {
                return Course::findEachBySQL(function ($course) use ($plugin) {
                    return PluginManager::getInstance()->setPluginActivated($plugin->getPluginId(), $course->id, true);
                },
                    "seminare.status IN (?)",
                    [array_keys($this->getSemTypes())]);
            } else {
                return false;
            }
        }
    
        /**
         * @param string $module
         * @return false|int
         */
        public function deActivateModuleInCourses($module)
        {
            $plugin = PluginManager::getInstance()->getPlugin($module);
            if ($plugin) {
                return Course::findEachBySQL(function ($course) use ($plugin) {
                    return PluginManager::getInstance()->setPluginActivated($plugin->getPluginId(), $course->id, false);
                },
                    "seminare.status IN (?)",
                    [array_keys($this->getSemTypes())]);
            } else {
                return false;
            }
    
        }
    
        /**
         * Returns the number of seminars of this sem_class in Stud.IP
         * @return integer
         */
        public function countSeminars()
        {
            $db = DBManager::get();
            $sum = 0;
            foreach ($GLOBALS['SEM_TYPE'] as $sem_type) {
                if ($sem_type['class'] == $this->data['id']) {
                    $sum += $sem_type->countSeminars();
                }
            }
            return $sum;
        }
    
    
        /**
         * @param string $modulename
         * @return bool
         */
        public function isModuleForbidden($modulename)
        {
            if (!empty($this->data['studygroup_mode'])) {
                return in_array($modulename, self::$studygroup_forbidden_modules);
            } else {
                return strpos($modulename, 'Studygroup') !== false;
            }
        }
    
        /**
         * Returns the metadata of a module regarding this sem_class object.
         * @param string $modulename
         * @return array('sticky' => (bool), 'activated' => (bool))
         */
        public function getModuleMetadata($modulename)
        {
            return $this->data['modules'][$modulename];
        }
    
        /**
         * Sets the metadata for each module at once.
         * @param array $module_array: array($module_name => array('sticky' => (bool), 'activated' => (bool)), ...)
         */
        public function setModules($module_array)
        {
            $this->data['modules'] = $module_array;
        }
    
        /**
         * Returns all metadata of the modules at once.
         * @return array: array($module_name => array('sticky' => (bool), 'activated' => (bool)), ...)
         */
        public function getModules()
        {
            return $this->data['modules'];
        }
    
        /**
         * @return StudipModule[]
         */
        public function getModuleObjects()
        {
            $result = [];
            foreach (array_keys($this->getModules()) as $module) {
                $plugin = PluginManager::getInstance()->getPlugin($module);
                if ($plugin) {
                    $result[$plugin->getPluginId()] = $plugin;
                }
            }
            return $result;
        }
    
        /**
         * @return string[]
         */
        public function getActivatedModules()
        {
            return array_keys(array_filter($this->data['modules'], function ($meta) {
                return $meta['activated'];
            }));
        }
    
        /**
         * @return StudipModule[]
         */
        public function getActivatedModuleObjects()
        {
            $result = [];
            foreach ($this->getActivatedModules() as $module) {
                $plugin = PluginManager::getInstance()->getPlugin($module);
                if ($plugin) {
                    $result[$plugin->getPluginId()] = $plugin;
                }
            }
            return $result;
        }
    
        /**
         * @return mixed|object
         */
        public function getAdminModuleObject()
        {
            if ($this->data['studygroup_mode']) {
                $module = 'CoreStudygroupAdmin';
            } else {
                $module = 'CoreAdmin';
            }
            return PluginManager::getInstance()->getPlugin($module);
        }
    
        /**
         * Returns true if a module is activated on default for this sem_class.
         * @param string $modulename
         * @return boolean
         */
        public function isModuleActivated($modulename)
        {
            return isset($this->data['modules'][$modulename])
                && $this->data['modules'][$modulename]['activated'];
        }
    
        /**
         * Returns if a module is allowed to be displayed for this sem_class.
         * @param string $modulename
         * @return boolean
         */
        public function isModuleAllowed($modulename)
        {
            return !$this->isModuleForbidden($modulename)
                && (empty($this->data['modules'][$modulename])
                || !$this->data['modules'][$modulename]['sticky']
                || $this->data['modules'][$modulename]['activated']);
        }
    
        /**
         * Returns if a module is mandatory for this sem_class.
         * @param string $module
         * @return boolean
         */
        public function isModuleMandatory($module)
        {
            return isset($this->data['modules'][$module])
                && $this->data['modules'][$module]['sticky']
                && $this->data['modules'][$module]['activated'];
        }
    
        public function getSemTypes()
        {
            $types = [];
            foreach (SemType::getTypes() as $id => $type) {
                if ($type['class'] == $this->data['id']) {
                    $types[$id] = $type;
                }
            }
            return $types;
        }
    
        /**
         * Checks if the current sem class is usable for course grouping.
         */
        public function isGroup()
        {
            return $this->data['is_group'];
        }
    
        /**
         * Checks if any SemClasses exist that provide grouping functionality.
         * @return SimpleCollection
         */
        public static function getGroupClasses()
        {
            return SimpleCollection::createFromArray(self::getClasses())->findBy('is_group', true);
        }
    
        /**
         * stores all data in the database
         * @return boolean success
         */
        public function store()
        {
            $db = DBManager::get();
            $statement = $db->prepare(
                "UPDATE sem_classes " .
                    "SET name = :name, " .
                    "description = :description, " .
                    "create_description = :create_description, " .
                    "studygroup_mode = :studygroup_mode, " .
                    "only_inst_user = :only_inst_user, " .
                    "default_read_level = :default_read_level, " .
                    "default_write_level = :default_write_level, " .
                    "bereiche = :bereiche, " .
                    "module = :module, " .
                    "show_browse = :show_browse, " .
                    "write_access_nobody = :write_access_nobody, " .
                    "topic_create_autor = :topic_create_autor, " .
                    "visible = :visible, " .
                    "course_creation_forbidden = :course_creation_forbidden, " .
                    "modules = :modules, " .
                    "title_dozent = :title_dozent, " .
                    "title_dozent_plural = :title_dozent_plural, " .
                    "title_tutor = :title_tutor, " .
                    "title_tutor_plural = :title_tutor_plural, " .
                    "title_autor = :title_autor, " .
                    "title_autor_plural = :title_autor_plural, " .
                    "admission_prelim_default = :admission_prelim_default, " .
                    "admission_type_default = :admission_type_default, " .
                    "show_raumzeit = :show_raumzeit, " .
                    "is_group = :is_group, " .
                    "chdate = UNIX_TIMESTAMP() " .
                "WHERE id = :id ".
            "");
            StudipCacheFactory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
            return $statement->execute([
                'id' => $this->data['id'],
                'name' => $this->data['name'],
                'description' => $this->data['description'],
                'create_description' => $this->data['create_description'],
                'studygroup_mode' => (int) $this->data['studygroup_mode'],
                'only_inst_user' => (int) $this->data['only_inst_user'],
                'default_read_level' => (int) $this->data['default_read_level'],
                'default_write_level' => (int) $this->data['default_write_level'],
                'bereiche' => (int) $this->data['bereiche'],
                'module' => (int) $this->data['module'],
                'show_browse' => (int) $this->data['show_browse'],
                'write_access_nobody' => (int) $this->data['write_access_nobody'],
                'topic_create_autor' => (int) $this->data['topic_create_autor'],
                'visible' => (int) $this->data['visible'],
                'course_creation_forbidden' => (int) $this->data['course_creation_forbidden'],
                'modules' => json_encode((object) $this->data['modules']),
                'title_dozent' => $this->data['title_dozent']
                    ? $this->data['title_dozent']
                    : null,
                'title_dozent_plural' => $this->data['title_dozent_plural']
                    ? $this->data['title_dozent_plural']
                    : null,
                'title_tutor' => $this->data['title_tutor']
                    ? $this->data['title_tutor']
                    : null,
                'title_tutor_plural' => $this->data['title_tutor_plural']
                    ? $this->data['title_tutor_plural']
                    : null,
                'title_autor' => $this->data['title_autor']
                    ? $this->data['title_autor']
                    : null,
                'title_autor_plural' => $this->data['title_autor_plural']
                    ? $this->data['title_autor_plural']
                    : null,
                'admission_prelim_default' => (int)$this->data['admission_prelim_default'],
                'admission_type_default' => (int)$this->data['admission_type_default'],
                'show_raumzeit' => (int) $this->data['show_raumzeit'],
                'is_group' => (int) $this->data['is_group']
            ]);
        }
    
        /**
         * Deletes the sem_class-object and all its sem_types. Will only delete,
         * if there are no seminars in this sem_class.
         * Remember to refresh the global $SEM_CLASS and $SEM_TYPE array.
         * @return boolean : success of deletion
         */
        public function delete()
        {
            if ($this->countSeminars() === 0) {
                foreach ($GLOBALS['SEM_TYPE'] as $sem_type) {
                    if ($sem_type['class'] == $this->data['id']) {
                        $sem_type->delete();
                    }
                }
                $GLOBALS['SEM_TYPE'] = SemType::getTypes();
                $db = DBManager::get();
                $statement = $db->prepare("
                    DELETE FROM sem_classes
                    WHERE id = :id
                ");
                StudipCacheFactory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
                return $statement->execute([
                    'id' => $this->data['id']
                ]);
            } else {
                return false;
            }
        }
    
        /**
         * Sets an attribute of sem_class->data
         * @param string $offset
         * @param mixed $value
         */
        public function set($offset, $value)
        {
            $this->data[$offset] = $value;
        }
    
        /***************************************************************************
         *                          ArrayAccess methods                            *
         ***************************************************************************/
    
        /**
         * deprecated, does nothing, should not be used
         * @param string $offset
         * @param mixed $value
         *
         * @todo Add void return type when Stud.IP requires PHP8 minimal
         */
        #[ReturnTypeWillChange]
        public function offsetSet($offset, $value)
        {
        }
    
        /**
         * Compatibility function with old $SEM_CLASS variable for plugins. Maps the
         * new array-structure to the old boolean values.
         * @param integer $offset: name of attribute
         * @return boolean|(localized)string
         *
         * @todo Add mixed return type when Stud.IP requires PHP8 minimal
         */
        #[ReturnTypeWillChange]
        public function offsetGet($offset)
        {
            switch ($offset) {
                case "name":
                    return gettext($this->data['name']);
                case "only_inst_user":
                    return (bool) $this->data['only_inst_user'];
                case "bereiche":
                    return (bool) $this->data['bereiche'];
                case "show_browse":
                    return (bool) $this->data['show_browse'];
                case "write_access_nobody":
                    return (bool) $this->data['write_access_nobody'];
                case "topic_create_autor":
                    return (bool) $this->data['topic_create_autor'];
                case "visible":
                    return (bool) $this->data['visible'];
                case "studygroup_mode":
                    return (bool) $this->data['studygroup_mode'];
                case "admission_prelim_default":
                   return (int) $this->data['admission_prelim_default'];
                case "admission_type_default":
                   return (int) $this->data['admission_type_default'];
                case "is_group":
                   return (bool) $this->data['is_group'];
            }
            //ansonsten
            return $this->data[$offset];
        }
    
        /**
         * ArrayAccess method to check if an attribute exists.
         * @param int $offset
         * @return bool
         *
         * @todo Add bool return type when Stud.IP requires PHP8 minimal
         */
        #[ReturnTypeWillChange]
        public function offsetExists($offset)
        {
            return isset($this->data[$offset]);
        }
    
        /**
         * deprecated, does nothing, should not be used
         * @param string $offset
         *
         * @todo Add void return type when Stud.IP requires PHP8 minimal
         */
        #[ReturnTypeWillChange]
        public function offsetUnset($offset)
        {
        }
    
        /***************************************************************************
         *                            static methods                               *
         ***************************************************************************/
    
        /**
         * Returns an array of all SemClasses in Stud.IP. Equivalent to global
         * $SEM_CLASS variable. This variable is statically stored in this class.
         * @return SemClass[] of SemClass
         */
        static public function getClasses()
        {
            if (!is_array(self::$sem_classes)) {
                $db = DBManager::get();
                self::$sem_classes = [];
    
                $cache = StudipCacheFactory::getCache();
                $class_array = unserialize($cache->read('DB_SEM_CLASSES_ARRAY'));
                if (!$class_array) {
    
                    try {
                        $statement = $db->prepare(
                            "SELECT * FROM sem_classes ORDER BY id ASC "
                        );
                        $statement->execute();
                        $class_array = $statement->fetchAll(PDO::FETCH_ASSOC);
    
                        if ($class_array) {
                            $cache = StudipCacheFactory::getCache();
                            $cache->write('DB_SEM_CLASSES_ARRAY', serialize($class_array));
                        }
                    } catch (PDOException $e) {
                        //for use without or before migration 92
                        $class_array = $GLOBALS['SEM_CLASS_OLD_VAR'];
                        if (is_array($class_array)) {
                            ksort($class_array);
                            foreach ($class_array as $id => $class) {
                                self::$sem_classes[$id] = new SemClass($class);
                            }
                        } else {
                            self::$sem_classes[1] = self::getDefaultSemClass();
                        }
                    }
                }
                foreach ($class_array as $sem_class) {
                    self::$sem_classes[$sem_class['id']] = new SemClass($sem_class);
                }
            }
            return self::$sem_classes;
        }
    
        /**
         * Refreshes the internal $sem_classes cache-variable.
         * @return array of SemClass
         */
        static public function refreshClasses()
        {
            StudipCacheFactory::getCache()->expire('DB_SEM_CLASSES_ARRAY');
            self::$sem_classes = null;
            return self::getClasses();
        }
    
        /**
         * Static method to recursively transform an object into an associative array.
         * @param mixed $obj: should be of class StdClass
         * @return array
         */
        static public function object2array($obj)
        {
            $arr_raw = is_object($obj) ? get_object_vars($obj) : $obj;
            foreach ($arr_raw as $key => $val) {
                $val = (is_array($val) || is_object($val)) ? self::object2array($val) : $val;
                $arr[$key] = $val;
            }
            return $arr;
        }
    
    
        /**
         * Static method only to keep the translationstrings of the values. It is
         * never used within the system.
         */
        static private function localization()
        {
            _("Lehre");
            _("Forschung");
            _("Organisation");
            _("Community");
            _("Arbeitsgruppen");
            _("importierte Kurse");
            _("Hauptveranstaltungen");
    
            _("Hier finden Sie alle in Stud.IP registrierten Lehrveranstaltungen");
            _("Verwenden Sie diese Kategorie, um normale Lehrveranstaltungen anzulegen");
            _("Hier finden Sie virtuelle Veranstaltungen zum Thema Forschung an der Universität");
            _("In dieser Kategorie können Sie virtuelle Veranstaltungen für Forschungsprojekte anlegen.");
            _("Hier finden Sie virtuelle Veranstaltungen zu verschiedenen Gremien an der Universität");
            _("Um virtuelle Veranstaltungen für Uni-Gremien anzulegen, verwenden Sie diese Kategorie");
            _("Hier finden Sie virtuelle Veranstaltungen zu unterschiedlichen Themen");
            _("Wenn Sie Veranstaltungen als Diskussiongruppen zu unterschiedlichen Themen anlegen möchten, verwenden Sie diese Kategorie.");
            _("Hier finden Sie verschiedene Arbeitsgruppen an der %s");
            _("Verwenden Sie diese Kategorie, um unterschiedliche Arbeitsgruppen anzulegen.");
            _("Veranstaltungen dieser Kategorie dienen als Gruppierungselement, um die Zusammengehörigkeit von Veranstaltungen anderer Kategorien abzubilden.");
        }
    
    }