Forked from
Stud.IP / Stud.IP
2442 commits behind the upstream repository.
-
Closes #2591 Merge request studip/studip!1749
Closes #2591 Merge request studip/studip!1749
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.");
}
}