diff options
| author | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:07:19 +0200 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:19:12 +0200 |
| commit | a3da1483a9e689846179159355badfec8073dbec (patch) | |
| tree | 770dcca6bdf5f6f2a11b0e7fcbbeda6919a3fc52 /lib/classes/coursewizardsteps/BasicDataWizardStep.php | |
current code from svn, revision 62608
Diffstat (limited to 'lib/classes/coursewizardsteps/BasicDataWizardStep.php')
| -rw-r--r-- | lib/classes/coursewizardsteps/BasicDataWizardStep.php | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/lib/classes/coursewizardsteps/BasicDataWizardStep.php b/lib/classes/coursewizardsteps/BasicDataWizardStep.php new file mode 100644 index 0000000..225b8ff --- /dev/null +++ b/lib/classes/coursewizardsteps/BasicDataWizardStep.php @@ -0,0 +1,612 @@ +<?php +/** + * BasicDataWizardStep.php + * Course wizard step for getting the basic course data. + * + * 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> + * @copyright 2015 Stud.IP Core-Group + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + */ + +class BasicDataWizardStep implements CourseWizardStep +{ + /** + * Returns the Flexi template for entering the necessary values + * for this step. + * + * @param Array $values Pre-set values + * @param int $stepnumber which number has the current step in the wizard? + * @param String $temp_id temporary ID for wizard workflow + * @return String a Flexi template for getting needed data. + */ + public function getStepTemplate($values, $stepnumber, $temp_id) + { + // Load template from step template directory. + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views/course/wizard/steps'); + if ($values[__CLASS__]['studygroup']) { + $tpl = $factory->open('basicdata/index_studygroup'); + $values[__CLASS__]['lecturers'][$GLOBALS['user']->id] = 1; + } else { + $tpl = $factory->open('basicdata/index'); + } + if ($this->setupTemplateAttributes($tpl, $values, $stepnumber, $temp_id)) { + return $tpl->render(); + } + } + + protected function setupTemplateAttributes($tpl, $values, $stepnumber, $temp_id) + { + // We only need our own stored values here. + $values = $values[__CLASS__]; + // Get all available course types and their categories. + $typestruct = []; + foreach (SemType::getTypes() as $type) { + $class = $type->getClass(); + // Creates a studygroup. + if ($values['studygroup']) { + // Get all studygroup types. + if ($class['studygroup_mode']) { + $typestruct[$class['name']][] = $type; + } + // Pre-set institute for studygroup assignment. + $values['institute'] = Config::get()->STUDYGROUP_DEFAULT_INST; + // Normal course. + } else { + if (!$class['course_creation_forbidden'] && !$class['studygroup_mode']) { + $typestruct[$class['name']][] = $type; + } + } + } + $tpl->set_attribute('types', $typestruct); + // Select a default type if none is given. + if (!$values['coursetype']) { + if ($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER && Request::isXhr()) { + $values['coursetype'] = $GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER; + } else { + $values['coursetype'] = 1; + } + } + + // Semester selection. + $semesters = []; + $now = time(); + // Allow only current or future semesters for selection. + foreach (Semester::getAll() as $s) { + if ($s->ende >= $now) { + if ($GLOBALS['perm']->have_perm("admin")) { + if ($s->id == $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE && + !$values['start_time'] && Request::isXhr()) { + $values['start_time'] = $s->beginn; + } + } else { + if ((time() >= $s->beginn - Config::get()->SEMESTER_TIME_SWITCH * 86400 * 7) + && (time() < $s->ende - Config::get()->SEMESTER_TIME_SWITCH * 86400 * 7)) { + $values['start_time'] = $s->beginn; + } + } + $semesters[] = $s; + } + } + if ($values['studygroup'] && (!count($typestruct) || !$values['institute']) ) { + $message = sprintf(_('Die Konfiguration der Studiengruppen ist unvollständig. ' . + 'Bitte wenden Sie sich an [die Stud.IP-Administration]%s .'), + URLHelper::getLink('dispatch.php/siteinfo/show') + ); + PageLayout::postError(formatReady($message)); + return false; + } + if (count($semesters) > 0) { + $tpl->set_attribute('semesters', array_reverse($semesters)); + // If no semester is set, use current as selected default. + if (!$values['start_time']) { + $values['start_time'] = Semester::findCurrent()->beginn; + } + } else { + $message = sprintf(_('Veranstaltungen können nur ' . + 'im aktuellen oder in zukünftigen Semestern angelegt werden. ' . + 'Leider wurde kein passendes Semester gefunden. Bitte wenden ' . + 'Sie sich an [die Stud.IP-Administration]%s .'), + URLHelper::getLink('dispatch.php/siteinfo/show') + ); + PageLayout::postError(formatReady($message)); + return false; + } + + // Create a I18NString for course name and description. + $values = $this->makeI18N($values, ['name', 'description']); + + // Get all allowed home institutes (my own). + $institutes = Institute::getMyInstitutes(); + if ($values['studygroup'] || count($institutes) > 0) { + $tpl->set_attribute('institutes', $institutes); + if (!$values['institute']) { + if ($GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT && Request::isXhr()) { + $values['institute'] = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT; + } else { + $values['institute'] = InstituteMember::getDefaultInstituteIdForUser($GLOBALS['user']->id); + + // if for some reason no default institute is set, use the first one listed + if (!$values['institute']) { + $values['institute'] = $institutes[0]['Institut_id']; + } + } + } + } else { + $message = sprintf(_('Um Veranstaltungen ' . + 'anlegen zu können, muss Ihr Account der Einrichtung, ' . + 'für die Sie eine Veranstaltung anlegen möchten, zugeordnet ' . + 'werden. Bitte wenden Sie sich an [die ' . + 'Stud.IP-Administration]%s .'), + URLHelper::getLink('dispatch.php/siteinfo/show') + ); + PageLayout::postError(formatReady($message)); + return false; + } + + // QuickSearch for participating institutes. + // No JS: Keep search value and results for displaying in search select box. + if ($values['part_inst_id']) { + Request::getInstance()->offsetSet('part_inst_id', $values['part_inst_id']); + } + if ($values['part_inst_id_parameter']) { + Request::getInstance()->offsetSet('part_inst_id_parameter', $values['part_inst_id_parameter']); + } + $instsearch = new StandardSearch('Institut_id', + _('Beteiligte Einrichtung hinzufügen'), + 'part_inst_id' + ); + $tpl->set_attribute('instsearch', QuickSearch::get('part_inst_id', $instsearch) + ->withButton(['search_button_name' => 'search_part_inst', 'reset_button_name' => 'reset_instsearch']) + ->fireJSFunctionOnSelect('STUDIP.CourseWizard.addParticipatingInst') + ->render()); + if (!$values['participating']) { + $values['participating'] = []; + } + + // Quicksearch for lecturers. + // No JS: Keep search value and results for displaying in search select box. + if ($values['lecturer_id']) { + Request::getInstance()->offsetSet('lecturer_id', $values['lecturer_id']); + } + if ($values['lecturer_id_parameter']) { + Request::getInstance()->offsetSet('lecturer_id_parameter', $values['lecturer_id_parameter']); + } + + // Check for deputies. + $deputies = Config::get()->DEPUTIES_ENABLE; + /* + * No lecturers set, add yourself so that at least one lecturer is + * present. But this can only be done if your own permission level + * is 'dozent'. + */ + if (!$values['lecturers'] && $GLOBALS['perm']->have_perm('dozent') && !$GLOBALS['perm']->have_perm('admin')) { + $values['lecturers'][$GLOBALS['user']->id] = true; + // Remove from deputies if set. + if ($deputies && $values['deputies'][$GLOBALS['user']->id]) { + unset($values['deputies'][$GLOBALS['user']->id]); + } + // Add your own default deputies if applicable. + if ($deputies && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) { + $values['deputies'] = array_merge($values['deputies'] ?: [], + array_flip(Deputy::findDeputies($GLOBALS['user']->id)->pluck('user_id'))); + } + } + // Add lecturer from my courses filter. + if ($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER && !$values['lecturers'] && Request::isXhr()) { + $values['lecturers'][$GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER] = true; + // Add this lecturer's default deputies if applicable. + if ($deputies && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) { + $values['deputies'] = array_merge($values['deputies'] ?: [], + array_flip(Deputy::findDeputies($GLOBALS['user']->cfg->ADMIN_COURSES_TEACHERFILTER)->pluck('user_id'))); + } + } + if (!$values['lecturers']) { + $values['lecturers'] = []; + } + if ($deputies && !$values['deputies']) { + $values['deputies'] = []; + } + + + + // Quicksearch for deputies if applicable. + if ($deputies) { + // No JS: Keep search value and results for displaying in search select box. + if ($values['deputy_id']) { + Request::getInstance()->offsetSet('deputy_id', $values['deputy_id']); + } + if ($values['deputy_id_parameter']) { + Request::getInstance()->offsetSet('deputy_id_parameter', $values['deputy_id_parameter']); + } + $deputysearch = new PermissionSearch('user', + _('Vertretung hinzufügen'), + 'user_id', + ['permission' => 'dozent', + 'exclude_user' => array_keys($values['deputies'])] + ); + $tpl->set_attribute('dsearch', QuickSearch::get('deputy_id', $deputysearch) + ->withButton(['search_button_name' => 'search_deputy', 'reset_button_name' => 'reset_dsearch']) + ->fireJSFunctionOnSelect('STUDIP.CourseWizard.addDeputy') + ->render()); + } + + if (!$values['tutors']) { + $values['tutors'] = []; + } + + list($lsearch, $tsearch) = array_values($this->getSearch($values['coursetype'], + array_merge([$values['institute']], array_keys($values['participating'])), + array_keys($values['lecturers']), array_keys($values['tutors']))); + // Quicksearch for lecturers. + $tpl->set_attribute('lsearch', $lsearch); + $tpl->set_attribute('tsearch', $tsearch); + $tpl->set_attribute('values', $values); + // AJAX URL needed for default deputy checking. + $tpl->set_attribute('ajax_url', $values['ajax_url'] ?: URLHelper::getLink('dispatch.php/course/wizard/ajax')); + $tpl->set_attribute('default_deputies_enabled', + ($deputies && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) ? 1 : 0); + + return $tpl; + } + + /** + * The function only needs to handle person adding and removing + * as other actions are handled by normal request processing. + * @param Array $values currently set values for the wizard. + * @return bool + */ + public function alterValues($values) + { + // We only need our own stored values here. + $values = $values[__CLASS__]; + + // Add a participating institute. + if (Request::submitted('add_part_inst') && Request::option('part_inst_id')) { + $values['participating'][Request::option('part_inst_id')] = true; + unset($values['part_inst_id']); + unset($values['part_inst_id_parameter']); + } + // Remove a participating institute. + if ($remove = array_keys(Request::getArray('remove_participating'))) { + $remove = $remove[0]; + unset($values['participating'][$remove]); + } + // Add a lecturer. + if (Request::submitted('add_lecturer') && Request::option('lecturer_id')) { + $values['lecturers'][Request::option('lecturer_id')] = true; + unset($values['lecturer_id']); + unset($values['lecturer_id_parameter']); + // Add default deputies if applicable. + if (Config::get()->DEPUTIES_ENABLE && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) { + $values['deputies'] = array_merge($values['deputies'] ?: [], + array_flip(array_keys(Request::option('lecturer_id')))); + } + } + // Remove a lecturer. + if ($remove = array_keys(Request::getArray('remove_lecturer'))) { + $remove = $remove[0]; + unset($values['lecturers'][$remove]); + } + // Add a deputy. + if (Request::submitted('add_deputy')) { + $values['deputies'][Request::option('deputy_id')] = true; + unset($values['deputy_id']); + unset($values['deputy_id_parameter']); + } + // Remove a deputy. + if ($remove = array_keys(Request::getArray('remove_deputy'))) { + $remove = $remove[0]; + unset($values['deputies'][$remove]); + } + // Add a tutor. + if (Request::submitted('add_tutor') && Request::option('tutor_id')) { + $values['tutors'][Request::option('tutor_id')] = true; + unset($values['tutor_id']); + unset($values['tutor_id_parameter']); + } + // Remove a tutor. + if ($remove = array_keys(Request::getArray('remove_tutor'))) { + $remove = $remove[0]; + unset($values['tutors'][$remove]); + } + return $values; + } + + /** + * Validates if given values are sufficient for completing the current + * course wizard step and switch to another one. If not, all errors are + * collected and shown via PageLayout::postMessage. + * + * @param mixed $values Array of stored values + * @return bool Everything ok? + */ + public function validate($values) + { + // We only need our own stored values here. + $values = $values[__CLASS__]; + $ok = true; + $errors = []; + if (!trim($values['name'])) { + $errors[] = _('Bitte geben Sie den Namen der Veranstaltung an.'); + } + if ($values['number'] != '') { + $course_number_format = Config::get()->COURSE_NUMBER_FORMAT; + if ($course_number_format && !preg_match('/^' . $course_number_format . '$/', $values['number'])) { + $errors[] = _('Die Veranstaltungsnummer hat ein ungültiges Format.'); + } + } + if (!$values['lecturers']) { + $errors[] = sprintf( + _('Bitte tragen Sie mindestens eine Person als %s ein.'), + htmlReady(get_title_for_status('dozent', 1, $values['coursetype'])) + ); + } + if (!$values['lecturers'][$GLOBALS['user']->id] && !$GLOBALS['perm']->have_perm('admin')) { + if (Config::get()->DEPUTIES_ENABLE) { + if (!$values['deputies'][$GLOBALS['user']->id]) { + $errors[] = sprintf( + _('Sie selbst müssen entweder als %s oder als Vertretung eingetragen sein.'), + htmlReady(get_title_for_status('dozent', 1, $values['coursetype'])) + ); + } + } else { + $errors[] = sprintf( + _('Sie müssen selbst als %s eingetragen sein.'), + htmlReady(get_title_for_status('dozent', 1, $values['coursetype'])) + ); + } + } + if (in_array($values['coursetype'], studygroup_sem_types())) { + if (!$values['accept']) { + $errors[] = _('Sie müssen die Nutzungsbedingungen akzeptieren.'); + } + } + if ($errors) { + $ok = false; + PageLayout::postError(_('Bitte beheben Sie erst folgende Fehler, bevor Sie fortfahren:'), $errors); + } + return $ok; + } + + /** + * Stores the given values to the given course. + * + * @param Course $course the course to store values for + * @param Array $values values to set + * @return Course The course object with updated values. + */ + public function storeValues($course, $values) + { + // We only need our own stored values here. + if (@$values['copy_basic_data'] === true) { + $source = Course::find($values['source_id']); + } + $values = $values[__CLASS__]; + $seminar = new Seminar($course); + + if (isset($source)) { + $course->setData($source->toArray('untertitel ort sonstiges art teilnehmer vorrausetzungen lernorga leistungsnachweis ects admission_turnout modules')); + foreach ($source->datafields as $one) { + $df = $one->getTypedDatafield(); + if ($df->isEditable()) { + $course->datafields->findOneBy('datafield_id', $one->datafield_id)->content = $one->content; + } + } + } + + $course->status = $values['coursetype']; + $course->start_time = $values['start_time']; + $course->duration_time = 0; + $course->name = new I18NString($values['name'], $values['name_i18n']); + $course->veranstaltungsnummer = $values['number']; + $course->beschreibung = new I18NString($values['description'], $values['description_i18n']); + $course->institut_id = $values['institute']; + + $semclass = $seminar->getSemClass(); + $course->visible = $semclass['visible']; + $course->admission_prelim = $semclass['admission_prelim_default']; + $course->lesezugriff = $semclass['default_read_level'] ?: 1; + $course->schreibzugriff = $semclass['default_write_level'] ?: 1; + + // Studygroups: access and description. + if (in_array($values['coursetype'], studygroup_sem_types())) { + $course->visible = 1; + $course->duration_time = -1; + switch ($values['access']) { + case 'all': + $course->admission_prelim = 0; + break; + case 'invisible': + if (!Config::get()->STUDYGROUPS_INVISIBLE_ALLOWED) { + $course->visible = 0; + } + case 'invite': + $course->admission_prelim = 1; + $course->admission_prelim_txt = Config::get()->STUDYGROUP_ACCEPTANCE_TEXT; + break; + } + } + if ($course->store()) { + StudipLog::log('SEM_CREATE', $course->id, null, 'Veranstaltung mit Assistent angelegt'); + $institutes = [$values['institute']]; + if (isset($values['participating']) && is_array($values['participating'])) { + $institutes = array_merge($institutes, array_keys($values['participating'])); + } + $seminar->setInstitutes($institutes); + $course->setSemesters([ + Semester::findByTimestamp($values['start_time']) + ]); + if (isset($values['lecturers']) && is_array($values['lecturers'])) { + foreach (array_keys($values['lecturers']) as $user_id) { + $seminar->addMember($user_id, 'dozent'); + } + } + if (isset($values['tutors']) && is_array($values['tutors'])) { + foreach (array_keys($values['tutors']) as $user_id) { + $seminar->addMember($user_id, 'tutor'); + } + } + if (Config::get()->DEPUTIES_ENABLE && isset($values['deputies']) && is_array($values['deputies'])) { + foreach ($values['deputies'] as $d => $assigned) { + Deputy::addDeputy($d, $course->id); + } + } + if ($semclass['admission_type_default'] == 3) { + $course_set_id = CourseSet::getGlobalLockedAdmissionSetId(); + CourseSet::addCourseToSet($course_set_id, $course->id); + } + return $course; + } else { + return false; + } + } + + /** + * Checks if the current step needs to be executed according + * to already given values. A good example are study areas which + * are only needed for certain sem_classes. + * + * @param Array $values values specified from previous steps + * @return bool Is the current step required for a new course? + */ + public function isRequired($values) + { + return true; + } + + /** + * Copy values for basic data wizard step from given course. + * @param Course $course + * @param Array $values + */ + public function copy($course, $values) + { + $data = [ + 'coursetype' => $course->status, + 'start_time' => $course->start_time, + 'name' => $course->name, + 'name_i18n' => $course->name->toArray(), + 'number' => $course->veranstaltungsnummer, + 'institute' => $course->institut_id, + 'description' => $course->beschreibung, + 'description_i18n' => $course->beschreibung->toArray() + ]; + $lecturers = $course->members->findBy('status', 'dozent')->pluck('user_id'); + $data['lecturers'] = array_flip($lecturers); + $tutors = $course->members->findBy('status', 'tutor')->pluck('user_id'); + $data['tutors'] = array_flip($tutors); + $participating = $course->institutes->pluck('institut_id'); + $data['participating'] = array_flip($participating); + unset($data['participating'][$course->institut_id]); + if (Config::get()->DEPUTIES_ENABLE) { + $data['deputies'] = array_flip(Deputy::findDeputies($course->id)->pluck('user_id')); + } + $values[__CLASS__] = $data; + return $values; + } + + /** + * Fetches the default deputies for a given person if the necessary + * config options are set. + * @param $user_id user whose default deputies to get + * @return Array Default deputy user_ids. + */ + public function getDefaultDeputies($user_id) + { + if (Config::get()->DEPUTIES_ENABLE && Config::get()->DEPUTIES_DEFAULTENTRY_ENABLE) { + return Deputy::findDeputies($user_id)->map(function($deputy) { + return ['id' => $deputy->user_id, 'name' => $deputy->getDeputyFullname()]; + }); + } else { + return []; + } + } + + public function getSearch($course_type, $institute_ids, $exclude_lecturers = [],$exclude_tutors = []) + { + if (SeminarCategories::getByTypeId($course_type)->only_inst_user) { + $search = 'user_inst'; + } else { + $search = 'user'; + } + $psearch = new PermissionSearch($search, + sprintf(_("%s hinzufügen"), get_title_for_status('dozent', 1, $course_type)), + 'user_id', + __CLASS__ . '::lsearchHelper' + ); + $lsearch = QuickSearch::get('lecturer_id', $psearch) + ->withButton(['search_button_name' => 'search_lecturer', 'reset_button_name' => 'reset_lsearch']) + ->fireJSFunctionOnSelect('STUDIP.CourseWizard.addLecturer') + ->render(); + + $tutor_psearch = new PermissionSearch($search, + sprintf(_("%s hinzufügen"), get_title_for_status('tutor', 1, $course_type)), + 'user_id', + __CLASS__ . '::tsearchHelper' + ); + $tsearch = QuickSearch::get('tutor_id', $tutor_psearch) + ->withButton(['search_button_name' => 'search_tutor', 'reset_button_name' => 'reset_tsearch']) + ->fireJSFunctionOnSelect('STUDIP.CourseWizard.addTutor') + ->render(); + + return compact('lsearch', 'tsearch'); + } + + public static function tsearchHelper($psearch, $context) + { + $ret['permission'] = ['tutor', 'dozent']; + $ret['exclude_user'] = array_keys((array)$context['tutors']); + $ret['institute'] = array_merge([$context['institute']], array_keys((array)$context['participating'])); + return $ret; + } + + public static function lsearchHelper($psearch, $context) + { + $ret['permission'] = 'dozent'; + $ret['exclude_user'] = array_keys((array)$context['lecturers']); + $ret['institute'] = array_merge([$context['institute']], array_keys((array)$context['participating'])); + return $ret; + } + + /** + * Creates I18N strings from the given values at the given indices. + * + * @param array $values this step's set values + * @param array $indices the values to convert to I18NStrings + * + * @return array modified values + */ + protected function makeI18N($values, $indices) + { + /** + * Create array for configured content languages + */ + $translations = array_combine( + array_keys($GLOBALS['CONTENT_LANGUAGES']), + array_fill(0, count($GLOBALS['CONTENT_LANGUAGES']), '') + ); + + foreach ($indices as $index) { + // There are values given => create an I18NString + if ($values[$index]) { + + $values[$index] = new I18NString($values[$index], $values[$index . '_i18n']); + + // Current index is not set (yet), create an empty I18NString + } else { + + $values[$index] = new I18NString('', $translations); + + } + } + + return $values; + } + +} |
