aboutsummaryrefslogtreecommitdiff
path: root/lib/models/Modul.php
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+github@gmail.com>2021-07-22 16:07:19 +0200
committerJan-Hendrik Willms <tleilax+github@gmail.com>2021-07-22 16:19:12 +0200
commita3da1483a9e689846179159355badfec8073dbec (patch)
tree770dcca6bdf5f6f2a11b0e7fcbbeda6919a3fc52 /lib/models/Modul.php
current code from svn, revision 62608
Diffstat (limited to 'lib/models/Modul.php')
-rw-r--r--lib/models/Modul.php992
1 files changed, 992 insertions, 0 deletions
diff --git a/lib/models/Modul.php b/lib/models/Modul.php
new file mode 100644
index 0000000..353391d
--- /dev/null
+++ b/lib/models/Modul.php
@@ -0,0 +1,992 @@
+<?php
+/**
+ * Modul.php
+ * Model class for Module (table mvv_modul)
+ *
+ * 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
+ */
+
+class Modul extends ModuleManagementModelTreeItem
+{
+ /**
+ * The default language of the deskriptor (defined in config).
+ *
+ * @var string
+ */
+ private $default_language;
+
+ /**
+ * The number of modulteile.
+ *
+ * @var int
+ */
+ private $count_modulteile;
+
+ protected static function configure($config = [])
+ {
+ $config['db_table'] = 'mvv_modul';
+
+ $config['has_one']['deskriptoren'] = [
+ 'class_name' => 'ModulDeskriptor',
+ 'assoc_foreign_key' => 'modul_id',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store'
+ ];
+ $config['belongs_to']['start_semester'] = [
+ 'class_name' => Semester::class,
+ 'foreign_key' => 'start',
+ ];
+ $config['belongs_to']['end_semester'] = [
+ 'class_name' => Semester::class,
+ 'foreign_key' => 'end',
+ ];
+ $config['has_many']['modulteile'] = [
+ 'class_name' => 'Modulteil',
+ 'assoc_foreign_key' => 'modul_id',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store',
+ 'order_by' => 'ORDER BY position'
+ ];
+ // Ist Novellierung von (quelle)
+ $config['has_one']['modul_quelle'] = [
+ 'class_name' => 'Modul',
+ 'foreign_key' => 'quelle',
+ 'assoc_func' => 'findCached',
+ ];
+ // Ist Variante von (variante)
+ $config['has_one']['modul_variante'] = [
+ 'class_name' => 'Modul',
+ 'foreign_key' => 'variante',
+ 'assoc_func' => 'findCached',
+ ];
+ // hauptverantwortliche Einrichtung
+ $config['has_one']['responsible_institute'] = [
+ 'class_name' => 'ModulInst',
+ 'assoc_func' => 'findPrimarilyResponsibleInstitute',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store'
+ ];
+ // beteiligte Einrichtungen
+ $config['has_many']['assigned_institutes'] = [
+ 'class_name' => 'ModulInst',
+ 'assoc_func' => 'findOtherResponsibleInstitutes',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store'
+ ];
+ $config['has_many']['contact_assignments'] = [
+ 'class_name' => 'MvvContactRange',
+ 'assoc_foreign_key' => 'range_id',
+ 'order_by' => 'ORDER BY position',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store'
+ ];
+ $config['has_many']['abschnitte_modul'] = [
+ 'class_name' => 'StgteilabschnittModul',
+ 'assoc_foreign_key' => 'modul_id',
+ 'order_by' => 'ORDER BY position,mkdate',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store'
+ ];
+ $config['has_and_belongs_to_many']['abschnitte'] = [
+ 'class_name' => 'StgteilAbschnitt',
+ 'thru_table' => 'mvv_stgteilabschnitt_modul',
+ 'thru_key' => 'modul_id',
+ 'thru_assoc_key' => 'abschnitt_id',
+ 'order_by' => 'ORDER BY position,mkdate'
+ ];
+ // Assigned languages of instruction
+ $config['has_many']['languages'] = [
+ 'class_name' => 'ModulLanguage',
+ 'assoc_foreign_key' => 'modul_id',
+ 'order_by' => 'ORDER BY position,mkdate',
+ 'on_delete' => 'delete',
+ 'on_store' => 'store'
+ ];
+
+ $config['additional_fields']['count_modulteile']['get'] =
+ function ($modul) { return $modul->count_modulteile; };
+ $config['additional_fields']['count_modulteile']['set'] = false;
+ $config['additional_fields']['languagesofinstruction']['get'] =
+ function ($modul) { return $modul->languages; };
+ $config['additional_fields']['languagesofinstruction']['set'] = false;
+ $config['additional_fields']['display_name']['get'] =
+ function ($modul) { return $modul->getDisplayName(); };
+
+ $config['alias_fields']['flexnow_id'] = 'flexnow_modul';
+
+ $config['default_values']['stat'] = $GLOBALS['MVV_MODUL']['STATUS']['default'];
+
+ parent::configure($config);
+ }
+
+ public function __construct($id = null)
+ {
+ parent::__construct($id);
+ $this->object_real_name = _('Modul');
+ $this->setDefaultLanguage();
+ }
+
+ /**
+ * @see ModuleManagementModel::getClassDisplayName
+ */
+ public static function getClassDisplayName($long = false)
+ {
+ return _('Modul');
+ }
+
+ /**
+ * Retrieves the module and all related data and some additional fields.
+ *
+ * @param string $modul_id The id of the module.
+ * @return object The module with additional data or a new module.
+ */
+ public static function getEnriched($modul_id)
+ {
+ $modul = parent::getEnrichedByQuery('
+ SELECT mvv_modul.*, mvv_modul_deskriptor.bezeichnung AS bezeichnung,
+ COUNT(DISTINCT(mvv_modulteil.modulteil_id)) AS count_modulteile
+ FROM mvv_modul
+ LEFT JOIN mvv_modul_deskriptor
+ ON mvv_modul.modul_id = mvv_modul_deskriptor.modul_id
+ LEFT JOIN mvv_modulteil
+ ON mvv_modul.modul_id = mvv_modulteil.modul_id
+ LEFT JOIN mvv_modul_inst
+ ON (mvv_modul.modul_id = mvv_modul_inst.modul_id
+ AND mvv_modul_inst.gruppe = ?)
+ WHERE mvv_modul.modul_id = ?',
+ [
+ 'hauptverantwortlich',
+ $modul_id
+ ]
+ );
+
+ if (sizeof($modul)) {
+ return $modul[$modul_id];
+ }
+ return self::get();
+ }
+
+ /**
+ * Returns all or a specified (by row count and offset) number of
+ * Module sorted and filtered by given parameters and enriched with some
+ * additional fields. This function is mainly used in the list view.
+ *
+ * @param string $sortby Field name to order by.
+ * @param string $order ASC or DESC direction of order.
+ * @param int $row_count The max number of objects to return.
+ * @param int $offset The first object to return in a result set.
+ * @param array $filter Key-value pairs of filed names and values
+ * to filter the result set.
+ * @return SimpleORMapCollection A collection of module objects.
+ */
+ public static function getAllEnriched($sortby = 'chdate', $order = 'ASC',
+ $row_count = null, $offset = null, $filter = null)
+ {
+ $sortby = self::createSortStatement($sortby, $order, 'bezeichnung,chdate',
+ words('bezeichnung count_modulteile chdate'));
+ return parent::getEnrichedByQuery('
+ SELECT mvv_modul.*, mvv_modul_deskriptor.bezeichnung AS bezeichnung,
+ COUNT(DISTINCT(mvv_modulteil.modulteil_id)) AS count_modulteile
+ FROM mvv_modul
+ LEFT JOIN mvv_modul_deskriptor
+ ON mvv_modul.modul_id = mvv_modul_deskriptor.modul_id
+ LEFT JOIN mvv_modulteil
+ ON mvv_modul.modul_id = mvv_modulteil.modul_id
+ LEFT JOIN mvv_modul_inst
+ ON (mvv_modul.modul_id = mvv_modul_inst.modul_id
+ AND mvv_modul_inst.gruppe = ?)
+ LEFT JOIN semester_data start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data end_sem
+ ON (mvv_modul.end = end_sem.semester_id) '
+ . self::getFilterSql($filter, true) . '
+ GROUP BY modul_id
+ ORDER BY ' . $sortby,
+ ['hauptverantwortlich'],
+ $row_count,
+ $offset
+ );
+ }
+
+ /**
+ * Returns the number of modules optional filtered by $filter.
+ *
+ * @param array $filter Key-value pairs of filed names and values
+ * to filter the result set.
+ * @return int The number of modules
+ */
+ public static function getCount($filter = null)
+ {
+ $query = '
+ SELECT COUNT(DISTINCT(mvv_modul.modul_id))
+ FROM mvv_modul
+ LEFT JOIN mvv_modul_inst
+ ON (mvv_modul.modul_id = mvv_modul_inst.modul_id
+ AND mvv_modul_inst.gruppe = ?)
+ LEFT JOIN semester_data as start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data as end_sem
+ ON (mvv_modul.end = end_sem.semester_id) '
+ . self::getFilterSql($filter, true);
+ $db = DBManager::get()->prepare($query);
+ $db->execute(['hauptverantwortlich']);
+ return $db->fetchColumn(0);
+ }
+
+ /**
+ * @see MvvTreeItem::getTrailParentId()
+ */
+ public function getTrailParentId()
+ {
+ return ($_SESSION['MVV/Modul/trail_parent_id']);
+ }
+
+ /**
+ * @see MvvTreeItem::getTrailParent()
+ */
+ public function getTrailParent()
+ {
+ return Abschluss::findCached($this->getTrailParentId());
+ }
+
+ /**
+ * @see MvvTreeItem::getChildren()
+ */
+ public function getChildren()
+ {
+ $_SESSION['MVV/Lvgruppe/trail_parent_id'] = $this->getId();
+ return Lvgruppe::getEnrichedByQuery('
+ SELECT ml.*
+ FROM mvv_lvgruppe ml
+ LEFT JOIN mvv_lvgruppe_modulteil USING (lvgruppe_id)
+ LEFT JOIN mvv_modulteil USING (modulteil_id)
+ WHERE modul_id = ? ',
+ [$this->getId()]
+ );
+ }
+
+ /**
+ * @see MvvTreeItem::hasChildren()
+ */
+ public function hasChildren()
+ {
+ return count($this->getChildren()) > 0;
+ }
+
+ /**
+ * @see MvvTreeItem::getParents()
+ */
+ public function getParents($mode = null)
+ {
+ return StgteilabschnittModul::findBySQL('modul_id = ?', [$this->id]);
+ }
+
+ public function getDisplayName($options = self::DISPLAY_DEFAULT) {
+ $options = ($options !== self::DISPLAY_DEFAULT)
+ ? $options : self::DISPLAY_CODE;
+ $with_code = $options & self::DISPLAY_CODE;
+ if ($this->isNew()) {
+ return parent::getDisplayName($options);
+ }
+ $name = ($with_code && trim($this->code)) ? $this->code . ' - ' : '';
+ $name .= $this->deskriptoren->bezeichnung;
+ if ($options & self::DISPLAY_SEMESTER) {
+ $sem_validity = $this->getDisplaySemesterValidity();
+ if ($sem_validity) {
+ $name .= ', ' . $sem_validity;
+ }
+ }
+ return trim($name);
+ }
+
+ /**
+ * Returns a string representation of this module's validity by semesters.
+ *
+ * @return string The string with the validity by semesters.
+ */
+ public function getDisplaySemesterValidity()
+ {
+ $ret = '';
+ $start_sem = Semester::find($this->start);
+ $end_sem = Semester::find($this->end);
+ if ($end_sem || $start_sem) {
+ if ($end_sem) {
+ if ($start_sem->name == $end_sem->name) {
+ $ret .= sprintf(_('gültig im %s'),
+ $start_sem->name);
+ } else {
+ $ret .= sprintf(_('gültig %s bis %s'),
+ $start_sem->name, $end_sem->name);
+ }
+ } else {
+ $ret .= sprintf(_('gültig ab %s'), $start_sem->name);
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Sets the default language for the module descriptor. Takes the language
+ * previously set by ApplicationSimpleORMap::setLanguage() or the one
+ * defined as default in mvv_config.php.
+ */
+ private function setDefaultLanguage()
+ {
+ if (isset($GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values']
+ [ModuleManagementModel::getLanguage()])) {
+ $this->default_language = ModuleManagementModel::getLanguage();
+ } else {
+ $this->default_language =
+ $GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['default'];
+ }
+ }
+
+ /**
+ * Returns the default language for the module descriptor.
+ *
+ * @return string Short name of language (see mvv_config.php)
+ */
+ public function getDefaultLanguage()
+ {
+ return $this->default_language;
+ }
+
+ /**
+ * Returns the Deskriptor in the given language. A Modul has always a
+ * Deskriptor in the default language. If the given language is unknown, the
+ * method returns the deskriptor in the default language.
+ *
+ * @param string $language The id of the language
+ * @param bool If true returns always a new descriptor
+ * @return object The Deskriptor.
+ */
+ public function getDeskriptor($language = null, $force_new = false) {
+ if (!isset($GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values'][$language])) {
+ $language = $this->default_language;
+ }
+ if (!$this->deskriptoren) {
+ // the module is new and has no descriptor
+ // return a new descriptor in the default language
+ $deskriptor = new ModulDeskriptor();
+ $deskriptor->setNewId();
+ $deskriptor->modul_id = $this->getId();
+ $this->deskriptoren = $deskriptor;
+ }
+
+ return $this->deskriptoren;
+ }
+
+ /**
+ * Assigns the responsible institute to this Modul.
+ * A Modul has only one (but always one) responsible institute.
+ *
+ * @param string $institut_id The id of the institute to assign.
+ * @return boolean True if institute was successfully assigned.
+ */
+ public function assignResponsibleInstitute($institut_id) {
+
+ $institute = Fachbereich::find($institut_id);
+ if (!$institute) {
+ return false;
+ }
+ if ($this->responsible_institute->institut_id != $institut_id) {
+ $this->responsible_institute && $this->responsible_institute->delete();
+ $resp_institute = new ModulInst();
+ $resp_institute->institut_id = $institute->id;
+ $resp_institute->modul_id = $this->id;
+ $resp_institute->gruppe = 'hauptverantwortlich';
+ $this->responsible_institute = $resp_institute;
+ }
+ $this->assigned_institutes->unsetBy('institut_id', $institute->id);
+ return true;
+ }
+
+ /**
+ * Assigns other institutes (by id) to this module.
+ *
+ * @param array $institut_ids Array of institute ids.
+ */
+ public function assignInstitutes($institut_ids) {
+ $institutes = [];
+ foreach ($institut_ids as $pos => $institut_id) {
+ $modul_inst = new ModulInst();
+ $modul_inst->modul_id = $this->id;
+ $modul_inst->institut_id = $institut_id;
+ $modul_inst->gruppe = 'verantwortlich';
+ $modul_inst->position = $pos;
+ $institutes[] = $modul_inst;
+ }
+ $this->assigned_institutes = SimpleORMapCollection::createFromArray($institutes);
+ }
+
+ /**
+ * Assignes languages of instruction to this part-module.
+ *
+ * @param type $languages An array of language keys defined in mvv_config.php.
+ */
+ public function assignLanguagesOfInstruction($languages)
+ {
+ $assigned_languages = [];
+ $languages_flipped = array_flip($languages);
+ foreach ($GLOBALS['MVV_MODUL']['SPRACHE']['values'] as $key => $language) {
+ if (isset($languages_flipped[$key])) {
+ $language = ModulLanguage::find([$this->id, $key]);
+ if (!$language) {
+ $language = new ModulLanguage();
+ $language->modul_id = $this->id;
+ $language->lang = $key;
+ }
+ $language->position = $languages_flipped[$key];
+ $assigned_languages[] = $language;
+ }
+ }
+
+ $this->languages = SimpleORMapCollection::createFromArray(
+ $assigned_languages);
+ }
+
+ public function getResponsibleInstitutes()
+ {
+ if ($this->responsible_institute) {
+ $inst = Institute::find($this->responsible_institute->institut_id);
+ if ($inst) {
+ return [$inst];
+ }
+ }
+ return parent::getResponsibleInstitutes();
+ }
+
+ /**
+ * Returns a "deep" copy of this object.
+ *
+ * @param boolean $deep Copy all assigned modulteile if true
+ * @return Modul A copy of this module.
+ */
+ public function copy($deep = true, $with_assignments = false)
+ {
+ $copy = clone $this;
+ $copy->setNew(true);
+ $copy->setNewId();
+
+ // reset flexnow id because it's a unique foreign key.
+ $copy->flexnow_modul = '';
+ if ($this->responsible_institute) {
+ $copy->responsible_institute = clone $this->responsible_institute;
+ $copy->responsible_institute->modul_id = $copy->id;
+ $copy->responsible_institute->setNew(true);
+ }
+
+ $copy->deskriptoren = clone $this->deskriptoren;
+ $copy->deskriptoren->modul_id = $copy->id;
+ $copy->deskriptoren->setNewId();
+ $copy->deskriptoren->setNew(true);
+
+ $institutes = [];
+ foreach ($this->assigned_institutes as $assigned_institute) {
+ $cloned_inst = clone $assigned_institute;
+ $cloned_inst->modul_id = $copy->id;
+ $cloned_inst->setNew(true);
+ $institutes[] = $cloned_inst;
+ }
+ $copy->assigned_institutes = SimpleORMapCollection::createFromArray($institutes);
+
+ $contacts = [];
+ foreach ($this->contact_assignments as $contact) {
+ $position = 1;
+ $cloned_contact = clone $contact;
+ $cloned_contact->position = $position++;
+ $cloned_contact->range_id = $copy->id;
+ $cloned_contact->setNewId();
+ $cloned_contact->setNew(true);
+ $contacts[] = $cloned_contact;
+ }
+ $copy->contact_assignments = SimpleORMapCollection::createFromArray($contacts);
+
+ $languages = [];
+ foreach ($this->languages as $assigned_language) {
+ $cloned_language = clone $assigned_language;
+ $cloned_language->setNew(true);
+ $languages[] = $cloned_language;
+ }
+ $copy->languages = SimpleORMapCollection::createFromArray($languages);
+
+ if ($deep) {
+ $modulteile = [];
+ $position = 1;
+ foreach ($this->modulteile as $modulteil) {
+ $modulteil_copy = $modulteil->copy(true, $with_assignments);
+ $modulteil_copy->position = $position++;
+ $modulteile[] = $modulteil_copy;
+ }
+ $copy->modulteile = SimpleORMapCollection::createFromArray($modulteile);
+
+ if ($with_assignments) {
+ $abschnitte_modul = [];
+ foreach ($this->abschnitte_modul as $abschnitt_modul) {
+ $cloned_abschnitt_modul = clone $abschnitt_modul;
+ $cloned_abschnitt_modul->setNew(true);
+ $abschnitte_modul[] = $cloned_abschnitt_modul;
+ }
+ $copy->abschnitte_modul = SimpleORMapCollection::createFromArray($abschnitte_modul);
+ }
+ }
+ return $copy;
+ }
+
+ public static function findBySearchTerm($term, $filter = null)
+ {
+ $term = '%' . $term . '%';
+ return parent::getEnrichedByQuery("
+ SELECT mvv_modul.*,
+ CONCAT(mvv_modul_deskriptor.bezeichnung, ' (', code, ')') AS name
+ FROM mvv_modul
+ LEFT JOIN mvv_modul_deskriptor USING(modul_id)
+ LEFT JOIN mvv_modul_inst
+ ON (mvv_modul.modul_id = mvv_modul_inst.modul_id)
+ LEFT JOIN semester_data as start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data as end_sem
+ ON (mvv_modul.end = end_sem.semester_id)
+ WHERE (code LIKE :term OR mvv_modul_deskriptor.bezeichnung LIKE :term) "
+ . self::getFilterSql($filter) . "
+ ORDER BY name",
+ [':term' => $term]
+ );
+ }
+
+ /**
+ * Returns all modules assigned to the given Studiengangteil-Abschnitt.
+ *
+ * @param string $abschnitt_id The id of a Studiengangteil-Abschnitt
+ * @param array $filter Key-value pairs of filed names and values
+ * to filter the result set.
+ * @return object A SimpleORMapCollection of modules.
+ */
+ public static function findByStgteilAbschnitt($abschnitt_id, $filter)
+ {
+ return parent::getEnrichedByQuery('
+ SELECT mvv_modul.* FROM mvv_modul
+ LEFT JOIN mvv_stgteilabschnitt_modul USING(modul_id)
+ LEFT JOIN semester_data start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data end_sem
+ ON (mvv_modul.end = end_sem.semester_id)
+ WHERE mvv_stgteilabschnitt_modul.abschnitt_id = ? '
+ . self::getFilterSql($filter) . '
+ ORDER BY position, mkdate',
+ [$abschnitt_id]
+ );
+ }
+
+ /**
+ * Primarily to find Module by Institute. Possible filters are all fields
+ * of the tables mvv_modul, mvv_modulteil, mvv_modul_inst and
+ * mvv_modul_deskriptor.
+ *
+ * Possible fileds to sort by are count_modulteile, bezeichnung (the name
+ * of the modul dereived from the descriptor in the default language) and
+ * all fields of table mvv_modul.
+ *
+ * @param string $sortby
+ * @param string $order
+ * @param array $filter
+ * @param int $row_count
+ * @param int $offset
+ * @return array Array of Module.
+ */
+ public static function findByInstitut($sortby = 'chdate', $order = 'ASC',
+ $filter = [], $row_count = null, $offset = null)
+ {
+ $sortby = self::createSortStatement($sortby, $order, 'chdate',
+ ['count_modulteile', 'bezeichnung']);
+ return parent::getEnrichedByQuery('
+ SELECT mvv_modul.*, mvv_modul_deskriptor.bezeichnung,
+ COUNT(DISTINCT(mvv_modulteil.modulteil_id)) AS count_modulteile
+ FROM mvv_modul
+ LEFT JOIN mvv_modulteil
+ ON mvv_modul.modul_id = mvv_modulteil.modul_id
+ INNER JOIN mvv_modul_inst
+ ON mvv_modul.modul_id = mvv_modul_inst.modul_id
+ LEFT JOIN mvv_modul_deskriptor
+ ON mvv_modul_deskriptor.modul_id = mvv_modul.modul_id
+ LEFT JOIN semester_data start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data end_sem
+ ON (mvv_modul.end = end_sem.semester_id) '
+ . self::getFilterSql($filter, true) .'
+ GROUP BY modul_id
+ ORDER BY ' . $sortby,
+ [],
+ $row_count,
+ $offset
+ );
+ }
+
+ /**
+ * Returns all modules the given LV-Gruppe is assigned to at least
+ * one Modulteile.
+ *
+ * @param string $lvgruppe_id The id of a LV-Gruppe.
+ * @return object A SimpleORMapCollection of modules.
+ */
+ public static function findByLvgruppe($lvgruppe_id)
+ {
+ return parent::getEnrichedByQuery('
+ SELECT mm.*
+ FROM mvv_modul mm
+ LEFT JOIN mvv_modulteil mmt USING(modul_id)
+ LEFT JOIN mvv_lvgruppe_modulteil mlm USING(modulteil_id)
+ WHERE mlm.lvgruppe_id = ? ',
+ [$lvgruppe_id]
+ );
+ }
+
+ /**
+ * Returns all Institutes assigned to the given modules.
+ *
+ * @param string $sortby Field to sort by.
+ * @param string $order Order of sorting (ASC or DESC).
+ * @param array $modul_ids Ids of modules.
+ * @return object a SimpleORMapColection of institutes.
+ */
+ public static function getAssignedInstitutes($sortby = 'name',
+ $order = 'ASC', $modul_ids = [])
+ {
+ return self::getAllAssignedInstitutes($sortby, $order,
+ ['mvv_modul.modul_id' => $modul_ids]);
+ }
+
+ /**
+ * Returns all institutes assigned to Module. Sorted and filtered by
+ * optional parameters.
+ *
+ * @param string $sortby DB field to sort by.
+ * @param string $order ASC or DESC
+ * @param array $filter Array of filter.
+ * @return array Array of found Fachbereiche.
+ */
+ public static function getAllAssignedInstitutes($sortby = 'name',
+ $order = 'ASC', $filter = null, $row_count = null, $offset = null)
+ {
+ $sortby = Fachbereich::createSortStatement($sortby, $order, 'name',
+ ['count_objects']);
+ return Fachbereich::getEnrichedByQuery('
+ SELECT Institute.*,
+ Institute.Name as `name`,
+ Institute.Institut_id AS institut_id,
+ COUNT(DISTINCT modul_id) as count_objects
+ FROM Institute
+ INNER JOIN mvv_modul_inst
+ ON Institute.Institut_id = mvv_modul_inst.institut_id
+ INNER JOIN mvv_modul USING(modul_id)
+ LEFT JOIN semester_data start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data end_sem
+ ON (mvv_modul.end = end_sem.semester_id)
+ '.Fachbereich::getFilterSql($filter, true).'
+ GROUP BY institut_id ORDER BY ' . $sortby,
+ [],
+ $row_count,
+ $offset
+ );
+ }
+
+ /**
+ * Returns an array with all types of status found by given
+ * modul ids as key and the number of associated module as
+ * value.
+ *
+ * @see mvv_config.php for defined status.
+ * @param array $modul_ids
+ * @return array An array with status key as key and an array of name of
+ * status and number of Module with this status.
+ */
+ public static function findStatusByIds($modul_ids = null)
+ {
+ if (is_array($modul_ids)) {
+ $stmt = DBManager::get()->prepare("
+ SELECT IFNULL(stat, '__undefined__') AS stat,
+ COUNT(modul_id) AS count_module
+ FROM mvv_modul WHERE modul_id IN (?)
+ GROUP BY stat
+ ");
+ $stmt->execute([$modul_ids]);
+ } else {
+ $stmt = DBManager::get()->prepare("
+ SELECT IFNULL(stat, '__undefined__') AS stat,
+ COUNT(modul_id) AS count_module
+ FROM mvv_modul GROUP BY stat
+ ");
+ $stmt->execute();
+ }
+
+ $result = [];
+ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $status) {
+ $result[$status['stat']] = [
+ 'name' => $GLOBALS['MVV_MODUL']['STATUS']['values'][$status['stat']]['name'],
+ 'count_objects' => $status['count_module']
+ ];
+ }
+ return $result;
+ }
+
+ /**
+ * Returns an array with ids of all modules found by the given filter.
+ * The fields from tables mvv_modul and mvv_modul_inst are possible filter
+ * options.
+ * If no filter is set an empty array will be returned.
+ *
+ * @see ModuleManagementModel::getFilterSql()
+ * @param array $filter Key-value pairs of filed names and values
+ * to filter the result set.
+ * @return array An array of Modul ids.
+ */
+ public static function findByFilter($filter)
+ {
+ $filter_sql = self::getFilterSql($filter, true);
+ if ($filter_sql == '') {
+ return [];
+ }
+ $stmt = DBManager::get()->prepare('
+ SELECT DISTINCT mvv_modul.modul_id
+ FROM mvv_modul
+ LEFT JOIN mvv_modulteil
+ ON mvv_modul.modul_id = mvv_modulteil.modul_id
+ LEFT JOIN mvv_modul_inst
+ ON (mvv_modul.modul_id = mvv_modul_inst.modul_id)
+ LEFT JOIN semester_data start_sem
+ ON (mvv_modul.start = start_sem.semester_id)
+ LEFT JOIN semester_data end_sem
+ ON (mvv_modul.end = end_sem.semester_id) '
+ . $filter_sql
+ );
+
+ $stmt->execute();
+ return $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
+ }
+
+ /**
+ * Retrieves all modules this module ia a variant of.
+ *
+ * @return array An array of all variants.
+ */
+ public function getVariants()
+ {
+ $variants = [];
+ foreach (Modul::findBySql('variante = '
+ . DBManager::get()->quote($this->getId())) as $variant) {
+ $variants[$variant->getId()] = $variant;
+ }
+ return $variants;
+ }
+
+ /**
+ * Search modules by search term. This function is used in the
+ * search frontend for modules.
+ *
+ * @param string $search_term
+ * @param boolean $only_public If true search only for modules
+ * with public status.
+ */
+ public static function search($search_term, $only_public = true)
+ {
+ $term = '%' . $search_term . '%';
+ if ($only_public) {
+ $public_status = ModuleManagementModel::getPublicStatus('Modul');
+ if (count($public_status)) {
+ $stmt = DBManager::get()->prepare('
+ SELECT mm.modul_id
+ FROM mvv_modul mm
+ INNER JOIN mvv_modul_deskriptor mmd USING(modul_id)
+ LEFT JOIN mvv_stgteilabschnitt_modul msm ON mmd.modul_id = msm.modul_id
+ LEFT JOIN mvv_stgteilabschnitt USING(abschnitt_id)
+ LEFT JOIN mvv_stgteilversion msv USING(version_id)
+ WHERE (
+ mm.code LIKE :term
+ OR mmd.bezeichnung LIKE :term
+ OR msm.bezeichnung LIKE :term
+ )
+ AND mm.stat IN (:stat)
+ AND msv.stat IN (:stat)
+ GROUP BY mm.modul_id
+ ');
+ $stmt->execute([':term' => $term, ':stat' => $public_status]);
+ return $stmt->fetchAll(PDO::FETCH_COLUMN);
+ }
+ } else {
+ $stmt = DBManager::get()->prepare('
+ SELECT mm.modul_id
+ FROM mvv_modul mm
+ LEFT JOIN mvv_modul_deskriptor mmd USING(modul_id)
+ WHERE code LIKE :term OR mmd.bezeichnung LIKE :term
+ GROUP BY modul_id
+ ');
+ $stmt->execute([':term' => $term]);
+ return $stmt->fetchAll(PDO::FETCH_COLUMN);
+ }
+ return [];
+ }
+
+ public function validate()
+ {
+ $ret = parent::validate();
+ if ($this->isDirty()) {
+ $messages = [];
+ $rejected = false;
+ if ($this->variante) {
+ $variante = Modul::find($this->variante);
+ if (is_null($variante)) {
+ $ret['variante'] = true;
+ $messages[] = _('Unbekanntes Modul als Vorlage.');
+ $rejected = true;
+ }
+ }
+ if (!$this->responsible_institute || !$this->responsible_institute->institut_id) {
+ $ret['rsponsible_institute'] = true;
+ $messages[] = _('Es muss mindestens eine verantwortliche Einrichtung zugewiesen werden.');
+ $rejected = true;
+ } else {
+ $this->responsible_institute->validate();
+ }
+ if ($this->start) {
+ $start_sem = Semester::find($this->start);
+ if (!$start_sem) {
+ $ret['start'] = true;
+ $messages[] = _('Ungültiges Semester.');
+ $rejected = true;
+ } else if ($this->end) {
+ $end_sem = Semester::find($this->end);
+ if ($end_sem) {
+ if ($start_sem->beginn > $end_sem->beginn) {
+ $ret['start'] = true;
+ $messages[] = _('Das Endsemester muss nach dem Startsemester liegen.');
+ $rejected = true;
+ }
+ } else {
+ $ret['end'] = true;
+ $messages[] = _('Ungültiges Endsemester.');
+ $rejected = true;
+ }
+ }
+ } else {
+ $ret['start'] = true;
+ $messages[] = _('Bitte ein Startsemester angeben.');
+ $rejected = true;
+ }
+ if (mb_strlen($this->code) < 3) {
+ $ret['code'] = true;
+ $messages[] = _('Der Modulcode ist zu kurz (mindestens 3 Zeichen).');
+ $rejected = true;
+ } else {
+ if ($this->isNew()) {
+ // The code of the Modul has to be unique
+ $existing = $this->findBySql('code = ' . DBManager::get()->quote($this->code));
+ if (sizeof($existing)) {
+ $ret['code'] = true;
+ $messages[] = sprintf(_('Es existiert bereits ein Modul mit dem Code "%s"!'),
+ $this->code);
+ $rejected = true;
+ }
+ }
+ }
+ if (!(preg_match('/\d{0,2}/', $this->dauer) && $this->dauer >= 1)) {
+ $ret['dauer'] = true;
+ $messages[] = _('Die Dauer (in Semestern) des Moduls muss angegeben werden.');
+ $rejected = true;
+ }
+ if (!((preg_match('/\d{0,4}/', $this->kapazitaet)
+ && $this->kapazitaet > 0) || $this->kapazitaet === '')) {
+ $ret['kapazitaet'] = true;
+ $messages[] = _('Die Kapazität/Teilnehmendenzahl des Moduls muss angegeben werden.');
+ $rejected = true;
+ }
+ if (!(preg_match('/\d{1,3}/', $this->kp) && $this->kp >= 1)) {
+ $ret['kp'] = true;
+ $messages[] = _('Die Kreditpunkte müssen angegeben werden.');
+ $rejected = true;
+ }
+ if (!(is_float($this->faktor_note * 1.0) && $this->faktor_note >= 0.1)) {
+ $ret['faktor_note'] = true;
+ $messages[] = _('Der Notenfaktor für die Endnote des Studiengangs muss angegeben werden.');
+ $rejected = true;
+ }
+ if ($this->fassung_nr) {
+ if (!is_numeric($this->fassung_nr)) {
+ $ret['fassung_nr'] = true;
+ $messages[] = _('Für Fassung bitte eine Zahl angeben.');
+ $rejected = true;
+ }
+ if (!$GLOBALS['MVV_MODUL']['FASSUNG_TYP'][$this->fassung_typ]) {
+ $ret['fassung_typ'] = true;
+ $messages[] = _('Bitte einen Typ der Fassung angeben.');
+ $rejected = true;
+ }
+ }
+ if ($rejected) {
+ throw new InvalidValuesException(join("\n", $messages), $ret);
+ }
+ $this->deskriptoren->validate();
+ foreach ($this->assigned_institutes as $assigned_institute) {
+ $assigned_institute->validate();
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Checks if modules with public status are available.
+ *
+ * @return boolean true if modules with public status available
+ */
+ public static function publicModulesAvailable()
+ {
+ $public_status = ModuleManagementModel::getPublicStatus('Modul');
+ if (count($public_status)) {
+ $stmt = DBManager::get()->prepare('
+ SELECT 1
+ FROM mvv_modul mm
+ INNER JOIN mvv_modul_deskriptor mmd USING(modul_id)
+ INNER JOIN mvv_stgteilabschnitt_modul msm ON mmd.modul_id = msm.modul_id
+ INNER JOIN mvv_stgteilabschnitt USING(abschnitt_id)
+ INNER JOIN mvv_stgteilversion msv USING(version_id)
+ WHERE mm.stat IN (:stat)
+ AND msv.stat IN (:stat) LIMIT 1
+ ');
+ $stmt->execute([':stat' => $public_status]);
+ return (bool)$stmt->fetchColumn();
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves all courses this Modul is assigned by its parts and assigned
+ * LV-Gruppen.
+ * Filtered by a given semester considering the global visibility or the
+ * the visibility for a given user.
+ *
+ * @param string $semester_id The id of a semester.
+ * @param mixed $only_visible Boolean true retrieves only visible courses, false
+ * retrieves all courses. If $only_visible is an user id it depends on the users
+ * status which courses will be retrieved.
+ * @return array An array of course data.
+ */
+ public function getAssignedCoursesBySemester($semester_id, $only_visible = true)
+ {
+ $courses = [];
+ foreach ($this->modulteile as $modulteil) {
+ $mt_courses = $modulteil->getAssignedCoursesBySemester($semester_id, $only_visible);
+ foreach ($mt_courses as $course) {
+ $courses[$course->id] = $course;
+ }
+ }
+ return $courses;
+ }
+}