diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md
index ec25fc4a..5d6cc059 100644
--- a/.github/ISSUE_TEMPLATE/Bug_report.md
+++ b/.github/ISSUE_TEMPLATE/Bug_report.md
@@ -32,6 +32,10 @@ labels: 0. Needs triage
#### Output of `sudo docker logs nextcloud-aio-mastercontainer`
+#### Output of `sudo docker inspect nextcloud-aio-mastercontainer`
+
+#### Output of `sudo docker ps -a`
+
#### Other valuable info
#### A picture of a cute animal
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f79c4ce2..7fe1067e 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -10,6 +10,8 @@ updates:
labels:
- 3. to review
- dependencies
+ cooldown:
+ default-days: 7
- package-ecosystem: composer
directory: "/php/"
schedule:
diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml
index 639b0785..a4f441c2 100644
--- a/.github/workflows/helm-release.yml
+++ b/.github/workflows/helm-release.yml
@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v6.0.1
- name: Turnstyle
- uses: softprops/turnstyle@15f9da4059166900981058ba251e0b652511c68f # v2
+ uses: softprops/turnstyle@e565d2d86403c5d23533937e95980570545e5586 # v2
with:
continue-after-seconds: 180
env:
diff --git a/.github/workflows/playwright-on-push.yml b/.github/workflows/playwright-on-push.yml
new file mode 100644
index 00000000..af8dec02
--- /dev/null
+++ b/.github/workflows/playwright-on-push.yml
@@ -0,0 +1,123 @@
+name: Playwright Tests on push
+
+on:
+ pull_request:
+ paths:
+ - 'php/**'
+ push:
+ branches:
+ - main
+ paths:
+ - 'php/**'
+
+concurrency:
+ group: playwright-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+env:
+ BASE_URL: https://localhost:8080
+
+jobs:
+ test:
+ timeout-minutes: 60
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v6.0.1
+
+ - uses: actions/setup-node@v6
+ with:
+ node-version: lts/*
+
+ - name: Install dependencies
+ run: cd php/tests && npm ci
+
+ - name: Install Playwright Browsers
+ run: cd php/tests && npx playwright install --with-deps chromium
+
+ - name: Set up php 8.4
+ uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2.36.0
+ with:
+ extensions: apcu
+ php-version: 8.4
+ coverage: none
+ ini-file: development
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Adjust some things and fix permissions
+ run: |
+ cd php
+ rm -r ./data
+ rm -r ./session
+ composer install --no-dev
+ composer clear-cache
+ sudo chmod 777 -R ./
+
+ - name: Start fresh development server
+ run: |
+ docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} || true
+ docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true
+ docker pull ghcr.io/nextcloud-releases/all-in-one:develop
+ docker run \
+ -d \
+ --init \
+ --name nextcloud-aio-mastercontainer \
+ --restart always \
+ --publish 8080:8080 \
+ --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
+ --volume ./php:/var/www/docker-aio/php \
+ --volume /var/run/docker.sock:/var/run/docker.sock:ro \
+ --env SKIP_DOMAIN_VALIDATION=true \
+ --env APACHE_PORT=11000 \
+ ghcr.io/nextcloud-releases/all-in-one:develop
+ echo Waiting for 10 seconds for the development container to start ...
+ sleep 10
+
+ - name: Run Playwright tests for initial setup
+ run: |
+ cd php/tests
+ export DEBUG=pw:api
+ if ! npx playwright test tests/initial-setup.spec.js; then
+ docker logs nextcloud-aio-mastercontainer
+ docker logs nextcloud-aio-borgbackup
+ exit 1
+ fi
+
+ - name: Start fresh development server
+ run: |
+ docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} || true
+ docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true
+ docker run \
+ -d \
+ --init \
+ --name nextcloud-aio-mastercontainer \
+ --restart always \
+ --publish 8080:8080 \
+ --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
+ --volume ./php:/var/www/docker-aio/php \
+ --volume /var/run/docker.sock:/var/run/docker.sock:ro \
+ --env SKIP_DOMAIN_VALIDATION=false \
+ --env APACHE_PORT=11000 \
+ ghcr.io/nextcloud-releases/all-in-one:develop
+ echo Waiting for 10 seconds for the development container to start ...
+ sleep 10
+
+ - name: Run Playwright tests for backup restore
+ run: |
+ cd php/tests
+ export DEBUG=pw:api
+ if ! npx playwright test tests/restore-instance.spec.js; then
+ docker logs nextcloud-aio-mastercontainer
+ docker logs nextcloud-aio-borgbackup
+ exit 1
+ fi
+
+ - uses: actions/upload-artifact@v6
+ if: ${{ !cancelled() }}
+ with:
+ name: playwright-report
+ path: php/tests/playwright-report/
+ retention-days: 14
+ overwrite: true
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright-on-workflow-dispatch.yml
similarity index 100%
rename from .github/workflows/playwright.yml
rename to .github/workflows/playwright-on-workflow-dispatch.yml
diff --git a/Containers/fulltextsearch/Dockerfile b/Containers/fulltextsearch/Dockerfile
index 7975bcbb..ed0cafe9 100644
--- a/Containers/fulltextsearch/Dockerfile
+++ b/Containers/fulltextsearch/Dockerfile
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest
# Probably from here https://github.com/elastic/elasticsearch/blob/main/distribution/docker/src/docker/Dockerfile
-FROM elasticsearch:8.19.9
+FROM elasticsearch:8.19.10
USER root
diff --git a/Containers/mastercontainer/Dockerfile b/Containers/mastercontainer/Dockerfile
index c5b91b7d..d2019e49 100644
--- a/Containers/mastercontainer/Dockerfile
+++ b/Containers/mastercontainer/Dockerfile
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest
# Docker CLI is a requirement
-FROM docker:29.1.3-cli AS docker
+FROM docker:29.1.4-cli AS docker
# Caddy is a requirement
FROM caddy:2.10.2-alpine AS caddy
diff --git a/Containers/nextcloud/Dockerfile b/Containers/nextcloud/Dockerfile
index 6b6be7f8..9c468bbb 100644
--- a/Containers/nextcloud/Dockerfile
+++ b/Containers/nextcloud/Dockerfile
@@ -8,7 +8,7 @@ ENV SOURCE_LOCATION=/usr/src/nextcloud
ENV REDIS_DB_INDEX=0
# AIO settings start # Do not remove or change this line!
-ENV NEXTCLOUD_VERSION=32.0.3
+ENV NEXTCLOUD_VERSION=32.0.4
ENV AIO_TOKEN=123456
ENV AIO_URL=localhost
# AIO settings end # Do not remove or change this line!
diff --git a/Containers/nextcloud/config/postgres.config.php b/Containers/nextcloud/config/postgres.config.php
index 71a657a7..0dc835cc 100644
--- a/Containers/nextcloud/config/postgres.config.php
+++ b/Containers/nextcloud/config/postgres.config.php
@@ -10,7 +10,7 @@ if (getenv('NEXTCLOUD_TRUSTED_CERTIFICATES_POSTGRES')) {
if (getenv('NEXTCLOUD_TRUSTED_CERTIFICATES_MYSQL')) {
$CONFIG = array(
'dbdriveroptions' => array(
- 'PDO::MYSQL_ATTR_SSL_CA' => '/var/www/html/data/certificates/ca-bundle.crt',
+ PDO::MYSQL_ATTR_SSL_CA => '/var/www/html/data/certificates/ca-bundle.crt',
),
);
}
diff --git a/Containers/nextcloud/config/s3.config.php b/Containers/nextcloud/config/s3.config.php
index 59217a78..6ea06697 100644
--- a/Containers/nextcloud/config/s3.config.php
+++ b/Containers/nextcloud/config/s3.config.php
@@ -10,6 +10,7 @@ if (getenv('OBJECTSTORE_S3_BUCKET')) {
'class' => '\OC\Files\ObjectStore\S3',
'arguments' => array(
'multibucket' => $multibucket === 'true',
+ 'num_buckets' => (int)getenv('OBJECTSTORE_S3_NUM_BUCKETS') ?: 64,
'bucket' => getenv('OBJECTSTORE_S3_BUCKET'),
'key' => getenv('OBJECTSTORE_S3_KEY') ?: '',
'secret' => getenv('OBJECTSTORE_S3_SECRET') ?: '',
diff --git a/Containers/nextcloud/entrypoint.sh b/Containers/nextcloud/entrypoint.sh
index 6825f04c..43432e6d 100644
--- a/Containers/nextcloud/entrypoint.sh
+++ b/Containers/nextcloud/entrypoint.sh
@@ -65,14 +65,6 @@ if env | grep -q NEXTCLOUD_TRUSTED_CERTIFICATES_; then
done
- # Custom logic for ldap conf
- if ! grep -q "TLS_" /etc/openldap/ldap.conf; then
- cat << EOL >> /etc/openldap/ldap.conf
-TLS_CACERT $CERTIFICATE_BUNDLE
-TLS_REQCERT try
-EOL
- fi
-
# Backwards compatibility with older instances
if [ -f "/var/www/html/config/postgres.config.php" ]; then
sed -i "s|/var/www/html/data/certificates/POSTGRES|/var/www/html/data/certificates/ca-bundle.crt|" /var/www/html/config/postgres.config.php
diff --git a/Containers/whiteboard/Dockerfile b/Containers/whiteboard/Dockerfile
index f6b8cc7d..37ba25e0 100644
--- a/Containers/whiteboard/Dockerfile
+++ b/Containers/whiteboard/Dockerfile
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest
# Probably from this file: https://github.com/nextcloud/whiteboard/blob/main/Dockerfile
-FROM ghcr.io/nextcloud-releases/whiteboard:v1.5.0
+FROM ghcr.io/nextcloud-releases/whiteboard:v1.5.1
USER root
RUN set -ex; \
diff --git a/community-containers/caddy/readme.md b/community-containers/caddy/readme.md
index a8baf9ea..803bbec2 100644
--- a/community-containers/caddy/readme.md
+++ b/community-containers/caddy/readme.md
@@ -17,6 +17,7 @@ This container bundles caddy and auto-configures it for you. It also covers [vau
- After the container was started the first time, you should see a new `nextcloud-aio-caddy` folder and inside there an `allowed-countries.txt` file when you open the files app with the default `admin` user. In there you can adjust the allowed country codes for caddy by adding them to the first line, e.g. `IT FR` would allow access from italy and france. Private ip-ranges are always allowed. Additionally, in order to activate this config, you need to get an account at https://dev.maxmind.com/geoip/geolite2-free-geolocation-data and download the `GeoLite2-Country.mmdb` and upload it with this exact name into the `nextcloud-aio-caddy` folder. Afterwards restart all containers from the AIO interface and your new config should be active!
- You can add your own Caddy configurations in `/data/caddy-imports/` inside the Caddy container (`sudo docker exec -it nextcloud-aio-caddy bash`). These will be imported on container startup. **Please note:** If you do not have CLI access to the server, you can now run docker commands via a web session by using this community container: https://github.com/nextcloud/all-in-one/tree/main/community-containers/container-management
- See https://github.com/nextcloud/all-in-one/tree/main/community-containers#community-containers how to add it to the AIO stack
+- If you want to remove the container again and revert back to the default, you need to disable the container via the AIO-interface and follow https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#8-removing-the-reverse-proxy
### Repository
https://github.com/szaimen/aio-caddy
diff --git a/local-instance.md b/local-instance.md
index 1da26280..8abbddb6 100644
--- a/local-instance.md
+++ b/local-instance.md
@@ -22,10 +22,11 @@ The normal way is the following:
**Hint:** You may have a look at [this video](https://youtu.be/zk-y2wVkY4c) for a more complete but possibly outdated example.
## 3. Use the ACME DNS-challenge
-You can alternatively use the ACME DNS-challenge to get a valid certificate for Nextcloud. Here is described how to set it up: https://github.com/nextcloud/all-in-one#how-to-get-nextcloud-running-using-the-acme-dns-challenge
+You can alternatively use the ACME DNS-challenge to get a valid certificate for Nextcloud. Here is described how to set it up using an external caddy reverse proxy: https://github.com/nextcloud/all-in-one#how-to-get-nextcloud-running-using-the-acme-dns-challenge
## 4. Use Cloudflare
If you do not have any control over the network, you may think about using Cloudflare Tunnel to get a valid certificate for your Nextcloud. However it will be opened to the public internet then. See https://github.com/nextcloud/all-in-one#how-to-run-nextcloud-behind-a-cloudflare-tunnel how to set this up.
## 5. Buy a certificate and use that
If none of the above ways work for you, you may simply buy a certificate from an issuer for your domain. You then download the certificate onto your server, configure AIO in [reverse proxy mode](./reverse-proxy.md) and use the certificate for your domain in your reverse proxy config.
+
diff --git a/nextcloud-aio-helm-chart/Chart.yaml b/nextcloud-aio-helm-chart/Chart.yaml
index 16e7a82c..7d990549 100755
--- a/nextcloud-aio-helm-chart/Chart.yaml
+++ b/nextcloud-aio-helm-chart/Chart.yaml
@@ -1,6 +1,6 @@
name: nextcloud-aio-helm-chart
description: A generated Helm Chart for Nextcloud AIO from Skippbox Kompose
-version: 12.3.0
+version: 12.4.0
apiVersion: v2
keywords:
- latest
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml
index 6eddefe9..6cdf8db8 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml
@@ -61,7 +61,7 @@ spec:
value: "{{ .Values.TIMEZONE }}"
- name: WHITEBOARD_HOST
value: nextcloud-aio-whiteboard
- image: ghcr.io/nextcloud-releases/aio-apache:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-apache:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml
index 26eda032..d7627802 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml
@@ -36,7 +36,7 @@ spec:
{{- end }}
initContainers:
- name: init-subpath
- image: ghcr.io/nextcloud-releases/aio-alpine:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729
command:
- mkdir
- "-p"
@@ -59,7 +59,7 @@ spec:
value: "{{ .Values.NEXTCLOUD_UPLOAD_LIMIT }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-clamav:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-clamav:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml
index c0984e1d..7e86c402 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml
@@ -36,9 +36,9 @@ spec:
- name: server_name
value: "{{ .Values.NC_DOMAIN }}"
{{- if contains "--o:support_key=" (join " " (.Values.ADDITIONAL_COLLABORA_OPTIONS | default list)) }}
- image: ghcr.io/nextcloud-releases/aio-collabora-online:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-collabora-online:20260114_114729
{{- else }}
- image: ghcr.io/nextcloud-releases/aio-collabora:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-collabora:20260114_114729
{{- end }}
readinessProbe:
exec:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml
index b7b54647..055ecd0a 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml
@@ -35,7 +35,7 @@ spec:
{{- end }}
initContainers:
- name: init-subpath
- image: ghcr.io/nextcloud-releases/aio-alpine:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729
command:
- mkdir
- "-p"
@@ -64,7 +64,7 @@ spec:
value: nextcloud
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-postgresql:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-postgresql:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml
index 14f19447..df30e6a8 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml
@@ -24,7 +24,7 @@ spec:
spec:
initContainers:
- name: init-volumes
- image: ghcr.io/nextcloud-releases/aio-alpine:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729
command:
- chmod
- "777"
@@ -54,7 +54,7 @@ spec:
value: basic
- name: xpack.security.enabled
value: "false"
- image: ghcr.io/nextcloud-releases/aio-fulltextsearch:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-fulltextsearch:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml
index 9f0c54c5..d2fc1375 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml
@@ -38,7 +38,7 @@ spec:
value: "{{ .Values.IMAGINARY_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-imaginary:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-imaginary:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml
index 3cbfa2fe..fe72d307 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml
@@ -38,7 +38,7 @@ spec:
# AIO settings start # Do not remove or change this line!
initContainers:
- name: init-volumes
- image: ghcr.io/nextcloud-releases/aio-alpine:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729
command:
- chmod
- "777"
@@ -190,7 +190,7 @@ spec:
value: "{{ .Values.WHITEBOARD_ENABLED }}"
- name: WHITEBOARD_SECRET
value: "{{ .Values.WHITEBOARD_SECRET }}"
- image: ghcr.io/nextcloud-releases/aio-nextcloud:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-nextcloud:20260114_114729
{{- if eq (.Values.RPSS_ENABLED | default "no") "yes" }} # AIO-config - do not change this comment!
securityContext:
# The items below only work in container context
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml
index a9822a80..5b05336e 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml
@@ -57,7 +57,7 @@ spec:
value: "6379"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-notify-push:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-notify-push:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml
index c8160edd..0e3a7fda 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml
@@ -24,7 +24,7 @@ spec:
spec:
initContainers:
- name: init-volumes
- image: ghcr.io/nextcloud-releases/aio-alpine:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729
command:
- chmod
- "777"
@@ -42,7 +42,7 @@ spec:
value: "{{ .Values.ONLYOFFICE_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-onlyoffice:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-onlyoffice:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml
index 8446167d..1ccebd79 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml
@@ -39,7 +39,7 @@ spec:
value: "{{ .Values.REDIS_PASSWORD }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-redis:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-redis:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml
index c28e7335..8635a6ce 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml
@@ -52,7 +52,7 @@ spec:
value: "{{ .Values.TURN_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-talk:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-talk:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml
index a0d36c08..2cfcaa53 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml
@@ -44,7 +44,7 @@ spec:
value: "{{ .Values.RECORDING_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-talk-recording:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-talk-recording:20260114_114729
readinessProbe:
exec:
command:
diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml
index e311f230..50dfc3c4 100755
--- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml
+++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml
@@ -50,7 +50,7 @@ spec:
value: redis
- name: TZ
value: "{{ .Values.TIMEZONE }}"
- image: ghcr.io/nextcloud-releases/aio-whiteboard:20251218_095503
+ image: ghcr.io/nextcloud-releases/aio-whiteboard:20260114_114729
readinessProbe:
exec:
command:
diff --git a/php/composer.lock b/php/composer.lock
index ed6667ed..ce1ae80f 100644
--- a/php/composer.lock
+++ b/php/composer.lock
@@ -391,16 +391,16 @@
},
{
"name": "laravel/serializable-closure",
- "version": "v2.0.7",
+ "version": "v2.0.8",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
- "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd"
+ "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd",
- "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b",
+ "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b",
"shasum": ""
},
"require": {
@@ -448,7 +448,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
- "time": "2025-11-21T20:52:36+00:00"
+ "time": "2026-01-08T16:22:46+00:00"
},
{
"name": "nikic/fast-route",
@@ -2755,22 +2755,22 @@
},
{
"name": "danog/advanced-json-rpc",
- "version": "v3.2.2",
+ "version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/danog/php-advanced-json-rpc.git",
- "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb"
+ "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/aadb1c4068a88c3d0530cfe324b067920661efcb",
- "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb",
+ "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/ae703ea7b4811797a10590b6078de05b3b33dd91",
+ "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91",
"shasum": ""
},
"require": {
"netresearch/jsonmapper": "^5",
"php": ">=8.1",
- "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
+ "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0 || ^6"
},
"replace": {
"felixfbecker/php-advanced-json-rpc": "^3"
@@ -2801,9 +2801,9 @@
"description": "A more advanced JSONRPC implementation",
"support": {
"issues": "https://github.com/danog/php-advanced-json-rpc/issues",
- "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.2"
+ "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.3"
},
- "time": "2025-02-14T10:55:15+00:00"
+ "time": "2026-01-12T21:07:10+00:00"
},
{
"name": "daverandom/libdns",
@@ -3455,16 +3455,16 @@
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "5.6.6",
+ "version": "6.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8"
+ "reference": "02600c041e7d0f4b7d1fe1d260565ec525472fa9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8",
- "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/02600c041e7d0f4b7d1fe1d260565ec525472fa9",
+ "reference": "02600c041e7d0f4b7d1fe1d260565ec525472fa9",
"shasum": ""
},
"require": {
@@ -3472,8 +3472,8 @@
"ext-filter": "*",
"php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.2",
- "phpdocumentor/type-resolver": "^1.7",
- "phpstan/phpdoc-parser": "^1.7|^2.0",
+ "phpdocumentor/type-resolver": "^2.0",
+ "phpstan/phpdoc-parser": "^2.0",
"webmozart/assert": "^1.9.1 || ^2"
},
"require-dev": {
@@ -3483,7 +3483,8 @@
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-webmozart-assert": "^1.2",
"phpunit/phpunit": "^9.5",
- "psalm/phar": "^5.26"
+ "psalm/phar": "^5.26",
+ "shipmonk/dead-code-detector": "^0.5.1"
},
"type": "library",
"extra": {
@@ -3513,44 +3514,44 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
- "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6"
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.0"
},
- "time": "2025-12-22T21:13:58+00:00"
+ "time": "2026-01-07T20:22:53+00:00"
},
{
"name": "phpdocumentor/type-resolver",
- "version": "1.12.0",
+ "version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
- "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195"
+ "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195",
- "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9",
+ "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
- "php": "^7.3 || ^8.0",
+ "php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.0",
- "phpstan/phpdoc-parser": "^1.18|^2.0"
+ "phpstan/phpdoc-parser": "^2.0"
},
"require-dev": {
"ext-tokenizer": "*",
"phpbench/phpbench": "^1.2",
- "phpstan/extension-installer": "^1.1",
- "phpstan/phpstan": "^1.8",
- "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
- "rector/rector": "^0.13.9",
- "vimeo/psalm": "^4.25"
+ "psalm/phar": "^4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-1.x": "1.x-dev"
+ "dev-1.x": "1.x-dev",
+ "dev-2.x": "2.x-dev"
}
},
"autoload": {
@@ -3571,22 +3572,22 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
- "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0"
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0"
},
- "time": "2025-11-21T15:09:14+00:00"
+ "time": "2026-01-06T21:53:42+00:00"
},
{
"name": "phpstan/phpdoc-parser",
- "version": "2.3.0",
+ "version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495"
+ "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495",
- "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374",
+ "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374",
"shasum": ""
},
"require": {
@@ -3618,9 +3619,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1"
},
- "time": "2025-08-30T15:50:23+00:00"
+ "time": "2026-01-12T11:33:04+00:00"
},
{
"name": "revolt/event-loop",
@@ -4735,16 +4736,16 @@
},
{
"name": "webmozart/assert",
- "version": "2.0.0",
+ "version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
- "reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54"
+ "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozarts/assert/zipball/1b34b004e35a164bc5bb6ebd33c844b2d8069a54",
- "reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649",
+ "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649",
"shasum": ""
},
"require": {
@@ -4791,9 +4792,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
- "source": "https://github.com/webmozarts/assert/tree/2.0.0"
+ "source": "https://github.com/webmozarts/assert/tree/2.1.2"
},
- "time": "2025-12-16T21:36:00+00:00"
+ "time": "2026-01-13T14:02:24+00:00"
}
],
"aliases": [],
diff --git a/php/psalm.xml b/php/psalm.xml
index d7ce38c9..576d82d2 100644
--- a/php/psalm.xml
+++ b/php/psalm.xml
@@ -20,5 +20,10 @@
+
+
+
+
+
diff --git a/php/public/index.php b/php/public/index.php
index b57f65a5..d4f4799f 100644
--- a/php/public/index.php
+++ b/php/public/index.php
@@ -91,10 +91,10 @@ $app->get('/containers', function (Request $request, Response $response, array $
$skip_domain_validation = isset($params['skip_domain_validation']);
return $view->render($response, 'containers.twig', [
- 'domain' => $configurationManager->GetDomain(),
- 'apache_port' => $configurationManager->GetApachePort(),
- 'borg_backup_host_location' => $configurationManager->GetBorgBackupHostLocation(),
- 'borg_remote_repo' => $configurationManager->GetBorgRemoteRepo(),
+ 'domain' => $configurationManager->domain,
+ 'apache_port' => $configurationManager->apache_port,
+ 'borg_backup_host_location' => $configurationManager->borg_backup_host_location,
+ 'borg_remote_repo' => $configurationManager->borg_remote_repo,
'borg_public_key' => $configurationManager->GetBorgPublicKey(),
'nextcloud_password' => $configurationManager->GetAndGenerateSecret('NEXTCLOUD_PASSWORD'),
'containers' => (new \AIO\ContainerDefinitionFetcher($container->get(\AIO\Data\ConfigurationManager::class), $container))->FetchDefinition(),
@@ -103,42 +103,42 @@ $app->get('/containers', function (Request $request, Response $response, array $
'has_backup_run_once' => $configurationManager->hasBackupRunOnce(),
'is_backup_container_running' => $dockerActionManager->isBackupContainerRunning(),
'backup_exit_code' => $dockerActionManager->GetBackupcontainerExitCode(),
- 'is_instance_restore_attempt' => $configurationManager->isInstanceRestoreAttempt(),
- 'borg_backup_mode' => $configurationManager->GetBackupMode(),
- 'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(),
+ 'is_instance_restore_attempt' => $configurationManager->instance_restore_attempt,
+ 'borg_backup_mode' => $configurationManager->backupMode,
+ 'was_start_button_clicked' => $configurationManager->wasStartButtonClicked,
'has_update_available' => $dockerActionManager->isAnyUpdateAvailable(),
'last_backup_time' => $configurationManager->GetLastBackupTime(),
'backup_times' => $configurationManager->GetBackupTimes(),
'current_channel' => $dockerActionManager->GetCurrentChannel(),
- 'is_clamav_enabled' => $configurationManager->isClamavEnabled(),
- 'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled(),
- 'is_collabora_enabled' => $configurationManager->isCollaboraEnabled(),
- 'is_talk_enabled' => $configurationManager->isTalkEnabled(),
- 'borg_restore_password' => $configurationManager->GetBorgRestorePassword(),
+ 'is_clamav_enabled' => $configurationManager->isClamavEnabled,
+ 'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled,
+ 'is_collabora_enabled' => $configurationManager->isCollaboraEnabled,
+ 'is_talk_enabled' => $configurationManager->isTalkEnabled,
+ 'borg_restore_password' => $configurationManager->borg_restore_password,
'daily_backup_time' => $configurationManager->GetDailyBackupTime(),
'is_daily_backup_running' => $configurationManager->isDailyBackupRunning(),
- 'timezone' => $configurationManager->GetTimezone(),
+ 'timezone' => $configurationManager->timezone,
'skip_domain_validation' => $configurationManager->shouldDomainValidationBeSkipped($skip_domain_validation),
- 'talk_port' => $configurationManager->GetTalkPort(),
- 'collabora_dictionaries' => $configurationManager->GetCollaboraDictionaries(),
- 'collabora_additional_options' => $configurationManager->GetAdditionalCollaboraOptions(),
+ 'talk_port' => $configurationManager->talk_port,
+ 'collabora_dictionaries' => $configurationManager->collabora_dictionaries,
+ 'collabora_additional_options' => $configurationManager->collabora_additional_options,
'automatic_updates' => $configurationManager->areAutomaticUpdatesEnabled(),
'is_backup_section_enabled' => $configurationManager->isBackupSectionEnabled(),
- 'is_imaginary_enabled' => $configurationManager->isImaginaryEnabled(),
- 'is_fulltextsearch_enabled' => $configurationManager->isFulltextsearchEnabled(),
+ 'is_imaginary_enabled' => $configurationManager->isImaginaryEnabled,
+ 'is_fulltextsearch_enabled' => $configurationManager->isFulltextsearchEnabled,
'additional_backup_directories' => $configurationManager->GetAdditionalBackupDirectoriesString(),
- 'nextcloud_datadir' => $configurationManager->GetNextcloudDatadirMount(),
- 'nextcloud_mount' => $configurationManager->GetNextcloudMount(),
- 'nextcloud_upload_limit' => $configurationManager->GetNextcloudUploadLimit(),
- 'nextcloud_max_time' => $configurationManager->GetNextcloudMaxTime(),
- 'nextcloud_memory_limit' => $configurationManager->GetNextcloudMemoryLimit(),
+ 'nextcloud_datadir' => $configurationManager->nextcloud_datadir_mount,
+ 'nextcloud_mount' => $configurationManager->nextcloud_mount,
+ 'nextcloud_upload_limit' => $configurationManager->nextcloud_upload_limit,
+ 'nextcloud_max_time' => $configurationManager->nextcloud_max_time,
+ 'nextcloud_memory_limit' => $configurationManager->nextcloud_memory_limit,
'is_dri_device_enabled' => $configurationManager->isDriDeviceEnabled(),
'is_nvidia_gpu_enabled' => $configurationManager->isNvidiaGpuEnabled(),
- 'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled(),
- 'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled(),
- 'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled(),
+ 'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled,
+ 'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled,
+ 'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled,
'community_containers' => $configurationManager->listAvailableCommunityContainers(),
- 'community_containers_enabled' => $configurationManager->GetEnabledCommunityContainers(),
+ 'community_containers_enabled' => $configurationManager->aio_community_containers,
'bypass_container_update' => $bypass_container_update,
]);
})->setName('profile');
diff --git a/php/src/Auth/AuthManager.php b/php/src/Auth/AuthManager.php
index 925ff89f..1d558aed 100644
--- a/php/src/Auth/AuthManager.php
+++ b/php/src/Auth/AuthManager.php
@@ -15,11 +15,11 @@ readonly class AuthManager {
}
public function CheckCredentials(string $password) : bool {
- return hash_equals($this->configurationManager->GetPassword(), $password);
+ return hash_equals($this->configurationManager->password, $password);
}
public function CheckToken(string $token) : bool {
- return hash_equals($this->configurationManager->GetToken(), $token);
+ return hash_equals($this->configurationManager->AIO_TOKEN, $token);
}
public function SetAuthState(bool $isLoggedIn) : void {
diff --git a/php/src/ContainerDefinitionFetcher.php b/php/src/ContainerDefinitionFetcher.php
index 7b092e45..fda8a7f9 100644
--- a/php/src/ContainerDefinitionFetcher.php
+++ b/php/src/ContainerDefinitionFetcher.php
@@ -41,7 +41,7 @@ readonly class ContainerDefinitionFetcher {
$data = json_decode((string)file_get_contents(DataConst::GetContainersDefinitionPath()), true, 512, JSON_THROW_ON_ERROR);
$additionalContainerNames = [];
- foreach ($this->configurationManager->GetEnabledCommunityContainers() as $communityContainer) {
+ foreach ($this->configurationManager->aio_community_containers as $communityContainer) {
if ($communityContainer !== '') {
$path = DataConst::GetCommunityContainersDirectory() . '/' . $communityContainer . '/' . $communityContainer . '.json';
$additionalData = json_decode((string)file_get_contents($path), true, 512, JSON_THROW_ON_ERROR);
@@ -56,42 +56,42 @@ readonly class ContainerDefinitionFetcher {
$containers = [];
foreach ($data['aio_services_v1'] as $entry) {
if ($entry['container_name'] === 'nextcloud-aio-clamav') {
- if (!$this->configurationManager->isClamavEnabled()) {
+ if (!$this->configurationManager->isClamavEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-onlyoffice') {
- if (!$this->configurationManager->isOnlyofficeEnabled()) {
+ if (!$this->configurationManager->isOnlyofficeEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-collabora') {
- if (!$this->configurationManager->isCollaboraEnabled()) {
+ if (!$this->configurationManager->isCollaboraEnabled) {
continue;
}
if ($this->configurationManager->isCollaboraSubscriptionEnabled()) {
$entry['image'] = 'ghcr.io/nextcloud-releases/aio-collabora-online';
}
} elseif ($entry['container_name'] === 'nextcloud-aio-talk') {
- if (!$this->configurationManager->isTalkEnabled()) {
+ if (!$this->configurationManager->isTalkEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-talk-recording') {
- if (!$this->configurationManager->isTalkRecordingEnabled()) {
+ if (!$this->configurationManager->isTalkRecordingEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-imaginary') {
- if (!$this->configurationManager->isImaginaryEnabled()) {
+ if (!$this->configurationManager->isImaginaryEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-fulltextsearch') {
- if (!$this->configurationManager->isFulltextsearchEnabled()) {
+ if (!$this->configurationManager->isFulltextsearchEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-docker-socket-proxy') {
- if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
+ if (!$this->configurationManager->isDockerSocketProxyEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-whiteboard') {
- if (!$this->configurationManager->isWhiteboardEnabled()) {
+ if (!$this->configurationManager->isWhiteboardEnabled) {
continue;
}
}
@@ -113,18 +113,18 @@ readonly class ContainerDefinitionFetcher {
if (isset($entry['volumes'])) {
foreach ($entry['volumes'] as $value) {
if($value['source'] === '%BORGBACKUP_HOST_LOCATION%') {
- $value['source'] = $this->configurationManager->GetBorgBackupHostLocation();
+ $value['source'] = $this->configurationManager->borg_backup_host_location;
if($value['source'] === '') {
continue;
}
}
if($value['source'] === '%NEXTCLOUD_MOUNT%') {
- $value['source'] = $this->configurationManager->GetNextcloudMount();
+ $value['source'] = $this->configurationManager->nextcloud_mount;
if($value['source'] === '') {
continue;
}
} elseif ($value['source'] === '%NEXTCLOUD_DATADIR%') {
- $value['source'] = $this->configurationManager->GetNextcloudDatadirMount();
+ $value['source'] = $this->configurationManager->nextcloud_datadir_mount;
if ($value['source'] === '') {
continue;
}
@@ -140,7 +140,7 @@ readonly class ContainerDefinitionFetcher {
}
}
if ($value['destination'] === '%NEXTCLOUD_MOUNT%') {
- $value['destination'] = $this->configurationManager->GetNextcloudMount();
+ $value['destination'] = $this->configurationManager->nextcloud_mount;
if($value['destination'] === '') {
continue;
}
@@ -168,39 +168,39 @@ readonly class ContainerDefinitionFetcher {
}
foreach ($valueDependsOn as $value) {
if ($value === 'nextcloud-aio-clamav') {
- if (!$this->configurationManager->isClamavEnabled()) {
+ if (!$this->configurationManager->isClamavEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-onlyoffice') {
- if (!$this->configurationManager->isOnlyofficeEnabled()) {
+ if (!$this->configurationManager->isOnlyofficeEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-collabora') {
- if (!$this->configurationManager->isCollaboraEnabled()) {
+ if (!$this->configurationManager->isCollaboraEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-talk') {
- if (!$this->configurationManager->isTalkEnabled()) {
+ if (!$this->configurationManager->isTalkEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-talk-recording') {
- if (!$this->configurationManager->isTalkRecordingEnabled()) {
+ if (!$this->configurationManager->isTalkRecordingEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-imaginary') {
- if (!$this->configurationManager->isImaginaryEnabled()) {
+ if (!$this->configurationManager->isImaginaryEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-fulltextsearch') {
- if (!$this->configurationManager->isFulltextsearchEnabled()) {
+ if (!$this->configurationManager->isFulltextsearchEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-docker-socket-proxy') {
- if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
+ if (!$this->configurationManager->isDockerSocketProxyEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-whiteboard') {
- if (!$this->configurationManager->isWhiteboardEnabled()) {
+ if (!$this->configurationManager->isWhiteboardEnabled) {
continue;
}
}
diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php
index 45586f9c..0330d082 100644
--- a/php/src/Controller/ConfigurationController.php
+++ b/php/src/Controller/ConfigurationController.php
@@ -67,63 +67,27 @@ readonly class ConfigurationController {
}
if (isset($request->getParsedBody()['delete_timezone'])) {
- $this->configurationManager->DeleteTimezone();
+ $this->configurationManager->deleteTimezone();
}
if (isset($request->getParsedBody()['timezone'])) {
$timezone = $request->getParsedBody()['timezone'] ?? '';
- $this->configurationManager->SetTimezone($timezone);
+ $this->configurationManager->timezone = $timezone;
}
if (isset($request->getParsedBody()['options-form'])) {
if (isset($request->getParsedBody()['collabora']) && isset($request->getParsedBody()['onlyoffice'])) {
throw new InvalidSettingConfigurationException("Collabora and Onlyoffice are not allowed to be enabled at the same time!");
}
- if (isset($request->getParsedBody()['clamav'])) {
- $this->configurationManager->SetClamavEnabledState(1);
- } else {
- $this->configurationManager->SetClamavEnabledState(0);
- }
- if (isset($request->getParsedBody()['onlyoffice'])) {
- $this->configurationManager->SetOnlyofficeEnabledState(1);
- } else {
- $this->configurationManager->SetOnlyofficeEnabledState(0);
- }
- if (isset($request->getParsedBody()['collabora'])) {
- $this->configurationManager->SetCollaboraEnabledState(1);
- } else {
- $this->configurationManager->SetCollaboraEnabledState(0);
- }
- if (isset($request->getParsedBody()['talk'])) {
- $this->configurationManager->SetTalkEnabledState(1);
- } else {
- $this->configurationManager->SetTalkEnabledState(0);
- }
- if (isset($request->getParsedBody()['talk-recording'])) {
- $this->configurationManager->SetTalkRecordingEnabledState(1);
- } else {
- $this->configurationManager->SetTalkRecordingEnabledState(0);
- }
- if (isset($request->getParsedBody()['imaginary'])) {
- $this->configurationManager->SetImaginaryEnabledState(1);
- } else {
- $this->configurationManager->SetImaginaryEnabledState(0);
- }
- if (isset($request->getParsedBody()['fulltextsearch'])) {
- $this->configurationManager->SetFulltextsearchEnabledState(1);
- } else {
- $this->configurationManager->SetFulltextsearchEnabledState(0);
- }
- if (isset($request->getParsedBody()['docker-socket-proxy'])) {
- $this->configurationManager->SetDockerSocketProxyEnabledState(1);
- } else {
- $this->configurationManager->SetDockerSocketProxyEnabledState(0);
- }
- if (isset($request->getParsedBody()['whiteboard'])) {
- $this->configurationManager->SetWhiteboardEnabledState(1);
- } else {
- $this->configurationManager->SetWhiteboardEnabledState(0);
- }
+ $this->configurationManager->isClamavEnabled = isset($request->getParsedBody()['clamav']);
+ $this->configurationManager->isOnlyofficeEnabled = isset($request->getParsedBody()['onlyoffice']);
+ $this->configurationManager->isCollaboraEnabled = isset($request->getParsedBody()['collabora']);
+ $this->configurationManager->isTalkEnabled = isset($request->getParsedBody()['talk']);
+ $this->configurationManager->isTalkRecordingEnabled = isset($request->getParsedBody()['talk-recording']);
+ $this->configurationManager->isImaginaryEnabled = isset($request->getParsedBody()['imaginary']);
+ $this->configurationManager->isFulltextsearchEnabled = isset($request->getParsedBody()['fulltextsearch']);
+ $this->configurationManager->isDockerSocketProxyEnabled = isset($request->getParsedBody()['docker-socket-proxy']);
+ $this->configurationManager->isWhiteboardEnabled = isset($request->getParsedBody()['whiteboard']);
}
if (isset($request->getParsedBody()['community-form'])) {
@@ -137,7 +101,7 @@ readonly class ConfigurationController {
$enabledCC[] = $item;
}
}
- $this->configurationManager->SetEnabledCommunityContainers($enabledCC);
+ $this->configurationManager->aio_community_containers = $enabledCC;
}
if (isset($request->getParsedBody()['delete_collabora_dictionaries'])) {
@@ -146,16 +110,16 @@ readonly class ConfigurationController {
if (isset($request->getParsedBody()['collabora_dictionaries'])) {
$collaboraDictionaries = $request->getParsedBody()['collabora_dictionaries'] ?? '';
- $this->configurationManager->SetCollaboraDictionaries($collaboraDictionaries);
+ $this->configurationManager->collabora_dictionaries = $collaboraDictionaries;
}
if (isset($request->getParsedBody()['delete_collabora_additional_options'])) {
- $this->configurationManager->DeleteAdditionalCollaboraOptions();
+ $this->configurationManager->deleteAdditionalCollaboraOptions();
}
if (isset($request->getParsedBody()['collabora_additional_options'])) {
$additionalCollaboraOptions = $request->getParsedBody()['collabora_additional_options'] ?? '';
- $this->configurationManager->SetAdditionalCollaboraOptions($additionalCollaboraOptions);
+ $this->configurationManager->collabora_additional_options = $additionalCollaboraOptions;
}
if (isset($request->getParsedBody()['delete_borg_backup_location_vars'])) {
diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php
index 27a06bc8..26dc22a3 100644
--- a/php/src/Controller/DockerController.php
+++ b/php/src/Controller/DockerController.php
@@ -89,7 +89,7 @@ readonly class DockerController {
}
public function startBackup(bool $forceStopNextcloud = false) : void {
- $this->configurationManager->SetBackupMode('backup');
+ $this->configurationManager->backupMode = 'backup';
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id, $forceStopNextcloud);
@@ -109,29 +109,25 @@ readonly class DockerController {
}
public function checkBackup() : void {
- $this->configurationManager->SetBackupMode('check');
+ $this->configurationManager->backupMode = 'check';
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
}
private function listBackup() : void {
- $this->configurationManager->SetBackupMode('list');
+ $this->configurationManager->backupMode = 'list';
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
}
public function StartBackupContainerRestore(Request $request, Response $response, array $args) : Response {
- $this->configurationManager->SetBackupMode('restore');
- $config = $this->configurationManager->GetConfig();
- $config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'] ?? '';
- if (isset($request->getParsedBody()['restore-exclude-previews'])) {
- $config['restore-exclude-previews'] = 1;
- } else {
- $config['restore-exclude-previews'] = '';
- }
- $this->configurationManager->WriteConfig($config);
+ $this->configurationManager->setMultiple(function (ConfigurationManager $confManager) use ($request) {
+ $confManager->backupMode = 'restore';
+ $confManager->selectedRestoreTime = $request->getParsedBody()['selected_restore_time'] ?? '';
+ $confManager->restoreExcludePreviews = isset($request->getParsedBody()['restore-exclude-previews']);
+ });
$id = self::TOP_CONTAINER;
$forceStopNextcloud = true;
@@ -144,22 +140,22 @@ readonly class DockerController {
}
public function StartBackupContainerCheckRepair(Request $request, Response $response, array $args) : Response {
- $this->configurationManager->SetBackupMode('check-repair');
+ $this->configurationManager->backupMode = 'check-repair';
$id = 'nextcloud-aio-borgbackup';
$this->PerformRecursiveContainerStart($id);
// Restore to backup check which is needed to make the UI logic work correctly
- $this->configurationManager->SetBackupMode('check');
+ $this->configurationManager->backupMode = 'check';
return $response->withStatus(201)->withHeader('Location', '.');
}
public function StartBackupContainerTest(Request $request, Response $response, array $args) : Response {
- $this->configurationManager->SetBackupMode('test');
- $config = $this->configurationManager->GetConfig();
- $config['instance_restore_attempt'] = 0;
- $this->configurationManager->WriteConfig($config);
+ $this->configurationManager->setMultiple(function (ConfigurationManager $confManager) {
+ $confManager->backupMode = 'test';
+ $confManager->instance_restore_attempt = false;
+ });
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);
@@ -181,21 +177,13 @@ readonly class DockerController {
$port = 443;
}
- if (isset($request->getParsedBody()['install_latest_major'])) {
- $installLatestMajor = 32;
- } else {
- $installLatestMajor = "";
- }
-
- $config = $this->configurationManager->GetConfig();
- // set AIO_URL
- $config['AIO_URL'] = $host . ':' . (string)$port . $path;
- // set wasStartButtonClicked
- $config['wasStartButtonClicked'] = 1;
- // set install_latest_major
- $config['install_latest_major'] = $installLatestMajor;
- $this->configurationManager->WriteConfig($config);
-
+ $this->configurationManager->setMultiple(function (ConfigurationManager $confManager) use ($request, $host, $port, $path) {
+ $confManager->install_latest_major = isset($request->getParsedBody()['install_latest_major']);
+ // set AIO_URL
+ $confManager->AIO_URL = $host . ':' . (string)$port . $path;
+ // set wasStartButtonClicked
+ $confManager->wasStartButtonClicked = true;
+ });
// Do not pull container images in case 'bypass_container_update' is set via url params
// Needed for local testing
$pullImage = !isset($request->getParsedBody()['bypass_container_update']);
@@ -213,10 +201,7 @@ readonly class DockerController {
}
public function startTopContainer(bool $pullImage) : void {
- $config = $this->configurationManager->GetConfig();
- // set AIO_TOKEN
- $config['AIO_TOKEN'] = bin2hex(random_bytes(24));
- $this->configurationManager->WriteConfig($config);
+ $this->configurationManager->AIO_TOKEN = bin2hex(random_bytes(24));
// Stop domaincheck since apache would not be able to start otherwise
$this->StopDomaincheckContainer();
@@ -244,7 +229,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->isCollaboraEnabled) {
$this->PerformRecursiveContainerStop('nextcloud-aio-collabora');
}
@@ -277,7 +262,7 @@ readonly class DockerController {
public function StartDomaincheckContainer() : void
{
# Don't start if domain is already set
- if ($this->configurationManager->GetDomain() !== '' || $this->configurationManager->wasStartButtonClicked()) {
+ if ($this->configurationManager->domain !== '' || $this->configurationManager->wasStartButtonClicked) {
return;
}
diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php
index 320bc477..2fb0a413 100644
--- a/php/src/Data/ConfigurationManager.php
+++ b/php/src/Data/ConfigurationManager.php
@@ -9,29 +9,209 @@ class ConfigurationManager
{
private array $secrets = [];
- public function GetConfig() : array
+ private array $config = [];
+
+ private bool $noWrite = false;
+
+ public string $AIO_TOKEN {
+ get => $this->get('AIO_TOKEN', '');
+ set { $this->set('AIO_TOKEN', $value); }
+ }
+
+ public string $password {
+ get => $this->get('password', '');
+ set { $this->set('password', $value); }
+ }
+
+ public bool $isDockerSocketProxyEnabled {
+ get => $this->get('isDockerSocketProxyEnabled', false);
+ set { $this->set('isDockerSocketProxyEnabled', $value); }
+ }
+
+ public bool $isWhiteboardEnabled {
+ get => $this->get('isWhiteboardEnabled', true);
+ set { $this->set('isWhiteboardEnabled', $value); }
+ }
+
+ public bool $restoreExcludePreviews {
+ get => $this->get('restore-exclude-previews', false);
+ set { $this->set('restore-exclude-previews', $value); }
+ }
+
+ public string $selectedRestoreTime {
+ get => $this->get('selected-restore-time', '');
+ set { $this->set('selected-restore-time', $value); }
+ }
+
+ public string $backupMode {
+ get => $this->get('backup-mode', '');
+ set { $this->set('backup-mode', $value); }
+ }
+
+ public bool $instance_restore_attempt {
+ get => $this->get('instance_restore_attempt', false);
+ set { $this->set('instance_restore_attempt', $value); }
+ }
+
+ public string $AIO_URL {
+ get => $this->get('AIO_URL', '');
+ set { $this->set('AIO_URL', $value); }
+ }
+
+ public bool $wasStartButtonClicked {
+ get => $this->get('wasStartButtonClicked', false);
+ set { $this->set('wasStartButtonClicked', $value); }
+ }
+
+ public bool $install_latest_major {
+ get => $this->get('install_latest_major', false);
+ set { $this->set('install_latest_major', $value); }
+ }
+
+ public bool $isClamavEnabled {
+ get => $this->get('isClamavEnabled', false);
+ set { $this->set('isClamavEnabled', $value); }
+ }
+
+ public bool $isOnlyofficeEnabled {
+ get => $this->get('isOnlyofficeEnabled', false);
+ set { $this->set('isOnlyofficeEnabled', $value); }
+ }
+
+ public bool $isCollaboraEnabled {
+ get => $this->get('isCollaboraEnabled', true);
+ set { $this->set('isCollaboraEnabled', $value); }
+ }
+
+ public bool $isTalkEnabled {
+ get => $this->get('isTalkEnabled', true);
+ set { $this->set('isTalkEnabled', $value); }
+ }
+
+ public bool $isTalkRecordingEnabled {
+ get => $this->isTalkEnabled && $this->get('isTalkRecordingEnabled', false);
+ set { $this->set('isTalkRecordingEnabled', $this->isTalkEnabled && $value); }
+ }
+
+ public bool $isImaginaryEnabled {
+ get => $this->get('isImaginaryEnabled', true);
+ set { $this->set('isImaginaryEnabled', $value); }
+ }
+
+ public bool $isFulltextsearchEnabled {
+ get => $this->get('isFulltextsearchEnabled', false);
+ // Elasticsearch does not work on kernels without seccomp anymore. See https://github.com/nextcloud/all-in-one/discussions/5768
+ set { $this->set('isFulltextsearchEnabled', ($this->isSeccompDisabled() && $value)); }
+ }
+
+ public string $domain {
+ get => $this->get('domain', '');
+ set { $this->SetDomain($value); }
+ }
+
+ public string $borg_backup_host_location {
+ get => $this->get('borg_backup_host_location', '');
+ set { $this->set('borg_backup_host_location', $value); }
+ }
+
+ public string $borg_remote_repo {
+ get => $this->get('borg_remote_repo', '');
+ set { $this->set('borg_remote_repo', $value); }
+ }
+
+ public string $borg_restore_password {
+ get => $this->get('borg_restore_password', '');
+ set { $this->set('borg_restore_password', $value); }
+ }
+
+ public string $apache_ip_binding {
+ get => $this->GetEnvironmentalVariableOrConfig('APACHE_IP_BINDING', 'apache_ip_binding', '');
+ set { $this->set('apache_ip_binding', $value); }
+ }
+
+ /**
+ * @throws InvalidSettingConfigurationException
+ */
+ public string $timezone {
+ get => $this->get('timezone', '');
+ set {
+ // This throws an exception if the validation fails.
+ $this->validateTimezone($value);
+ $this->set('timezone', $value);
+ }
+ }
+
+ /**
+ * @throws InvalidSettingConfigurationException
+ */
+ public string $collabora_dictionaries {
+ get => $this->get('collabora_dictionaries', '');
+ set {
+ // This throws an exception if the validation fails.
+ $this->validateCollaboraDictionaries($value);
+ $this->set('collabora_dictionaries', $value);
+ }
+ }
+
+ /**
+ * @throws InvalidSettingConfigurationException
+ */
+ public string $collabora_additional_options {
+ get => $this->get('collabora_additional_options', '');
+ set {
+ // This throws an exception if the validation fails.
+ $this->validateCollaboraAdditionalOptions($value);
+ $this->set('collabora_additional_options', $value);
+ }
+ }
+
+ public array $aio_community_containers {
+ get => explode(' ', $this->get('aio_community_containers', ''));
+ set { $this->set('aio_community_containers', implode(' ', $value)); }
+ }
+
+ public string $turn_domain {
+ get => $this->get('turn_domain', '');
+ set { $this->set('turn_domain', $value); }
+ }
+
+ private function GetConfig() : array
{
- if(file_exists(DataConst::GetConfigFile()))
+ if ($this->config === [] && file_exists(DataConst::GetConfigFile()))
{
$configContent = (string)file_get_contents(DataConst::GetConfigFile());
- return json_decode($configContent, true, 512, JSON_THROW_ON_ERROR);
+ $this->config = json_decode($configContent, true, 512, JSON_THROW_ON_ERROR);
}
- return [];
+ return $this->config;
}
- public function GetPassword() : string {
- return $this->GetConfig()['password'];
+ private function get(string $key, mixed $fallbackValue = null) : mixed {
+ return $this->GetConfig()[$key] ?? $fallbackValue;
}
- public function GetToken() : string {
- return $this->GetConfig()['AIO_TOKEN'];
+ private function set(string $key, mixed $value) : void {
+ $this->GetConfig();
+ $this->config[$key] = $value;
+ // Only write if this isn't called via setMultiple().
+ if ($this->noWrite !== true) {
+ $this->WriteConfig();
+ }
}
- public function SetPassword(string $password) : void {
- $config = $this->GetConfig();
- $config['password'] = $password;
- $this->WriteConfig($config);
+ /**
+ * This allows to assign multiple attributes without saving the config to disk in between (as would
+ * calling set() do).
+ */
+ public function setMultiple(\Closure $closure) : void {
+ $this->noWrite = true;
+ try {
+ $this->GetConfig();
+ $closure($this);
+ $this->WriteConfig();
+ } finally {
+ $this->noWrite = false;
+ }
}
public function GetAndGenerateSecret(string $secretId) : string {
@@ -39,17 +219,17 @@ class ConfigurationManager
return '';
}
- $config = $this->GetConfig();
- if(!isset($config['secrets'][$secretId])) {
- $config['secrets'][$secretId] = bin2hex(random_bytes(24));
- $this->WriteConfig($config);
+ $secrets = $this->get('secrets', []);
+ if (!isset($secrets[$secretId])) {
+ $secrets[$secretId] = bin2hex(random_bytes(24));
+ $this->set('secrets', $secrets);
}
if ($secretId === 'BORGBACKUP_PASSWORD' && !file_exists(DataConst::GetBackupSecretFile())) {
- $this->DoubleSafeBackupSecret($config['secrets'][$secretId]);
+ $this->DoubleSafeBackupSecret($secrets[$secretId]);
}
- return $config['secrets'][$secretId];
+ return $secrets[$secretId];
}
public function GetRegisteredSecret(string $secretId) : string {
@@ -122,14 +302,6 @@ class ConfigurationManager
return $backupTimes;
}
- public function wasStartButtonClicked() : bool {
- if (isset($this->GetConfig()['wasStartButtonClicked'])) {
- return true;
- } else {
- return false;
- }
- }
-
private function isx64Platform() : bool {
if (php_uname('m') === 'x86_64') {
return true;
@@ -138,155 +310,10 @@ class ConfigurationManager
}
}
- public function isClamavEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isClamavEnabled']) && $config['isClamavEnabled'] === 1) {
- return true;
- } else {
- return false;
- }
- }
-
- public function isDockerSocketProxyEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isDockerSocketProxyEnabled']) && $config['isDockerSocketProxyEnabled'] === 1) {
- return true;
- } else {
- return false;
- }
- }
-
- public function SetDockerSocketProxyEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isDockerSocketProxyEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isWhiteboardEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isWhiteboardEnabled']) && $config['isWhiteboardEnabled'] === 0) {
- return false;
- } else {
- return true;
- }
- }
-
- public function SetWhiteboardEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isWhiteboardEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function SetClamavEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isClamavEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isImaginaryEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isImaginaryEnabled']) && $config['isImaginaryEnabled'] === 0) {
- return false;
- } else {
- return true;
- }
- }
-
- public function SetImaginaryEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isImaginaryEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isFulltextsearchEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isFulltextsearchEnabled']) && $config['isFulltextsearchEnabled'] === 1) {
- return true;
- } else {
- return false;
- }
- }
-
- public function SetFulltextsearchEnabledState(int $value) : void {
- // Elasticsearch does not work on kernels without seccomp anymore. See https://github.com/nextcloud/all-in-one/discussions/5768
- if ($this->isSeccompDisabled()) {
- $value = 0;
- }
-
- $config = $this->GetConfig();
- $config['isFulltextsearchEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isOnlyofficeEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isOnlyofficeEnabled']) && $config['isOnlyofficeEnabled'] === 1) {
- return true;
- } else {
- return false;
- }
- }
-
- public function SetOnlyofficeEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isOnlyofficeEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isCollaboraEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isCollaboraEnabled']) && $config['isCollaboraEnabled'] === 0) {
- return false;
- } else {
- return true;
- }
- }
-
- public function SetCollaboraEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isCollaboraEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isTalkEnabled() : bool {
- $config = $this->GetConfig();
- if (isset($config['isTalkEnabled']) && $config['isTalkEnabled'] === 0) {
- return false;
- } else {
- return true;
- }
- }
-
- public function SetTalkEnabledState(int $value) : void {
- $config = $this->GetConfig();
- $config['isTalkEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
- public function isTalkRecordingEnabled() : bool {
- if (!$this->isTalkEnabled()) {
- return false;
- }
- $config = $this->GetConfig();
- if (isset($config['isTalkRecordingEnabled']) && $config['isTalkRecordingEnabled'] === 1) {
- return true;
- } else {
- return false;
- }
- }
-
- public function SetTalkRecordingEnabledState(int $value) : void {
- if (!$this->isTalkEnabled()) {
- $value = 0;
- }
-
- $config = $this->GetConfig();
- $config['isTalkRecordingEnabled'] = $value;
- $this->WriteConfig($config);
- }
-
/**
* @throws InvalidSettingConfigurationException
+ *
+ * We can't turn this into a private validation method because of the second argument.
*/
public function SetDomain(string $domain, bool $skipDomainValidation) : void {
// Validate that at least one dot is contained
@@ -336,7 +363,7 @@ class ConfigurationManager
}
// Get the apache port
- $port = $this->GetApachePort();
+ $port = $this->apache_port;
if (!filter_var($dnsRecordIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
if ($port === '443') {
@@ -392,83 +419,32 @@ class ConfigurationManager
}
}
- // Write domain
- $config = $this->GetConfig();
- $config['domain'] = $domain;
- // Reset the borg restore password when setting the domain
- $config['borg_restore_password'] = '';
- $this->WriteConfig($config);
- }
-
- public function GetDomain() : string {
- $config = $this->GetConfig();
- if(!isset($config['domain'])) {
- $config['domain'] = '';
- }
-
- return $config['domain'];
+ $this->setMultiple(function (ConfigurationManager $confManager) use ($domain) {
+ // Write domain
+ // Don't set the domain via the attribute, or we create a loop.
+ $confManager->set('domain', $domain);
+ // Reset the borg restore password when setting the domain
+ $confManager->borg_restore_password = '';
+ });
}
public function GetBaseDN() : string {
- $domain = $this->GetDomain();
+ $domain = $this->domain;
if ($domain === "") {
return "";
}
return 'dc=' . implode(',dc=', explode('.', $domain));
}
- public function GetBackupMode() : string {
- $config = $this->GetConfig();
- if(!isset($config['backup-mode'])) {
- $config['backup-mode'] = '';
- }
-
- return $config['backup-mode'];
- }
-
- public function SetBackupMode(string $mode) : void {
- $config = $this->GetConfig();
- $config['backup-mode'] = $mode;
- $this->WriteConfig($config);
- }
-
- public function GetSelectedRestoreTime() : string {
- $config = $this->GetConfig();
- if(!isset($config['selected-restore-time'])) {
- $config['selected-restore-time'] = '';
- }
-
- return $config['selected-restore-time'];
- }
-
- public function GetRestoreExcludePreviews() : string {
- $config = $this->GetConfig();
- if(!isset($config['restore-exclude-previews'])) {
- $config['restore-exclude-previews'] = '';
- }
-
- return $config['restore-exclude-previews'];
- }
-
- public function GetAIOURL() : string {
- $config = $this->GetConfig();
- if(!isset($config['AIO_URL'])) {
- $config['AIO_URL'] = '';
- }
-
- return $config['AIO_URL'];
- }
-
/**
* @throws InvalidSettingConfigurationException
*/
public function SetBorgLocationVars(string $location, string $repo) : void {
$this->ValidateBorgLocationVars($location, $repo);
-
- $config = $this->GetConfig();
- $config['borg_backup_host_location'] = $location;
- $config['borg_remote_repo'] = $repo;
- $this->WriteConfig($config);
+ $this->setMultiple(function (ConfigurationManager $confManager) use ($location, $repo) {
+ $confManager->borg_backup_host_location = $location;
+ $confManager->borg_remote_repo = $repo;
+ });
}
private function ValidateBorgLocationVars(string $location, string $repo) : void {
@@ -492,8 +468,8 @@ class ConfigurationManager
// Prevent backup to be contained in Nextcloud Datadir as this will delete the backup archive upon restore
// See https://github.com/nextcloud/all-in-one/issues/6607
- if (str_starts_with($location . '/', rtrim($this->GetNextcloudDatadirMount(), '/') . '/')) {
- throw new InvalidSettingConfigurationException("The path must not be a children of or equal to NEXTCLOUD_DATADIR, which is currently set to " . $this->GetNextcloudDatadirMount());
+ if (str_starts_with($location . '/', rtrim($this->nextcloud_datadir_mount, '/') . '/')) {
+ throw new InvalidSettingConfigurationException("The path must not be a children of or equal to NEXTCLOUD_DATADIR, which is currently set to " . $this->nextcloud_datadir_mount);
}
} else {
@@ -514,10 +490,10 @@ class ConfigurationManager
public function DeleteBorgBackupLocationItems() : void {
// Delete the variables
- $config = $this->GetConfig();
- $config['borg_backup_host_location'] = '';
- $config['borg_remote_repo'] = '';
- $this->WriteConfig($config);
+ $this->setMultiple(function (ConfigurationManager $confManager) {
+ $confManager->borg_backup_host_location = '';
+ $confManager->borg_remote_repo = '';
+ });
// Also delete the borg config file to be able to start over
if (file_exists(DataConst::GetBackupKeyFile())) {
@@ -537,12 +513,12 @@ class ConfigurationManager
throw new InvalidSettingConfigurationException("Please enter the password!");
}
- $config = $this->GetConfig();
- $config['borg_backup_host_location'] = $location;
- $config['borg_remote_repo'] = $repo;
- $config['borg_restore_password'] = $password;
- $config['instance_restore_attempt'] = 1;
- $this->WriteConfig($config);
+ $this->setMultiple(function (ConfigurationManager $confManager) use ($location, $repo, $password) {
+ $confManager->borg_backup_host_location = $location;
+ $confManager->borg_remote_repo = $repo;
+ $confManager->borg_restore_password = $password;
+ $confManager->instance_restore_attempt = true;
+ });
}
/**
@@ -553,7 +529,7 @@ class ConfigurationManager
throw new InvalidSettingConfigurationException("Please enter your current password.");
}
- if ($currentPassword !== $this->GetPassword()) {
+ if ($currentPassword !== $this->password) {
throw new InvalidSettingConfigurationException("The entered current password is not correct.");
}
@@ -570,88 +546,60 @@ class ConfigurationManager
}
// All checks pass so set the password
- $this->SetPassword($newPassword);
+ $this->set('password', $newPassword);
}
- public function GetApachePort() : string {
- $envVariableName = 'APACHE_PORT';
- $configName = 'apache_port';
- $defaultValue = '443';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
+ public string $apache_port {
+ get => $this->GetEnvironmentalVariableOrConfig('APACHE_PORT', 'apache_port', '443');
+ set { $this->set('apache_port', $value); }
}
-
- public function GetTalkPort() : string {
- $envVariableName = 'TALK_PORT';
- $configName = 'talk_port';
- $defaultValue = '3478';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
- }
-
- public function GetTurnDomain() : string {
- $config = $this->GetConfig();
- if(!isset($config['turn_domain'])) {
- $config['turn_domain'] = '';
- }
-
- return $config['turn_domain'];
+
+ public string $talk_port {
+ get => $this->GetEnvironmentalVariableOrConfig('TALK_PORT', 'talk_port', '3478');
+ set { $this->set('talk_port', $value); }
}
/**
* @throws InvalidSettingConfigurationException
*/
- public function WriteConfig(array $config) : void {
+ private function WriteConfig() : void {
if(!is_dir(DataConst::GetDataDirectory())) {
throw new InvalidSettingConfigurationException(DataConst::GetDataDirectory() . " does not exist! Something was set up falsely!");
}
+ // Shouldn't happen, but as a precaution we won't write an empty config to disk.
+ if ($this->config === []) {
+ return;
+ }
$df = disk_free_space(DataConst::GetDataDirectory());
- $content = json_encode($config, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR);
+ $content = json_encode($this->config, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR);
$size = strlen($content) + 10240;
if ($df !== false && (int)$df < $size) {
throw new InvalidSettingConfigurationException(DataConst::GetDataDirectory() . " does not have enough space for writing the config file! Not writing it back!");
}
file_put_contents(DataConst::GetConfigFile(), $content);
+ $this->config = [];
}
private function GetEnvironmentalVariableOrConfig(string $envVariableName, string $configName, string $defaultValue) : string {
$envVariableOutput = getenv($envVariableName);
+ $configValue = $this->get($configName, '');
if ($envVariableOutput === false) {
- $config = $this->GetConfig();
- if (!isset($config[$configName]) || $config[$configName] === '') {
- $config[$configName] = $defaultValue;
+ if ($configValue === '') {
+ $this->set($configName, $defaultValue);
+ return $defaultValue;
}
- return $config[$configName];
+ return $configValue;
}
- if(file_exists(DataConst::GetConfigFile())) {
- $config = $this->GetConfig();
- if (!isset($config[$configName])) {
- $config[$configName] = '';
- }
- if ($envVariableOutput !== $config[$configName]) {
- $config[$configName] = $envVariableOutput;
- $this->WriteConfig($config);
+
+ if (file_exists(DataConst::GetConfigFile())) {
+ if ($envVariableOutput !== $configValue) {
+ $this->set($configName, $envVariableOutput);
}
}
+
return $envVariableOutput;
}
- public function GetBorgBackupHostLocation() : string {
- $config = $this->GetConfig();
- if(!isset($config['borg_backup_host_location'])) {
- $config['borg_backup_host_location'] = '';
- }
-
- return $config['borg_backup_host_location'];
- }
-
- public function GetBorgRemoteRepo() : string {
- $config = $this->GetConfig();
- if(!isset($config['borg_remote_repo'])) {
- $config['borg_remote_repo'] = '';
- }
-
- return $config['borg_remote_repo'];
- }
-
public function GetBorgPublicKey() : string {
if (!file_exists(DataConst::GetBackupPublicKey())) {
return "";
@@ -660,65 +608,35 @@ class ConfigurationManager
return trim((string)file_get_contents(DataConst::GetBackupPublicKey()));
}
- public function GetBorgRestorePassword() : string {
- $config = $this->GetConfig();
- if(!isset($config['borg_restore_password'])) {
- $config['borg_restore_password'] = '';
- }
-
- return $config['borg_restore_password'];
+ public string $nextcloud_mount {
+ get => $this->GetEnvironmentalVariableOrConfig('NEXTCLOUD_MOUNT', 'nextcloud_mount', '');
+ set { $this->set('nextcloud_mount', $value); }
}
- public function isInstanceRestoreAttempt() : bool {
- $config = $this->GetConfig();
- if(!isset($config['instance_restore_attempt'])) {
- $config['instance_restore_attempt'] = '';
- }
- if ($config['instance_restore_attempt'] === 1) {
- return true;
- }
- return false;
+ public string $nextcloud_datadir_mount {
+ get => $this->GetEnvironmentalVariableOrConfig('NEXTCLOUD_DATADIR', 'nextcloud_datadir', 'nextcloud_aio_nextcloud_data');
+ set { $this->set('nextcloud_datadir_mount', $value); }
}
- public function GetNextcloudMount() : string {
- $envVariableName = 'NEXTCLOUD_MOUNT';
- $configName = 'nextcloud_mount';
- $defaultValue = '';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
+ public string $nextcloud_upload_limit {
+ get => $this->GetEnvironmentalVariableOrConfig('NEXTCLOUD_UPLOAD_LIMIT', 'nextcloud_upload_limit', '16G');
+ set { $this->set('nextcloud_upload_limit', $value); }
}
-
- public function GetNextcloudDatadirMount() : string {
- $envVariableName = 'NEXTCLOUD_DATADIR';
- $configName = 'nextcloud_datadir';
- $defaultValue = 'nextcloud_aio_nextcloud_data';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
- }
-
- public function GetNextcloudUploadLimit() : string {
- $envVariableName = 'NEXTCLOUD_UPLOAD_LIMIT';
- $configName = 'nextcloud_upload_limit';
- $defaultValue = '16G';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
- }
-
- public function GetNextcloudMemoryLimit() : string {
- $envVariableName = 'NEXTCLOUD_MEMORY_LIMIT';
- $configName = 'nextcloud_memory_limit';
- $defaultValue = '512M';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
+
+ public string $nextcloud_memory_limit {
+ get => $this->GetEnvironmentalVariableOrConfig('NEXTCLOUD_MEMORY_LIMIT', 'nextcloud_memory_limit', '512M');
+ set { $this->set('nextcloud_memory_limit', $value); }
}
public function GetApacheMaxSize() : int {
- $uploadLimit = (int)rtrim($this->GetNextcloudUploadLimit(), 'G');
+ $uploadLimit = (int)rtrim($this->nextcloud_upload_limit, 'G');
return $uploadLimit * 1024 * 1024 * 1024;
}
- public function GetNextcloudMaxTime() : string {
- $envVariableName = 'NEXTCLOUD_MAX_TIME';
- $configName = 'nextcloud_max_time';
- $defaultValue = '3600';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
+ public string $nextcloud_max_time {
+ get => $this->GetEnvironmentalVariableOrConfig('NEXTCLOUD_MAX_TIME', 'nextcloud_max_time', '3600');
+ set { $this->set('nextcloud_max_time', $value); }
}
public function GetBorgRetentionPolicy() : string {
@@ -779,10 +697,7 @@ class ConfigurationManager
}
public function isSeccompDisabled() : bool {
- if ($this->GetCollaboraSeccompDisabledState() === 'true') {
- return true;
- }
- return false;
+ return $this->GetCollaboraSeccompDisabledState() === 'true';
}
/**
@@ -862,14 +777,6 @@ class ConfigurationManager
}
}
- public function shouldLatestMajorGetInstalled() : bool {
- $config = $this->GetConfig();
- if(!isset($config['install_latest_major'])) {
- $config['install_latest_major'] = '';
- }
- return $config['install_latest_major'] !== '';
- }
-
public function GetAdditionalBackupDirectoriesString() : string {
if (!file_exists(DataConst::GetAdditionalBackupDirectoriesFile())) {
return '';
@@ -885,25 +792,13 @@ class ConfigurationManager
}
public function isDailyBackupRunning() : bool {
- if (file_exists(DataConst::GetDailyBackupBlockFile())) {
- return true;
- }
- return false;
- }
-
- public function GetTimezone() : string {
- $config = $this->GetConfig();
- if(!isset($config['timezone'])) {
- $config['timezone'] = '';
- }
-
- return $config['timezone'];
+ return file_exists(DataConst::GetDailyBackupBlockFile());
}
/**
* @throws InvalidSettingConfigurationException
*/
- public function SetTimezone(string $timezone) : void {
+ private function validateTimezone(string $timezone) : void {
if ($timezone === "") {
throw new InvalidSettingConfigurationException("The timezone must not be empty!");
}
@@ -911,16 +806,13 @@ class ConfigurationManager
if (!preg_match("#^[a-zA-Z0-9_\-\/\+]+$#", $timezone)) {
throw new InvalidSettingConfigurationException("The entered timezone does not seem to be a valid timezone!");
}
-
- $config = $this->GetConfig();
- $config['timezone'] = $timezone;
- $this->WriteConfig($config);
}
- public function DeleteTimezone() : void {
- $config = $this->GetConfig();
- $config['timezone'] = '';
- $this->WriteConfig($config);
+ /**
+ * Provide an extra method since our `timezone` attribute setter prevents setting an empty timezone.
+ */
+ public function deleteTimezone() : void {
+ $this->set('timezone', '');
}
public function shouldDomainValidationBeSkipped(bool $skipDomainValidation) : bool {
@@ -938,19 +830,10 @@ class ConfigurationManager
return 'deck twofactor_totp tasks calendar contacts notes';
}
- public function GetCollaboraDictionaries() : string {
- $config = $this->GetConfig();
- if(!isset($config['collabora_dictionaries'])) {
- $config['collabora_dictionaries'] = '';
- }
-
- return $config['collabora_dictionaries'];
- }
-
/**
* @throws InvalidSettingConfigurationException
*/
- public function SetCollaboraDictionaries(string $CollaboraDictionaries) : void {
+ private function validateCollaboraDictionaries(string $CollaboraDictionaries) : void {
if ($CollaboraDictionaries === "") {
throw new InvalidSettingConfigurationException("The dictionaries must not be empty!");
}
@@ -958,22 +841,19 @@ class ConfigurationManager
if (!preg_match("#^[a-zA-Z_ ]+$#", $CollaboraDictionaries)) {
throw new InvalidSettingConfigurationException("The entered dictionaries do not seem to be a valid!");
}
-
- $config = $this->GetConfig();
- $config['collabora_dictionaries'] = $CollaboraDictionaries;
- $this->WriteConfig($config);
}
+ /**
+ * Provide an extra method since the corresponding attribute setter prevents setting an empty value.
+ */
public function DeleteCollaboraDictionaries() : void {
- $config = $this->GetConfig();
- $config['collabora_dictionaries'] = '';
- $this->WriteConfig($config);
+ $this->set('collabora_dictionaries', '');
}
/**
* @throws InvalidSettingConfigurationException
*/
- public function SetAdditionalCollaboraOptions(string $additionalCollaboraOptions) : void {
+ private function validateCollaboraAdditionalOptions(string $additionalCollaboraOptions) : void {
if ($additionalCollaboraOptions === "") {
throw new InvalidSettingConfigurationException("The additional options must not be empty!");
}
@@ -981,32 +861,17 @@ class ConfigurationManager
if (!preg_match("#^--o:#", $additionalCollaboraOptions)) {
throw new InvalidSettingConfigurationException("The entered options must start with '--o:'. So the config does not seem to be a valid!");
}
-
- $config = $this->GetConfig();
- $config['collabora_additional_options'] = $additionalCollaboraOptions;
- $this->WriteConfig($config);
- }
-
- public function GetAdditionalCollaboraOptions() : string {
- $config = $this->GetConfig();
- if(!isset($config['collabora_additional_options'])) {
- $config['collabora_additional_options'] = '';
- }
-
- return $config['collabora_additional_options'];
}
public function isCollaboraSubscriptionEnabled() : bool {
- if (str_contains($this->GetAdditionalCollaboraOptions(), '--o:support_key=')) {
- return true;
- }
- return false;
+ return str_contains($this->collabora_additional_options, '--o:support_key=');
}
- public function DeleteAdditionalCollaboraOptions() : void {
- $config = $this->GetConfig();
- $config['collabora_additional_options'] = '';
- $this->WriteConfig($config);
+ /**
+ * Provide an extra method since the corresponding attribute setter prevents setting an empty value.
+ */
+ public function deleteAdditionalCollaboraOptions() : void {
+ $this->set('collabora_additional_options', '');
}
public function GetApacheAdditionalNetwork() : string {
@@ -1016,13 +881,6 @@ class ConfigurationManager
return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
}
- public function GetApacheIPBinding() : string {
- $envVariableName = 'APACHE_IP_BINDING';
- $configName = 'apache_ip_binding';
- $defaultValue = '';
- return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
- }
-
private function GetDisableBackupSection() : string {
$envVariableName = 'AIO_DISABLE_BACKUP_SECTION';
$configName = 'disable_backup_section';
@@ -1038,16 +896,6 @@ class ConfigurationManager
}
}
- private function GetCommunityContainers() : string {
- $config = $this->GetConfig();
- if(!isset($config['aio_community_containers'])) {
- $config['aio_community_containers'] = '';
- }
-
- return $config['aio_community_containers'];
- }
-
-
public function listAvailableCommunityContainers() : array {
$cc = [];
$dir = scandir(DataConst::GetCommunityContainersDirectory());
@@ -1083,17 +931,6 @@ class ConfigurationManager
return $cc;
}
- /** @return list */
- public function GetEnabledCommunityContainers(): array {
- return explode(' ', $this->GetCommunityContainers());
- }
-
- public function SetEnabledCommunityContainers(array $enabledCommunityContainers) : void {
- $config = $this->GetConfig();
- $config['aio_community_containers'] = implode(' ', $enabledCommunityContainers);
- $this->WriteConfig($config);
- }
-
private function GetEnabledDriDevice() : string {
$envVariableName = 'NEXTCLOUD_ENABLE_DRI_DEVICE';
$configName = 'nextcloud_enable_dri_device';
@@ -1134,4 +971,97 @@ class ConfigurationManager
return true;
}
}
+
+ public function setAioVariables(array $input) : void {
+ if ($input === []) {
+ return;
+ }
+ $this->setMultiple(function(ConfigurationManager $confManager) use ($input) {
+ foreach ($input as $variable) {
+ $keyWithValue = $confManager->replaceEnvPlaceholders($variable);
+ [$key, $value] = explode('=', $keyWithValue, 2);
+ // Set if there's an attribute corresponding to the key.
+ if (isset($key, $confManager->$key)) {
+ $confManager->$key = $value;
+ }
+ }
+ });
+ }
+
+ //
+ // Replaces placeholders in $envValue with their values.
+ // E.g. "%NC_DOMAIN%:%APACHE_PORT" becomes "my.nextcloud.com:11000"
+ public function replaceEnvPlaceholders(string $envValue): string {
+ // $pattern breaks down as:
+ // % - matches a literal percent sign
+ // ([^%]+) - capture group that matches one or more characters that are NOT percent signs
+ // % - matches the closing percent sign
+ //
+ // Assumes literal percent signs are always matched and there is no
+ // escaping.
+ $pattern = '/%([^%]+)%/';
+ $matchCount = preg_match_all($pattern, $envValue, $matches);
+
+ if ($matchCount === 0) {
+ return $envValue;
+ }
+
+ $placeholders = $matches[0]; // ["%PLACEHOLDER1%", "%PLACEHOLDER2%", ...]
+ $placeholderNames = $matches[1]; // ["PLACEHOLDER1", "PLACEHOLDER2", ...]
+ $placeholderPatterns = array_map(static fn(string $p) => '/' . preg_quote($p) . '/', $placeholders); // ["/%PLACEHOLDER1%/", ...]
+ $placeholderValues = array_map($this->getPlaceholderValue(...), $placeholderNames); // ["val1", "val2"]
+ // Guaranteed to be non-null because we found the placeholders in the preg_match_all.
+ return (string) preg_replace($placeholderPatterns, $placeholderValues, $envValue);
+ }
+
+ private function getPlaceholderValue(string $placeholder) : string {
+ return match ($placeholder) {
+ 'NC_DOMAIN' => $this->domain,
+ 'NC_BASE_DN' => $this->GetBaseDN(),
+ 'AIO_TOKEN' => $this->AIO_TOKEN,
+ 'BORGBACKUP_REMOTE_REPO' => $this->borg_remote_repo,
+ 'BORGBACKUP_MODE' => $this->backupMode,
+ 'AIO_URL' => $this->AIO_URL,
+ 'SELECTED_RESTORE_TIME' => $this->selectedRestoreTime,
+ 'RESTORE_EXCLUDE_PREVIEWS' => $this->restoreExcludePreviews ? '1' : '',
+ 'APACHE_PORT' => $this->apache_port,
+ 'APACHE_IP_BINDING' => $this->apache_ip_binding,
+ 'TALK_PORT' => $this->talk_port,
+ 'TURN_DOMAIN' => $this->turn_domain,
+ 'NEXTCLOUD_MOUNT' => $this->nextcloud_mount,
+ 'BACKUP_RESTORE_PASSWORD' => $this->borg_restore_password,
+ 'CLAMAV_ENABLED' => $this->isClamavEnabled ? 'yes' : '',
+ 'TALK_RECORDING_ENABLED' => $this->isTalkRecordingEnabled ? 'yes' : '',
+ 'ONLYOFFICE_ENABLED' => $this->isOnlyofficeEnabled ? 'yes' : '',
+ 'COLLABORA_ENABLED' => $this->isCollaboraEnabled ? 'yes' : '',
+ 'TALK_ENABLED' => $this->isTalkEnabled ? 'yes' : '',
+ 'UPDATE_NEXTCLOUD_APPS' => ($this->isDailyBackupRunning() && $this->areAutomaticUpdatesEnabled()) ? 'yes' : '',
+ 'TIMEZONE' => $this->timezone === '' ? 'Etc/UTC' : $this->timezone,
+ 'COLLABORA_DICTIONARIES' => $this->collabora_dictionaries === '' ? 'de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru' : $this->collabora_dictionaries,
+ 'IMAGINARY_ENABLED' => $this->isImaginaryEnabled ? 'yes' : '',
+ 'FULLTEXTSEARCH_ENABLED' => $this->isFulltextsearchEnabled ? 'yes' : '',
+ 'DOCKER_SOCKET_PROXY_ENABLED' => $this->isDockerSocketProxyEnabled ? 'yes' : '',
+ 'NEXTCLOUD_UPLOAD_LIMIT' => $this->nextcloud_upload_limit,
+ 'NEXTCLOUD_MEMORY_LIMIT' => $this->nextcloud_memory_limit,
+ 'NEXTCLOUD_MAX_TIME' => $this->nextcloud_max_time,
+ 'BORG_RETENTION_POLICY' => $this->GetBorgRetentionPolicy(),
+ 'FULLTEXTSEARCH_JAVA_OPTIONS' => $this->GetFulltextsearchJavaOptions(),
+ 'NEXTCLOUD_TRUSTED_CACERTS_DIR' => $this->GetTrustedCacertsDir(),
+ 'ADDITIONAL_DIRECTORIES_BACKUP' => $this->GetAdditionalBackupDirectoriesString() !== '' ? 'yes' : '',
+ 'BORGBACKUP_HOST_LOCATION' => $this->borg_backup_host_location,
+ 'APACHE_MAX_SIZE' => (string)($this->GetApacheMaxSize()),
+ 'COLLABORA_SECCOMP_POLICY' => $this->GetCollaboraSeccompPolicy(),
+ 'NEXTCLOUD_STARTUP_APPS' => $this->GetNextcloudStartupApps(),
+ 'NEXTCLOUD_ADDITIONAL_APKS' => $this->GetNextcloudAdditionalApks(),
+ 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS' => $this->GetNextcloudAdditionalPhpExtensions(),
+ 'INSTALL_LATEST_MAJOR' => $this->install_latest_major ? 'yes' : '',
+ 'REMOVE_DISABLED_APPS' => $this->shouldDisabledAppsGetRemoved() ? 'yes' : '',
+ // Allow to get local ip-address of database container which allows to talk to it even in host mode (the container that requires this needs to be started first then)
+ 'AIO_DATABASE_HOST' => gethostbyname('nextcloud-aio-database'),
+ // Allow to get local ip-address of caddy container and add it to trusted proxies automatically
+ 'CADDY_IP_ADDRESS' => in_array('caddy', $this->aio_community_containers, true) ? gethostbyname('nextcloud-aio-caddy') : '',
+ 'WHITEBOARD_ENABLED' => $this->isWhiteboardEnabled ? 'yes' : '',
+ default => $this->GetRegisteredSecret($placeholder),
+ };
+ }
}
diff --git a/php/src/Data/Setup.php b/php/src/Data/Setup.php
index f8f43e4b..e409eef8 100644
--- a/php/src/Data/Setup.php
+++ b/php/src/Data/Setup.php
@@ -17,7 +17,7 @@ readonly class Setup {
}
$password = $this->passwordGenerator->GeneratePassword(8);
- $this->configurationManager->SetPassword($password);
+ $this->configurationManager->password = $password;
return $password;
}
diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php
index 9e8a8ff2..7d0d54bd 100644
--- a/php/src/Docker/DockerActionManager.php
+++ b/php/src/Docker/DockerActionManager.php
@@ -115,9 +115,9 @@ readonly class DockerActionManager {
$containerName = $container->GetIdentifier();
$internalPort = $container->GetInternalPort();
if ($internalPort === '%APACHE_PORT%') {
- $internalPort = $this->configurationManager->GetApachePort();
+ $internalPort = $this->configurationManager->apache_port;
} elseif ($internalPort === '%TALK_PORT%') {
- $internalPort = $this->configurationManager->GetTalkPort();
+ $internalPort = $this->configurationManager->talk_port;
}
if ($internalPort !== "" && $internalPort !== 'host') {
@@ -205,7 +205,7 @@ readonly class DockerActionManager {
foreach ($container->GetVolumes()->GetVolumes() as $volume) {
// // NEXTCLOUD_MOUNT gets added via bind-mount later on
// if ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') {
- // if ($volume->name === $this->configurationManager->GetNextcloudMount()) {
+ // if ($volume->name === $this->configurationManager->nextcloud_mount) {
// continue;
// }
// }
@@ -228,15 +228,7 @@ readonly class DockerActionManager {
$requestBody['HostConfig']['Binds'] = $volumes;
}
- $aioVariables = $container->GetAioVariables()->GetVariables();
- foreach ($aioVariables as $variable) {
- $config = $this->configurationManager->GetConfig();
- $variable = $this->replaceEnvPlaceholders($variable);
- $variableArray = explode('=', $variable);
- $config[$variableArray[0]] = $variableArray[1];
- $this->configurationManager->WriteConfig($config);
- sleep(1);
- }
+ $this->configurationManager->setAioVariables($container->GetAioVariables()->GetVariables());
$envs = $container->GetEnvironmentVariables()->GetVariables();
// Special thing for the nextcloud container
@@ -244,7 +236,7 @@ readonly class DockerActionManager {
$envs[] = $this->GetAllNextcloudExecCommands();
}
foreach ($envs as $key => $env) {
- $envs[$key] = $this->replaceEnvPlaceholders($env);
+ $envs[$key] = $this->configurationManager->replaceEnvPlaceholders($env);
}
if (count($envs) > 0) {
@@ -261,13 +253,13 @@ readonly class DockerActionManager {
$port = $value->port;
$protocol = $value->protocol;
if ($port === '%APACHE_PORT%') {
- $port = $this->configurationManager->GetApachePort();
+ $port = $this->configurationManager->apache_port;
// Do not expose udp if AIO is in reverse proxy mode
if ($port !== '443' && $protocol === 'udp') {
continue;
}
} else if ($port === '%TALK_PORT%') {
- $port = $this->configurationManager->GetTalkPort();
+ $port = $this->configurationManager->talk_port;
}
$portWithProtocol = $port . '/' . $protocol;
$exposedPorts[$portWithProtocol] = null;
@@ -283,13 +275,13 @@ readonly class DockerActionManager {
$port = $value->port;
$protocol = $value->protocol;
if ($port === '%APACHE_PORT%') {
- $port = $this->configurationManager->GetApachePort();
+ $port = $this->configurationManager->apache_port;
// Do not expose udp if AIO is in reverse proxy mode
if ($port !== '443' && $protocol === 'udp') {
continue;
}
} else if ($port === '%TALK_PORT%') {
- $port = $this->configurationManager->GetTalkPort();
+ $port = $this->configurationManager->talk_port;
// Skip publishing talk tcp port if it is set to 443
if ($port === '443' && $protocol === 'tcp') {
continue;
@@ -297,7 +289,7 @@ readonly class DockerActionManager {
}
$ipBinding = $value->ipBinding;
if ($ipBinding === '%APACHE_IP_BINDING%') {
- $ipBinding = $this->configurationManager->GetApacheIPBinding();
+ $ipBinding = $this->configurationManager->apache_ip_binding;
// Do not expose if AIO is in internal network mode
if ($ipBinding === '@INTERNAL') {
continue;
@@ -403,7 +395,7 @@ readonly class DockerActionManager {
// // Special things for the nextcloud container which should not be exposed in the containers.json
// } elseif ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') {
// foreach ($container->GetVolumes()->GetVolumes() as $volume) {
- // if ($volume->name !== $this->configurationManager->GetNextcloudMount()) {
+ // if ($volume->name !== $this->configurationManager->nextcloud_mount) {
// continue;
// }
// $mounts[] = ["Type" => "bind", "Source" => $volume->name, "Target" => $volume->mountPoint, "ReadOnly" => !$volume->isWritable, "BindOptions" => [ "Propagation" => "rshared"]];
@@ -422,8 +414,8 @@ readonly class DockerActionManager {
}
// Additional Collabora options
- if ($this->configurationManager->GetAdditionalCollaboraOptions() !== '') {
- $requestBody['Cmd'] = [$this->configurationManager->GetAdditionalCollaboraOptions()];
+ if ($this->configurationManager->collabora_additional_options !== '') {
+ $requestBody['Cmd'] = [$this->configurationManager->collabora_additional_options];
}
}
@@ -515,82 +507,6 @@ readonly class DockerActionManager {
}
}
- // Replaces placeholders in $envValue with their values.
- // E.g. "%NC_DOMAIN%:%APACHE_PORT" becomes "my.nextcloud.com:11000"
- private function replaceEnvPlaceholders(string $envValue): string {
- // $pattern breaks down as:
- // % - matches a literal percent sign
- // ([^%]+) - capture group that matches one or more characters that are NOT percent signs
- // % - matches the closing percent sign
- //
- // Assumes literal percent signs are always matched and there is no
- // escaping.
- $pattern = '/%([^%]+)%/';
- $matchCount = preg_match_all($pattern, $envValue, $matches);
-
- if ($matchCount === 0) {
- return $envValue;
- }
-
- $placeholders = $matches[0]; // ["%PLACEHOLDER1%", "%PLACEHOLDER2%", ...]
- $placeholderNames = $matches[1]; // ["PLACEHOLDER1", "PLACEHOLDER2", ...]
- $placeholderPatterns = array_map(static fn(string $p) => '/' . preg_quote($p) . '/', $placeholders); // ["/%PLACEHOLDER1%/", ...]
- $placeholderValues = array_map($this->getPlaceholderValue(...), $placeholderNames); // ["val1", "val2"]
- // Guaranteed to be non-null because we found the placeholders in the preg_match_all.
- return (string) preg_replace($placeholderPatterns, $placeholderValues, $envValue);
- }
-
- private function getPlaceholderValue(string $placeholder) : string {
- return match ($placeholder) {
- 'NC_DOMAIN' => $this->configurationManager->GetDomain(),
- 'NC_BASE_DN' => $this->configurationManager->GetBaseDN(),
- 'AIO_TOKEN' => $this->configurationManager->GetToken(),
- 'BORGBACKUP_REMOTE_REPO' => $this->configurationManager->GetBorgRemoteRepo(),
- 'BORGBACKUP_MODE' => $this->configurationManager->GetBackupMode(),
- 'AIO_URL' => $this->configurationManager->GetAIOURL(),
- 'SELECTED_RESTORE_TIME' => $this->configurationManager->GetSelectedRestoreTime(),
- 'RESTORE_EXCLUDE_PREVIEWS' => $this->configurationManager->GetRestoreExcludePreviews(),
- 'APACHE_PORT' => $this->configurationManager->GetApachePort(),
- 'APACHE_IP_BINDING' => $this->configurationManager->GetApacheIPBinding(),
- 'TALK_PORT' => $this->configurationManager->GetTalkPort(),
- 'TURN_DOMAIN' => $this->configurationManager->GetTurnDomain(),
- 'NEXTCLOUD_MOUNT' => $this->configurationManager->GetNextcloudMount(),
- 'BACKUP_RESTORE_PASSWORD' => $this->configurationManager->GetBorgRestorePassword(),
- 'CLAMAV_ENABLED' => $this->configurationManager->isClamavEnabled() ? 'yes' : '',
- 'TALK_RECORDING_ENABLED' => $this->configurationManager->isTalkRecordingEnabled() ? 'yes' : '',
- 'ONLYOFFICE_ENABLED' => $this->configurationManager->isOnlyofficeEnabled() ? 'yes' : '',
- 'COLLABORA_ENABLED' => $this->configurationManager->isCollaboraEnabled() ? 'yes' : '',
- 'TALK_ENABLED' => $this->configurationManager->isTalkEnabled() ? 'yes' : '',
- 'UPDATE_NEXTCLOUD_APPS' => ($this->configurationManager->isDailyBackupRunning() && $this->configurationManager->areAutomaticUpdatesEnabled()) ? 'yes' : '',
- 'TIMEZONE' => $this->configurationManager->GetTimezone() === '' ? 'Etc/UTC' : $this->configurationManager->GetTimezone(),
- 'COLLABORA_DICTIONARIES' => $this->configurationManager->GetCollaboraDictionaries() === '' ? 'de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru' : $this->configurationManager->GetCollaboraDictionaries(),
- 'IMAGINARY_ENABLED' => $this->configurationManager->isImaginaryEnabled() ? 'yes' : '',
- 'FULLTEXTSEARCH_ENABLED' => $this->configurationManager->isFulltextsearchEnabled() ? 'yes' : '',
- 'DOCKER_SOCKET_PROXY_ENABLED' => $this->configurationManager->isDockerSocketProxyEnabled() ? 'yes' : '',
- 'NEXTCLOUD_UPLOAD_LIMIT' => $this->configurationManager->GetNextcloudUploadLimit(),
- 'NEXTCLOUD_MEMORY_LIMIT' => $this->configurationManager->GetNextcloudMemoryLimit(),
- 'NEXTCLOUD_MAX_TIME' => $this->configurationManager->GetNextcloudMaxTime(),
- 'BORG_RETENTION_POLICY' => $this->configurationManager->GetBorgRetentionPolicy(),
- 'FULLTEXTSEARCH_JAVA_OPTIONS' => $this->configurationManager->GetFulltextsearchJavaOptions(),
- 'NEXTCLOUD_TRUSTED_CACERTS_DIR' => $this->configurationManager->GetTrustedCacertsDir(),
- 'ADDITIONAL_DIRECTORIES_BACKUP' => $this->configurationManager->GetAdditionalBackupDirectoriesString() !== '' ? 'yes' : '',
- 'BORGBACKUP_HOST_LOCATION' => $this->configurationManager->GetBorgBackupHostLocation(),
- 'APACHE_MAX_SIZE' => (string)($this->configurationManager->GetApacheMaxSize()),
- 'COLLABORA_SECCOMP_POLICY' => $this->configurationManager->GetCollaboraSeccompPolicy(),
- 'NEXTCLOUD_STARTUP_APPS' => $this->configurationManager->GetNextcloudStartupApps(),
- 'NEXTCLOUD_ADDITIONAL_APKS' => $this->configurationManager->GetNextcloudAdditionalApks(),
- 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS' => $this->configurationManager->GetNextcloudAdditionalPhpExtensions(),
- 'INSTALL_LATEST_MAJOR' => $this->configurationManager->shouldLatestMajorGetInstalled() ? 'yes' : '',
- 'REMOVE_DISABLED_APPS' => $this->configurationManager->shouldDisabledAppsGetRemoved() ? 'yes' : '',
- // Allow to get local ip-address of database container which allows to talk to it even in host mode (the container that requires this needs to be started first then)
- 'AIO_DATABASE_HOST' => gethostbyname('nextcloud-aio-database'),
- // Allow to get local ip-address of caddy container and add it to trusted proxies automatically
- 'CADDY_IP_ADDRESS' => in_array('caddy', $this->configurationManager->GetEnabledCommunityContainers(), true) ? gethostbyname('nextcloud-aio-caddy') : '',
- 'WHITEBOARD_ENABLED' => $this->configurationManager->isWhiteboardEnabled() ? 'yes' : '',
- default => $this->configurationManager->GetRegisteredSecret($placeholder),
- };
- }
-
private function isContainerUpdateAvailable(string $id): string {
$container = $this->containerDefinitionFetcher->GetContainerById($id);
@@ -606,7 +522,7 @@ readonly class DockerActionManager {
public function isAnyUpdateAvailable(): bool {
// return early if instance is not installed
- if (!$this->configurationManager->wasStartButtonClicked()) {
+ if (!$this->configurationManager->wasStartButtonClicked) {
return false;
}
$id = 'nextcloud-aio-apache';
diff --git a/php/templates/containers.twig b/php/templates/containers.twig
index c318e8a6..9c55350e 100644
--- a/php/templates/containers.twig
+++ b/php/templates/containers.twig
@@ -17,7 +17,7 @@
- Nextcloud AIO v12.4.0
+ Nextcloud AIO v12.5.0
{# Add 2nd tab warning #}
diff --git a/readme.md b/readme.md
index bcbf7d57..66059954 100644
--- a/readme.md
+++ b/readme.md
@@ -340,7 +340,7 @@ Although it does not seems like it is the case but from AIO perspective a Cloudf
For a reverse proxy example guide for Tailscale, see this guide by [@Perseus333](https://github.com/Perseus333): https://github.com/nextcloud/all-in-one/discussions/6817
### How to get Nextcloud running using the ACME DNS-challenge?
-You can install AIO in reverse proxy mode where is also documented how to get it running using the ACME DNS-challenge for getting a valid certificate for AIO. See the [reverse proxy documentation](./reverse-proxy.md). (Meant is the `Caddy with ACME DNS-challenge` section). Also see https://github.com/dani-garcia/vaultwarden/wiki/Running-a-private-vaultwarden-instance-with-Let%27s-Encrypt-certs#getting-a-custom-caddy-build for additional docs on this topic.
+You can install AIO behind an external reverse proxy where is also documented how to get it running using the ACME DNS-challenge for getting a valid certificate for AIO. See the [reverse proxy documentation](./reverse-proxy.md). (Meant is the `Caddy with ACME DNS-challenge` section). Also see https://github.com/dani-garcia/vaultwarden/wiki/Running-a-private-vaultwarden-instance-with-Let%27s-Encrypt-certs#getting-a-custom-caddy-build for additional docs on this topic.
### How to run Nextcloud locally? No domain wanted, or wanting intranet access within your LAN.
If you do not want to open Nextcloud to the public internet, you may have a look at the following documentation on how to set it up locally: [local-instance.md](./local-instance.md), but keep in mind you're still required to have https working properly.
diff --git a/reverse-proxy.md b/reverse-proxy.md
index 50a6bccd..bdeb3244 100644
--- a/reverse-proxy.md
+++ b/reverse-proxy.md
@@ -564,19 +564,14 @@ Note: this will cause that a non root user can bind privileged ports.
Second, see these screenshots for a working config:
-
+
-
+
-
+
-
+
-
-
-`proxy_set_header Accept-Encoding $http_accept_encoding;`
-
-⚠️ **Please note:** Nextcloud will complain that X-XXS-Protection is set to the wrong value, this is intended by NPMplus.
⚠️ **Please note:** look into [this](#adapting-the-sample-web-server-configurations-below) to adapt the above example configuration.