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 | |
current code from svn, revision 62608
Diffstat (limited to 'lib/classes/coursewizardsteps')
5 files changed, 1675 insertions, 0 deletions
diff --git a/lib/classes/coursewizardsteps/AdvancedBasicDataWizardStep.php b/lib/classes/coursewizardsteps/AdvancedBasicDataWizardStep.php new file mode 100644 index 0000000..1843084 --- /dev/null +++ b/lib/classes/coursewizardsteps/AdvancedBasicDataWizardStep.php @@ -0,0 +1,144 @@ +<?php +/** + * AdvancedBasicDataWizardStep.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> + * @author Stefan Osterloh <s.osterloh@uni-oldenburg.de> + * @copyright 2015 Stud.IP Core-Group + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + */ + +class AdvancedBasicDataWizardStep extends BasicDataWizardStep +{ + /** + * 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) + { + $values = $this->adjustValues($values); + + // We only need our own stored values here. + if ($values[__CLASS__]['studygroup']) { + return parent::getStepTemplate($values, $stepnumber, $temp_id); + } + + // Load template from step template directory. + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'].'/app/views/course/wizard/steps'); + $template = $factory->open('advancedbasicdata/index'); + + if ($template = $this->setupTemplateAttributes($template, $values, $stepnumber, $temp_id)) { + $values = $this->makeI18N($values[__CLASS__], ['name', 'description', 'subtitle', 'kind']); + + $template->set_attribute('values', array_merge($template->values, $values)); + + return $template->render(); + } + } + + /** + * 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) + { + $values = $this->adjustValues($values); + return parent::validate($values); + } + + /** + * 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) + { + $values = $this->adjustValues($values); + $course = parent::storeValues($course, $values); + + // There probably was an error upon storing + if (!$course) { + return false; + } + + // Studygroup? -> nothing to do here + if ($values[__CLASS__]['studygroup']) { + return $course; + } + + // Add advanced data + $course->untertitel = new I18NString($values[__CLASS__]['subtitle'], $values[__CLASS__]['subtitle_i18n']); + $course->art = new I18NString($values[__CLASS__]['kind'], $values[__CLASS__]['kind_i18n']); + $course->ects = $values[__CLASS__]['ects']; + $course->admission_turnout = $values[__CLASS__]['maxmembers']; + if ($course->store() === false) { + PageLayout::postError(sprintf(_('Es ist ein Fehler beim Speichern der erweiterten Einstellungen für %s aufgetreten. Kontrollieren Sie bitte:') + , htmlReady($course->name)), + [_('Untertitel der Veranstalung'), + _('Art der Veranstaltung'), + _('ECTS-Punkte der Veranstaltung'), + _('Max. Teilnehmendenzahl der Veranstaltung')]); + } + return $course; + } + + /** + * This method will adjust the given values from parent class + * or use previously set values from this class. + * + * @param array $values Array of values + * @return array of adjusted values + */ + private function adjustValues($values) + { + $parent_class = get_parent_class($this); + + if (!isset($values[__CLASS__]) && isset($values[$parent_class])) { + $values[__CLASS__] = $values[$parent_class]; + } else { + $values[$parent_class] = $values[__CLASS__]; + } + + return $values; + } + + /** + * Copy values for basic data wizard step from given course. + * @param Course $course + * @param Array $values + */ + public function copy($course, $values) + { + $values = parent::copy($course, $values); + $values = $this->adjustValues($values); + + $values[__CLASS__] = array_merge($values[__CLASS__], [ + 'subtitle' => $course->untertitel, + 'subtitle_i18n' => $course->untertitel->toArray(), + 'kind' => $course->art, + 'kind_i18n' => $course->art->toArray(), + 'ects' => $course->ects, + 'maxmembers' => $course->admission_turnout, + ]); + + return $values; + } +} 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; + } + +} diff --git a/lib/classes/coursewizardsteps/LVGroupsWizardStep.php b/lib/classes/coursewizardsteps/LVGroupsWizardStep.php new file mode 100644 index 0000000..6d5f20c --- /dev/null +++ b/lib/classes/coursewizardsteps/LVGroupsWizardStep.php @@ -0,0 +1,527 @@ +<?php +/** + * LVGroupsWizardStep.php + * Course wizard step for assigning LV Groups. + * + * 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 Timo Hartge <hartge@data-quest.de> + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + */ + +require_once dirname(__FILE__) . '/../StudipLvgruppeSelection.class.php'; + +class LVGroupsWizardStep 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) + { + // retrieve class of step 1 from step registry + $step_one_class = CourseWizardStepRegistry::findOneBySQL('number = 1 AND enabled = 1') + ->classname; + + // store start time of semester selected in first step + $course_start_time = $values[$step_one_class]['start_time']; + + // We only need our own stored values here. + $values = $values[__CLASS__]; + + // Load template from step template directory. + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views/course/wizard/steps'); + $tpl = $factory->open('lvgroups/index'); + $tpl->set_attribute('values', $values); + + $lvgtree = new StudipLvgruppeSelection(); + + $selection = self::get_selection($temp_id); + if (!empty($values['lvgruppe_selection']['areas'])) { + foreach ($values['lvgruppe_selection']['areas'] as $area_id) { + $lvgroup = Lvgruppe::find($area_id); + $selection->add($lvgroup); + } + } + + $selection_details = $values['lvgruppe_selection']['area_details']; + + if ($_SESSION[__CLASS__]['course_start_time'] != $course_start_time) { + // don't store previously opened nodes + // because we get in trouble if the semester has changed + $open_nodes = []; + } else { + $open_nodes = !empty($values['open_lvg_nodes']) ? $values['open_lvg_nodes'] : []; + } + + $_SESSION[__CLASS__]['course_start_time'] = $course_start_time; + + $tpl->set_attribute('open_lvg_nodes', $open_nodes); + $tpl->set_attribute('selection', $selection); + $tpl->set_attribute('selection_details', $selection_details); + $tpl->set_attribute('tree', $lvgtree->getRootItem()->getChildren()); + + $tpl->set_attribute('ajax_url', $values['ajax_url'] ?: URLHelper::getLink('dispatch.php/course/wizard/ajax')); + $tpl->set_attribute('no_js_url', $values['no_js_url'] ?: 'dispatch.php/course/wizard/forward/'.$stepnumber.'/'.$temp_id); + $tpl->set_attribute('stepnumber', $stepnumber); + $tpl->set_attribute('temp_id', $temp_id); + return $tpl->render(); + } + + /** + * Returns a LvGruppen-Selection object for a given course ID. + * + * @param string either the MD5ish ID of a course or something falsy to + * indicate a course that is currently being created + * + * @return mixed LvGruppen-Selection object representing + * the selection form + */ + public function get_selection($course_id) + { + if (self::isCourseId($course_id)) { + $selection = new StudipLvgruppeSelection($course_id); + } else { + $lvgruppen = []; + if (isset($GLOBALS['sem_create_data']) + && isset($GLOBALS['sem_create_data']['sem_lvgruppen'])) { + $lvgruppen = $GLOBALS['sem_create_data']['sem_lvgruppen']; + } + $selection = new StudipLvgruppeSelection(); + $selection->setLvgruppen($lvgruppen); + } + return $selection; + } + + /** + * Every (non-empty) string is a valid course ID except the string '-' + * + * @param mixed the value to check + * @return bool TRUE if it is courseID-ish, FALSE otherwise + */ + public static function isCourseId($id) + { + return is_string($id) && $id !== '' && $id !== '-'; + } + + public function getLVGroupTreeLevel($parentId, $parentClass) + { + $level = []; + $children = []; + $searchtree = []; + + $course = Course::findCurrent(); + if ($course) { + $course_start = $course->start_time; + $course_end = ($course->end_time < 0 || is_null($course->end_time)) ? PHP_INT_MAX : $course->end_time; + } else { + $semester = Semester::findByTimestamp($_SESSION[__CLASS__]['course_start_time']); + $course_start = $semester->beginn; + $course_end = $semester->ende; + } + + $mvvid = explode('-', $parentId); + $mvvobj = $parentClass::find($mvvid[0]); + $children = $mvvobj->getChildren(); + + $i = 1; + foreach ($children as $c) { + + if (isset($c->stat)) { + if ($c->stat != 'genehmigt') { + continue; + } elseif (isset($c->start) || isset($c->end)) { + $mvv_start = Semester::find($c->start); + $mvv_start = $mvv_start ? $mvv_start->beginn : 0; + $mvv_end = Semester::find($c->end); + $mvv_end = $mvv_end ? $mvv_end->ende : PHP_INT_MAX; + + if ($course_end < $mvv_start || $course_start > $mvv_end) { + continue; + } + } + } + + // name of module maybe differs from original module title if it + // is assigned to a Studiengangteilabschnitt + if (is_a($c, 'Modul')) { + $stgteilabschnitt_modul = StgteilabschnittModul::findOneBySql( + '`abschnitt_id` = ? AND `modul_id` = ?', [$mvvid[0], $c->id]); + $name = $stgteilabschnitt_modul->getDisplayName(); + } else { + $name = $c->getDisplayName(); + } + + $level[] = [ + 'id' => $c->id . '-' . $mvvid[1] . $i++, + 'name' => $name, + 'has_children' => $c->hasChildren(), + 'parent' => $c->getTrailParentId(), + 'assignable' => $c->isAssignable(), + 'mvvclass' => get_class($c) + ]; + } + + if (Request::isXhr()) { + return json_encode($level); + } else { + return $level; + } + } + + public function searchLVGroupTree($searchterm) + { + $result = []; + $selection = self::get_selection(Request::get('cid')); + $selectedlvg = []; + if (!empty($selection)) { + $selectedlvg = $selection->getLvGruppenIDs(); + } + + $course = Course::findCurrent(); + if ($course) { + $course_start = $course->start_time; + $course_end = ($course->end_time < 0 || is_null($course->end_time)) ? PHP_INT_MAX : $course->end_time; + } else { + $semester = Semester::findByTimestamp($_SESSION[__CLASS__]['course_start_time']); + $course_start = $semester->beginn; + $course_end = $semester->ende; + } + + $status_modul = []; + foreach ($GLOBALS['MVV_MODUL']['STATUS']['values'] as $name => $status) { + if ($status['public'] && $status['visible']) { + $status_modul[] = $name; + } + } + + $status_version = []; + foreach ($GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'] as $name => $status) { + if ($status['public'] && $status['visible']) { + $status_version[] = $name; + } + } + + $filter = [ + 'mvv_modul.stat' => $status_modul, + 'mvv_stgteilversion.stat' => $status_version, + 'start_sem.beginn' => $course_end, + 'end_sem.ende' => $course_start + ]; + + foreach (Lvgruppe::findBySearchTerm($searchterm, $filter) as $area) { + if (in_array($area->id, $selectedlvg)) { + continue; + } + + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views'); + $html = $factory->render('course/wizard/steps/lvgroups/lvgroup_searchentry', compact('area', 'inlist')); + $data = [ + 'id' => $area->id, + 'html_string' => $html + ]; + $result[] = $data; + } + return json_encode($result); + } + + public function getLVGroupDetails($id) { + $mvvid = explode('-', $id); + $area = Lvgruppe::find($mvvid[0]); + + $course = Course::findCurrent(); + if ($course) { + $course_start = $course->start_time; + $course_end = ($course->end_time < 0 || is_null($course->end_time)) ? PHP_INT_MAX : $course->end_time; + } else { + $semester = Semester::findByTimestamp($_SESSION[__CLASS__]['course_start_time']); + $course_start = $semester->beginn; + $course_end = $semester->ende; + } + + $status_modul = []; + foreach ($GLOBALS['MVV_MODUL']['STATUS']['values'] as $name => $status) { + if ($status['public'] && $status['visible']) { + $status_modul[] = $name; + } + } + + ModuleManagementModelTreeItem::setObjectFilter('Modul', + function ($modul) use ($course_start, $course_end, $status_modul) { + if (!in_array($modul->stat, $status_modul)) { + return false; + } + $modul_start = Semester::find($modul->start)->beginn ?: 0; + $modul_end = Semester::find($modul->end)->ende ?: PHP_INT_MAX; + return ($modul_start <= $course_end && $modul_end >= $course_start); + }); + + $status_version = []; + foreach ($GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'] as $name => $status) { + if ($status['public'] && $status['visible']) { + $status_version[] = $name; + } + } + + ModuleManagementModelTreeItem::setObjectFilter('StgteilVersion', + function ($version) use ($status_version) { + return in_array($version->stat, $status_version); + }); + + $trails = $area->getTrails([ + 'Modulteil', + 'StgteilabschnittModul', + 'StgteilAbschnitt', + 'StgteilVersion', + 'Studiengang']); + $pathes = ModuleManagementModelTreeItem::getPathes($trails); + + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views'); + $html = $factory->render('course/lvgselector/entry_trails', + compact('area', 'pathes')); + + $data = [ + 'id' => $area->id, + 'html_string' => $html + ]; + if (Request::isXhr()) { + return json_encode($data); + } else { + return $data; + } + } + + public function getAncestorTree($id) + { + $mvvid = explode('-', $id); + $area = Lvgruppe::find($mvvid[0]); + + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/app/views'); + $html = $factory->render('course/wizard/steps/lvgroups/lvgroup_entry', compact('area')); + + $data = [ + 'id' => $area->id, + 'html_string' => $html + ]; + return json_encode($data); + } + + + /** + * Catch form submits other than "previous" and "next" and handle the + * given values. This is only important for no-JS situations. + * @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__]; + + if (Request::submitted('open_nodes')) { + $already_open_nodes = unserialize(Request::get('open_nodes')); + foreach ($already_open_nodes as $open_lvgnode) { + $values['open_lvg_nodes'][] = $open_lvgnode; + } + } + + if (Request::option('open_node')) { + $node_to_open = Request::get('open_node'); + if (!in_array($node_to_open, $values['open_lvg_nodes'])) { + $values['open_lvg_nodes'][] = $node_to_open; + } else { + $k = array_search($node_to_open, $values['open_lvg_nodes']); + unset($values['open_lvg_nodes'][$k]); + } + } + + if (Request::submitted('lvgruppe_selection')) { + + $lvgruppe_selection = Request::getArray('lvgruppe_selection'); + + if (isset($lvgruppe_selection['details'])) { + foreach (array_keys($lvgruppe_selection['details']) as $lvgid) { + $detail = $this->getLVGroupDetails($lvgid); + $values['lvgruppe_selection']['area_details'][$detail['id']] = $detail['html_string']; + } + } + + if (isset($lvgruppe_selection['remove'])) { + $new_areas = []; + foreach ($lvgruppe_selection['areas'] as $area) { + if(!key_exists($area, $lvgruppe_selection['remove'])) { + $new_areas[] = $area; + } + } + $values['lvgruppe_selection']['areas'] = $new_areas; + } + } + + if ($assign = array_keys(Request::getArray('assign'))) { + $values['lvgruppe_selection']['areas'][] = $assign[0]; + } + + 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) + { + $ok = true; + $errors = []; + + $coursetype = 1; + foreach ($values as $class) { + if ($class['coursetype']) { + $coursetype = $class['coursetype']; + break; + } + } + $category = SeminarCategories::GetByTypeId($coursetype); + + if ($category->bereiche) { + if (isset($values['StudyAreasLVGroupsCombinedWizardStep']) + && !count($values['StudyAreasLVGroupsCombinedWizardStep']['studyareas']) + && !count($values[__CLASS__]['lvgruppe_selection']['areas']) + && $values[__CLASS__]['step'] > $values['StudyAreasLVGroupsCombinedWizardStep']['step']) { + $ok = false; + $errors[] = _('Die Veranstaltung muss mindestens einem Studienbereich oder einer LV-Gruppe zugeordnet sein.'); + } + } else { + // optional step if study areas step is activated and at least one area is assigned + if (!count($values[__CLASS__]['lvgruppe_selection']['areas'])) { + $ok = false; + $errors[] = _('Der Veranstaltung muss mindestens eine Lehrveranstaltungsgruppe zugeordnet sein.'); + } + } + if ($errors) { + 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) + { + if ($this->is_locked($values)) { + throw new AccessDeniedException(); + } + + // Leave early if no values are set + if (!isset($values[__CLASS__]['lvgruppe_selection']['areas']) + || !is_array($values[__CLASS__]['lvgruppe_selection']['areas'])) + { + return $course; + } + + $selection = new StudipLvgruppeSelection($course->id); + foreach ($values[__CLASS__]['lvgruppe_selection']['areas'] as $lvg_id) { + $area = Lvgruppe::find($lvg_id); + $selection->add($area); + } + LvGruppe::setLvgruppen($course->id, $selection->getLvgruppenIDs()); + + return $course; + } + + /** + * Checks if the current step needs to be executed according + * to already given values. + * + * @param Array $values values specified from previous steps + * @return bool Is the current step required for a new course? + */ + public function isRequired($values) + { + // is locked? + // Set global state in MVV_ACCESS_ASSIGN_LVGRUPPEN + $locked = $this->is_locked($values); + + $coursetype = 1; + foreach ($values as $class) + { + if ($class['coursetype']) + { + $coursetype = $class['coursetype']; + break; + } + } + $category = SeminarCategories::GetByTypeId($coursetype); + + return (!$locked && $category->module); + } + + public function is_locked($values) + { + global $perm; + + // Has user access to this function? Access state is configured in global config. + $access_right = Config::get()->MVV_ACCESS_ASSIGN_LVGRUPPEN; + + // the id of the home institute + // get the institute from the first step (normally "BasicDataWizardStep") + $inst_id = reset($values)['institute']; + if ($access_right == 'fakadmin') { + // is fakadmin at faculty of given home institute + $db = DBManager::get(); + $st = $db->prepare("SELECT a.Institut_id FROM user_inst a + LEFT JOIN Institute b ON (a.Institut_id = b.Institut_id AND b.Institut_id = b.fakultaets_id) + LEFT JOIN Institute c ON (c.Institut_id = b.Institut_id) + WHERE a.user_id = ? AND a.inst_perms='admin' AND NOT ISNULL(b.Institut_id) + AND c.Institut_id = ? LIMIT 1"); + $st->execute([$GLOBALS['user']->id, $inst_id]); + return !((bool) $st->fetchColumn()); + } + return !$perm->have_studip_perm($access_right, $inst_id); + + } + + /** + * Copy values for study areas wizard step from given course. + * @param Course $course + * @param Array $values + */ + public function copy($course, $values) + { + $data = []; + $selection = new StudipLvgruppeSelection($course->id); + foreach ($selection->getAreas() as $a) { + /* + * Check if areas assigned to given course are + * still assignable. + */ + if ($a->isAssignable()) { + $data[] = $a->id; + } + } + + $values[__CLASS__]['lvgruppe_selection']['areas'] = $data; + return $values; + } + +} diff --git a/lib/classes/coursewizardsteps/StudyAreasLVGroupsCombinedWizardStep.php b/lib/classes/coursewizardsteps/StudyAreasLVGroupsCombinedWizardStep.php new file mode 100644 index 0000000..3bb02f5 --- /dev/null +++ b/lib/classes/coursewizardsteps/StudyAreasLVGroupsCombinedWizardStep.php @@ -0,0 +1,103 @@ +<?php +/** + * StudyAreasWizardStep.php + * Course wizard step for assigning study areas. + * + * 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> + * @author Peter Thienel <thienel@data-quest.de> + * @copyright 2015 Stud.IP Core-Group + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + */ + +class StudyAreasLVGroupsCombinedWizardStep extends StudyAreasWizardStep +{ + + /** + * 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) + { + $ok = true; + $errors = []; + + $coursetype = 1; + foreach ($values[get_class($this)] as $class) { + if (!empty($class['coursetype'])) { + $coursetype = $class['coursetype']; + break; + } + } + $category = SeminarCategories::GetByTypeId($coursetype); + if ($category->module) { + if (isset($values['LVGroupsWizardStep']) + && !count($values['LVGroupsWizardStep']['lvgruppe_selection']['areas']) + && !count($values[get_class($this)]['studyareas']) + && $values[get_class($this)]['step'] > $values['LVGroupsWizardStep']['step']) { + $ok = false; + $errors[] = _('Die Veranstaltung muss mindestens einem Studienbereich oder einer LV-Gruppe zugeordnet sein.'); + } + } else { + if (!$values[get_class($this)]['studyareas']) { + $ok = false; + $errors[] = _('Die Veranstaltung muss mindestens einem Studienbereich zugeordnet sein.'); + } + } + if ($errors) { + PageLayout::postMessage(MessageBox::error( + _('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) + { + // get courstype configuration to check + // whether assignment of lv-gruppen is possible + $coursetype = 1; + foreach ($values as $class) { + if (!empty($class['coursetype'])) { + $coursetype = $class['coursetype']; + break; + } + } + $category = SeminarCategories::GetByTypeId($coursetype); + $areas_required = true; + if ($category->module + && $values['LVGroupsWizardStep']['lvgruppe_selection']['areas']) + { + $areas_required = false; + } + if ($areas_required + || ($values[get_class($this)]['studyareas'] && is_array($values[__CLASS__]['studyareas']))) + { + $course->study_areas = SimpleORMapCollection::createFromArray( + StudipStudyArea::findMany($values[get_class($this)]['studyareas']) + ); + if ($course->store()) { + return $course; + } else { + return false; + } + } + return $course; + } + +} diff --git a/lib/classes/coursewizardsteps/StudyAreasWizardStep.php b/lib/classes/coursewizardsteps/StudyAreasWizardStep.php new file mode 100644 index 0000000..054f638 --- /dev/null +++ b/lib/classes/coursewizardsteps/StudyAreasWizardStep.php @@ -0,0 +1,289 @@ +<?php +/** + * StudyAreasWizardStep.php + * Course wizard step for assigning study areas. + * + * 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 StudyAreasWizardStep 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) + { + // We only need our own stored values here. + $values = $values[get_class($this)]; + // Load template from step template directory. + $factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'].'/app/views/course/wizard/steps'); + $tpl = $factory->open('studyareas/index'); + if ($values['studyareas']) + { + $tree = $this->buildPartialSemTree(StudipStudyArea::backwards(StudipStudyArea::findMany($values['studyareas']))); + $tpl->set_attribute('assigned', $tree); + } else { + $tpl->set_attribute('assigned', []); + } + $tpl->set_attribute('values', $values); + + // First tree level is always shown. + $tree = StudipStudyArea::findByParent(StudipStudyArea::ROOT); + + if (count($tree) == 0) { + PageLayout::postError(formatReady(_('Das Anlegen einer ' . + 'Veranstaltung ist nicht möglich, da keine Studienbereiche ' . + 'existieren. Bitte wenden Sie sich an [die ' . + 'Stud.IP-Administration]' . + URLHelper::getLink('dispatch.php/siteinfo/show') . ' .'))); + return false; + } + + /* + * Someone works without JS activated, load all ancestors and + * children of open node. + */ + if ($values['open_node']) { + $tpl->set_attribute('open_nodes', + $this->buildPartialSemTree( + StudipStudyArea::backwards( + StudipStudyArea::findByParent( + $values['open_node'])), true)); + } + /* + * Someone works without JS and has entered a search term: + * build the partial tree with search results. + */ + if ($values['searchterm']) { + $search = $this->searchSemTree($values['searchterm'], true); + if ($search) { + $tpl->set_attribute('open_nodes', $search); + $tpl->set_attribute('search_result', $search); + unset($values['open_node']); + } else { + PageLayout::postMessage(MessageBox::info(_('Es wurde kein Suchergebnis gefunden.'))); + unset($values['searchterm']); + } + } + $tpl->set_attribute('tree', $tree); + $tpl->set_attribute('ajax_url', $values['ajax_url'] ?: URLHelper::getLink('dispatch.php/course/wizard/ajax')); + $tpl->set_attribute('no_js_url', $values['no_js_url'] ? : 'dispatch.php/course/wizard/forward/'.$stepnumber.'/'.$temp_id); + $tpl->set_attribute('stepnumber', $stepnumber); + $tpl->set_attribute('temp_id', $temp_id); + return $tpl->render(); + } + + /** + * Catch form submits other than "previous" and "next" and handle the + * given values. This is only important for no-JS situations. + * @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[get_class($this)]; + // A node has been clicked in order to open the subtree. + if (Request::option('open_node')) { + $values['open_node'] = Request::get('open_node'); + } + // Assign a node to the course. + if ($assign = array_keys(Request::getArray('assign'))) { + if ($values['studyareas']) { + $values['studyareas'][] = $assign[0]; + } else { + $values['studyareas'] = $assign; + } + } + // Unassign an assigned node. + if ($unassign = array_keys(Request::getArray('unassign'))) { + $unassign = $unassign[0]; + // Use array_filter to remove the given entry from assigned nodes. + $values['studyareas'] = array_filter( + $values['studyareas'], + function ($e) use ($unassign) { return $e != $unassign; } + ); + } + // Search for a given term in the semtree. + if (Request::submitted('start_search')) { + $values['searchterm'] = Request::get('search'); + unset($values['open_node']); + } + // Reset search -> normal semtree view. + if (Request::submitted('reset_search')) { + unset($values['searchterm']); + } + 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[get_class($this)]; + $ok = true; + $errors = []; + if (!$values['studyareas']) { + $ok = false; + $errors[] = _('Die Veranstaltung muss mindestens einem Studienbereich zugeordnet sein.'); + } + if ($errors) { + PageLayout::postMessage(MessageBox::error( + _('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. + $values = $values[get_class($this)]; + $course->study_areas = SimpleORMapCollection::createFromArray( + StudipStudyArea::findMany($values['studyareas'])); + if ($course->store()) { + 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) + { + $coursetype = 1; + foreach ($values as $class) + { + if ($class['coursetype']) + { + $coursetype = $class['coursetype']; + break; + } + } + $category = SeminarCategories::GetByTypeId($coursetype); + return $category->bereiche; + } + + /** + * Copy values for study areas wizard step from given course. + * @param Course $course + * @param Array $values + */ + public function copy($course, $values) + { + $data = []; + foreach ($course->study_areas as $a) { + /* + * Check if areas assigned to given course are + * still assignable. + */ + if ($a->isAssignable()) { + $data['studyareas'][] = $a->id; + } + } + $values[get_class($this)] = $data; + return $values; + } + + public function getSemTreeLevel($parentId) + { + $level = []; + $children = StudipStudyArea::findByParent($parentId); + foreach ($children as $c) { + if (!$c->isHidden()) { + $level[] = [ + 'id' => $c->sem_tree_id, + 'name' => (string) $c->getName(), + 'has_children' => $c->hasChildren(), + 'parent' => $parentId, + 'assignable' => $c->isAssignable() + ]; + } + } + if (Request::isXhr()) { + return json_encode($level); + } else { + return $level; + } + } + + public function searchSemTree($searchterm, $id_only=false) + { + $result = []; + $search = StudipStudyArea::search($searchterm); + $search = array_filter($search, function($a) { return !$a->isHidden(); }); + $root = StudipStudyArea::backwards($search); + $result = $this->buildPartialSemTree($root, $id_only); + if ($id_only) { + return $result; + } else { + return json_encode($result); + } + } + + public function getAncestorTree($id) + { + $result = []; + $node = StudipStudyArea::find($id); + $root = StudipStudyArea::backwards([$node]); + $result = $this->buildPartialSemTree($root); + return json_encode($result); + } + + protected function buildPartialSemTree($node, $id_only=false) { + $children = []; + foreach ($node->required_children as $c) + { + if ($id_only) { + $children[] = $c->id; + $children = array_merge($children, $this->buildPartialSemTree($c, $id_only)); + } else { + $data = [ + 'id' => $c->id, + 'name' => (string)$c->name, + 'has_children' => $c->hasChildren(), + 'parent' => $node->id, + 'assignable' => $c->isAssignable(), + 'children' => $this->buildPartialSemTree($c) + ]; + $children[] = $data; + } + } + return $children; + } + +} |
