Skip to content
Snippets Groups Projects
Select Git revision
  • ba187680db11fc21bd8ed095492c94d06e17ef9b
  • main default protected
  • studip-rector
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • pipeline-improvements
  • webpack-optimizations
  • rector
  • icon-renewal
  • http-client-and-factories
  • jsonapi-atomic-operations
  • vueify-messages
  • tic-2341
  • 135-translatable-study-areas
  • extensible-sorm-action-parameters
  • sorm-configuration-trait
  • jsonapi-mvv-routes
  • docblocks-for-magic-methods
19 results

courseset.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.
    courseset.php 35.49 KiB
    <?php
    
    /**
     * CoursesetController - Course sets
     *
     * 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      Thomas Hackl <thomas.hackl@uni-passau.de>
     * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
     * @category    Stud.IP
     * @since       3.0
     */
    
    class Admission_CoursesetController extends AuthenticatedController
    {
        /**
         * Things to do before every page load.
         */
        public function before_filter(&$action, &$args)
        {
            parent::before_filter($action, $args);
    
            PageLayout::setTitle(_('Anmeldesets'));
            // Get only own courses if user doesn't have permission to edit institute-wide coursesets.
            $this->onlyOwnCourses = true;
            if ($GLOBALS['perm']->have_perm('admin') || ($GLOBALS['perm']->have_perm('dozent') && Config::get()->ALLOW_DOZENT_COURSESET_ADMIN)) {
                // We have access to institute-wide course sets, so all courses may be assigned.
                $this->onlyOwnCourses = false;
                Navigation::activateItem('/browse/coursesets/sets');
            } else {
                throw new AccessDeniedException();
            }
    
            PageLayout::addScript('studip-admission.js');
    
            $views = new ActionsWidget();
            $views->addLink(
                _('Anmeldeset anlegen'),
                $this->configureURL(),
                Icon::create('add')
            )->setActive($action === 'configure');
            Sidebar::Get()->addWidget($views);
    
            if (!isset($this->instant_course_set_view)) {
                $this->instant_course_set_view = false;
            }
        }
    
        /**
         * Show all coursesets the current user has access to.
         */
        public function index_action()
        {
            $this->course_set_details = Request::option('course_set_details');
            if ($this->course_set_details && Request::isXhr()) {
                $courseset = new CourseSet($this->course_set_details);
                $this->render_text($courseset->toString());
                return;
            }
            $this->ruleTypes = AdmissionRule::getAvailableAdmissionRules(false);
            $this->coursesets = [];
            foreach (words('current_institut_id current_rule_types set_name_prefix current_semester_id current_rule_types') as $param) {
                $this->$param = $_SESSION[self::class][$param] ?? null;
            }
            if (Request::submitted('choose_institut')) {
                $this->current_institut_id = Request::option('choose_institut_id');
                $this->current_rule_types = Request::getArray('choose_rule_type');
                $this->set_name_prefix = trim(Request::get('set_name_prefix'));
                $this->current_semester_id = Request::option('select_semester_id');
            }
            if ($this->current_semester_id === null) {
                $this->current_semester_id = $_SESSION['_default_sem'];
            } else if ($this->current_semester_id !== '0') {
                $_SESSION['_default_sem'] = $this->current_semester_id;
            }
            if (!isset($this->current_rule_types)) {
                $this->current_rule_types = [
                    'ParticipantRestrictedAdmission' => true,
                ];
            }
            $filter['course_set_name'] = $this->set_name_prefix;
            $filter['semester_id'] = $this->current_semester_id != 'all' ? $this->current_semester_id : null;
            $filter['rule_types'] = array_keys($this->current_rule_types);
            $this->myInstitutes = CoursesetModel::getInstitutes($filter);
            if (!$this->current_institut_id) {
                if (!isset($this->myInstitutes['all']['count']) || $this->myInstitutes['all']['count'] < 100) {
                    $this->current_institut_id = 'all';
                } else {
                    list($this->current_institut_id) = reset($this->myInstitutes);
                }
            }
            $chunks = explode('_', $this->current_institut_id);
            $institut_id = $chunks[0];
            $all = $chunks[1] ?? null;
            if ($institut_id == 'all') {
                $institutes = array_keys($this->myInstitutes);
            } else if ($all == 'all') {
                $institutes[] = $institut_id;
                $institutes = array_merge($institutes, Institute::find($institut_id)->sub_institutes->pluck('institut_id'));
            } else {
                $institutes = [$institut_id];
            }
    
            foreach ($institutes as $one) {
                if ($this->myInstitutes[$one]['count']) {
                    $sets = CourseSet::getCoursesetsByInstituteId($one, $filter);
                    foreach ($sets as $set) {
                        $courseset = new CourseSet($set['set_id']);
                        $this->coursesets[$set['set_id']] = $courseset;
                    }
                }
            }
            uasort($this->coursesets, function($a,$b) {
                return strnatcasecmp($a->getName(), $b->getName());
            });
    
            if (!isset($_SESSION[self::class])) {
                $_SESSION[self::class] = [];
            }
            foreach (words('current_institut_id current_rule_types set_name_prefix current_semester_id current_rule_types') as $param) {
                $_SESSION[self::class][$param] = $this->$param;
            }
            $not_distributed_coursesets = array_filter(array_map(function ($cs) {
                    return ($cs->isSeatDistributionEnabled() && $cs->getSeatDistributionTime() < (time() - 1000) && !$cs->hasAlgorithmRun())
                            ? $cs->getName()
                            : null;
            }, $this->coursesets));
            if (count($not_distributed_coursesets)) {
                PageLayout::postInfo(
                    _('"Es existieren Anmeldesets, die zum Zeitpunkt der Platzverteilung nicht gelost wurden. Stellen Sie sicher, dass der Cronjob "Losverfahren überprüfen" ausgeführt wird."'),
                    array_unique($not_distributed_coursesets));
            }
        }
    
        /**
         * Configure a new or existing course set.
         */
        public function configure_action($coursesetId = '')
        {
            $this->courseset = null;
            $allCourses = [];
            if ($GLOBALS['perm']->have_perm('root')) {
                if ($coursesetId) {
                    // Load course set data.
                    $this->courseset = new CourseSet($coursesetId);
                    $this->myInstitutes = [];
                    $selectedInstitutes = $this->courseset->getInstituteIds();
                    foreach ($selectedInstitutes as $id => $selected) {
                        $this->myInstitutes[$id] = new Institute($id);
                    }
                    $this->selectedInstitutes = $this->myInstitutes;
                    $selectedCourses = $this->courseset->getCourses();
                    if (!$this->instant_course_set_view) {
                        $allCourses = CoursesetModel::getInstCourses(array_keys($this->selectedInstitutes), $coursesetId, [], $this->courseset->getSemester());
                        $this->selectedSemester = $this->courseset->getSemester();
                    }
                } else {
                    $this->myInstitutes = [];
                    $this->selectedInstitutes = [];
                    $selectedCourses = [];
                    $this->selectedSemester = $_SESSION['_default_sem'] ?: Semester::findCurrent()->semester_id;
                }
                Config::get()->AJAX_AUTOCOMPLETE_DISABLED = false;
                $this->instSearch = QuickSearch::get('institute_id', new StandardSearch('Institut_id'))
                    ->withoutButton()
                    ->render();
            } else {
                $this->myInstitutes = [];
                $myInstitutes = Institute::getMyInstitutes();
                foreach ($myInstitutes as $institute) {
                    $this->myInstitutes[$institute['Institut_id']] = $institute;
                }
                if ($coursesetId) {
                    // Load course set data.
                    $this->courseset = new CourseSet($coursesetId);
                    $selectedInstitutes = $this->courseset->getInstituteIds();
                    $this->selectedInstitutes = [];
                    foreach ($selectedInstitutes as $id => $selected) {
                        $this->selectedInstitutes[$id] = new Institute($id);
                    }
                    $selectedCourses = $this->courseset->getCourses();
                    if (!$this->instant_course_set_view) {
                        $allCourses = CoursesetModel::getInstCourses(array_keys($this->selectedInstitutes), $coursesetId, [], $this->courseset->getSemester(), $this->onlyOwnCourses);
                        $this->selectedSemester = $this->courseset->getSemester();
                    }
                } else {
                    $this->selectedSemester = $_SESSION['_default_sem'] ?: Semester::findCurrent()->semester_id;
                    $this->selectedInstitutes = $this->myInstitutes;
                    $allCourses = CoursesetModel::getInstCourses(array_keys($this->myInstitutes), $coursesetId, [], $this->selectedSemester, $this->onlyOwnCourses);
                    $selectedCourses = [];
                }
            }
            // If an institute search has been conducted, we need to consider parameters from flash.
            if ($this->flash['name'] || $this->flash['institutes'] || $this->flash['courses'] ||
                    $this->flash['rules'] || $this->flash['userlists'] || $this->flash['infotext'] ||
                    $this->flash['semester']) {
                if (!$this->courseset) {
                    $this->courseset = new CourseSet($coursesetId);
                }
                if ($this->flash['name']) {
                    $this->courseset->setName($this->flash['name']);
                }
                if ($this->flash['institutes']) {
                    $institutes = $this->flash['institutes'];
                    $this->courseset->setInstitutes($institutes);
                    if ($GLOBALS['perm']->have_perm('root')) {
                        $this->myInstitutes = [];
                        foreach ($institutes as $id) {
                            $this->myInstitutes[$id] = new Institute($id);
                            $this->selectedInstitutes[$id] = $this->myInstitutes[$id];
                        }
                    }
                    $selectedCourses = $this->courseset->getCourses();
                    $allCourses = CoursesetModel::getInstCourses(array_flip($institutes), $coursesetId, $selectedCourses, $this->selectedSemester, $this->onlyOwnCourses);
                }
                if ($this->flash['courses']) {
                    $courses = $this->flash['courses'];
                    $this->courseset->setCourses($courses);
                    $selectedCourses = $courses;
                }
                if ($this->flash['rules']) {
                    $this->courseset->setAdmissionRules($this->flash['rules']);
                }
                if ($this->flash['userlists']) {
                    $this->courseset->setUserlists($this->flash['userlists']);
                }
                if ($this->flash['infotext']) {
                    $this->courseset->setInfoText($this->flash['infotext']);
                }
                if ($this->flash['private']) {
                    $this->courseset->setPrivate($this->flash['private']);
                }
                if ($this->flash['semester']) {
                    $this->selectedSemester = $this->flash['semester'];
                }
            }
            // Fetch all lists with special user chances.
            $this->myUserlists = AdmissionUserList::getUserLists($GLOBALS['user']->id);
            $fac = $this->get_template_factory();
            $tpl = $fac->open('admission/courseset/instcourses');
            $tpl->set_attribute('allCourses', $allCourses);
            $tpl->set_attribute('selectedCourses', $selectedCourses);
            $this->coursesTpl = $tpl->render();
            $tpl = $fac->open('admission/courseset/institutes');
            if ($coursesetId) {
                $tpl->set_attribute('courseset', $this->courseset);
            }
            $tpl->set_attribute('instSearch', $this->instSearch);
            $tpl->set_attribute('selectedInstitutes', $this->selectedInstitutes);
            $tpl->set_attribute('myInstitutes', $this->myInstitutes);
            $tpl->set_attribute('controller', $this);
            if ($GLOBALS['perm']->have_perm('admin') || ($GLOBALS['perm']->have_perm('dozent') && Config::get()->ALLOW_DOZENT_COURSESET_ADMIN)) {
                $tpl->set_attribute('rights', true);
            } else {
                $tpl->set_attribute('rights', false);
            }
            $this->instTpl = $tpl->render();
        }
    
        /**
         * Saves the given course set to database.
         *
         * @param String $coursesetId the course set to save or empty if it is a
         * new course set
         */
        public function save_action($coursesetId = '')
        {
            if (!Request::submitted('submit') || !Request::get('name') || !$this->instant_course_set_view && !Request::getArray('institutes')) {
                $this->flash['name'] = Request::get('name');
                $this->flash['institutes'] = Request::getArray('institutes');
                $this->flash['courses'] = Request::getArray('courses');
                $this->flash['rules'] = Request::getArray('rules');
                $this->flash['userlists'] = Request::getArray('userlists');
                $this->flash['infotext'] = Request::get('infotext');
                $this->flash['private'] = (bool) Request::get('private');
                $this->flash['semester'] = Request::option('semester');
                if (Request::submitted('add_institute')) {
                    $this->flash['institutes'] = array_merge($this->flash['institutes'], [Request::option('institute_id')]);
                } else {
                    $this->flash['institute_id'] = Request::get('institute_id');
                    $this->flash['institute_id_parameter'] = Request::get('institute_id_parameter');
                }
                if (!Request::submitted('add_institute') && !Request::option('name')) {
                    $this->flash['error'] = _('Bitte geben Sie einen Namen für das Anmeldeset an!');
                }
                if (!Request::submitted('add_institute') && !Request::getArray('institutes')) {
                    $this->flash['error'] = _('Bitte geben Sie mindestens eine Einrichtung an, zu der das Anmeldeset gehört!');
                }
                $this->redirect($this->url_for('admission/courseset/configure', $coursesetId));
            } else {
                $courseset = new CourseSet($coursesetId);
                if (!$courseset->getUserId()) {
                    $courseset->setUserId($GLOBALS['user']->id);
                }
                $courseset->setName(Request::get('name'));
                $courseset->setInstitutes(Request::getArray('institutes'));
                if (Request::option('semester')) {
                    $courseset->setCourses(Request::getArray('courses'));
                }
                $courseset->setUserLists(Request::getArray('userlists'));
                if (!$this->instant_course_set_view && $courseset->isUserAllowedToEdit($GLOBALS['user']->id)) {
                    $courseset->setPrivate((bool) Request::get('private'));
                }
                $courseset->setInfoText(Request::get('infotext'));
                $courseset->clearAdmissionRules();
                foreach (Request::getManyObjects('rules', 'AdmissionRule') as $rule) {
                    $courseset->addAdmissionRule($rule);
                }
                $courseset->store();
                if (Request::option('semester')) {
                    $_SESSION['_default_sem'] = Request::option('semester');
                }
                PageLayout::postSuccess(sprintf(_('Das Anmeldeset: %s wurde gespeichert'), htmlReady($courseset->getName())));
                if ($this->instant_course_set_view) {
                    $this->redirect($this->url_for('course/admission'));
                } else {
                    $this->redirect($this->url_for('admission/courseset/configure', $courseset->getId()));
                }
            }
        }
    
        /**
         * Deletes the given course set.
         *
         * @param String $coursesetId the course set to delete
         */
        public function delete_action($coursesetId)
        {
            $this->courseset = new CourseSet($coursesetId);
    
            if (!$this->courseset->isUserAllowedToEdit(User::findCurrent()->id)) {
                throw new AccessDeniedException(_('Sie dürfen diese Anmelderegel nicht löschen.'));
            }
    
            if (Request::bool('really')) {
                $this->courseset->delete();
            }
    
            $this->redirect($this->url_for('admission/courseset'));
        }
    
        /**
         * Fetches courses at institutes specified by a given course set, filtered by a
         * given semester.
         *
         * @param String $coursesetId The courseset to fetch institute assignments
         * from
         * @see CoursesetModel::getInstCourses
         */
        public function instcourses_action($coursesetId = '')
        {
            CSRFProtection::verifyUnsafeRequest();
            $this->selectedCourses = [];
            //autoload
            if ($coursesetId && !Request::getArray('courses')) {
                $courseset = new CourseSet($coursesetId);
                $this->selectedCourses = $courseset->getCourses();
            } else if (Request::getArray('courses')) {
                $this->selectedCourses = Request::getArray('courses');
            }
            $this->allCourses = CoursesetModel::getInstCourses(Request::getArray('institutes'),
                $coursesetId, $this->selectedCourses, Request::option('semester'), $this->onlyOwnCourses ?: Request::get('course_filter'));
        }
    
        /**
         * Fetches available institutes for the current user.
         */
        public function institutes_action()
        {
            CSRFProtection::verifyUnsafeRequest();
            $this->myInstitutes = Institute::getMyInstitutes();
            $this->selectedInstitutes = [];
            foreach(Request::getArray('institutes') as $institute) {
                $this->selectedInstitutes[$institute] = new Institute($institute);
            }
            Config::get()->AJAX_AUTOCOMPLETE_DISABLED = false;
            $this->instSearch = QuickSearch::get('institute_id', new StandardSearch('Institut_id'))
                ->withOutButton()
                ->render();
        }
    
        /**
         * Configure settings for several courses at once.
         *
         * @param String $set_id course set ID to fetch courses from
         * @param String $csv    export course members to file
         */
        public function configure_courses_action($set_id, $csv = null)
        {
            PageLayout::setTitle(_('Ausgewählte Veranstaltungen konfigurieren'));
    
            $courseset = new CourseSet($set_id);
            $this->set_id = $courseset->getId();
            $this->courses = Course::findMany($courseset->getCourses(), "ORDER BY VeranstaltungsNummer, Name");
            $this->applications = AdmissionPriority::getPrioritiesStats($courseset->getId());
    
            $this->participant_restriction = $courseset->hasAdmissionRule('ParticipantRestrictedAdmission');
    
            $distinct_members = [];
            $multi_members = [];
            foreach($this->courses as $course) {
                $all_members = $course->members->findBy('status', words('user autor'))->pluck('user_id');
                $all_members = array_merge($all_members, $course->admission_applicants->findBy('status', words('accepted awaiting'))->pluck('user_id'));
                $all_members = array_unique($all_members);
                foreach ($all_members as $one) {
                    if (!isset($multi_members[$one])) {
                        $multi_members[$one] = 0;
                    }
                    $multi_members[$one]++;
                }
                $distinct_members = array_unique(array_merge($distinct_members, $all_members));
            }
    
            $multi_members = array_filter($multi_members, function($a) {return $a > 1;});
            $this->count_distinct_members = count($distinct_members);
            $this->count_multi_members = count($multi_members);
    
            if ($csv === 'csv') {
                $captions = [
                    _('Nummer'), _('Name'), _('versteckt'), _('Zeiten'), _('Lehrende'),
                    _('max. Teilnehmende'), _('Teilnehmende aktuell'), _('Anzahl Anmeldungen'),
                    _('Anzahl Anmeldungen Prio 1'), _('Warteliste'), _('max. Anzahl Warteliste'),
                    _('automatisches Nachrücken aus der Warteliste') , _('vorläufige Anmeldung'),
                    _('verbindliche Anmeldung'),
                ];
                $data = [];
                foreach ($this->courses as $course) {
                    $row = [];
                    $row[] = $course->veranstaltungsnummer;
                    $row[] = $course->name;
                    $row[] = $course->visible ? _("nein") : _("ja");
                    $row[] = join('; ', $course->cycles->toString());
                    $row[] = join(', ', $course->members->findBy('status','dozent')->orderBy('position')->pluck('Nachname'));
                    $row[] = $course->admission_turnout;
                    $row[] = $course->getNumParticipants();
                    $row[] = $this->applications[$course->id]['c'];
                    $row[] = $this->applications[$course->id]['h'];
                    $row[] = $course->admission_disable_waitlist ? _('nein') : _('ja');
                    $row[] = $course->admission_waitlist_max > 0 ? $course->admission_waitlist_max : '';
                    $row[] = $course->admission_disable_waitlist_move ? _('nein') : _('ja');
                    $row[] = $course->admission_prelim ? _('ja') : _('nein');
                    $row[] = $course->admission_binding ? _('ja') : _('nein');
    
                    $data[] = $row;
                }
                $tmpname = md5(uniqid('tmp'));
                if (array_to_csv($data, $GLOBALS['TMP_PATH'].'/'.$tmpname, $captions)) {
                    $this->redirect(
                        FileManager::getDownloadURLForTemporaryFile(
                            $tmpname,
                            'Veranstaltungen_' . $courseset->getName() . '.csv'
                        )
                    );
                    return;
                }
            }
            if (in_array($csv, words('download_all_members download_multi_members'))) {
                $liste = [];
                $multi_members = $all_participants = [];
                foreach($this->courses as $course) {
                    $participants = $course->members->findBy('status', words('user autor'))->toGroupedArray('user_id', words('username vorname nachname email status'));
                    $participants += $course->admission_applicants->findBy('status', words('accepted awaiting'))->toGroupedArray('user_id', words('username vorname nachname email status'));
                    $all_participants += $participants;
                    foreach (array_keys($participants) as $one) {
                        $multi_members[$one][] = $course->name . ($course->veranstaltungsnummer ? '|'. $course->veranstaltungsnummer : '');
                    }
                    foreach ($participants as $part) {
                        $liste[] = [$part['username'], $part['vorname'], $part['nachname'], $part['email'], $course->name . ($course->veranstaltungsnummer ? '|'. $course->veranstaltungsnummer : '') , $part['status']];
                    }
                }
                if ($csv == 'download_all_members') {
                    $captions = [_('Username'), _('Vorname'), _('Nachname'), _('E-Mail'), _('Veranstaltung'), _('Status')];
                    if (count($liste)) {
                        $tmpname = md5(uniqid('tmp'));
                        if (array_to_csv($liste, $GLOBALS['TMP_PATH'].'/'.$tmpname, $captions)) {
                            $this->redirect(
                                FileManager::getDownloadURLForTemporaryFile(
                                    $tmpname,
                                    'Gesamtteilnehmendenliste_' . $courseset->getName() . '.csv'
                                )
                            );
                            return;
                        }
                    }
                } else {
                    $liste = [];
                    $multi_members = array_filter($multi_members, function ($a) {return count($a) > 1;});
                    $c = 0;
                    $max_count = [];
                    foreach ($multi_members as $user_id => $courses) {
                        $member = $all_participants[$user_id];
                        $liste[$c] = [$member['username'], $member['vorname'], $member['nachname'], $member['email']];
                        foreach ($courses as  $one) {
                            $liste[$c][] = $one;
                        }
                        $max_count[] = count($courses);
                        $c++;
                    }
                    $captions = [_('Nutzername'), _('Vorname'), _('Nachname'), _('E-Mail')];
                    foreach (range(1,max($max_count)) as $num) {
                        $captions[] = _('Veranstaltung') . ' ' . $num;
                    }
                if (count($liste)) {
                        $tmpname = md5(uniqid('tmp'));
                        if (array_to_csv($liste, $GLOBALS['TMP_PATH'].'/'.$tmpname, $captions)) {
                            $this->redirect(
                                FileManager::getDownloadURLForTemporaryFile(
                                    $tmpname,
                                    'Mehrfachanmeldungen_' . $courseset->getName() . '.csv'
                                )
                            );
                            return;
                        }
                    }
                }
            }
    
            if (Request::submitted('configure_courses_save')) {
                CSRFProtection::verifyUnsafeRequest();
                $admission_turnouts = Request::intArray('configure_courses_turnout');
                $admission_waitlists = Request::intArray('configure_courses_disable_waitlist');
                $admission_waitlists_max = Request::intArray('configure_courses_waitlist_max');
                $admission_disable_waitlist_move = Request::intArray('admission_disable_waitlist_move');
                $admission_bindings = Request::intArray('configure_courses_binding');
                $admission_prelims = Request::intArray('configure_courses_prelim');
                $hidden = Request::intArray('configure_courses_hidden');
                $ok = 0;
                foreach($this->courses as $course) {
                    if ($GLOBALS['perm']->have_studip_perm(Config::get()->ALLOW_DOZENT_COURSESET_ADMIN ? 'dozent' : 'admin', $course->id)) {
                        $do_update_admission = $course->admission_turnout < $admission_turnouts[$course->id];
                        $course->admission_turnout = $admission_turnouts[$course->id];
                        $course->admission_disable_waitlist = isset($admission_waitlists[$course->id]) ? 0 : 1;
                        $course->admission_waitlist_max = $course->admission_disable_waitlist ? 0 : $admission_waitlists_max[$course->id];
                        $course->admission_disable_waitlist_move = isset($admission_disable_waitlist_move[$course->id]) ? 0 : 1;
                        $course->admission_binding = @$admission_bindings[$course->id] ?: 0;
                        $course->admission_prelim = @$admission_prelims[$course->id] ?: 0;
                        $course->visible = @$hidden[$course->id] ? 0 : 1;
    
                        $ok += $course->store();
                        if ($do_update_admission) {
                            AdmissionApplication::addMembers($course->id);
                        }
                    }
                }
                if ($ok) {
                    PageLayout::postSuccess(_('Die zugeordneten Veranstaltungen wurden konfiguriert.'));
                }
                $this->redirect($this->url_for('admission/courseset/configure/' . $courseset->getId()));
                return;
            }
        }
    
        /**
         * Show users who are on an assigned user factor list.
         *
         * @param String $set_id course set to fetch the user lists from
         */
        public function factored_users_action($set_id)
        {
            PageLayout::setTitle(_('Liste der Personen'));
    
            $courseset = new CourseSet($set_id);
            $factored_users = $courseset->getUserFactorList();
            $applicants = AdmissionPriority::getPriorities($set_id);
            $this->users = User::findAndMapMany(function($u) use ($factored_users, $applicants) {
                return array_merge(
                    $u->toArray('username vorname nachname'),
                    ['applicant' => isset($applicants[$u->id]), 'factor' => $factored_users[$u->id]]
                );
            }, array_keys($factored_users), 'ORDER BY Nachname, Vorname');
        }
    
        /**
         * Gets the list of applicants for the courses belonging to this course set.
         *
         * @param String $set_id course set ID
         * @param String $csv    export users to file
         */
        public function applications_list_action($set_id, $csv = null)
        {
            PageLayout::setTitle(_('Liste der Anmeldungen'));
    
            $courseset = new CourseSet($set_id);
            $applicants = AdmissionPriority::getPriorities($set_id);
            $users = User::findMany(array_keys($applicants), 'ORDER BY Nachname, Vorname');
            $courses = SimpleCollection::createFromArray(Course::findMany($courseset->getCourses()));
            $captions = [_('Nachname'), _('Vorname'), _('Nutzername'), _('Veranstaltung'), _('Nummer'), _('Studiengang'), _('Priorität')];
            $data = [];
            $studycourses = function ($st) {
                return sprintf(
                    '%s (%s)',
                    trim($st->studycourse_name . ' ' . $st->degree_name),
                    $st->semester
                );
            };
            foreach ($users as $user) {
                $app_courses = $applicants[$user->id];
                asort($app_courses);
    
                foreach ($app_courses as $course_id => $prio) {
                    $row = [];
                    $row[] = $user->nachname;
                    $row[] = $user->vorname;
                    $row[] = $user->username;
                    $row[] = $courses->findOneBy('id', $course_id)->name;
                    $row[] = $courses->findOneBy('id', $course_id)->veranstaltungsnummer;
                    $row[] = implode('; ', $user->studycourses->map($studycourses));
                    $row[] = $prio;
                    if ($csv) {
                        $row[] = $user->email;
                    }
                    $data[] = $row;
                }
            }
            if ($csv) {
                $tmpname = md5(uniqid('tmp'));
                $captions[] = _("E-Mail");
                if (array_to_csv($data, $GLOBALS['TMP_PATH'].'/'.$tmpname, $captions)) {
                    $this->redirect(
                        FileManager::getDownloadURLForTemporaryFile(
                            $tmpname,
                            'Anmeldungen_' . $courseset->getName() . '.csv'
                        )
                    );
                    return;
                }
            }
            $this->captions = $captions;
            $this->data = $data;
            $this->set_id = $courseset->getId();
        }
    
        public function applicants_message_action($set_id)
        {
            $courseset = new CourseSet($set_id);
            $applicants = AdmissionPriority::getPriorities($set_id);
            $_SESSION['sms_data'] = [];
            $_SESSION['sms_data']['p_rec'] = User::findAndMapMany(function ($u) {
                return $u->username;
            }, array_unique(array_keys($applicants)));
            $this->redirect(URLHelper::getURL('dispatch.php/messages/write',
                ['default_subject' => _("Anmeldung:") . ' ' . $courseset->getName(),
                      'emailrequest'    => 1
                ]
            ));
        }
    
        public function copy_action($set_id)
        {
            $courseset = new CourseSet($set_id);
            $cloned_courseset = clone $courseset;
            $cloned_courseset->setName(_("Kopie von:") . ' ' . $cloned_courseset->getName());
            $cloned_courseset->store();
            foreach ($cloned_courseset->getAdmissionRules() as $id => $rule) {
                if ($rule instanceOf ParticipantRestrictedAdmission) {
                    if ($rule->getDistributionTime() && $rule->getDistributionTime() < time()) {
                        $rule->setDistributionTime(strtotime('+1 month 23:59'));
                        $rule->store();
                        $cloned_courseset->setAlgorithmRun(false);
                        PageLayout::postInfo(sprintf(
                            _('Bitte passen Sie das Datum der automatischen Platzverteilung an, es wurde automatisch auf %s festgelegt!'),
                            strftime('%x %X', $rule->getDistributiontime())
                        ));
                    }
                } else if ($rule->getEndTime() && $rule->getEndTime() < time()) {
                    PageLayout::postInfo(sprintf(
                        _('Der Gültigkeitszeitraum der Regel %s endet in der Vergangenheit!'),
                        htmlReady($rule->getName())
                    ));
                }
            }
            $this->redirect(URLHelper::getURL('dispatch.php/admission/courseset/configure/' .
                $cloned_courseset->getId(), ['is_copy' => 1]));
        }
    
        /**
         * Gets courses fulfilling the given condition.
         *
         * @param String $seminare_condition SQL condition
         */
        public function get_courses($seminare_condition)
        {
            list($institut_id, $all) = explode('_', $this->current_institut_id);
            // Prepare count statements
            $query = "SELECT count(*)
                    FROM seminar_user
                    WHERE seminar_id = ? AND status IN ('user', 'autor')";
            $count0_statement = DBManager::get()->prepare($query);
    
            $query = "SELECT SUM(status = 'accepted') AS count2,
                    SUM(status = 'awaiting') AS count3
                    FROM admission_seminar_user
                    WHERE seminar_id = ?
                    GROUP BY seminar_id";
            $count1_statement = DBManager::get()->prepare($query);
    
            $parameters = [];
    
            $sql = "SELECT seminare.seminar_id,seminare.Name as course_name,seminare.VeranstaltungsNummer as course_number,
                    admission_prelim, admission_turnout,seminar_courseset.set_id
                    FROM seminar_courseset
                    INNER JOIN courseset_rule csr ON csr.set_id=seminar_courseset.set_id AND csr.type='ParticipantRestrictedAdmission'
                    INNER JOIN seminare ON seminar_courseset.seminar_id=seminare.seminar_id
                    ";
            if ($institut_id === 'all'  && $GLOBALS['perm']->have_perm('root')) {
                $sql .= "WHERE 1 {$seminare_condition} ";
            } elseif ($all == 'all') {
                $sql .= "INNER JOIN Institute USING (Institut_id)
                WHERE Institute.fakultaets_id = ? {$seminare_condition}
                ";
                $parameters[] = $institut_id;
            } else {
                $sql .= "WHERE seminare.Institut_id = ? {$seminare_condition}
                ";
                $parameters[] = $institut_id;
            }
            $sql .= "GROUP BY seminare.Seminar_id ORDER BY seminar_courseset.set_id, seminare.Name";
    
            $statement = DBManager::get()->prepare($sql);
            $statement->execute($parameters);
            $ret = [];
            while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $seminar_id = $row['seminar_id'];
                $ret[$seminar_id] = $row;
    
                $count0_statement->execute([$seminar_id]);
                $count = $count0_statement->fetchColumn();
    
                $ret[$seminar_id]['count_teilnehmer']     = $count;
    
                $count1_statement->execute([$seminar_id]);
                $counts = $count1_statement->fetch(PDO::FETCH_ASSOC);
    
                $ret[$seminar_id]['count_prelim'] = (int)$counts['count2'];
                $ret[$seminar_id]['count_waiting']  = (int)$counts['count3'];
                $cs = new CourseSet($row['set_id']);
                $ret[$seminar_id]['cs_name'] = $cs->getName();
                $ret[$seminar_id]['distribution_time'] = $cs->getSeatDistributionTime();
                if ($ta = $cs->getAdmissionRule('TimedAdmission')) {
                    $ret[$seminar_id]['start_time'] = $ta->getStartTime();
                    $ret[$seminar_id]['end_time'] = $ta->getEndTime();
                }
                if (!$cs->hasAlgorithmRun()) {
                    $ret[$seminar_id]['count_claiming'] = $cs->getNumApplicants();
                }
            }
            return $ret;
        }
    
        /**
         * Performs bulk operation on a set of coursesets.
         */
        public function bulk_action()
        {
            if (!Request::isPost()) {
                throw new MethodNotAllowedException();
            }
    
            $ids = Request::optionArray('ids');
            if (Request::submitted('delete')) {
                $deleted = 0;
                foreach ($ids as $id) {
                    $courseset = new CourseSet($id);
                    if ($courseset->isUserAllowedToEdit(User::findCurrent()->id)) {
                        $courseset->delete();
                        $deleted += 1;
                    }
                }
                if ($deleted > 0) {
                    PageLayout::postSuccess(_('Die Anmeldesets wurden gelöscht.'));
                }
            }
    
            $this->redirect('admission/courseset');
        }
    
    }