From 1201e3d0f93420f76dcd917211121f2d8b79d05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Noack?= <noack@data-quest.de> Date: Fri, 18 Oct 2024 11:18:49 +0000 Subject: [PATCH] Resolve #3697 "Geplante Teilnehmendenzahl als Pflichtangabe bei der Anlage von LV" Closes #3697 Merge request studip/studip!2566 --- app/controllers/admin/sem_classes.php | 1 + app/views/admin/sem_classes/details.php | 6 ++++- .../course/wizard/steps/basicdata/index.php | 6 +++++ .../6.0.23_tic3967_turnout_mandatory.php | 25 +++++++++++++++++++ lib/classes/SemClass.php | 2 ++ .../coursewizardsteps/BasicDataWizardStep.php | 14 ++++++++++- .../javascripts/bootstrap/course_wizard.js | 9 +++++++ .../assets/javascripts/lib/admin_sem_class.js | 3 ++- 8 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 db/migrations/6.0.23_tic3967_turnout_mandatory.php diff --git a/app/controllers/admin/sem_classes.php b/app/controllers/admin/sem_classes.php index 7993c4b308d..3dc9f269b46 100644 --- a/app/controllers/admin/sem_classes.php +++ b/app/controllers/admin/sem_classes.php @@ -126,6 +126,7 @@ class Admin_SemClassesController extends AuthenticatedController $sem_class->set('show_raumzeit', Request::int("show_raumzeit")); $sem_class->set('is_group', Request::int("is_group")); $sem_class->set('unlimited_forbidden', Request::bool('unlimited_forbidden')); + $sem_class->set('admission_turnout_mandatory', Request::bool('admission_turnout_mandatory')); $sem_class->store(); foreach (array_keys($sem_class->getModules()) as $module_name) { if ($sem_class->isModuleMandatory($module_name) && !$old_data_sem_class->isModuleMandatory($module_name)) { diff --git a/app/views/admin/sem_classes/details.php b/app/views/admin/sem_classes/details.php index 54f4f903353..530f553a511 100644 --- a/app/views/admin/sem_classes/details.php +++ b/app/views/admin/sem_classes/details.php @@ -205,11 +205,15 @@ <?= _('Unbegrenzte Laufzeit verbieten') ?> </label> + <label> + <input type="checkbox" id="admission_turnout_mandatory" value="1" <?= $sem_class['admission_turnout_mandatory'] ? 'checked' : '' ?>> + <?= _('Geplante Teilnehmendenzahl muss angegeben werden') ?> + </label> + <label> <?= _('Kurzer Beschreibungstext zum Anlegen einer Veranstaltung') ?> <textarea id="create_description" maxlength="200" style="width: 100%"><?= htmlReady($sem_class['create_description']) ?></textarea> </label> - </fieldset> <fieldset class="attribute_table"> diff --git a/app/views/course/wizard/steps/basicdata/index.php b/app/views/course/wizard/steps/basicdata/index.php index 372d587821d..1ec8a37888f 100644 --- a/app/views/course/wizard/steps/basicdata/index.php +++ b/app/views/course/wizard/steps/basicdata/index.php @@ -56,6 +56,12 @@ <input type="text" name="number" id="wizard-number" size="20" maxlength="99" value="<?= htmlReady($values['number'] ?? '') ?>" <? if ($course_number_format) : ?>pattern="<?= htmlReady($course_number_format) ?>" <? endif ?>/> </section> +<section data-mandatory="<?=htmlready(json_encode($admission_turnout_mandatory_types))?>" <?=in_array($values['coursetype'],$admission_turnout_mandatory_types) ? '' : 'style="display: none"' ?>> + <label for="wizard-maxmembers" class="required"> + <?= _('max. Teilnehmendenzahl') ?> + </label> + <input type="number" name="maxmembers" id="wizard-maxmember" min="0" value="<?= htmlReady($values['maxmembers'] ?? '') ?>"/> +</section> <section> <label for="wizard-description"> <?= _('Beschreibung') ?> diff --git a/db/migrations/6.0.23_tic3967_turnout_mandatory.php b/db/migrations/6.0.23_tic3967_turnout_mandatory.php new file mode 100644 index 00000000000..be1bfff551a --- /dev/null +++ b/db/migrations/6.0.23_tic3967_turnout_mandatory.php @@ -0,0 +1,25 @@ +<?php + +final class Tic3967TurnoutMandatory extends Migration +{ + public function description() + { + return 'adds option to make admission turnout mandatory'; + } + + public function up() + { + DBManager::get()->exec(" + ALTER TABLE `sem_classes` ADD `admission_turnout_mandatory` TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER `is_group` + "); + $cache = StudipCacheFactory::getCache(); + $cache->expire('DB_SEM_CLASSES_ARRAY'); + } + + public function down() + { + DBManager::get()->exec("ALTER TABLE `sem_classes` DROP `admission_turnout_mandatory`"); + $cache = StudipCacheFactory::getCache(); + $cache->expire('DB_SEM_CLASSES_ARRAY'); + } +} diff --git a/lib/classes/SemClass.php b/lib/classes/SemClass.php index 2a038e20787..e541d8c371f 100644 --- a/lib/classes/SemClass.php +++ b/lib/classes/SemClass.php @@ -386,6 +386,7 @@ class SemClass implements ArrayAccess "show_raumzeit = :show_raumzeit, " . "is_group = :is_group, " . "unlimited_forbidden = :unlimited_forbidden, " . + "admission_turnout_mandatory = :admission_turnout_mandatory," . "chdate = UNIX_TIMESTAMP() " . "WHERE id = :id ". ""); @@ -430,6 +431,7 @@ class SemClass implements ArrayAccess 'show_raumzeit' => (int) $this->data['show_raumzeit'], 'is_group' => (int) $this->data['is_group'], 'unlimited_forbidden' => (int) $this->data['unlimited_forbidden'], + 'admission_turnout_mandatory' => (int) $this->data['admission_turnout_mandatory'], ]); } diff --git a/lib/classes/coursewizardsteps/BasicDataWizardStep.php b/lib/classes/coursewizardsteps/BasicDataWizardStep.php index 53c8c5026ec..022f9338488 100644 --- a/lib/classes/coursewizardsteps/BasicDataWizardStep.php +++ b/lib/classes/coursewizardsteps/BasicDataWizardStep.php @@ -48,6 +48,7 @@ class BasicDataWizardStep implements CourseWizardStep $values = $values[__CLASS__] ?? []; // Get all available course types and their categories. $typestruct = []; + $admission_turnout_mandatory_types = []; foreach (SemType::getTypes() as $type) { $class = $type->getClass(); // Creates a studygroup. @@ -62,10 +63,14 @@ class BasicDataWizardStep implements CourseWizardStep } else { if (!$class['course_creation_forbidden'] && !$class['studygroup_mode']) { $typestruct[$class['name']][] = $type; + if ($class['admission_turnout_mandatory']) { + $admission_turnout_mandatory_types[] = $type['id']; + } } } } $tpl->set_attribute('types', $typestruct); + $tpl->set_attribute('admission_turnout_mandatory_types', $admission_turnout_mandatory_types); // Select a default type if none is given. if (empty($values['coursetype'])) { if ($GLOBALS['user']->cfg->MY_COURSES_TYPE_FILTER && Request::isXhr()) { @@ -376,6 +381,11 @@ class BasicDataWizardStep implements CourseWizardStep $errors[] = _('Sie müssen die Nutzungsbedingungen akzeptieren.'); } } + $sem_type = new SemType((int)$values['coursetype']); + $sem_class = $sem_type->getClass(); + if ($sem_class['admission_turnout_mandatory'] && !$values['maxmembers']) { + $errors[] = _('Sie müssen die maximale Teilnehmendenzahl angeben'); + } if ($errors) { $ok = false; PageLayout::postError(_('Bitte beheben Sie erst folgende Fehler, bevor Sie fortfahren:'), $errors); @@ -422,6 +432,7 @@ class BasicDataWizardStep implements CourseWizardStep $course->admission_prelim = $semclass['admission_prelim_default']; $course->lesezugriff = $semclass['default_read_level'] ?: 1; $course->schreibzugriff = $semclass['default_write_level'] ?: 1; + $course->admission_turnout = $values['maxmembers']; // Studygroups: access and description. if (in_array($values['coursetype'], studygroup_sem_types())) { @@ -523,7 +534,8 @@ class BasicDataWizardStep implements CourseWizardStep 'institute' => $course->institut_id, 'description' => $course->beschreibung, 'description_i18n' => is_object($course->beschreibung) ? - $course->beschreibung->toArray() : $course->beschreibung + $course->beschreibung->toArray() : $course->beschreibung, + 'maxmembers' => $course->getSemClass()->offsetGet('admission_turnout_mandatory') ? $course->admission_turnout : '', ]; $lecturers = $course->members->findBy('status', 'dozent')->pluck('user_id'); $data['lecturers'] = array_flip($lecturers); diff --git a/resources/assets/javascripts/bootstrap/course_wizard.js b/resources/assets/javascripts/bootstrap/course_wizard.js index ef85e96661f..a053af50f0e 100644 --- a/resources/assets/javascripts/bootstrap/course_wizard.js +++ b/resources/assets/javascripts/bootstrap/course_wizard.js @@ -11,4 +11,13 @@ STUDIP.ready(function() { $(this).closest('section,footer').css('order') ); }); + $('#wizard-coursetype').on('change', function() { + let semtype = $(this).val(); + let mandatory_types = $('#wizard-maxmember').parent('section').data('mandatory'); + if (mandatory_types.includes(semtype)) { + $('#wizard-maxmember').parent('section').show(); + } else { + $('#wizard-maxmember').parent('section').hide(); + } + }); }); diff --git a/resources/assets/javascripts/lib/admin_sem_class.js b/resources/assets/javascripts/lib/admin_sem_class.js index 5ac95324681..10f698c636b 100644 --- a/resources/assets/javascripts/lib/admin_sem_class.js +++ b/resources/assets/javascripts/lib/admin_sem_class.js @@ -66,7 +66,8 @@ const admin_sem_class = { admission_type_default: jQuery('#admission_type_default').val(), show_raumzeit: jQuery('#show_raumzeit').is(':checked') ? 1 : 0, is_group: jQuery('#is_group').is(':checked') ? 1 : 0, - unlimited_forbidden: jQuery('#unlimited_forbidden').is(':checked') ? 1 : 0 + unlimited_forbidden: jQuery('#unlimited_forbidden').is(':checked') ? 1 : 0, + admission_turnout_mandatory: jQuery('#admission_turnout_mandatory').is(':checked') ? 1 : 0 }, type: 'POST', dataType: 'json', -- GitLab