diff options
| author | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:07:19 +0200 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:19:12 +0200 |
| commit | a3da1483a9e689846179159355badfec8073dbec (patch) | |
| tree | 770dcca6bdf5f6f2a11b0e7fcbbeda6919a3fc52 /lib/models/ModuleManagementModel.php | |
current code from svn, revision 62608
Diffstat (limited to 'lib/models/ModuleManagementModel.php')
| -rw-r--r-- | lib/models/ModuleManagementModel.php | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/lib/models/ModuleManagementModel.php b/lib/models/ModuleManagementModel.php new file mode 100644 index 0000000..14d19b1 --- /dev/null +++ b/lib/models/ModuleManagementModel.php @@ -0,0 +1,971 @@ +<?php +/** + * ModuleManagementModel.php + * Parent class of all MVV-Models + * + * 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 Peter Thienel <thienel@data-quest.de> + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + * @since 3.5 + */ + +require_once 'config/mvv_config.php'; + +abstract class ModuleManagementModel extends SimpleORMap +{ + /** + * Usable as option for ModuleManagementModel::getDisplayName(). + * Use the deafault display options for this object. + */ + const DISPLAY_DEFAULT = 1; + + /** + * Usable as option for ModuleManagementModel::getDisplayName(). + * Displays semesters of the validity period if available for this object. + */ + const DISPLAY_SEMESTER = 2; + + /** + * Usable as option for ModuleManagementModel::getDisplayName(). + * Displays the code (usually a unique identifier) if available for this object. + */ + const DISPLAY_CODE = 4; + + /** + * Usable as option for ModuleManagementModel::getDisplayName(). + * Displays the name of the faculty if available for this object. + */ + const DISPLAY_FACULTY = 8; + + /** + * Usable as option for ModuleManagementModel::getDisplayName(). + * Displays the name of the Fach (subject of study) if available for this object. + */ + const DISPLAY_FACH = 16; + + /** + * Usable as option for ModuleManagementModel::getDisplayName(). + * Displays the name of the Studiengangteil if available for this object. + */ + const DISPLAY_STGTEIL = 32; + + /** + * Usable as option ModuleManagementModel::getDisplayName(). + * Displays the name of the Abschluss if available for this object. + */ + const DISPLAY_ABSCHLUSS = 64; + + /** + * Usable as option ModuleManagementModel::getDisplayName(). + * Displays the name of the Abschluss-Kategorie + * if available for this object. + */ + const DISPLAY_KATEGORIE = 128; + + + protected static $filter_params = []; + protected $is_dirty = false; + private static $language = null; + protected static $perm_object = null; + public $object_real_name = ''; + + protected static $object_cache = []; + + /** + * Common configuration for all module management models. This will ensure + * that the caches are flushed whenever an object changes or is removed. + * + * @param array $config Configuration from derived class + */ + protected static function configure($config = []) + { + $config['registered_callbacks']['after_store'][] = function () { + self::clearCache(); + }; + $config['registered_callbacks']['after_delete'][] = function () { + self::clearCache(); + }; + + parent::configure($config); + } + + /** + * Returns a collection of a MVV object type found by search term optionally + * filtered. + * + * @see ModuleManagementModel::getFilterSql() + * @param string $search_term The term to search for. + * @param array $filter Filter parameters as key value pairs. + * @return SimpleORMapCollection A collection of "self" objects. + */ + public static function findBySearchTerm($search_term, $filter = null) + { + return new SimpleORMapCollection(); + } + + /** + * Returns an array of all objects of "self" type. + * + * @return array An array of "self" objects. + */ + public static function getAll() + { + return parent::findBySQL('1'); + } + + /** + * Returns an object by given id or a new object. + * + * @param string $id The id of the object. + * @return ModuleManagementModel An object of "self" type. + */ + public static function get($id = null) + { + return static::findCached($id) + ?? new static($id); + } + + /** + * Returns an object by given id with all relations and additional fields. + * + * @param tring $id The id of the object. + * @return ModuleManagementModel + */ + public static function getEnriched($id) + { + return parent::find($id); + } + + /** + * Verifies whether the given user has sufficient rights to create, modify + * or delete this object and throws an exception if not. + * + * @param string $user_id The user's id. + * @return boolean True if rights are sufficient + * @throws Exception if rights are not sufficient. + */ + public function verifyPermission($user_id = null) + { + $user_id = $user_id ?: $GLOBALS['user']->id; + $perm = MvvPerm::get($this); + // PERM_CREATE means a permission to store a new one or delete one + if ($this->isNew() || $this->isDeleted()) { + if (!$perm->haveObjectPerm(MvvPerm::PERM_CREATE, $user_id)) { + throw new Exception(sprintf( + 'Permission denied! The user is not allowed to ' + . 'create/delete an object of type %s.', static::class)); + } + } else { + if (!$perm->haveObjectPerm(MvvPerm::PERM_WRITE, $user_id)) { + throw new Exception(sprintf( + 'Permission denied! The user is not allowed to store an ' + . 'object of type %s', static::class)); + } + } + + // check the permissions for every single db field except primary keys + if ($this->isNew()) { + $fields = array_diff(array_keys($this->db_fields), + array_values($this->pk)); + } else { + $fields = array_keys($this->db_fields); + } + foreach ($fields as $field) { + if ($this->isFieldDirty($field) + && !$perm->haveFieldPerm($field, MvvPerm::PERM_WRITE, $user_id)) { + throw new Exception(sprintf( + 'Permission denied! The user is not allowed to change ' + . 'value of field %s.%s.', static::class, $field)); + } + } + + // check the permissions for every single relation + foreach (array_keys($this->relations) as $relation) { + $options = $this->getRelationOptions($relation); + if ((isset($options['on_store']) || isset($options['on_delete'])) && + ($options['type'] === 'has_one' || + $options['type'] === 'has_many' || + $options['type'] === 'has_and_belongs_to_many')) { + if (isset($this->relations[$relation])) { + if ($options['type'] === 'has_one') { + $this->checkRelation($relation, $this->{$relation}, $perm, $user_id); + } else { + // datafields gets special treatment... + if ($relation == 'datafields') { + foreach ($this->datafields as $entry) { + if ($entry->isNew() || $entry->isDirty()) { + if (!$perm->haveDfEntryPerm($entry->datafield_id, MvvPerm::PERM_WRITE)) { + throw new Exception(sprintf( + 'Permission denied! The user is not ' + . 'allowed to change value of field %s::datafields[%s] ("%s").', static::class, $entry->datafield_id, $entry->datafield->name)); + } + } + } + } else { + foreach ($this->{$relation} as $r) { + $this->checkRelation($relation, $r, $perm, $user_id); + } + } + } + } + } + } + return true; + } + + /** + * Checks the rights for a relation. + * + * @param string $relation_name Field name of relation. + * @param ModuleManagementModel $relation_object + * @param int $perm + * @param type $user_id + * @return boolean + * @throws Exception + */ + private function checkRelation($relation_name, $relation_object, $perm, $user_id) + { + if (($relation_object->isNew() || $relation_object->isDeleted()) + && !$perm->haveFieldPerm($relation_name, MvvPerm::PERM_CREATE, $user_id)) { + throw new Exception(sprintf( + 'Permission denied! The user is not allowed to create/delete a relation %s::%s.', + get_class($relation_object), $relation_name)); + } elseif ($relation_object->isDirty()) { + if ($relation_object instanceof ModuleManagementModel) { + $relation_object->verifyPermission($user_id); + } elseif (!$perm->haveFieldPerm($relation_name, MvvPerm::PERM_WRITE, $user_id)) { + throw new Exception(sprintf( + 'Permission denied! The user is not allowed to modify a relation %s::%s.', + get_class($relation_object), $relation_name)); + } + } + return true; + } + + /** + * @see SimpleOrMap::store() + * Optional validation of values. Triggers logging of changes. + * + * @param boolean $validate True to validate values. + */ + public function store(/* $validate = true */) + { + $validate = true; + if (func_num_args() > 0) { + $validate = func_get_arg(0) !== false; + } + if ($validate) { + $this->validate(); + } + if ($this->isNew() || $this->isDirty()) { + $this->editor_id = $GLOBALS['user']->id; + } + + $stored = false; + if ($this->isNew()) { + $this->author_id = $GLOBALS['user']->id; + $stored = parent::store(); + if ($stored) { + $this->logChanges('new'); + } + } else { + $this->logChanges('update'); + $stored = parent::store(); + } + return $stored; + } + + /** + * Validates the values before store. Throws an InvalidValuesException + * normally catched by form validation. + * + * @throws InvalidValuesException + */ + public function validate() { + + } + + /** + * @see SimpleOrMap::delete() + * Triggers logging. + */ + public function delete() { + $this->logChanges('delete'); + return parent::delete(); + } + + /** + * Logs all changes of this object. + * + * @param type $action new, update or delete + * @return boolean Return true if logging was successful. + */ + protected function logChanges ($action = null) { + + switch ($this->db_table) { + case 'abschluss' : + $logging = 'MVV_ABSCHLUSS'; + $num_index = 1; + break; + case 'mvv_abschl_kategorie' : + $logging = 'MVV_KATEGORIE'; + $num_index = 1; + break; + case 'mvv_abschl_zuord' : + $logging = 'MVV_ABS_ZUORD'; + $num_index = 2; + break; + case 'mvv_dokument' : + $logging = 'MVV_DOKUMENT'; + $num_index = 1; + break; + case 'mvv_dokument_zuord' : + $logging = 'MVV_DOK_ZUORD'; + $num_index = 3; + break; + case 'fach' : + $logging = 'MVV_FACH'; + $num_index = 1; + break; + case 'mvv_fachberater' : + $logging = 'MVV_FACHBERATER'; + $num_index = 2; + break; + case 'mvv_fach_inst' : + $logging = 'MVV_FACHINST'; + $num_index = 2; + break; + case 'mvv_lvgruppe' : + $logging = 'MVV_LVGRUPPE'; + $num_index = 1; + break; + case 'mvv_lvgruppe_modulteil' : + $logging = 'MVV_LVMODULTEIL'; + $num_index = 2; + break; + case 'mvv_lvgruppe_seminar' : + $logging = 'MVV_LVSEMINAR'; + $num_index = 2; + break; + case 'mvv_modul' : + $logging = 'MVV_MODUL'; + $num_index = 1; + break; + case 'mvv_modulteil' : + $logging = 'MVV_MODULTEIL'; + $num_index = 1; + break; + case 'mvv_modulteil_deskriptor' : + $logging = 'MVV_MODULTEIL_DESK'; + $num_index = 1; + break; + case 'mvv_modulteil_language' : + $logging = 'MVV_MODULTEIL_LANG'; + $num_index = 2; + break; + case 'mvv_modulteil_stgteilabschnitt' : + $logging = 'MVV_MODULTEIL_STGTEILABS'; + $num_index = 3; + break; + case 'mvv_modul_deskriptor' : + $logging = 'MVV_MODUL_DESK'; + $num_index = 1; + break; + case 'mvv_modul_inst' : + $logging = 'MVV_MODULINST'; + $num_index = 2; + break; + case 'mvv_modul_language' : + $logging = 'MVV_MODUL_LANG'; + $num_index = 2; + break; + case 'mvv_modul_user' : + $logging = 'MVV_MODUL_USER'; + $num_index = 3; + break; + case 'mvv_stgteil' : + $logging = 'MVV_STGTEIL'; + $num_index = 1; + break; + case 'mvv_stgteilabschnitt' : + $logging = 'MVV_STGTEILABS'; + $num_index = 1; + break; + case 'mvv_stgteilabschnitt_modul' : + $logging = 'MVV_STGTEILABS_MODUL'; + $num_index = 2; + break; + case 'mvv_stgteilversion' : + $logging = 'MVV_STGTEILVERSION'; + $num_index = 1; + break; + case 'mvv_stgteil_bez' : + $logging = 'MVV_STGTEILBEZ'; + $num_index = 1; + break; + case 'mvv_stg_stgteil' : + $logging = 'MVV_STG_STGTEIL'; + $num_index = 3; + break; + case 'mvv_studiengang' : + $logging = 'MVV_STUDIENGANG'; + $num_index = 1; + break; + default: + return false; + } + + if ($logging) { + + $aff = null; + $coaff = null; + $debuginfo =null; + + switch ($action) { + case 'new': + $logging .= '_NEW'; + $debuginfo = $this->getDisplayName(); + break; + case 'update': + $logging .= '_UPDATE'; + break; + case 'delete': + $logging .= '_DEL'; + $debuginfo = $this->getDisplayName(); + break; + default: + return false; + } + + $id_array = $this->getId(); + switch ($num_index) { + case '1': + $aff = $id_array; + break; + case '2': + $aff = $id_array[0]; + $coaff = $id_array[1]; + break; + case '3': + $aff = $id_array[0]; + $coaff = $id_array[1]; + $debuginfo = $id_array[2]; + break; + default: + return false; + } + + if ($action == 'update') { + foreach ($this->content as $name => $value) { + if ($name == 'author_id' || $name == 'editor_id' || $name == 'mkdate' || $name == 'chdate' ) continue; + if ($this->isFieldDirty($name)) { + $info = ($num_index == 3) ? $debuginfo.';'.$value : $value; + StudipLog::log($logging, $aff, $coaff, $this->db_table.'.'.$name, $info, $editor_id); + } + } + } else { + StudipLog::log($logging, $aff, $coaff, $this->db_table, $debuginfo, $editor_id); + } + + return true; + } + return false; + } + + /** + * Sets a new id for this object. + */ + public function setNewId() + { + $this->setId($this->getNewId()); + } + + /** + * Enriches the model with data from other joined tables. + * + * @param string $query complete sql with all fields in select statement + * from main table + * @param array $params Array with the parameters used in query + * @param int $row_count Number of rows to return + * @param int $offset Offset where the result set starts + * @return object SimpleOrMapCollection with all found objects or empty array + */ + public static function getEnrichedByQuery($query = null, $params = [], + $row_count = null, $offset = null) + { + $enriched = []; + $params = array_merge($params, self::$filter_params); + self::$filter_params = []; + if (!is_null($query)) { + if (!is_null($row_count)) { + $limit_sql = ' LIMIT ?'; + $params[] = intval($row_count); + if (!is_null($offset)) { + $limit_sql .= ' OFFSET ?'; + $params[] = intval($offset); + } + } else { + $limit_sql = ''; + } + $stmt = DBManager::get()->prepare($query . $limit_sql); + $stmt->execute($params); + $model_object = new static(); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $data) { + $pkey = []; + foreach ($model_object->pk as $pk) { + $pkey[]= $data[$model_object->db_fields[$pk]['name']]; + } + $data_object = clone $model_object; + foreach ($data as $key => $value) { + if (isset($data_object->db_fields[$key])) { + $data_object->setValue($key, $value); + } else { + $data_object->content[mb_strtolower($key)] = $value; + $data_object->content_db[mb_strtolower($key)] = $value; + } + } + $data_object->setId($pkey); + $data_object->setNew(false); + $enriched[join('', $pkey)] = $data_object; + } + } + return SimpleORMapCollection::createFromArray($enriched); + } + + /** + * Returns the name of the object to display in a specific context. The + * default is the value from the db fields "name" or "bezeichnung" or an + * empty string if no such fields exist. This method is overwritten by most + * of the mvv objects to display more complex names glued together from + * fields of related objects. + * + * @param mixed $options An optional parameter to set display options. + * @return string The name for + */ + public function getDisplayName($options = self::DISPLAY_DEFAULT) + { + if ($this->isField('name')) { + return (string) $this->getValue('name'); + } + if ($this->isField('bezeichnung')) { + return (string) $this->getValue('bezeichnung'); + } + return ''; + } + + /** + * Returns the display name of this class. + * + * @return string the display name of this class + */ + public static function getClassDisplayName($long = false) + { + return 'Module Management Model'; + } + + /** + * Creates a sql clause to set constraints in a query by given filters. + * + * The values of the given filters can be either a scalar value or an array. + * + * If the value of the given filter is '__undefined__' then it matches + * against NULL or an empty string. + * + * If the column name has + * a comparison operator at its end (delimited by a blank), this operator + * is used. + * + * To filter for semesters the column name has to be 'start_sem.beginn' for + * the start semester and 'end_sem.ende' for the end semester according to + * the joins with the semester_data table and table aliases in the sql + * statement. + * + * @param array $filter An associative array with filters where the key is + * the column name to filter against the given value. + * @param bool $where if true returns a complete where statement + * @param string SQL-where part glued with an "OR" at the end of the + * filter sql part. + * @return string The sql clause + */ + public static function getFilterSql($filter, $where = false, $or_sql = null) + { + $sql_parts = []; + foreach ((array) $filter as $col => $val) { + $col = trim($col); + if (is_array($val)) { + if (sizeof($val)) { + $sql_parts[] = $col . ' IN(' + . join(',', array_map( + function ($val) { + return DBManager::get()->quote($val); + }, $val)) + . ') '; + } + } else if (trim($val)) { + if ($val == '__undefined__') { + $sql_parts[] = '(ISNULL(' . $col . ') OR ' . $col . " = '')"; + } else { + if (preg_match('/([\w\.]+)\s+([\<\>\!]\=?)/', $col, $matches)) { + $sql_parts[] = trim($matches[1]) . ' ' . $matches[2] . ' ' + . DBManager::get()->quote($val) . ' '; + } else if ($col == 'start_sem.beginn') { + if ((int) $val >= 0) { + // start semester filter for Module, Studiengaenge, ... + $sql_parts[] = '(start_sem.beginn <= ' + . DBManager::get()->quote($val) + . ' OR ISNULL(start_sem.beginn))'; + } + } else if ($col == 'end_sem.ende') { + if ((int) $val >= 0) { + // end semester filter for Module, Studiengaenge, ... + $sql_parts[] = '(end_sem.ende >= ' + . DBManager::get()->quote($val) + . ' OR ISNULL(end_sem.ende))'; + } + } else { + $sql_parts[] = $col . ' = ' + . DBManager::get()->quote($val) . ' '; + } + } + } + } + $sql = implode(' AND ', $sql_parts); + if (mb_strlen($sql)) { + if ($or_sql) { + $sql = '(' . $sql . ') OR (' . $or_sql . ')'; + } + $sql = $where ? ' WHERE (' . $sql . ') ' : ' AND (' . $sql . ') '; + } + return $sql; + } + + /** + * Verifies a field name or an array of field names if they are permitted for + * sorting the result. If ok returns the given sort fields. If not, returns + * the given standard_field or null. + * + * @param string|array $sort the fields to check + * @param string $additional_fields additional allowed fields + * @return string|null the verified sort fields + */ + protected static function checkSortFields($sort, $standard_field = null, + $additional_fields = []) + { + if (!is_array($sort)) { + $sort = explode(',', $sort); + } + $sorm = new static(); + if (sizeof(array_intersect( + array_merge(array_keys($sorm->db_fields), $additional_fields), + $sort))) { + return implode(',', $sort); + } + return $standard_field; + } + + /** + * Checks for valid fields and creates a sort statement for queries. + * + * @param string|array $sort The field(s) to sort by. + * @param string $order The direction (ASC|DESC) + * @param string $standard_field + * @param array $additional_fields Calculated columns. + * @return string The sort query part. + */ + protected static function createSortStatement($sort, $order = 'ASC', + $standard_field = null, $additional_fields = []) + { + $order = mb_strtoupper(trim($order)) !== 'DESC' ? ' ASC' : ' DESC'; + if (!is_array($sort)) { + $sort = explode(',', $sort); + } + $sort = array_map('trim', $sort); + $sorm_name = static::class; + $sorm = new $sorm_name(); + $allowed_fields = array_intersect( + $sort, + array_merge( + array_keys($sorm->db_fields), + $additional_fields + ) + ); + if (count($allowed_fields) > 0) { + return implode("{$order},", $allowed_fields) . $order; + } + return $standard_field; + } + + /** + * Returns an SimpleOrMap object as an Array. + * Its like a static version of SimpleOrMap::toArray but + * returns all content fields. Usefull as callback function. + * + * @param SimpleORMap $sorm The SimpleOrMap object to transform + * @param bool $to_utf8 If true (default), the data will be utf8 transformed. + * @return array The array with all content fields from object. + */ + public static function getContentArray(SimpleORMap $sorm, $to_utf8 = true) + { + return $sorm->contentToArray($to_utf8); + } + + /** + * Returns the number of objects of this type. Optionally reduced by + * filter criteria. + * + * @param array An array with filter criteria. + * See ApplicationSimpleORMap::getFilter(). + * @return int The number of rows. + */ + public static function getCount($filter = null) + { + if ($filter) { + $filter_sql = self::getFilterSql($filter, true); + } else { + $filter_sql = ''; + } + $sorm = new static(); + $db = DBManager::get()->query('SELECT COUNT(*) FROM ' + . $sorm->db_table . $filter_sql); + return $db->fetchColumn(0); + } + + /** + * Returns the number of rows found by the given sql and filters. + * + * @param string $sql The sql query part. + * @param array $filter An array of filters with respect to the query part. + * @return int The number of rows. + */ + public static function getCountBySql($sql, $filter = null) + { + $stmt = DBManager::get()->prepare($sql . self::getFilterSql($filter, + true)); + $stmt->execute(self::$filter_params); + return $stmt->fetchColumn(0); + } + + /** + * Sets the language for localized fields and the locale environment + * globally. + * Possible values are configured in mvv_config.php. + * + * @see mvv_config.php + * @param string $language The language. + */ + public static final function setLanguage($language) + { + $language = mb_strtoupper(mb_strstr($language . '_', '_', true)); + if (isset($GLOBALS['MVV_LANGUAGES']['values'][$language])) { + $locale = $GLOBALS['MVV_LANGUAGES']['values'][$language]['locale']; + setLocaleEnv($locale); + self::setContentLanguage($language); + // load config file again + require $GLOBALS['STUDIP_BASE_PATH'] . '/config/mvv_config.php'; + } + } + + /** + * Switches the content to the given language. + * Compared to ModuleManagementModel::setLanguage() strings translated with + * gettext are always in the prefered language selected by the user. + * + * @param string $language The language code (see mvv_config.php) + */ + public static function setContentLanguage($language) + { + if (!is_array($GLOBALS['MVV_LANGUAGES']['values'][$language])) { + throw new InvalidArgumentException(); + } + $locale = $GLOBALS['MVV_LANGUAGES']['values'][$language]['locale']; + I18NString::setContentLanguage($locale); + self::$language = $language; + } + + public function getAvailableTranslations() + { + $translations[] = $GLOBALS['MVV_LANGUAGES']['default']; + $stmt = DBManager::get()->prepare('SELECT DISTINCT `lang` ' + . 'FROM i18n ' + . 'WHERE `object_id` = ? AND `table` = ?'); + $stmt->execute([$this->id, $this->db_table]); + foreach ($stmt->fetchAll() as $locale) { + $language = mb_strtoupper(mb_strstr($locale['lang'], '_', true)); + if (is_array($GLOBALS['MVV_LANGUAGES']['values'][$language])) { + $translations[] = $language; + } + } + return $translations; + } + + + /** + * Returns the currently selected language. + * + * @return string The currently selected language. + */ + public static final function getLanguage() + { return 'DE'; + $language = self::$language ?: $GLOBALS['MVV_LANGUAGES']['default']; + return $language; + } + + /** + * Returns the suffix for ordinal numbers if the selected locale is EN or + * a simple point if not. + * + * @param type $num + * @return string The ordinal suffix or a point. + */ + public static function getLocaleOrdinalNumberSuffix($num) + { + if (ModuleManagementModel::getLanguage() == 'EN') { + if ($num % 100 < 11 || $num % 100 > 13) { + switch ($num % 10) { + case 1: return 'st'; + case 2: return 'nd'; + case 3: return 'rd'; + } + } + return 'th'; + } + return '.'; + } + + /** + * Returns an array of all values for given class with status "public" + * defined by configuration. + * + * @return array Array of defined values for status "public". + */ + public static function getPublicStatus($class_name = null) + { + $class_name = $class_name ?: static::class; + $class_name = 'MVV_' . mb_strtoupper($class_name); + $public_status = []; + if (is_array($GLOBALS[$class_name]['STATUS']['values'])) { + foreach ($GLOBALS[$class_name]['STATUS']['values'] as $key => $status) { + if ($status['public']) { + $public_status[] = $key; + } + } + } + return $public_status; + } + + /** + * Returns the status of this object. + * Some MVV objects have a status declared in mvv_config.php. + * + * @return string|null The status or null if the object has no status. + */ + public function getStatus() + { + if ($this->isField('stat')) { + return $this->stat; + } + return null; + } + + /** + * Returns whether this object has a public status. Public status means that + * this object is public visible. The possible status are defined + * in mvv_config.php. The set of possible status can be restricted by an + * optional filter. Only the statis given in filter are checkrd. + * + * @param array $filter An array of status keys. + * @return boolean True if object has an public status. + */ + public function hasPublicStatus($filter = null) + { + $public_status = ModuleManagementModel::getPublicStatus(static::class); + $filtered_status = $filter + ? array_intersect(words($filter), $public_status) + : $public_status; + $status = $this->getStatus(); + return $status ? in_array($status, $filtered_status) : false; + } + + public function getResponsibleInstitutes() + { + return []; + } + + /** + * Returns a string that identify a variant of this object. Returns an empty + * string if no variant exists for this object. + * + * @return string String to identify a variant. + */ + public function getVariant() + { + return ''; + } + + /** + * Locates and returns an item from cache + * + * @param string $id Id of the item + * @param string $index Optional index for the cache, defaults to class name + * @return ModuleManagementModel object or null + */ + public static function findCached($id, $index = null) + { + // Prevent warnings + if ($id === null || $id === false) { + return null; + } + + return self::fromCache($index ?? static::class, $id, function () use ($id) { + return static::find($id); + }); + } + + /** + * Clears the cache for a given index or completely. If this method is + * called on this abstract class, it will always clear the whole cache. + * Otherwise it will clear the cache for the subclass of this class or + * the given index. + * + * @param mixed $index Optional index to clear + */ + public static function clearCache($index = null) + { + if (static::class === self::class && $index === null) { + static::$object_cache = []; + } else { + $index = $index ?? static::class; + unset(static::$object_cache[$index]); + } + } + + /** + * Loads an item from cache and retrieves and stores it if it is not + * present. + * + * @param string $index Index for the cache + * @param string $id Id of the item + * @param Callable $finder Function to actually locate the item. + * @return ModuleManagementModel object or null + */ + protected static function fromCache($index, $id, Callable $finder) + { + // Leave immeditately if $id cannot be used as array index + if (!is_string($id) && !is_int($id)) { + return $finder(); + } + + if (!isset(static::$object_cache[$index])) { + static::$object_cache[$index] = []; + } + if (!array_key_exists($id, static::$object_cache[$index])) { + static::$object_cache[$index][$id] = $finder(); + } + return static::$object_cache[$index][$id]; + } +} |
