From c99f6c68d52e31357e3bbd563df293f5b74ed9d9 Mon Sep 17 00:00:00 2001 From: Jens Schwidder Date: Mon, 20 Apr 2026 20:25:51 +0200 Subject: [PATCH 01/10] #1444 Option to remove RSS links (#1466) * #1444 Control rendering of RSS links * #1444 RSS links release notes --- RELEASE_NOTES.md | 9 ++ application/configs/application.ini | 1 + library/Application/View/Helper/RssLink.php | 85 +++++++++++++ .../Application/View/Helper/RssMetaLink.php | 58 +++++++++ modules/home/views/scripts/index/index.phtml | 8 +- .../views/scripts/browse/doctypes.phtml | 4 +- .../views/scripts/browse/index.phtml | 4 +- .../views/scripts/browse/series.phtml | 4 +- .../views/scripts/browse/years.phtml | 5 +- .../scripts/index/browsecollection.phtml | 5 +- .../views/scripts/index/nohits.phtml | 8 +- .../views/scripts/index/results.phtml | 8 +- .../scripts/index/simpleSearchForm.phtml | 4 +- .../Application/View/Helper/RssLinkTest.php | 118 ++++++++++++++++++ .../View/Helper/RssMetaLinkTest.php | 95 ++++++++++++++ 15 files changed, 375 insertions(+), 41 deletions(-) create mode 100644 library/Application/View/Helper/RssLink.php create mode 100644 library/Application/View/Helper/RssMetaLink.php create mode 100644 tests/library/Application/View/Helper/RssLinkTest.php create mode 100644 tests/library/Application/View/Helper/RssMetaLinkTest.php diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6f66e43a7..3e07921c8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,14 @@ # OPUS 4 Release Notes +## Release 4.10 - 2026-05-12 + +### RSS-Links + +RSS-Links können nun ausgeblendet werden. Sie werden automatisch ausgeblendet, +wenn ein User keinen Zugriff auf das RSS-Modul hat. + + rss.showLinks = 0 + ## Release 4.9 - 2026-04-14 ### Unterstützte PHP-Versionen diff --git a/application/configs/application.ini b/application/configs/application.ini index 9e941e643..a775403a8 100644 --- a/application/configs/application.ini +++ b/application/configs/application.ini @@ -250,6 +250,7 @@ search.index.enrichment.blacklist = 'opus_doi_json' ; RSS Feed rss.maxDocs = 100 +rss.showLinks = 1 ;DOCTYPE VALIDATION SCHEMA FILE ; TODO determine path dynamically (does this belong into the framework) diff --git a/library/Application/View/Helper/RssLink.php b/library/Application/View/Helper/RssLink.php new file mode 100644 index 000000000..9b1e3d0c8 --- /dev/null +++ b/library/Application/View/Helper/RssLink.php @@ -0,0 +1,85 @@ +isShowRssLinks() || ! $this->isRssAllowed()) { + return ''; + } + + $view = $this->view; + + if (is_string($options)) { + $rssUrl = $options; + } else { + $basicOptions = [ + 'module' => 'rss', + 'controller' => 'index', + 'action' => 'index', + ]; + + if (is_array($options)) { + $rssUrl = $view->url(array_merge($basicOptions, $options), null, true); + } else { + $rssUrl = $view->url($basicOptions, null, true); + } + } + + $imagePath = $view->layoutPath() . '/img/feed_small.png'; + $alt = $view->translate('rss_icon'); + $title = $view->translate('rss_title'); + + $output = "" . PHP_EOL; + $output .= " \"{$alt}\"" . PHP_EOL; + $output .= ""; + + return $output; + } + + protected function isShowRssLinks(): bool + { + $config = $this->getConfig(); + return isset($config->rss->showLinks) && filter_var($config->rss->showLinks, FILTER_VALIDATE_BOOLEAN); + } + + protected function isRssAllowed(): bool + { + $realm = Realm::getInstance(); + return $realm->checkModule('rss'); + } +} diff --git a/library/Application/View/Helper/RssMetaLink.php b/library/Application/View/Helper/RssMetaLink.php new file mode 100644 index 000000000..9b9ad5710 --- /dev/null +++ b/library/Application/View/Helper/RssMetaLink.php @@ -0,0 +1,58 @@ +isShowRssLinks() || ! $this->isRssAllowed()) { + return ''; + } + + $view = $this->view; + + if ($rssUrl === null) { + $rssUrl = $view->baseUrl() . '/rss/index/index'; + } + + $view->headLink([ + 'rel' => 'alternate', + 'type' => 'application/rss+xml', + 'href' => $view->serverUrl() . $rssUrl, + ]); + + return ''; + } +} diff --git a/modules/home/views/scripts/index/index.phtml b/modules/home/views/scripts/index/index.phtml index b19e045e4..5811d0105 100644 --- a/modules/home/views/scripts/index/index.phtml +++ b/modules/home/views/scripts/index/index.phtml @@ -31,13 +31,7 @@ ?> -headLink([ - 'rel' => 'alternate', - 'type' => 'application/rss+xml', - 'href' => $this->serverUrl() . $this->baseUrl() . '/rss/index/index', - ]); - ?> +rssMetaLink() ?> title = 'home_index_index_pagetitle' ?> diff --git a/modules/solrsearch/views/scripts/browse/doctypes.phtml b/modules/solrsearch/views/scripts/browse/doctypes.phtml index 814c9fef7..cb93e7f0f 100644 --- a/modules/solrsearch/views/scripts/browse/doctypes.phtml +++ b/modules/solrsearch/views/scripts/browse/doctypes.phtml @@ -41,9 +41,7 @@
  • translate($facetitem->getText())) ?>  (getCount() ?>) - - <?= $this->translate('rss_icon') ?> - + rssLink(['searchtype' => 'simple', 'query' => '*:*', 'doctypefq' => $facetitem->getText()]) ?>
  • diff --git a/modules/solrsearch/views/scripts/browse/index.phtml b/modules/solrsearch/views/scripts/browse/index.phtml index eb469f268..d995cf034 100644 --- a/modules/solrsearch/views/scripts/browse/index.phtml +++ b/modules/solrsearch/views/scripts/browse/index.phtml @@ -44,9 +44,7 @@ showLatestDocuments) : ?>
  • translate('latest_documents_title')) ?>  - - <?= $this->translate('rss_icon') ?> - + rssLink() ?>
  • showDocumentTypes) : ?> diff --git a/modules/solrsearch/views/scripts/browse/series.phtml b/modules/solrsearch/views/scripts/browse/series.phtml index eef51c2bc..5fe693067 100644 --- a/modules/solrsearch/views/scripts/browse/series.phtml +++ b/modules/solrsearch/views/scripts/browse/series.phtml @@ -38,9 +38,7 @@ series as $seriesitem) : ?>
  • - - <?= $this->translate('rss_icon') ?> - + rssLink(['searchtype' => 'series', 'id' => $seriesitem['id']]) ?>
  • diff --git a/modules/solrsearch/views/scripts/browse/years.phtml b/modules/solrsearch/views/scripts/browse/years.phtml index 1baa05f8a..41c59af95 100644 --- a/modules/solrsearch/views/scripts/browse/years.phtml +++ b/modules/solrsearch/views/scripts/browse/years.phtml @@ -41,10 +41,7 @@
  • translate($facetitem->getText())) ?>  (getCount() ?>) - - <?= $this->translate('rss_icon') ?> - + rssLink(['searchtype' => 'simple', 'query' => '*:*', 'yearfq' => $facetitem->getText()]) ?>
  • diff --git a/modules/solrsearch/views/scripts/index/browsecollection.phtml b/modules/solrsearch/views/scripts/index/browsecollection.phtml index eaf5e031b..5e28286f6 100644 --- a/modules/solrsearch/views/scripts/index/browsecollection.phtml +++ b/modules/solrsearch/views/scripts/index/browsecollection.phtml @@ -84,10 +84,7 @@ use Opus\Common\Config; getDisplayNameForBrowsingContext($this->collectionRole)) ?> () - - <?= $this->translate('rss_icon') ?> - + rssLink(['searchtype' => 'collection', 'id' => $child->getId()]) ?> diff --git a/modules/solrsearch/views/scripts/index/nohits.phtml b/modules/solrsearch/views/scripts/index/nohits.phtml index 1e65c61bb..fb0834ffd 100644 --- a/modules/solrsearch/views/scripts/index/nohits.phtml +++ b/modules/solrsearch/views/scripts/index/nohits.phtml @@ -31,13 +31,7 @@ ?> -headLink([ - 'rel' => 'alternate', - 'type' => 'application/rss+xml', - 'href' => $this->serverUrl() . $this->url($this->rssUrl), - ]); - ?> +rssMetaLink($this->url($this->rssUrl)) ?> searchType === 'simple') { diff --git a/modules/solrsearch/views/scripts/index/results.phtml b/modules/solrsearch/views/scripts/index/results.phtml index 7b7a55243..863087a6a 100644 --- a/modules/solrsearch/views/scripts/index/results.phtml +++ b/modules/solrsearch/views/scripts/index/results.phtml @@ -37,13 +37,7 @@ if ($this->jQueryEnabled()) { } ?> -headLink([ - 'rel' => 'alternate', - 'type' => 'application/rss+xml', - 'href' => $this->serverUrl() . $this->url($this->rssUrl), - ]); - ?> +rssMetaLink($this->url($this->rssUrl)) ?> specialTitle)) : ?>

    specialTitle) ?>

    diff --git a/modules/solrsearch/views/scripts/index/simpleSearchForm.phtml b/modules/solrsearch/views/scripts/index/simpleSearchForm.phtml index aae0f5a09..636a3b449 100644 --- a/modules/solrsearch/views/scripts/index/simpleSearchForm.phtml +++ b/modules/solrsearch/views/scripts/index/simpleSearchForm.phtml @@ -60,9 +60,7 @@ if ($this->jQueryEnabled()) { ?> translate('solrsearch_title_latest'); ?> - - <?= $this->translate('rss_icon') ?> - + rssLink() ?>

    diff --git a/tests/library/Application/View/Helper/RssLinkTest.php b/tests/library/Application/View/Helper/RssLinkTest.php new file mode 100644 index 000000000..c0636141c --- /dev/null +++ b/tests/library/Application/View/Helper/RssLinkTest.php @@ -0,0 +1,118 @@ +helper = new Application_View_Helper_RssLink(); + $this->helper->setView($this->getView()); + } + + public function testRssLinkDefault() + { + $output = $this->helper->rssLink(); + + $expected = << + rss_icon + +EOT; + + $this->assertEquals($expected, $output); + } + + public function testRssLinkWithArray() + { + $output = $this->helper->rssLink(['searchtype' => 'collection', 'id' => 147]); + + $expected = << + rss_icon + +EOT; + + $this->assertEquals($expected, $output); + } + + public function testRssLinkWithString() + { + $output = $this->helper->rssLink('/rss/index/index/searchtype/latest'); + + $expected = << + rss_icon + +EOT; + + $this->assertEquals($expected, $output); + } + + public function testRssLinkDisabled() + { + $this->adjustConfiguration([ + 'rss' => ['showLinks' => false], + ]); + + $output = $this->helper->rssLink(); + + $this->assertEquals('', $output); + } + + public function testRssLinkNoAccess() + { + $this->enableSecurity(); + + $guest = UserRole::fetchByName('guest'); + $guest->removeAccessModule('rss'); + $guest->store(); + + $output = $this->helper->rssLink(); + + $guest->appendAccessModule('rss'); + $guest->store(); + + $this->assertEquals('', $output); + } +} diff --git a/tests/library/Application/View/Helper/RssMetaLinkTest.php b/tests/library/Application/View/Helper/RssMetaLinkTest.php new file mode 100644 index 000000000..d33e8472f --- /dev/null +++ b/tests/library/Application/View/Helper/RssMetaLinkTest.php @@ -0,0 +1,95 @@ +helper = new Application_View_Helper_RssMetaLink(); + $this->helper->setView($this->getView()); + } + + public function testRssMetaLink() + { + $output = $this->helper->rssMetaLink(); + + $this->assertEquals('', $output); + + $view = $this->getView(); + + $links = $view->headLink()->toString(); + + $this->assertEquals( + '', + $links + ); + } + + public function testRssMetaLinkDisabled() + { + $this->adjustConfiguration([ + 'rss' => ['showLinks' => false], + ]); + + $this->helper->rssMetaLink(); + + $this->assertEquals('', $this->getView()->headLink()->toString()); + } + + public function testRssMetaLinkNoAccess() + { + $this->enableSecurity(); + + $guest = UserRole::fetchByName('guest'); + $guest->removeAccessModule('rss'); + $guest->store(); + + $this->helper->rssMetaLink(); + + $guest->appendAccessModule('rss'); + $guest->store(); + + $this->assertEquals('', $this->getView()->headLink()->toString()); + } +} From 9dcad18bd16a6e9e5cb800caaeb6c6d083ee26e4 Mon Sep 17 00:00:00 2001 From: j3nsch Date: Mon, 20 Apr 2026 20:31:39 +0200 Subject: [PATCH 02/10] #1460 OPUS 4.10 release preparations --- CHANGES.md | 5 +++++ README.md | 2 +- RELEASE_NOTES.md | 2 ++ application/configs/application.ini | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f81e1dbfb..17bd6a1a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # OPUS 4 Change Log +## Release 4.10 - 2026-05-12 + +OPUS 4.10 Project on GitHub +https://github.com/orgs/OPUS4/projects/74 + ## Release 4.9 - 2026-04-14 OPUS 4.9 Project on GitHub diff --git a/README.md b/README.md index 91c6eb755..2c5dd05a5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ mostly. ## OPUS 4 -The current version of OPUS 4 is __4.9__. It is available on the [master][MASTER] branch and compatible with +The current version of OPUS 4 is __4.10__. It is available on the [master][MASTER] branch and compatible with PHP 8.1 to 8.2. PHP 8.3 and beyond are not supported yet. [Documentation][DOC] diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3e07921c8..65a684b40 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -9,6 +9,8 @@ wenn ein User keinen Zugriff auf das RSS-Modul hat. rss.showLinks = 0 +-- + ## Release 4.9 - 2026-04-14 ### Unterstützte PHP-Versionen diff --git a/application/configs/application.ini b/application/configs/application.ini index a775403a8..9a5515e18 100644 --- a/application/configs/application.ini +++ b/application/configs/application.ini @@ -70,7 +70,7 @@ name = 'OPUS 4' logoLink = home security = 1 workspacePath = APPLICATION_PATH "/workspace" -version = 4.9 +version = 4.10 update.latestVersionCheckUrl = "https://api.github.com/repos/opus4/application/releases/latest" snippets.basePath = APPLICATION_PATH "/scripts/snippets" From 57884c4cd9c8e3b356e5b3c49d4e0d77a49dcaf1 Mon Sep 17 00:00:00 2001 From: Jens Schwidder Date: Tue, 21 Apr 2026 15:36:49 +0200 Subject: [PATCH 03/10] #630 Command for setting passwords (#1467) * #630 Replace password script with command * #630 addCommand not supported in PHP 8.1 --- bin/install.sh | 2 +- build.xml | 6 +- .../Console/Admin/ChangePasswordCommand.php | 128 ++++++++++++++++++ library/Application/Console/App.php | 2 + scripts/change-password.php | 49 ------- 5 files changed, 135 insertions(+), 52 deletions(-) create mode 100644 library/Application/Console/Admin/ChangePasswordCommand.php delete mode 100644 scripts/change-password.php diff --git a/bin/install.sh b/bin/install.sh index 9faf92435..86ed33af6 100755 --- a/bin/install.sh +++ b/bin/install.sh @@ -174,7 +174,7 @@ do fi done -php "$BASEDIR/scripts/change-password.php" admin "$ADMIN_PWD" +php "$BASEDIR/bin/opus4" account:setpwd admin -p "$ADMIN_PWD" # # Configure Solr connection diff --git a/build.xml b/build.xml index 02445f212..43962e401 100644 --- a/build.xml +++ b/build.xml @@ -316,9 +316,11 @@ - - + + + + diff --git a/library/Application/Console/Admin/ChangePasswordCommand.php b/library/Application/Console/Admin/ChangePasswordCommand.php new file mode 100644 index 000000000..8b1423ac5 --- /dev/null +++ b/library/Application/Console/Admin/ChangePasswordCommand.php @@ -0,0 +1,128 @@ +--password option can be used to provide a new password without interaction, however this should be used carefully, since the password won't be hidden. +EOT; + + $this->setName('account:setpwd') + ->setDescription('Set user password') + ->setHelp($help) + ->addArgument( + self::ARGUMENT_USER, + InputArgument::REQUIRED, + 'User login' + ) + ->addOption( + self::OPTION_PASSWORD, + '-p', + InputOption::VALUE_REQUIRED, + 'New password' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $formatter = new FormatterHelper(); + + $login = $input->getArgument(self::ARGUMENT_USER); + + try { + $account = Account::fetchAccountByLogin($login); + } catch (SecurityException $e) { + $formatted = $formatter->formatBlock('User not found', 'error'); + $output->writeln($formatted); + return self::FAILURE; + } + + $password = $input->getOption(self::OPTION_PASSWORD); + + if ($password === null) { + $helper = new QuestionHelper(); + + $question = new Question('New password: '); + $question->setHidden(true); + $question->setHiddenFallback(false); + $password = $helper->ask($input, $output, $question); + + $question = new Question('Confirm password: '); + $question->setHidden(true); + $question->setHiddenFallback(false); + $confirm = $helper->ask($input, $output, $question); + + if ($password !== $confirm) { + $formatted = $formatter->formatBlock('Passwords do not match.', 'error'); + $output->writeln($formatted); + return self::FAILURE; + } + } + + try { + $account->setPassword($password)->store(); + } catch (SecurityException $e) { + $formatted = $formatter->formatBlock('Setting password failed', 'error'); + $output->writeln($formatted); + return self::FAILURE; + } + + $output->writeln('Password successfully changed.'); + + return self::SUCCESS; + } +} diff --git a/library/Application/Console/App.php b/library/Application/Console/App.php index 901f6b926..4dfd0eef0 100644 --- a/library/Application/Console/App.php +++ b/library/Application/Console/App.php @@ -74,6 +74,8 @@ public function __construct() $this->add(new Application_Console_Collection_MoveCommand()); $this->add(new Application_Console_Collection_RemoveCommand()); + $this->add(new Application_Console_Admin_ChangePasswordCommand()); + if (class_exists(TaskManager::class)) { /* Tasks commands do not work without the TaskManager. If the current PHP version is less than 7.4 diff --git a/scripts/change-password.php b/scripts/change-password.php deleted file mode 100644 index 2a717dd1e..000000000 --- a/scripts/change-password.php +++ /dev/null @@ -1,49 +0,0 @@ -setPassword($password)->store(); From 6d5fa2dcc79db8c9f11d68dd49ca4a6e27d26f30 Mon Sep 17 00:00:00 2001 From: Jens Schwidder Date: Tue, 5 May 2026 19:18:06 +0200 Subject: [PATCH 04/10] #867 Using database for editable configuration (#1474) * #867 Use database for editable config options * #867 Update OPUS 4 internal version * #867 Fixed validation tests * #867 Remove useless use statement * #867 Added to release notes * #867 Use section name as fallback if translation is missing * #867 Use activatedLanguages for UI language option * #867 Fix validation bug if no language is selected * #867 Fix only using active language --- RELEASE_NOTES.md | 18 ++++ application/configs/options.yml | 9 ++ composer.json | 4 +- db/masterdata/022-set-opus-version.sql | 2 +- library/Application/Bootstrap.php | 15 +++- .../Form/Element/SupportedLanguages.php | 2 +- .../admin/controllers/ConfigController.php | 5 +- modules/admin/forms/Configuration.php | 83 +++++++++++++------ modules/admin/language/config.tmx | 27 ++++-- modules/admin/models/Option.php | 80 +++++++++++------- modules/admin/models/Options.php | 35 +++----- modules/admin/models/options.json | 18 ---- .../setup/controllers/LanguageController.php | 8 +- public/index.php | 8 -- scripts/update/021-Move-config-xml-to-db.php | 76 +++++++++++++++++ .../modules/admin/forms/ConfigurationTest.php | 25 ++++-- tests/modules/admin/models/OptionTest.php | 12 +-- tests/modules/admin/models/OptionsTest.php | 2 +- 18 files changed, 289 insertions(+), 140 deletions(-) create mode 100644 application/configs/options.yml delete mode 100644 modules/admin/models/options.json create mode 100755 scripts/update/021-Move-config-xml-to-db.php diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 65a684b40..74dead2df 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,24 @@ ## Release 4.10 - 2026-05-12 +### Update + +Das Updateskript muss ausgeführt werden, da es Änderungen an der Datenbank und +zusätzliche Updateschritte gibt. + +### Konfiguration + +Bislang sind nur wenige Optionen in der Administration editierbar. Diese wurde +bisher in `application/configs/config.xml` gespeichert. Dafür gibt es jetzt eine +Tabelle in der Datenbank. Beim Update wird der Inhalt von `config.xml` in die +Datenbank übertragen und die Datei dann (optional) gelöscht. + +Die editierbaren Optionen werden nun in `application/configs/options.yml` +definiert. Generell kann die Liste lokal erweitert werden. Im Standard werden +im Laufe der Zeit mehr Optionen in der Weboberfläche verfügbar gemacht werden. + +TODO Link to documentation + ### RSS-Links RSS-Links können nun ausgeblendet werden. Sie werden automatisch ausgeblendet, diff --git a/application/configs/options.yml b/application/configs/options.yml new file mode 100644 index 000000000..4652309ad --- /dev/null +++ b/application/configs/options.yml @@ -0,0 +1,9 @@ +searchengine.solr.parameterDefaults.rows: + type: int + section: searching + options: + min: 10 + +browsing.series.sortByTitle: + type: bool + section: browsing diff --git a/composer.json b/composer.json index 787b8eaa2..2de75308e 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "opus4/zf1-future": "1.25.*", "jpgraph/jpgraph": "dev-master", "opus4-repo/opus4-common": "^4.9", - "opus4-repo/framework": "^4.9", + "opus4-repo/framework": "dev-master as 4.10", "opus4-repo/search": "^4.9", "opus4-repo/opus4-bibtex": "^4.9", "opus4-repo/opus4-import": "^4.9", @@ -32,7 +32,7 @@ "opus4-repo/opus4-job": "^4.9", "opus4-repo/opus4-security": "^4.9", "opus4-repo/opus4-sword": "^4.9", - "opus4-repo/opus4-app-common": "^4.9", + "opus4-repo/opus4-app-common": "dev-main as 4.10", "opus4-repo/opus4-deepgreen": "^4.9", "components/jquery": "3.4.*", "components/jqueryui": "1.12.*", diff --git a/db/masterdata/022-set-opus-version.sql b/db/masterdata/022-set-opus-version.sql index 9af826166..9c81e0eb0 100644 --- a/db/masterdata/022-set-opus-version.sql +++ b/db/masterdata/022-set-opus-version.sql @@ -11,7 +11,7 @@ START TRANSACTION; -- Set internal OPUS version (for controlling updates) TRUNCATE TABLE `opus_version`; -INSERT INTO `opus_version` (`version`) VALUES (20); +INSERT INTO `opus_version` (`version`) VALUES (21); COMMIT; diff --git a/library/Application/Bootstrap.php b/library/Application/Bootstrap.php index 4ed1957ab..d3b1d2783 100644 --- a/library/Application/Bootstrap.php +++ b/library/Application/Bootstrap.php @@ -30,9 +30,11 @@ */ use Opus\App\Common\Configuration; +use Opus\Common\Config; use Opus\Common\Log\LogService; use Opus\Common\Repository; use Opus\Db\DatabaseBootstrap; +use Opus\Db2\Configuration as ConfigurationDatabase; use Opus\Search\Plugin\Index; /** @@ -258,7 +260,7 @@ protected function _setupPageCache() */ protected function _initTranslation() { - $this->bootstrap(['Configuration', 'Session', 'Logging', 'ZendCache']); + $this->bootstrap(['Configuration', 'OnlineConfiguration', 'Session', 'Logging', 'ZendCache']); $logService = LogService::getInstance(); $logger = $logService->getLog('translation'); @@ -411,4 +413,15 @@ protected function _initIndexPlugin() // TODO this is a dependency on a specific implementation (refactor to remove) $cache::setIndexPluginClass(Index::class); } + + protected function _initOnlineConfiguration() + { + $this->bootstrap('Database'); + + $configuration = new ConfigurationDatabase(); + $onlineConfig = $configuration->getConfig(); + + $config = Config::get(); + $config->merge($onlineConfig); + } } diff --git a/library/Application/Form/Element/SupportedLanguages.php b/library/Application/Form/Element/SupportedLanguages.php index 00392334e..72cfbecc7 100644 --- a/library/Application/Form/Element/SupportedLanguages.php +++ b/library/Application/Form/Element/SupportedLanguages.php @@ -116,7 +116,7 @@ public function getLanguageOptions() */ public function setValue($value) { - if (! is_array($value)) { + if (! is_array($value) && $value !== null) { $values = array_map('trim', explode(',', $value)); } else { $values = $value; diff --git a/modules/admin/controllers/ConfigController.php b/modules/admin/controllers/ConfigController.php index 167eace80..032f5bf15 100644 --- a/modules/admin/controllers/ConfigController.php +++ b/modules/admin/controllers/ConfigController.php @@ -29,8 +29,6 @@ * @license http://www.gnu.org/licenses/gpl.html General Public License */ -use Opus\App\Common\Configuration; - class Admin_ConfigController extends Application_Controller_Action { public function indexAction() @@ -48,8 +46,7 @@ public function indexAction() case Admin_Form_Configuration::RESULT_SAVE: if ($form->isValid($data)) { $config = new Zend_Config([], true); - $form->updateModel($config); - Configuration::save($config); + $form->updateModel($config); // TODO $config object is not needed } else { break; } diff --git a/modules/admin/forms/Configuration.php b/modules/admin/forms/Configuration.php index b65dcbd60..8d850fa7b 100644 --- a/modules/admin/forms/Configuration.php +++ b/modules/admin/forms/Configuration.php @@ -33,25 +33,20 @@ /** * Form for editing selected OPUS 4 configuration options. - * - * TODO Application_Form_Abstract should be enough (not ID element needed) */ class Admin_Form_Configuration extends Application_Form_Model_Abstract { /** * Prefix for translation keys of configuration options. * - * TODO wird auf von Admin_Model_Option verwendet + * TODO wird auch von Admin_Model_Option verwendet */ public const LABEL_TRANSLATION_PREFIX = 'admin_config_'; /** @var array Configured options for form. */ private $options; - /** - * @param null|Zend_Config $config - */ - public function __construct($config = null) + public function __construct(?array $config = null) { if ($config !== null) { $options = new Admin_Model_Options($config); @@ -73,19 +68,27 @@ public function init() $this->options = $options->getOptions(); } - foreach ($this->options as $name => $option) { + foreach ($this->options as $option) { $section = $option->getSection(); + $elementOptions = $option->getOptions(); + + $translator = $this->getTranslator(); + + if ($translator->isTranslated($option->getDescription())) { + $elementOptions['description'] = $option->getDescription(); + } + + if ($translator->isTranslated($option->getLabel())) { + $elementOptions['label'] = $option->getLabel(); + } else { + $elementOptions['label'] = $option->getKey(); + } + $element = $this->createElement( $option->getElementType(), - $name, - array_merge( - [ - 'label' => $option->getLabel(), - 'description' => $option->getDescription(), - ], - $option->getOptions() - ) + $option->getElementId(), + $elementOptions ); $this->addElement($element); @@ -95,6 +98,8 @@ public function init() $this->removeElement(self::ELEMENT_MODEL_ID); $this->setAttrib('class', 'admin_config'); + + $this->sortSections(); } /** @@ -104,9 +109,9 @@ public function init() */ public function populateFromModel($config) { - foreach ($this->options as $name => $option) { + foreach ($this->options as $option) { $value = Configuration::getValueFromConfig($config, $option->getKey()); - $this->getElement($name)->setValue($value); + $this->getElement($option->getElementId())->setValue($value); } } @@ -117,15 +122,18 @@ public function populateFromModel($config) */ public function updateModel($config) { - foreach ($this->options as $name => $option) { - $value = $this->getElement($name)->getValue(); + foreach ($this->options as $option) { + $value = $this->getElement($option->getElementId())->getValue(); // TODO move into Admin_Model_Option? if (is_array($value)) { $value = implode(',', $value); } + if (strlen(trim($value)) === 0) { + $value = null; + } - Configuration::setValueInConfig($config, $option->getKey(), $value); + $option->setValue($value); } } @@ -134,20 +142,25 @@ public function updateModel($config) * * If necessary a new display group is created. * - * @param Zend_Form_Element $element Form element - * @param string $section Name of section * @throws Zend_Form_Exception */ - public function addElementToSection($element, $section) + public function addElementToSection(Zend_Form_Element $element, string $section): void { $group = $this->getDisplayGroup($section); + $translator = $this->getTranslator(); + $sectionKey = self::LABEL_TRANSLATION_PREFIX . 'section_' . $section; + + if (! $translator->isTranslated($sectionKey)) { + $sectionKey = ucfirst($section); + } + if ($group === null) { $this->addDisplayGroup( [$element], $section, [ - 'legend' => self::LABEL_TRANSLATION_PREFIX . 'section_' . $section, + 'legend' => $sectionKey, 'decorators' => ['FormElements', 'Fieldset'], ] ); @@ -155,4 +168,24 @@ public function addElementToSection($element, $section) $group->addElement($element); } } + + public function sortSections(): void + { + $groups = $this->getDisplayGroups(); + + $names = array_keys($groups); + unset($names['actions']); + + sort($names); + + $sorted = []; + + foreach ($names as $section) { + $sorted[$section] = $groups[$section]; + } + + $sorted['actions'] = $groups['actions']; + + $this->setDisplayGroups($sorted); + } } diff --git a/modules/admin/language/config.tmx b/modules/admin/language/config.tmx index 98bb735bb..1211c5130 100644 --- a/modules/admin/language/config.tmx +++ b/modules/admin/language/config.tmx @@ -26,7 +26,7 @@ - + Search @@ -53,25 +53,34 @@ + + + Languages + + + Sprachen + + + - Supported Languages + User Interface Languages - Unterstützte Sprachen + Weboberfläche Sprachen - Languages for the user interface. Select at least one. + Languages for the user interface. Select at least one (e.g. 'en'). - Sprachen für die Benutzeroberfläche. Mindestens eine auswählen. + Sprachen für die Benutzeroberfläche. Mindestens eine angeben (z.B. 'de'). - + Default search results/page @@ -80,7 +89,7 @@ - + Number of results that should be display on a single page. @@ -89,7 +98,7 @@ - + Sort series by title @@ -98,7 +107,7 @@ - + Activates alphabetical sorting of series in browsing list and publish form. diff --git a/modules/admin/models/Option.php b/modules/admin/models/Option.php index 30f61032f..3ad0af9ba 100644 --- a/modules/admin/models/Option.php +++ b/modules/admin/models/Option.php @@ -29,58 +29,53 @@ * @license http://www.gnu.org/licenses/gpl.html General Public License */ +use Opus\Db2\Configuration; + class Admin_Model_Option extends Application_Model_Abstract { /** @var string Name of configuration option. */ - private $name; + private $key; /** @var array Parameters for option. */ private $config; - /** - * @param string $name Name of option - * @param array $config Parameters for option - */ - public function __construct($name, $config) + public function __construct(string $key, array $config) { - $this->name = $name; + $this->key = $key; $this->config = $config; } /** - * @return string + * Returns option key. */ - public function getName() + public function getKey(): string { - return $this->name; + return $this->key; } /** - * @return string + * Returns translation key for option label. */ - public function getKey() + public function getLabel(): string { - return $this->config['key']; + return Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . $this->key; } /** - * Returns label name for option. - * - * @return string + * Returns translation key for option description. */ - public function getLabel() + public function getDescription(): string { - return Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . $this->name; + return Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . $this->key . '_description'; } - /** - * Returns translation key for option description. - * - * @return string - */ - public function getDescription() + public function getType(): string { - return Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . $this->name . '_description'; + if (isset($this->config['type'])) { + return $this->config['type']; + } + + return 'text'; } /** @@ -90,13 +85,27 @@ public function getDescription() */ public function getElementType() { - if (isset($this->config['type'])) { - $type = $this->config['type']; - } else { + $type = $this->getType(); + + if (null === $type) { $type = 'text'; } - return $type; + switch ($this->getType()) { + case 'int': + return 'number'; + case 'bool': + return 'checkbox'; + case 'string': + return 'text'; + default: + return $type; + } + } + + public function getElementId(): string + { + return str_replace('.', '_', $this->getKey()); } /** @@ -128,4 +137,17 @@ public function getOptions() return []; } } + + public function getValue(): string + { + $configuration = new Configuration(); + return $configuration->getOption($this->getKey()); + } + + public function setValue(?string $value): self + { + $configuration = new Configuration(); + $configuration->setOption($this->getKey(), $value); + return $this; + } } diff --git a/modules/admin/models/Options.php b/modules/admin/models/Options.php index 6d07df5de..c2a1f20f3 100644 --- a/modules/admin/models/Options.php +++ b/modules/admin/models/Options.php @@ -38,54 +38,41 @@ class Admin_Model_Options extends Application_Model_Abstract /** * Path to options configuration. */ - public const OPTIONS_CONFIG_FILE = '/modules/admin/models/options.json'; + public const OPTIONS_CONFIG_FILE = '/application/configs/options.yml'; /** @var array Option objects. */ private $options; - /** @var Zend_Config */ + /** @var array */ private $config; - /** - * @param Zend_Config|null $config - * - * TODO allow providing Zend_Config object - */ - public function __construct($config = null) + public function __construct(?array $config = null) { - if ($config !== null && is_array($config)) { - $this->config = new Zend_Config($config); - } + $this->config = $config; } /** * Returns options configuration from file. - * - * @return array */ - public function getOptions() + public function getOptions(): array { if ($this->options === null) { $this->options = []; - $config = $this->getConfig(); - $options = $config->toArray(); - foreach ($options as $name => $parameters) { - $this->options[$name] = new Admin_Model_Option($name, $parameters); + $config = $this->getConfig(); + + foreach ($config as $optionKey => $parameters) { + $this->options[$optionKey] = new Admin_Model_Option($optionKey, $parameters); } } return $this->options; } - /** - * @return Zend_Config - * @throws Zend_Config_Exception - */ - public function getConfig() + public function getConfig(): array { if ($this->config === null) { - $this->config = new Zend_Config_Json(APPLICATION_PATH . self::OPTIONS_CONFIG_FILE); + $this->config = yaml_parse_file(APPLICATION_PATH . self::OPTIONS_CONFIG_FILE); } return $this->config; diff --git a/modules/admin/models/options.json b/modules/admin/models/options.json deleted file mode 100644 index 24565c4a7..000000000 --- a/modules/admin/models/options.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "maxSearchResults": { - "key": "searchengine.solr.parameterDefaults.rows", - "type": "number", - "section": "searchopt", - "options": { - "min": 10, - "required": "true" - } - }, - "seriesSortByTitle": { - "key": "browsing.series.sortByTitle", - "type": "checkbox", - "section": "browsing" - } -} - - diff --git a/modules/setup/controllers/LanguageController.php b/modules/setup/controllers/LanguageController.php index 537f2adca..7409d9fa5 100644 --- a/modules/setup/controllers/LanguageController.php +++ b/modules/setup/controllers/LanguageController.php @@ -29,8 +29,6 @@ * @license http://www.gnu.org/licenses/gpl.html General Public License */ -use Opus\App\Common\Configuration; - /** * TODO rename controller to TranslationController * TODO sorting using table header @@ -494,8 +492,7 @@ public function settingsAction() // TODO provide form with options config $form = new Admin_Form_Configuration([ - 'supportedLanguages' => [ - 'key' => 'activatedLanguages', + 'activatedLanguages' => [ 'type' => 'supportedLanguages', 'section' => 'general', ], @@ -513,7 +510,6 @@ public function settingsAction() if ($form->isValid($data)) { $config = new Zend_Config([], true); $form->updateModel($config); - Configuration::save($config); } else { break; } @@ -529,7 +525,7 @@ public function settingsAction() $form->populateFromModel($config); - $element = $form->getElement('supportedLanguages'); + $element = $form->getElement('activatedLanguages'); if (! isset($config->activatedLanguages)) { $element->setValue($config->supportedLanguages); } diff --git a/public/index.php b/public/index.php index 9d0bc9982..250fdf0cf 100644 --- a/public/index.php +++ b/public/index.php @@ -71,14 +71,6 @@ $config->merge($localConfig); -// configuration file that is modified via application user interface -if (is_readable(APPLICATION_PATH . '/application/configs/config.xml')) { - $onlineConfig = new Zend_Config_Xml( - APPLICATION_PATH . '/application/configs/config.xml' - ); - $config->merge($onlineConfig); -} - // Create application, bootstrap, and run $application = new Zend_Application(APPLICATION_ENV, $config); diff --git a/scripts/update/021-Move-config-xml-to-db.php b/scripts/update/021-Move-config-xml-to-db.php new file mode 100755 index 000000000..1e1cd8c32 --- /dev/null +++ b/scripts/update/021-Move-config-xml-to-db.php @@ -0,0 +1,76 @@ +writeln("Importing '{$configPath}' into database."); + +if (! is_readable($configPath)) { + $output->writeln('File \'' . $configPath . '\' is not readable.'); + exit(); +} + +// Import options from config.xml +$config = new Zend_Config_Xml($configPath); +$configDatabase = new Configuration(); +$configDatabase->import($config, true); + +// Show imported options +$imported = $configDatabase->getConfig(); +if (count($imported) > 0) { + $options = $configDatabase->arr2ini($imported->toArray()); + $output->writeln('Imported options:'); + foreach ($options as $key => $value) { + $output->writeln(' ' . $key . ' = ' . $value); + } +} + +// Remove config.xml file +$helper = new Application_Update_Helper(); + +if ($helper->askYesNo("Delete '{$configPath}' file [Y/n]?", true)) { + $output->write("Removeing '{$configPath}' file ... "); + $filesystem = new Filesystem(); + $filesystem->remove($configPath); + $output->writeln('done'); +} diff --git a/tests/modules/admin/forms/ConfigurationTest.php b/tests/modules/admin/forms/ConfigurationTest.php index 3a3a844ed..1a24799e8 100644 --- a/tests/modules/admin/forms/ConfigurationTest.php +++ b/tests/modules/admin/forms/ConfigurationTest.php @@ -29,6 +29,8 @@ * @license http://www.gnu.org/licenses/gpl.html General Public License */ +use Opus\Db2\Configuration; + class Admin_Form_ConfigurationTest extends ControllerTestCase { /** @var string[] */ @@ -51,7 +53,7 @@ public function testPopulateFromModel() 'searchengine' => ['solr' => ['parameterDefaults' => ['rows' => '20']]], ])); // searchengine.solr.parameterDefaults.rows - $element = $form->getElement('maxSearchResults'); + $element = $form->getElement('searchengine_solr_parameterDefaults_rows'); $this->assertNotNull($element); $this->assertEquals(20, $element->getValue()); @@ -61,13 +63,16 @@ public function testUpdateModel() { $form = new Admin_Form_Configuration(); - $form->getElement('maxSearchResults')->setValue(15); + $form->getElement('searchengine_solr_parameterDefaults_rows')->setValue(15); $config = new Zend_Config([], true); $form->updateModel($config); - $this->assertEquals(15, $config->searchengine->solr->parameterDefaults->rows); + $configDatabase = new Configuration(); + $this->assertEquals(15, $configDatabase->getOption( + 'searchengine.solr.parameterDefaults.rows' + )); } public function testValidationSuccess() @@ -75,8 +80,7 @@ public function testValidationSuccess() $form = new Admin_Form_Configuration(); $this->assertTrue($form->isValid([ - 'supportedLanguages' => ['de'], - 'maxSearchResults' => '10', + 'searchengine_solr_parameterDefaults_rows' => '10', ])); } @@ -85,7 +89,16 @@ public function testValidationFailure() $form = new Admin_Form_Configuration(); $this->assertFalse($form->isValid([ - 'supportedLanguages' => ['ru'], + 'searchengine_solr_parameterDefaults_rows' => '5', ])); } + + public function testLoadOptionsConfig() + { + $optionsConfigPath = APPLICATION_PATH . '/application/configs/options.yml'; + + $options = yaml_parse_file($optionsConfigPath); + + $this->assertIsArray($options); + } } diff --git a/tests/modules/admin/models/OptionTest.php b/tests/modules/admin/models/OptionTest.php index da7ec993d..03ad499ad 100644 --- a/tests/modules/admin/models/OptionTest.php +++ b/tests/modules/admin/models/OptionTest.php @@ -38,8 +38,7 @@ public function setUp(): void { parent::setUp(); - $this->model = new Admin_Model_Option('test', [ - 'key' => 'supportedLanguages', + $this->model = new Admin_Model_Option('supportedLanguages', [ 'type' => 'number', 'section' => 'search', 'options' => [ @@ -86,20 +85,23 @@ public function testGetDefaultElementType() public function testGetLabel() { - $this->assertEquals(Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . 'test', $this->model->getLabel()); + $this->assertEquals( + Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . 'supportedLanguages', + $this->model->getLabel() + ); } public function testGetDescription() { $this->assertEquals( - Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . 'test_description', + Admin_Form_Configuration::LABEL_TRANSLATION_PREFIX . 'supportedLanguages_description', $this->model->getDescription() ); } public function testGetName() { - $this->assertEquals('test', $this->model->getName()); + $this->assertEquals('supportedLanguages', $this->model->getKey()); } public function testGetKey() diff --git a/tests/modules/admin/models/OptionsTest.php b/tests/modules/admin/models/OptionsTest.php index e74f4c855..aaa63be32 100644 --- a/tests/modules/admin/models/OptionsTest.php +++ b/tests/modules/admin/models/OptionsTest.php @@ -50,7 +50,7 @@ public function testGetOptions() foreach ($options as $name => $option) { $this->assertInstanceOf(Admin_Model_Option::class, $option); - $this->assertEquals($name, $option->getName()); + $this->assertEquals($name, $option->getKey()); } } } From fb866796df80de1b87d64143b95ad2402c901e40 Mon Sep 17 00:00:00 2001 From: Jens Schwidder Date: Mon, 11 May 2026 10:16:04 +0200 Subject: [PATCH 05/10] #792 Sprachverwaltung entfernen (#1468) * #792 Remove languages from admin menu * #792 Remove Language form and elements * #792 Use opus4-i18n for Language form element * #792 Remove languages from default SQL * #792 Remove Language form translations * #792 Remove LanguageController * #792 Remove Languages from access control tests * #792 Use new Languages class * #792 Old Language class not used anymore * #792 Use dev dependencies * #1469 Use lang ref name if not translated * #792 If no active languages configured, use all * #792 Fixed coding style * #1469 Do not use local lang translations * #792 Log missing languages * #1469 Move ref-name fallback to opus4-i18n * #792 Use part2_b for default languages * #792 Support adding local languages * #866 Normalize lang ID for form element * #792 Depend on dev framework * #792 Do not reset configuration table Database update creates options, that are otherwise lost. * #792 Add prefix i18n to language options * #792 Removed old languages translation test * #792 Removed unused use statement * #1477 Removing resource_languages * #1477 Make update script executable * #1477 Remove resource_languages * #792 Added languages config to admin UI * #792 Use proper ISO 639 codes for deu, fra * #867 Add instructions for defining options * #1460 Updated release notes * #792 Fixed trim with null * #792 Updated unit test --- RELEASE_NOTES.md | 29 +- application/configs/application.ini | 6 +- application/configs/navigationModules.xml | 44 --- application/configs/options.yml | 35 +++ composer.json | 5 +- db/masterdata/002_create_languages.sql | 44 --- db/masterdata/022-set-opus-version.sql | 2 +- .../Controller/Action/Helper/Translation.php | 8 +- library/Application/Form/Element/Language.php | 81 ++++- .../Application/Form/Element/LanguageType.php | 44 --- library/Application/Security/AclProvider.php | 1 - .../View/Helper/DocumentAbstract.php | 4 +- .../Application/View/Helper/DocumentTitle.php | 4 +- .../View/Helper/LanguageWebForm.php | 5 +- .../admin/controllers/LanguageController.php | 57 ---- modules/admin/forms/Configuration.php | 2 +- modules/admin/forms/Document/Tags.php | 4 +- modules/admin/forms/Language.php | 95 ------ modules/admin/language/config.tmx | 29 +- modules/admin/language/language.tmx | 138 -------- modules/admin/language/resources.tmx | 9 - modules/default/language/fieldvalues.tmx | 99 ------ modules/oai/models/DefaultServer.php | 5 +- .../scripts/index/prefixes/XMetaDissPlus.xslt | 8 +- .../views/scripts/index/prefixes/oai_dc.xslt | 8 +- scripts/update/021-Move-config-xml-to-db.php | 2 +- .../update/022-Remove-resource_languages.php | 25 +- .../Action/Helper/TranslationTest.php | 11 - .../Form/Element/LanguageScopeTest.php | 83 ----- .../Application/Form/Element/LanguageTest.php | 7 + .../Form/Element/LanguageTypeTest.php | 79 ----- .../Application/Security/AccessTest.php | 1 - tests/modules/MainMenuTest.php | 1 - .../DnbinstituteControllerTest.php | 2 +- .../controllers/LanguageControllerTest.php | 295 ------------------ tests/modules/admin/forms/LanguageTest.php | 175 ----------- tests/modules/admin/models/OptionsTest.php | 2 +- tests/security/AdministratorRoleTest.php | 1 - tests/security/CollectionsAdminTest.php | 1 - tests/security/DocumentsAdminTest.php | 1 - tests/security/FullAdminTest.php | 1 - tests/security/FullAndPartialAdminTest.php | 1 - tests/security/LicencesAdminTest.php | 1 - tests/security/SecurityAdminTest.php | 1 - .../AccessModuleAdminOneResourceOnlyTest.php | 1 - .../support/AccessModuleSetupAndAdminTest.php | 1 - 46 files changed, 218 insertions(+), 1240 deletions(-) delete mode 100644 db/masterdata/002_create_languages.sql delete mode 100644 library/Application/Form/Element/LanguageType.php delete mode 100644 modules/admin/controllers/LanguageController.php delete mode 100644 modules/admin/forms/Language.php delete mode 100644 modules/admin/language/language.tmx rename library/Application/Form/Element/LanguageScope.php => scripts/update/022-Remove-resource_languages.php (78%) mode change 100644 => 100755 delete mode 100644 tests/library/Application/Form/Element/LanguageScopeTest.php delete mode 100644 tests/library/Application/Form/Element/LanguageTypeTest.php delete mode 100644 tests/modules/admin/controllers/LanguageControllerTest.php delete mode 100644 tests/modules/admin/forms/LanguageTest.php diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 74dead2df..fbc15967d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,10 +2,31 @@ ## Release 4.10 - 2026-05-12 -### Update +Der neue Release bringt insbesondere Veränderungen bei der Sprachverwaltung und +den Optionen, die in der Administration editierbar sind. + +### Update auf OPUS 4.10 + +Das Updateskript, `bin/update.sh`, muss ausgeführt werden, da es Änderungen an +der Datenbank und weitere Updateschritte gibt. + +- Tabelle `configuration` wird angelegt +- Tabelle `languages` wird entfernt +- Konfiguration in `config.xml` with in die Datenbank übertragen und die + Datei optional gelöscht +- Aktive Sprachen werden in neue Konfigurationsoptionen übernommen -Das Updateskript muss ausgeführt werden, da es Änderungen an der Datenbank und -zusätzliche Updateschritte gibt. +### Sprachverwaltung + +Die Sprachverwaltung wurde aus der Administration entfernt. Dafür werden jetzt +485 ISO 639 Sprachen im Standard unterstützt. Welche Sprachen in den Formularen +zur Auswahl stehen sollen, lässt sich über Optionen einstellen. + +Mehr dazu findet sich im OPUS 4 Handbuch unter +https://www.opus-repository.org/userdoc/admin/languages.html + +Beim Update werden die aktiven Sprachen automatisch in die neue Konfiguration +übernommen. ### Konfiguration @@ -18,8 +39,6 @@ Die editierbaren Optionen werden nun in `application/configs/options.yml` definiert. Generell kann die Liste lokal erweitert werden. Im Standard werden im Laufe der Zeit mehr Optionen in der Weboberfläche verfügbar gemacht werden. -TODO Link to documentation - ### RSS-Links RSS-Links können nun ausgeblendet werden. Sie werden automatisch ausgeblendet, diff --git a/application/configs/application.ini b/application/configs/application.ini index 9a5515e18..98c5f7dc9 100644 --- a/application/configs/application.ini +++ b/application/configs/application.ini @@ -61,7 +61,11 @@ db.debug = 0 ; LOCALE SETTINGS resources.locale.default = 'de' -; SUPPORTED LANGUAGES +; Languages selectable for documents +i18n.languages.active = deu, eng, fra, rus, spa, mul +i18n.languages.sortByName = 0 + +; SUPPORTED USER INTERFACE LANGUAGES supportedLanguages = de,en ;GENERAL SETTINGS diff --git a/application/configs/navigationModules.xml b/application/configs/navigationModules.xml index 9f00eac02..ce1a1cf7c 100644 --- a/application/configs/navigationModules.xml +++ b/application/configs/navigationModules.xml @@ -364,50 +364,6 @@ - - mvc - - admin_title_languages_description - group-cafelatte - admin - language - index - languages - - - mvc - - admin - language - show - - - - mvc - - admin - language - new - - - - mvc - - admin - language - edit - - - - mvc - - admin - language - delete - - - - mvc diff --git a/application/configs/options.yml b/application/configs/options.yml index 4652309ad..b4c1e7851 100644 --- a/application/configs/options.yml +++ b/application/configs/options.yml @@ -1,3 +1,28 @@ +# +# Defines options available in administration user interface (Settings->Options). +# +# The 'type' defines how the option appears in the form. Supported types are +# - string (default) +# - int +# - bool +# +# The 'section' allows grouping options. The names are arbitrary and are used as section +# labels if no translations for the section exist. +# +# 'options' can be used to pass additional options to the form element, for +# instance to increase the size. +# +# The translation keys for options use the following patterns: +# - admin_config_section_SECTIONNAME +# - admin_config_OPTIONKEY +# - admin_config_OPTIONKEY_description +# +# For instance: +# - admin_config_section_languages +# - admin_config_i18n.languages.active +# - admin_config_i18n.languages.active_description +# + searchengine.solr.parameterDefaults.rows: type: int section: searching @@ -7,3 +32,13 @@ searchengine.solr.parameterDefaults.rows: browsing.series.sortByTitle: type: bool section: browsing + +i18n.languages.active: + type: string + section: languages + options: + size: 60 + +i18n.languages.sortByName: + type: bool + section: languages diff --git a/composer.json b/composer.json index 2de75308e..3aa80256f 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ "ext-yaml": "*", "opus4/zf1-future": "1.25.*", "jpgraph/jpgraph": "dev-master", - "opus4-repo/opus4-common": "^4.9", - "opus4-repo/framework": "dev-master as 4.10", + "opus4-repo/opus4-common": "dev-master as 4.10", + "opus4-repo/framework": "dev-lang407 as 4.10", "opus4-repo/search": "^4.9", "opus4-repo/opus4-bibtex": "^4.9", "opus4-repo/opus4-import": "^4.9", @@ -34,6 +34,7 @@ "opus4-repo/opus4-sword": "^4.9", "opus4-repo/opus4-app-common": "dev-main as 4.10", "opus4-repo/opus4-deepgreen": "^4.9", + "opus4-repo/opus4-i18n": "dev-main as 4.10", "components/jquery": "3.4.*", "components/jqueryui": "1.12.*", "oomphinc/composer-installers-extender": "^2.0", diff --git a/db/masterdata/002_create_languages.sql b/db/masterdata/002_create_languages.sql deleted file mode 100644 index 6f44a3bd6..000000000 --- a/db/masterdata/002_create_languages.sql +++ /dev/null @@ -1,44 +0,0 @@ --- MySQL dump 10.11 --- --- Server version 5.0.67-0ubuntu6 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `languages` --- Based on http://sil.org/iso639-3/iso-639-3_20090210.tab --- - -LOCK TABLES `languages` WRITE; -/*!40000 ALTER TABLE `languages` DISABLE KEYS */; -INSERT INTO `languages` (`id`, `part2_b`, `part2_t`, `part1`, `scope`, `type`, `ref_name`, `comment`, `active`) VALUES -('1','ger','deu','de','I','L','German','',1), -('2','eng','eng','en','I','L','English','',1), -('3','ita','ita','it','I','L','Italian','',0), -('4','fre','fra','fr','I','L','French','',1), -('5','por','por','pt','I','L','Portuguese','',0), -('6','rus','rus','ru','I','L','Russian','',1), -('7','spa','spa','es','I','L','Spanish','',1), -('8','mul','mul','','I','L','Multiple languages','',1); -/*!40000 ALTER TABLE `languages` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2009-04-09 8:56:35 diff --git a/db/masterdata/022-set-opus-version.sql b/db/masterdata/022-set-opus-version.sql index 9c81e0eb0..d2316260f 100644 --- a/db/masterdata/022-set-opus-version.sql +++ b/db/masterdata/022-set-opus-version.sql @@ -11,7 +11,7 @@ START TRANSACTION; -- Set internal OPUS version (for controlling updates) TRUNCATE TABLE `opus_version`; -INSERT INTO `opus_version` (`version`) VALUES (21); +INSERT INTO `opus_version` (`version`) VALUES (22); COMMIT; diff --git a/library/Application/Controller/Action/Helper/Translation.php b/library/Application/Controller/Action/Helper/Translation.php index 17574d8c8..b92a9940c 100644 --- a/library/Application/Controller/Action/Helper/Translation.php +++ b/library/Application/Controller/Action/Helper/Translation.php @@ -31,7 +31,6 @@ use Opus\Common\Enrichment; use Opus\Document; -use Opus\Language; /** * Helper for handling translations. @@ -103,12 +102,7 @@ public function getKeyForField($modelName, $fieldName) $translationKey = $this->normalizeModelName($modelName) . '_' . $fieldName; return preg_replace('/Opus_Common_/', 'Opus_', $translationKey); // TODO LAMINAS fix keys } else { - switch ($modelName) { - case Language::class: - return $this->normalizeModelName($modelName) . '_' . $fieldName; - default: - return $fieldName; - } + return $fieldName; } } diff --git a/library/Application/Form/Element/Language.php b/library/Application/Form/Element/Language.php index acfe7e09f..bd65e7383 100644 --- a/library/Application/Form/Element/Language.php +++ b/library/Application/Form/Element/Language.php @@ -29,7 +29,10 @@ * @license http://www.gnu.org/licenses/gpl.html General Public License */ -use Opus\Common\Language; +use Opus\App\Common\Configuration; +use Opus\Common\Log; +use Opus\I18n\I18nException; +use Opus\I18n\Languages; /** * TODO override setLabel for more robust translation @@ -52,6 +55,23 @@ public function init() } } + /** + * @param string|null $value + * @return void + */ + public function setValue($value) + { + if ($value !== null) { + $language = Languages::getLanguage($value); + + if (null !== $language) { + $value = $language->getId(); + } + } + + parent::setValue($value); + } + /** * @return array */ @@ -65,15 +85,68 @@ public static function getLanguageList() /** * Setup language list. + * + * TODO reduce responsibilities of this function */ public static function initLanguageList() { + $config = Configuration::getInstance()->getConfig(); + + if (! isset($config->i18n->languages->active)) { + throw new Exception('no active languages configured'); + } + + $optionValue = $config->i18n->languages->active; + + if (strlen(trim($optionValue)) > 0) { + // Use configured languages + $activeLanguages = explode(',', $optionValue); + } else { + // Use all languages + $helper = new Languages(); + $activeLanguages = array_keys($helper->getAllAsArray()); + } + + $activeLanguages = array_filter($activeLanguages, function ($lang) { + return ! empty($lang); + }); + + if (isset($config->i18n->languages->local)) { + $localLanguages = $config->i18n->languages->local->toArray(); + $languages = new Languages(); + try { + $languages->addLanguages($localLanguages); + } catch (I18nException $ex) { + Log::get()->err('Error loading local languages: ' . $ex->getMessage()); + } + } + $translate = Application_Translate::getInstance(); + $locale = $translate->getLocale(); + $languages = []; - foreach (Language::getAllActiveTable() as $languageRow) { - $langId = $languageRow['part2_t']; - $languages[$langId] = $translate->translateLanguage($langId); + + foreach ($activeLanguages as $lang) { + $part2b = trim($lang); + $language = Languages::getLanguage($part2b); + + if ($language === null) { + Log::get()->err("Language '{$part2b}' not found"); + continue; + } + + $langId = $language->getPart2t(); + $translation = $language->getDisplayName($locale); + + // TODO support local translations + + $languages[$langId] = $translation; } + + if (isset($config->i18n->languages->sortByName) && filter_var($config->i18n->languages->sortByName, FILTER_VALIDATE_BOOLEAN)) { + asort($languages); + } + self::$languageList = $languages; } } diff --git a/library/Application/Form/Element/LanguageType.php b/library/Application/Form/Element/LanguageType.php deleted file mode 100644 index b00fafcea..000000000 --- a/library/Application/Form/Element/LanguageType.php +++ /dev/null @@ -1,44 +0,0 @@ -addMultiOption($value, 'Opus_Language_Type_Value_' . $value); - } - } -} diff --git a/library/Application/Security/AclProvider.php b/library/Application/Security/AclProvider.php index 678ba3519..3d7256192 100644 --- a/library/Application/Security/AclProvider.php +++ b/library/Application/Security/AclProvider.php @@ -65,7 +65,6 @@ class Application_Security_AclProvider 'licences', 'collections', 'series', - 'languages', 'statistics', 'institutions', 'enrichments', diff --git a/library/Application/View/Helper/DocumentAbstract.php b/library/Application/View/Helper/DocumentAbstract.php index 75b534ea7..5a0ba612b 100644 --- a/library/Application/View/Helper/DocumentAbstract.php +++ b/library/Application/View/Helper/DocumentAbstract.php @@ -30,7 +30,7 @@ */ use Opus\Common\DocumentInterface; -use Opus\Common\Language; +use Opus\I18n\Languages; /** * Helper for printing the abstract of a OPUS document. @@ -48,7 +48,7 @@ class Application_View_Helper_DocumentAbstract extends Application_View_Helper_D public function documentAbstract($document = null) { if ($this->isPreferUserInterfaceLanguage()) { - $language = Language::getPart2tForPart1(Application_Translate::getInstance()->getLocale()); + $language = Languages::getPart2t(Application_Translate::getInstance()->getLocale()); $abstract = $document->getMainAbstract($language); } else { diff --git a/library/Application/View/Helper/DocumentTitle.php b/library/Application/View/Helper/DocumentTitle.php index e62177eed..9672553d8 100644 --- a/library/Application/View/Helper/DocumentTitle.php +++ b/library/Application/View/Helper/DocumentTitle.php @@ -30,7 +30,7 @@ */ use Opus\Common\DocumentInterface; -use Opus\Common\Language; +use Opus\I18n\Languages; /** * Helper for printing the title of a OPUS document. @@ -55,7 +55,7 @@ class Application_View_Helper_DocumentTitle extends Application_View_Helper_Docu public function documentTitle($document = null) { if ($this->isPreferUserInterfaceLanguage()) { - $language = Language::getPart2tForPart1(Application_Translate::getInstance()->getLocale()); + $language = Languages::getPart2t(Application_Translate::getInstance()->getLocale()); $title = $document->getMainTitle($language); } else { diff --git a/library/Application/View/Helper/LanguageWebForm.php b/library/Application/View/Helper/LanguageWebForm.php index 2bad888c3..a102ee810 100644 --- a/library/Application/View/Helper/LanguageWebForm.php +++ b/library/Application/View/Helper/LanguageWebForm.php @@ -29,7 +29,7 @@ * @license http://www.gnu.org/licenses/gpl.html General Public License */ -use Opus\Common\Language; +use Opus\I18n\Languages; /** * View helper for tranform long language form in short language form (Part2 in Part1). @@ -55,8 +55,7 @@ class Application_View_Helper_LanguageWebForm extends Zend_View_Helper_Abstract public function languageWebForm($value) { if (! array_key_exists($value, $this->langCache)) { - $lang = Language::getPropertiesByPart2T($value); - $this->langCache[$value] = $lang['part1']; + $this->langCache[$value] = Languages::getPart1($value); } return $this->langCache[$value]; } diff --git a/modules/admin/controllers/LanguageController.php b/modules/admin/controllers/LanguageController.php deleted file mode 100644 index 52e7238ea..000000000 --- a/modules/admin/controllers/LanguageController.php +++ /dev/null @@ -1,57 +0,0 @@ -setFormClass(Admin_Form_Language::class); - parent::init(); - } - - /** - * @param LanguageInterface $model - * @return bool - */ - public function isDeletable($model) - { - return ! $model->isUsed(); - } -} diff --git a/modules/admin/forms/Configuration.php b/modules/admin/forms/Configuration.php index 8d850fa7b..3fb9d6375 100644 --- a/modules/admin/forms/Configuration.php +++ b/modules/admin/forms/Configuration.php @@ -129,7 +129,7 @@ public function updateModel($config) if (is_array($value)) { $value = implode(',', $value); } - if (strlen(trim($value)) === 0) { + if ($value !== null && strlen(trim($value)) === 0) { $value = null; } diff --git a/modules/admin/forms/Document/Tags.php b/modules/admin/forms/Document/Tags.php index b7ba242b5..e6ba9093c 100644 --- a/modules/admin/forms/Document/Tags.php +++ b/modules/admin/forms/Document/Tags.php @@ -30,7 +30,7 @@ */ use Opus\Common\DocumentInterface; -use Opus\Common\Language; +use Opus\I18m\Languages; /** * Unterformular fuer GND Subjects im Metadaten-Formular. @@ -186,7 +186,7 @@ public function processPost($data, $context) $lang = $data[self::ELEMENT_LANGUAGE]; if ($lang === null) { $translate = Application_Translate::getInstance(); - $lang = Language::getPart2tForPart1($translate->getLocale()); + $lang = Languages::getPart2t($translate->getLocale()); } $this->addMultipleSubjectsFromString($data[self::ELEMENT_SUBJECTS], $lang); return Admin_Form_Document::RESULT_SHOW; diff --git a/modules/admin/forms/Language.php b/modules/admin/forms/Language.php deleted file mode 100644 index b7519298a..000000000 --- a/modules/admin/forms/Language.php +++ /dev/null @@ -1,95 +0,0 @@ -setRemoveEmptyCheckbox(false); - $this->setLabelPrefix('Opus_Language_'); - $this->setUseNameAsLabel(true); - $this->setModelClass(Language::class); - - $this->addElement('checkbox', self::ELEMENT_ACTIVE); - $this->addElement('text', self::ELEMENT_REFNAME, ['required' => true]); - $this->addElement('text', self::ELEMENT_PART2T, ['required' => true]); - $this->addElement('text', self::ELEMENT_PART2B); - $this->addElement('text', self::ELEMENT_PART1); - $this->addElement('LanguageScope', self::ELEMENT_SCOPE); - $this->addElement('LanguageType', self::ELEMENT_TYPE); - $this->addElement('text', self::ELEMENT_COMMENT); - } - - /** - * @param LanguageInterface $language - */ - public function populateFromModel($language) - { - $this->getElement(self::ELEMENT_MODEL_ID)->setValue($language->getId()); - $this->getElement(self::ELEMENT_ACTIVE)->setValue($language->getActive()); - $this->getElement(self::ELEMENT_PART2B)->setValue($language->getPart2B()); - $this->getElement(self::ELEMENT_PART2T)->setValue($language->getPart2T()); - $this->getElement(self::ELEMENT_PART1)->setValue($language->getPart1()); - $this->getElement(self::ELEMENT_SCOPE)->setValue($language->getScope()); - $this->getElement(self::ELEMENT_TYPE)->setValue($language->getType()); - $this->getElement(self::ELEMENT_REFNAME)->setValue($language->getRefName()); - $this->getElement(self::ELEMENT_COMMENT)->setValue($language->getComment()); - } - - /** - * @param LanguageInterface $language - */ - public function updateModel($language) - { - $language->setActive($this->getElementValue(self::ELEMENT_ACTIVE)); - $language->setPart2B($this->getElementValue(self::ELEMENT_PART2B)); - $language->setPart2T($this->getElementValue(self::ELEMENT_PART2T)); - $language->setPart1($this->getElementValue(self::ELEMENT_PART1)); - $language->setScope($this->getElementValue(self::ELEMENT_SCOPE)); - $language->setType($this->getElementValue(self::ELEMENT_TYPE)); - $language->setRefName($this->getElementValue(self::ELEMENT_REFNAME)); - $language->setComment($this->getElementValue(self::ELEMENT_COMMENT)); - } -} diff --git a/modules/admin/language/config.tmx b/modules/admin/language/config.tmx index 1211c5130..0ca26d1ca 100644 --- a/modules/admin/language/config.tmx +++ b/modules/admin/language/config.tmx @@ -49,7 +49,7 @@ Browsing - Browsing + Browsen @@ -116,6 +116,33 @@ + + + Selectable languages + + + Auswählbare Sprachen + + + + + + Languages selectable for a document (ISO 639). + + + Sprachen, die für ein Dokument auswählbar sind (ISO 639). + + + + + + Sort alphabetical + + + Alphabetisch sortieren + + + diff --git a/modules/admin/language/language.tmx b/modules/admin/language/language.tmx deleted file mode 100644 index 9e8f4e1aa..000000000 --- a/modules/admin/language/language.tmx +++ /dev/null @@ -1,138 +0,0 @@ - - - - -
    -
    - - - - - - 639-2 Identifier (bibliographic code) - - - 639-2 Bezeichner (bibliografischer Code) - - - - - - 639-2 Identifier (terminology code) - - - 639-2 Bezeichner (Terminologie-Code) - - - - - - 639-1 identifier - - - 639-1 Bezeichnung - - - - - - Scope - - - Umfang - - - - - - Type - - - Typ - - - - - - Language Name - - - Sprache - - - - - - Comment - - - Kommentar - - - - - - Active - - - Aktiv - - - - - - Store Data - - - Daten abspeichern - - - - - - Edit Language - - - Sprache ändern - - - - - - New Language - - - Neue Sprache - - - - - - Admin Languages - - - Sprachen verwalten - - - - - - Show Language - - - Sprache anzeigen - - - - - - Delete Language - - - Sprache löschen - - - - - -
    diff --git a/modules/admin/language/resources.tmx b/modules/admin/language/resources.tmx index d2edd47f6..e33e644b7 100644 --- a/modules/admin/language/resources.tmx +++ b/modules/admin/language/resources.tmx @@ -34,15 +34,6 @@
    - - - Manage languages - - - Sprachen verwalten - - - Manage licences diff --git a/modules/default/language/fieldvalues.tmx b/modules/default/language/fieldvalues.tmx index 3b9c85651..47f4e9fc1 100644 --- a/modules/default/language/fieldvalues.tmx +++ b/modules/default/language/fieldvalues.tmx @@ -698,105 +698,6 @@ - - - - - - - - - - - - - - Individual - - - Individuell - - - - - - Macrolanguage - - - Macrosprache - - - - - - Special - - - Spezial - - - - - - - - - - - - - - - - - Ancient - - - Antik - - - - - - Constructed - - - Konstruiert - - - - - - Extinct - - - Ausgestorben - - - - - - Historical - - - Historisch - - - - - - Living - - - Lebend - - - - - - Special - - - Spezial - - - NULL diff --git a/modules/oai/models/DefaultServer.php b/modules/oai/models/DefaultServer.php index 501d71760..f9f03a2f0 100644 --- a/modules/oai/models/DefaultServer.php +++ b/modules/oai/models/DefaultServer.php @@ -335,7 +335,10 @@ protected function handleRequestIntern($oaiRequest, $requestUri) */ protected function setupProcessor() { - $this->proc->registerPHPFunctions('Opus\Common\Language::getLanguageCode'); + $this->proc->registerPHPFunctions([ + 'Opus\I18n\Languages::getPart1', + 'Opus\I18n\Languages::getPart2b', + ]); Application_Xslt::registerViewHelper($this->proc, $this->getViewHelpers()); $this->proc->setParameter('', 'urnResolverUrl', $this->getConfig()->urn->resolverUrl); $this->proc->setParameter('', 'doiResolverUrl', $this->getConfig()->doi->resolverUrl); diff --git a/modules/oai/views/scripts/index/prefixes/XMetaDissPlus.xslt b/modules/oai/views/scripts/index/prefixes/XMetaDissPlus.xslt index a85b7d963..dc146654e 100644 --- a/modules/oai/views/scripts/index/prefixes/XMetaDissPlus.xslt +++ b/modules/oai/views/scripts/index/prefixes/XMetaDissPlus.xslt @@ -152,7 +152,7 @@ - + @@ -254,7 +254,7 @@ - + @@ -270,7 +270,7 @@ - + @@ -369,7 +369,7 @@ - + diff --git a/modules/oai/views/scripts/index/prefixes/oai_dc.xslt b/modules/oai/views/scripts/index/prefixes/oai_dc.xslt index b47c5d058..7a2d4a415 100644 --- a/modules/oai/views/scripts/index/prefixes/oai_dc.xslt +++ b/modules/oai/views/scripts/index/prefixes/oai_dc.xslt @@ -138,7 +138,7 @@ - + @@ -202,7 +202,7 @@ - + @@ -218,7 +218,7 @@ - + @@ -362,7 +362,7 @@ - + diff --git a/scripts/update/021-Move-config-xml-to-db.php b/scripts/update/021-Move-config-xml-to-db.php index 1e1cd8c32..6fd9cbf53 100755 --- a/scripts/update/021-Move-config-xml-to-db.php +++ b/scripts/update/021-Move-config-xml-to-db.php @@ -53,7 +53,7 @@ // Import options from config.xml $config = new Zend_Config_Xml($configPath); $configDatabase = new Configuration(); -$configDatabase->import($config, true); +$configDatabase->import($config, false); // No reset because `MigrateLanguages` schema update creates options // Show imported options $imported = $configDatabase->getConfig(); diff --git a/library/Application/Form/Element/LanguageScope.php b/scripts/update/022-Remove-resource_languages.php old mode 100644 new mode 100755 similarity index 78% rename from library/Application/Form/Element/LanguageScope.php rename to scripts/update/022-Remove-resource_languages.php index faae0a3f3..44a261f6f --- a/library/Application/Form/Element/LanguageScope.php +++ b/scripts/update/022-Remove-resource_languages.php @@ -25,20 +25,21 @@ * along with OPUS; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * @copyright Copyright (c) 2008, OPUS 4 development team + * @copyright Copyright (c) 2026, OPUS 4 development team * @license http://www.gnu.org/licenses/gpl.html General Public License */ -class Application_Form_Element_LanguageScope extends Application_Form_Element_SelectWithNull -{ - public function init() - { - parent::init(); +require_once dirname(__FILE__) . '/../common/update.php'; - $values = ['Null', 'I', 'M', 'S']; +use Opus\SecurityStorage; +use Symfony\Component\Console\Output\ConsoleOutput; - foreach ($values as $value) { - $this->addMultiOption($value, 'Opus_Language_Scope_Value_' . $value); - } - } -} +$output = new ConsoleOutput(); + +$output->writeln(''); +$output->write('Removing \'resource_languages\' from role permissions...'); + +$security = new SecurityStorage(); +$security->removeResource('resource_languages'); + +$output->writeln(' done'); diff --git a/tests/library/Application/Controller/Action/Helper/TranslationTest.php b/tests/library/Application/Controller/Action/Helper/TranslationTest.php index b5e890a27..befa9ffdd 100644 --- a/tests/library/Application/Controller/Action/Helper/TranslationTest.php +++ b/tests/library/Application/Controller/Action/Helper/TranslationTest.php @@ -30,7 +30,6 @@ */ use Opus\Common\Identifier; -use Opus\Common\Language; use Opus\Common\Model\ModelException; use Opus\Common\Note; use Opus\Common\Person; @@ -281,14 +280,4 @@ public function testTranslationOfOpusEnrichmentFields($className) ); } } - - public function testTranslationOfLanguages() - { - $languages = Language::getAll(); - - foreach ($languages as $language) { - $key = $language->getPart2T(); - $this->assertNotEquals($key, $this->translate->translateLanguage($key)); - } - } } diff --git a/tests/library/Application/Form/Element/LanguageScopeTest.php b/tests/library/Application/Form/Element/LanguageScopeTest.php deleted file mode 100644 index 0b495cacb..000000000 --- a/tests/library/Application/Form/Element/LanguageScopeTest.php +++ /dev/null @@ -1,83 +0,0 @@ -keys = ['Null', 'I', 'M', 'S']; - - $this->formElementClass = 'Application_Form_Element_LanguageScope'; - $this->expectedDecorators = [ - 'ViewHelper', - 'Errors', - 'Description', - 'ElementHtmlTag', - 'LabelNotEmpty', - 'dataWrapper', - 'ElementHint', - ]; - $this->expectedDecoratorCount = count($this->expectedDecorators); - $this->staticViewHelper = 'viewFormSelect'; - parent::setUp(); - } - - public function testOptions() - { - $element = $this->getElement(); - - $options = $element->getMultiOptions(); - - $this->assertEquals(count($this->keys), count($options)); - - foreach ($this->keys as $key) { - $this->assertTrue(array_key_exists($key, $options), "Key '$key' is missing."); - } - } - - public function testOptionsTranslated() - { - $translator = Application_Translate::getInstance(); - - foreach ($this->keys as $key) { - $this->assertTrue( - $translator->isTranslated('Opus_Language_Scope_Value_' . $key), - "Key '$key' not translated." - ); - } - } -} diff --git a/tests/library/Application/Form/Element/LanguageTest.php b/tests/library/Application/Form/Element/LanguageTest.php index 31a4b9f4f..68c6e1b25 100644 --- a/tests/library/Application/Form/Element/LanguageTest.php +++ b/tests/library/Application/Form/Element/LanguageTest.php @@ -95,6 +95,13 @@ public function testValidation() $this->assertTrue($element->isValid('deu')); } + public function testAutoMapValueToLanguageId() + { + $element = $this->getElement(); + $element->setValue('ger'); + $this->assertEquals('deu', $element->getValue()); + } + public function testUnknownLanguage() { $this->markTestIncomplete(); diff --git a/tests/library/Application/Form/Element/LanguageTypeTest.php b/tests/library/Application/Form/Element/LanguageTypeTest.php deleted file mode 100644 index b71d49e30..000000000 --- a/tests/library/Application/Form/Element/LanguageTypeTest.php +++ /dev/null @@ -1,79 +0,0 @@ -keys = ['Null', 'A', 'C', 'E', 'H', 'L', 'S']; - $this->formElementClass = 'Application_Form_Element_LanguageType'; - $this->expectedDecorators = [ - 'ViewHelper', - 'Errors', - 'Description', - 'ElementHtmlTag', - 'LabelNotEmpty', - 'dataWrapper', - 'ElementHint', - ]; - $this->expectedDecoratorCount = count($this->expectedDecorators); - $this->staticViewHelper = 'viewFormSelect'; - parent::setUp(); - } - - public function testOptions() - { - $element = $this->getElement(); - - $options = $element->getMultiOptions(); - - $this->assertEquals(count($this->keys), count($options)); - - foreach ($this->keys as $key) { - $this->assertTrue(array_key_exists($key, $options), "Key '$key' is missing."); - } - } - - public function testOptionsTranslated() - { - $translator = Application_Translate::getInstance(); - - foreach ($this->keys as $key) { - $this->assertTrue($translator->isTranslated('Opus_Language_Type_Value_' . $key)); - } - } -} diff --git a/tests/library/Application/Security/AccessTest.php b/tests/library/Application/Security/AccessTest.php index 4c2a385ab..842c09b54 100644 --- a/tests/library/Application/Security/AccessTest.php +++ b/tests/library/Application/Security/AccessTest.php @@ -57,7 +57,6 @@ public function testLicenceAdminMenuFiltering() $this->assertNotQuery('//a[@href="/review"]'); $this->assertNotQuery('//a[@href="/admin/collectionroles"]'); $this->assertNotQuery('//a[@href="/admin/series"]'); - $this->assertNotQuery('//a[@href="/admin/language"]'); $this->assertNotQuery('//a[@href="/admin/index/security"]'); $this->assertNotQuery('//a[@href="/admin/dnbinstitute"]'); } diff --git a/tests/modules/MainMenuTest.php b/tests/modules/MainMenuTest.php index 3077d9049..668676270 100644 --- a/tests/modules/MainMenuTest.php +++ b/tests/modules/MainMenuTest.php @@ -78,7 +78,6 @@ public function testAdminControllerIndexPages() "/admin/collectionroles", "/admin/series", "/admin/security", - "/admin/language", "/admin/statistic", "/admin/oailink", "/admin/dnbinstitute", diff --git a/tests/modules/admin/controllers/DnbinstituteControllerTest.php b/tests/modules/admin/controllers/DnbinstituteControllerTest.php index beb251f73..bfe098340 100644 --- a/tests/modules/admin/controllers/DnbinstituteControllerTest.php +++ b/tests/modules/admin/controllers/DnbinstituteControllerTest.php @@ -342,7 +342,7 @@ public function testUserAccessToInstituteWithoutInstituteRights() $testRole = UserRole::new(); $testRole->setName('TestRole'); $testRole->appendAccessModule('admin'); - $testRole->appendAccessModule('resource_languages'); + $testRole->appendAccessModule('resource_collections'); $this->roleId = $testRole->store(); $userAccount = Account::new(); diff --git a/tests/modules/admin/controllers/LanguageControllerTest.php b/tests/modules/admin/controllers/LanguageControllerTest.php deleted file mode 100644 index ddd306b9d..000000000 --- a/tests/modules/admin/controllers/LanguageControllerTest.php +++ /dev/null @@ -1,295 +0,0 @@ -setController('language'); - parent::setUp(); - } - - /** - * @return LanguageInterface[] - */ - public function getModels() - { - return Language::getAll(); - } - - public function testShowAction() - { - $this->createsModels = true; - - $language = Language::new(); - - $language->setActive(true); - $language->setRefName('German'); - $language->setPart2T('deu'); - $language->setPart2B('ger'); - $language->setPart1('de'); - $language->setScope('I'); - $language->setType('L'); - $language->setComment('test comment'); - - $modelId = $language->store(); - - $this->dispatch('/admin/language/show/id/' . $modelId); - - $model = Language::get($modelId); - $model->delete(); - - $this->assertResponseCode(200); - $this->assertController('language'); - $this->assertAction('show'); - - $this->assertQueryContentRegex('div#Active', '/Yes|Ja/'); - $this->assertQueryContentContains('div#RefName', 'German'); - $this->assertQueryContentContains('div#Part2T', 'deu'); - $this->assertQueryContentContains('div#Part2B', 'ger'); - $this->assertQueryContentContains('div#Part1', 'de'); - $this->assertQueryContentRegex('div#Scope', '/Individual|Individuell/'); - $this->assertQueryContentRegex('div#Type', '/Living|Lebend/'); - $this->assertQueryContentContains('div#Comment', 'test comment'); - - // TODO $this->validateXHTML(); - } - - /** - * Test, ob Active Status für Wert false (0) angezeigt wird. - */ - public function testShowActiveValueForInactiveLicence() - { - $this->dispatch('/admin/language/show/id/3'); // Italian (3) is disabled - $this->assertResponseCode(200); - $this->assertController('language'); - $this->assertAction('show'); - - $this->assertQueryContentRegex('div#Active', '/No|Nein/'); - } - - public function testNewActionSave() - { - $this->createsModels = true; - - $post = [ - 'Active' => '1', - 'RefName' => 'German', - 'Part2T' => 'deu', - 'Part2B' => 'ger', - 'Part1' => 'de', - 'Scope' => 'I', - 'Type' => 'L', - 'Comment' => 'test comment', - 'Save' => 'Speichern', - ]; - - $this->getRequest()->setPost($post)->setMethod('POST'); - - $this->dispatch('/admin/language/new'); - - $this->assertRedirect('Should be a redirect to show action.'); - $this->assertRedirectRegex('/^\/admin\/language\/show/'); // Regex weil danach noch '/id/xxx' kommt - $this->verifyFlashMessage('controller_crud_save_success', self::MESSAGE_LEVEL_NOTICE); - - // Neue Lizenz anzeigen - $location = $this->getLocation(); - - $this->resetRequest(); - $this->resetResponse(); - - $this->dispatch($location); - $this->assertResponseCode(200); - - $this->assertQueryContentRegex('div#Active', '/Yes|Ja/'); - $this->assertQueryContentContains('div#RefName', 'German'); - $this->assertQueryContentContains('div#Part2T', 'deu'); - $this->assertQueryContentContains('div#Part2B', 'ger'); - $this->assertQueryContentContains('div#Part1', 'de'); - $this->assertQueryContentRegex('div#Scope', '/Individual|Individuell/'); - $this->assertQueryContentRegex('div#Type', '/Living|Lebend/'); - $this->assertQueryContentContains('div#Comment', 'test comment'); - } - - public function testNewActionCancel() - { - $this->createsModels = true; - - $modelCount = count($this->getModels()); - - $post = [ - 'RefName' => 'TestGerman', - 'Part2T' => 'tge', - 'Cancel' => 'Abbrechen', - ]; - - $this->getRequest()->setPost($post)->setMethod('POST'); - - $this->dispatch('/admin/language/new'); - - $this->assertRedirectTo('/admin/language', 'Should be a redirect to index action.'); - - $this->assertEquals( - $modelCount, - count(Language::getAll()), - 'Es sollte keine neue Sprache geben.' - ); - } - - /** - * Tests 'edit' action. - */ - public function testEditActionShowForm() - { - $this->dispatch('/admin/language/edit/id/3'); - $this->assertResponseCode(200); - $this->assertController('language'); - $this->assertAction('edit'); - - $this->assertQueryContentContains('div#RefName-element', 'Italian'); - $this->assertQuery('li.save-element'); - $this->assertQuery('li.cancel-element'); - $this->assertQueryCount('input#Id', 1); - } - - public function testEditActionSave() - { - $this->createsModels = true; - - $model = Language::new(); - - $model->setRefName('Test'); - $model->setPart2T('tst'); - - $modelId = $model->store(); - - $this->getRequest()->setMethod('POST')->setPost([ - 'Id' => $modelId, - 'Active' => '1', - 'RefName' => 'RefNameModified', - 'Part2T' => 'n2t', - 'Part2B' => 'tet', - 'Part1' => 'us', - 'Scope' => 'I', - 'Type' => 'L', - 'Comment' => 'test comment', - 'Save' => 'Speichern', - ]); - - $this->dispatch('/admin/language/edit'); - $this->assertRedirectTo('/admin/language/show/id/' . $modelId); - $this->verifyFlashMessage('controller_crud_save_success', self::MESSAGE_LEVEL_NOTICE); - - $model = Language::get($modelId); - - $this->assertEquals(1, $model->getActive()); - $this->assertEquals('RefNameModified', $model->getRefName()); - $this->assertEquals('n2t', $model->getPart2T()); - $this->assertEquals('tet', $model->getPart2B()); - $this->assertEquals('us', $model->getPart1()); - $this->assertEquals('I', $model->getScope()); - $this->assertEquals('L', $model->getType()); - $this->assertEquals('test comment', $model->getComment()); - } - - public function testEditActionCancel() - { - $this->createsModels = true; - - $model = Language::new(); - - $model->setRefName('Test'); - $model->setPart2T('tst'); - - $modelId = $model->store(); - - $this->getRequest()->setMethod('POST')->setPost([ - 'Id' => $modelId, - 'RefName' => 'RefNameModified', - 'Part2T' => 'tes', - 'Cancel' => 'Abbrechen', - ]); - - $this->dispatch('/admin/language/edit'); - $this->assertRedirectTo('/admin/language'); - - $model = Language::get($modelId); - - $this->assertEquals('Test', $model->getRefName()); - } - - public function testDeleteActionShowForm() - { - $this->useEnglish(); - - $this->dispatch('/admin/language/delete/id/3'); - - $this->assertQueryContentContains('legend', 'Delete Language'); - $this->assertQueryContentContains('span.displayname', 'Italian'); - $this->assertQuery('input#ConfirmYes'); - $this->assertQuery('input#ConfirmNo'); - } - - /** - * @return int - */ - public function createNewModel() - { - $model = Language::new(); - - $model->setRefName('TestLang'); - $model->setPart2T('lan'); - - return $model->store(); - } - - /** - * @param int $identifier - * @return LanguageInterface - * @throws NotFoundException - */ - public function getModel($identifier) - { - return Language::get($identifier); - } -} diff --git a/tests/modules/admin/forms/LanguageTest.php b/tests/modules/admin/forms/LanguageTest.php deleted file mode 100644 index 20094f569..000000000 --- a/tests/modules/admin/forms/LanguageTest.php +++ /dev/null @@ -1,175 +0,0 @@ -assertEquals(11, count($form->getElements())); - - $this->assertNotNull($form->getElement('Active')); - $this->assertNotNull($form->getElement('Part2B')); - $this->assertNotNull($form->getElement('Part2T')); - $this->assertNotNull($form->getElement('Part1')); - $this->assertNotNull($form->getElement('RefName')); - $this->assertNotNull($form->getElement('Comment')); - $this->assertNotNull($form->getElement('Scope')); - $this->assertNotNull($form->getElement('Type')); - - $this->assertNotNull($form->getElement('Save')); - $this->assertNotNull($form->getElement('Cancel')); - $this->assertNotNull($form->getElement('Id')); - } - - public function testPopulateFromModel() - { - $form = new Admin_Form_Language(); - - $language = Language::new(); - $language->setActive(true); - $language->setPart2B('ger'); - $language->setPart2T('deu'); - $language->setRefName('German'); - $language->setPart1('de'); - $language->setScope('M'); - $language->setType('H'); - $language->setComment('test comment'); - - $form->populateFromModel($language); - - $this->assertEquals(1, $form->getElement('Active')->getValue()); - $this->assertEquals('ger', $form->getElement('Part2B')->getValue()); - $this->assertEquals('deu', $form->getElement('Part2T')->getValue()); - $this->assertEquals('de', $form->getElement('Part1')->getValue()); - $this->assertEquals('German', $form->getElement('RefName')->getValue()); - $this->assertEquals('test comment', $form->getElement('Comment')->getValue()); - $this->assertEquals('M', $form->getElement('Scope')->getValue()); - $this->assertEquals('H', $form->getElement('Type')->getValue()); - $this->assertNull($form->getElement('Id')->getValue()); - } - - public function testPopulateFromModelWithId() - { - $form = new Admin_Form_Language(); - - $language = Language::get(2); - - $form->populateFromModel($language); - - $this->assertEquals(2, $form->getElement('Id')->getValue()); - } - - public function testUpdateModel() - { - $form = new Admin_Form_Language(); - - $form->getElement('Id')->setValue(99); - $form->getElement('Active')->setChecked(true); - $form->getElement('RefName')->setValue('German'); - $form->getElement('Part2B')->setValue('ger'); - $form->getElement('Part2T')->setValue('deu'); - $form->getElement('Part1')->setValue('de'); - $form->getElement('Comment')->setValue('Deutsch'); - $form->getElement('Scope')->setValue('I'); - $form->getElement('Type')->setValue('L'); - - $language = Language::new(); - - $form->updateModel($language); - - $this->assertNull($language->getId()); - $this->assertEquals(1, $language->getActive()); - $this->assertEquals('German', $language->getRefName()); - $this->assertEquals('ger', $language->getPart2B()); - $this->assertEquals('deu', $language->getPart2T()); - $this->assertEquals('de', $language->getPart1()); - $this->assertEquals('Deutsch', $language->getComment()); - $this->assertEquals('I', $language->getScope()); - $this->assertEquals('L', $language->getType()); - } - - public function testValidationEmptyPost() - { - $form = new Admin_Form_Language(); - - $this->assertFalse($form->isValid([])); - - $this->assertContains('isEmpty', $form->getErrors('RefName')); - $this->assertContains('isEmpty', $form->getErrors('Part2T')); - } - - public function testValidationEmptyFields() - { - $form = new Admin_Form_Language(); - - $this->assertFalse($form->isValid([ - 'RefName' => ' ', - 'Part2T' => ' ', - ])); - - $this->assertContains('isEmpty', $form->getErrors('RefName')); - $this->assertContains('isEmpty', $form->getErrors('Part2T')); - } - - public function testValidationInvalidValues() - { - $form = new Admin_Form_Language(); - - $this->assertFalse($form->isValid([ - 'RefName' => 'German', - 'Part2T' => 'deu', - 'Scope' => 'X', - 'Type' => 'Y', - ])); - - $this->assertNotContains('isEmpty', $form->getErrors('RefName')); - $this->assertNotContains('isEmpty', $form->getErrors('Part2T')); - $this->assertContains('notInArray', $form->getErrors('Scope')); - $this->assertContains('notInArray', $form->getErrors('Type')); - } - - public function testValidationTrue() - { - $form = new Admin_Form_Language(); - - $this->assertTrue($form->isValid([ - 'RefName' => 'German', - 'Part2T' => 'deu', - ])); - } -} diff --git a/tests/modules/admin/models/OptionsTest.php b/tests/modules/admin/models/OptionsTest.php index aaa63be32..d992c8a6a 100644 --- a/tests/modules/admin/models/OptionsTest.php +++ b/tests/modules/admin/models/OptionsTest.php @@ -46,7 +46,7 @@ public function testGetOptions() $options = $this->model->getOptions(); $this->assertIsArray($options); - $this->assertCount(2, $options); + $this->assertCount(4, $options); foreach ($options as $name => $option) { $this->assertInstanceOf(Admin_Model_Option::class, $option); diff --git a/tests/security/AdministratorRoleTest.php b/tests/security/AdministratorRoleTest.php index 41e6e3c90..9023202bb 100644 --- a/tests/security/AdministratorRoleTest.php +++ b/tests/security/AdministratorRoleTest.php @@ -62,7 +62,6 @@ public function testAdministratorRoleHasSuperPrivileges() $this->assertQuery('//a[@href="/admin/index/security"]'); $this->assertQuery('//a[@href="/admin/collectionroles"]'); $this->assertQuery('//a[@href="/admin/series"]'); - $this->assertQuery('//a[@href="/admin/language"]'); $this->assertQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertQuery('//a[@href="/admin/index/info"]'); $this->assertQuery('//a[@href="/review"]'); diff --git a/tests/security/CollectionsAdminTest.php b/tests/security/CollectionsAdminTest.php index d2b9cf387..17dd48133 100644 --- a/tests/security/CollectionsAdminTest.php +++ b/tests/security/CollectionsAdminTest.php @@ -63,7 +63,6 @@ public function testAdminMenuFiltering() $this->assertNotQuery('//a[@href="/admin/index/security"]'); $this->assertQuery('//a[@href="/admin/collectionroles"]'); $this->assertNotQuery('//a[@href="/admin/series"]'); - $this->assertNotQuery('//a[@href="/admin/language"]'); $this->assertNotQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertNotQuery('//a[@href="/review"]'); $this->assertNotQuery('//a[@href="/admin/index/setup"]'); diff --git a/tests/security/DocumentsAdminTest.php b/tests/security/DocumentsAdminTest.php index a920ce4e6..dc5473f39 100644 --- a/tests/security/DocumentsAdminTest.php +++ b/tests/security/DocumentsAdminTest.php @@ -64,7 +64,6 @@ public function testAdminMenuFiltering() $this->assertNotQuery('//a[@href="/admin/index/security"]'); $this->assertNotQuery('//a[@href="/admin/collectionroles"]'); $this->assertNotQuery('//a[@href="/admin/series"]'); - $this->assertNotQuery('//a[@href="/admin/language"]'); $this->assertNotQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertNotQuery('//a[@href="/admin/index/setup"]'); $this->assertNotQuery('//a[@href="/review"]'); diff --git a/tests/security/FullAdminTest.php b/tests/security/FullAdminTest.php index 091ef8b3c..969ecede0 100644 --- a/tests/security/FullAdminTest.php +++ b/tests/security/FullAdminTest.php @@ -63,7 +63,6 @@ public function testAdminMenuFiltering() $this->assertQuery('//a[@href="/admin/index/security"]'); $this->assertQuery('//a[@href="/admin/collectionroles"]'); $this->assertQuery('//a[@href="/admin/series"]'); - $this->assertQuery('//a[@href="/admin/language"]'); $this->assertQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertQuery('//a[@href="/admin/index/info"]'); $this->assertNotQuery('//a[@href="/review"]'); diff --git a/tests/security/FullAndPartialAdminTest.php b/tests/security/FullAndPartialAdminTest.php index 28d946ea2..e7474b9b8 100644 --- a/tests/security/FullAndPartialAdminTest.php +++ b/tests/security/FullAndPartialAdminTest.php @@ -63,7 +63,6 @@ public function testAdminMenuFiltering() $this->assertQuery('//a[@href="/admin/index/security"]'); $this->assertQuery('//a[@href="/admin/collectionroles"]'); $this->assertQuery('//a[@href="/admin/series"]'); - $this->assertQuery('//a[@href="/admin/language"]'); $this->assertQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertQuery('//a[@href="/admin/index/info"]'); $this->assertQuery('//a[@href="/admin/index/setup"]'); diff --git a/tests/security/LicencesAdminTest.php b/tests/security/LicencesAdminTest.php index 70b6e6ed8..97a97c8c4 100644 --- a/tests/security/LicencesAdminTest.php +++ b/tests/security/LicencesAdminTest.php @@ -63,7 +63,6 @@ public function testAdminMenuFiltering() $this->assertNotQuery('//a[@href="/admin/index/security"]'); $this->assertNotQuery('//a[@href="/admin/collectionroles"]'); $this->assertNotQuery('//a[@href="/admin/series"]'); - $this->assertNotQuery('//a[@href="/admin/language"]'); $this->assertNotQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertNotQuery('//a[@href="/admin/index/setup"]'); $this->assertNotQuery('//a[@href="/review"]'); diff --git a/tests/security/SecurityAdminTest.php b/tests/security/SecurityAdminTest.php index 2260573e1..8ad0b7bf5 100644 --- a/tests/security/SecurityAdminTest.php +++ b/tests/security/SecurityAdminTest.php @@ -62,7 +62,6 @@ public function testAdminMenuFiltering() $this->assertNotQuery('//a[@href="/admin/documents"]'); $this->assertNotQuery('//a[@href="/admin/licence"]'); $this->assertNotQuery('//a[@href="/admin/series"]'); - $this->assertNotQuery('//a[@href="/admin/language"]'); $this->assertNotQuery('//a[@href="/admin/dnbinstitute"]'); $this->assertQuery('//a[@href="/admin/index/info"]'); // Untermenü für Informationen $this->assertNotQuery('//a[@href="/review"]'); diff --git a/tests/support/AccessModuleAdminOneResourceOnlyTest.php b/tests/support/AccessModuleAdminOneResourceOnlyTest.php index cb1eb2201..fdbeaf96b 100644 --- a/tests/support/AccessModuleAdminOneResourceOnlyTest.php +++ b/tests/support/AccessModuleAdminOneResourceOnlyTest.php @@ -90,7 +90,6 @@ public function testAdminMenuFiltering() $this->assertElement('//a[@href="/admin/index/security"]', false); $this->assertElement('//a[@href="/admin/collectionroles"]', false); $this->assertElement('//a[@href="/admin/series"]', false); - $this->assertElement('//a[@href="/admin/language"]', false); $this->assertElement('//a[@href="/admin/dnbinstitute"]', false); $this->assertElement('//a[@href="/admin/index/setup"]', $this->acls['module_setup']); $this->assertElement('//a[@href="/review"]', false); diff --git a/tests/support/AccessModuleSetupAndAdminTest.php b/tests/support/AccessModuleSetupAndAdminTest.php index 913771298..245b0b2a5 100644 --- a/tests/support/AccessModuleSetupAndAdminTest.php +++ b/tests/support/AccessModuleSetupAndAdminTest.php @@ -92,7 +92,6 @@ public function testAdminMenuFiltering() $this->assertElement('//a[@href="/admin/index/security"]', $this->acls['module_admin']); $this->assertElement('//a[@href="/admin/collectionroles"]', $this->acls['module_admin']); $this->assertElement('//a[@href="/admin/series"]', $this->acls['module_admin']); - $this->assertElement('//a[@href="/admin/language"]', $this->acls['module_admin']); $this->assertElement('//a[@href="/admin/dnbinstitute"]', $this->acls['module_admin']); $this->assertElement('//a[@href="/admin/index/info"]', $this->acls['module_admin']); $this->assertElement('//a[@href="/admin/index/setup"]', $this->acls['module_admin']); From a78d4d97309a8fd10d30a636a8acb7db033bc04a Mon Sep 17 00:00:00 2001 From: j3nsch Date: Mon, 11 May 2026 12:02:11 +0200 Subject: [PATCH 06/10] #792 Remove language sorting in publish form --- modules/publish/models/Validation.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/publish/models/Validation.php b/modules/publish/models/Validation.php index 94e9b66a7..e1f493a86 100644 --- a/modules/publish/models/Validation.php +++ b/modules/publish/models/Validation.php @@ -431,7 +431,6 @@ private function languageSelect() { $languages = $this->getLanguages(); if (isset($languages) || count($languages) >= 1) { - asort($languages); return $languages; } return null; From ae75f5a490bc6353b69e29c745e9c85afe9e2c5e Mon Sep 17 00:00:00 2001 From: Jens Schwidder Date: Mon, 11 May 2026 15:51:38 +0200 Subject: [PATCH 07/10] #866 Language element supports unknown values (#1478) --- library/Application/Form/Element/Language.php | 8 ++++++++ tests/library/Application/Form/Element/LanguageTest.php | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/library/Application/Form/Element/Language.php b/library/Application/Form/Element/Language.php index bd65e7383..7089bc0d7 100644 --- a/library/Application/Form/Element/Language.php +++ b/library/Application/Form/Element/Language.php @@ -69,6 +69,14 @@ public function setValue($value) } } + if (! in_array($value, array_keys(self::getLanguageList()))) { + $label = Locale::getDisplayName($value); + if ($label !== $value) { + $label .= " ({$value})"; + } + $this->addMultiOption($value, $label); + } + parent::setValue($value); } diff --git a/tests/library/Application/Form/Element/LanguageTest.php b/tests/library/Application/Form/Element/LanguageTest.php index 68c6e1b25..07a002e93 100644 --- a/tests/library/Application/Form/Element/LanguageTest.php +++ b/tests/library/Application/Form/Element/LanguageTest.php @@ -104,6 +104,11 @@ public function testAutoMapValueToLanguageId() public function testUnknownLanguage() { - $this->markTestIncomplete(); + $language = $this->getElement(); + $langCode = 'cmn'; + + $language->setValue($langCode); + $this->assertEquals($langCode, $language->getValue()); + $this->assertTrue($language->isValid($langCode)); } } From 3b2f0df830945a3d4218b50e4725f7c682586684 Mon Sep 17 00:00:00 2001 From: j3nsch Date: Tue, 12 May 2026 12:29:09 +0200 Subject: [PATCH 08/10] #1460 Updating to release dependencies --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 3aa80256f..e9fd67e37 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ "ext-yaml": "*", "opus4/zf1-future": "1.25.*", "jpgraph/jpgraph": "dev-master", - "opus4-repo/opus4-common": "dev-master as 4.10", - "opus4-repo/framework": "dev-lang407 as 4.10", + "opus4-repo/opus4-common": "^4.10", + "opus4-repo/framework": "dev-master as 4.10", "opus4-repo/search": "^4.9", "opus4-repo/opus4-bibtex": "^4.9", "opus4-repo/opus4-import": "^4.9", @@ -32,9 +32,9 @@ "opus4-repo/opus4-job": "^4.9", "opus4-repo/opus4-security": "^4.9", "opus4-repo/opus4-sword": "^4.9", - "opus4-repo/opus4-app-common": "dev-main as 4.10", + "opus4-repo/opus4-app-common": "^4.10", "opus4-repo/opus4-deepgreen": "^4.9", - "opus4-repo/opus4-i18n": "dev-main as 4.10", + "opus4-repo/opus4-i18n": "^4.10", "components/jquery": "3.4.*", "components/jqueryui": "1.12.*", "oomphinc/composer-installers-extender": "^2.0", From f99c5eb43d372dbb7176a74bf5e172eef051320f Mon Sep 17 00:00:00 2001 From: j3nsch Date: Tue, 12 May 2026 17:27:55 +0200 Subject: [PATCH 09/10] #1480 Added configuration --- RELEASE_NOTES.md | 18 +++++++++++++++++- application/configs/application.ini | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index fbc15967d..ce1ef9993 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,7 +3,8 @@ ## Release 4.10 - 2026-05-12 Der neue Release bringt insbesondere Veränderungen bei der Sprachverwaltung und -den Optionen, die in der Administration editierbar sind. +den Optionen, die in der Administration editierbar sind. Weiterhin gab es eine +Reihe kleinerer Fehlerbehebungen. ### Update auf OPUS 4.10 @@ -46,6 +47,21 @@ wenn ein User keinen Zugriff auf das RSS-Modul hat. rss.showLinks = 0 +### DeepGreen Client + +Es gibt zwei neue Optionen, die steuern wie beim Import von Dokumenten mit +nicht erlaubten Dateitypen umgegangen werden soll. + + deepgreen.import.importAllFiles = 0 + deepgreen.import.importSupportedFiles = 1 + +Wenn **importAllFiles** aktiviert ist, werden alle Dateien importiert. Sollen +nur die in der OPUS 4 Konfiguration erlaubt Dateitypen importiert werden, kann +**importSupportedFiles** aktiviert werden. Sind beide Optionen deaktiviert, +werden Dokumente mit nicht erlaubten Dateitypen nicht importiert. Es geplant +in diesen Fällen in Zukunft Benachrichtigungen in der Administration anzuzeigen +und die Konfigurationsmöglichkeiten weiter auszubauen. + -- ## Release 4.9 - 2026-04-14 diff --git a/application/configs/application.ini b/application/configs/application.ini index 98c5f7dc9..093d6ffe7 100644 --- a/application/configs/application.ini +++ b/application/configs/application.ini @@ -766,6 +766,9 @@ console.commandProvider[] = "Opus\Import\Console\ImportCommandProvider" ; DeepGreen configuration deepgreen.configFile = APPLICATION_PATH'/application/configs/deepgreen.ini' +deepgreen.import.importAllFiles = 0 +deepgreen.import.importSupportedFiles = 1 + ; Staging, Testing and Development configurations ===================================================================== [staging : production] From 159c897e11165b4c10715769926ec8d3179e491e Mon Sep 17 00:00:00 2001 From: j3nsch Date: Tue, 12 May 2026 17:28:11 +0200 Subject: [PATCH 10/10] #1460 Update dependencies for release --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index e9fd67e37..f80e6a7a9 100644 --- a/composer.json +++ b/composer.json @@ -24,16 +24,16 @@ "opus4/zf1-future": "1.25.*", "jpgraph/jpgraph": "dev-master", "opus4-repo/opus4-common": "^4.10", - "opus4-repo/framework": "dev-master as 4.10", + "opus4-repo/framework": "^4.10", "opus4-repo/search": "^4.9", - "opus4-repo/opus4-bibtex": "^4.9", - "opus4-repo/opus4-import": "^4.9", + "opus4-repo/opus4-bibtex": "^4.10", + "opus4-repo/opus4-import": "^4.10", "opus4-repo/opus4-pdf": "^4.9", "opus4-repo/opus4-job": "^4.9", "opus4-repo/opus4-security": "^4.9", "opus4-repo/opus4-sword": "^4.9", "opus4-repo/opus4-app-common": "^4.10", - "opus4-repo/opus4-deepgreen": "^4.9", + "opus4-repo/opus4-deepgreen": "^4.10", "opus4-repo/opus4-i18n": "^4.10", "components/jquery": "3.4.*", "components/jqueryui": "1.12.*",