<?php /** * my_courses.php - Model for user and seminar related * pages under "Meine Veranstaltungen" * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * @author David Siegfried <david@ds-labs.de> * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 or later * @category Stud.IP * @since 3.1 */ require_once 'lib/meine_seminare_func.inc.php'; require_once 'lib/object.inc.php'; class MyRealmModel { /** * Check the voting system * @param string $my_obj * @param string $user_id * @param $object_id */ public static function checkVote(&$my_obj, $user_id, $object_id) { $count = 0; $neue = 0; $statusgruppen = Statusgruppen::findByRange_id($object_id); if (!$GLOBALS['perm']->have_studip_perm("tutor", $object_id)) { $statusgruppen = array_filter($statusgruppen, function ($sg) use ($user_id) { return $sg->isMember($user_id); }); } $threshold = object_get_visit_threshold(); $statement = DBManager::get()->prepare(" SELECT COUNT(DISTINCT questionnaires.questionnaire_id) AS count, COUNT(IF((questionnaires.chdate > IFNULL(object_user_visits.visitdate, :threshold) AND questionnaires.user_id !=:user_id), questionnaires.questionnaire_id, NULL)) AS new, MAX(IF((questionnaires.chdate > IFNULL(object_user_visits.visitdate, :threshold) AND questionnaires.user_id !=:user_id), questionnaires.chdate, 0)) AS last_modified FROM questionnaire_assignments INNER JOIN questionnaires ON (questionnaires.questionnaire_id = questionnaire_assignments.questionnaire_id) LEFT JOIN object_user_visits ON(object_user_visits.object_id = questionnaires.questionnaire_id AND object_user_visits.user_id = :user_id AND object_user_visits.plugin_id = -1) WHERE ( ( questionnaire_assignments.range_id = :course_id AND questionnaire_assignments.range_type IN ('course', 'institute') ) OR ( questionnaire_assignments.range_id IN (:statusgruppe_ids) AND questionnaire_assignments.range_type = 'statusgruppe' ) ) AND questionnaires.startdate IS NOT NULL AND questionnaires.startdate <= UNIX_TIMESTAMP() AND ( questionnaires.stopdate IS NULL OR questionnaires.stopdate > UNIX_TIMESTAMP() ) GROUP BY questionnaire_assignments.range_id "); $statement->execute([ 'threshold' => $threshold, 'user_id' => $user_id, 'course_id' => $object_id, 'plugin_id' => -1, 'statusgruppe_ids' => array_map(function ($g) { return $g->getId(); }, $statusgruppen) ]); $result = $statement->fetch(PDO::FETCH_ASSOC); if (!empty($result)) { $count = $result['count']; $neue = $result['new']; if (!is_null($result['last_modified']) && (int)$result['last_modified'] != 0) { if (!isset($my_obj['last_modified']) || $my_obj['last_modified'] < $result['last_modified']) { $my_obj['last_modified'] = $result['last_modified']; } } } $sql = "SELECT COUNT(a.eval_id) as count, COUNT(IF((chdate > IFNULL(b.visitdate, :threshold) AND d.author_id !=:user_id ), a.eval_id, NULL)) AS neue, MAX(IF ((chdate > IFNULL(b.visitdate, :threshold) AND d.author_id != :user_id), chdate, 0)) AS last_modified FROM eval_range a INNER JOIN eval d ON (a.eval_id = d.eval_id AND d.startdate < UNIX_TIMESTAMP() AND (d.stopdate > UNIX_TIMESTAMP() OR d.startdate + d.timespan > UNIX_TIMESTAMP() OR (d.stopdate IS NULL AND d.timespan IS NULL))) LEFT JOIN object_user_visits b ON (b.object_id = a.eval_id AND b.user_id = :user_id AND b.plugin_id = :plugin_id) WHERE a.range_id = :course_id GROUP BY a.range_id"; $statement = DBManager::get()->prepare($sql); $statement->bindValue(':user_id', $user_id); $statement->bindValue(':course_id', $object_id); $statement->bindValue(':threshold', object_get_visit_threshold()); $statement->bindValue(':plugin_id', -2); $statement->execute(); $result = $statement->fetch(PDO::FETCH_ASSOC); if (!empty($result)) { $count += $result['count']; $neue += $result['neue']; if (isset($my_obj['last_modified'], $result['last_modified']) && $result['last_modified']) { if ($my_obj['last_modified'] < $result['last_modified']) { $my_obj['last_modified'] = $result['last_modified']; } } } if ($neue || $count > 0) { $nav = new Navigation('vote', '#vote'); if ($neue) { $nav->setImage(Icon::create('vote', Icon::ROLE_ATTENTION, [ 'title' => sprintf( ngettext( '%1$u Fragebogen, %2$u neuer', '%1$u Fragebögen, %2$u neue', $count ), $count, $neue ) ])); $nav->setBadgeNumber($neue); } else if ($count) { $nav->setImage(Icon::create('vote', Icon::ROLE_CLICKABLE, [ 'title' => sprintf( ngettext( '%u Fragebogen', '%u Fragebögen', $count ), $count ) ])); } return $nav; } return null; } /** * Get all courses vor given user in selected semesters */ public static function getCourses($min_sem_key, $max_sem_key, $params = []) { // init $order_by = $params['order_by'] ?? null; $order = $params['order'] ?? null; $deputies_enabled = $params['deputies_enabled']; $sem_data = Semester::getAllAsArray(); $semester_ids = []; if (is_numeric($min_sem_key) && is_numeric($max_sem_key)) { foreach ($sem_data as $index => $data) { if ($index >= $min_sem_key && $index <= $max_sem_key) { $semester_ids[] = $data['semester_id'] ?? ''; } } } $semesters = Semester::findMany($semester_ids); $studygroup_filter = !empty($params['studygroups_enabled']); $ordering = ''; // create ordering if (!$order_by) { if (Config::get()->IMPORTANT_SEMNUMBER) { $ordering = 'veranstaltungsnummer asc, name asc'; } else { $ordering .= 'name asc'; } } else { $ordering .= $order_by . ' ' . $order; } // search for your own courses // Filtering by Semester $courses = Course::findThru($GLOBALS['user']->id, [ 'thru_table' => 'seminar_user', 'thru_key' => 'user_id', 'thru_assoc_key' => 'seminar_id', 'assoc_foreign_key' => 'seminar_id' ]); if ($deputies_enabled) { $deputy_courses = Deputy::findDeputyCourses($GLOBALS['user']->id)->pluck('course'); if (!empty($deputy_courses)) { $courses = array_merge($courses, $deputy_courses); } } // create a new collection for more functionality $courses = new SimpleCollection($courses); if ($studygroup_filter) { $courses = $courses->filter(function ($a) { return !$a->isStudygroup(); }); } $courses = $courses->filter(function ($a) use ($semesters) { foreach ($semesters as $semester) { if ($a->isInSemester($semester)) { return true; } } return false; }); return self::sortCourses($courses, $ordering); } public static function getSelectedSemesters($sem = '') { $sem_data = Semester::getAllAsArray(); $semesters = []; $current_sem = null; foreach ($sem_data as $sem_key => $one_sem) { $current_sem = $sem_key; if (!$one_sem['past']) break; } if (isset($sem_data[$current_sem + 1])) { $max_sem = $current_sem + 1; } else { $max_sem = $current_sem; } // Get the needed semester if (!in_array($sem, ['', 'current', 'future', 'last', 'lastandnext'])) { $semesters[] = Semester::getIndexById($sem); } else { switch ($sem) { case 'current': $semesters[] = $current_sem; break; case 'future': $semesters[] = $current_sem; $semesters[] = $max_sem; break; case 'last': $semesters[] = $current_sem - 1; $semesters[] = $current_sem; break; case 'lastandnext': $semesters[] = $current_sem - 1; $semesters[] = $current_sem; $semesters[] = $max_sem; break; default: $semesters = array_keys($sem_data); break; } } return $semesters; } /** * Returns a list of all matching courses prepared for further treatment. * * @param string $sem Semester index * @param array $params Additional parameters * @return array */ public static function getPreparedCourses($sem = '', $params = []) { $semesters = self::getSelectedSemesters($sem); $current_semester_nr = Semester::getIndexById(@Semester::findCurrent()->id); $min_sem_key = min($semesters); $max_sem_key = max($semesters); $group_field = $params['group_field']; $courses = self::getCourses($min_sem_key, $max_sem_key, $params); $show_semester_name = UserConfig::get($GLOBALS['user']->id)->SHOWSEM_ENABLE; $sem_courses = []; $param_array = 'name seminar_id visible veranstaltungsnummer start_time duration_time status visible '; $param_array .= 'chdate admission_binding modules admission_prelim'; // filtering courses $member_ships = User::findCurrent()->course_memberships->toGroupedArray('seminar_id', 'status gruppe'); $children = []; $semester_assign = []; foreach ($courses as $course) { // export object to array for simple handling $_course = $course->toArray($param_array); $_course['start_semester'] = $course->start_semester ? $course->start_semester->name : null; $_course['end_semester'] = $course->end_semester ? $course->end_semester->name : null; $_course['sem_class'] = $course->getSemClass(); $_course['obj_type'] = 'sem'; $visits = get_objects_visits([$course->id], 0, null, null, $course->tools->pluck('plugin_id')); if ($group_field === 'sem_tree_id') { $_course['sem_tree'] = $course->study_areas->toArray(); } $deputy = null; $user_status = $member_ships[$course->id]['status'] ?? null; if (!$user_status && Config::get()->DEPUTIES_ENABLE && Deputy::isDeputy($GLOBALS['user']->id, $course->id)) { $user_status = 'dozent'; $deputy = Deputy::findOneBySQL('range_id = ? AND user_id = ?', [$course->id, $GLOBALS['user']->id]); $is_deputy = true; } else { $is_deputy = false; } // get teachers only if grouping selected (for better performance) if ($group_field === 'dozent_id') { $teachers = new SimpleCollection($course->getMembersWithStatus('dozent')); $_course['teachers'] = $teachers->map(function (CourseMember $a) { return $a->user->getFullName('no_title_rev'); }); } $_course['last_visitdate'] = $visits[$course->id][0]['last_visitdate']; $_course['visitdate'] = $visits[$course->id][0]['visitdate']; $_course['user_status'] = $user_status; $_course['gruppe'] = !$is_deputy ? $member_ships[$course->id]['gruppe'] ?? null : ($deputy ? $deputy->gruppe : null); $_course['sem_number_end'] = $course->isOpenEnded() ? $max_sem_key : Semester::getIndexById($course->end_semester->id); $_course['sem_number'] = Semester::getIndexById($course->start_semester->id); $_course['tools'] = $course->tools; $_course['name'] = $course->name; $_course['temp_name'] = $course->name; $_course['number'] = $course->veranstaltungsnummer; $_course['is_deputy'] = $is_deputy; if ($show_semester_name && count($course->semesters) !== 1 && !$course->getSemClass()['studygroup_mode']) { $_course['name'] .= ' (' . $course->getTextualSemester() . ')'; } if ($course->parent_course) { $_course['parent_course'] = $course->parent_course; } $_course['is_group'] = $course->getSemClass()->isGroup(); $_course['navigation'] = self::getAdditionalNavigations( $_course['seminar_id'], $_course, $_course['sem_class'], $GLOBALS['user']->id, $visits[$course->id] ); // add the the course to the correct semester if (empty($_course['parent_course'])) { if ($course->isOpenEnded()) { if ($current_semester_nr >= $min_sem_key && $current_semester_nr <= $max_sem_key) { $sem_courses[$current_semester_nr][$course->id] = $_course; $semester_assign[$course->id] = $current_semester_nr; } else { $sem_courses[$max_sem_key][$course->id] = $_course; $semester_assign[$course->id] = $max_sem_key; } } else { for ($i = $min_sem_key; $i <= $max_sem_key; $i += 1) { if ($i >= $_course['sem_number'] && $i <= $_course['sem_number_end']) { $sem_courses[$i][$course->id] = $_course; $semester_assign[$course->id] = $i; } } } } else { $children[$_course['parent_course']][] = $_course; } } // Now sort children directly under their parent. foreach ($children as $parent => $kids) { $sem_courses[$semester_assign[$parent]][$parent]['children'] = $kids; } if (!empty($params['main_navigation'])) { return $sem_courses; } krsort($sem_courses); // grouping if ($group_field === 'sem_number' && !$params['order_by']) { foreach ($sem_courses as $index => $courses) { uasort($courses, function ($a, $b) { $extra_condition = 0; if (Config::get()->IMPORTANT_SEMNUMBER) { $extra_condition = strcmp($a['number'], $b['number']); } return ($a['gruppe'] - $b['gruppe']) ?: $extra_condition ?: strcmp($a['temp_name'], $b['temp_name']); }); $sem_courses[$index] = $courses; } } // Group by teacher if ($group_field === 'dozent_id') { self::groupByTeacher($sem_courses); } // Group by Sem Status if ($group_field === 'sem_status') { self::groupBySemStatus($sem_courses); } // Group by colors if ($group_field === 'gruppe') { self::groupByGruppe($sem_courses); } // Group by sem_tree if ($group_field === 'sem_tree_id') { self::groupBySemTree($sem_courses); } // Group by mvv module if ($group_field === 'mvv') { self::groupByMVVModule($sem_courses); } return $sem_courses; } /** * Get the whole icon-navigation for a given course * @param $object_id * @param $my_obj_values * @param null $sem_class * @param $user_id * @return array */ public static function getAdditionalNavigations($object_id, &$my_obj_values, $sem_class, $user_id, $visit_data = []) { $navigation = []; foreach (self::getDefaultModules() as $plugin_id => $plugin) { // Go to next module if current module is not available and not voting-module if ($plugin !== 'vote' && !$my_obj_values['tools']->findOneBy('plugin_id', $plugin_id)) { $navigation[$plugin_id] = null; continue; } if (!Config::get()->VOTE_ENABLE && $plugin_id === 'vote') { continue; } if ($plugin === 'vote') { $navigation[$plugin_id] = self::checkVote($my_obj_values, $user_id, $object_id); } else if ($tool = $my_obj_values['tools']->findOneBy('plugin_id', $plugin_id)) { if (Seminar_Perm::get()->have_studip_perm($tool->getVisibilityPermission(), $object_id, $user_id)) { $navigation[$plugin_id] = $plugin->getIconNavigation($object_id, $visit_data[$plugin_id]['visitdate'], $user_id); } else { $navigation[$plugin_id] = null; } } } foreach ($my_obj_values['tools'] as $tool) { if (array_key_exists($tool->plugin_id, $navigation)) { continue; } $module = $tool->getStudipModule(); if (!$module || get_class($module) === 'CoreAdmin' || get_class($module) === 'CoreStudygroupAdmin') { continue; } if (Seminar_Perm::get()->have_studip_perm($tool->getVisibilityPermission(), $object_id, $user_id)) { $navigation[$tool->plugin_id] = $module->getIconNavigation($object_id, $visit_data[$tool->plugin_id]['visitdate'], $user_id); } else { $navigation[$tool->plugin_id] = null; } } return $navigation; } /** * This function reset all visits on every available modules * @param $object * @param $user_id * @return bool */ public static function setObjectVisits($object, $user_id, $timestamp = null) { // load plugins, so they have a chance to register themselves as observers PluginEngine::getPlugins('StandardPlugin'); // Update news, votes and evaluations $query = "INSERT INTO object_user_visits (object_id, user_id, plugin_id, visitdate, last_visitdate) ( SELECT questionnaire_id, :user_id, '-1', :timestamp, 0 FROM questionnaire_assignments WHERE range_id = :id ) UNION ( SELECT eval_id, :user_id, '-2', :timestamp, 0 FROM eval_range WHERE range_id = :id ) UNION ( SELECT `news_id`, :user_id, `pluginid`, :timestamp, 0 FROM `news_range` JOIN `plugins` ON (`pluginclassname` = 'CoreOverview') WHERE `range_id` = :id ) ON DUPLICATE KEY UPDATE last_visitdate = IFNULL(visitdate, 0), visitdate = :timestamp"; $statement = DBManager::get()->prepare($query); $statement->bindValue(':user_id', $user_id); $statement->bindValue(':timestamp', $timestamp ?: time()); $statement->bindValue('id', $object->id); $statement->execute(); // Update all activated modules foreach ($object->tools as $module) { object_set_visit($object->id, $module->plugin_id); } // Update object itself object_set_visit($object->id, 0); return true; } /** * Returns a list of all the courses the user is in the waiting list for. * * @param string $user_id Id of the user * @return array */ public static function getWaitingList($user_id) { $sql = "SELECT set_id, priorities.seminar_id,'claiming' as status, seminare.Name, seminare.Ort, priorities.priority, coursesets.name AS cname, seminare.admission_binding FROM priorities INNER JOIN seminare USING(seminar_id) INNER JOIN coursesets USING (set_id) WHERE priorities.user_id = ? ORDER BY coursesets.name, priorities.priority"; $claiming = DBManager::get()->fetchAll($sql, [$user_id]); $csets = []; foreach ($claiming as $k => $claim) { if (!$csets[$claim['set_id']]) { $csets[$claim['set_id']] = new CourseSet($claim['set_id']); } $cs = $csets[$claim['set_id']]; if (!$cs->hasAlgorithmRun()) { $claiming[$k]['admission_endtime'] = $cs->getSeatDistributionTime(); $num_claiming = count(AdmissionPriority::getPrioritiesByCourse($claim['set_id'], $claim['seminar_id'])); $free = Course::find($claim['seminar_id'])->getFreeSeats(); if ($free <= 0) { $claiming[$k]['admission_chance'] = 0; } else if ($free >= $num_claiming) { $claiming[$k]['admission_chance'] = 100; } else { $claiming[$k]['admission_chance'] = round(($free / $num_claiming) * 100); } } else { unset($claiming[$k]); } } $stmt = DBManager::get()->prepare( "SELECT admission_seminar_user.*, seminare.status as sem_status, " . "seminare.Name, seminare.Ort, seminare.admission_binding " . "FROM admission_seminar_user " . "INNER JOIN seminare USING(seminar_id) " . "WHERE user_id = ? " . "ORDER BY admission_seminar_user.status, name"); $stmt->execute([$user_id]); return array_merge($claiming, $stmt->fetchAll(PDO::FETCH_ASSOC)); } /** * Get all user assigned institutes based on simple or map * @return array */ public static function getMyInstitutes(): array { $memberShips = InstituteMember::findByUser($GLOBALS['user']->id); if (empty($memberShips)) { return []; } $institutes = []; $insts = new SimpleCollection($memberShips); foreach ($insts as $inst) { $index = $inst->institute->id; $visits = get_objects_visits([$index], 0, null, null, $inst->institute->tools->pluck('plugin_id')); $institutes[$index] = $inst->institute->toArray(); $institutes[$index]['tools'] = $inst->institute->tools; $institutes[$index]['perms'] = $inst->inst_perms; $institutes[$index]['visitdate'] = $visits[$index][0]['visitdate']; $institutes[$index]['last_visitdate'] = $visits[$index][0]['last_visitdate']; $institutes[$index]['obj_type'] = 'inst'; $institutes[$index]['navigation'] = self::getAdditionalNavigations( $index, $institutes[$index], SemClass::getDefaultInstituteClass($inst->institute->type), $GLOBALS['user']->id, $visits[$index] ); } return $institutes; } /** * Groups the list of courses by sem tree criteria. * * @param array $sem_courses List of courses */ public static function groupBySemTree(&$sem_courses) { $_tmp_courses = []; foreach ($sem_courses as $sem_key => $collection) { if (!isset($_tmp_courses[$sem_key])) { $_tmp_courses[$sem_key] = []; } //We have to store the sem_tree names separately //since we first need the sem_tree IDs as array keys. //This makes it more easy to get the ordering //of the sem_tree objects. $sem_tree_names = []; foreach ($collection as $course) { if (!empty($course['sem_tree'])) { foreach ($course['sem_tree'] as $tree) { $sem_tree_names[$tree['sem_tree_id']] = $tree['name']; if (!isset($_tmp_courses[$sem_key][(string)$tree['sem_tree_id']])) { $_tmp_courses[$sem_key][(string)$tree['sem_tree_id']] = []; } $_tmp_courses[$sem_key][(string)$tree['sem_tree_id']][$course['seminar_id']] = $course; } } else { if (!isset($_tmp_courses[$sem_key][''])) { $_tmp_courses[$sem_key][''] = []; } $_tmp_courses[$sem_key][''][$course['seminar_id']] = $course; } } // Create sort order for assigned sem_tree entries. $entries = StudipStudyArea::findMany(array_keys($sem_tree_names)); $order = []; foreach ($entries as $entry) { $order[$entry->getId()] = $entry->getIndex(); } $max = max(array_map('strlen', $order)); // Now sort courses by sem_tree entry order. uksort($_tmp_courses[$sem_key], function ($a, $b) use ($order, $max) { return str_pad($order[$a] ?? '', $max, '0') - str_pad($order[$b] ?? '', $max, '0'); }); //At this point the $_tmp_courses array is sorted by the ordering //of the sem_tree. //Now we have to replace the sem_tree IDs in the second layer //of the $_tmp_courses array with the sem_tree names: foreach ($_tmp_courses[$sem_key] as $sem_tree_id => $courses) { foreach ($courses as $course) { $index = (string)($sem_tree_names[$sem_tree_id] ?? ''); if (!isset($_tmp_courses[$sem_key][$index])) { $_tmp_courses[$sem_key][$index] = []; } $_tmp_courses[$sem_key][$index][$course['seminar_id']] = $course; } if (isset($sem_tree_names[$sem_tree_id])) { unset($_tmp_courses[$sem_key][$sem_tree_id]); } } } //After the $_tmp_courses array has been built we must sort the //third layer (course collection) by group (color), //by number (at your option) and by name: foreach ($_tmp_courses as $sem_key => $sem_tree) { foreach ($sem_tree as $sem_tree_name => $collection) { //We must sort all courses by their group and their name: uasort($collection, function ($a, $b) { if (Config::get()->IMPORTANT_SEMNUMBER) { return strnatcasecmp($a['gruppe'], $b['gruppe']) ?: strnatcasecmp($a['number'], $b['number']) ?: strnatcasecmp($a['temp_name'], $b['temp_name']); } else { return strnatcasecmp($a['gruppe'], $b['gruppe']) ?: strnatcasecmp($a['temp_name'], $b['temp_name']); } }); $_tmp_courses[$sem_key][$sem_tree_name] = $collection; } } $sem_courses = $_tmp_courses; } /** * Groups the list of courses by defined group. * * @param array $sem_courses */ public static function groupByGruppe(&$sem_courses) { $_tmp_courses = []; foreach ($sem_courses as $sem_key => $collection) { foreach ($collection as $course) { $_tmp_courses[$sem_key][$course['gruppe']][$course['seminar_id']] = $course; ksort($_tmp_courses[$sem_key]); } } $sem_courses = $_tmp_courses; } /** * Groups the list of courses by the corresponding course's status. * * @param array $sem_courses */ public static function groupBySemStatus(&$sem_courses) { $_tmp_courses = []; foreach ($sem_courses as $sem_key => $collection) { foreach ($collection as $course) { $sem_status = $GLOBALS['SEM_TYPE'][$course['status']]["name"] . " (" . $GLOBALS['SEM_CLASS'][$GLOBALS['SEM_TYPE'][$course['status']]["class"]]["name"] . ")"; $_tmp_courses[$sem_key][$sem_status][$course['seminar_id']] = $course; } // reorder array uksort($_tmp_courses[$sem_key], function ($a, $b) { if (ucfirst($a) == ucfirst($b)) return 0; return ucfirst($a) < ucfirst($b) ? -1 : 1; }); } $sem_courses = $_tmp_courses; } /** * Groups the list of courses by the teacher(s) of the course. * * @param array $sem_courses */ public static function groupByTeacher(&$sem_courses) { $_tmp_courses = []; foreach ($sem_courses as $sem_key => $collection) { foreach ($collection as $course) { if (!empty($course['teachers'])) { foreach ($course['teachers'] as $fullname) { $_tmp_courses[$sem_key][$fullname][$course['seminar_id']] = $course; } } else { $_tmp_courses[$sem_key][""][$course['seminar_id']] = $course; } ksort($_tmp_courses[$sem_key]); } } $sem_courses = $_tmp_courses; } /** * Groups the list of courses by associated mvv module of the course.. * * @param array $sem_courses */ public static function groupByMVVModule(array &$sem_courses): void { $_tmp_courses = []; foreach ($sem_courses as $sem_key => $collection) { $_tmp_courses[$sem_key] = []; foreach ($collection as $course) { $modules = Course::getMVVModulesForCourseId($course['seminar_id']); if ($modules) { $modules = array_map(function (Modul $module) { return $module->getDisplayName(); }, $modules); } else { $modules = [_('Keinem Modul zugeordnet')]; } foreach ($modules as $module) { if (!isset($_tmp_courses[$sem_key][$module])) { $_tmp_courses[$sem_key][$module] = []; } $_tmp_courses[$sem_key][$module][$course['seminar_id']] = $course; } } ksort($_tmp_courses[$sem_key]); } $sem_courses = $_tmp_courses; } /** * Retrieves all study groups for the current user. * * @returns array A two-dimensional array. The second dimension contains * data for each study group. Most fields of the Course model are * present in the second dimension and there are additional fields * like the colour (gruppe) or the start and end semester. */ public static function getStudygroups() { $studygroup_sem_types = array_filter( array_keys($GLOBALS['SEM_TYPE']), function ($sem_type_id) { return (bool) $GLOBALS['SEM_CLASS'][$GLOBALS['SEM_TYPE'][$sem_type_id]['class']]['studygroup_mode']; } ); $studygroup_memberships = CourseMember::findBySQL( 'INNER JOIN `seminare` USING (`seminar_id`) WHERE `seminar_user`.`user_id` = :me AND `seminare`.`status` IN (:studygroup_semtypes) GROUP BY `seminar_id` ORDER BY `seminar_user`.`gruppe` ASC, `seminare`.`name` ASC', [ 'me' => User::findCurrent()->id, 'studygroup_semtypes' => $studygroup_sem_types ] ); $studygroups = []; Course::findEachMany( function ($studygroup) use (&$studygroups) { $studygroups[$studygroup->id] = $studygroup; }, array_map( function ($membership) { return $membership->seminar_id; }, $studygroup_memberships ) ); $data_fields = 'name seminar_id visible veranstaltungsnummer start_time duration_time status visible ' . 'chdate admission_binding admission_prelim'; $studygroup_data = []; foreach ($studygroup_memberships as $membership) { if (!isset($studygroups[$membership->seminar_id])) { continue; } $studygroup = $studygroups[$membership->seminar_id]; $visit_data = get_objects_visits([$studygroup->id], 0, null, null, $studygroup->tools->pluck('plugin_id')); $data = $studygroup->toArray($data_fields); $data['tools'] = $studygroup->tools; $data['sem_class'] = $studygroup->getSemClass(); $data['start_semester'] = $studygroup->start_semester->name; $data['end_semester'] = $studygroup->end_semester->name ?? ''; $data['obj_type'] = 'sem'; $data['user_status'] = $membership->status; $data['gruppe'] = $membership->gruppe; $data['visitdate'] = $visit_data[$studygroup->id][0]['visitdate']; $data['last_visitdate'] = $visit_data[$studygroup->id][0]['last_visitdate']; $data['navigation'] = self::getAdditionalNavigations( $studygroup->id, $data, $data['sem_class'], $GLOBALS['user']->id, $visit_data[$studygroup->id] ); $studygroup_data[$studygroup->id] = $data; } return $studygroup_data; } /** * Calc nav elements to get the table-column-width * @param $my_obj * @param string $group_field * @return int */ public static function calc_nav_elements($my_obj, $group_field = 'sem_number') { $nav_elements = 0; if (empty($my_obj)) { return $nav_elements; } foreach ($my_obj as $courses) { if(!empty($courses)) { if ($group_field !== 'sem_number') { // tlx: If array is 2-dimensional, merge it into a 1-dimensional $courses = call_user_func_array('array_merge', $courses); } foreach ($courses as $course) { $nav_elements = max($nav_elements, count(self::array_rtrim($course['navigation']))); } } } return $nav_elements; } /** * Calculates and returns the maximum length of the given course's navigations. * * @param array $collection * @return int */ public static function calc_single_navigation($collection) { $nav_elements = 0; if (!empty($collection)) { foreach ($collection as $course) { $nav_elements = max($nav_elements, count(self::array_rtrim($course['navigation']))); } } return $nav_elements; } /** * Trims an array from it's null value from the right. * * @param array $array The array to trim * @return array The trimmed array * @author tlx */ public static function array_rtrim($array) { $temp = array_reverse($array, true); $empty = true; while ($empty && !empty($temp)) { $item = reset($temp); if ($empty = ($item === null)) { $temp = array_slice($temp, 1, null, true); } } return array_reverse($temp, true); } /** * Sorts the list of courses by given order and parent/child relation. * * @param array $courses List of courses * @param string $order Order to sort by * @return SimpleCollection */ private static function sortCourses($courses, $order) { $sorted = $courses->orderBy($order); // First get all courses that can act as parent and have child courses. $parents = $courses->filter(function ($c) { return $c->getSemClass()->isGroup() && count($c->children) > 0; }); // Sort children directly after parents. Only necessary if parents exist. if (count($parents) > 0) { $withChildren = new SimpleCollection(); foreach ($sorted as $c) { if ($c->parent_course === null) { $withChildren->append($c); if (count($c->children) > 0) { foreach ($sorted->findBy('parent_course', $c->id) as $child) { $withChildren->append($child); } } } } $sorted = $withChildren; } return $sorted; } public static function getDefaultModules($range_type = 'course') { $default_modules = []; if ($range_type === 'course') { $sem_class = array_values(SemClass::getClasses())[0] ?? SemClass::getDefaultSemClass(); } else { $sem_class = SemClass::getDefaultInstituteClass(1); } foreach ($sem_class->getActivatedModuleObjects() as $id => $plugin) { if (get_class($plugin) === 'CoreAdmin' || get_class($plugin) === 'CoreStudygroupAdmin') { continue; } $default_modules[$id] = $plugin; } $default_modules[-1] = 'vote'; return $default_modules; } }