mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-06-10 11:00:22 +00:00
freedombox Debian release 24.22
-----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEfWrbdQ+RCFWJSEvmd8DHXntlCAgFAmcXBsIWHGp2YWxsZXJv eUBtYWlsYm94Lm9yZwAKCRB3wMdee2UICLYHD/9dEGFq/ILCf8q0lgw3qkyDs8V9 jlvLx/hsK60RhYcSakWaT8QbjACnFGn5nwAMMxcQWIq3Nc/XSs9NMx+7nbZUVmXK ZqWWLU65pfpMP0EE6zpGBq8ZNhnzIdX4jwGhu8KNUQjoaaMLUk7m7DH3sBVeaMIt FxrOW2Yso9+R5QOOcpJXUmrg5z9JUeYzd+F0EqyUa4uL3zZsU1ELFShskepXJuTH mx1Nkjdcs56Kx40YJlh1qxwBAPsAks4Kpc2KDWQsj7sNvO6gScRAYc0Rr70b8DUl g2uPLzFnNV5jp1Ug3lJZoUCwAwBB4Xr736fVl0L6l0mk9AtMqhrArMnvlTsL2571 0KcqHenMsXbk5+ti/Z9KlAJ/FvvLFqjz43cyinyiVrOIAmkS6Uo0lSJnnwSE5G8B uJs+xEKFit3uD4KzSzum4tQe4W1pRGV8DxO1STXZxbSfAGqS9JDuez4O8LkN1+8E daP9eWtDhwYoWNihmZ5nSImHrhzCPPryWQmk0F8l7n5lMBAddvICEufLlAl6ngoZ j65ye1N38BvcKvMZvUkcTPIaSgjlgtf4+ShM+Qe5c0dE0sV5TnhFXbpVh+SYr4yZ zSYg0aTta2INSMyt5Kuv7/fAm3b82hNoRI/ZLlA1JmK9sGGJZEnBROD/qE4BFqsZ woEzuyTf1G8yI6nIMA== =0v+a -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEfWrbdQ+RCFWJSEvmd8DHXntlCAgFAmcblXEWHGp2YWxsZXJv eUBtYWlsYm94Lm9yZwAKCRB3wMdee2UICJShD/9CHfCPo03mlOjG/HcpJJdAJBJM Zn9o7eABYVGwqdVS0Wqu8yX0X1QcFk/Gj/az0kyf7DjAUTpum9keBmLQNE0Grh+f zuDF7XQ54YaqThQo1wyOeycLR4VeGl67ATqG6hilQ6BguJLmtyFJHmGcM6M+Mny1 HKW5wxZW2Mzk+SkLlwjNy0fULy6KKU/rskCV8GYfH1GBW7l4Kosu5FTQ2lHEliEs mrQR7hLwL7wEJRAxlb0EwYVyATm4teol4Z93J03NQ7p+fXTiYA/6eZbRQDq2bOPb +nW7j7g8+/a5OmRS2i65tHQqa+DKedc8OA7b3bNjPXFUo8XeBZY/Wvi/e7fa6wR3 IwFSE4QXeFNEocjdALITOybMsyzhHG5Hh0nA+qec7Udj3FAQrZsQya+7P+hWetyQ Yhf3CVV3eqDJNAYRHsdqhCiSHhtZf8KkqeP5gLDpg1EylOUjtn7m7wk8OOAD7JUa NLcJwZqfQLBbAFD4PTCWWiIwdc1O2unefcMwlsR86rmi1AQgtign/IOt7z+E+XDy Ps9gjg1vUzDwuYXgJTPZv/+bPUgp5W68EWz3eqhIpXmrED7VmO9hE3k6CuVdgqwE jBrWG9JV3BAAaTqFsMtiyTFeuV5CbOYwNvc1BGB23I6mXBn3XkSSvyuQ5yWoLuU4 1tFj/fJgD7vVC9Dxrw== =Vpo6 -----END PGP SIGNATURE----- Merge tag 'v24.22' into debian/bookworm-backports freedombox Debian release 24.22 Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
commit
9761c7ba47
40
.ci/Containerfile.functional-tests-stable
Normal file
40
.ci/Containerfile.functional-tests-stable
Normal file
@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
FROM debian:stable
|
||||
|
||||
CMD /sbin/init
|
||||
USER root:root
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN echo "deb http://deb.debian.org/debian bookworm-backports main" > /etc/apt/sources.list.d/backports.list; echo "deb-src http://deb.debian.org/debian bookworm-backports main" >> /etc/apt/sources.list.d/backports.list
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
# Update all packages
|
||||
RUN apt-get dist-upgrade -y
|
||||
|
||||
# Install freedombox package so that plint:plinth user/group are created etc.
|
||||
RUN apt-get install -y freedombox/bookworm-backports
|
||||
RUN systemctl mask plinth.service
|
||||
|
||||
# Don't ask for the secret in first wizard
|
||||
RUN rm -f /var/lib/plinth/firstboot-wizard-secret
|
||||
|
||||
# Dependencies of the freedombox Debian package
|
||||
RUN apt-get build-dep -y freedombox/bookworm-backports
|
||||
|
||||
# Build and test dependencies
|
||||
RUN apt-get install -y \
|
||||
build-essential \
|
||||
parted \
|
||||
sshpass \
|
||||
wget
|
||||
|
||||
|
||||
# Install functional test dependencies
|
||||
ADD https://salsa.debian.org/freedombox-team/freedombox/-/raw/main/plinth/tests/functional/install.sh /usr/src/install.sh
|
||||
RUN bash /usr/src/install.sh; rm -f /usr/src/install.sh
|
||||
|
||||
# Allow daemons to start when container is started
|
||||
RUN rm -f /usr/sbin/policy-rc.d
|
||||
@ -25,4 +25,4 @@ RUN apt-get install -y build-essential \
|
||||
RUN apt-get install -y $(./run --list-dependencies)
|
||||
|
||||
# Coverage should know that test_functional.py files are tests
|
||||
RUN pip3 install --break-system-packages splinter pytest-bdd
|
||||
RUN pip3 install --break-system-packages splinter
|
||||
@ -1,66 +1,27 @@
|
||||
---
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
.app-server:
|
||||
stage: functional-tests
|
||||
dependencies: []
|
||||
except:
|
||||
- $GITLAB_USER_LOGIN == "weblate"
|
||||
script:
|
||||
- BUILD_JOB_ID=$(curl -s "https://salsa.debian.org/api/v4/projects/$CI_PROJECT_ID/pipelines/$CI_PIPELINE_ID/jobs?scope[]=success" | jq -r '.[] | select(.name==env.BUILD_JOB_NAME) | .id')
|
||||
- export AWS_DEFAULT_REGION=us-east-1
|
||||
- |
|
||||
aws lambda invoke --function-name launch_app_server --payload '{"launch_template_name": "'"$LAUNCH_TEMPLATE_NAME"'", "instance_name": "'"$INSTANCE_NAME"'", "ci_project_id": "'"$CI_PROJECT_ID"'", "build_job_id": "'"$BUILD_JOB_ID"'"}' response.json
|
||||
- echo "APP_SERVER_IP_1=$(jq -r '.app_server_ip' response.json)" >> app-servers.env
|
||||
- echo "INSTANCE_ID_1=$(jq -r '.instance_id' response.json)" >> app-servers.env
|
||||
- |
|
||||
aws lambda invoke --function-name launch_app_server --payload '{"launch_template_name": "'"$LAUNCH_TEMPLATE_NAME"'", "instance_name": "'"$INSTANCE_NAME"'", "ci_project_id": "'"$CI_PROJECT_ID"'", "build_job_id": "'"$BUILD_JOB_ID"'"}' response.json
|
||||
- echo "APP_SERVER_IP_2=$(jq -r '.app_server_ip' response.json)" >> app-servers.env
|
||||
- echo "INSTANCE_ID_2=$(jq -r '.instance_id' response.json)" >> app-servers.env
|
||||
tags:
|
||||
- functional-tests
|
||||
artifacts:
|
||||
reports:
|
||||
dotenv: app-servers.env
|
||||
|
||||
.run-functional-tests:
|
||||
stage: functional-tests
|
||||
timeout: 3h
|
||||
needs: []
|
||||
dependencies: []
|
||||
tags:
|
||||
- functional-tests
|
||||
timeout: 10h
|
||||
# Need to find another way of running the cleanup step even on failure
|
||||
allow_failure: true
|
||||
when: delayed
|
||||
# Wait for the app-server to come up. Saves time for the CI runners.
|
||||
start_in: 3 minutes
|
||||
except:
|
||||
- $GITLAB_USER_LOGIN == "weblate"
|
||||
before_script:
|
||||
- apt-get update
|
||||
- apt-get install -y sudo curl wget
|
||||
- ./plinth/tests/functional/install.sh
|
||||
- adduser tester --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --disabled-password && echo "tester:password" | chpasswd
|
||||
- sudo -u plinth ./run --develop > plinth.log 2>&1 &
|
||||
- 'echo -e "Cmnd_Alias FREEDOMBOX_ACTION_DEV = /usr/share/plinth/actions/actions, `pwd`/actions/actions\nDefaults!FREEDOMBOX_ACTION_DEV closefrom_override\nplinth ALL=(ALL:ALL) NOPASSWD:SETENV : FREEDOMBOX_ACTION_DEV\n" > /etc/sudoers.d/01-freedombox-development'
|
||||
- while ! grep -q "Setup thread finished" plinth.log; do sleep 1; echo -n .; done
|
||||
script:
|
||||
- cp -r . /home/tester/freedombox && chown -R tester:tester /home/tester/freedombox
|
||||
- |
|
||||
sudo APP_SERVER_URL_1="https://$APP_SERVER_IP_1" APP_SERVER_URL_2="https://$APP_SERVER_IP_2" -u tester bash -c \
|
||||
'cd /home/tester/freedombox && py.test-3 -v --durations=10 --include-functional --splinter-headless -n 2 --dist=loadscope --template=html1/index.html --report=functional-tests.html'
|
||||
after_script:
|
||||
- echo "INSTANCE_ID_1=$INSTANCE_ID_1" >> app-servers.env
|
||||
- echo "INSTANCE_ID_2=$INSTANCE_ID_2" >> app-servers.env
|
||||
- cp /home/tester/freedombox/functional-tests.html .
|
||||
- cp -r /home/tester/freedombox/screenshots/ .
|
||||
- FREDOMBOX_URL=https://localhost FREEDOMBOX_SSH_PORT=22 FREEDOMBOX_SAMBA_PORT=445 pytest -v --durations=10 --include-functional --splinter-headless --instafail --template=html1/index.html --report=functional-tests.html
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
dotenv: app-servers.env
|
||||
paths:
|
||||
- functional-tests.html
|
||||
- screenshots/
|
||||
|
||||
# Does not run if the previous job times out or is cancelled
|
||||
.terminate-app-server:
|
||||
stage: functional-tests
|
||||
script:
|
||||
- export AWS_DEFAULT_REGION=us-east-1
|
||||
- |
|
||||
aws lambda invoke --function-name terminate_app_server --payload '{"instance_id": "'"$INSTANCE_ID_1"'"}' response.json
|
||||
aws lambda invoke --function-name terminate_app_server --payload '{"instance_id": "'"$INSTANCE_ID_2"'"}' response.json
|
||||
tags:
|
||||
- functional-tests
|
||||
- plinth.log
|
||||
|
||||
21
.ci/gitlab-runner/config.toml
Normal file
21
.ci/gitlab-runner/config.toml
Normal file
@ -0,0 +1,21 @@
|
||||
concurrent = 1
|
||||
check_interval = 0
|
||||
|
||||
[[runners]]
|
||||
name = "freedombox-functional"
|
||||
url = "https://salsa.debian.org"
|
||||
token = "<insert-server-provided-token-here>"
|
||||
executor = "custom"
|
||||
builds_dir = "/freedombox"
|
||||
cache_dir = "/cache"
|
||||
[runners.custom]
|
||||
prepare_exec = "/var/lib/fbx-functional/bin/prepare.sh"
|
||||
prepare_exec_timeout = 1200
|
||||
|
||||
run_exec = "/var/lib/fbx-functional/bin/run.sh"
|
||||
|
||||
cleanup_exec = "/var/lib/fbx-functional/bin/cleanup.sh"
|
||||
cleanup_exec_timeout = 1200
|
||||
|
||||
graceful_kill_timeout = 200
|
||||
force_kill_timeout = 200
|
||||
7
.ci/gitlab-runner/custom-driver/README
Normal file
7
.ci/gitlab-runner/custom-driver/README
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This directory contains a custom driver for Gitlab-CI Runner. This is used to
|
||||
run functional tests.
|
||||
|
||||
Based on https://docs.gitlab.com/runner/executors/custom_examples/lxd.html under
|
||||
Expat license.
|
||||
4
.ci/gitlab-runner/custom-driver/base.sh
Normal file
4
.ci/gitlab-runner/custom-driver/base.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
CONTAINER_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-$CUSTOM_ENV_CI_JOB_ID"
|
||||
10
.ci/gitlab-runner/custom-driver/cleanup.sh
Executable file
10
.ci/gitlab-runner/custom-driver/cleanup.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source ${current_dir}/base.sh # Get variables from base.
|
||||
|
||||
echo "Deleting container $CONTAINER_ID"
|
||||
|
||||
podman container stop "$CONTAINER_ID"
|
||||
podman container rm -f "$CONTAINER_ID"
|
||||
48
.ci/gitlab-runner/custom-driver/prepare.sh
Executable file
48
.ci/gitlab-runner/custom-driver/prepare.sh
Executable file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source ${current_dir}/base.sh # Get variables from base.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# trap any error, and mark it as a system failure.
|
||||
trap "exit $SYSTEM_FAILURE_EXIT_CODE" ERR
|
||||
|
||||
start_container () {
|
||||
if podman container exists "$CONTAINER_ID" ; then
|
||||
echo 'Found old container, deleting'
|
||||
podman container stop "$CONTAINER_ID"
|
||||
podman container rm -f "$CONTAINER_ID"
|
||||
fi
|
||||
|
||||
podman pull registry.salsa.debian.org/freedombox-team/freedombox:functional-tests-stable
|
||||
podman run --name "$CONTAINER_ID" --systemd=always \
|
||||
--privileged \
|
||||
--cap-add=SYS_ADMIN --cap-add=NET_ADMIN --cap-add=MKNOD \
|
||||
--detach registry.salsa.debian.org/freedombox-team/freedombox:functional-tests-stable /sbin/init
|
||||
|
||||
if podman exec "$CONTAINER_ID" systemctl is-system-running --wait; then
|
||||
echo 'Container started.'
|
||||
else
|
||||
echo 'Container started degraded.'
|
||||
fi
|
||||
}
|
||||
|
||||
install_dependencies () {
|
||||
podman exec "$CONTAINER_ID" /usr/bin/bash <<EOF
|
||||
set -eo pipefail
|
||||
echo 'Package: *' > /etc/apt/preferences.d/unstable
|
||||
echo 'Pin: release a=unstable' >> /etc/apt/preferences.d/unstable
|
||||
echo 'Pin-Priority: 400' >> /etc/apt/preferences.d/unstable
|
||||
echo 'deb http://deb.debian.org/debian unstable main' > /etc/apt/sources.list.d/unstable.list
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y gitlab-runner git-lfs
|
||||
EOF
|
||||
}
|
||||
|
||||
echo "Running in $CONTAINER_ID"
|
||||
|
||||
start_container
|
||||
|
||||
install_dependencies
|
||||
14
.ci/gitlab-runner/custom-driver/run.sh
Executable file
14
.ci/gitlab-runner/custom-driver/run.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
source ${current_dir}/base.sh # Get variables from base.
|
||||
|
||||
podman exec --interactive "$CONTAINER_ID" /bin/bash < "${1}"
|
||||
if [ $? -ne 0 ]; then
|
||||
# Exit using the variable, to make the build as failure in GitLab
|
||||
# CI.
|
||||
exit $BUILD_FAILURE_EXIT_CODE
|
||||
fi
|
||||
11
.ci/update-container-image.sh
Executable file
11
.ci/update-container-image.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#! /bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
CONTAINER=$1
|
||||
CONTAINER="${CONTAINER:-gitlabci}"
|
||||
|
||||
podman login registry.salsa.debian.org
|
||||
|
||||
# Build and upload a new image to the container registry
|
||||
podman build -t registry.salsa.debian.org/freedombox-team/freedombox:${CONTAINER} -f .ci/Containerfile.${CONTAINER} .
|
||||
podman push registry.salsa.debian.org/freedombox-team/freedombox:${CONTAINER}
|
||||
@ -1,8 +0,0 @@
|
||||
#! /bin/bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
docker login registry.salsa.debian.org
|
||||
|
||||
# Build and upload a new image to the container registry
|
||||
DOCKER_BUILDKIT=1 docker build -t registry.salsa.debian.org/freedombox-team/freedombox:gitlabci -f .ci/Dockerfile.gitlabci .
|
||||
docker push registry.salsa.debian.org/freedombox-team/freedombox:gitlabci
|
||||
@ -47,63 +47,9 @@ doc-tests:
|
||||
script:
|
||||
- make check-doc
|
||||
|
||||
.app-server-stable:
|
||||
variables:
|
||||
LAUNCH_TEMPLATE_NAME: functional-tests-stable
|
||||
INSTANCE_NAME: app-server-stable
|
||||
BUILD_JOB_NAME: build-backports
|
||||
extends: .app-server
|
||||
|
||||
.run-functional-tests-stable:
|
||||
needs:
|
||||
- job: app-server-stable
|
||||
artifacts: true
|
||||
run-functional-tests-stable:
|
||||
extends: .run-functional-tests
|
||||
|
||||
.terminate-app-server-stable:
|
||||
needs:
|
||||
- job: run-functional-tests-stable
|
||||
artifacts: true
|
||||
extends: .terminate-app-server
|
||||
|
||||
.app-server-testing:
|
||||
variables:
|
||||
LAUNCH_TEMPLATE_NAME: functional-tests-testing
|
||||
INSTANCE_NAME: app-server-testing
|
||||
BUILD_JOB_NAME: build
|
||||
extends: .app-server
|
||||
|
||||
.run-functional-tests-testing:
|
||||
needs:
|
||||
- job: app-server-testing
|
||||
artifacts: true
|
||||
extends: .run-functional-tests
|
||||
|
||||
.terminate-app-server-testing:
|
||||
needs:
|
||||
- job: run-functional-tests-testing
|
||||
artifacts: true
|
||||
extends: .terminate-app-server
|
||||
|
||||
.app-server-unstable:
|
||||
variables:
|
||||
LAUNCH_TEMPLATE_NAME: functional-tests-unstable
|
||||
INSTANCE_NAME: app-server-unstable
|
||||
BUILD_JOB_NAME: build
|
||||
extends: .app-server
|
||||
|
||||
.run-functional-tests-unstable:
|
||||
needs:
|
||||
- job: app-server-unstable
|
||||
artifacts: true
|
||||
extends: .run-functional-tests
|
||||
|
||||
.terminate-app-server-unstable:
|
||||
needs:
|
||||
- job: run-functional-tests-unstable
|
||||
artifacts: true
|
||||
extends: .terminate-app-server
|
||||
|
||||
extract-source:
|
||||
extends: .provisioning-extract-source
|
||||
|
||||
|
||||
@ -72,15 +72,18 @@ RedirectMatch "^/$" "/plinth"
|
||||
## Enable strict sandboxing enabled with some exceptions:
|
||||
## - Allow running Javascript.
|
||||
## - Allow popups as sometimes we use <a target=_blank>
|
||||
## - Allow popups to have different sandbox requirements as we launch apps' web
|
||||
## clients.
|
||||
## - Allow forms to support configuration forms.
|
||||
## -
|
||||
## - Allow policies to treat same origin differently from other origins
|
||||
## - Allow downloads such as backup tarballs.
|
||||
##
|
||||
## Disable browser guessing of MIME types. FreedoBox already sets good content
|
||||
## types for all the common file types.
|
||||
##
|
||||
<LocationMatch "^/(plinth|freedombox)">
|
||||
Header set Referrer-Policy 'same-origin'
|
||||
Header set Content-Security-Policy "font-src 'self'; frame-src 'none'; img-src 'self'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'self'; style-src 'self'; worker-src 'self'; default-src 'self'; base-uri 'none'; sandbox allow-scripts allow-popups allow-forms allow-same-origin allow-downloads; form-action 'self'; frame-ancestors 'none'; block-all-mixed-content;"
|
||||
Header set Content-Security-Policy "font-src 'self'; frame-src 'none'; img-src 'self'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'self'; style-src 'self'; worker-src 'self'; default-src 'self'; base-uri 'none'; sandbox allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms allow-same-origin allow-downloads; form-action 'self'; frame-ancestors 'none'; block-all-mixed-content;"
|
||||
Header set X-Content-Type-Options 'nosniff'
|
||||
</LocationMatch>
|
||||
|
||||
|
||||
81
debian/changelog
vendored
81
debian/changelog
vendored
@ -1,3 +1,84 @@
|
||||
freedombox (24.22) unstable; urgency=medium
|
||||
|
||||
[ Sunil Mohan Adapa ]
|
||||
* apache2: Allow popups to have different sandbox policy
|
||||
* firstboot: Improve the setup complete page with more setups
|
||||
* firstboot: Hide navigation toggler in mobile layouts
|
||||
* firstboot: Make logo image responsive during first setup
|
||||
* firstboot: Show spinner instead of message during first setup
|
||||
* css: Fix height of navbar in mobile layout during first boot
|
||||
* css: Navbar styling fixes in mobile layout
|
||||
* upgrades: Remove step upgrade during first setup
|
||||
* networks: Remove first boot steps for connectivity/topology
|
||||
* upgrades: Show notification to remind user to run updates manually
|
||||
* first_boot: Allow the next steps page to be revisited
|
||||
* first_boot: Add notification for next steps after first setup
|
||||
* wordpress: tests: functional: Fix tests on Trixie
|
||||
* calibre: tests: functional: Fix occasional failure in add book test
|
||||
* ci: Generalize script to update container, switch to podman
|
||||
* ci: Dockerfile: Drop obsolete dependency on pytest-bdd
|
||||
* ci: Rename Dockerfiles to Containerfiles
|
||||
* ci: Add docker container for functional-tests:stable
|
||||
* ci: Add gitlab runner configuration
|
||||
* ci: Add a custom driver for gitlab runner for podman
|
||||
* .gitlab-ci.yml: Update for new infrastructure
|
||||
* ci: Update functional test timeout to 10h
|
||||
* *: tags: Adjust tags and style
|
||||
* context_processors: Use active menu urls to decide what to highlight
|
||||
* help, system: Stop using submenu.sorted_items
|
||||
* context_processors: Stop adding unused 'submenu' to context
|
||||
* tags: css: Minor styling cleanups
|
||||
* tags: js: Minor fixes and refactoring
|
||||
* tests: functional: Create utility to set user preferred locale
|
||||
* tags: Localization fixes
|
||||
* tests: functional: Add package for printing test failures instantly
|
||||
* ci: Enable showing test failures immediately as they fail
|
||||
* help: tests: Fix tests failing due to tags related changes
|
||||
* *: Remove unused imports to fix flake8 errors
|
||||
* nextcloud: Fix install failure due to PrivateTmp=yes
|
||||
* utils: Improve safe formatter by handling more cases
|
||||
* operation: Use safe formatter for translating messages
|
||||
* middleware: Show translated error messages when operation completes
|
||||
* setup: Translate errors when installing/updating/repairing apps
|
||||
|
||||
[ gallegonovato ]
|
||||
* Translated using Weblate (Spanish)
|
||||
|
||||
[ Burak Yavuz ]
|
||||
* Translated using Weblate (Turkish)
|
||||
|
||||
[ 大王叫我来巡山 ]
|
||||
* Translated using Weblate (Chinese (Simplified Han script))
|
||||
|
||||
[ 109247019824 ]
|
||||
* Translated using Weblate (Bulgarian)
|
||||
* Translated using Weblate (Bulgarian)
|
||||
* Translated using Weblate (Bulgarian)
|
||||
|
||||
[ Besnik Bleta ]
|
||||
* Translated using Weblate (Albanian)
|
||||
* Translated using Weblate (Albanian)
|
||||
|
||||
[ Veiko Aasa ]
|
||||
* syncthing: Fix app setup in Debian testing
|
||||
* ssh: Start server after nslcd service
|
||||
|
||||
[ Joseph Nuthalapati ]
|
||||
* backups: Use new utility for handling file uploads
|
||||
* *: Implements tags for apps
|
||||
|
||||
[ Jiří Podhorecký ]
|
||||
* Translated using Weblate (Czech)
|
||||
|
||||
[ Ihor Hordiichuk ]
|
||||
* Translated using Weblate (Ukrainian)
|
||||
|
||||
[ James Valleroy ]
|
||||
* locale: Update translation strings
|
||||
* doc: Fetch latest manual
|
||||
|
||||
-- James Valleroy <jvalleroy@mailbox.org> Mon, 21 Oct 2024 20:42:43 -0400
|
||||
|
||||
freedombox (24.21~bpo12+1) bookworm-backports; urgency=medium
|
||||
|
||||
* Rebuild for bookworm-backports.
|
||||
|
||||
@ -8,6 +8,58 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f
|
||||
|
||||
The following are the release notes for each !FreedomBox version.
|
||||
|
||||
== FreedomBox 24.22 (2024-10-21) ==
|
||||
|
||||
=== Highlights ===
|
||||
|
||||
* Add tags for apps
|
||||
|
||||
=== Other Changes ===
|
||||
|
||||
* .gitlab-ci.yml: Update for new infrastructure
|
||||
* Remove unused imports to fix flake8 errors
|
||||
* apache2: Allow popups to have different sandbox policy
|
||||
* backups: Use new utility for handling file uploads
|
||||
* calibre: tests: functional: Fix occasional failure in add book test
|
||||
* ci: Add a custom driver for gitlab runner for podman
|
||||
* ci: Add docker container for functional-tests:stable
|
||||
* ci: Add gitlab runner configuration
|
||||
* ci: Dockerfile: Drop obsolete dependency on pytest-bdd
|
||||
* ci: Enable showing test failures immediately as they fail
|
||||
* ci: Generalize script to update container, switch to podman
|
||||
* ci: Rename Dockerfiles to Containerfiles
|
||||
* ci: Update functional test timeout to 10h
|
||||
* context_processors: Stop adding unused 'submenu' to context
|
||||
* context_processors: Use active menu urls to decide what to highlight
|
||||
* css: Fix height of navbar in mobile layout during first boot
|
||||
* css: Navbar styling fixes in mobile layout
|
||||
* first_boot: Add notification for next steps after first setup
|
||||
* first_boot: Allow the next steps page to be revisited
|
||||
* firstboot: Hide navigation toggler in mobile layouts
|
||||
* firstboot: Improve the setup complete page with more setups
|
||||
* firstboot: Make logo image responsive during first setup
|
||||
* firstboot: Show spinner instead of message during first setup
|
||||
* help, system: Stop using submenu.sorted_items
|
||||
* help: tests: Fix tests failing due to tags related changes
|
||||
* locale: Update translations for Albanian, Bulgarian, Chinese (Simplified Han script), Czech, Spanish, Turkish, Ukrainian
|
||||
* middleware: Show translated error messages when operation completes
|
||||
* networks: Remove first boot steps for connectivity/topology
|
||||
* nextcloud: Fix install failure due to !PrivateTmp=yes
|
||||
* operation: Use safe formatter for translating messages
|
||||
* setup: Translate errors when installing/updating/repairing apps
|
||||
* ssh: Start server after nslcd service
|
||||
* syncthing: Fix app setup in Debian testing
|
||||
* tags: Adjust tags and style
|
||||
* tags: Localization fixes
|
||||
* tags: css: Minor styling cleanups
|
||||
* tags: js: Minor fixes and refactoring
|
||||
* tests: functional: Add package for printing test failures instantly
|
||||
* tests: functional: Create utility to set user preferred locale
|
||||
* upgrades: Remove step upgrade during first setup
|
||||
* upgrades: Show notification to remind user to run updates manually
|
||||
* utils: Improve safe formatter by handling more cases
|
||||
* wordpress: tests: functional: Fix tests on Trixie
|
||||
|
||||
== FreedomBox 24.21 (2024-10-07) ==
|
||||
|
||||
=== Highlights ===
|
||||
|
||||
@ -8,6 +8,58 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f
|
||||
|
||||
The following are the release notes for each !FreedomBox version.
|
||||
|
||||
== FreedomBox 24.22 (2024-10-21) ==
|
||||
|
||||
=== Highlights ===
|
||||
|
||||
* Add tags for apps
|
||||
|
||||
=== Other Changes ===
|
||||
|
||||
* .gitlab-ci.yml: Update for new infrastructure
|
||||
* Remove unused imports to fix flake8 errors
|
||||
* apache2: Allow popups to have different sandbox policy
|
||||
* backups: Use new utility for handling file uploads
|
||||
* calibre: tests: functional: Fix occasional failure in add book test
|
||||
* ci: Add a custom driver for gitlab runner for podman
|
||||
* ci: Add docker container for functional-tests:stable
|
||||
* ci: Add gitlab runner configuration
|
||||
* ci: Dockerfile: Drop obsolete dependency on pytest-bdd
|
||||
* ci: Enable showing test failures immediately as they fail
|
||||
* ci: Generalize script to update container, switch to podman
|
||||
* ci: Rename Dockerfiles to Containerfiles
|
||||
* ci: Update functional test timeout to 10h
|
||||
* context_processors: Stop adding unused 'submenu' to context
|
||||
* context_processors: Use active menu urls to decide what to highlight
|
||||
* css: Fix height of navbar in mobile layout during first boot
|
||||
* css: Navbar styling fixes in mobile layout
|
||||
* first_boot: Add notification for next steps after first setup
|
||||
* first_boot: Allow the next steps page to be revisited
|
||||
* firstboot: Hide navigation toggler in mobile layouts
|
||||
* firstboot: Improve the setup complete page with more setups
|
||||
* firstboot: Make logo image responsive during first setup
|
||||
* firstboot: Show spinner instead of message during first setup
|
||||
* help, system: Stop using submenu.sorted_items
|
||||
* help: tests: Fix tests failing due to tags related changes
|
||||
* locale: Update translations for Albanian, Bulgarian, Chinese (Simplified Han script), Czech, Spanish, Turkish, Ukrainian
|
||||
* middleware: Show translated error messages when operation completes
|
||||
* networks: Remove first boot steps for connectivity/topology
|
||||
* nextcloud: Fix install failure due to !PrivateTmp=yes
|
||||
* operation: Use safe formatter for translating messages
|
||||
* setup: Translate errors when installing/updating/repairing apps
|
||||
* ssh: Start server after nslcd service
|
||||
* syncthing: Fix app setup in Debian testing
|
||||
* tags: Adjust tags and style
|
||||
* tags: Localization fixes
|
||||
* tags: css: Minor styling cleanups
|
||||
* tags: js: Minor fixes and refactoring
|
||||
* tests: functional: Add package for printing test failures instantly
|
||||
* tests: functional: Create utility to set user preferred locale
|
||||
* upgrades: Remove step upgrade during first setup
|
||||
* upgrades: Show notification to remind user to run updates manually
|
||||
* utils: Improve safe formatter by handling more cases
|
||||
* wordpress: tests: functional: Fix tests on Trixie
|
||||
|
||||
== FreedomBox 24.21 (2024-10-07) ==
|
||||
|
||||
=== Highlights ===
|
||||
|
||||
@ -3,4 +3,4 @@
|
||||
Package init file.
|
||||
"""
|
||||
|
||||
__version__ = '24.21'
|
||||
__version__ = '24.22'
|
||||
|
||||
@ -434,7 +434,7 @@ class Info(FollowerComponent):
|
||||
def __init__(self, app_id, version, is_essential=False, depends=None,
|
||||
name=None, icon=None, icon_filename=None,
|
||||
short_description=None, description=None, manual_page=None,
|
||||
clients=None, donation_url=None):
|
||||
clients=None, donation_url=None, tags=None):
|
||||
"""Store the basic properties of an app as a component.
|
||||
|
||||
Each app must contain at least one component of this type to provide
|
||||
@ -504,6 +504,9 @@ class Info(FollowerComponent):
|
||||
'donation_url' is a link to a webpage that describes how to
|
||||
donate to the upstream project.
|
||||
|
||||
'tags' is a list of tags that describe the app. Tags help users to find
|
||||
similar apps or alternatives and discover use cases.
|
||||
|
||||
"""
|
||||
self.component_id = app_id + '-info'
|
||||
self.app_id = app_id
|
||||
@ -518,9 +521,32 @@ class Info(FollowerComponent):
|
||||
self.manual_page = manual_page
|
||||
self.clients = clients
|
||||
self.donation_url = donation_url
|
||||
self._tags = tags or []
|
||||
if clients:
|
||||
clients_module.validate(clients)
|
||||
|
||||
@property
|
||||
def tags(self) -> list[str]:
|
||||
"""Return a list of untranslated tags on the app.
|
||||
|
||||
These can only be retrieved after Django has been configured.
|
||||
"""
|
||||
# Store untranslated original strings instead of proxy objects
|
||||
from django.utils.translation import override
|
||||
with override(language=None):
|
||||
return [str(tag) for tag in self._tags]
|
||||
|
||||
@classmethod
|
||||
def list_tags(self) -> list[str]:
|
||||
"""Return a list of untranslated tags."""
|
||||
tags: set[str] = set()
|
||||
from django.utils.translation import override
|
||||
with override(language=None):
|
||||
for app in App.list():
|
||||
tags.update((str(tag) for tag in app.info.tags))
|
||||
|
||||
return list(tags)
|
||||
|
||||
|
||||
class EnableState(LeaderComponent):
|
||||
"""A component to hold the enable state of an app using a simple flag.
|
||||
@ -626,8 +652,8 @@ def _initialize_module(module_name, module):
|
||||
for app_class in app_classes:
|
||||
app_class()
|
||||
except Exception as exception:
|
||||
logger.exception('Exception while running init for %s: %s', module,
|
||||
exception)
|
||||
logger.exception('Exception while running init for module %s: %s',
|
||||
module_name, exception)
|
||||
if cfg.develop:
|
||||
raise
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import re
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from plinth import cfg, menu
|
||||
from plinth import cfg
|
||||
from plinth.utils import is_user_admin
|
||||
|
||||
|
||||
@ -27,10 +27,11 @@ def common(request):
|
||||
request, user=request.user)
|
||||
|
||||
slash_indices = [match.start() for match in re.finditer('/', request.path)]
|
||||
active_menu_urls = [request.path[:index + 1] for index in slash_indices]
|
||||
active_menu_urls = [
|
||||
request.path[:index + 1] for index in slash_indices[2:]
|
||||
] # Ignore the first two slashes '/plinth/apps/'
|
||||
return {
|
||||
'cfg': cfg,
|
||||
'submenu': menu.main_menu.active_item(request),
|
||||
'active_menu_urls': active_menu_urls,
|
||||
'box_name': _(cfg.box_name),
|
||||
'user_is_admin': is_user_admin(request, True),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -32,10 +32,10 @@ def _collect_operations_results(request, app):
|
||||
operations = operation_module.manager.collect_results(app.app_id)
|
||||
for operation in operations:
|
||||
if operation.exception:
|
||||
views.messages_error(request, operation.message,
|
||||
views.messages_error(request, operation.translated_message,
|
||||
operation.exception)
|
||||
else:
|
||||
messages.success(request, operation.message)
|
||||
messages.success(request, operation.translated_message)
|
||||
|
||||
|
||||
class SetupMiddleware(MiddlewareMixin):
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
Discover, load and manage FreedomBox applications.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import importlib
|
||||
import logging
|
||||
import pathlib
|
||||
@ -16,7 +15,7 @@ from plinth.signals import pre_module_loading
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
loaded_modules = collections.OrderedDict()
|
||||
loaded_modules = dict()
|
||||
_modules_to_load = None
|
||||
|
||||
|
||||
@ -36,8 +35,8 @@ def load_modules():
|
||||
for module_import_path in get_modules_to_load():
|
||||
module_name = module_import_path.split('.')[-1]
|
||||
try:
|
||||
loaded_modules[module_name] = importlib.import_module(
|
||||
module_import_path)
|
||||
module = importlib.import_module(module_import_path)
|
||||
loaded_modules[module_name] = module
|
||||
except Exception as exception:
|
||||
logger.exception('Could not import %s: %s', module_import_path,
|
||||
exception)
|
||||
|
||||
@ -4,9 +4,8 @@ Decorators for the backup views.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import os
|
||||
|
||||
from . import SESSION_PATH_VARIABLE
|
||||
from . import privileged, SESSION_PATH_VARIABLE
|
||||
|
||||
|
||||
def delete_tmp_backup_file(function):
|
||||
@ -15,12 +14,12 @@ def delete_tmp_backup_file(function):
|
||||
XXX: Implement a better way to delete uploaded files.
|
||||
|
||||
"""
|
||||
|
||||
@functools.wraps(function)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
path = request.session.get(SESSION_PATH_VARIABLE, None)
|
||||
if path:
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
privileged.remove_uploaded_archive(path)
|
||||
del request.session[SESSION_PATH_VARIABLE]
|
||||
return function(request, *args, **kwargs)
|
||||
|
||||
|
||||
@ -8,11 +8,13 @@ import re
|
||||
import subprocess
|
||||
import tarfile
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth.actions import privileged, secret_str
|
||||
from plinth.utils import Version
|
||||
|
||||
TIMEOUT = 30
|
||||
BACKUPS_DATA_PATH = pathlib.Path('/var/lib/plinth/backups-data/')
|
||||
BACKUPS_UPLOAD_PATH = pathlib.Path('/var/lib/freedombox/backups-upload/')
|
||||
MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/'
|
||||
|
||||
|
||||
@ -143,6 +145,24 @@ def list_repo(path: str,
|
||||
return json.loads(process.stdout.decode())
|
||||
|
||||
|
||||
@privileged
|
||||
def add_uploaded_archive(file_name: str, temporary_file_path: str):
|
||||
"""Store an archive uploaded by the user."""
|
||||
BACKUPS_UPLOAD_PATH.mkdir(parents=True, exist_ok=True)
|
||||
action_utils.move_uploaded_file(temporary_file_path, BACKUPS_UPLOAD_PATH,
|
||||
file_name, allow_overwrite=True,
|
||||
permissions=0o600)
|
||||
|
||||
|
||||
@privileged
|
||||
def remove_uploaded_archive(file_path: str):
|
||||
"""Delete the archive uploaded by the user."""
|
||||
resolved_file_path = pathlib.Path(file_path).resolve()
|
||||
if (resolved_file_path.is_relative_to(BACKUPS_UPLOAD_PATH)
|
||||
and resolved_file_path.is_file()):
|
||||
resolved_file_path.unlink()
|
||||
|
||||
|
||||
def _get_borg_version():
|
||||
"""Return the version of borgbackup."""
|
||||
process = _run(['borg', '--version'], stdout=subprocess.PIPE)
|
||||
|
||||
@ -5,7 +5,6 @@ Views for the backups app.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
|
||||
@ -190,11 +189,13 @@ class UploadArchiveView(SuccessMessageMixin, FormView):
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
"""store uploaded file."""
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
||||
self.request.session[SESSION_PATH_VARIABLE] = tmp_file.name
|
||||
for chunk in self.request.FILES['backups-file'].chunks():
|
||||
tmp_file.write(chunk)
|
||||
"""Store uploaded file."""
|
||||
uploaded_file = self.request.FILES['backups-file']
|
||||
# Hold on to Django's uploaded file. It will be used by other views.
|
||||
privileged.add_uploaded_archive(uploaded_file.name,
|
||||
uploaded_file.temporary_file_path())
|
||||
self.request.session[SESSION_PATH_VARIABLE] = str(
|
||||
privileged.BACKUPS_UPLOAD_PATH / uploaded_file.name)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ class BepastyApp(app_module.App):
|
||||
icon_filename='bepasty',
|
||||
short_description=_('File & Snippet Sharing'),
|
||||
description=_description, manual_page='bepasty',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-bepasty', info.name,
|
||||
|
||||
@ -19,3 +19,5 @@ backup = {
|
||||
},
|
||||
'services': ['uwsgi'],
|
||||
}
|
||||
|
||||
tags = [_('File sharing'), _('Pastebin')]
|
||||
|
||||
@ -56,7 +56,7 @@ class CalibreApp(app_module.App):
|
||||
name=_('calibre'), icon_filename='calibre',
|
||||
short_description=_('E-book Library'),
|
||||
description=_description, manual_page='Calibre',
|
||||
clients=manifest.clients,
|
||||
clients=manifest.clients, tags=manifest.tags,
|
||||
donation_url='https://calibre-ebook.com/donate')
|
||||
self.add(info)
|
||||
|
||||
|
||||
@ -16,3 +16,5 @@ backup = {
|
||||
},
|
||||
'services': ['calibre-server-freedombox']
|
||||
}
|
||||
|
||||
tags = [_('Ebook'), _('Library'), _('Ebook reader')]
|
||||
|
||||
@ -105,8 +105,17 @@ def _visit_library(browser, name):
|
||||
|
||||
functional.eventually(_service_available)
|
||||
|
||||
functional.eventually(browser.find_by_css,
|
||||
args=[f'.calibre-push-button[data-lid="{name}"]'])
|
||||
def _library_available():
|
||||
"""Refresh until the expected library is available."""
|
||||
available = browser.find_by_css(
|
||||
f'.calibre-push-button[data-lid="{name}"]')
|
||||
if not available:
|
||||
time.sleep(0.5)
|
||||
functional.visit(browser, '/calibre')
|
||||
|
||||
return available
|
||||
|
||||
functional.eventually(_library_available)
|
||||
link = browser.find_by_css(f'.calibre-push-button[data-lid="{name}"]')
|
||||
if not link:
|
||||
raise ValueError('Library not found')
|
||||
|
||||
@ -51,7 +51,8 @@ class CoturnApp(app_module.App):
|
||||
info = app_module.Info(app_id=self.app_id, version=self._version,
|
||||
name=_('Coturn'), icon_filename='coturn',
|
||||
short_description=_('VoIP Helper'),
|
||||
description=_description, manual_page='Coturn')
|
||||
description=_description, manual_page='Coturn',
|
||||
tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-coturn', info.name, info.short_description,
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
backup = {'secrets': {'directories': ['/etc/coturn']}, 'services': ['coturn']}
|
||||
|
||||
tags = [_('Video conference'), _('STUN'), _('TURN')]
|
||||
|
||||
@ -47,7 +47,8 @@ class DelugeApp(app_module.App):
|
||||
short_description=_('BitTorrent Web Client'),
|
||||
description=_description, manual_page='Deluge',
|
||||
clients=manifest.clients,
|
||||
donation_url='https://www.patreon.com/deluge_cas')
|
||||
donation_url='https://www.patreon.com/deluge_cas',
|
||||
tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-deluge', info.name, info.short_description,
|
||||
|
||||
@ -17,3 +17,5 @@ backup = {
|
||||
},
|
||||
'services': ['deluged', 'deluge-web']
|
||||
}
|
||||
|
||||
tags = [_('File sharing'), _('BitTorrent'), _('Web client'), _('P2P')]
|
||||
|
||||
@ -56,11 +56,13 @@ class EjabberdApp(app_module.App):
|
||||
"""Create components for the app."""
|
||||
super().__init__()
|
||||
|
||||
info = app_module.Info(
|
||||
app_id=self.app_id, version=self._version, depends=['coturn'],
|
||||
name=_('ejabberd'), icon_filename='ejabberd',
|
||||
short_description=_('Chat Server'), description=_description,
|
||||
manual_page='ejabberd', clients=manifest.clients)
|
||||
info = app_module.Info(app_id=self.app_id, version=self._version,
|
||||
depends=['coturn'], name=_('ejabberd'),
|
||||
icon_filename='ejabberd',
|
||||
short_description=_('Chat Server'),
|
||||
description=_description,
|
||||
manual_page='ejabberd',
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-ejabberd', info.name,
|
||||
|
||||
@ -119,3 +119,10 @@ backup = {
|
||||
},
|
||||
'services': ['ejabberd']
|
||||
}
|
||||
|
||||
tags = [
|
||||
_('Encrypted messaging'),
|
||||
_('Audio chat'),
|
||||
_('Video chat'),
|
||||
_('XMPP'),
|
||||
]
|
||||
|
||||
@ -63,7 +63,7 @@ class EmailApp(plinth.app.App):
|
||||
icon_filename='email',
|
||||
short_description=_('Email Server'),
|
||||
description=_description, manual_page='Email',
|
||||
clients=manifest.clients,
|
||||
clients=manifest.clients, tags=manifest.tags,
|
||||
donation_url='https://rspamd.com/support.html')
|
||||
self.add(info)
|
||||
|
||||
|
||||
@ -78,3 +78,5 @@ backup = {
|
||||
},
|
||||
'services': ['postfix', 'dovecot', 'rspamd']
|
||||
}
|
||||
|
||||
tags = [_('Email server'), _('IMAP'), _('Spam control')]
|
||||
|
||||
@ -61,7 +61,7 @@ class FeatherWikiApp(app_module.App):
|
||||
short_description=_('Personal Notebooks'),
|
||||
description=_description,
|
||||
manual_page='FeatherWiki',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-featherwiki', info.name,
|
||||
|
||||
@ -14,3 +14,5 @@ clients = [{
|
||||
}]
|
||||
|
||||
backup = {'data': {'directories': [str(wiki_dir)]}}
|
||||
|
||||
tags = [_('Wiki'), _('Note taking'), _('Website'), _('Quine'), _('Non-Debian')]
|
||||
|
||||
@ -8,6 +8,7 @@ import os
|
||||
import sys
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg
|
||||
@ -19,12 +20,6 @@ first_boot_steps = [
|
||||
'url': 'first_boot:welcome',
|
||||
'order': 0
|
||||
},
|
||||
{
|
||||
# TODO: Rename this, or merge with 'firstboot_completed'.
|
||||
'id': 'firstboot_complete',
|
||||
'url': 'first_boot:complete',
|
||||
'order': 10
|
||||
}
|
||||
]
|
||||
|
||||
_all_first_boot_steps = None
|
||||
@ -54,8 +49,38 @@ class FirstBootApp(app_module.App):
|
||||
def setup(self, old_version):
|
||||
"""Install and configure the app."""
|
||||
super().setup(old_version)
|
||||
|
||||
if not old_version:
|
||||
self._show_next_steps_notification()
|
||||
|
||||
self.enable()
|
||||
|
||||
def _show_next_steps_notification(self):
|
||||
"""After first setup, show notification for next steps."""
|
||||
from plinth.notification import Notification
|
||||
title = gettext_noop('Setup complete! Next steps:')
|
||||
message = gettext_noop(
|
||||
'Initial setup has been completed. Perform the next steps to make '
|
||||
'your {box_name} operational.')
|
||||
data = {
|
||||
'app_name': 'translate:' + gettext_noop('Next steps'),
|
||||
'app_icon': 'fa-arrow-right',
|
||||
'box_name': 'translate:' + cfg.box_name
|
||||
}
|
||||
actions = [{
|
||||
'type': 'link',
|
||||
'class': 'primary',
|
||||
'text': gettext_noop('See next steps'),
|
||||
'url': 'first_boot:complete'
|
||||
}, {
|
||||
'type': 'dismiss'
|
||||
}]
|
||||
Notification.update_or_create(id='first-boot-complete',
|
||||
app_id='first_boot', severity='info',
|
||||
title=title, message=message,
|
||||
actions=actions, data=data,
|
||||
group='admin', dismissed=False)
|
||||
|
||||
|
||||
def _clear_first_boot_steps(sender, module_name, **kwargs):
|
||||
"""Flush the cache of first boot steps so it is recreated."""
|
||||
@ -96,9 +121,9 @@ def _get_steps():
|
||||
def next_step():
|
||||
"""Return the resolved next first boot step URL required to go to.
|
||||
|
||||
If there are no more step remaining, return index page.
|
||||
If there are no more step remaining, return 'complete' page.
|
||||
"""
|
||||
return next_step_or_none() or 'index'
|
||||
return next_step_or_none() or 'first_boot:complete'
|
||||
|
||||
|
||||
def next_step_or_none():
|
||||
|
||||
@ -20,6 +20,7 @@ LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class FirstBootMiddleware(MiddlewareMixin):
|
||||
"""Forward to firstboot page if firstboot isn't finished yet."""
|
||||
|
||||
@staticmethod
|
||||
def process_request(request):
|
||||
"""Handle a request as Django middleware request handler."""
|
||||
@ -46,11 +47,11 @@ class FirstBootMiddleware(MiddlewareMixin):
|
||||
|
||||
# If user requests a step other than the welcome step, verify that they
|
||||
# indeed completed the secret verification by looking at the session.
|
||||
if (user_requests_firstboot and
|
||||
not request.path.startswith(reverse('first_boot:welcome')) and
|
||||
first_boot.firstboot_wizard_secret_exists() and
|
||||
not request.session.get('firstboot_secret_provided', False) and
|
||||
not is_user_admin(request)):
|
||||
if (user_requests_firstboot
|
||||
and not request.path.startswith(reverse('first_boot:welcome'))
|
||||
and first_boot.firstboot_wizard_secret_exists()
|
||||
and not request.session.get('firstboot_secret_provided', False)
|
||||
and not is_user_admin(request)):
|
||||
return HttpResponseRedirect(reverse('first_boot:welcome'))
|
||||
|
||||
# Redirect to first boot if requesting normal page and first
|
||||
@ -63,7 +64,7 @@ class FirstBootMiddleware(MiddlewareMixin):
|
||||
# No more steps in first boot
|
||||
first_boot.set_completed()
|
||||
|
||||
# Redirect to index page if request firstboot after it is
|
||||
# Redirect to 'complete' page if user requested firstboot after it is
|
||||
# finished.
|
||||
if firstboot_completed and user_requests_firstboot:
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
return HttpResponseRedirect(reverse('first_boot:complete'))
|
||||
|
||||
@ -12,6 +12,15 @@ no-brand
|
||||
{% block mainmenu_left_collapse %}
|
||||
{% endblock %}
|
||||
|
||||
{% block notifications_dropdown %}
|
||||
{% endblock %}
|
||||
|
||||
{% block mainmenu_toggler %}
|
||||
{% if user.is_authenticated %}
|
||||
{{ block.super }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block mainmenu_right %}
|
||||
{% if user.is_authenticated %}
|
||||
{% include "firstboot_navbar.html" %}
|
||||
|
||||
@ -8,27 +8,79 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Setup Complete!" %}</h2>
|
||||
<h2>{% trans "Setup Complete! Next Steps:" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Without any apps, your {{ box_name }} cannot do very much.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<ol class='next-steps'>
|
||||
<li>
|
||||
<div class="app-icon fa fa-refresh"></div>
|
||||
<div class="step-text">
|
||||
{% url 'upgrades:index' as upgrades_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Automatic <a href="{{ upgrades_url }}" target="_blank">software
|
||||
update</a> runs daily by default. For the first time, manually run
|
||||
it now.
|
||||
{% endblocktrans %}
|
||||
<form class="form" method="post" target="_blank"
|
||||
action="{% url 'upgrades:upgrade' %}">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="btn btn-default"
|
||||
value="{% trans "Update now" %}"/>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<div class="text-center">
|
||||
<a class="btn btn-lg btn-primary" href="{% url 'apps' %}">
|
||||
{% trans "Install Apps" %}</a>
|
||||
</div>
|
||||
<li>
|
||||
<div class="app-icon fa fa-eye-slash"></div>
|
||||
<div class="step-text">
|
||||
{% url 'privacy:index' as privacy_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Review <a href="{{ privacy_url }}" target="_blank">privacy options</a>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
{% url 'networks:index' as networks_url %}
|
||||
{% blocktrans trimmed %}
|
||||
You may want to check the <a href="{{ networks_url }}">network
|
||||
setup</a> and modify it if necessary.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
</p>
|
||||
<li>
|
||||
<div class="app-icon fa fa-signal"></div>
|
||||
<div class="step-text">
|
||||
{% url 'networks:index' as networks_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Review and setup <a href="{{ networks_url }}" target="_blank">network
|
||||
connections</a>. Change the default Wi-Fi password, if applicable.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="app-icon fa fa-tags"></div>
|
||||
<div class="step-text">
|
||||
{% url 'names:index' as names_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Configure a <a href="{{ names_url }}" target="_blank">domain name</a>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="app-icon fa fa-files-o"></div>
|
||||
<div class="step-text">
|
||||
{% url 'backups:index' as backups_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Configure and schedule remote
|
||||
<a href="{{ backups_url }}" target="_blank">backups</a>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="app-icon fa fa-th"></div>
|
||||
<div class="step-text">
|
||||
{% url 'apps' as apps_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Put {{ box_name }} to use by installing
|
||||
<a href="{{ apps_url }}" target="_blank">apps</a>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -31,22 +31,12 @@ class WelcomeView(FormView):
|
||||
|
||||
|
||||
class CompleteView(TemplateView):
|
||||
"""Show summary after all firstboot setup is done.
|
||||
|
||||
After viewing this page the firstboot module can't be accessed anymore.
|
||||
"""
|
||||
"""Show next steps after all firstboot wizard steps are done."""
|
||||
|
||||
template_name = 'firstboot_complete.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Mark as done as soon as page is served."""
|
||||
response = super().get(self, request, *args, **kwargs)
|
||||
first_boot.mark_step_done('firstboot_complete')
|
||||
return response
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add network connections to context list."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Setup Complete')
|
||||
context['firstboot_complete'] = True
|
||||
return context
|
||||
|
||||
@ -48,7 +48,7 @@ class GitwebApp(app_module.App):
|
||||
name=_('Gitweb'), icon_filename='gitweb',
|
||||
short_description=_('Simple Git Hosting'),
|
||||
description=_description, manual_page='GitWeb',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-gitweb', info.name, info.short_description,
|
||||
|
||||
@ -33,3 +33,5 @@ clients = [
|
||||
]
|
||||
|
||||
backup = {'data': {'directories': [GIT_REPO_PATH]}}
|
||||
|
||||
tags = [_('Git hosting'), _('Version control'), _('Developer tool')]
|
||||
|
||||
@ -18,8 +18,12 @@ import pytest
|
||||
from django import urls
|
||||
from django.conf import settings
|
||||
from django.http import Http404
|
||||
from django.urls import re_path
|
||||
|
||||
from plinth import cfg, module_loader
|
||||
from plinth import cfg
|
||||
from plinth import menu as menu_module
|
||||
from plinth import module_loader
|
||||
from plinth.modules import help as help_module
|
||||
from plinth.modules.help import views
|
||||
|
||||
# For all tests, use plinth.urls instead of urls configured for testing
|
||||
@ -35,8 +39,12 @@ def _is_page(response):
|
||||
@pytest.fixture(autouse=True, scope='module')
|
||||
def fixture_app_urls():
|
||||
"""Make sure app's URLs are part of plinth.urls."""
|
||||
urls = [
|
||||
re_path(r'^apps/$', views.index, name='apps'),
|
||||
re_path(r'^system/$', views.index, name='system')
|
||||
]
|
||||
with patch('plinth.module_loader._modules_to_load', new=[]) as modules, \
|
||||
patch('plinth.urls.urlpatterns', new=[]):
|
||||
patch('plinth.urls.urlpatterns', new=urls):
|
||||
modules.append('plinth.modules.help')
|
||||
module_loader.include_urls()
|
||||
yield
|
||||
@ -48,6 +56,13 @@ def fixture_developer_configuration():
|
||||
cfg.read_file(cfg.get_develop_config_path())
|
||||
|
||||
|
||||
@pytest.fixture(name='menu', autouse=True)
|
||||
def fixture_menu():
|
||||
"""Initialized menu module."""
|
||||
menu_module.init()
|
||||
help_module.HelpApp() # Create all menu components
|
||||
|
||||
|
||||
@pytest.mark.parametrize("view_name, view", (
|
||||
('feedback', views.feedback),
|
||||
('support', views.support),
|
||||
|
||||
@ -16,7 +16,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import get_language_from_request
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from plinth import __version__, cfg
|
||||
from plinth import __version__, cfg, menu
|
||||
from plinth.modules.upgrades import views as upgrades_views
|
||||
|
||||
from . import privileged
|
||||
@ -24,8 +24,11 @@ from . import privileged
|
||||
|
||||
def index(request):
|
||||
"""Serve the index page"""
|
||||
return TemplateResponse(request, 'help_index.html',
|
||||
{'title': _('Documentation and FAQ')})
|
||||
menu_items = menu.main_menu.active_item(request).sorted_items()
|
||||
return TemplateResponse(request, 'help_index.html', {
|
||||
'title': _('Documentation and FAQ'),
|
||||
'menu_items': menu_items
|
||||
})
|
||||
|
||||
|
||||
def contribute(request):
|
||||
|
||||
@ -52,7 +52,7 @@ class I2PApp(app_module.App):
|
||||
name=_('I2P'), icon_filename='i2p',
|
||||
short_description=_('Anonymity Network'),
|
||||
description=_description, manual_page='I2P',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-i2p', info.name, info.short_description,
|
||||
|
||||
@ -39,3 +39,5 @@ backup = {
|
||||
},
|
||||
'services': ['i2p']
|
||||
}
|
||||
|
||||
tags = [_('Anonymity network'), _('Censorship resistance')]
|
||||
|
||||
@ -45,7 +45,7 @@ class IkiwikiApp(app_module.App):
|
||||
name=_('ikiwiki'), icon_filename='ikiwiki',
|
||||
short_description=_('Wiki and Blog'),
|
||||
description=_description, manual_page='Ikiwiki',
|
||||
clients=manifest.clients,
|
||||
clients=manifest.clients, tags=manifest.tags,
|
||||
donation_url='https://ikiwiki.info/tipjar/')
|
||||
self.add(info)
|
||||
|
||||
|
||||
@ -11,3 +11,5 @@ clients = [{
|
||||
}]
|
||||
|
||||
backup = {'data': {'directories': ['/var/lib/ikiwiki/', '/var/www/ikiwiki/']}}
|
||||
|
||||
tags = [_('Wiki'), _('Blog'), _('Website')]
|
||||
|
||||
@ -42,7 +42,7 @@ class InfinotedApp(app_module.App):
|
||||
short_description=_('Gobby Server'),
|
||||
description=_description,
|
||||
manual_page='Infinoted',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-infinoted', info.name,
|
||||
|
||||
@ -42,3 +42,5 @@ backup = {
|
||||
},
|
||||
'services': ['infinoted']
|
||||
}
|
||||
|
||||
tags = [_('Note taking'), _('Collaborative editing'), _('Gobby')]
|
||||
|
||||
@ -43,7 +43,7 @@ class JanusApp(app_module.App):
|
||||
icon_filename='janus',
|
||||
short_description=_('Video Room'),
|
||||
description=_description, manual_page='Janus',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-janus', info.name, info.short_description,
|
||||
|
||||
@ -12,3 +12,5 @@ clients = [{
|
||||
}]
|
||||
|
||||
backup: dict = {}
|
||||
|
||||
tags = [_('Video conference'), _('WebRTC'), _('Web conference')]
|
||||
|
||||
@ -38,7 +38,7 @@ class JSXCApp(app_module.App):
|
||||
name=_('JSXC'), icon_filename='jsxc',
|
||||
short_description=_('Chat Client'),
|
||||
description=_description, manual_page='JSXC',
|
||||
clients=manifest.clients)
|
||||
clients=manifest.clients, tags=manifest.tags)
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-jsxc', info.name, info.short_description,
|
||||
|
||||
@ -12,3 +12,5 @@ clients = [{
|
||||
}]
|
||||
|
||||
backup: dict = {}
|
||||
|
||||
tags = [_('Web chat'), _('XMPP'), _('Client')]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user