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

my_courses.php

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    my_courses.php 43.32 KiB
    <?php
    /**
     * my_courses.php - Controller 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    Jan-Hendrik Willms <tleilax+studip@gmail.com>
     * @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 MyCoursesController extends AuthenticatedController
    {
        /**
         * @var Callable
         */
        private $performance_timer = null;
    
        public function before_filter(&$action, &$args)
        {
            parent::before_filter($action, $args);
    
            if ($GLOBALS['perm']->have_perm('admin')) {
                $this->redirect('admin/courses/index');
                return;
            }
    
            // we are defintely not in an lecture or institute
            closeObject();
            $_SESSION['links_admin_data'] = '';
    
            // measure performance of #index_action
            if ($action === 'index') {
                $this->performance_timer = Metrics::startTimer();
            }
        }
    
        public function after_filter($action, $args)
        {
            parent::after_filter($action, $args);
    
            // send performance metric
            if (isset($this->performance_timer)) {
                $timer = $this->performance_timer;
                $timer('core.my_courses_time');
            }
        }
    
        /**
         * Autor / Tutor / Teacher action
         */
        public function index_action()
        {
            if ($GLOBALS['perm']->have_perm('root')) {
                throw new AccessDeniedException();
            }
    
            if ($GLOBALS['perm']->have_perm('admin')) {
                $this->redirect('my_courses/admin');
                return;
            }
    
            Navigation::activateItem('/browse/my_courses/list');
            PageLayout::setHelpKeyword('Basis.MeineVeranstaltungen');
            PageLayout::setTitle(_('Meine Veranstaltungen'));
    
            $this->sem_data = Semester::getAllAsArray();
    
            $sem_key     = $this->getSemesterKey();
            $group_field = $this->getGroupField();
    
            $sem_courses  = MyRealmModel::getPreparedCourses($sem_key, [
                'group_field'         => $group_field,
                'order_by'            => null,
                'order'               => 'asc',
                'studygroups_enabled' => Config::get()->MY_COURSES_ENABLE_STUDYGROUPS,
                'deputies_enabled'    => Config::get()->DEPUTIES_ENABLE,
            ]);
    
            // Waiting list
            $this->waiting_list = MyRealmModel::getWaitingList($GLOBALS['user']->id);
    
            // Deputies
            $this->my_bosses                   = Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE ? Deputy::findDeputyBosses() : [];
            $this->deputies_edit_about_enabled = Config::get()->DEPUTIES_EDIT_ABOUT_ENABLE;
    
            // Check for new contents
            if ($tabularasa = $this->flash['tabularasa']) {
                $details = [];
                if ($this->check_for_new($sem_courses, $group_field)) {
                    $details[] = sprintf(
                        _('Seit Ihrem letzten Seitenaufruf (%s) sind allerdings neue Inhalte hinzugekommen.'),
                        reltime($tabularasa)
                    );
                }
    
                PageLayout::postSuccess(_('Alles als gelesen markiert!'), $details);
            }
    
            $this->setupSidebar($sem_key, $group_field, $this->check_for_new($sem_courses, $group_field));
    
            $temp_courses = [];
            $groups = [];
            if (is_array($sem_courses)) {
                foreach ($sem_courses as $_outer_index => $_outer) {
                    if ($group_field === 'sem_number') {
                        $_courses = [];
    
                        foreach ($_outer as $course) {
                            $_courses[$course['seminar_id']] = $course;
                            if (!empty($course['children']) && is_array($course['children'])) {
                                foreach ($course['children'] as $child) {
                                    $_courses[$child['seminar_id']] = $child;
                                }
                            }
                        }
    
                        $groups[] = [
                            'id' => $_outer_index,
                            'name' => (string)$this->sem_data[$_outer_index]['name'],
                            'data' => [
                                [
                                    'id' => md5($_outer_index),
                                    'label' => false,
                                    'ids' => array_keys($_courses),
                                ],
                            ],
                        ];
                        $temp_courses = array_merge($temp_courses, $_courses);
                    } else {
                        $count = 1;
                        $_groups = [];
                        foreach ($_outer as $_inner_index => $_inner) {
                            $_courses = [];
    
                            foreach ($_inner as $course) {
                                $_courses[$course['seminar_id']] = $course;
                                if ($course['children']) {
                                    foreach ($course['children'] as $child) {
                                        $_courses[$child['seminar_id']] = $child;
                                    }
                                }
                            }
    
                            $label = $_inner_index;
                            if ($group_field === 'sem_tree_id' && !$label) {
                                $label = _('keine Zuordnung');
                            } elseif ($group_field === 'gruppe') {
                                $label = _('Gruppe') . ' ' . $count++;
                            }
    
                            $_groups[] = [
                                'id' => md5($_outer_index . $_inner_index),
                                'label' => $label,
                                'ids' => array_keys($_courses),
                            ];
    
                            $temp_courses = array_merge($temp_courses, $_courses);
                        }
    
                        $groups[] = [
                            'id' => $_outer_index,
                            'name' => (string)$this->sem_data[$_outer_index]['name'],
                            'data' => $_groups,
                        ];
                    }
                }
            }
    
            $data = [
                'courses' => $this->sanitizeNavigations(array_map([$this, 'convertCourse'], $temp_courses)),
                'groups'  => $groups,
                'user_id' => $GLOBALS['user']->id,
                'config'  => [
                    'allow_dozent_visibility'  => Config::get()->ALLOW_DOZENT_VISIBILITY,
                    'open_groups'              => $GLOBALS['user']->cfg->MY_COURSES_OPEN_GROUPS,
                    'sem_number'               => Config::get()->IMPORTANT_SEMNUMBER,
                    'display_type'             => $GLOBALS['user']->cfg->MY_COURSES_TILED_DISPLAY ? 'tiles' : 'tables',
                    'responsive_type'          => $GLOBALS['user']->cfg->MY_COURSES_TILED_DISPLAY_RESPONSIVE ? 'tiles' : 'tables',
                    'navigation_show_only_new' => $GLOBALS['user']->cfg->MY_COURSES_SHOW_NEW_ICONS_ONLY,
                    'group_by'                 => $this->getGroupField(),
                ],
            ];
    
            PageLayout::addHeadElement(
                'script',
                ['type' => 'text/javascript'],
                'window.STUDIP.MyCoursesData = ' . json_encode($data) . ';'
            );
        }
    
        /**
         * PDF export of course overview
         */
        public function courseexport_action()
        {
            if ($GLOBALS['perm']->have_perm('admin')) {
                throw new AccessDeniedException();
            }
    
            $this->with_modules = Request::bool('modules');
    
            $this->sem_data = Semester::getAllAsArray();
    
            $this->group_field = 'sem_number';
    
            // Needed parameters for selecting courses
            $params = [
                'group_field'         => $this->group_field,
                'order_by'            => null,
                'order'               => 'asc',
                'studygroups_enabled' => Config::get()->MY_COURSES_ENABLE_STUDYGROUPS,
                'deputies_enabled'    => Config::get()->DEPUTIES_ENABLE,
            ];
    
            $this->sem_courses  = MyRealmModel::getPreparedCourses('all', $params);
    
            $factory  = $this->get_template_factory();
            $template = $factory->open('my_courses/courseexport');
            $template->set_attributes($this->get_assigned_variables());
            $template->image_style = 'height: 6px; width: 8px;';
    
            $doc = new ExportPDF();
            $doc->addPage();
            $doc->SetFont('helvetica', '', 10);
            $doc->writeHTML($template->render(), false, false, true);
    
            $this->render_pdf($doc, 'courseexport.pdf');
        }
    
        /**
         * Seminar group administration - cluster your seminars by colors or
         * change grouping mechanism
         */
        public function groups_action($sem = null, $studygroups = false)
        {
            if ($GLOBALS['perm']->have_perm('admin')) {
                throw new AccessDeniedException();
            }
    
            $this->title = _('Meine Veranstaltungen') . ' - ' . _('Farbgruppierungen');
    
            PageLayout::setTitle($this->title);
    
    
            PageLayout::setHelpKeyword('Basis.VeranstaltungenOrdnen');
            Navigation::activateItem('/browse/my_courses/list');
    
            $this->current_semester = $sem ?: Semester::findCurrent()->semester_id;
            $this->semesters = Semester::findAllVisible();
    
            $forced_grouping = Config::get()->MY_COURSES_FORCE_GROUPING;
            if ($forced_grouping == 'not_grouped') {
                $forced_grouping = 'sem_number';
            }
    
            $no_grouping_allowed = ($forced_grouping == 'sem_number' || !in_array($forced_grouping, getValidGroupingFields()));
    
            $group_field = $GLOBALS['user']->cfg->MY_COURSES_GROUPING ? : $forced_grouping;
    
            $groups     = [];
            $add_fields = '';
            $add_query  = '';
    
            if ($group_field == 'sem_tree_id') {
                $add_fields = ', sem_tree_id';
                $add_query  = "LEFT JOIN seminar_sem_tree sst ON (sst.seminar_id=seminare.Seminar_id)";
            } else if ($group_field == 'dozent_id') {
                $add_fields = ', su1.user_id as dozent_id';
                $add_query  = "LEFT JOIN seminar_user as su1 ON (su1.seminar_id=seminare.Seminar_id AND su1.status='dozent')";
            }
    
            $dbv = DbView::getView('sem_tree');
    
            $query = "SELECT seminare.VeranstaltungsNummer AS sem_nr, seminare.Name, seminare.Seminar_id,
                             seminare.status AS sem_status, seminar_user.gruppe, seminare.visible,
                             {$dbv->sem_number_sql} AS sem_number,
                             {$dbv->sem_number_end_sql} AS sem_number_end {$add_fields}
                      FROM seminar_user
                      JOIN semester_data sd
                      LEFT JOIN seminare USING (Seminar_id)
                      {$add_query}
                      WHERE seminar_user.user_id = ?";
            if (Config::get()->MY_COURSES_ENABLE_STUDYGROUPS && !$studygroups) {
                $studygroup_types = DBManager::get()->quote(studygroup_sem_types());
                $query .= " AND seminare.status NOT IN ({$studygroup_types})";
            } elseif ($studygroups) {
                $studygroup_types = DBManager::get()->quote(studygroup_sem_types());
                $query .= " AND seminare.status IN ({$studygroup_types})";
            }
    
            if (Config::get()->DEPUTIES_ENABLE) {
                $query .= " UNION "
                    . Deputy::getMySeminarsQuery('gruppe', $dbv->sem_number_sql, $dbv->sem_number_end_sql, $add_fields, $add_query);
            }
            $query .= " ORDER BY sem_nr ASC";
    
            $statement = DBManager::get()->prepare($query);
            $statement->execute([$GLOBALS['user']->id, studygroup_sem_types()]);
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $my_sem[$row['Seminar_id']] = [
                    'obj_type'       => 'sem',
                    'sem_nr'         => $row['sem_nr'],
                    'name'           => $row['Name'],
                    'visible'        => $row['visible'],
                    'gruppe'         => $row['gruppe'],
                    'sem_status'     => $row['sem_status'],
                    'sem_number'     => $row['sem_number'],
                    'sem_number_end' => $row['sem_number_end'],
                ];
                if ($group_field) {
                    fill_groups($groups, $row[$group_field], [
                        'seminar_id' => $row['Seminar_id'],
                        'sem_nr'     => $row['sem_nr'],
                        'name'       => $row['Name'],
                        'gruppe'     => $row['gruppe']
                    ]);
                }
            }
    
            if ($group_field == 'sem_number') {
                correct_group_sem_number($groups, $my_sem);
            } else {
                add_sem_name($my_sem);
            }
    
            sort_groups($group_field, $groups);
    
            // Ensure that a seminar is never in multiple groups
            $sem_ids = [];
            foreach ($groups as $group_id => $seminars) {
                foreach ($seminars as $index => $seminar) {
                    if (in_array($seminar['seminar_id'], $sem_ids)) {
                        unset($seminars[$index]);
                    } else {
                        $sem_ids[] = $seminar['seminar_id'];
                    }
                }
                if (empty($seminars)) {
                    unset($groups[$group_id]);
                } else {
                    $groups[$group_id] = $seminars;
                }
            }
            $this->studygroups         = $studygroups;
            $this->no_grouping_allowed = $no_grouping_allowed;
            $this->groups              = $groups;
            $this->group_names         = get_group_names($group_field, $groups);
            $this->group_field         = $group_field;
            $this->my_sem              = $my_sem;
            $this->cid                 = Request::get('cid') ? Request::get('cid') : '';
        }
    
        /**
         * Storage function for the groups action.
         * Stores selected grouping category and actual group settings.
         */
        public function store_groups_action($studygroups = false)
        {
            if ($GLOBALS['perm']->have_perm('admin')) {
                throw new AccessDeniedException();
            }
    
            $deputies_enabled = Config::get()->DEPUTIES_ENABLE;
            $GLOBALS['user']->cfg->store(
                'MY_COURSES_GROUPING',
                Request::get('select_group_field', $GLOBALS['user']->cfg->MY_COURSES_GROUPING)
            );
            $gruppe = Request::getArray('gruppe');
            if (!empty($gruppe)) {
                $query = "UPDATE seminar_user SET gruppe = ? WHERE Seminar_id = ? AND user_id = ?";
                $user_statement = DBManager::get()->prepare($query);
    
                $query = "UPDATE deputies SET gruppe = ? WHERE range_id = ? AND user_id = ?";
                $deputy_statement = DBManager::get()->prepare($query);
    
                foreach ($gruppe as $key => $value) {
                    $user_statement->execute([$value,
                        $key,
                        $GLOBALS['user']->id
                    ]);
                    $updated = $user_statement->rowCount();
    
                    if ($deputies_enabled && !$updated) {
                        $deputy_statement->execute([
                            $value,
                            $key,
                            $GLOBALS['user']->id
                        ]);
                    }
                }
            }
    
            if (Request::get('cid')) {
                $redirect = "course/overview?cid=" . Request::get('cid');
            } else {
                $redirect = 'my_courses/index';
            }
    
            $this->redirect($studygroups ? 'my_studygroups/index' : $redirect);
        }
    
    
        /**
         * @param string $type
         * @param string $sem
         */
        public function tabularasa_action($sem = 'all', $timestamp = null)
        {
            NotificationCenter::postNotification('OverviewWillClear', $GLOBALS['user']->id);
    
            $timestamp        = $timestamp ?: time();
            $deputies_enabled = Config::get()->DEPUTIES_ENABLE;
    
            $semesters   = MyRealmModel::getSelectedSemesters($sem);
            $min_sem_key = min($semesters);
            $max_sem_key = max($semesters);
            $courses     = MyRealmModel::getCourses($min_sem_key, $max_sem_key, compact('deputies_enabled'));
            foreach ($courses as $index => $course) {
                MyRealmModel::setObjectVisits($course, $GLOBALS['user']->id, $timestamp);
            }
    
            NotificationCenter::postNotification('OverviewDidClear', $GLOBALS['user']->id);
    
            $this->flash['tabularasa'] = $timestamp;
            $this->redirect('my_courses/index');
        }
    
    
        /**
         * This action display only a message
         */
        public function decline_binding_action()
        {
            if ($GLOBALS['perm']->have_perm('admin')) {
                throw new AccessDeniedException();
            }
            PageLayout::postError(_('Die Anmeldung ist verbindlich. Bitte wenden Sie sich an die Lehrenden.'));
            $this->redirect('my_courses/index');
        }
    
        /**
         * This action remove a user from course
         * @param $course_id
         */
        public function decline_action($course_id, $waiting = null)
        {
            $current_seminar = Seminar::getInstance($course_id);
            $ticket_check    = Seminar_Session::check_ticket(Request::option('studipticket'));
            if (LockRules::Check($course_id, 'participants')) {
                $lockdata = LockRules::getObjectRule($course_id);
                PageLayout::postError(sprintf(
                    _('Sie können sich nicht von der Veranstaltung <b>%s</b> abmelden.'),
                    htmlReady($current_seminar->name)
                ));
                if ($lockdata['description']) {
                    PageLayout::postInfo(formatLinks($lockdata['description']));
                }
                $this->redirect('my_courses/index');
                return;
            }
    
            // Ensure last teacher cannot leave course
            $course = Course::find($course_id);
            if ($course->members->findOneBy('user_id', $GLOBALS['user']->id)->status === 'dozent'
                && count($course->getMembersWithStatus('dozent')) === 1
            ) {
                PageLayout::postError(sprintf(
                    _('Sie können sich nicht von der Veranstaltung <b>%s</b> abmelden.'),
                    htmlReady($current_seminar->name)
                ));
                $this->redirect('my_courses/index');
                return;
            }
    
            if (Request::option('cmd') == 'back') {
                $this->redirect('my_courses/index');
                return;
            }
    
            if (Request::option('cmd') != 'kill' && Request::option('cmd') != 'kill_admission') {
                if ($current_seminar->admission_binding && Request::get('cmd') != 'suppose_to_kill_admission' && !LockRules::Check($current_seminar->getId(), 'participants')) {
                    PageLayout::postError(sprintf(_("Die Veranstaltung <b>%s</b> ist als <b>bindend</b> angelegt.
                        Wenn Sie sich abmelden wollen, müssen Sie sich an die Lehrende der Veranstaltung wenden."),
                        htmlReady($current_seminar->name)));
                    $this->redirect('my_courses/index');
                    return;
                }
    
                if (Request::get('cmd') == 'suppose_to_kill') {
                    // check course admission
                    list(,$admission_end_time) = @array_values($current_seminar->getAdmissionTimeFrame());
    
                    $admission_enabled = $current_seminar->isAdmissionEnabled();
                    $admission_locked   = $current_seminar->isAdmissionLocked();
    
                    if ($admission_enabled || $admission_locked || (int)$current_seminar->admission_prelim == 1) {
                        $message = sprintf(
                            _('Wollen Sie sich von der teilnahmebeschränkten Veranstaltung "%s" wirklich abmelden? Sie verlieren damit die Berechtigung für die Veranstaltung und müssen sich ggf. neu anmelden!'),
                            htmlReady($current_seminar->name)
                        );
                    } else if (isset($admission_end_time) && $admission_end_time < time()) {
                        $message = sprintf(
                            _('Wollen Sie sich von der teilnahmebeschränkten Veranstaltung "%s" wirklich abmelden? Der Anmeldezeitraum ist abgelaufen und Sie können sich nicht wieder anmelden!'),
                            htmlReady($current_seminar->name)
                        );
                    } else {
                        $message = sprintf(_('Wollen Sie sich von der Veranstaltung "%s" wirklich abmelden?'), htmlReady($current_seminar->name));
                    }
                    $cmd = 'kill';
                } else {
                    if (admission_seminar_user_get_position($GLOBALS['user']->id, $course_id) === false) {
                        $message = sprintf(
                            _('Wollen Sie sich von der Anmeldeliste der Veranstaltung "%s" wirklich abmelden?'),
                            htmlReady($current_seminar->name)
                        );
                    } else {
                        $message = sprintf(
                            _('Wollen Sie sich von der Warteliste der Veranstaltung "%s" wirklich abmelden? Sie verlieren damit die bereits erreichte Position und müssen sich ggf. neu anmelden!'),
                            htmlReady($current_seminar->name)
                        );
                    }
                    $cmd = 'kill_admission';
                }
    
                PageLayout::postQuestion(
                    $message,
                    $this->declineURL($course_id, ['cmd' => $cmd, 'studipticket' => Seminar_Session::get_ticket()]),
                    $this->declineURL($course_id, ['cmd' => 'back', 'studipticket' => Seminar_Session::get_ticket()])
                );
                $this->redirect('my_courses/index');
                return;
            } else {
                if (!LockRules::Check($course_id, 'participants') && $ticket_check && Request::option('cmd') != 'back' && Request::get('cmd') != 'kill_admission') {
                    $query     = "DELETE FROM seminar_user WHERE user_id = ? AND Seminar_id = ?";
                    $statement = DBManager::get()->prepare($query);
                    $statement->execute([$GLOBALS['user']->id, $course_id]);
                    if ($statement->rowCount() == 0) {
                        PageLayout::postError(
                            _('In der ausgewählten Veranstaltung wurde die gesuchten Personen nicht gefunden und konnte daher nicht ausgetragen werden.')
                        );
                    } else {
                        // LOGGING
                        StudipLog::log('SEM_USER_DEL', $course_id, $GLOBALS['user']->id, 'Hat sich selbst ausgetragen');
                        // enable others to do something after the user has been deleted
                        NotificationCenter::postNotification('UserDidLeaveCourse', $course_id, $GLOBALS['user']->id);
    
                        // Delete course related datafield entries
                        DatafieldEntryModel::deleteBySQL('range_id = ? AND sec_range_id = ?', [$GLOBALS['user']->id, $course_id]);
    
                        // Delete from statusgroups
                        foreach (Statusgruppen::findBySeminar_id($course_id) as $group) {
                            $group->removeUser($GLOBALS['user']->id, true);
                        }
    
                        // Are successor available
                        update_admission($course_id);
    
                        // If this course is a child of another course...
                        if ($current_seminar->parent_course) {
                            // ... check if user is member in another sibling ...
                            $other = CourseMember::findBySQL(
                                "`user_id` = :user AND `Seminar_id` IN (:courses) AND `Seminar_id` != :this",
                                [
                                    'user' => $GLOBALS['user']->id,
                                    'courses' => $current_seminar->parent->children->pluck('seminar_id'),
                                    'this' => $course_id
                                ]
                            );
    
                            // ... and delete from parent course if this was the only
                            // course membership in this family.
                            if (count($other) == 0) {
                                $m = CourseMember::find([$current_seminar->parent_course, $GLOBALS['user']->id]);
                                if ($m) {
                                    $m->delete();
                                }
                            }
                        }
    
                        PageLayout::postSuccess(sprintf(
                            _("Erfolgreich von Veranstaltung <b>%s</b> abgemeldet."),
                            htmlReady($current_seminar->name)
                        ));
                    }
                } else {
                    // LOGGING
                    StudipLog::log('SEM_USER_DEL', $course_id, $GLOBALS['user']->id, 'Hat sich selbst aus der Warteliste ausgetragen');
                    if ($current_seminar->isAdmissionEnabled()) {
                        $prio_delete = AdmissionPriority::unsetPriority($current_seminar->getCourseSet()->getId(), $GLOBALS['user']->id, $course_id);
                    }
                    $query     = "DELETE FROM admission_seminar_user WHERE user_id = ? AND seminar_id = ?";
                    $statement = DBManager::get()->prepare($query);
                    $statement->execute([$GLOBALS['user']->id,
                        $course_id]);
                    NotificationCenter::postNotification('UserDidLeaveWaitingList', $course_id, $GLOBALS['user']->id);
                    if ($statement->rowCount() || $prio_delete) {
                        //Warteliste neu sortieren
                        renumber_admission($course_id);
                        //Pruefen, ob es Nachruecker gibt
                        update_admission($course_id);
                        PageLayout::postSuccess(sprintf(
                            _("Der Eintrag in der Anmelde- bzw. Warteliste der Veranstaltung <b>%s</b> wurde aufgehoben. Wenn Sie an der Veranstaltung teilnehmen wollen, müssen Sie sich erneut bewerben."),
                            htmlReady($current_seminar->name)
                        ));
                    }
                }
                $this->redirect('my_courses/index');
            }
        }
    
    
        /**
         * Overview for achived courses
         */
        public function archive_action()
        {
            if ($GLOBALS['perm']->have_perm('admin')) {
                throw new AccessDeniedException();
            }
    
            PageLayout::setTitle(_('Meine archivierten Veranstaltungen'));
            PageLayout::setHelpKeyword('Basis.MeinArchiv');
            Navigation::activateItem('/browse/my_courses/archive');
    
            if (Config::get()->ENABLE_ARCHIVE_SEARCH) {
                $actions = Sidebar::get()->addWidget(new ActionsWidget());
                $actions->addLink(
                    _('Suche im Archiv'),
                    URLHelper::getURL('dispatch.php/search/archive'),
                    Icon::create('search')
                );
            }
    
            $sortby = Request::option('sortby', 'name');
    
            $query = "SELECT semester, name, seminar_id, status, archiv_file_id,
                             LENGTH(forumdump) > 0 AS forumdump, # Test for existence
                             LENGTH(wikidump) > 0 AS wikidump    # Test for existence
                      FROM archiv_user
                      LEFT JOIN archiv USING (seminar_id)
                      WHERE user_id = :user_id
                      GROUP BY seminar_id
                      ORDER BY start_time DESC, :sortby";
            $statement = DBManager::get()->prepare($query);
            $statement->bindValue(':user_id', $GLOBALS['user']->id);
            $statement->bindValue(':sortby', $sortby, StudipPDO::PARAM_COLUMN);
            $statement->execute();
            $this->seminars = $statement->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_ASSOC); // Groups by semester
        }
    
        /**
         * Checks the whole course selection deppending on grouping eneabled or not
         * @param $my_obj
         * @param string $group_field
         * @return bool
         */
        public function check_for_new($my_obj, $group_field = 'sem_number')
        {
            if (empty($my_obj)) {
                return false;
            }
    
            foreach ($my_obj as $courses) {
    
                if (is_array($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) {
                        if ($this->check_course($course)) {
                            return true;
                        }
                    }
                }
            }
    
            return false;
        }
    
    
    
        /**
         * Set the selected semester and redirects to index
         * @param null $sem
         */
        public function set_semester_action()
        {
            $sem = Request::option('sem_select', null);
            if (!is_null($sem)) {
                $GLOBALS['user']->cfg->store('MY_COURSES_SELECTED_CYCLE', $sem);
                PageLayout::postSuccess(
                    _('Das gewünschte Semester bzw. die gewünschte Semester-Filteroption wurde ausgewählt!')
                );
            }
    
            $this->redirect('my_courses/index');
        }
    
        /**
         * Checks the selected courses for news (e.g. forum posts,...)
         * Returns true if something new happens and enables the reset function
         * @param $seminar_content
         * @return bool
         */
        public function check_course($seminar_content)
        {
            $last_modified_timestamp = $seminar_content['last_modified'] ?? 0;
            if ($seminar_content['visitdate'] <= $seminar_content['chdate'] || $last_modified_timestamp > 0) {
                $last_modified = $seminar_content['visitdate'] <= $seminar_content['chdate']
                && $seminar_content['chdate'] > $last_modified_timestamp
                    ? $seminar_content['chdate']
                    : $last_modified_timestamp;
                if ($last_modified) {
                    return true;
                }
            }
    
            $plugins_navigation = $seminar_content['navigation'];
    
            foreach ($plugins_navigation as $navigation) {
                if ($navigation && $navigation->isVisible(true) && $navigation->hasBadgeNumber()) {
                    return true;
                }
            }
    
            return false;
        }
    
        /**
         * Remove yourself as default deputy of the given boss.
         * @param $boss_id
         */
        public function delete_boss_action($boss_id)
        {
            $deputy = Deputy::find([$boss_id, $GLOBALS['user']->id]);
            $boss = $deputy->boss;
            if ($deputy && $deputy->delete()) {
                PageLayout::postSuccess(sprintf(
                    _('Sie wurden als Standardvertretung von %s entfernt.'),
                    htmlReady($boss->getFullname())
                ));
            } else {
                PageLayout::postError(sprintf(
                    _('Sie konnten nicht als Standardvertretung von %s entfernt werden.'),
                    htmlReady($boss->getFullname())
                ));
            }
            $this->redirect($this->url_for('my_courses'));
        }
    
        /**
         * Get widget for grouping selected courses (e.g. by colors, ...)
         *
         * @param string $group_field
         */
        private function setGroupingSelector($group_field)
        {
            $groups  = [
                'sem_number'  => _('Standard'),
                'sem_tree_id' => _('Studienbereich'),
                'sem_status'  => _('Typ'),
                'gruppe'      => _('Farbgruppen'),
                'dozent_id'   => _('Lehrende'),
            ];
            $views = Sidebar::get()->addWidget(new ViewsWidget());
            $views->setTitle(_('Gruppierung'));
            foreach ($groups as $key => $group) {
                $views->addLink(
                    $group,
                    $this->url_for('my_courses/store_groups', ['select_group_field' => $key])
                )->setActive($key === $group_field);
            }
        }
    
        /**
         * Returns a widget for semester selection
         * @param $sem
         */
        private function setSemesterWidget($sem)
        {
            $semesters = new SimpleCollection(Semester::getAll());
            $semesters = $semesters->orderBy('beginn desc');
    
            $sidebar = Sidebar::Get();
    
            $widget = new SelectWidget(_('Semesterfilter'), $this->url_for('my_courses/set_semester'), 'sem_select');
            $widget->setMaxLength(50);
            $widget->addElement(new SelectElement('current', _('Aktuelles Semester'), $sem === 'current'));
            $widget->addElement(new SelectElement('future', _('Aktuelles und nächstes Semester'), $sem === 'future'));
            $widget->addElement(new SelectElement('last', _('Aktuelles und letztes Semester'), $sem === 'last'));
            $widget->addElement(new SelectElement('lastandnext', _('Letztes, aktuelles, nächstes Semester'), $sem === 'lastandnext'));
            if (Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS) {
                $widget->addElement(new SelectElement('all', _('Alle Semester'), $sem === 'all'));
            }
    
            $query = "SELECT semester_data.semester_id
                      FROM seminare
                      LEFT JOIN semester_courses ON (semester_courses.course_id = seminare.Seminar_id)
                      LEFT JOIN semester_data ON (semester_courses.semester_id = semester_data.semester_id)
                      LEFT JOIN seminar_user USING (Seminar_id)
                      WHERE seminar_user.user_id = ? AND seminare.status NOT IN (?)
                      GROUP BY semester_data.semester_id";
            $statement = DBManager::get()->prepare($query);
            $statement->execute([
                $GLOBALS['user']->id,
                studygroup_sem_types(),
            ]);
            $courses = $statement->fetchAll(PDO::FETCH_COLUMN);
    
            if (!empty($semesters)) {
                $group = new SelectGroupElement(_('Semester auswählen'));
                foreach ($semesters as $semester) {
                    if ($semester->visible || in_array($semester->id,$courses)) {
                        $group->addElement(new SelectElement($semester->id, $semester->name, $sem === $semester->id));
                    }
                }
                $widget->addElement($group);
            }
            $sidebar->addWidget($widget);
        }
    
        protected function setupSidebar($sem, $group_field, $new_contents)
        {
            // Get permission that allows creating courses
            $sem_create_perm = Config::get()->SEM_CREATE_PERM;
            if (!in_array($sem_create_perm, ['root', 'admin', 'dozent'])) {
                $sem_create_perm = 'dozent';
            }
    
            // create settings url depended on selected cycle
            if (isset($sem) && !in_array($sem, words('future all last current'))) {
                $this->settings_url = "dispatch.php/my_courses/groups/{$sem}";
            } else {
                $this->settings_url = 'dispatch.php/my_courses/groups';
            }
    
            $sidebar = Sidebar::get();
            $this->setSemesterWidget($sem);
    
            $setting_widget = $sidebar->addWidget(new ActionsWidget());
    
            if ($new_contents) {
                $setting_widget->addLink(
                    _('Alles als gelesen markieren'),
                    $this->url_for("my_courses/tabularasa/{$sem}/" . time()),
                    Icon::create('accept')
                );
            }
            $setting_widget->addLink(
                _('Farbgruppierung ändern'),
                URLHelper::getURL($this->settings_url),
                Icon::create('group4')
            )->asDialog();
    
            if (Config::get()->MAIL_NOTIFICATION_ENABLE) {
                $setting_widget->addLink(
                    _('Benachrichtigungen anpassen'),
                    URLHelper::getURL('dispatch.php/settings/notification'),
                    Icon::create('mail')
                );
            }
    
            if ($sem_create_perm === 'dozent' && $GLOBALS['perm']->have_perm('dozent')) {
                $setting_widget->addLink(
                    _('Neue Veranstaltung anlegen'),
                    URLHelper::getURL('dispatch.php/course/wizard'),
                    Icon::create('add')
                )->asDialog();
            }
    
            $setting_widget->addLink(
                _('Veranstaltung hinzufügen'),
                URLHelper::getURL('dispatch.php/search/courses'),
                Icon::create('search')
            );
            if (Config::get()->STUDYGROUPS_ENABLE) {
                $setting_widget->addLink(
                    _('Neue Studiengruppe anlegen'),
                    URLHelper::getURL('dispatch.php/course/wizard', ['studygroup' => 1]),
                    Icon::create('studygroup')
                )->asDialog();
            }
    
            $this->setGroupingSelector($group_field);
    
            $views = $sidebar->addWidget(new ViewsWidget());
            $views->id = 'tiled-courses-sidebar-switch';
            $views->addLink(
                _('Tabellarische Ansicht'),
                '#tabular'
            )->setActive(!$GLOBALS['user']->cfg->MY_COURSES_TILED_DISPLAY);
            $views->addLink(
                _('Kachelansicht'),
                '#tiles'
            )->setActive($GLOBALS['user']->cfg->MY_COURSES_TILED_DISPLAY);
    
            $options = $sidebar->addWidget(new OptionsWidget());
            $options->id = 'tiled-courses-new-contents-toggle';
            $options->addCheckbox(
                _('Nur neue Inhalte anzeigen'),
                $GLOBALS['user']->cfg->MY_COURSES_SHOW_NEW_ICONS_ONLY,
                '#'
            );
    
            $export_widget = $sidebar->addWidget(new ExportWidget());
            $export_widget->addLink(
                _('Veranstaltungsübersicht'),
                $this->url_for('my_courses/courseexport', ['modules' => '1']),
                Icon::create('export')
            );
            $export_widget->addLink(
                _('Veranstaltungsübersicht ohne Module'),
                $this->url_for('my_courses/courseexport'),
                Icon::create('export')
            );
        }
    
        private function convertCourse($course)
        {
            $is_teacher = in_array($course['user_status'], ['tutor', 'dozent']);
    
            $avatar = $course['sem_class']['studygroup_mode']
                    ? StudygroupAvatar::getAvatar($course['seminar_id'])
                    : CourseAvatar::getAvatar($course['seminar_id']);
    
            $extra_navigation = false;
            if ($is_teacher) {
                $adminmodule = $course['sem_class']->getAdminModuleObject();
                if ($adminmodule) {
                    $adminnavigation = $adminmodule->getIconNavigation($course['seminar_id'], 0, $GLOBALS['user']->id);
                    $extra_navigation = [
                        'url'   => URLHelper::getURL($adminnavigation->getURL(), ['cid' => $course['seminar_id']]),
                        'icon'  => $adminnavigation->getImage()->getShape(),
                        'label' => $adminnavigation->getLinkAttributes()['title'] ?? _('Verwaltung'),
                    ];
                }
            }
    
            return [
                'id'                => (string) $course['seminar_id'],
                'name'              => (string) $course['name'],
                'number'            => (string) $course['veranstaltungsnummer'],
                'group'             => (int) $course['gruppe'],
                'admission_binding' => (bool) $course['admission_binding'],
                'children'          => array_column($course['children'] ?? [], 'seminar_id'),
                'parent'            => $course['parent_course'] ?? null,
    
                'is_teacher'    => in_array($course['user_status'], ['tutor', 'dozent']),
                'is_studygroup' => (bool) $course['sem_class']['studygroup_mode'],
                'is_hidden'     => !$course['visible'],
                'is_deputy'     => (bool) $course['is_deputy'],
                'is_group'      => (bool) $course['is_group'],
    
                'avatar' => $avatar->getURL(Avatar::MEDIUM),
    
                'navigation'       => $this->reduceNavigation($course['navigation']),
                'extra_navigation' => $extra_navigation,
            ];
        }
    
        private function reduceNavigation($nav)
        {
            if (!$nav) {
                return [];
            }
    
            $result = [];
            foreach (MyRealmModel::array_rtrim($nav) as $key => $n) {
                if (!$n || !$n->isVisible(true)) {
                    $item = false;
                } else {
                    $attr = $n->getLinkAttributes();
                    if (empty($attr['title']) && $n->getImage()) {
                        $attr['title'] = (string) ($n->getImage()->getAttributes()['title'] ?? '');
                    }
                    if (empty($attr['title'])) {
                        $attr['title'] = (string) $n->getTitle();
                    }
                    $attr['title'] = (string) $attr['title'];
    
                    $item = [
                        'url'       => $n->getURL(),
                        'icon'      => $this->convertIcon($n->getImage()),
                        'attr'      => $attr,
                        'important' => $n->getImage()->signalsAttention(),
                    ];
                }
                $result[$key] = $item;
            }
    
            return $result;
        }
    
        private function sanitizeNavigations(array $courses)
        {
            // Count occurences of slots
            $counters = [];
            foreach ($courses as $course) {
                foreach ($course['navigation'] as $key => $value) {
                    if (!isset($counters[$key])) {
                        $counters[$key] = 0;
                    }
                    if ($value) {
                        $counters[$key] += 1;
                    }
                }
            }
    
            // Detect which slots are not set at all
            $remove = array_keys(array_filter($counters, function ($counter) {
                return !$counter;
            }));
    
            // Set positions by predefined positions without the always empty slots
            $positions = array_diff(array_keys(MyRealmModel::getDefaultModules()), $remove);
    
            // Get other positions based on count
            arsort($counters);
            foreach ($counters as $key => $count) {
                if ($count && !in_array($key, $positions)) {
                    $positions[] = $key;
                }
            }
    
            // Sort and filter course navigations
            return array_map(
                function ($course) use ($positions) {
                    $course['navigation'] = array_filter($course['navigation'], function ($key) use ($positions) {
                        return in_array($key, $positions);
                    }, ARRAY_FILTER_USE_KEY);
                    uksort($course['navigation'], function ($a, $b) use ($positions) {
                        return array_search($a, $positions) - array_search($b, $positions);
                    });
                    $course['navigation'] = array_values($course['navigation']);
                    return $course;
                },
                $courses
            );
        }
    
        private function convertIcon(Icon $icon)
        {
            return [
                'role' => $icon->getRole(),
                'shape' => $icon->getShape(),
            ];
        }
    
        private function getSemesterKey()
        {
            $config_sem = $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE;
            if (!Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS && $config_sem === 'all') {
                $config_sem = 'future';
            }
    
            $sem = Request::get(
                'sem_select',
                $config_sem ?: Config::get()->MY_COURSES_DEFAULT_CYCLE
            );
    
            if ($sem && !in_array($sem, ['future', 'all', 'last', 'current'])) {
                Request::set('sem_select', $sem);
            }
    
            return $sem;
        }
    
        private function getGroupField()
        {
            $group_field = $GLOBALS['user']->cfg->MY_COURSES_GROUPING;
    
            $forced_grouping = in_array(Config::get()->MY_COURSES_FORCE_GROUPING, getValidGroupingFields())
                             ? Config::get()->MY_COURSES_FORCE_GROUPING
                             : 'sem_number';
    
            if ($forced_grouping === 'not_grouped') {
                $forced_grouping = 'sem_number';
            }
    
            if (!$group_field || !in_array($group_field, getValidGroupingFields())) {
                $group_field = 'sem_number';
            }
    
            if ($group_field === 'sem_number' && $forced_grouping !== 'sem_number') {
                $group_field = $forced_grouping;
            }
    
            return $group_field === 'not_grouped' ? 'sem_number' : $group_field;
        }
    }