From fd4e0ddcb24fa4fd3a35e82fb53b2ad9489dfd46 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 9 Jul 2026 13:15:02 +0200 Subject: [PATCH] Store office suite choice in a single config option Previously it was three boolean options, which were supposed to be mutually exclusive, which had to be taken care of manually. Also previously there was only indirect indication of the choice "none", which is brittle, too. Signed-off-by: Pablo Zmdl --- php/public/containers-form-submit.js | 61 ++--------- php/public/index.php | 4 +- php/src/ContainerDefinitionFetcher.php | 13 +-- .../Controller/ConfigurationController.php | 23 +--- php/src/Controller/DockerController.php | 3 +- php/src/Data/ConfigurationManager.php | 100 +++++++++++++----- php/src/Data/OfficeSuite.php | 18 ++++ .../includes/optional-containers.twig | 14 ++- 8 files changed, 121 insertions(+), 115 deletions(-) create mode 100644 php/src/Data/OfficeSuite.php diff --git a/php/public/containers-form-submit.js b/php/public/containers-form-submit.js index 3c163cab..f6e5c447 100644 --- a/php/public/containers-form-submit.js +++ b/php/public/containers-form-submit.js @@ -1,6 +1,7 @@ document.addEventListener("DOMContentLoaded", function () { + const optionsForm = document.getElementById('options-form'); // Don't run if the expected form isn't present. - if (document.getElementById('options-form') === null) { + if (optionsForm === null) { return; } @@ -20,14 +21,8 @@ document.addEventListener("DOMContentLoaded", function () { const communityContainersCheckboxes = document.querySelectorAll("#community-form input[type='checkbox']"); // Office suite radio buttons - const collaboraRadio = document.getElementById('office-collabora'); - const onlyofficeRadio = document.getElementById('office-onlyoffice'); - const euroofficeRadio = document.getElementById('office-eurooffice'); - const noneRadio = document.getElementById('office-none'); - const collaboraHidden = document.getElementById('collabora'); - const onlyofficeHidden = document.getElementById('onlyoffice'); - const euroofficeHidden = document.getElementById('eurooffice'); - let initialOfficeSelection = null; + const officeSuiteChoiceList = optionsForm.elements['office_suite_choice']; + const initialOfficeSelection = document.getElementById('initial-office-suite')?.value ?? ''; optionsContainersCheckboxes.forEach(checkbox => { initialStateOptionsContainers[checkbox.id] = checkbox.checked; // Use checked property to capture actual initial state @@ -37,19 +32,6 @@ document.addEventListener("DOMContentLoaded", function () { initialStateCommunityContainers[checkbox.id] = checkbox.checked; // Use checked property to capture actual initial state }); - // Store initial office suite selection - if (collaboraRadio && onlyofficeRadio && euroofficeRadio && noneRadio) { - if (collaboraRadio.checked) { - initialOfficeSelection = 'collabora'; - } else if (onlyofficeRadio.checked) { - initialOfficeSelection = 'onlyoffice'; - } else if (euroofficeRadio.checked) { - initialOfficeSelection = 'eurooffice'; - } else { - initialOfficeSelection = 'none'; - } - } - // Function to compare current states to initial states function checkForOptionContainerChanges() { let hasChanges = false; @@ -60,32 +42,8 @@ document.addEventListener("DOMContentLoaded", function () { } }); - // Check office suite changes and sync to hidden inputs - if (collaboraRadio && onlyofficeRadio && euroofficeRadio && noneRadio && collaboraHidden && onlyofficeHidden && euroofficeHidden) { - let currentOfficeSelection = null; - if (collaboraRadio.checked) { - currentOfficeSelection = 'collabora'; - collaboraHidden.value = 'on'; - onlyofficeHidden.value = ''; - euroofficeHidden.value = ''; - } else if (onlyofficeRadio.checked) { - currentOfficeSelection = 'onlyoffice'; - collaboraHidden.value = ''; - onlyofficeHidden.value = 'on'; - euroofficeHidden.value = ''; - } else if (euroofficeRadio.checked) { - currentOfficeSelection = 'eurooffice'; - collaboraHidden.value = ''; - onlyofficeHidden.value = ''; - euroofficeHidden.value = 'on'; - } else { - currentOfficeSelection = 'none'; - collaboraHidden.value = ''; - onlyofficeHidden.value = ''; - euroofficeHidden.value = ''; - } - - if (currentOfficeSelection !== initialOfficeSelection) { + if (officeSuiteChoiceList) { + if (officeSuiteChoiceList.value !== initialOfficeSelection) { hasChanges = true; } } @@ -156,12 +114,7 @@ document.addEventListener("DOMContentLoaded", function () { handleTalkVisibility(); // Ensure talk-recording is correctly initialized // Add event listeners for office suite radio buttons - if (collaboraRadio && onlyofficeRadio && euroofficeRadio && noneRadio) { - collaboraRadio.addEventListener('change', checkForOptionContainerChanges); - onlyofficeRadio.addEventListener('change', checkForOptionContainerChanges); - euroofficeRadio.addEventListener('change', checkForOptionContainerChanges); - noneRadio.addEventListener('change', checkForOptionContainerChanges); - } + officeSuiteChoiceList?.forEach((elem) => elem.addEventListener('change', checkForOptionContainerChanges)); // Initial call to check for changes checkForOptionContainerChanges(); diff --git a/php/public/index.php b/php/public/index.php index 46c39786..1b0a675f 100644 --- a/php/public/index.php +++ b/php/public/index.php @@ -153,9 +153,7 @@ $app->get('/containers', function (Request $request, Response $response, array $ 'backup_times' => $configurationManager->getBackupTimes(), 'current_channel' => $dockerActionManager->GetCurrentChannel(), 'is_clamav_enabled' => $configurationManager->isClamavEnabled, - 'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled, - 'is_eurooffice_enabled' => $configurationManager->isEuroofficeEnabled, - 'is_collabora_enabled' => $configurationManager->isCollaboraEnabled, + 'office_suite' => $configurationManager->getOfficeSuiteString(), 'is_talk_enabled' => $configurationManager->isTalkEnabled, 'borg_restore_password' => $configurationManager->borgRestorePassword, 'daily_backup_time' => $configurationManager->getDailyBackupTime(), diff --git a/php/src/ContainerDefinitionFetcher.php b/php/src/ContainerDefinitionFetcher.php index 7fb8604a..44ab329e 100644 --- a/php/src/ContainerDefinitionFetcher.php +++ b/php/src/ContainerDefinitionFetcher.php @@ -12,6 +12,7 @@ use AIO\Container\ContainerVolume; use AIO\Container\ContainerVolumes; use AIO\Data\ConfigurationManager; use AIO\Data\DataConst; +use AIO\Data\OfficeSuite; use AIO\Docker\DockerActionManager; readonly class ContainerDefinitionFetcher { @@ -75,15 +76,15 @@ readonly class ContainerDefinitionFetcher { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-onlyoffice') { - if (!$this->configurationManager->isOnlyofficeEnabled) { + if ($this->configurationManager->officeSuite !== OfficeSuite::Onlyoffice) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-eurooffice') { - if (!$this->configurationManager->isEuroofficeEnabled) { + if ($this->configurationManager->officeSuite !== OfficeSuite::Eurooffice) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->isCollaboraEnabled) { + if ($this->configurationManager->officeSuite !== OfficeSuite::Collabora) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-talk') { @@ -192,15 +193,15 @@ readonly class ContainerDefinitionFetcher { continue; } } elseif ($value === 'nextcloud-aio-onlyoffice') { - if (!$this->configurationManager->isOnlyofficeEnabled) { + if ($this->configurationManager->officeSuite !== OfficeSuite::Onlyoffice) { continue; } } elseif ($value === 'nextcloud-aio-eurooffice') { - if (!$this->configurationManager->isEuroofficeEnabled) { + if ($this->configurationManager->officeSuite !== OfficeSuite::Eurooffice) { continue; } } elseif ($value === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->isCollaboraEnabled) { + if ($this->configurationManager->officeSuite !== OfficeSuite::Collabora) { continue; } } elseif ($value === 'nextcloud-aio-talk') { diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index 0fa7a2ba..f96c8723 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -5,6 +5,7 @@ namespace AIO\Controller; use AIO\Data\ConfigurationManager; use AIO\Data\InvalidSettingConfigurationException; +use AIO\Data\OfficeSuite; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; @@ -76,24 +77,10 @@ readonly class ConfigurationController { } if (isset($request->getParsedBody()['options-form'])) { - $officeSuiteChoice = $request->getParsedBody()['office_suite_choice'] ?? ''; - - if ($officeSuiteChoice === 'collabora') { - $this->configurationManager->isCollaboraEnabled = true; - $this->configurationManager->isOnlyofficeEnabled = false; - $this->configurationManager->isEuroofficeEnabled = false; - } elseif ($officeSuiteChoice === 'onlyoffice') { - $this->configurationManager->isCollaboraEnabled = false; - $this->configurationManager->isOnlyofficeEnabled = true; - $this->configurationManager->isEuroofficeEnabled = false; - } elseif ($officeSuiteChoice === 'eurooffice') { - $this->configurationManager->isCollaboraEnabled = false; - $this->configurationManager->isOnlyofficeEnabled = false; - $this->configurationManager->isEuroofficeEnabled = true; - } else { - $this->configurationManager->isCollaboraEnabled = false; - $this->configurationManager->isOnlyofficeEnabled = false; - $this->configurationManager->isEuroofficeEnabled = false; + if (isset($request->getParsedBody()['office_suite_choice'])) { + $inputValue = strval($request->getParsedBody()['office_suite_choice'] ?? ''); + $officeSuite = OfficeSuite::tryFrom($inputValue) ?? OfficeSuite::None; + $this->configurationManager->officeSuite = $officeSuite; } $this->configurationManager->isClamavEnabled = isset($request->getParsedBody()['clamav']); $this->configurationManager->isTalkEnabled = isset($request->getParsedBody()['talk']); diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php index 86f7c78c..a3656415 100644 --- a/php/src/Controller/DockerController.php +++ b/php/src/Controller/DockerController.php @@ -11,6 +11,7 @@ use AIO\Docker\DockerActionManager; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use AIO\Data\ConfigurationManager; +use AIO\Data\OfficeSuite; use Slim\Psr7\NonBufferedBody; readonly class DockerController { @@ -299,7 +300,7 @@ readonly class DockerController { // This is a hack but no better solution was found for the meantime // Stop Collabora first to make sure it force-saves // See https://github.com/nextcloud/richdocuments/issues/3799 - if ($id === self::TOP_CONTAINER && $this->configurationManager->isCollaboraEnabled) { + if ($id === self::TOP_CONTAINER && $this->configurationManager->officeSuite === OfficeSuite::Collabora) { $this->PerformRecursiveContainerStop('nextcloud-aio-collabora', false, $addToStreamingResponseBody); } diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index 3398f5c6..e268fea1 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -95,22 +95,9 @@ class ConfigurationManager set { $this->set('isClamavEnabled', $value); } } - public bool $isOnlyofficeEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isOnlyofficeEnabled', false); - set { $this->set('isOnlyofficeEnabled', $value); } - } - - public bool $isEuroofficeEnabled { - // Only enabled if no other office suite is enabled. - get => !$this->isOtherOfficeThanEuroOfficeEnabled() && $this->get('isEuroofficeEnabled', true); - set { $this->set('isEuroofficeEnabled', $value); } - } - - public bool $isCollaboraEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isCollaboraEnabled', false); - set { $this->set('isCollaboraEnabled', $value); } + public OfficeSuite $officeSuite { + get => $this->readOfficeSuite(); + set { $this->writeOfficeSuite($value); } } public bool $isTalkEnabled { @@ -416,6 +403,75 @@ class ConfigurationManager } } + private function has(string $key) : bool { + return array_key_exists($key, $this->getConfig()); + } + + private function unset(string ...$keys) : void { + $changed = false; + $this->getConfig(); + foreach ($keys as $key) { + if ($this->has($key)) { + unset($this->config[$key]); + $changed = true; + } + } + // Only write if this isn't called in between startTransaction() and commitTransaction(). + if ($changed && $this->noWrite !== true) { + $this->writeConfig(); + } + } + + private function writeOfficeSuite(OfficeSuite $officeSuite) : void + { + $this->set('officeSuite', $officeSuite->value); + // Remove the deprecated options. + $this->unset('isCollaboraEnabled', 'isOnlyofficeEnabled', 'isEuroofficeEnabled'); + } + + private function readOfficeSuite() : OfficeSuite + { + if ($this->has('officeSuite')) { + $configValue = (string) $this->get('officeSuite', ''); + return OfficeSuite::tryFrom($configValue) ?? OfficeSuite::None; + } + + // Check the three boolean legacy options. Convert to boolean because very old configs could have + // `1`/`0` or even `"1"`/`"0"`/`""` as values. + if (boolval($this->get('isCollaboraEnabled', false)) === true) { + return OfficeSuite::Collabora; + } + if (boolval($this->get('isOnlyofficeEnabled', false)) === true) { + return OfficeSuite::Onlyoffice; + } + if (boolval($this->get('isEuroofficeEnabled', false)) === true) { + return OfficeSuite::Eurooffice; + } + + // All offices disabled. + if ( + $this->has('isCollaboraEnabled') && boolval($this->get('isCollaboraEnabled')) === false + && $this->has('isOnlyofficeEnabled') && boolval($this->get('isOnlyofficeEnabled')) === false + && ( + // Eurooffice can be unset, which should be treated as `false`, too, because it means that + // the office choice wasn't ever saved after Eurooffice was introduced, but the user had + // previously disabled both available options, which means they want no office. + !$this->has('isEuroofficeEnabled') + || boolval($this->get('isEuroofficeEnabled')) === false + ) + ) { + return OfficeSuite::None; + } + + // Default + return OfficeSuite::Eurooffice; + } + + public function getOfficeSuiteString() : string + { + return $this->officeSuite->value; + } + /** * This allows to assign multiple attributes without saving the config to disk in between. It must be * followed by a call to commitTransaction(), which then writes all changes to disk. @@ -532,12 +588,6 @@ class ConfigurationManager return ''; } - // Helper function for EuroOffice to make sure that it does not - // get enabled on existing instances after updating the default - private function isOtherOfficeThanEuroOfficeEnabled() : bool { - return $this->isCollaboraEnabled || $this->isOnlyofficeEnabled; - } - /** * @throws InvalidSettingConfigurationException * @@ -1164,9 +1214,9 @@ class ConfigurationManager 'BACKUP_RESTORE_PASSWORD' => $this->borgRestorePassword, 'CLAMAV_ENABLED' => $this->isClamavEnabled ? 'yes' : '', 'TALK_RECORDING_ENABLED' => $this->isTalkRecordingEnabled ? 'yes' : '', - 'ONLYOFFICE_ENABLED' => $this->isOnlyofficeEnabled ? 'yes' : '', - 'EUROOFFICE_ENABLED' => $this->isEuroofficeEnabled ? 'yes' : '', - 'COLLABORA_ENABLED' => $this->isCollaboraEnabled ? 'yes' : '', + 'ONLYOFFICE_ENABLED' => $this->officeSuite === OfficeSuite::Onlyoffice ? 'yes' : '', + 'EUROOFFICE_ENABLED' => $this->officeSuite === OfficeSuite::Eurooffice ? 'yes' : '', + 'COLLABORA_ENABLED' => $this->officeSuite === OfficeSuite::Collabora ? 'yes' : '', 'TALK_ENABLED' => $this->isTalkEnabled ? 'yes' : '', 'UPDATE_NEXTCLOUD_APPS' => ($this->isDailyBackupRunning() && $this->areAutomaticUpdatesEnabled()) ? 'yes' : '', 'TIMEZONE' => $this->timezone === '' ? 'Etc/UTC' : $this->timezone, diff --git a/php/src/Data/OfficeSuite.php b/php/src/Data/OfficeSuite.php new file mode 100644 index 00000000..61b8abd7 --- /dev/null +++ b/php/src/Data/OfficeSuite.php @@ -0,0 +1,18 @@ + @@ -49,14 +49,13 @@ {% endif %} - @@ -86,14 +85,13 @@ {% endif %} - @@ -123,7 +121,6 @@ {% endif %} - {% if isAnyRunning == false %}
@@ -133,7 +130,7 @@ name="office_suite_choice" value="" class="office-radio" - {% if is_collabora_enabled == false and is_onlyoffice_enabled == false and is_eurooffice_enabled == false %} + {% if office_suite == '' %} checked="checked" {% endif %} > @@ -143,6 +140,7 @@
{% endif %} +

Additional Optional Containers

@@ -270,7 +268,7 @@ {% endif %} -{% if is_collabora_enabled == true and isAnyRunning == false and was_start_button_clicked == true %} +{% if office_suite == 'collabora' and isAnyRunning == false and was_start_button_clicked == true %}

Nextcloud Office dictionaries

{% if collabora_dictionaries == "" %}