Compare commits

...

57 Commits

Author SHA1 Message Date
Frederico Gomes
ad1b420397
wireguard: Enable FB to connect to a server using IPv6
This MR enables FreedomBox to connect as a "client" to a WireGuard
"server" using IPv6.

- Validate IPv4/6 with ip_interface
- Created helper functions to build NM settings for IPv4/6
- Modify get_settings to include settings for either IP version 4 or 6
- Created helper function to get NM address info
- Modify get_nm_info to work with IPv4 and IPv6
- Modified tests to use validate_ip_address_with_network
- Added IPv6 valid and invalid patterns to tests

Tested:

- IPv4 works unchanged
- IPv6 parsing + NM settings generation works
- IPv6 display in Show Server UI

Not tested:

- Needs IPv6 WireGuard server for full connectivity test

Closes: #1762

Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-26 18:41:20 -04:00
James Valleroy
9fd7a3b3af
debian/control: Add !nocheck for python3-segno
Tests:

- Succesfully build with nocheck profile:

gbp buildpackage --git-pbuilder --git-pbuilder-options="--profiles nocheck"

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 09:34:02 -04:00
Frederico Gomes
ea48448a72
wireguard: Added functional test for auto add client flow
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:51 -04:00
Frederico Gomes
050a5366c1
wireguard: Bump version
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:48 -04:00
Frederico Gomes
bc34a50a24
wireguard: Include python3-segno in app packages
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:46 -04:00
Frederico Gomes
ff2ac949cf
mypy: Added override for segno
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:43 -04:00
Frederico Gomes
941a597bc9
wireguard: Add security warning in template
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:38 -04:00
Frederico Gomes
5f25fc56a5
wireguard: Create URL for client config QR action
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:34 -04:00
Frederico Gomes
fa4e6002b3
wireguard: Add Show QR Code button
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:31 -04:00
Frederico Gomes
f9984ea9e4
themes-icons: Add QR Code icon
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:27 -04:00
Frederico Gomes
18d6f2d5db
wireguard: Add action for QR code generation
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:55:21 -04:00
Frederico Gomes
68e28cdebc
build-dependencies: Add python3-segno
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:54:58 -04:00
Frederico Gomes
79c978e03a
wireguard: Add client config file section and download button
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:54:53 -04:00
Frederico Gomes
ce2ce04979
wireguard: Create URL for client config download action
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:54:48 -04:00
Frederico Gomes
8e9b2a0631
wireguard: Create view to handle client config actions
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:54:32 -04:00
Frederico Gomes
60a6ac2a0d
wireguard: Create mixin for reusing generated client conf
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:54:09 -04:00
Frederico Gomes
bb6729a99a
wireguard: Create function for building conf files
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:54:03 -04:00
Frederico Gomes
81e60dff0e
wireguard: Add template to show generated client info
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:52:09 -04:00
Frederico Gomes
8cbcd39bb2
wireguard: Add empty form for AutoAddClientView
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:51:59 -04:00
Frederico Gomes
77a91fc357
wireguard/theme: Add icon to auto add client button
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:51:48 -04:00
Frederico Gomes
ebac5e86a3
wireguard: Add auto add client button
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:51:34 -04:00
Frederico Gomes
0a67183fe4
wireguard: Add URL for AutoAddClientView
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:51:29 -04:00
Frederico Gomes
36e1dd9e74
wireguard: Create AutoAddClientView
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:50:52 -04:00
Frederico Gomes
5fe11cedb6
wireguard: add wrapper function that generates client key pair
Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-23 08:50:35 -04:00
jay
3d96f41053
Translated using Weblate (Dutch)
Currently translated at 93.9% (1813 of 1929 strings)
2026-05-21 18:11:36 +00:00
Hosted Weblate user 151773
511de5e106
Translated using Weblate (German)
Currently translated at 99.3% (1916 of 1929 strings)
2026-05-21 18:11:33 +00:00
Luca Boccassi
9abe624265
Install and use sysusers.d/tmpfiles.d config files
sysusers.d/tmpfiles.d config files allow a package to use
declarative configuration instead of manually written maintainer
scripts. This also allows image-based systems to be created
with /usr/ only, and also allows for factory resetting a system
and recreating /etc/ on boot.

https://www.freedesktop.org/software/systemd/man/latest/sysusers.d.html
https://www.freedesktop.org/software/systemd/man/latest/tmpfiles.d.html

Tests:

- /var/lib/plinth and /var/lib/plinth/sessions/ are created on package install.
  Ownership is plinth:plinth. 0755 is permissions.

- /var/lib/plinth/firstboot-wizard-secret file is created on package install.
  Ownership is plinth:plinth. 0400 is permissions. During first wizard,
  providing the secret works.

- /var/lib/plinth/backups-data is owned by root:root.

- When upgrading from old package to new the permissions don't change.

- When reinstalling the new package, the permissions do not change.

- User is created same as before.
  plinth987:987:FreedomBox service:/var/lib/plinth:/usr/sbin/nologin
- Group is created same as before.
  plinth987:
- id plinth
  uid=987(plinth) gid=987(plinth) groups=987(plinth)

- Upgrading from old package to new does not change user and group records.

- Reinstalling new version does not change user and group records.

[sunil: Don't recursively change ownership for /var/lib/plinth/]
[sunil: Change ownership specifically for /var/lib/plinth/firstboot-wizard-secret]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
Tested-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-05-13 15:37:45 -07:00
Luca Boccassi
a4b3d53141
Stop deleting system user on remove/purge
This is widely considered bad practice, as the kernel recycles
UIDs/GIDs. So any potential leftover file/directory can then
become owned by the next user/group that gets added, with
unpredictable consequences.
2026-05-13 15:37:41 -07:00
Sunil Mohan Adapa
529bbf77df
bepasty: Don't remove old system user and group
As removing a system user and group is considered a bad practice.  Old unused
system accounts are mostly harmless.

Tests:

- Bepasty functional tests pass.

- Installing bepasty and uploading a file works.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-05-13 15:37:38 -07:00
Sunil Mohan Adapa
e48ff1afbc
infinoted: Use systemd-sysusers for creating a system user account
- Drop dependency on 'adduser' package.

Tests:

- Functional tests for infinoted work.

- Installing infinoted app works. The system user and group are created with
proper UID/GID, shell, gecos, and home directory.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-05-13 15:37:35 -07:00
Sunil Mohan Adapa
f4be9039d2
syncthing: Use systemd-sysusers for creating a system user account
- Drop dependency on 'adduser' package.

Tests:

- Functional tests for syncthing work.

- Installing syncthing app works. The system user and group are created with
proper UID/GID, shell, gecos, and home directory.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-05-13 15:37:32 -07:00
James Valleroy
0f98ed67e7
Release v26.8 to unstable
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-11 20:32:27 -04:00
James Valleroy
27e9b2ac0e
doc: Fetch latest manual
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-05-11 20:31:36 -04:00
Sunil Mohan Adapa
066f22ff35
api: Drop access-info API
This was meant to be used by the mobile client. But it is in unmaintained and
unusable state. We can re-introduce the API when mobile client is back in
action.

See: https://github.com/freedombox/FreedomBox/pull/1215

[Joseph]
This API is not called by the mobile client, so it is safe to remove.

Reviewed-by: Joseph Nuthalapati <njoseph@riseup.net>
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-05-10 18:32:35 +05:30
Dietmar
bacaa59725
Translated using Weblate (German)
Currently translated at 99.2% (1914 of 1929 strings)
2026-05-05 19:11:34 +02:00
Pierfrancesco Passerini
2b28850b63
Translated using Weblate (Italian)
Currently translated at 100.0% (1929 of 1929 strings)
2026-05-05 19:11:32 +02:00
Pierfrancesco Passerini
d0c20a74f9
Translated using Weblate (Italian)
Currently translated at 99.8% (1927 of 1929 strings)
2026-05-02 17:09:50 +02:00
James Valleroy
5c42e04813
Release v26.7.1 to unstable
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-28 18:26:58 -04:00
James Valleroy
91296b6e81
doc: Fetch latest manual
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-28 18:26:20 -04:00
James Valleroy
449b78ae81
locale: Update translation strings
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-28 18:01:49 -04:00
Sunil Mohan Adapa
495f5f8a0d
radicale: tests: functional: Better checking for well-known URLs
- When SOGO app is enabled, radicale functional tests fail.

Tests:

- Enable SOGO app and run radicale functional tests. They fail without patch and
pass with the patch.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-27 19:19:53 -04:00
Sunil Mohan Adapa
2bd33ed428
radicale: Fix issue with parsing new configuration file
The latest version of radicale calendar server's configuration file does not
parse with augeas. This is because it contains the following entry in [headers]
section:

Content-Security-Policy = default-src 'self'; object-src 'none'

The semicolon is treated as comment by the lens which is not correct. Fix this
by overriding comment_re in the lens.

Tests:

- Updated test case works when using augparse.

- With the patch, latest upstream configuration file parses without errors.

- Functional tests work for radicale in testing distribution. Without patch
radicale fails to install.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-27 19:19:47 -04:00
Sunil Mohan Adapa
ae50ceaeb0
radicale, bepasty: Fix issue with failed diagnostic test
Fixes: #2571.

- During backup, a service related to backup is stopped and then started back
again after the backup. In case of .socket unit, the .service unit is not being
stopped and it continues to listen on the socket path. When the .socket unit is
started back again, it tries to listen on the socket path and fails.

- To fix the issue, when running stop, restart, etc. operations on a .socket
file, try to perform that operations that we actually intend.

Tests:

- Unit tests pass

- Functional tests for bepasty and radicale work.

- After taking a radicale backup uwsgi-app@radicale.socket does not become
inactive (works when service is running or stopped). uwsgi-app@radicale.service
stops if it is running, backup doesn't fail if service is not running.

- After taking a radicale backup uwsgi-app@bepasty-freedombox.socket does not
become inactive (works when service is running or stopped).
uwsgi-app@bepasty-freedombox.service stops if it is running, backup doesn't fail
if service is not running.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-27 19:13:14 -04:00
Frederico Gomes
dc3439fd70
radicale: Enable lc_username for case-insensitive auth
Enable lc_username = True in [auth] section during setup.

Bump version.

Signed-off-by: Frederico Gomes <fredericojfgomes@gmail.com>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-25 13:25:49 -04:00
James Valleroy
5cebe7ffe0
Release v26.7 to unstable
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-20 20:26:20 -04:00
James Valleroy
52ce78ca1e
doc: Fetch latest manual
Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
2026-04-20 20:25:18 -04:00
James Valleroy
5b615a169b
debian: tests: Add test to access interface status
Tests:

- Build the Debian package and run autopkgtest with qemu backend. The
  test is passed.

- Revert the recent fix for loading icons. Build the Debian package
  and run autopkgtests again. It is failed due to maximum redirects
  followed.

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-04-20 10:39:09 -07:00
Pierfrancesco Passerini
5b8e3b8f85
Translated using Weblate (Italian)
Currently translated at 99.4% (1918 of 1929 strings)
2026-04-18 23:09:49 +02:00
Coucouf
6b033b3f45
Translated using Weblate (French)
Currently translated at 100.0% (1929 of 1929 strings)
2026-04-16 15:09:51 +00:00
bittin1ddc447d824349b2
be84971c5a
Translated using Weblate (Swedish)
Currently translated at 100.0% (1929 of 1929 strings)
2026-04-15 10:10:34 +00:00
Dietmar
f9de7865ed
Translated using Weblate (Italian)
Currently translated at 99.3% (1917 of 1929 strings)
2026-04-09 10:09:55 +00:00
Jiří Podhorecký
cbb7998565
Translated using Weblate (Czech)
Currently translated at 100.0% (1929 of 1929 strings)
2026-04-09 10:09:54 +00:00
Burak Yavuz
a3c4d177df
Translated using Weblate (Turkish)
Currently translated at 100.0% (1929 of 1929 strings)
2026-04-09 10:09:52 +00:00
Dietmar
abc066a92b
Translated using Weblate (German)
Currently translated at 99.0% (1910 of 1929 strings)
2026-04-09 10:09:50 +00:00
大王叫我来巡山
516a8e23d0
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 60.9% (1176 of 1929 strings)
2026-04-08 09:09:54 +02:00
Besnik Bleta
6d7da4b43f
Translated using Weblate (Albanian)
Currently translated at 99.6% (1923 of 1929 strings)
2026-04-08 09:09:53 +02:00
Burak Yavuz
7ff91f3e86
Translated using Weblate (Turkish)
Currently translated at 99.2% (1914 of 1929 strings)
2026-04-08 09:09:49 +02:00
86 changed files with 1473 additions and 690 deletions

65
debian/changelog vendored
View File

@ -1,3 +1,68 @@
freedombox (26.8) unstable; urgency=medium
[ Pierfrancesco Passerini ]
* Translated using Weblate (Italian)
[ Dietmar ]
* Translated using Weblate (German)
[ Sunil Mohan Adapa ]
* api: Drop access-info API
[ James Valleroy ]
* doc: Fetch latest manual
-- James Valleroy <jvalleroy@mailbox.org> Mon, 11 May 2026 20:32:09 -0400
freedombox (26.7.1) unstable; urgency=medium
[ Frederico Gomes ]
* radicale: Enable lc_username for case-insensitive auth
[ Sunil Mohan Adapa ]
* radicale, bepasty: Fix issue with failed diagnostic test
* radicale: Fix issue with parsing new configuration file
* radicale: tests: functional: Better checking for well-known URLs
[ James Valleroy ]
* locale: Update translation strings
* doc: Fetch latest manual
-- James Valleroy <jvalleroy@mailbox.org> Tue, 28 Apr 2026 18:26:38 -0400
freedombox (26.7) unstable; urgency=medium
[ Burak Yavuz ]
* Translated using Weblate (Turkish)
[ Besnik Bleta ]
* Translated using Weblate (Albanian)
[ 大王叫我来巡山 ]
* Translated using Weblate (Chinese (Simplified Han script))
[ Dietmar ]
* Translated using Weblate (German)
* Translated using Weblate (Italian)
[ Jiří Podhorecký ]
* Translated using Weblate (Czech)
[ bittin1ddc447d824349b2 ]
* Translated using Weblate (Swedish)
[ Coucouf ]
* Translated using Weblate (French)
[ Pierfrancesco Passerini ]
* Translated using Weblate (Italian)
[ James Valleroy ]
* debian: tests: Add test to access interface status
* doc: Fetch latest manual
-- James Valleroy <jvalleroy@mailbox.org> Mon, 20 Apr 2026 20:25:51 -0400
freedombox (26.6) unstable; urgency=medium
[ Coucouf ]

3
debian/control vendored
View File

@ -11,6 +11,7 @@ Uploaders:
James Valleroy <jvalleroy@mailbox.org>,
Build-Depends:
debhelper-compat (= 13),
dh-sequence-installsysusers,
dblatex,
dh-python,
docbook-xsl,
@ -51,6 +52,7 @@ Build-Depends:
python3-pytest-runner <!nocheck>,
python3-requests,
python3-ruamel.yaml,
python3-segno <!nocheck>,
python3-setuptools,
python3-systemd,
python3-typeshed <!nocheck>,
@ -74,7 +76,6 @@ Depends:
${python3:Depends},
${misc:Depends},
${freedombox:Depends},
adduser,
augeas-tools,
bind9-dnsutils,
curl,

View File

@ -13,21 +13,9 @@ sed -i 's+-:ALL EXCEPT root fbx (admin) (sudo):ALL+-:ALL EXCEPT root fbx plinth
case "$1" in
configure)
if ! getent group plinth >/dev/null; then
addgroup --system --quiet plinth
fi
if ! getent passwd plinth >/dev/null; then
adduser --system --quiet --ingroup plinth --no-create-home --home /var/lib/plinth plinth
fi
chown plinth: /var/lib/plinth
chown plinth: /var/lib/plinth/sessions
if [ ! -e '/var/lib/freedombox/is-freedombox-disk-image' ]; then
umask 377
base64 < /dev/urandom | head -c 16 | sed -e 's+$+\n+' > /var/lib/plinth/firstboot-wizard-secret
chown plinth:plinth /var/lib/plinth/firstboot-wizard-secret
db_subst plinth/firstboot_wizard_secret secret $(cat /var/lib/plinth/firstboot-wizard-secret)
db_input high plinth/firstboot_wizard_secret || true
db_go

View File

@ -4,7 +4,6 @@ set -e
case "$1" in
purge)
deluser --system --quiet plinth || true
rm -rf /var/lib/plinth
# Remove legacy directory too

1
debian/freedombox.sysusers vendored Normal file
View File

@ -0,0 +1 @@
u! plinth - "FreedomBox service" /var/lib/plinth

3
debian/freedombox.tmpfiles vendored Normal file
View File

@ -0,0 +1,3 @@
d /var/lib/plinth 0755 plinth plinth
d /var/lib/plinth/sessions 0755 plinth plinth
Z /var/lib/plinth/firstboot-wizard-secret 0400 plinth plinth

16
debian/tests/access-web-interface vendored Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
set -e
# Wait for FreedomBox setup to complete.
sleep 30
journalctl --unit=plinth --unit=freedombox-privileged
# Get FreedomBox status
curl --location --cookie "" --fail --write-out "%{response_code}" --insecure \
--stderr - https://localhost/freedombox/status/
# Access FreedomBox interface
curl --location --cookie "" --fail --write-out "%{response_code}" --insecure \
--stderr - https://localhost/freedombox/

10
debian/tests/control vendored
View File

@ -16,3 +16,13 @@ Restrictions: needs-root, breaks-testbed
Test-Command: PYTHONPATH='/usr/lib/python3/dist-packages/' py.test-3 -p no:cacheprovider --cov=plinth --cov-report=html:debci/htmlcov --cov-report=term
Depends: e2fsprogs, git, python3-pytest, python3-pytest-cov, python3-pytest-django, @
Restrictions: breaks-testbed
#
# Try to access the FreedomBox web interface.
#
# iptables package installs alternatives files, with iptables-nft as default alternative.
# Without it, firewalld has this error: INVALID_IPV: 'ipv4' is not a valid backend or is unavailable
#
Tests: access-web-interface
Depends: iptables, @
Restrictions: needs-root, isolation-machine, breaks-testbed

View File

@ -1,7 +1,7 @@
#language en
##TAG:TRANSLATION-HEADER-START
~- [[FreedomBox/Guide/Passkeys|English]] - [[DebianWiki/EditorGuide#translation|(+)]] -~
~- [[FreedomBox/Guide/Passkeys|English]] - [[es/FreedomBox/Guide/Passkeys|Español]] - [[DebianWiki/EditorGuide#translation|(+)]] -~
##TAG:TRANSLATION-HEADER-END
<<TableOfContents>>

View File

@ -8,6 +8,23 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f
The following are the release notes for each !FreedomBox version.
== FreedomBox 26.8 (2026-05-11) ==
* locale: Update translations for German, Italian
* api: Drop access-info API
== FreedomBox 26.7.1 (2026-04-28) ==
* radicale, bepasty: Fix issue with failed diagnostic test
* radicale: Enable lc_username for case-insensitive auth
* radicale: Fix issue with parsing new configuration file
* radicale: tests: functional: Better checking for well-known URLs
== FreedomBox 26.7 (2026-04-20) ==
* debian: tests: Add test to access interface status
* locale: Update translations for Albanian, Chinese (Simplified Han script), Czech, French, German, Italian, Swedish, Turkish
== FreedomBox 26.6 (2026-04-06) ==
=== Highlights ===

View File

@ -1,6 +1,11 @@
#language en
##For Translators - to have a constantly up to date translation header in you page, you can just add a line like the following (with the comment's character at the start of the line removed)
## <<Include(sudo, ,from="^##TAG:TRANSLATION-HEADER-START",to="^##TAG:TRANSLATION-HEADER-END")>>
##TAG:TRANSLATION-HEADER-START
~- [[DebianWiki/EditorGuide#translation|Translation(s)]]: English - [[es/FreedomBox/Manual/Users|Español]] -~
##TAG:TRANSLATION-HEADER-END
----
<<TableOfContents()>>

View File

@ -0,0 +1,174 @@
#language es
<<Include(FreedomBox/Guide/Passkeys, ,from="^##TAG:TRANSLATION-HEADER-START",to="^##TAG:TRANSLATION-HEADER-END")>>
<<TableOfContents>>
## BEGIN_INCLUDE
== Usa claves de acceso (passkeys) para mejorar la seguridad de inicio de sesión ==
{{{#!wiki tip
Se recomiendan encarecidamente las claves de acceso frente a las contraseñas.
}}}
'''Disponible desde''': !FreedomBox 26.6
!FreedomBox permite a los usuarios iniciar sesión en su cuenta con claves de acceso.
Las claves de acceso son una forma de verificar la identidad del usuario usando firmas digitales. Son una alternativa más segura que las contraseñas.
La información secreta se conserva con el usuario en su teléfono, portátil o en un token hardware y se desbloquea mediante un PIN, huella dactilar o reconocimiento facial.
No se almacenan secretos en el servidor. El servidor solo conoce la información pública que puede usarse para verificar las firmas del usuario.
=== ¿Cómo funcionan las claves de acceso? ===
Después de que el usuario inicie sesión en su cuenta, se puede añadir una o más claves de acceso a la cuenta desde la página "Administrar claves de acceso".
Al añadir una clave de acceso, el hardware de la clave de acceso (o autenticador) generará un par de claves pública/privada vinculado al dominio y a la cuenta de usuario.
La clave privada se mantiene en el hardware y la clave pública se proporciona al servidor. El servidor almacena la clave pública junto con la cuenta de usuario.
Más tarde, cuando un usuario intenta iniciar sesión en su cuenta, el servidor envía una larga cadena aleatoria al autenticador llamada santo (santo y seña).
El hardware firma digitalmente el santo con la clave privada y la envía (la seña) al servidor.
El servidor puede verificar que la firma la ha hecho el poseedor de la clave privada usando solo la clave pública que tiene (esto es una propiedad de los pares de claves pública/privada).
Una vez verificada, el servidor inicia sesión en la cuenta de usuario asociada a esa clave pública.
Durante este proceso, el navegador actúa como intermediario de confianza entre el hardware de la clave de acceso y el servidor.
Garantiza que el usuario se verifique proporcionando PIN, huella, reconocimiento facial, etc.
También garantiza que una clave de acceso solo se use con el dominio para el que está destinada.
=== Mejor seguridad ===
Las claves de acceso ofrecen mayor seguridad que las contraseñas:
* '''Autenticación multifactor''': Durante el registro de una clave de acceso y durante el inicio de sesión,
!FreedomBox solicita que el navegador verifique al usuario.
Esto significa que el usuario necesitará desbloquear el dispositivo autenticador proporcionando un PIN, huella o reconocimiento facial.
Esto actúa como uno de los factores de autenticación: "algo que sabes" o "algo que eres".
Otro factor de autenticación es "algo que tienes": el hardware que almacena tu clave de acceso (como Solokey, Nitrokey o Yubikey) o un teléfono.
Juntos, esto es similar a usar una contraseña con un segundo factor de autenticación.
Por tanto, las claves de acceso pueden reemplazar la autenticación en dos pasos siendo mucho más cómodas y fáciles de usar.
* '''Sin reutilización''': Las claves de acceso nunca se reutilizan. Para cada dominio se genera y usa una clave de acceso separada.
Los navegadores aseguran que la clave de acceso de un dominio nunca se use con otro dominio.
A diferencia de las contraseñas reutilizadas, cuando un sitio web o servicio se ve comprometido, los maleantes no pueden usar eso para acceder a tu cuenta en otro sitio o servicio.
Esto también previene ataques de phishing donde sitios impotores se hacen pasar por sitios legítimos.
* '''No hay secretos en el servidor''': El sitio que permite el uso de claves de acceso para el inicio de sesión no almacena información secreta.
Solo guarda la parte pública de la clave de acceso. Si esta información es obtenida por un adversario, no podrá iniciar sesión en el sitio.
Solo la clave privada almacenada en el dispositivo autenticador puede usarse para iniciar sesión
(ya que solo una clave privada puede crear las firmas necesarias para el proceso de inicio de sesión).
* '''No adivinación''': La parte secreta de una clave de acceso es mucho más dificil de adivinar que una contraseña.
No existe el riesgo de que alguien adivine tu contraseña y acceda a tu cuenta.
No existe el riesgo de que configures accidentalmente una contraseña predecible.
* '''Comodidad''': Los usuarios no necesitan recordar nombre de usuario, correo electrónico o un secreto para iniciar sesión en un sitio.
No necesitan recibir OTP por SMS o correo, usar una app TOTP ni confirmar el inicio desde una app móvil.
Tras hacer clic en el botón "Login mediante clave de acceso", desbloquean su autenticador con un PIN, huella o reconocimiento facial.
Luego tocan físicamente el autenticador (si es necesario). El usuario queda entonces autenticado. Hay menos cosas que recordar.
Incluso el PIN de un autenticador hardware suele ser más fácil de recordar que una contraseña.
Esta comodidad anima a los usuarios a usar este mecanismo, lo que a la larga mejora la seguridad.
=== Hardware necesario para claves de acceso ===
{{{#!wiki tip
'''El proyecto !FreedomBox recomienda [[https://solokeys.com/|Solokeys]]''' para el almacenamiento de claves de acceso:
* El [[https://github.com/solokeys/solo2|firmware]] (el sistema operativo del hardware) es software libre.
* Los [[https://github.com/solokeys/solo2-hw|diseños del hardware]] también son libres.
* El equipo de Solokeys y el equipo de !FreedomBox colaboran entre sí.
}}}
Hay varias formas de empezar con claves de acceso:
* '''Hardware de claves de acceso separado''': La forma recomendada de almacenar claves de acceso es en una llave hardware específica.
En esta configuración, la clave privada de la clave de acceso nunca abandona el dispositivo hardware.
Además, suelen estar diseñadas para que sea difícil para un atacante con acceso físico extraer la clave de acceso.
Otra ventaja de estos dispositivos es que pueden utilizarse con todos tus dispositivos existentes, como teléfonos, portátiles y escritorios.
Estos dispositivos interactúan con teléfonos y equipos mediante USB, Bluetooth o NFC.
En caso de NFC, el dispositivo funciona por proximidad con el teléfono sin energía adicional.
Al usar un hardware separado, sin embargo, debes tener un método de respaldo para iniciar sesión si pierdes el dispositivo hardware.
Esto puede ser otra clave de acceso en otro hardware o una contraseña. Ver la sección de copia de seguridad más abajo.
* '''Hardware empotrado para claves de acceso''': Cuando no hay un dispositivo hardware separado, es preferible el hardware especializado, como un TPM, integrado en el equipo.
Esta configuración seguirá asegurando que las claves de acceso no abandonen el hardware.
Una desventaja es que la clave de acceso solo funciona con ese equipo y tendrás que registrar cada dispositivo que uses por separado.
* '''Administradores de contraseñas''': Como último recurso, se pueden usar gestores de contraseñas que soporten claves de acceso y funcionen con tu navegador o sistema operativo.
Android, iOS y Windows ofrecen administradores así.
Las claves de acceso guardadas en administradores suelen sincronizarse con la nube y una brecha en ese servicio/cuenta podría comprometer todas tus cuentas.
Sin embargo, funcionan en múltiples dispositivos y normalmente no tienes que preocuparte por perder un solo dispositivo hardware.
=== Nombrar tu clave de acceso ===
En !FreedomBox, cuando se añade una clave de acceso a tu cuenta, por defecto se nombra 'Key 1'. La siguiente se llamará 'Key 2' y así sucesivamente.
Sin embargo, es buena práctica nombrarlas para saber en qué dispositivo están almacenadas. Por ejemplo, puedes llamarlas 'Clave del Solokey primario', 'Clave del tfn Android', etc.
Si se pierde un dispositivo, puedes iniciar sesión y eliminar esa clave de la lista de claves de acceso asociadas a tu cuenta.
=== Múltiples dominios ===
Cada clave de acceso está estrictamente ligada a un dominio y nunca se usa para otro dominio. Esto es necesario para evitar que un dominio impostor pueda suplantar a uno legítimo.
Por tanto, si tu !FreedomBox está configurada con múltiples dominios, el navegador y el dispositivo autenticador tratarán cada dominio como cuentas separadas a efectos de autenticación con claves de acceso.
Esto significa que debes registrar claves de acceso separadas para cada uno de tus dominios.
Por ejemplo, supón que tu !FreedomBox tiene configurados los dominios midominio1.fbx.one y midominio2.ejemplo.
Visita midominio1.fbx.one, inicia sesión en tu cuenta y añade una clave de acceso. Esta clave de acceso quedará ligada a ese dominio.
Cuando intentes iniciar sesión, la clave de acceso funcionará si accedes a midominio1.fbx.one pero no funcionará al acceder a midominio2.ejemplo.
Para hacer que el segundo dominio funcione, necesitas añadir una segunda clave de acceso mientras accedes a tu !FreedomBox con el nombre de dominio midominio2.ejemplo.
Entonces se almacenarán dos claves de acceso en tu token hardware. La primera estará ligada a midominio1.fbx.one y solo se usará cuando accedas a ese dominio.
La segunda estará ligada a midominio2.ejemplo y solo se usará cuando accedas a ese dominio.
=== Múltiples cuentas de usuario ===
Cuando usas un hardware de claves de acceso para varias cuentas de usuario en el mismo !FreedomBox, se crearán claves de acceso separadas para cada cuenta.
A cada clave de acceso se le asignará el nombre de usuario de la cuenta a la que esté ligada. Esta información se almacena en la clave de acceso así como en el servidor.
Durante el inicio de sesión, el navegador te pedirá que selecciones la cuenta de usuario a la que quieres acceder.
Si solo existe una clave de acceso para un dominio dado, no se mostrará el diálogo de selección y el usuario iniciará sesión en la cuenta correspondiente a esa clave de acceso.
=== Copia de seguridad de la clave de acceso ===
En caso de pérdida del dispositivo que almacena tu clave de acceso, necesitas métodos alternativos para iniciar sesión en tu cuenta:
1. Puedes registrar y mantener dos claves de acceso en dos dispositivos separados. Por ejemplo, tu clave de acceso primaria podría estar en un token Solokey y
la segunda en un teléfono Android o en otro token Solokey. Si uno se pierde, puedes iniciar sesión con el otro. Este es el método recomendado.
1. !FreedomBox sigue soportando contraseñas incluso después de registrar claves de acceso.
Así que, si se pierde un dispositivo con clave de acceso, puedes iniciar sesión con una contraseña.
1. Si olvidas tu contraseña y tu cuenta no es la única cuenta administradora en el !FreedomBox, puedes pedir a un administrador que restablezca tu contraseña.
Después de eso podrás registrar una nueva clave de acceso almacenada en un nuevo dispositivo.
=== Plataformas compatibles ===
Las passkeys se basan en WebAuthn, un estándar publicado por el World Wide Web Consortium.
Por tanto, la implementación de !FreedomBox debería funcionar allí donde funcionen las clave de acceso. Se ha probado de la siguiente manera:
|| '''SO/Dispositivo''' || '''Navegador''' || '''Autenticador''' || '''Resultado''' ||
|| GNU/Linux || Firefox || Solokeys || Ok ||
|| GNU/Linux || Firefox || Yubikey || Ok ||
|| GNU/Linux || Chromium || Solokeys || Ok ||
|| GNU/Linux || GNOME Web || - || KO (El navegador no soporta WebAuthn) ||
|| Windows || Firefox || Windows Hello || Ok ||
|| Windows || Firefox || Solokeys || Ok ||
|| Windows || Firefox || Android Phone || Ok ||
|| Windows || Chrome || Windows Hello || Ok ||
|| Windows || Chrome || Solokeys || Ok ||
|| Windows || Chrome || Android Phone || Ok ||
|| Windows || Edge || Windows Hello || Ok ||
|| Windows || Edge || Solokeys || Ok ||
|| Windows || Edge || Android Phone || Ok ||
|| Android || Firefox || Google Password Manager || Ok ||
|| Android || Firefox || Solokeys USB || KO (No se detecta el toque tras introducir el PIN) ||
|| Android || Firefox || Solokeys NFC || KO (Es necesario entender la configuración NFC) ||
|| Android || Firefox || Otro dispositivo || Sin probar ||
|| Android || Chrome || Google Password Manager || Ok ||
|| Android || Chrome || Solokeys USB || KO (No se detecta el toque tras introducir el PIN) ||
|| Android || Chrome || Solokeys NFC || KO (Es necesario entender la configuración NFC) ||
|| Android || Chrome || Otro dispositivo || Sin probar ||
## END_INCLUDE
Volver a la [[es/FreedomBox/Features|descripción de Funcionalidades]] o a las páginas del [[es/FreedomBox/Manual|manual]].
<<Include(es/FreedomBox/Portal)>>
----
CategoryFreedomBox

View File

@ -8,6 +8,23 @@ For more technical details, see the [[https://salsa.debian.org/freedombox-team/f
The following are the release notes for each !FreedomBox version.
== FreedomBox 26.8 (2026-05-11) ==
* locale: Update translations for German, Italian
* api: Drop access-info API
== FreedomBox 26.7.1 (2026-04-28) ==
* radicale, bepasty: Fix issue with failed diagnostic test
* radicale: Enable lc_username for case-insensitive auth
* radicale: Fix issue with parsing new configuration file
* radicale: tests: functional: Better checking for well-known URLs
== FreedomBox 26.7 (2026-04-20) ==
* debian: tests: Add test to access interface status
* locale: Update translations for Albanian, Chinese (Simplified Han script), Czech, French, German, Italian, Swedish, Turkish
== FreedomBox 26.6 (2026-04-06) ==
=== Highlights ===

View File

@ -1,6 +1,6 @@
#language es
~- [[DebianWiki/EditorGuide#translation|Translation(s)]]: [[FreedomBox/Manual/Users|English]] - Español -~
<<Include(FreedomBox/Manual/Users, ,from="^##TAG:TRANSLATION-HEADER-START",to="^##TAG:TRANSLATION-HEADER-END")>>
<<TableOfContents()>>
@ -9,25 +9,36 @@
== Usuarios y Grupos ==
Puedes otorgar acceso a tu !FreedomBox a otros usuarios. Proporciona el nombre del usuario y su contraseña y asignale un grupo. Actualmente se soportan los grupos
Esta app puede usarse para crear, editar y eliminar cuentas de usuario en !FreedomBox. Muchas apps con interfaz web en !FreedomBox admiten inicio de sesión único mediante OpenID Connect.
Esto significa que si has iniciado sesión en la interfaz web de !FreedomBox, no es necesario iniciar sesión de forma separada en la app.
Otras apps permiten usar las cuentas de !FreedomBox mediante LDAP.
Finalmente, hay algunas apps que gestionan sus propias cuentas de usuario de forma independiente a las cuentas que tengas en !FreedomBox.
Puedes otorgar acceso a tu FreedomBox a otros usuarios. Proporciona el nombre del usuario y su contraseña y asignale un grupo. Actualmente se soportan los grupos:
* admin
* bit-torrent
* calibre
* ed2k
* feed-reader
* freedombox-share
* freedombox-ssh
* git-access
* minidlna
* syncthing
* kiwix
* syncthing-access
* vpn
* web-search
* wiki
El usuario podrá ingresar a los servicios que soporten ingreso único (single-sign-on) mediante LDAP si figuran en el grupo apropriado.
Los usuarios del grupo admin podrán ingresar en todos los servicios.
También podrán acceder al sistema vía SSH y tendrán privilegios administrativos (sudo).
Los grupos de un usuario pueden modificarse más adelante.
Los usuarios del grupo `admin` podrán ingresar en todos los servicios. También pueden ingresar al sistema por SSH y escalar a privilegios administrativos (sudo).
!FreedomBox admite el inicio de sesión con claves de acceso.
Las claves de acceso son una alternativa más segura que las contraseñas y son la forma recomendada de autenticarse en !FreedomBox.
Lee más en la [[FreedomBox/Guide/Passkeys|guía de claves de acceso de FreedomBox]].
Estas características se pueden cambiar más tarde.
Asimismo es posible establecer una clave pública SSH que permitirá al usuario ingresar al sistema de modo seguro sin emplear su contraseña. Pueder dar de alta varias claves, una en cada línea. Las líneas en blanco o que comiencen por # se ignoran.
Asimismo es posible establecer una clave pública SSH que permitirá al usuario ingresar al sistema de modo seguro sin emplear su contraseña. Puedes dar de alta varias claves, una en cada línea. Las líneas en blanco o que comiencen por # se ignoran.
El idioma de la interfaz se puede establecer individualmente para cada usuario. Por omisión se emplea el del navegador.

View File

@ -80,6 +80,7 @@
<<Include(es/FreedomBox/Manual/Users, , from="## BEGIN_INCLUDE", to="## END_INCLUDE")>>
= Guías =
<<Include(es/FreedomBox/Guide/Passkeys, , from="## BEGIN_INCLUDE", to="## END_INCLUDE")>>
<<Include(es/FreedomBox/Guide/ExposeLocalService,, from="## BEGIN_INCLUDE", to="## END_INCLUDE")>>
= Hardware =

View File

@ -3,4 +3,4 @@
Package init file.
"""
__version__ = '26.6'
__version__ = '26.8'

View File

@ -120,14 +120,6 @@ def service_disable(service_name: str, check: bool = False):
except subprocess.CalledProcessError:
pass
if service_name.endswith('.socket'):
# Instead, may need to query the unit for associated .service file.
base_name = service_name.rpartition('.')[0]
try:
service_stop(f'{base_name}.service', check=check)
except subprocess.CalledProcessError:
pass
def service_mask(service_name: str, check: bool = False):
"""Mask a service"""
@ -143,25 +135,62 @@ def service_start(service_name: str, check: bool = False):
"""Start a service with systemd."""
service_action(service_name, 'start', check=check)
# When starting a .socket unit, there is not need to start the .service
# unit as it will be automatically started when a request is received on
# the socket.
def _get_service_unit(socket_name: str) -> str:
"""Return the .service unit name for a .socket unit."""
# Instead, may need to query the unit for associated .service file.
base_name = socket_name.rpartition('.')[0]
return f'{base_name}.service'
def service_stop(service_name: str, check: bool = False):
"""Stop a service with systemd."""
service_action(service_name, 'stop', check=check)
# When stopping a .socket unit, most of the time, we must also stop
# .service unit. This frees up resources when disabling the app. It also
# stops using resources that are being backed up.
if service_name.endswith('.socket'):
service_action(_get_service_unit(service_name), 'stop', check=check)
def service_restart(service_name: str, check: bool = False):
"""Restart a service with systemd."""
if not service_name.endswith('.socket'):
service_action(service_name, 'restart', check=check)
else:
# When restarting a .socket unit, most of the time, we actually want to
# restart the corresponding .service unit. This reloads the
# configuration changes as needed. To restart, all we need to do stop
# the service. It will be automatically started again by .socket unit.
service_action(_get_service_unit(service_name), 'stop', check=check)
def service_try_restart(service_name: str, check: bool = False):
"""Try to restart a service with systemd."""
if not service_name.endswith('.socket'):
service_action(service_name, 'try-restart', check=check)
else:
# When try-restarting a .socket unit, most of the time, we actually
# want to restart the corresponding .service unit. This reloads the
# configuration changes as needed. To restart, all we need to do stop
# the service. It will be automatically started again by .socket unit.
service_action(_get_service_unit(service_name), 'stop', check=check)
def service_reload(service_name: str, check: bool = False):
"""Reload a service with systemd."""
if not service_name.endswith('.socket'):
service_action(service_name, 'reload', check=check)
else:
# When reloading a .socket unit, most of the time, we actually want to
# reload the corresponding .service unit. This reloads the
# configuration changes as needed.
service_action(_get_service_unit(service_name), 'reload', check=check)
def service_try_reload_or_restart(service_name: str, check: bool = False):
@ -169,7 +198,14 @@ def service_try_reload_or_restart(service_name: str, check: bool = False):
Do nothing if service is not running.
"""
if not service_name.endswith('.socket'):
service_action(service_name, 'try-reload-or-restart', check=check)
else:
# When reloading a .socket unit, most of the time, we actually want to
# reload the corresponding .service unit. This reloads the
# configuration changes as needed.
service_action(_get_service_unit(service_name),
'try-reload-or-restart', check=check)
def service_reset_failed(service_name: str, check: bool = False):

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-04-16 02:28+0000\n"
"Last-Translator: MohammedSaalif <2300031323@kluniversity.in>\n"
"Language-Team: Arabic <https://hosted.weblate.org/projects/freedombox/"
@ -6386,7 +6386,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6395,14 +6395,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2020-06-10 15:41+0000\n"
"Last-Translator: aiman an <an1f3@hotmail.com>\n"
"Language-Team: Arabic (Saudi Arabia) <https://hosted.weblate.org/projects/"
@ -6390,7 +6390,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6399,14 +6399,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
@ -6340,7 +6340,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6349,14 +6349,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-12-17 07:00+0000\n"
"Last-Translator: 109247019824 "
"<109247019824@users.noreply.hosted.weblate.org>\n"
@ -6686,7 +6686,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6695,14 +6695,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-04-01 03:02+0000\n"
"Last-Translator: MURALA SAI GANESH <saiganeshmurala@gmail.com>\n"
"Language-Team: Bengali <https://hosted.weblate.org/projects/freedombox/"
@ -6409,7 +6409,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6418,14 +6418,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-02-06 23:02+0000\n"
"Last-Translator: kosagi <marti.torra@natana.cat>\n"
"Language-Team: Catalan <https://hosted.weblate.org/projects/freedombox/"
@ -7170,7 +7170,7 @@ msgstr ""
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7179,14 +7179,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-08 18:48+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-04-09 10:09+0000\n"
"Last-Translator: Jiří Podhorecký <j.podhorecky@volny.cz>\n"
"Language-Team: Czech <https://hosted.weblate.org/projects/freedombox/"
"freedombox/cs/>\n"
@ -7160,7 +7160,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7174,7 +7174,7 @@ msgstr ""
"klientská aplikace</a>. K Radicale má přístup každý uživatel s přihlašovacím "
"jménem {box_name}."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7184,7 +7184,7 @@ msgstr ""
"nových kalendářů a adresářů kontaktů. Nepodporuje přidávání událostí či "
"kontaktů, to je třeba dělat v tomu určeném klientovi."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9782,14 +9782,12 @@ msgstr "Upravit uživatele %(username)s"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Přihlášení pomocí passkey se nezdařilo: "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Tato aplikace nepodporuje diagnostiku"
msgstr "Prohlížeč nepodporuje přístupové klíče."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -9798,28 +9796,29 @@ msgstr "Přihlášení"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Přihlásit se pomocí přístupového klíče"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Aktualizovat nastavení"
msgstr "Aktualizovat přístupový klíč"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Přidání přístupového klíče selhalo: "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Přístupové klíče"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"Práce s přístupovými klíči vyžaduje použití rozhraní JavaScript API "
"prohlížeče. Chcete-li pokračovat, povolte ve svém prohlížeči podporu jazyka "
"JavaScript."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -9830,47 +9829,46 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Přístupové klíče jsou způsob, jak ověřit identitu uživatele pomocí "
"digitálních podpisů. Jsou bezpečnější alternativou k heslům. Tajné informace "
"jsou uchovávány u uživatele v jeho telefonu, notebooku nebo hardwarovém "
"tokenu a odemykají se pomocí PINu, otisku prstu nebo identifikace obličeje. "
"Na serveru nejsou uloženy žádné tajné informace. Server zná pouze veřejné "
"informace, které lze použít k ověření podpisů uživatele."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Přidat heslo"
msgstr "Přidat přístupový klíč"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Doména"
msgstr "Pro doménu"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "Přidáno"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Naposledy skenováno: "
msgstr "Naposledy použito"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "K uživatelskému účtu nebyly přidány žádné přístupové klíče."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "Smazat přístupový klíč <em class=\"passkey-name\"></em>?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
"K opětovnému přidání tohoto přístupového klíče budete potřebovat zařízení."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Smazat uživatele"
msgstr "Smazat přístupový klíč"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -9886,6 +9884,8 @@ msgstr "Upravit uživatele %(username)s"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Pro lepší zabezpečení použijte <a href=\"%(passkeys_url)s\">přístupové "
"klíče</a>."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -9951,20 +9951,16 @@ msgid "Password changed successfully."
msgstr "Heslo úspěšně změněno."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Sdílení s tímto názvem už existuje."
msgstr "Přístupový klíč s tímto identifikátorem již existuje."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Upravit uživatele"
msgstr "Upravit přístupový klíč"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "Použitý přístupový klíč není znám."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -10745,10 +10741,8 @@ msgid " System"
msgstr " Systém"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Správa aliasů"
msgstr "Správa přístupových klíčů"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"
@ -10804,7 +10798,7 @@ msgstr "Mobilní"
#: plinth/templates/clients.html:82
msgid "Android APK"
msgstr ""
msgstr "Android APK"
#: plinth/templates/clients.html:91
msgid "Play Store"

View File

@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: FreedomBox UI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:19+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Danish <https://hosted.weblate.org/projects/freedombox/"
@ -7365,7 +7365,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, fuzzy, python-brace-format
#| msgid ""
#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7385,14 +7385,14 @@ msgstr ""
"carddav-clients\">understøttet klient-applikation</a>. Radicale kan tilgås "
"af enhver bruger der har et log ind til {box_name}."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -9,9 +9,9 @@ msgid ""
msgstr ""
"Project-Id-Version: FreedomBox UI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-29 11:09+0000\n"
"Last-Translator: Dietmar <sagen@permondes.de>\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-05-21 18:11+0000\n"
"Last-Translator: Hosted Weblate user 151773 <paul.schell87@gmail.com>\n"
"Language-Team: German <https://hosted.weblate.org/projects/freedombox/"
"freedombox/de/>\n"
"Language: de\n"
@ -19,7 +19,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.17-dev\n"
"X-Generator: Weblate 2026.6.dev0\n"
#: plinth/config.py:103
#, python-brace-format
@ -324,7 +324,7 @@ msgstr ""
#: plinth/modules/backups/forms.py:79
msgid "Hour of the day to trigger backup operation"
msgstr "Uhrzeit, zu der der Sicherungsvorgang ausgelöst werden soll"
msgstr "Uhrzeit für den Start des Sicherungsvorgangs"
#: plinth/modules/backups/forms.py:81
msgid ""
@ -3810,7 +3810,7 @@ msgstr "Heimautomatisierung"
#: plinth/modules/homeassistant/manifest.py:63
msgid "IoT"
msgstr ""
msgstr "IdD"
#: plinth/modules/homeassistant/manifest.py:64
#: plinth/modules/networks/manifest.py:8
@ -6722,7 +6722,7 @@ msgstr ""
#: plinth/modules/oidc/__init__.py:56
msgid "OpenID Connect Provider"
msgstr ""
msgstr "OpenID Verbindungsanbieter"
#: plinth/modules/oidc/templates/oauth2_provider/authorize.html:14
msgid "Application"
@ -7303,7 +7303,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7317,7 +7317,7 @@ msgstr ""
"clients\">unterstützte Client Software</a> notwendig. Radicale kann von "
"jedem Benutzer mit einem {box_name}-Konto verwendet werden."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7328,7 +7328,7 @@ msgstr ""
"Kontaktdaten wird nicht unterstützt; dies muss über einen separaten Client "
"erfolgen."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9996,14 +9996,12 @@ msgstr "Benutzer %(username)s bearbeiten"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Die Anmeldung mit dem Passkey ist fehlgeschlagen: "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Diese App unterstützt keine Diagnose"
msgstr "Der Browser unterstützt keine Passkeys."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -10012,28 +10010,29 @@ msgstr "Anmelden"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Mit Passkey anmelden"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Übernehmen der Änderungen"
msgstr "Passkey aktualisieren"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Hinzufügen des Passkeys ist fehlgeschlagen: "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Passkeys"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"Für die Verwendung von Passkeys ist die JavaScript-API des Browsers "
"erforderlich. Bitte aktivieren Sie JavaScript in Ihrem Browser, um "
"fortzufahren."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -10044,47 +10043,49 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Passkeys sind eine Methode zur Überprüfung der Benutzeridentität mithilfe "
"digitaler Signaturen. Sie stellen eine sicherere Alternative zu Passwörtern "
"dar. Die geheimen Informationen werden beim Benutzer auf seinem Smartphone, "
"Laptop oder einem Hardware-Token gespeichert und mittels PIN, Fingerabdruck "
"oder Gesichtserkennung entsperrt. Auf dem Server werden keine geheimen "
"Informationen gespeichert. Der Server kennt lediglich die öffentlichen "
"Informationen, die zur Überprüfung der Signaturen des Benutzers verwendet "
"werden können."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Kennwort hinzufügen"
msgstr "Passkey hinzufügen"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Domain"
msgstr "Für Domäne"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "Hinzugefügt"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Zuletzt gescannt: "
msgstr "Zuletzt verwendet"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "Dem Benutzerkonto wurden keine Passkeys hinzugefügt."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "Passkey <em class=\"passkey-name\"></em> löschen?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
"Sie benötigen das Gerät, auf dem dieser Passkey gespeichert ist, um ihn "
"erneut hinzuzufügen."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Benutzer löschen"
msgstr "Passkey löschen"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -10100,6 +10101,7 @@ msgstr "Benutzer <em>%(username)s</em> bearbeiten"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Verwenden Sie <a href=\"%(passkeys_url)s\">Passkeys</a> für mehr Sicherheit."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -10166,20 +10168,16 @@ msgid "Password changed successfully."
msgstr "Passwort erfolgreich geändert."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Eine Freigabe mit diesem Namen existiert bereits."
msgstr "Ein Passkey mit dieser Kennung existiert bereits."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Benutzer bearbeiten"
msgstr "Passkey bearbeiten"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "Der verwendete Passkey ist nicht bekannt."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -10335,17 +10333,15 @@ msgstr "Informationen zu dieser %(box_name)s:"
#: plinth/modules/wireguard/templates/wireguard.html:25
msgid "Property"
msgstr ""
msgstr "Eigenschaft"
#: plinth/modules/wireguard/templates/wireguard.html:26
msgid "Value"
msgstr ""
msgstr "Wert"
#: plinth/modules/wireguard/templates/wireguard.html:33
#, fuzzy
#| msgid "Endpoint"
msgid "Endpoint(s)"
msgstr "Endpunkt"
msgstr "Endpunkt(e)"
#: plinth/modules/wireguard/templates/wireguard.html:41
#, python-format
@ -10720,7 +10716,7 @@ msgstr "Foto"
#: plinth/modules/zoph/manifest.py:26
msgid "Organizer"
msgstr ""
msgstr "Organisator"
#: plinth/modules/zoph/templates/zoph-pre-setup.html:15
#: plinth/modules/zoph/templates/zoph-pre-setup.html:28
@ -10988,10 +10984,8 @@ msgid " System"
msgstr " System"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Aliase verwalten"
msgstr "Passkeys verwalten"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -6341,7 +6341,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6350,14 +6350,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-02-22 13:17+0000\n"
"Last-Translator: James Valleroy <jvalleroy@mailbox.org>\n"
"Language-Team: Greek <https://hosted.weblate.org/projects/freedombox/"
@ -7441,7 +7441,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, fuzzy, python-brace-format
#| msgid ""
#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7460,7 +7460,7 @@ msgstr ""
"clients/\">πελάτη</a> . Το Radicale μπορεί να προσεγγιστεί από οποιονδήποτε "
"χρήστη με {box_name} πιστοποιητικά."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7471,7 +7471,7 @@ msgstr ""
"γεγονότων ή επαφών, το οποίο πρέπει να γίνει χρησιμοποιώντας ένα ξεχωριστό "
"πελάτη."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2024-11-01 17:00+0000\n"
"Last-Translator: gallegonovato <fran-carro@hotmail.es>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/freedombox/"
@ -7381,7 +7381,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, fuzzy, python-brace-format
#| msgid ""
#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7401,7 +7401,7 @@ msgstr ""
"supported-clients\">aplicación cliente soportada</a>. Cualquier persona "
"autenticada en {box_name} puede acceder a Radicale."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7411,7 +7411,7 @@ msgstr ""
"de nuevos calendarios y agendas. No soporta añadir eventos o contactos, que "
"debe hacerse usando un cliente separado."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-01-09 20:01+0000\n"
"Last-Translator: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>\n"
"Language-Team: Estonian <https://hosted.weblate.org/projects/freedombox/"
@ -6384,7 +6384,7 @@ msgstr ""
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6393,14 +6393,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:19+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Persian <https://hosted.weblate.org/projects/freedombox/"
@ -7062,7 +7062,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7071,14 +7071,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Plinth 0.6\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2016-01-31 22:24+0530\n"
"Last-Translator: Sunil Mohan Adapa <sunil@medhas.org>\n"
"Language-Team: Plinth Developers <freedombox-"
@ -7390,7 +7390,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7399,14 +7399,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: FreedomBox UI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-29 11:09+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-04-16 15:09+0000\n"
"Last-Translator: Coucouf <coucouf@coucouf.fr>\n"
"Language-Team: French <https://hosted.weblate.org/projects/freedombox/"
"freedombox/fr/>\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.17-dev\n"
"X-Generator: Weblate 5.17.1-dev\n"
#: plinth/config.py:103
#, python-brace-format
@ -7326,7 +7326,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7342,7 +7342,7 @@ msgstr ""
"a>. Tous les utilisateur disposant dun compte sur la {box_name} ont accès à "
"Radicale."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7353,7 +7353,7 @@ msgstr ""
"charge lajout dévénements ou de contacts, opérations qui doivent être "
"réalisées avec un client dédié."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -10049,14 +10049,12 @@ msgstr "Paramètres du compte %(username)s"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Échec de la connexion par clé daccès : "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Cette application nest pas compatible avec les tests de diagnostic"
msgstr "Le navigateur nest pas compatible avec les clés daccès."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -10065,28 +10063,28 @@ msgstr "Sidentifier"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Connexion avec une clé daccès"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Appliquer les changements"
msgstr "Mettre à jour la clé daccès"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Échec dajout dune clé daccès : "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Clés daccès"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"Lutilisation de clés daccès nécessite lutilisation de lAPI Javascript du "
"navigateur. Veuillez activer Javascript dans votre navigateur pour continuer."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -10097,47 +10095,48 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Les clés daccès sont un moyen de vérifier lidentité de lutilisateur au "
"travers de signatures numériques. Elles présentent un alternative plus sure "
"à lusage des mots de passe. Les secrets sont conservés sur le téléphone, "
"lordinateur ou le jeton dauthentification matériel et protégés par un code "
"ou une empreinte biométrique. Aucun secret nest conservé sur les serveur. "
"Le serveur na besoin de connaître que les informations publiques permettant "
"de vérifier la signature de lutilisateurice."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Ajouter un mot de passe"
msgstr "Ajouter une clé daccès"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Domaine"
msgstr "Pour le domaine"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "Ajoutée"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Dernière analyse : "
msgstr "Dernière utilisation"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "Aucune clé daccès pour ce compte."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "Supprimer la clé daccès <em class=\"passkey-name\"></em>?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
"Vous aurez besoin de lappareil contenant cette clé daccès pour pouvoir "
"lajouter à nouveau."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Supprimer l'utilisateur"
msgstr "Supprimer la clé daccès"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -10153,6 +10152,8 @@ msgstr "Modifier lutilisateur <em>%(username)s</em>"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Utilisez une <a href=\"%(passkeys_url)s\">clé daccès</a> pour une meilleure "
"sécurité."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -10220,20 +10221,16 @@ msgid "Password changed successfully."
msgstr "Le mot de passe a été changé."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Un partage existe déjà avec ce nom."
msgstr "Il y a déjà un clé daccès avec cet identifiant."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Modification de lutilisateur"
msgstr "Modifier la clé daccès"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "La clé daccès utilisée est inconnue."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -11036,10 +11033,8 @@ msgid " System"
msgstr " Système"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Configurer des alias"
msgstr "Gérer les clés daccès"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-12-30 10:51+0000\n"
"Last-Translator: gallegonovato <fran-carro@hotmail.es>\n"
"Language-Team: Galician <https://hosted.weblate.org/projects/freedombox/"
@ -6385,7 +6385,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6394,14 +6394,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2021-01-18 12:32+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Gujarati <https://hosted.weblate.org/projects/freedombox/"
@ -6776,7 +6776,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6785,14 +6785,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-03-31 06:09+0000\n"
"Last-Translator: bsurajpatra <ankitsuraj1111@gmail.com>\n"
"Language-Team: Hindi <https://hosted.weblate.org/projects/freedombox/"
@ -7329,7 +7329,7 @@ msgstr "क्वासेलड्रोइड"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, fuzzy, python-brace-format
#| msgid ""
#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7347,14 +7347,14 @@ msgstr ""
"radicale.org/clients/\">समर्थित क्लाइंट एप्लिकेशन</a> कि जरुरत है. राडिकैल किसी "
"{box_name} यूसर पहुंचा जा सकता है एक लॉगिन के साथ."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "राडिकैल"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-06-04 15:01+0000\n"
"Last-Translator: András Szűcs "
"<andrascc86288f63c44cb5@users.noreply.hosted.weblate.org>\n"
@ -7496,7 +7496,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, fuzzy, python-brace-format
#| msgid ""
#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7517,7 +7517,7 @@ msgstr ""
"alkalmazásra</a> is. A Radicale elérhető bármely felhasználó számára a "
"{box_name} eszközön."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7527,7 +7527,7 @@ msgstr ""
"címjegyzékek létrehozását támogatja. Nem támogatja az események vagy "
"kapcsolatok hozzáadását, ezeket külön kliens segítségével kell elvégezni."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Indonesian (FreedomBox)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:19+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Indonesian <https://hosted.weblate.org/projects/freedombox/"
@ -7047,7 +7047,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7056,14 +7056,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-31 06:09+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-05-05 17:11+0000\n"
"Last-Translator: Pierfrancesco Passerini <p.passerini@gmail.com>\n"
"Language-Team: Italian <https://hosted.weblate.org/projects/freedombox/"
"freedombox/it/>\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.17-dev\n"
"X-Generator: Weblate 5.17.1\n"
#: plinth/config.py:103
#, python-brace-format
@ -7219,7 +7219,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7233,7 +7233,7 @@ msgstr ""
"client supportata</a>. Ogni utente con un profilo {box_name} può accedere a "
"Radicale."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7243,7 +7243,7 @@ msgstr ""
"nuovi calendari e rubriche. Non supporta l'aggiunta di eventi o contatti, "
"che dovranno essere gestiti tramite un client separato."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9872,14 +9872,12 @@ msgstr "Modifica l'utente %(username)s"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Aaccesso con passkey non riuscito: "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Questa applicazione non supporta la diagnostica"
msgstr "Il browser non supporta le passkey."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -9888,28 +9886,28 @@ msgstr "Login"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Accedi con passkey"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Aggiorna impostazioni"
msgstr "Aggiorna Passkey"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Aggiunta di passkey non riuscita: "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Passkey"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"L'uso di passkey richiede l'accesso alle API Javascript del browser. Prima "
"di continuare, accertati che tale supporto sia presente."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -9920,47 +9918,46 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Le passkey permettono la verifica degli accessi mediante l'utilizzo di firme "
"digitali. Rappresentano un'alternativa più sicura alle password. Le "
"informazioni riservate vengono conservate dall'utente sul proprio telefono, "
"laptop o su un token hardware e sbloccate tramite PIN, impronta digitale o "
"riconoscimento del volto. Nessuna informazione riservata viene memorizzata "
"sul server. Il server conosce solo le informazioni pubbliche che possono "
"essere utilizzate per verificare le firme dell'utente."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Aggiungi la password"
msgstr "Aggiungi Passkey"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Dominio"
msgstr "Per Dominio"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "Aggiunto"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Ultima scansione: "
msgstr "Ultimo utilizzo"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "Nessuna passkey aggiunta all'account utente."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "Elimina passkey <em class=\"passkey-name\"></em>?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
msgstr "Avrai bisogno di questa passkey per aggiungere di nuovo il dispositivo."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Cancella l'utente"
msgstr "Cancella Passkey"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -9976,6 +9973,8 @@ msgstr "Modifica utente <em>%(username)s</em>"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Utilizzare <a href=\"%(passkeys_url)s\">passkey</a> per una maggiore "
"sicurezza."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -10040,20 +10039,16 @@ msgid "Password changed successfully."
msgstr "La password è stata aggiornata."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Una condivisione con questo nome esiste già."
msgstr "Esiste già un Passkey con quell'identificatore."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Modifica utente"
msgstr "Modifica Passkey"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "Passkey sconosciuta."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -10842,10 +10837,8 @@ msgid " System"
msgstr " Sistema"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Gestire gli alias"
msgstr "Gestire le Passkey"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-10-24 16:02+0000\n"
"Last-Translator: Jun Nogata <nogajun@gmail.com>\n"
"Language-Team: Japanese <https://hosted.weblate.org/projects/freedombox/"
@ -6348,7 +6348,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6357,14 +6357,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2020-07-16 16:41+0000\n"
"Last-Translator: Yogesh <yogesh@karnatakaeducation.org.in>\n"
"Language-Team: Kannada <https://hosted.weblate.org/projects/freedombox/"
@ -6343,7 +6343,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6352,14 +6352,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:19+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Lithuanian <https://hosted.weblate.org/projects/freedombox/"
@ -6363,7 +6363,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6372,14 +6372,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:20+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Latvian <https://hosted.weblate.org/projects/freedombox/"
@ -6362,7 +6362,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6371,14 +6371,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: FreedomBox UI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2024-10-27 23:30+0000\n"
"Last-Translator: Sunil Mohan Adapa <sunil@medhas.org>\n"
"Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/"
@ -7433,7 +7433,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, fuzzy, python-brace-format
#| msgid ""
#| "Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7451,7 +7451,7 @@ msgstr ""
"href=\"http://radicale.org/clients/\">støttet klientprogram </a>. Radicale "
"kan nås av alle brukere med {box_name}-innlogging."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7461,7 +7461,7 @@ msgstr ""
"kalendre og adressebøker. Den tilbyr ikke å legge inn nye hendelser eller "
"kontakter, det må gjøres med en egen klient."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2025-09-17 09:01+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-05-21 18:11+0000\n"
"Last-Translator: jay <jj8z@proton.me>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/freedombox/"
"freedombox/nl/>\n"
"Language: nl\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.14-dev\n"
"X-Generator: Weblate 2026.6.dev0\n"
"X-Language: nl_NL\n"
"X-Source-Language: C\n"
@ -412,10 +412,8 @@ msgid "Passphrase"
msgstr "Wachtwoordzin"
#: plinth/modules/backups/forms.py:187
#, fuzzy
#| msgid "Passphrase; Only needed when using encryption."
msgid "Only needed when using encryption."
msgstr "Wachtwoordzin; Alleen nodig bij het gebruik van versleuteling."
msgstr "Alleen nodig bij het gebruik van versleuteling."
#: plinth/modules/backups/forms.py:190
msgid "Confirm Passphrase"
@ -482,10 +480,8 @@ msgid "SSH server password"
msgstr "SSH-server wachtwoord"
#: plinth/modules/backups/forms.py:262
#, fuzzy
#| msgid "Disable password authentication"
msgid "Required for password-based authentication."
msgstr "Wachtwoord authenticatie uitschakelen"
msgstr "Benodigd voor wachtwoord authenticatie."
#: plinth/modules/backups/forms.py:275
#, fuzzy
@ -824,10 +820,9 @@ msgid "Authentication to remote server failed."
msgstr "Authenticatie naar externe server is mislukt."
#: plinth/modules/backups/views.py:50
#, fuzzy
#| msgid "Error establishing connection to server: {}"
msgid "Error establishing connection to server: {} {} {}"
msgstr "Fout bij het tot stand brengen van een verbinding met de server: {}"
msgstr ""
"Fout bij het tot stand brengen van een verbinding met de server: {} {} {}"
#: plinth/modules/backups/views.py:82
msgid "Backup schedule updated."
@ -1470,14 +1465,7 @@ msgid "Webserver Home Page"
msgstr "Startpagina van de webserver"
#: plinth/modules/config/forms.py:37
#, fuzzy, python-brace-format
#| msgid ""
#| "Choose the default page that must be served when someone visits your "
#| "{box_name} on the web. A typical use case is to set your blog or wiki as "
#| "the home page when someone visits the domain name. Note that once the "
#| "home page is set to something other than {box_name} Service (Plinth), "
#| "your users must explicitly type /plinth or /freedombox to reach "
#| "{box_name} Service (Plinth)."
#, python-brace-format
msgid ""
"Choose the default page that must be served when someone visits your "
"{box_name} on the web. A typical use case is to set your blog or wiki as the "
@ -1489,8 +1477,8 @@ msgstr ""
"{box_name} op internet bezoekt. Vaak wordt het blog of wiki ingesteld als de "
"startpagina wanneer iemand de domeinnaam bezoekt. Merk op dat zodra de "
"startpagina is ingesteld op iets anders dan {box_name} service (Plinth), "
"gebruikers expliciet /plinth of /freedombox moeten typen om de {box_name} "
"service (Plinth) te bereiken."
"gebruikers expliciet /freedombox moeten typen om de {box_name} service "
"(Plinth) te bereiken."
#: plinth/modules/config/forms.py:48
msgid "Show advanced apps and features"
@ -2446,16 +2434,12 @@ msgstr ""
"automatisch aangemaakt en wijzen naar de eerste admin gebruiker."
#: plinth/modules/email/__init__.py:41
#, fuzzy
#| msgid ""
#| "<a href=\"/plinth/apps/roundcube/\">Roundcube app</a> provides web "
#| "interface for users to access email."
msgid ""
"<a href=\"/freedombox/apps/roundcube/\">Roundcube app</a> provides web "
"interface for users to access email."
msgstr ""
"<a href=\"/plinth/apps/roundcube/\">Roundcube app</a> biedt gebruikers een "
"webinterface om toegang te krijgen tot e-mail."
"<a href=\"/freedombox/apps/roundcube/\">Roundcube app</a> biedt gebruikers "
"een webinterface om toegang te krijgen tot e-mail."
#: plinth/modules/email/__init__.py:43
msgid ""
@ -7172,7 +7156,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7186,7 +7170,7 @@ msgstr ""
"clienttoepassing</a> nodig. Radicale kan worden benaderd door elke "
"{box_name} gebruiker."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7197,7 +7181,7 @@ msgstr ""
"gebeurtenissen of contactpersonen, die moeten worden gedaan met behulp van "
"een aparte client."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9771,10 +9755,8 @@ msgstr ""
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Deze toepassing heeft geen diagnosetests"
msgstr "De browser ondersteunt geen passkeys."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -9834,10 +9816,8 @@ msgid "Added"
msgstr ""
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Laatst gescand: "
msgstr "Laatst gebruikt"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2024-07-13 12:09+0000\n"
"Last-Translator: Monika <adamdomenek@protonmail.com>\n"
"Language-Team: Polish <https://hosted.weblate.org/projects/freedombox/"
@ -7012,7 +7012,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7021,14 +7021,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-04-09 22:41+0000\n"
"Last-Translator: tuliogit <wikigeolog@gmx.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/freedombox/"
@ -7266,7 +7266,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7280,7 +7280,7 @@ msgstr ""
"radicale.org/master.html#supported-clients\"&gt;&lt;/a&gt; . O Radicale pode "
"ser acessado por qualquer usuário com um login {box_name} ."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7290,7 +7290,7 @@ msgstr ""
"novos calendários e catálogos de endereços. Não permite adicionar eventos ou "
"contatos, o que deve ser feito usando um cliente separado."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radical"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-03-15 18:09+0000\n"
"Last-Translator: OwlGale <owlgale@users.noreply.hosted.weblate.org>\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/freedombox/"
@ -7224,7 +7224,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7238,7 +7238,7 @@ msgstr ""
"clients\">поддерживаемое клиентское приложение</a>. Доступ к Radicale может "
"получить любой пользователь с логином {box_name}."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7248,7 +7248,7 @@ msgstr ""
"создание новых календарей и адресных книг. Он не поддерживает добавление "
"событий или контактов, для этого требуется отдельный клиент."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2021-04-27 13:32+0000\n"
"Last-Translator: HelaBasa <R45XvezA@protonmail.ch>\n"
"Language-Team: Sinhala <https://hosted.weblate.org/projects/freedombox/"
@ -6343,7 +6343,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6352,14 +6352,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:19+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Slovenian <https://hosted.weblate.org/projects/freedombox/"
@ -6698,7 +6698,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6707,14 +6707,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-26 09:09+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-04-08 07:09+0000\n"
"Last-Translator: Besnik Bleta <besnik@programeshqip.org>\n"
"Language-Team: Albanian <https://hosted.weblate.org/projects/freedombox/"
"freedombox/sq/>\n"
@ -7237,7 +7237,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7252,7 +7252,7 @@ msgstr ""
"Radicale mund të hyhet nga cilido përdorues me kredenciale hyrjeje në "
"{box_name}."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7262,7 +7262,7 @@ msgstr ""
"të ri dhe librash adresash. Smbulon shtim veprimtarish ose kontaktesh, çka "
"duhen bërë duke përdorur një tjetër klient."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9899,14 +9899,12 @@ msgstr "Përpunoni përdoruesin %(username)s"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Hyrja me kyçkalim dështoi: "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Ky aplikacion smbulon diagnostikime"
msgstr "Shfletuesi smbulon kyçkalime."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -9915,28 +9913,28 @@ msgstr "Hyrje"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Hyni me kyçkalim"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Përditësoni ujdisjen"
msgstr "Përditësoni Kyçkalimin"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Shtimi i kyçkalimit dështoi: "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Kyçkalime"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"Puna me kyçkalime lyp përdorim të API-t Javascript të shfletuesit. Ju "
"lutemi, që të vazhdohet, aktivizoni mbulim Javascript-i në shfletuesin tuaj."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -9947,47 +9945,46 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Kyçkalimet janë një mënyrë verifikimi identiteti përdoruesit duke përdorur "
"nënshkrime dixhitale. Janë një alternativë më e siguruar se fjalëkalimet. "
"Informacioni i fshehtë mbahet nga përdoruesi në telefonin e tij, portativin, "
"ose token-in hardware dhe shkyçet duke përdorur një PIN, shenja gishtash ose "
"“face ID”. Sruhet të fshehta te shërbyesi. Shërbyesi njeh vetëm "
"informacionin publik që mund të përdoret për të verifikuar nënshkrime të "
"përdoruesit."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Shtoni fjalëkalim"
msgstr "Shtoni kyçkalim"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Përkatësi"
msgstr "Për Përkatësi"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "U shtua"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Skanuar së fundi më: "
msgstr "Përdorur Së Fundi Më"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "Te llogari përdoruesi, su shtuan kyçkalime."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "Të fshihet kyçkalimi <em class=\"passkey-name\"></em>?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
msgstr "Që ta rishtoni, do tju duhet pajisja e këtij kyçkalimi."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Fshi përdorues"
msgstr "Fshije kyçkalimin"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -10003,6 +10000,7 @@ msgstr "Përpunoni Përdoruesin <em>%(username)s</em>"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Për siguri më të mirë, përdorni <a href=\"%(passkeys_url)s\">kyçkalime</a>."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -10069,20 +10067,16 @@ msgid "Password changed successfully."
msgstr "Fjalëkalimi u ndryshua me sukses."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Ka tashmë një pjesë me këtë emër."
msgstr "Ka tashmë një kyçkalim me atë identifikues."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Përpunoni Përdorues"
msgstr "Përpunoni Kyçkalim"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "Kyçkalimi i përdorur snjihet."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -10251,7 +10245,7 @@ msgstr "IP VPN-je %(box_name)s për shërbime"
#: plinth/modules/wireguard/templates/wireguard.html:50
msgid "Peers"
msgstr ""
msgstr "Ortakë"
#: plinth/modules/wireguard/templates/wireguard.html:52
msgid "Peers allowed to connect to this server:"
@ -10873,10 +10867,8 @@ msgid " System"
msgstr " Sistem"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Administroni Aliase"
msgstr "Administroni kyçkalime"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2022-09-14 17:20+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\n"
"Language-Team: Serbian <https://hosted.weblate.org/projects/freedombox/"
@ -6597,7 +6597,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6606,14 +6606,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-11 22:09+0000\n"
"Last-Translator: Daniel Wiik <d.wiik@outlook.com>\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-04-15 10:10+0000\n"
"Last-Translator: bittin1ddc447d824349b2 <bittin@reimu.nl>\n"
"Language-Team: Swedish <https://hosted.weblate.org/projects/freedombox/"
"freedombox/sv/>\n"
"Language: sv\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.17-dev\n"
"X-Generator: Weblate 5.17.1-dev\n"
#: plinth/config.py:103
#, python-brace-format
@ -7176,7 +7176,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7190,7 +7190,7 @@ msgstr ""
"användarklient</a>. Radikal kan nås av alla användare med en {box_name} "
"inloggning."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7200,7 +7200,7 @@ msgstr ""
"skapandet av nya kalendrar och adressböcker. Det stöder inte att lägga till "
"händelser eller kontakter, som måste göras med hjälp av en separat klient."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9808,14 +9808,12 @@ msgstr "Redigera användare %(username)s"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Inloggning med lösennyckel misslyckades: "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Den här appen stöder inte diagnostik"
msgstr "Webbläsaren stöder inte lösennycklar."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -9824,28 +9822,28 @@ msgstr "Logga in"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Logga in med lösennyckel"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Uppdatera inställningar"
msgstr "Uppdatera lösennyckel"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Att lägga till lösennyckel misslyckades: "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Lösennycklar"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"Att arbeta med lösennycklar kräver att du använder webbläsarens Javascript "
"API. Vänligen aktivera Javascript stöd i din webbläsare för att fortsätta."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -9856,47 +9854,45 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Lösennycklar är sätt att verifiera användarens identitet med hjälp av "
"digitala signaturer. De är ett säkrare alternativ till lösenord. Hemlig "
"information hålls med användaren på sin telefon, bärbar dator eller en "
"hårdvaru token och låst med en PIN, fingeravtryck eller ansikte ID. Inga "
"hemligheter lagras på servern. Servern vet bara den offentliga informationen "
"som kan användas för att verifiera användarens signaturer."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Lägg till lösenord"
msgstr "Lägg till lösennyckel"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Domän"
msgstr "För domän"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "Tillagd"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Senast skannad: "
msgstr "Senast använd"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "Inga lösennycklar har lagts till på användarkontot."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "Radera lösennyckel <em class=\"passkey-name\"></em>?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
msgstr "Du behöver denna lösennyckels enhet för att lägga till den igen."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Radera användare"
msgstr "Radera lösennyckel"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -9912,6 +9908,7 @@ msgstr "Redigera användare <em>%(username)s</em>"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Använd <a href=\"%(passkeys_url)s\">lösennycklar</a> för bättre säkerhet."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -9977,20 +9974,16 @@ msgid "Password changed successfully."
msgstr "Lösenordet har ändrats."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Det finns redan en share med det här namnet."
msgstr "Lösennyckel med den identifieraren finns redan."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Redigera användar"
msgstr "Redigera Lösennyckel"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "Lösennyckel som används är inte känd."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -10777,10 +10770,8 @@ msgid " System"
msgstr " System"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Hantera alias"
msgstr "Hantera lösennycklar"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"
@ -10836,7 +10827,7 @@ msgstr "Mobil"
#: plinth/templates/clients.html:82
msgid "Android APK"
msgstr ""
msgstr "Android APK"
#: plinth/templates/clients.html:91
msgid "Play Store"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-03-02 19:53+0000\n"
"Last-Translator: James Valleroy <jvalleroy@mailbox.org>\n"
"Language-Team: Tamil <https://hosted.weblate.org/projects/freedombox/"
@ -7140,7 +7140,7 @@ msgstr "குவாசெல்ட்ராய்டு"
msgid "IRC"
msgstr "Irc"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7153,7 +7153,7 @@ msgstr ""
"radicale.org/master.html#supported-comients\"> ஆதரிக்கப்பட்ட கிளையன்ட் பயன்பாடு </"
"a> தேவை. {box_name} உள்நுழைவுடன் எந்த பயனரும் ராடிகலை அணுகலாம்."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7163,7 +7163,7 @@ msgstr ""
"முகவரி புத்தகங்களை உருவாக்குவதை மட்டுமே ஆதரிக்கிறது. இது நிகழ்வுகள் அல்லது "
"தொடர்புகளைச் சேர்ப்பதை ஆதரிக்காது, இது ஒரு தனி கிளையண்டைப் பயன்படுத்தி செய்யப்பட வேண்டும்."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "தீவிரமான"

View File

@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: FreedomBox UI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-05-14 17:03+0000\n"
"Last-Translator: Sripath Roy Koganti <sripathroy@swecha.net>\n"
"Language-Team: Telugu <https://hosted.weblate.org/projects/freedombox/"
@ -7011,7 +7011,7 @@ msgstr "క్వాసెల్ డ్రొఇడ్"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7024,7 +7024,7 @@ msgstr ""
"href=\"https://radicale.org/master.html#supported-clients\">మద్దతు ఉన్న క్లయింట్ "
"అప్లికేషన్</a> అవసరం. {box_name} లాగిన్ ఉన్న ఏ యూజర్ అయినా Radicaleని యాక్సెస్ చేయవచ్చు."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7034,7 +7034,7 @@ msgstr ""
"రూపొందించడానికి మాత్రమే మద్దతు ఇస్తుంది. ఈవెంట్‌లు లేదా పరిచయాలను జోడించడానికి ఇది మద్దతు ఇవ్వదు, ఇది "
"ప్రత్యేక క్లయింట్‌ని ఉపయోగించి చేయాలి."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "రాడికేల్"

View File

@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-25 07:09+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-04-09 10:09+0000\n"
"Last-Translator: Burak Yavuz <hitowerdigit@hotmail.com>\n"
"Language-Team: Turkish <https://hosted.weblate.org/projects/freedombox/"
"freedombox/tr/>\n"
@ -7191,7 +7191,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7205,7 +7205,7 @@ msgstr ""
"istemci uygulaması</a> gereklidir. Radicale'ye {box_name} oturum açma adı "
"ile herhangi bir kullanıcı tarafından erişilebilir."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7215,7 +7215,7 @@ msgstr ""
"temel bir web arayüzü sağlar. Ayrı bir istemci kullanılarak yapılması "
"zorunlu olan olayların veya kişilerin eklenmesini desteklemez."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"
@ -9835,14 +9835,12 @@ msgstr "%(username)s kullanıcısını düzenle"
#: plinth/modules/users/templates/users_login.html:30
msgid "Logging in with passkey failed: "
msgstr ""
msgstr "Geçiş anahtarıyla oturum açma başarısız oldu: "
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "Bu uygulama tanılamayı desteklemiyor"
msgstr "Tarayıcı geçiş anahtarlarını desteklemiyor."
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -9851,28 +9849,29 @@ msgstr "Oturum aç"
#: plinth/modules/users/templates/users_login.html:70
#: plinth/modules/users/templates/users_login.html:72
msgid "Log in with passkey"
msgstr ""
msgstr "Geçiş anahtarıyla oturum aç"
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "Ayarlamayı güncelle"
msgstr "Geçiş Anahtarını Güncelle"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
msgstr ""
msgstr "Geçiş anahtarını ekleme başarısız oldu: "
#: plinth/modules/users/templates/users_passkeys.html:48
#: plinth/modules/users/views.py:314
msgid "Passkeys"
msgstr ""
msgstr "Geçiş anahtarları"
#: plinth/modules/users/templates/users_passkeys.html:57
msgid ""
"Working with passkeys requires using browser's Javascript API. Please enable "
"Javascript support in your browser to continue."
msgstr ""
"Geçiş anahtarlarıyla çalışmak, tarayıcının Javascript API'sinin "
"kullanılmasını gerektirir. Devam etmek için lütfen tarayıcınızda Javascript "
"desteğini etkinleştirin."
#: plinth/modules/users/templates/users_passkeys.html:66
msgid ""
@ -9883,47 +9882,46 @@ msgid ""
"server knows only the public information that can used to verify user's "
"signatures."
msgstr ""
"Geçiş anahtarları, dijital imzaları kullanarak kullanıcının kimliğini "
"doğrulamasının bir yoludur. Parolalara göre daha güvenli bir "
"alternatiftirdir. Gizli bilgiler kullanıcının telefonunda, dizüstü "
"bilgisayarında veya donanım belirtecinde saklanır ve PIN, parmak izi veya "
"yüz kimliği kullanılarak kilidi açılır. Sunucuda hiçbir gizli anahtar "
"saklanmaz. Sunucu sadece kullanıcının imzalarını doğrulamak için "
"kullanılabilecek genel bilgileri bilir."
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "Parola ekle"
msgstr "Geçiş anahtarı ekle"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "Etki alanı"
msgstr "Etki Alanı İçin"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
msgstr "Eklenen"
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "Son taranan: "
msgstr "Son Kullanılan"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
msgstr ""
msgstr "Kullanıcı hesabına eklenen geçiş anahtarları yok."
#: plinth/modules/users/templates/users_passkeys.html:135
msgid "Delete passkey <em class=\"passkey-name\"></em>?"
msgstr ""
msgstr "<em class=\"passkey-name\"></em> geçiş anahtarı silinsin mi?"
#: plinth/modules/users/templates/users_passkeys.html:145
msgid "You will need this passkey's device to add it back again."
msgstr ""
msgstr "Tekrar eklemek için bu geçiş anahtarının cihazına ihtiyacınız olacak."
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "Kullanıcıyı sil"
msgstr "Geçiş anahtarını sil"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -9939,6 +9937,8 @@ msgstr "<em>%(username)s</em> Kullanıcısını Düzenleyin"
#, python-format
msgid "Use <a href=\"%(passkeys_url)s\">passkeys</a> for better security."
msgstr ""
"Daha iyi güvenlik için <a href=\"%(passkeys_url)s\">geçiş anahtarlarını</a> "
"kullanın."
#: plinth/modules/users/templates/users_update.html:29
#, python-format
@ -10005,20 +10005,16 @@ msgid "Password changed successfully."
msgstr "Parola başarılı olarak değiştirildi."
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "Bu ada sahip bir paylaşım zaten var."
msgstr "Bu tanımlayıcıya sahip geçiş anahtarı zaten var."
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "Kullanıcıyı Düzenle"
msgstr "Geçiş Anahtarını Düzenle"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
msgstr ""
msgstr "Kullanılan geçiş anahtarı bilinmiyor."
#: plinth/modules/wireguard/__init__.py:20
msgid "WireGuard is a fast, modern, secure VPN tunnel."
@ -10802,10 +10798,8 @@ msgid " System"
msgstr " Sistem"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "Kod Adlarını Yönet"
msgstr "Geçiş anahtarlarını yönet"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-12-17 07:00+0000\n"
"Last-Translator: Максим Горпиніч <gorpinicmaksim0@gmail.com>\n"
"Language-Team: Ukrainian <https://hosted.weblate.org/projects/freedombox/"
@ -7224,7 +7224,7 @@ msgstr "Quasseldroid"
msgid "IRC"
msgstr "IRC"
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -7238,7 +7238,7 @@ msgstr ""
"clients\">підтримувана клієнтська програма</a>. Radicale може отримати "
"доступ будь-який користувач із логіном {box_name}."
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
@ -7248,7 +7248,7 @@ msgstr ""
"календарів та адресних книг. Він не підтримує додавання подій або контактів, "
"для цього необхідно використовувати окремий клієнт."
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr "Radicale"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2021-07-28 08:34+0000\n"
"Last-Translator: bruh <quangtrung02hn16@gmail.com>\n"
"Language-Team: Vietnamese <https://hosted.weblate.org/projects/freedombox/"
@ -6774,7 +6774,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6783,14 +6783,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Plinth\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"PO-Revision-Date: 2026-03-04 05:09+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2026-04-08 07:09+0000\n"
"Last-Translator: 大王叫我来巡山 "
"<hamburger2048@users.noreply.hosted.weblate.org>\n"
"Language-Team: Chinese (Simplified Han script) <https://hosted.weblate.org/"
@ -6542,7 +6542,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6555,14 +6555,14 @@ msgstr ""
"master.html#supported-clients\">支持的客户端应用程序</a>。任何拥有 "
"{box_name} 登录名的用户都可以访问 Radicale。"
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""
@ -8851,10 +8851,8 @@ msgstr ""
#: plinth/modules/users/templates/users_login.html:41
#: plinth/modules/users/templates/users_passkeys.html:41
#, fuzzy
#| msgid "This app does not support diagnostics"
msgid "Browser does not support passkeys."
msgstr "此应用程序不支持诊断"
msgstr "浏览器不支持 passkeys。"
#: plinth/modules/users/templates/users_login.html:57
msgid "Login"
@ -8866,10 +8864,8 @@ msgid "Log in with passkey"
msgstr ""
#: plinth/modules/users/templates/users_passkey_edit.html:19
#, fuzzy
#| msgid "Update setup"
msgid "Update Passkey"
msgstr "更新安装程序"
msgstr "更新 Passkey"
#: plinth/modules/users/templates/users_passkeys.html:30
msgid "Adding passkey failed: "
@ -8898,26 +8894,20 @@ msgstr ""
#: plinth/modules/users/templates/users_passkeys.html:83
#: plinth/modules/users/templates/users_passkeys.html:85
#, fuzzy
#| msgid "Add password"
msgid "Add passkey"
msgstr "添加密码"
msgstr "添加 passkey"
#: plinth/modules/users/templates/users_passkeys.html:93
#, fuzzy
#| msgid "Domain"
msgid "For Domain"
msgstr "域名"
msgstr "针对域名"
#: plinth/modules/users/templates/users_passkeys.html:94
msgid "Added"
msgstr ""
#: plinth/modules/users/templates/users_passkeys.html:95
#, fuzzy
#| msgid "Last scanned: "
msgid "Last Used"
msgstr "上次扫描: "
msgstr "上次使用"
#: plinth/modules/users/templates/users_passkeys.html:126
msgid "No passkeys added to user account."
@ -8932,10 +8922,8 @@ msgid "You will need this passkey's device to add it back again."
msgstr ""
#: plinth/modules/users/templates/users_passkeys.html:152
#, fuzzy
#| msgid "Delete user"
msgid "Delete passkey"
msgstr "删除用户"
msgstr "删除 passkey"
#: plinth/modules/users/templates/users_passkeys.html:155
#: plinth/modules/users/templates/users_update.html:72
@ -9011,16 +8999,12 @@ msgid "Password changed successfully."
msgstr "已成功更改密码。"
#: plinth/modules/users/views.py:420
#, fuzzy
#| msgid "A share with this name already exists."
msgid "Passkey with that identifier already exists."
msgstr "已存在同名共享。"
msgstr "已存在该标识符的 Passkey。"
#: plinth/modules/users/views.py:431
#, fuzzy
#| msgid "Edit User"
msgid "Edit Passkey"
msgstr "编辑用户"
msgstr "编辑 Passkey"
#: plinth/modules/users/views.py:518 plinth/modules/users/views.py:542
msgid "Passkey used is not known."
@ -9736,10 +9720,8 @@ msgid " System"
msgstr " 系统"
#: plinth/templates/base.html:179 plinth/templates/base.html:180
#, fuzzy
#| msgid "Manage Aliases"
msgid "Manage passkeys"
msgstr "管理别名"
msgstr "管理 passkeys"
#: plinth/templates/base.html:186 plinth/templates/base.html:187
msgid "Change password"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-07 00:12+0000\n"
"POT-Creation-Date: 2026-04-28 22:01+0000\n"
"PO-Revision-Date: 2025-02-07 12:01+0000\n"
"Last-Translator: pesder <j_h_liau@yahoo.com.tw>\n"
"Language-Team: Chinese (Traditional Han script) <https://hosted.weblate.org/"
@ -6674,7 +6674,7 @@ msgstr ""
msgid "IRC"
msgstr ""
#: plinth/modules/radicale/__init__.py:27
#: plinth/modules/radicale/__init__.py:26
#, python-brace-format
msgid ""
"Radicale is a CalDAV and CardDAV server. It allows synchronization and "
@ -6683,14 +6683,14 @@ msgid ""
"a> is needed. Radicale can be accessed by any user with a {box_name} login."
msgstr ""
#: plinth/modules/radicale/__init__.py:33
#: plinth/modules/radicale/__init__.py:32
msgid ""
"Radicale provides a basic web interface, which only supports creating new "
"calendars and addressbooks. It does not support adding events or contacts, "
"which must be done using a separate client."
msgstr ""
#: plinth/modules/radicale/__init__.py:55
#: plinth/modules/radicale/__init__.py:52
#: plinth/modules/radicale/manifest.py:74
msgid "Radicale"
msgstr ""

View File

@ -10,6 +10,4 @@ from plinth.modules.api import views
urlpatterns = [
re_path(r'^api/(?P<version>[0-9]+)/shortcuts/?$', public(views.shortcuts)),
re_path(r'^api/(?P<version>[0-9]+)/access-info/?$',
public(views.access_info)),
]

View File

@ -7,22 +7,10 @@ import copy
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpRequest, HttpResponse
from django.http import HttpResponse
from django.templatetags.static import static
from plinth import frontpage
from plinth.modules import names
def access_info(request: HttpRequest, **kwargs) -> HttpResponse:
"""API view to return a list of domains and types."""
domains = [{
'domain': domain.name,
'type': domain.domain_type.component_id
} for domain in names.components.DomainName.list()]
response = {'domains': domains}
return HttpResponse(json.dumps(response), content_type='application/json')
def shortcuts(request, **kwargs):

View File

@ -91,8 +91,6 @@ def setup(domain_name: str):
# Migrate from old bepasty:bepasty ownership to root:root
shutil.chown(CONF_FILE, user='root', group='root')
action_utils.run(['deluser', 'bepasty'], check=False)
action_utils.run(['delgroup', 'bepasty'], check=False)
@privileged
@ -151,6 +149,6 @@ def _generate_password():
@privileged
def uninstall():
"""Remove bepasty user, group and data."""
"""Remove data and configuration file."""
shutil.rmtree(DATA_DIR, ignore_errors=True)
CONF_FILE.unlink(missing_ok=True)

View File

@ -0,0 +1,6 @@
# Create system user and group to run infinoted as system daemon (as opposed to
# running in particular user's account). The user is created at boot or when
# 'systemd-sysusers freedombox-infinoted.conf' is run. To override this file as
# system administrator, create a file /etc/sysusers.d/freedombox-infinoted.conf
# or link it to /dev/null.
u! infinoted - "Infinoted collaborative editing server" /var/lib/infinoted

View File

@ -1,10 +1,8 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configure infinoted."""
import grp
import os
import pathlib
import pwd
import shutil
import subprocess
import time
@ -126,21 +124,8 @@ def setup():
action_utils.service_daemon_reload()
# Create infinoted group if needed.
try:
grp.getgrnam('infinoted')
except KeyError:
action_utils.run(['addgroup', '--system', 'infinoted'], check=True)
# Create infinoted user if needed.
try:
pwd.getpwnam('infinoted')
except KeyError:
action_utils.run([
'adduser', '--system', '--ingroup', 'infinoted', '--home',
DATA_DIR, '--gecos', 'Infinoted collaborative editing server',
'infinoted'
], check=True)
# Create an 'infinoted' system user and group, if needed.
action_utils.run(['systemd-sysusers', 'freedombox-infinoted.conf'])
for directory in (DATA_DIR, KEY_DIR, SYNC_DIR):
if not os.path.exists(directory):

View File

@ -5,7 +5,6 @@ FreedomBox app for radicale.
import logging
import augeas
from django.utils.translation import gettext_lazy as _
from plinth import app as app_module
@ -37,15 +36,13 @@ _description = [
logger = logging.getLogger(__name__)
CONFIG_FILE = '/etc/radicale/config'
class RadicaleApp(app_module.App):
"""FreedomBox app for Radicale."""
app_id = 'radicale'
_version = 5
_version = 6
def __init__(self) -> None:
"""Create components for the app."""
@ -129,7 +126,7 @@ class RadicaleApp(app_module.App):
if Version(package['new_version']) > Version('4~'):
return False
rights = get_rights_value()
rights = privileged.get_rights_value()
install(['radicale'], force_configuration='new')
privileged.setup()
privileged.configure(rights)
@ -140,28 +137,3 @@ class RadicaleApp(app_module.App):
"""De-configure and uninstall the app."""
super().uninstall()
privileged.uninstall()
def load_augeas():
"""Prepares the augeas."""
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
# INI file lens
aug.set('/augeas/load/Puppet/lens', 'Puppet.lns')
aug.set('/augeas/load/Puppet/incl[last() + 1]', CONFIG_FILE)
aug.load()
return aug
def get_rights_value():
"""Returns the current Rights value."""
aug = load_augeas()
value = aug.get('/files' + CONFIG_FILE + '/rights/type')
if value == 'from_file':
# Default rights file is equivalent to owner_only.
value = 'owner_only'
return value

View File

@ -0,0 +1,45 @@
(* Radicale module for Augeas
Based on Puppet lens.
Manage config file for http://radicale.org/
/etc/radicale/config is a standard INI File.
*)
module Radicale =
autoload xfm
(************************************************************************
* INI File settings
*
* /etc/radicale/config only supports "#" as commentary and "=" as separator
*************************************************************************)
let comment = IniFile.comment "#" "#"
let comment_re = /[#]/
let sep = IniFile.sep "=" "="
(************************************************************************
* ENTRY
* /etc/radicale/config uses standard INI File entries
*************************************************************************)
let entry = IniFile.entry_generic (Util.indent . key IniFile.entry_re) sep comment_re comment
(************************************************************************
* RECORD
* /etc/radicale/config uses standard INI File records
*************************************************************************)
let title = IniFile.indented_title IniFile.record_re
let record = IniFile.record title entry
(************************************************************************
* LENS & FILTER
* /etc/radicale/config uses standard INI File records
*************************************************************************)
let lns = IniFile.lns record comment
let filter = (incl "/etc/radicale/config")
let xfm = transform lns filter

View File

@ -0,0 +1,81 @@
module Test_radicale =
let conf = "
[server]
[encoding]
[well-known]
[auth]
[git]
[rights]
[storage]
[logging]
[headers]
Content-Security-Policy = default-src 'self'; object-src 'none'
"
test Radicale.lns get conf =
{}
{ "server"
{} }
{ "encoding"
{} }
{ "well-known"
{} }
{ "auth"
{} }
{ "git"
{} }
{ "rights"
{} }
{ "storage"
{} }
{ "logging"
{} }
{ "headers"
{ "Content-Security-Policy" = "default-src 'self'; object-src 'none'" }
{} }
test Radicale.lns put conf after
set "server/hosts" "127.0.0.1:5232, [::1]:5232";
set "server/base_prefix" "/radicale/";
set "well-known/caldav" "/radicale/%(user)s/caldav/";
set "well-known/cardav" "/radicale/%(user)s/carddav/";
set "auth/type" "remote_user";
set "rights/type" "owner_only";
set "headers/Content-Security-Policy" "default-src 'self'; object-src 'none'"
= "
[server]
hosts=127.0.0.1:5232, [::1]:5232
base_prefix=/radicale/
[encoding]
[well-known]
caldav=/radicale/%(user)s/caldav/
cardav=/radicale/%(user)s/carddav/
[auth]
type=remote_user
[git]
[rights]
type=owner_only
[storage]
[logging]
[headers]
Content-Security-Policy = default-src 'self'; object-src 'none'
"

View File

@ -24,6 +24,7 @@ def setup():
"""
aug = load_augeas()
aug.set('auth/type', 'remote_user')
aug.set('auth/lc_username', 'True')
aug.save()
# Service is started again by socket.
action_utils.service_stop(SERVICE_NAME)
@ -55,12 +56,24 @@ def load_augeas():
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
# INI file lens
aug.transform('Puppet', CONFIG_FILE)
aug.transform('Radicale', CONFIG_FILE)
aug.set('/augeas/context', '/files' + CONFIG_FILE)
aug.load()
return aug
def get_rights_value():
"""Returns the current Rights value."""
aug = load_augeas()
value = aug.get('rights/type')
if value == 'from_file':
# Default rights file is equivalent to owner_only.
value = 'owner_only'
return value
@privileged
def uninstall():
"""Remove all radicale collections."""

View File

@ -83,21 +83,21 @@ def _set_access_rights(browser, access_rights_type):
def _calendar_is_available(browser):
"""Return whether calendar is available at well-known URL."""
conf = functional.config['DEFAULT']
url = functional.base_url + '/.well-known/caldav'
logging.captureWarnings(True)
request = requests.get(url, auth=(conf['username'], conf['password']),
verify=False)
logging.captureWarnings(False)
return request.status_code != 404
return _well_known_available(browser, '/.well-known/caldav')
def _addressbook_is_available(browser):
"""Return whether addressbook is available at well-known URL."""
return _well_known_available(browser, '/.well-known/carddav')
def _well_known_available(browser, wellknown_url: str):
"""Return whether a well-known URL redirects to radicale."""
conf = functional.config['DEFAULT']
url = functional.base_url + '/.well-known/carddav'
url = functional.base_url + wellknown_url
logging.captureWarnings(True)
request = requests.get(url, auth=(conf['username'], conf['password']),
verify=False)
verify=False, allow_redirects=False)
logging.captureWarnings(False)
return request.status_code != 404
return (request.status_code == 301
and request.headers['Location'].endswith('/radicale/'))

View File

@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _
from plinth.views import AppView
from . import get_rights_value, privileged
from . import privileged
from .forms import RadicaleForm
@ -20,13 +20,13 @@ class RadicaleAppView(AppView):
def get_initial(self):
"""Return the values to fill in the form."""
initial = super().get_initial()
initial['access_rights'] = get_rights_value()
initial['access_rights'] = privileged.get_rights_value()
return initial
def form_valid(self, form):
"""Change the access control of Radicale service."""
data = form.cleaned_data
if get_rights_value() != data['access_rights']:
if privileged.get_rights_value() != data['access_rights']:
privileged.configure(data['access_rights'])
messages.success(self.request,
_('Access rights configuration updated'))

View File

@ -0,0 +1,6 @@
# Create system user and group to run syncthing as system daemon (as opposed to
# running in particular user's account). The user is created at boot or when
# 'systemd-sysusers freedombox-syncthing.conf' is run. To override this file as
# system administrator, create a file /etc/sysusers.d/freedombox-syncthing.conf
# or link it to /dev/null.
u! syncthing - "Syncthing file synchronization server" /var/lib/syncthing

View File

@ -1,9 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configure Syncthing."""
import grp
import os
import pwd
import shutil
import time
@ -32,21 +30,8 @@ def augeas_load(conf_file):
@privileged
def setup():
"""Perform post-install actions for Syncthing."""
# Create syncthing group if needed.
try:
grp.getgrnam('syncthing')
except KeyError:
action_utils.run(['addgroup', '--system', 'syncthing'], check=True)
# Create syncthing user if needed.
try:
pwd.getpwnam('syncthing')
except KeyError:
action_utils.run([
'adduser', '--system', '--ingroup', 'syncthing', '--home',
DATA_DIR, '--gecos', 'Syncthing file synchronization server',
'syncthing'
], check=True)
# Create a 'syncthing' system user and group, if needed.
action_utils.run(['systemd-sysusers', 'freedombox-syncthing.conf'])
if not os.path.exists(DATA_DIR):
os.makedirs(DATA_DIR, mode=0o750)

View File

@ -37,7 +37,7 @@ class WireguardApp(app_module.App):
app_id = 'wireguard'
_version = 1
_version = 2
def __init__(self) -> None:
"""Create components for the app."""
@ -64,7 +64,8 @@ class WireguardApp(app_module.App):
clients=info.clients)
self.add(shortcut)
packages = Packages('packages-wireguard', ['wireguard'])
packages = Packages('packages-wireguard',
['wireguard', 'python3-segno'])
self.add(packages)
firewall = Firewall('firewall-wireguard', info.name,

View File

@ -55,14 +55,12 @@ def validate_endpoint(endpoint):
raise ValidationError('Invalid endpoint.')
def validate_ipv4_address_with_network(value: str):
"""Check that value is a valid IPv4 address with an optional network."""
def validate_ip_address_with_network(value: str):
"""Check that value is a valid IP address with an optional network."""
try:
ipaddress.IPv4Interface(value)
except ipaddress.AddressValueError:
raise ValidationError(_('Enter a valid IPv4 address.'))
except ipaddress.NetmaskValueError:
raise ValidationError(_('Enter a valid network prefix or net mask.'))
ipaddress.ip_interface(value)
except ValueError:
raise ValidationError(_('Not a valid IP address.'))
class AddClientForm(forms.Form):
@ -74,6 +72,11 @@ class AddClientForm(forms.Form):
validators=[validate_key])
class AutoAddClientForm(forms.Form):
"""Empty form for auto-client addition UX."""
pass
class AddServerForm(forms.Form):
"""Form to add server."""
peer_endpoint = forms.CharField(
@ -93,10 +96,11 @@ class AddServerForm(forms.Form):
help_text=_(
'IP address assigned to this machine on the VPN after connecting '
'to the endpoint. This value is usually provided by the server '
'operator. Example: 192.168.0.10. You can also specify the '
'network. This will allow reaching machines in the network. '
'operator. Example: 192.168.0.10 or '
'2a03:7c80:4b2c:91a2:5d41:ffee:9b82:7c17. You can also specify '
'the network. This will allow reaching machines in the network. '
'Examples: 10.68.12.43/24 or 10.68.12.43/255.255.255.0.'),
validators=[validate_ipv4_address_with_network])
validators=[validate_ip_address_with_network])
private_key = forms.CharField(
label=_('Private key of this machine'), strip=True, help_text=_(
@ -120,31 +124,44 @@ class AddServerForm(forms.Form):
'Typically checked for a VPN service through which all traffic '
'is sent.'))
def _build_ipv4_settings(self, iface) -> dict:
"""Build IPv4 NM settings from interfaces."""
return {
'method': 'manual',
'address': str(iface.ip),
'netmask': str(iface.netmask),
'gateway': '',
'dns': '',
'second_dns': '',
}
def _build_ipv6_settings(self, iface) -> dict:
"""Build IPv6 NM settings from interfaces."""
return {
'method': 'manual',
'address': str(iface.ip),
'prefix': iface.network.prefixlen,
'gateway': '',
'dns': '',
'second_dns': '',
}
def get_settings(self) -> dict[str, dict]:
"""Return NM settings dict from cleaned data."""
ip_address_and_network = self.cleaned_data['ip_address_and_network']
ip_address_and_network = ipaddress.IPv4Interface(
ip_address_and_network)
ip_address = str(ip_address_and_network.ip)
prefixlen = ip_address_and_network.network.prefixlen
ip_interface = ipaddress.ip_interface(
self.cleaned_data['ip_address_and_network']
)
if self.cleaned_data['default_route']:
allowed_ips = ['0.0.0.0/0', '::/0']
else:
allowed_ips = [f'{ip_address}/{prefixlen}']
allowed_ips = [str(ip_interface)]
settings = {
'common': {
'type': 'wireguard',
'zone': 'external',
},
'ipv4': {
'method': 'manual',
'address': ip_address,
'netmask': str(ip_address_and_network.netmask),
'gateway': '',
'dns': '',
'second_dns': '',
},
'wireguard': {
'peer_endpoint': self.cleaned_data['peer_endpoint'],
'peer_public_key': self.cleaned_data['peer_public_key'],
@ -153,4 +170,10 @@ class AddServerForm(forms.Form):
'allowed_ips': allowed_ips,
}
}
if ip_interface.version == 4:
settings['ipv4'] = self._build_ipv4_settings(ip_interface)
else:
settings['ipv6'] = self._build_ipv6_settings(ip_interface)
return settings

View File

@ -85,6 +85,12 @@
</div>
<div class="btn-toolbar">
<a title="{% trans 'Auto add a new peer' %}"
role="button" class="btn btn-primary btn-auto-add-client"
href="{% url 'wireguard:auto-add-client' %}">
{% icon 'magic' %}
{% trans "Add Client Automatically" %}
</a>
<a title="{% trans 'Add a new peer' %}"
role="button" class="btn btn-default btn-add-client"
href="{% url 'wireguard:add-client' %}">

View File

@ -0,0 +1,77 @@
{% extends "base.html" %}
{% comment %}
# SPDX-License-Identifier: AGPL-3.0-or-later
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% load extras %}
{% block content %}
<h3>{{ title }}</h3>
<form class="form form-auto-add-client" method="post">
{% csrf_token %}
{{ form|bootstrap }}
{% if client_privkey %}
<div class="form-group">
<table class="table table-sm">
<tr><td>{% trans "IP Address" %}</td><td>{{ next_ip }}</td></tr>
<tr><td>{% trans "Endpoint" %}</td><td>{{ endpoint }}</td></tr>
<tr><td>{% trans "Public Key" %}</td>
<td class="pubkey-val">{{ client_pubkey }}</td></tr>
<tr><td>{% trans "Private Key" %}</td>
<td>
<details class="privkey-val">
<summary>{% trans "Click to reveal" %}</summary>
{{ client_privkey }}
</details>
</td></tr>
</table>
<div class="alert alert-warning">
<strong>{% trans "Important:" %}</strong>
{% trans "Save the private key now. This page shows it only once!" %}
</div>
{% endif %}
<h4>{% trans "Client configuration file" %}</h4>
<p>
{% blocktrans trimmed %}
Download the configuration file (.conf) for manual import,
or use the QR code to import directly from your WireGuard mobile app.
{% endblocktrans %}
</p>
<p class="alert alert-warning">
<strong>{% trans "Warning:" %}</strong>
{% blocktrans trimmed %}
Treat this QR code like a password.
Anyone who scans it can gain VPN access if the connection is enabled.
{% endblocktrans %}
</p>
<div class="form-group">
<div class="btn-toolbar">
<a href="{% url 'wireguard:auto-add-client-download' %}"
class="btn btn-default">
{% icon 'download' %}
{% trans "Download config file" %}
</a>
<a href="{% url 'wireguard:auto-add-client-qr' %}"
class="btn btn-default" target="_blank">
{% icon 'qrcode' %}
{% trans "Show QR Code" %}
</a>
</div>
<div class="form-actions">
<input type="submit" class="btn btn-primary btn-auto-add-connection"
value="{% trans "Add Connection" %}"/>
<a href="{% url 'wireguard:index' %}" class="btn btn-secondary">
{% trans "Cancel" %}
</a>
</div>
</form>
{% endblock %}

View File

@ -7,7 +7,7 @@ import pytest
from django.core.exceptions import ValidationError
from plinth.modules.wireguard.forms import (validate_endpoint,
validate_ipv4_address_with_network,
validate_ip_address_with_network,
validate_key)
@ -73,22 +73,33 @@ def test_validate_endpoint_invalid_patterns(endpoint):
'1.2.3.4/24',
'1.2.3.4/255.255.255.0',
'1.2.3.4/0.0.0.255',
'::1',
'2001:db8::1',
'2001:db8::1/64',
'fe80::1/64',
'::/0',
])
def test_validate_ipv4_address_with_network_valid_patterns(value):
def test_validate_ip_address_with_network_valid_patterns(value):
"""Test validating IPv4 address with network works for valid values."""
validate_ipv4_address_with_network(value)
validate_ip_address_with_network(value)
@pytest.mark.parametrize('value', [
'::1',
'1.2.3.4/',
'invalid-ip/24',
'1.2.3.4/x',
'1.2.3.4/-1',
'1.2.3.4/33',
'1.2.3.4/9.8.7.6',
'2001:db8::1/',
'2001:db8::1/129',
'2001:db8::1/x',
'2001:db8::1/-1',
'2001:db8::1/255.255.255.0',
'2001:db8::1::1',
'12345::1',
])
def test_validate_ipv4_address_with_network_invalid_patterns(value):
def test_validate_ip_address_with_network_invalid_patterns(value):
"""Test validating IPv4 address with network works for invalid values."""
with pytest.raises(ValidationError):
validate_ipv4_address_with_network(value)
validate_ip_address_with_network(value)

View File

@ -113,6 +113,53 @@ class TestWireguardApp(functional.BaseAppTests):
assert not self._client_exists(session_browser,
self._client_public_key2)
def test_auto_add_client(self, session_browser):
"""Test the automatic client generation and addition flow."""
functional.nav_to_module(session_browser, 'wireguard')
# Start server if needed (reuse existing logic)
# Extract to reusable method
start_server_button = session_browser.find_by_css('.btn-start-server')
if start_server_button:
with functional.wait_for_page_update(session_browser):
start_server_button.first.click()
session_browser.find_by_css('.btn-auto-add-client').first.click()
client_pubkey = session_browser.find_by_css(
'.pubkey-val').first.text.strip()
# Verify private key reveal
privkey_reveal = session_browser.find_by_css(
'.privkey-val')
assert privkey_reveal, "Private key reveal should be present"
privkey_reveal.click()
client_privkey = session_browser.find_by_css(
'.privkey-val').text.splitlines()[1]
assert len(client_privkey) == 44, (("Private key should be base64 "
"(44 chars)"))
# Verify config download and QR links
download_link = session_browser.links.find_by_href(
'/freedombox/apps/wireguard/client/auto-add/action/download/')
qr_link = session_browser.links.find_by_href(
'/freedombox/apps/wireguard/client/auto-add/action/qr/')
assert download_link, "Download config link should exist"
assert qr_link, "QR code link should exist"
# Submit to add the client
with functional.wait_for_page_update(session_browser):
session_browser.find_by_css(
'.btn-auto-add-connection').first.click()
# Verify client was added successfully
assert self._client_exists(session_browser, client_pubkey), ((
"Auto-generated client should exist"))
# Clean up
self._delete_client(session_browser, client_pubkey)
assert not self._client_exists(session_browser, client_pubkey)
@staticmethod
def _get_server_href(browser, key):
"""Return the href for server show page."""

View File

@ -13,6 +13,14 @@ urlpatterns = [
views.EnableServerView.as_view(), name='enable-server'),
re_path(r'^apps/wireguard/client/add/$', views.AddClientView.as_view(),
name='add-client'),
re_path(r'^apps/wireguard/client/auto-add/$',
views.AutoAddClientView.as_view(), name='auto-add-client'),
re_path(r'^apps/wireguard/client/auto-add/action/download/$',
views.ClientActionsView.as_view(action='download'),
name='auto-add-client-download'),
re_path(r'^apps/wireguard/client/auto-add/action/qr/$',
views.ClientActionsView.as_view(action='qr'),
name='auto-add-client-qr'),
re_path(r'^apps/wireguard/client/(?P<public_key>[^/]+)/show/$',
views.ShowClientView.as_view(), name='show-client'),
re_path(r'^apps/wireguard/client/(?P<public_key>[^/]+)/edit/$',

View File

@ -19,6 +19,18 @@ IP_TEMPLATE = '10.84.0.{}'
logger = logging.getLogger(__name__)
def _get_nm_address_info(settings_ipv4, settings_ipv6) -> tuple[str, str]:
"""Extract IP address info from NM IPv4/IPv6 settings."""
for settings in [settings_ipv4, settings_ipv6]:
if settings and settings.get_num_addresses():
nm_address = settings.get_address(0)
address = nm_address.get_address()
prefix = str(nm_address.get_prefix())
return address, address + '/' + prefix
return '', ''
def get_nm_info():
"""Get information from network manager."""
setting_name = nm.SETTING_WIREGUARD_SETTING_NAME
@ -64,12 +76,14 @@ def get_nm_info():
info['default_route'] = True
settings_ipv4 = connection.get_setting_ip4_config()
if settings_ipv4 and settings_ipv4.get_num_addresses():
nm_address = settings_ipv4.get_address(0)
address = nm_address.get_address()
prefix = str(nm_address.get_prefix())
info['ip_address'] = address
info['ip_address_and_network'] = address + '/' + prefix
settings_ipv6 = connection.get_setting_ip6_config()
ip_address, ip_address_and_network = _get_nm_address_info(
settings_ipv4, settings_ipv6
)
info['ip_address'] = ip_address
info['ip_address_and_network'] = ip_address_and_network
connections[info['interface']] = info
@ -160,6 +174,13 @@ def _generate_private_key():
return process.stdout.decode().strip()
def generate_client_keypair():
"""Generate client private/public keypair."""
private_key = _generate_private_key()
public_key = _get_public_key_from_private_key(private_key)
return private_key, public_key
def _find_next_interface():
"""Find next unused wireguard interface name."""
output = subprocess.check_output(['wg', 'show',
@ -303,3 +324,25 @@ def remove_client(public_key):
settings.remove_peer(peer_index)
connection.commit_changes(True)
network.reactivate_connection(connection.get_uuid())
def build_client_config(client_ip: str, client_privkey: str,
client_pubkey: str, endpoint: str) -> str:
"""Generate WireGuard client config."""
info = get_info()
server_info = info['my_server']
if not server_info:
raise RuntimeError("WireGuard server not configured")
server_pubkey = server_info['public_key']
return f"""[Interface]
PrivateKey = {client_privkey}
Address = {client_ip}/32
DNS = 10.84.0.1
[Peer]
PublicKey = {server_pubkey}
AllowedIPs = 0.0.0.0/0
Endpoint = {endpoint}
PersistentKeepalive = 25"""

View File

@ -3,16 +3,19 @@
Views for WireGuard application.
"""
import segno
import urllib.parse
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.http import Http404
from django.http import Http404, HttpResponse
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import FormView, TemplateView, View
from io import BytesIO
from plinth import network
from plinth.modules.names.components import DomainName
from plinth.views import AppView
@ -82,6 +85,126 @@ class AddClientView(SuccessMessageMixin, FormView):
return super().form_valid(form)
class SessionClientDataMixin:
"""Shared session data loading for auto-client views."""
def get_session_client_data(self, request):
"""Extract client data from session."""
next_ip = request.session.get('next_ip')
pubkey = request.session.get('client_pubkey')
privkey = request.session.get('client_privkey')
endpoint = request.session.get('endpoint')
if not all([next_ip, privkey, pubkey, endpoint]):
raise Http404("Session expired")
return {
'next_ip': next_ip,
'privkey': privkey,
'pubkey': pubkey,
'endpoint': endpoint
}
def get_client_config(self, request):
"""Rebuild client config from session."""
data = self.get_session_client_data(request)
return utils.build_client_config(
data['next_ip'], data['privkey'],
data['pubkey'], data['endpoint']
)
class ClientActionsView(SessionClientDataMixin, View):
action = None
def get(self, request):
if self.action == 'download':
config = self.get_client_config(request)
response = HttpResponse(config, content_type='text/plain')
response['Content-Disposition'] = \
'attachment; filename="wg-client.conf"'
return response
elif self.action == 'qr':
qrcode = segno.make(config)
buffer = BytesIO()
qrcode.save(buffer, kind='svg', scale=5)
return HttpResponse(buffer.getvalue(),
content_type='image/svg+xml')
raise Http404("Invalid action")
class AutoAddClientView(SuccessMessageMixin, FormView):
"""View to add a client with keypair generation."""
form_class = forms.AutoAddClientForm
template_name = 'wireguard_auto_add_client.html'
success_url = reverse_lazy('wireguard:index')
success_message = _('Added new client.')
def get_context_data(self, **kwargs):
"""Return additional context for rendering the template."""
context = super().get_context_data(**kwargs)
context['title'] = _('Add Allowed Client')
context['domains'] = []
info = utils.get_info()
server_info = info['my_server']
if server_info:
domains = DomainName.list_names(filter_for_service='wireguard')
filtered_domains = [
domain for domain in domains if not domain.endswith('.local')
]
port = server_info.get('listen_port', 51820)
endpoint = f"{filtered_domains[0]}:{port}"
try:
client_privkey, client_pubkey = utils.generate_client_keypair()
# Get next IP
connection = utils._server_connection()
setting_name = utils.nm.SETTING_WIREGUARD_SETTING_NAME
settings = connection.get_setting_by_name(setting_name)
next_ip = utils._get_next_available_ip_address(settings)
data = {
'next_ip': next_ip,
'client_privkey': client_privkey,
'client_pubkey': client_pubkey,
'endpoint': endpoint
}
# Add properties to template context
context['domains'] = filtered_domains
context.update(data)
# Store info on instance for reuse
self.request.session.update(data)
except Exception as e:
messages.warning(f"Client key generation failed: {e}")
pass
return context
def form_valid(self, form):
"""Add client using generated public key."""
try:
client_pubkey = self.request.session.pop('client_pubkey')
utils.add_client(client_pubkey)
except KeyError:
messages.warning(self.request,
_('Session expired. Please try again.'))
return redirect('wireguard:auto-add-client')
except ValueError:
messages.warning(self.request, _('Client already exists'))
return redirect('wireguard:index')
return super().form_valid(form)
class ShowClientView(SuccessMessageMixin, TemplateView):
"""View to show a client's details."""
template_name = 'wireguard_show_client.html'

View File

@ -20,7 +20,8 @@ from plinth.action_utils import (get_addresses, get_hostname,
service_try_reload_or_restart,
service_try_restart, service_unmask, umask)
UNKNOWN = 'unknowndeamon'
UNKNOWN = 'unknowndeamon.service'
UNKNOWN_SOCKET = 'unknowndeamon.socket'
systemctl_path = pathlib.Path('/usr/bin/systemctl')
systemd_installed = pytest.mark.skipif(not systemctl_path.exists(),
@ -76,20 +77,101 @@ def test_service_enable_and_disable():
@patch('plinth.action_utils.service_action')
@systemd_installed
def test_service_actions(mock):
"""Trivial white box test for trivial implementations."""
def test_service_start(mock):
"""Test staring a service."""
service_start(UNKNOWN)
mock.assert_called_with(UNKNOWN, 'start', check=False)
mock.mock_calls = [call(UNKNOWN, 'start', check=False)]
mock.reset_mock()
service_start(UNKNOWN, check=True)
mock.mock_calls = [call(UNKNOWN, 'start', check=True)]
@patch('plinth.action_utils.service_action')
@systemd_installed
def test_service_stop(mock):
"""Test stopping a service."""
service_stop(UNKNOWN)
mock.assert_called_with(UNKNOWN, 'stop', check=False)
assert mock.mock_calls == [call(UNKNOWN, 'stop', check=False)]
mock.reset_mock()
service_stop(UNKNOWN, check=True)
mock.mock_calls = [call(UNKNOWN, 'stop', check=True)]
mock.reset_mock()
service_stop(UNKNOWN_SOCKET)
assert mock.mock_calls == [
call(UNKNOWN_SOCKET, 'stop', check=False),
call(UNKNOWN, 'stop', check=False)
]
@patch('plinth.action_utils.service_action')
@systemd_installed
def test_service_restart(mock):
"""Test restaring a service."""
service_restart(UNKNOWN)
mock.assert_called_with(UNKNOWN, 'restart', check=False)
assert mock.mock_calls == [call(UNKNOWN, 'restart', check=False)]
mock.reset_mock()
service_restart(UNKNOWN, check=True)
mock.mock_calls = [call(UNKNOWN, 'restart', check=True)]
mock.reset_mock()
service_restart(UNKNOWN_SOCKET)
assert mock.mock_calls == [call(UNKNOWN, 'stop', check=False)]
@patch('plinth.action_utils.service_action')
@systemd_installed
def test_service_try_restart(mock):
"""Test try-restaring a service."""
service_try_restart(UNKNOWN)
mock.assert_called_with(UNKNOWN, 'try-restart', check=False)
assert mock.mock_calls == [call(UNKNOWN, 'try-restart', check=False)]
mock.reset_mock()
service_try_restart(UNKNOWN, check=True)
mock.mock_calls = [call(UNKNOWN, 'try-restart', check=True)]
mock.reset_mock()
service_restart(UNKNOWN_SOCKET)
assert mock.mock_calls == [call(UNKNOWN, 'stop', check=False)]
@patch('plinth.action_utils.service_action')
@systemd_installed
def test_service_reload(mock):
"""Test reloading a service."""
service_reload(UNKNOWN)
mock.assert_called_with(UNKNOWN, 'reload', check=False)
assert mock.mock_calls == [call(UNKNOWN, 'reload', check=False)]
mock.reset_mock()
service_reload(UNKNOWN, check=True)
mock.mock_calls = [call(UNKNOWN, 'reload', check=True)]
mock.reset_mock()
service_reload(UNKNOWN_SOCKET)
assert mock.mock_calls == [call(UNKNOWN, 'reload', check=False)]
@patch('plinth.action_utils.service_action')
@systemd_installed
def test_service_try_reload_or_restart(mock):
"""Test try-reload-or-restart on a service."""
service_try_reload_or_restart(UNKNOWN)
mock.assert_called_with(UNKNOWN, 'try-reload-or-restart', check=False)
assert mock.mock_calls == [
call(UNKNOWN, 'try-reload-or-restart', check=False)
]
mock.reset_mock()
service_try_reload_or_restart(UNKNOWN, check=True)
mock.mock_calls = [call(UNKNOWN, 'try-reload-or-restart', check=True)]
mock.reset_mock()
service_try_reload_or_restart(UNKNOWN_SOCKET)
assert mock.mock_calls == [
call(UNKNOWN, 'try-reload-or-restart', check=False)
]
@pytest.mark.usefixtures('needs_root')

View File

@ -178,6 +178,7 @@ module = [
"plinth.tests.config_local",
"pytest_splinter.*",
"ruamel.*",
"segno.*",
"selenium.*",
"splinter.*",
"stronghold.*",

View File

@ -0,0 +1,10 @@
<svg
width="512"
height="512"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor">
<path
d="M 363.74832,181.71778 455.38913,90.07697 421.92303,56.610874 330.28222,148.25168 Z M 503.55528,90.07697 c 0,5.317043 -1.8766,10.32132 -5.62981,14.07453 L 95.706781,506.37019 C 91.953574,510.1234 86.949297,512 81.632254,512 76.315211,512 71.310935,510.1234 67.557728,506.37019 L 5.6298106,444.44227 C 1.8766035,440.68907 0,435.68479 0,430.36775 0,425.0507 1.8766035,420.04643 5.6298106,416.29322 L 407.8485,14.074527 c 3.75321,-3.753208 8.75749,-5.6298111 14.07453,-5.6298111 5.31704,0 10.32132,1.8766031 14.07453,5.6298111 l 61.92791,61.927916 c 3.75321,3.753208 5.62981,8.757484 5.62981,14.074527 z M 81.00672,30.651191 111.65791,40.034209 81.00672,49.417227 71.623702,80.068418 62.240684,49.417227 31.589493,40.034209 62.240684,30.651191 71.623702,0 Z m 109.46854,50.668296 61.30238,18.766033 -61.30238,18.76604 -18.76604,61.30238 L 152.94319,118.85156 91.640806,100.08552 152.94319,81.319487 171.70922,20.017104 Z M 481.34881,230.82224 512,240.20525 l -30.65119,9.38302 -9.38302,30.65119 -9.38302,-30.65119 -30.65119,-9.38302 30.65119,-9.38301 9.38302,-30.6512 z M 281.17776,30.651191 l 30.6512,9.383018 -30.6512,9.383018 -9.38301,30.651191 -9.38302,-30.651191 -30.65119,-9.383018 30.65119,-9.383018 L 271.79475,0 Z"
style="stroke-width:0.312767" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,15 @@
<svg
width="512"
height="512"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor">
<path
d="M 0,0 V 256 H 256 V 213.33333 H 42.666667 V 42.66667 H 213.33333 v 192 H 256 V 0 Z M 0,512 V 298.66667 H 213.33333 V 512 H 21.333333 V 469.33333 H 170.66667 v -128 H 42.666667 V 512 Z M 298.66667,213.33333 V 0 H 512 V 213.33333 H 320 v -42.66666 h 149.33333 v -128 h -128 v 170.66666 z"
id="path1"
style="stroke-width:0.333333" />
<path
d="m 85.333333,85.33333 h 85.333337 v 85.33334 H 85.333333 Z m 298.666667,0 h 42.66667 V 128 H 384 Z M 85.333333,384 H 128 v 42.66667 H 85.333333 Z M 256,298.66667 h 42.66667 v 42.66666 H 256 Z m 128,42.66666 h 42.66667 V 384 H 384 Z m 0,85.33334 h 42.66667 v 42.66666 H 384 Z M 341.33333,256 v 42.66667 h 85.33334 v 42.66666 h 42.66666 V 256 Z M 512,384 h -42.66667 v 85.33333 H 426.66667 V 512 H 512 Z M 341.33333,341.33333 H 298.66667 V 384 H 256 v 42.66667 h 85.33333 z M 256,469.33333 H 384 V 512 H 256 Z"
id="path2"
style="stroke-width:0.333333" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB