From 5f8c492f51f3e0eda579157312b4ed5f7fa024e1 Mon Sep 17 00:00:00 2001 From: Peter Thienel Date: Fri, 20 Dec 2024 14:20:33 +0000 Subject: =?UTF-8?q?Resolve=20"Sprachauswahl=20f=C3=BCr=20Originalfassung?= =?UTF-8?q?=20der=20Modul(teil)-Deskriptoren",=20fixes=20#4261?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #4261 Merge request studip/studip!3729 --- app/controllers/materialien/files.php | 2 +- app/controllers/module/download.php | 15 +- app/controllers/module/module.php | 412 +++++++++++++++------ app/controllers/search/angebot.php | 2 +- app/controllers/search/studiengaenge.php | 38 +- app/controllers/seminar/details.php | 4 +- app/controllers/shared/download.php | 5 +- app/controllers/shared/modul.php | 118 +++--- app/controllers/studiengaenge/studiengaenge.php | 8 +- app/views/admin/datafields/edit.php | 6 +- app/views/admin/datafields/index.php | 2 +- app/views/admin/datafields/new.php | 4 +- app/views/materialien/files/add_dokument.php | 34 +- app/views/module/institute/details.php | 4 +- app/views/module/module/change_language.php | 36 ++ app/views/module/module/details.php | 19 +- app/views/module/module/index.php | 20 +- app/views/module/module/modul.php | 51 ++- app/views/module/module/module.php | 31 +- app/views/module/module/modulteil.php | 8 +- app/views/module/module/select_module_language.php | 30 ++ app/views/shared/deskriptor_language.php | 26 +- app/views/shared/modul/_modullv.php | 4 +- app/views/shared/modul/description.php | 14 +- app/views/shared/modul/overview.php | 18 +- config/mvv_config.php | 24 -- db/migrations/6.0.37_step_4261.php | 64 ++++ lib/classes/MVV.php | 7 +- lib/classes/globalsearch/GlobalSearchModules.php | 2 +- lib/models/Lvgruppe.php | 5 +- lib/models/Modul.php | 28 +- lib/models/ModulDeskriptor.php | 10 +- lib/models/ModuleManagementModel.php | 49 +-- lib/models/Modulteil.php | 23 +- lib/models/ModulteilDeskriptor.php | 10 +- lib/navigation/MVVNavigation.php | 3 + 36 files changed, 757 insertions(+), 379 deletions(-) create mode 100644 app/views/module/module/change_language.php create mode 100644 app/views/module/module/select_module_language.php create mode 100644 db/migrations/6.0.37_step_4261.php diff --git a/app/controllers/materialien/files.php b/app/controllers/materialien/files.php index 25fd5b2..a8d4823 100644 --- a/app/controllers/materialien/files.php +++ b/app/controllers/materialien/files.php @@ -147,7 +147,7 @@ class Materialien_FilesController extends MVVController CSRFProtection::verifyUnsafeRequest(); $stored = false; - foreach($GLOBALS['MVV_LANGUAGES']['values'] as $key => $entry) { + foreach($GLOBALS['CONTENT_LANGUAGES'] as $key => $entry) { if (Request::get('doc_url_'.$key)) { $file = $this->upload_fileurl($mvvfile_id, Request::get('doc_url_'.$key)); if ($file) { diff --git a/app/controllers/module/download.php b/app/controllers/module/download.php index be527d5..e5f5be8 100644 --- a/app/controllers/module/download.php +++ b/app/controllers/module/download.php @@ -10,14 +10,15 @@ class Module_DownloadController extends MVVController public function details_action($modul_id, $language = null) { - $language = Request::get('display_language', $language); - ModuleManagementModel::setLanguage($language); - $modul = Modul::find($modul_id); if (!$modul) { throw new Exception(_('Ungültiges Modul')); } - $this->getDetails($modul_id, $language); + $language = Request::get('display_language', $language) ?? $modul->original_language; + I18NString::setDefaultLanguage($modul->original_language); + I18NString::setContentLanguage($language); + + $this->getDetails($modul_id); $this->download = true; $as_pdf = Request::int('pdf'); @@ -51,7 +52,7 @@ class Module_DownloadController extends MVVController } } - private function getDetails($id, $language = null) + private function getDetails($id) { $modul = Modul::find($id); if (!$modul) { @@ -81,7 +82,7 @@ class Module_DownloadController extends MVVController $modulTeilData = []; foreach ($modul->modulteile as $modulTeil) { - $deskriptor = $modulTeil->getDeskriptor($language); + $deskriptor = $modulTeil->getDeskriptor(); $num_bezeichnung = $GLOBALS['MVV_MODULTEIL']['NUM_BEZEICHNUNG']['values'][$modulTeil->num_bezeichnung]['name'] ?? ''; $name_kurz = sprintf('%s %d', $num_bezeichnung, $modulTeil->nummer); @@ -134,7 +135,7 @@ class Module_DownloadController extends MVVController $this->semesterSelector = Semester::GetSemesterSelector(null, $currentSemester->getId(), 'semester_id', false); $this->modul = $modul; $this->pruefungsEbene = $GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$modul->pruef_ebene]['name'] ?? null; - $this->modulDeskriptor = $modul->getDeskriptor($language); + $this->modulDeskriptor = $modul->getDeskriptor(); $this->startSemester = Semester::find($modul->start); if ($modul->responsible_institute->institute) { $this->instituteName = $modul->responsible_institute->getDisplayName(); diff --git a/app/controllers/module/module.php b/app/controllers/module/module.php index 10674d0..08597f1 100644 --- a/app/controllers/module/module.php +++ b/app/controllers/module/module.php @@ -15,7 +15,10 @@ class Module_ModuleController extends MVVController { parent::before_filter($action, $args); // set navigation - Navigation::activateItem($this->me . '/module/module'); + Navigation::getItem('mvv/module/module') + ->setActive(static::class === Module_ModuleController::class); + Navigation::getItem('mvv/module/institutes') + ->setActive(static::class === Module_InstituteController::class); $this->filter = $this->sessGet('filter', []); $this->action = $action; $this->modul_id = ''; @@ -105,6 +108,14 @@ class Module_ModuleController extends MVVController $this->setSidebar(); } + public function select_module_language_action() + { + $this->content_languages = $GLOBALS['CONTENT_LANGUAGES']; + $this->default_language = Config::get()->MVV_DEFAULT_LANGUAGE; + PageLayout::setTitle(_('Ausgabeprache wählen')); + $this->render_template('module/module/select_module_language'); + } + public function modul_action($modul_id = null, $institut_id = null) { $own_institutes = MvvPerm::getOwnInstitutes(); @@ -133,13 +144,20 @@ class Module_ModuleController extends MVVController if ($this->modul->isNew()) { PageLayout::setTitle(_('Neues Modul anlegen')); $success_message = ('Das Modul "%s" wurde angelegt.'); - $this->display_language = $this->modul->getDefaultLanguage(); - $this->deskriptor = $this->modul->getDeskriptor($this->display_language, true); + $language = Request::option('display_language'); + $content_languages = $GLOBALS['CONTENT_LANGUAGES']; + if (!empty($content_languages[$language])) { + $this->display_language = $language; + } else { + $this->display_language = Config::get()->MVV_DEFAULT_LANGUAGE; + } + $this->modul->original_language = $this->display_language; + $this->deskriptor = $this->modul->getDeskriptor(); $this->reset_search('Modul'); if (!$modul_id) { PageLayout::postInfo(sprintf( - _('Sie legen ein neues Modul an. Das Modul muss zunächst in der Ausgabesprache %s angelegt werden.'), - $GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['name'] + _('Sie legen ein neues Modul an. Das Modul wird zunächst in der Ausgabesprache %s angelegt (Originalsprache).'), + $GLOBALS['CONTENT_LANGUAGES'][$this->display_language]['name'] )); } // set default language of instruction @@ -151,39 +169,37 @@ class Module_ModuleController extends MVVController } else { $this->display_language = Request::option( 'display_language', - $this->modul->getDefaultLanguage() + $this->modul->original_language ); - $this->deskriptor = $this->modul->getDeskriptor($this->display_language, true); - $this->translations = $this->deskriptor->getAvailableTranslations(); + $this->deskriptor = $this->modul->getDeskriptor(); + $this->translations = $this->deskriptor->getAvailableTranslations($this->modul->original_language); + $content_languages = $GLOBALS['CONTENT_LANGUAGES']; if (!in_array($this->display_language, $this->translations)) { - PageLayout::setTitle( + PageLayout::postInfo( sprintf( - _('Modul: %s in der Ausgabesprache %s neu anlegen.'), + _('Modul: %1$s in der Ausgabesprache %2$s neu anlegen.'), $this->modul->getDisplayName(), - $GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['name'] + $content_languages[$this->display_language]['name'] ) ); - } else { - PageLayout::setTitle(sprintf( - _('Modul: %s bearbeiten'), $this->modul->getDisplayName() - )); } $success_message = _('Das Modul "%s" wurde geändert.'); - // language selector as sidebar widget - $template_factory = $this->get_template_factory(); - $sidebar_template = $template_factory->render('shared/deskriptor_language', [ - 'modul' => $this->modul, - 'sprache' => $this->display_language, - 'link' => $this->modulURL($this->modul->id, $this->institut_id), - 'url' => $this->url] - ); - - $widget = new SidebarWidget(); - $widget->setTitle(_('Ausgabesprache')); - $widget->addElement(new WidgetElement($sidebar_template)); - $sidebar->addWidget($widget, 'language'); + $views_widget = new ViewsWidget(); + $views_widget->setTitle(_('Ausgabesprache')); + $languages = $this->translations; + $content_languages = array_merge(array_flip($languages), $GLOBALS['CONTENT_LANGUAGES']); + foreach ($content_languages as $code => $language) { + $views_widget->addLink($language['name'] + . ' (' . ($code === $this->modul->original_language ? _('Originalfassung') . ', ' : '') + . (in_array($code, $languages) ? _('bearbeiten') : _('neu anlegen')) . ')', + URLHelper::getLink($this->modulURL($this->modul->id, $this->institut_id), + ['display_language' => $code] + ) + )->setActive($code === $this->display_language); + } + $sidebar->insertWidget($views_widget, 'actions','language'); $action_widget = $sidebar->getWidget('actions'); $action_widget->addLink( @@ -223,21 +239,22 @@ class Module_ModuleController extends MVVController // list all variants $variants = $this->modul->getVariants(); if (count($variants)) { - $widget = new SidebarWidget(); - $widget->setTitle(_('Varianten')); - $widget->addElement(new WidgetElement( - $template_factory->render('shared/modul_variants', - [ - 'variants' => $variants, - 'link' => $this->modulURL() - ]) - )); + $widget = new TemplateWidget( + _('Varianten'), + $this->get_template_factory()->open('shared/modul_variants'), + [ + 'variants' => $variants, + 'link' => $this->modulURL(), + ] + ); $sidebar->addWidget($widget, 'variants'); } } + $this->semester = array_reverse(Semester::getAll()); - $this->def_lang = $this->display_language === $this->modul->getDefaultLanguage(); - ModuleManagementModel::setContentLanguage($this->display_language); + $this->def_lang = $this->display_language === $this->modul->original_language; + I18NString::setDefaultLanguage($this->modul->original_language); + I18NString::setContentLanguage($this->display_language); if (!$this->def_lang) { $action_widget = $sidebar->getWidget('actions'); $action_widget->addLink( @@ -247,7 +264,6 @@ class Module_ModuleController extends MVVController ); } - $this->language = $GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['content_language']; if (Request::isPost()) { CSRFProtection::verifyUnsafeRequest(); $stored = false; @@ -279,6 +295,13 @@ class Module_ModuleController extends MVVController $this->modul->fassung_typ = Request::option('fassung_typ'); $this->modul->version = trim(Request::get('version')); $this->modul->verantwortlich = trim(Request::get('verantwortlich')); + // change original language + if ( + !$this->modul->isNew() + && $this->modul->original_language !== Request::option('original_language') + ) { + $this->setOriginalLanguage($this->modul, Request::option('original_language')); + } } $deskriptor_fields = ['bezeichnung', 'verantwortlich', @@ -290,10 +313,16 @@ class Module_ModuleController extends MVVController foreach ($deskriptor_fields as $deskriptor_field) { if ($this->deskriptor->isI18nField($deskriptor_field)) { - $this->deskriptor->$deskriptor_field->setLocalized( - trim(Request::get($deskriptor_field)), - $this->language - ); + if ($this->display_language === $this->modul->original_language) { + $this->deskriptor->$deskriptor_field->setOriginal( + trim(Request::get($deskriptor_field)) + ); + } else { + $this->deskriptor->$deskriptor_field->setLocalized( + trim(Request::get($deskriptor_field)), + $this->display_language + ); + } } else { $this->deskriptor->setValue( $deskriptor_field, @@ -307,7 +336,7 @@ class Module_ModuleController extends MVVController $df = $this->deskriptor->datafields->findOneBy('datafield_id', $df_key); if ($df) { $tdf = $df->getTypedDatafield(); - $tdf->setContentLanguage($this->language); + $tdf->setContentLanguage($this->display_language); $tdf->setValueFromSubmit($df_value); $tdf->store(); } @@ -652,54 +681,56 @@ class Module_ModuleController extends MVVController if ($this->modulteil->isNew()) { PageLayout::setTitle(_('Neuen Modulteil anlegen')); $success_message = ('Der Modulteil "%s" wurde angelegt.'); - $this->display_language = $this->modulteil->getDefaultLanguage(); - $this->deskriptor = $this->modulteil->getDeskriptor($this->display_language, true); + $this->display_language = Request::option( + 'display_language', + $this->modul->original_language + ); + $this->deskriptor = $this->modulteil->getDeskriptor(); PageLayout::postInfo(sprintf( - _('Sie legen einen neuen Modulteil für das Modul %s an. Der Modulteil muss zunächst in der Ausgabesprache %s angelegt werden.'), + _('Sie legen einen neuen Modulteil für das Modul %1$s an. Der Modulteil muss zunächst in der Ausgabesprache %2$s angelegt werden.'), htmlReady($this->modul->getDisplayName()), - htmlReady($GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['name']) + htmlReady($GLOBALS['CONTENT_LANGUAGES'][$this->display_language]['name']) )); // set default language of instruction - if ($GLOBALS['MVV_MODULTEIL']['SPRACHE']['default']) { + if (!empty($GLOBALS['MVV_MODULTEIL']['SPRACHE']['default'])) { $this->modulteil->assignLanguagesOfInstruction([ $GLOBALS['MVV_MODULTEIL']['SPRACHE']['default'] ]); } } else { - $this->display_language = Request::option('display_language', $this->modulteil->getDefaultLanguage()); - $this->deskriptor = $this->modulteil->getDeskriptor($this->display_language, true); - $this->translations = $this->deskriptor->getAvailableTranslations(); - + $this->display_language = Request::option('display_language', $this->modul->original_language); + $this->deskriptor = $this->modulteil->getDeskriptor(); + $this->translations = $this->deskriptor->getAvailableTranslations($this->modul->original_language); if (!in_array($this->display_language, $this->translations)) { PageLayout::setTitle(sprintf( - _('Modulteil: "%s" in der Ausgabesprache "%s" neu anlegen.'), + _('Modulteil: %1$s in der Ausgabesprache %2$s neu anlegen.'), $this->modulteil->getDisplayName(), - $GLOBALS['MVV_MODULTEIL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['name'] + $GLOBALS['CONTENT_LANGUAGES'][$this->display_language]['name'] )); } else { PageLayout::setTitle(sprintf(_('Modulteil: %s'), htmlReady($this->modulteil->getDisplayName()))); } - $success_message = _('Der Modulteil "%s" wurde geändert.'); - // sidebar widget for selecting language - $template_factory = $this->get_template_factory(); + $success_message = _('Der Modulteil %s wurde geändert.'); + $sidebar = Sidebar::get(); - $widget = new ListWidget(); - $widget->setTitle(_('Ausgabesprache')); - $widget_element = new WidgetElement( - $template_factory->render('shared/deskriptor_language', - [ - 'modul' => $this->modulteil, - 'sprache' => $this->display_language, - 'link' => $this->modulteilURL($this->modulteil->id), - 'url' => $this->url - ] - ) - ); - $widget->addElement($widget_element); - $sidebar->addWidget($widget, 'languages'); + $views_widget = new ViewsWidget(); + $views_widget->setTitle(_('Ausgabesprache')); + $content_languages = array_merge(array_flip($this->translations), $GLOBALS['CONTENT_LANGUAGES']); + foreach ($content_languages as $code => $language) { + $views_widget->addLink($language['name'] + . ' (' . ($code === $this->modul->original_language ? _('Originalfassung') . ', ' : '') + . (in_array($code, $this->translations) ? _('bearbeiten') : _('neu anlegen')) . ')', + URLHelper::getLink($this->modulteilURL($this->modulteil->id), + ['display_language' => $code] + ) + )->setActive($code === $this->display_language); + } + $sidebar->insertWidget($views_widget, 'actions','language'); } - $this->def_lang = $this->display_language === $this->modulteil->getDefaultLanguage(); + $this->def_lang = $this->display_language === $this->modul->original_language; + I18NString::setDefaultLanguage($this->modul->original_language); + I18NString::setContentLanguage($this->display_language); if (!$this->def_lang) { $action_widget = $sidebar->getWidget('actions'); @@ -711,7 +742,6 @@ class Module_ModuleController extends MVVController ); } - $this->language = $GLOBALS['MVV_MODULTEIL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['content_language']; if (Request::submitted('store')) { CSRFProtection::verifyUnsafeRequest(); $stored = false; @@ -748,12 +778,21 @@ class Module_ModuleController extends MVVController foreach ($deskriptor_fields as $deskriptor_field) { if ($this->deskriptor->isI18nField($deskriptor_field)) { - $this->deskriptor->$deskriptor_field->setLocalized( - trim(Request::get($deskriptor_field)), - $this->language - ); + if ($this->display_language === $this->modul->original_language) { + $this->deskriptor->$deskriptor_field->setOriginal( + trim(Request::get($deskriptor_field)) + ); + } else { + $this->deskriptor->$deskriptor_field->setLocalized( + trim(Request::get($deskriptor_field)), + $this->display_language + ); + } } else { - $this->deskriptor->setValue($deskriptor_field, trim(Request::get($deskriptor_field))); + $this->deskriptor->setValue( + $deskriptor_field, + trim(Request::get($deskriptor_field)) + ); } } @@ -762,7 +801,7 @@ class Module_ModuleController extends MVVController $df = $this->deskriptor->datafields->findOneBy('datafield_id', $df_key); if ($df) { $tdf = $df->getTypedDatafield(); - $tdf->setContentLanguage($this->language); + $tdf->setContentLanguage($this->display_language); $tdf->setValueFromSubmit($df_value); $tdf->store(); } @@ -795,23 +834,25 @@ class Module_ModuleController extends MVVController return; } } - if ($this->display_language !== $this->modulteil->getDefaultLanguage() && $this->deskriptor->isNew()) { + if ($this->display_language !== $this->modul->original_language && $this->deskriptor->isNew()) { PageLayout::postInfo(sprintf( _('Neue Beschreibung zum Modulteil "%s" in der Ausgabesprache %s anlegen.'), htmlReady($this->modulteil->getDisplayName()), - htmlReady($GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values'][$this->display_language]['name']) + htmlReady($GLOBALS['CONTENT_LANGUAGES'][$this->display_language]['name']) )); } $this->cancel_url = $this->detailsURL($this->modulteil->modul_id); - $action_widget = Sidebar::get()->getWidget('actions'); - $action_widget->addLink( - _('Log-Einträge dieses Modulteils'), - $this->url_for('shared/log_event/show/Modulteil/' . $this->modulteil->id, - ['object2_type' => 'ModulteilDeskriptor', 'object2_id' => $this->deskriptor->id] - ), - Icon::create('log') - )->asDialog(); + if (!$this->modulteil->isNew()) { + $action_widget = Sidebar::get()->getWidget('actions'); + $action_widget->addLink( + _('Log-Einträge dieses Modulteils'), + $this->url_for('shared/log_event/show/Modulteil/' . $this->modulteil->id, + ['object2_type' => 'ModulteilDeskriptor', 'object2_id' => $this->deskriptor->id] + ), + Icon::create('log') + )->asDialog(); + } $this->render_template('module/module/modulteil', $this->layout); } @@ -828,7 +869,7 @@ class Module_ModuleController extends MVVController if (is_null($deskriptor)) { throw new Trails\Exception(404, _('Unbekannter Deskriptor')); } - $def_lang = $deskriptor->modulteil->getDefaultLanguage(); + $def_lang = $deskriptor->modulteil->modul->original_language; if ($language === $def_lang) { throw new Trails\Exception(403, _('Ein Deskriptor in der Original-Sprache kann nicht gelöscht werden.')); } @@ -1159,6 +1200,135 @@ class Module_ModuleController extends MVVController } } + public function change_language_action(string $module_id): void + { + $module = Modul::find($module_id); + if (!$module) { + throw new UnexpectedValueException('Unknown module.'); + } + PageLayout::setTitle(_('Originalsprache ändern')); + $this->translations = $module->deskriptoren->getAvailableTranslations($module->original_language); + $this->content_languages = $GLOBALS['CONTENT_LANGUAGES']; + $this->original_language = $module->original_language; + $this->module_id = $module->id; + } + + public function store_language_action(string $module_id): void + { + $module = Modul::find($module_id); + if (!$module) { + throw new UnexpectedValueException('Unknown module.'); + } + CSRFProtection::verifyUnsafeRequest(); + $new_language = Request::option('new_language'); + if ($new_language !== $module->original_language) { + $descriptor = $module->deskriptoren; + if (Request::bool('swap_data')) { + self::swap_translation($descriptor, $new_language, $module->original_language); + $module->modulteile->each( + fn($component) => self::swap_translation( + $component->deskriptoren, + $new_language, + $module->original_language + ) + ); + } else { + self::swap_language($module->deskriptoren, $new_language, $module->original_language); + $module->modulteile->each( + fn($component) => self::swap_language( + $component->deskriptoren, + $new_language, + $module->original_language + ) + ); + } + $module->original_language = $new_language; + $module->store(); + } else { + throw new UnexpectedValueException('New language has to be different from current language.'); + } + $this->relocate('module/module/index'); + } + + /** + * Swaps the language of the descriptor, but not the translated content. + * + * @param ModulDeskriptor|ModulteilDeskriptor $descriptor The descriptor of a module or a module component. + * @param string $new_language The language to swap to as language code. + * @param string $old_language The current languageas language code. + * @return int|bool Number of swapped translations or false if no translation is available. + */ + private static function swap_language( + ModulDeskriptor|ModulteilDeskriptor $descriptor, + string $new_language, + string $old_language + ): int|bool + { + // change language setting in i18n for this descriptor if a translation exists + $translations = $descriptor->getAvailableTranslations($old_language); + if (count($translations) > 1) { + $metadata = $descriptor->getTableMetadata(); + foreach (array_keys($metadata['fields']) as $field) { + if ($descriptor->isI18nField($field)) { + $i18n_field = $descriptor->$field; + $i18n_field->setDefaultLanguage($old_language); + $translation = $i18n_field->translation($new_language); + if (!empty($translation)) { + $translations = $i18n_field->toArray(); + unset($translations[$new_language]); + $translations[$old_language] = $translation; + $i18n_field->setTranslations($translations); + } + $i18n_field->setDefaultLanguage($new_language); + } + } + return $descriptor->store(); + } + return false; + } + + /** + * Swaps the language of the descriptor, swaps the translated content also. + * + * @param ModulDeskriptor|ModulteilDeskriptor $descriptor The descriptor of a module or a module component. + * @param string $new_language The language to swap to as language code. + * @param string $old_language The current language as language code. + * @return int|bool Number of swapped translations or false if no translation is available. + */ + private static function swap_translation( + ModulDeskriptor|ModulteilDeskriptor $descriptor, + string $new_language, + string $old_language + ): int|bool + { + if ( + !array_key_exists($new_language, $GLOBALS['CONTENT_LANGUAGES']) + || !array_key_exists($old_language, $GLOBALS['CONTENT_LANGUAGES']) + ) { + throw new UnexpectedValueException('Language code refers to no content language.'); + } + $count = 0; + $meta_data = $descriptor->getTableMetadata(); + $descriptor_class = $descriptor::class; + foreach ($meta_data['fields'] as $field => $foo) { + if ($descriptor->isI18nField($field)) { + $i18n_field = $descriptor->$field; + $i18n_field->setDefaultLanguage($old_language); + $new_base_content = $i18n_field->translation($new_language); + $old_base_content = $i18n_field->original(); + $i18n_field->setDefaultLanguage($new_language); + if (!empty($new_base_content)) { + $i18n_field->setOriginal($new_base_content); + $i18n_field->setLocalized($old_base_content, $old_language); + } + $lang = $i18n_field->toArray(); + unset($lang[$new_language]); + $i18n_field->setTranslations($lang); + } + } + return $descriptor->store(); + } + /** * do the search */ @@ -1332,25 +1502,14 @@ class Module_ModuleController extends MVVController { $sidebar = Sidebar::get(); - $widget = new ViewsWidget(); - $widget->addLink( - _('Liste der Module'), - $this->url_for('module/module/index') - )->setActive(get_called_class() === 'Module_ModuleController'); - $widget->addLink( - _('Gruppiert nach verantwortlichen Einrichtungen'), - $this->url_for('module/institute/index') - )->setActive(get_called_class() === 'Module_InstituteController'); - $sidebar->addWidget($widget, 'views'); - $widget = new ActionsWidget(); $widget->setTitle(_('Aktionen')); if (MvvPerm::havePermCreate('Modul')) { $widget->addLink( _('Neues Modul anlegen'), - $this->modulURL(), + $this->select_module_languageURL(), Icon::create('add') - ); + )->asDialog('size=auto'); } $sidebar->addWidget($widget, 'actions'); @@ -1573,4 +1732,47 @@ class Module_ModuleController extends MVVController WHERE `mvv_studiengang`.`abschluss_id` = ?"; return DBManager::get()->fetchFirst($query, [$abschluss_id]); } + + /** + * Sets the original language for the given module. + * + * @param Modul $module Sets the original language for this module. + * @param string $original_language The original language as language code. + * @return void + */ + private function setOriginalLanguage(Modul $module, string $original_language): void + { + $content_language = $GLOBALS['CONTENT_LANGUAGES'][$original_language]; + if (empty($content_language)) { + throw new InvalidArgumentException("Original language $original_language is not defined"); + } + if ($module->deskriptoren) { + $current_language = $module->original_language; + $module->original_language = $original_language; + DBManager::get()->execute(" + UPDATE `i18n` + SET `lang` = ? + WHERE `object_id` = ? + AND `table` = 'mvv_modul_deskriptor' + AND `lang` = ?", + [ + $original_language, + $module->deskriptoren->id, + $current_language + ]); + $module->modulteile->each(fn($component) => DBManager::get()->execute(" + UPDATE `i18n` + SET `lang` = ? + WHERE `object_id` IN (?) + AND `table` = 'mvv_modulteil_deskriptor' + AND `lang` = ?", + [ + $original_language, + $component->deskriptoren->id, + $current_language + ] + ) + ); + } + } } diff --git a/app/controllers/search/angebot.php b/app/controllers/search/angebot.php index f2b5321..0c0f402 100644 --- a/app/controllers/search/angebot.php +++ b/app/controllers/search/angebot.php @@ -172,7 +172,7 @@ class Search_AngebotController extends MVVController public function verlauf_action($stgteil_id, $stgteil_bez_id = null, $studiengang_id = null) { - ModuleManagementModel::setLanguage($_SESSION['_language']); + ModuleManagementModel::setContentLanguage($_SESSION['_language']); $response = $this->relay('search/studiengaenge/verlauf', $stgteil_id, $stgteil_bez_id, $studiengang_id); diff --git a/app/controllers/search/studiengaenge.php b/app/controllers/search/studiengaenge.php index 0dd5c7d..8f08c32 100644 --- a/app/controllers/search/studiengaenge.php +++ b/app/controllers/search/studiengaenge.php @@ -341,15 +341,17 @@ class Search_StudiengaengeController extends MVVController // add links to export Modulhandbücher as PDF $widget = new ActionsWidget(); $widget->setTitle(_('Aktuelle Modulhandbücher')); - $avl_lang = array_keys($GLOBALS['MVV_MODUL_DESKRIPTOR']['SPRACHE']['values']); - - foreach ($avl_lang as $language) { - if ($language === $GLOBALS['MVV_LANGUAGES']['default']) { + $content_languages = array_merge( + ['original' => ['name' => _('Originalfassung')]], + $GLOBALS['CONTENT_LANGUAGES'] + ); + foreach (array_keys($content_languages) as $language) { + if ($language === 'original') { $title = _('Originalfassung als PDF'); } else { $title = sprintf( - _('Zweitfassung (%s) als PDF'), - $GLOBALS['MVV_LANGUAGES']['values'][$language]['name'] + _('Ausgabesprache (%s) als PDF'), + $content_languages[$language]['name'] ); } // get link without registered parameters @@ -373,7 +375,7 @@ class Search_StudiengaengeController extends MVVController public function info_action($studiengang_id, $language = null) { $language = $language ?: Request::get('language', $_SESSION['_language']); - ModuleManagementModel::setLanguage($language); + ModuleManagementModel::setContentLanguage($language); $this->studiengang = Studiengang::find($studiengang_id); if (!$this->studiengang) { @@ -391,23 +393,23 @@ class Search_StudiengaengeController extends MVVController $file = $document->mvv_file; if ($file->extern_visible) { $mvv_file_ref = null; - foreach ($GLOBALS['MVV_LANGUAGES']['values'] as $key => $mvv_language) { - if ($mvv_language['locale'] === $_SESSION['_language']) { - $mvv_file_ref = $file->file_refs->findOneBy('file_language', $key); + foreach ($GLOBALS['CONTENT_LANGUAGES'] as $language_key => $mvv_language) { + if ($language_key === $_SESSION['_language']) { + $mvv_file_ref = $file->file_refs->findOneBy('file_language', $language_key); } else { - $mvv_file_ref = $file->file_refs->findOneBy('file_language', $GLOBALS['MVV_LANGUAGES']['default']); + $mvv_file_ref = $file->file_refs->findOneBy('file_language', Config::get()->MVV_DEFAULT_LANGUAGE); } } if ($mvv_file_ref) { $filetype = $mvv_file_ref->file_ref->getFileType(); $this->all_documents[$file->category][$file->id] = - [ - 'name' => $file->getDisplayName(), - 'url' => $mvv_file_ref->file_ref->getDownloadURL(), - 'metadata_url' => $mvv_file_ref->file_ref->file->metadata['url'] ?? null, - 'extension' => $mvv_file_ref->file_ref->file->getExtension(), - 'is_link' => ($filetype instanceof URLFile) - ]; + [ + 'name' => $file->getDisplayName(), + 'url' => $mvv_file_ref->file_ref->getDownloadURL(), + 'metadata_url' => $mvv_file_ref->file_ref->file->metadata['url'] ?? null, + 'extension' => $mvv_file_ref->file_ref->file->getExtension(), + 'is_link' => ($filetype instanceof URLFile) + ]; } } } diff --git a/app/controllers/seminar/details.php b/app/controllers/seminar/details.php index 3ce2fab..6750fa6 100644 --- a/app/controllers/seminar/details.php +++ b/app/controllers/seminar/details.php @@ -21,7 +21,7 @@ class Seminar_DetailsController extends MVVController public function before_filter(&$action, &$args) { parent::before_filter($action, $args); - ModuleManagementModel::setLanguage($_SESSION['_language']); + ModuleManagementModel::setContentLanguage($_SESSION['_language']); if (Request::isXhr()) { $this->set_layout(null); @@ -38,4 +38,4 @@ class Seminar_DetailsController extends MVVController $this->mvv_pathes = MvvCourse::get($seminar_id)->getTrails($trail_classes); } -} \ No newline at end of file +} diff --git a/app/controllers/shared/download.php b/app/controllers/shared/download.php index f94bc86..6e39998 100644 --- a/app/controllers/shared/download.php +++ b/app/controllers/shared/download.php @@ -154,7 +154,7 @@ class Shared_DownloadController extends AuthenticatedController public function getMVVPluginModulDescription($modul, $display_language = null) { if ($display_language == null) { - $display_language = $GLOBALS['MVV_LANGUAGES']['default']; + $display_language = Config::get()->MVV_DEFAULT_LANGUAGE; } $path = $GLOBALS['STUDIP_BASE_PATH'] . '/app/views/shared/modul/'; @@ -168,8 +168,7 @@ class Shared_DownloadController extends AuthenticatedController $factory = new Flexi\Factory($path); $type = 1; - if (count($modul->modulteile) == 1) { - $modulteil = $modul->modulteile->first(); + if (count($modul->modulteile) === 1) { $type = 2; } elseif (count($modul->modulteile) === 0) { $type = 3; diff --git a/app/controllers/shared/modul.php b/app/controllers/shared/modul.php index d1a6ecf..2cb9a85 100644 --- a/app/controllers/shared/modul.php +++ b/app/controllers/shared/modul.php @@ -13,8 +13,6 @@ * @since 3.5 */ - - class Shared_ModulController extends AuthenticatedController { @@ -28,98 +26,91 @@ class Shared_ModulController extends AuthenticatedController public function overview_action($modul_id, $semester_id = null) { $display_language = Request::option('display_language', $_SESSION['_language']); - ModuleManagementModel::setLanguage($display_language); + ModuleManagementModel::setContentLanguage($display_language); - $modul = Modul::find($modul_id); - if (!$modul->hasPublicStatus()) { + $this->modul = Modul::find($modul_id); + if (!$this->modul->hasPublicStatus()) { throw new AccessDeniedException(); } - if ($modul) { - $this->details_id = $modul->getId(); + if ($this->modul) { + $this->details_id = $this->modul->getId(); $type = 1; - if (count($modul->modulteile) == 1) { - $modulteil = $modul->modulteile->first(); + if (count($this->modul->modulteile) == 1) { + $modulteil = $this->modul->modulteile->first(); $type = 3; if (count($modulteil->lvgruppen) > 0) { $type = 2; } - } else if (count($modul->modulteile) == 0) { + } else if (count($this->modul->modulteile) == 0) { $type = 3; } if (!$semester_id) { - $currentSemester = Semester::findDefault(); + $current_semester = Semester::findDefault(); } else { - $currentSemester = Semester::find($semester_id); + $current_semester = Semester::find($semester_id); } $sws = 0; - $institut = new Institute($modul->responsible_institute->institut_id); - $modulTeileData = []; - foreach ($modul->modulteile as $modulTeil) { - - $modulTeilDeskriptor = $modulTeil->getDeskriptor($display_language); - - $sws += (int) $modulTeil->sws; - - $num_bezeichnung = $GLOBALS['MVV_MODULTEIL']['NUM_BEZEICHNUNG']['values'][$modulTeil->num_bezeichnung]['name'] ?? ''; - - $name_kurz = sprintf('%s %d', $num_bezeichnung, $modulTeil->nummer); - - $modulTeileData[$modulTeil->getId()] = [ - 'name' => $modulTeil->getDisplayName(), + $institut = new Institute($this->modul->responsible_institute->institut_id); + $modulteile_data = []; + foreach ($this->modul->modulteile as $modulteil) { + $modulteil_deskriptor = $modulteil->getDeskriptor(); + $sws += (int) $modulteil->sws; + $num_bezeichnung = $GLOBALS['MVV_MODULTEIL']['NUM_BEZEICHNUNG']['values'][$modulteil->num_bezeichnung]['name'] ?? ''; + $name_kurz = sprintf('%s %d', $num_bezeichnung, $modulteil->nummer); + $modulteile_data[$modulteil->getId()] = [ + 'name' => $modulteil->getDisplayName(), 'name_kurz' => $name_kurz, - 'voraussetzung' => $modulTeilDeskriptor->voraussetzung, - 'pruef_leistung' => $modulTeilDeskriptor->pruef_leistung, - 'pruef_vorleistung' => $modulTeilDeskriptor->pruef_vorleistung, - 'kommentar' => $modulTeilDeskriptor->kommentar, - 'kapazitaet' => $modulTeil->kapazitaet, + 'voraussetzung' => $modulteil_deskriptor->voraussetzung, + 'pruef_leistung' => $modulteil_deskriptor->pruef_leistung, + 'pruef_vorleistung' => $modulteil_deskriptor->pruef_vorleistung, + 'kommentar' => $modulteil_deskriptor->kommentar, + 'kapazitaet' => $modulteil->kapazitaet, 'lvGruppen' => [] ]; - $lvGruppen = Lvgruppe::findByModulteil($modulTeil->getId()); + $lvGruppen = Lvgruppe::findByModulteil($modulteil->getId()); foreach ($lvGruppen as $lvGruppe) { - $ids = array_column($lvGruppe->getAssignedCoursesBySemester($currentSemester['semester_id'], $GLOBALS['user']->id), 'seminar_id'); + $ids = array_column($lvGruppe->getAssignedCoursesBySemester($current_semester['semester_id'], $GLOBALS['user']->id), 'seminar_id'); $courses = Course::findMany($ids, 'order by Veranstaltungsnummer, Name'); - $modulTeileData[$modulTeil->getId()]['lvGruppen'][$lvGruppe->getId()] = [ + $modulteile_data[$modulteil->getId()]['lvGruppen'][$lvGruppe->getId()] = [ 'courses' => $courses, 'alt_texte' => $lvGruppe->alttext ]; } } - $this->modulTeile = $modulTeileData; - $this->deskriptor = $modul->getDeskriptor($display_language); + $this->modulteile = $modulteile_data; + $this->deskriptor = $this->modul->getDeskriptor(); $this->institut = $institut; - $this->semester = $currentSemester; + $this->semester = $current_semester; $this->sws = $sws; - $this->pruef_ebene = $GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$modul->pruef_ebene]['name'] ?? null; - $this->modul = $modul; + $this->pruef_ebene = $GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$this->modul->pruef_ebene]['name'] ?? null; $this->type = $type; $this->self_url = $this->url_for('modul/show/' . $modul_id); $this->detail_url = $this->url_for('modul/detail/' . $modul_id); - $this->teilnahmeVoraussetzung = $modul->getDeskriptor()->voraussetzung; - PageLayout::setTitle($modul->getDisplayName() . ' (' . _('Veranstaltungsübersicht') .')'); + PageLayout::setTitle($this->modul->getDisplayName() . ' (' . _('Veranstaltungsübersicht') .')'); } } public function description_action($id) { - $modul = Modul::find($id); - $perm = MvvPerm::get($modul); - if (!($modul->hasPublicStatus() || $perm->haveObjectPerm(MvvPerm::PERM_READ))) { + $this->modul = Modul::find($id); + $perm = MvvPerm::get($this->modul); + if (!($this->modul->hasPublicStatus() || $perm->haveObjectPerm(MvvPerm::PERM_READ))) { throw new AccessDeniedException(); } - $type = 1; - if (count($modul->modulteile) == 1) { - $modulteil = $modul->modulteile->first(); - $type = 3; + $this->type = 1; + if (count($this->modul->modulteile) == 1) { + $modulteil = $this->modul->modulteile->first(); + $this->type = 3; if (count($modulteil->lvgruppen) > 0) { - $type = 2; + $this->type = 2; } - } else if (count($modul->modulteile) == 0) { - $type = 3; + } else if (count($this->modul->modulteile) == 0) { + $this->type = 3; } if (!Request::get('sem_select')) { @@ -128,28 +119,27 @@ class Shared_ModulController extends AuthenticatedController $currentSemester = Semester::find(Request::get('sem_select')); } - $display_language = Request::get('display_language', $_SESSION['_language']); - ModuleManagementModel::setLanguage($display_language); + $this->display_language = Request::get('display_language', $this->modul->original_language); + ModuleManagementModel::setContentLanguage($this->display_language); + I18NString::setDefaultLanguage($this->modul->original_language); + I18NString::setContentLanguage($this->display_language); $this->semesterSelector = Semester::getSemesterSelector(null, $currentSemester['semester_id'], 'semester_id', false); - $this->modul = $modul; - $this->pruefungsEbene = isset($GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$modul->pruef_ebene]) - ? $GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$modul->pruef_ebene]['name'] + $this->pruefungsEbene = isset($GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$this->modul->pruef_ebene]) + ? $GLOBALS['MVV_MODUL']['PRUEF_EBENE']['values'][$this->modul->pruef_ebene]['name'] : null; - $this->modulDeskriptor = $modul->getDeskriptor($display_language); - $this->startSemester = Semester::findByTimestamp($modul->start); + $this->modulDeskriptor = $this->modul->getDeskriptor(); + $this->startSemester = Semester::findByTimestamp($this->modul->start); - if (!$modul->responsible_institute) { + if (!$this->modul->responsible_institute) { $this->instituteName = null; - } elseif ($modul->responsible_institute->institute) { - $this->instituteName = $modul->responsible_institute->institute->name; + } elseif ($this->modul->responsible_institute->institute) { + $this->instituteName = $this->modul->responsible_institute->institute->name; } else { $this->instituteName = _('Unbekannte Einrichtung'); } - $this->type = $type; $this->semester = $currentSemester; - $this->display_language = $display_language; - PageLayout::setTitle($modul->getDisplayName() . ' (' . _('Vollständige Modulbeschreibung') .')'); + PageLayout::setTitle($this->modul->getDisplayName() . ' (' . _('Vollständige Modulbeschreibung') .')'); } public function mail_action($modul_id, $semester_id) diff --git a/app/controllers/studiengaenge/studiengaenge.php b/app/controllers/studiengaenge/studiengaenge.php index 44f5d83..809d1ab 100644 --- a/app/controllers/studiengaenge/studiengaenge.php +++ b/app/controllers/studiengaenge/studiengaenge.php @@ -1215,11 +1215,11 @@ class Studiengaenge_StudiengaengeController extends MVVController $file = $document->mvv_file; if ($file->extern_visible) { $mvv_file_ref = null; - foreach ($GLOBALS['MVV_LANGUAGES']['values'] as $key => $mvv_language) { - if ($mvv_language['locale'] === $_SESSION['_language']) { - $mvv_file_ref = $file->file_refs->findOneBy('file_language', $key); + foreach ($GLOBALS['CONTENT_LANGUAGES'] as $code => $content_language) { + if ($code === $_SESSION['_language']) { + $mvv_file_ref = $file->file_refs->findOneBy('file_language', $code); } else { - $mvv_file_ref = $file->file_refs->findOneBy('file_language', $GLOBALS['MVV_LANGUAGES']['default']); + $mvv_file_ref = $file->file_refs->findOneBy('file_language', Config::get()->MVV_DEFAULT_LANGUAGE); } } if ($mvv_file_ref) { diff --git a/app/views/admin/datafields/edit.php b/app/views/admin/datafields/edit.php index 46b7aa1..c3f67b4 100644 --- a/app/views/admin/datafields/edit.php +++ b/app/views/admin/datafields/edit.php @@ -71,7 +71,7 @@ use Studip\Button, Studip\LinkButton; - $value) : ?> + $value) : ?> @@ -90,7 +90,7 @@ use Studip\Button, Studip\LinkButton; object_type === 'studycourse'): ?> - + - $value) : ?> + $value) : ?> disable('name') ?>> + disable('name') ?>>
- - _("Datei hinzufügen"), 'class' => 'text-bottom']); ?> - + + asImg(['title' => _('Datei hinzufügen'), 'class' => 'text-bottom']); ?> + -
-
+
    - +
  • - + 'text-bottom']); ?> - filename) ?> - file_ref->size) ?> -