Don't run Eurooffice if prior to its introduction all office suites were disabled (#8441)

This commit is contained in:
Pablo Zmdl
2026-07-17 19:38:56 +02:00
committed by GitHub
8 changed files with 121 additions and 115 deletions
+7 -54
View File
@@ -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();
+1 -3
View File
@@ -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(),
+7 -6
View File
@@ -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') {
+5 -18
View File
@@ -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']);
+2 -1
View File
@@ -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);
}
+75 -25
View File
@@ -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,
+18
View File
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace AIO\Data;
enum OfficeSuite: string
{
case Collabora = 'collabora';
case Onlyoffice = 'onlyoffice';
case Eurooffice = 'eurooffice';
case None = '';
}
@@ -20,7 +20,7 @@
name="office_suite_choice"
value="eurooffice"
class="office-radio"
{% if is_eurooffice_enabled == true %}
{% if office_suite == 'eurooffice' %}
checked="checked"
{% endif %}
>
@@ -49,14 +49,13 @@
</a>
{% endif %}
</label>
<input type="hidden" id="eurooffice" name="eurooffice" value="" data-initial-state="{% if is_eurooffice_enabled == true %}true{% else %}false{% endif %}">
<input
type="radio"
id="office-collabora"
name="office_suite_choice"
value="collabora"
class="office-radio"
{% if is_collabora_enabled == true %}
{% if office_suite == 'collabora' %}
checked="checked"
{% endif %}
>
@@ -86,14 +85,13 @@
</a>
{% endif %}
</label>
<input type="hidden" id="collabora" name="collabora" value="" data-initial-state="{% if is_collabora_enabled == true %}true{% else %}false{% endif %}">
<input
type="radio"
id="office-onlyoffice"
name="office_suite_choice"
value="onlyoffice"
class="office-radio"
{% if is_onlyoffice_enabled == true %}
{% if office_suite == 'onlyoffice' %}
checked="checked"
{% endif %}
>
@@ -123,7 +121,6 @@
</a>
{% endif %}
</label>
<input type="hidden" id="onlyoffice" name="onlyoffice" value="" data-initial-state="{% if is_onlyoffice_enabled == true %}true{% else %}false{% endif %}">
</div>
{% if isAnyRunning == false %}
<div class="office-none-card">
@@ -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 @@
</label>
</div>
{% endif %}
<input type="hidden" id="initial-office-suite" value="{{ office_suite }}">
<input class="options-form-submit" type="submit" value="Save changes" />
<h3>Additional Optional Containers</h3>
<p>
@@ -270,7 +268,7 @@
<script type="text/javascript" src="disable-containers.js?v1"></script>
{% 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 %}
<h3>Nextcloud Office dictionaries</h3>
{% if collabora_dictionaries == "" %}