Compare commits

...

11 Commits

Author SHA1 Message Date
Sunil Mohan Adapa
68ccb46ecf
tests: functional: Increase systemd rate limits for starting units
- If functional tests run fast, daemon will be stopped and started many times.
This is hitting rate limit for some daemons.

Tests:

- Configuration file is created as expected. systemd read the value as shown by
'systemctl show'.

- Mumble functional tests pass without having to insert sleeps.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-02-24 12:52:13 -08:00
James Valleroy
2044fa3e84
mumble: murmurd renamed to mumble-server
- Update backup manifest config file location.

Fixes #2518

Tests:

- mumble functional tests are passed.

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-02-24 12:28:16 -08:00
Sunil Mohan Adapa
bc4730c33c
action_utils: Fix issue with type checking a generator
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-02-24 10:58:00 -08:00
Sunil Mohan Adapa
365c1c3484
doc/dev: Set new theme for developer documentation
- Change theme to 'Book' based on Sphinx pydata theme. It supports dark/light
modes. Looks more modern and better defaults/options.

- Add logo on the top left corner.

- Add menu to go to repository, view page source, edit page source, and report
issue on current page.

- Don't repeat 'FreedomBox Authors' in authors text and copyright text. Show
only once as suggested by the theme.

- Use theme option to show license link instead of overriding the template.

Tests:

- After building the theme, all options work as expected.

- Footer appears as expected.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
2026-02-23 13:41:54 -08:00
Benedek Nagy
32fae4c3d3
doc/dev: always have an up-to-date copyright year
Signed-off-by: Benedek Nagy <contact@nbenedek.me>
2026-02-22 17:11:36 -08:00
Sunil Mohan Adapa
9a16e20fa9
letsencrypt: When copying certificate reset the umask reliably
- When there is an error writing to certificate files, the umask is not reset
properly. Fix this my using umask context manager from action utils. This could
be core reason behind: #2564.

Tests:

- Changing the domain name creates the file /var/lib/quassel/quasselCert.pem
with the proper permissions of 0o600. If in Quassel app's Let's Encrypt component
the certificate file path is changed, then two files are created on domain name
change. Private key is created with 0o600 permissions and certificate file is
created with 0o644 permissions.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-02-22 14:17:51 -05:00
Sunil Mohan Adapa
03b4a78fd0
quassel: Explicitly set permissions on the domain configuration file
Closes: #2564

Tests:

- Changing the domain name in quassel app page set the expected file
permissions. Changing the value in the code results in file getting created with
changed permissions.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-02-22 14:17:47 -05:00
Sunil Mohan Adapa
ac83de6635
action_utils: Implement utility to change umask temporarily
- When we set umask we typically want to change it back to original value after
the operation. Implement a context manager to help with this.

Tests:

- Unit tests pass.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2026-02-22 14:17:44 -05:00
James Valleroy
b1177a82f8
Translated using Weblate (Greek)
Currently translated at 46.1% (869 of 1885 strings)
2026-02-22 14:17:00 +01:00
Βασίλης Χατζηκαμάρης
77112e9faf
Translated using Weblate (Greek)
Currently translated at 45.5% (858 of 1885 strings)
2026-02-18 15:09:50 +00:00
Coucouf
d9f20b205b
Translated using Weblate (French)
Currently translated at 100.0% (1885 of 1885 strings)
2026-02-18 15:09:48 +00:00
13 changed files with 287 additions and 171 deletions

View File

@ -19,6 +19,7 @@ Install the following Debian packages:
* python3-sphinx
* python3-sphinx-autobuild
* python3-sphinx-book-theme
* python3-django
* python3-django-axes
* python3-django-captcha

122
doc/dev/_static/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,15 +0,0 @@
{%- extends "alabaster/layout.html" %}
{%- block footer %}
<div class="footer">
{% if show_copyright %}&copy;{{ copyright }} | {% endif %}
Licensed under the <a href="https://creativecommons.org/licenses/by-sa/4.0/">
CC BY-SA 4.0</a> license
{%- if show_source and has_source and sourcename %}
{% if show_copyright or theme_show_powered_by %}|{% endif %}
<a href="{{ pathto('_sources/' + sourcename, true)|e }}"
rel="nofollow">{{ _('Page source') }}</a>
{%- endif %}
</div>
{% endblock %}

View File

@ -15,6 +15,7 @@ list see the documentation: http://www.sphinx-doc.org/en/master/config
#
import os
import sys
from datetime import datetime
import django
@ -26,7 +27,7 @@ django.setup()
# pylint: disable=invalid-name
project = 'FreedomBox'
copyright = '2021-2025, FreedomBox Authors'
copyright = f'2021-{datetime.now().year}'
author = 'FreedomBox Authors'
# The short X.Y version
@ -82,15 +83,23 @@ pygments_style = None
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = 'sphinx_book_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'fixed_sidebar': True,
'show_related': True,
'home_page_in_toc': True,
'repository_provider': 'gitlab',
'repository_url': 'https://salsa.debian.org/freedombox-team/freedombox/',
'use_edit_page_button': True,
'use_source_button': True,
'use_repository_button': True,
'use_issues_button': True,
'path_to_docs': 'doc/dev/',
'extra_footer': 'Licensed under the <a href="https://creativecommons.org/'
'licenses/by-sa/4.0/">CC BY-SA 4.0</a> license.',
}
# Add any paths that contain custom static files (such as style sheets) here,
@ -221,3 +230,4 @@ autodoc_mock_imports = [
]
html_favicon = './_static/favicon.ico'
html_logo = './_static/logo.svg'

View File

@ -10,6 +10,7 @@ import shutil
import subprocess
import tempfile
from contextlib import contextmanager
from typing import Generator
import augeas
@ -838,3 +839,13 @@ def run(command, **kwargs):
raise exception
return process
@contextmanager
def umask(mask) -> Generator:
"""Set the umask temporarily for a operation and then revert it."""
old_umask = os.umask(mask)
try:
yield
finally:
os.umask(old_umask)

View File

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-02-03 01:14+0000\n"
"PO-Revision-Date: 2022-09-14 17:20+0000\n"
"Last-Translator: ikmaak <info@ikmaak.nl>\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/"
"freedombox/el/>\n"
"Language: el\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 4.14.1-dev\n"
"X-Generator: Weblate 5.16.1-dev\n"
#: plinth/config.py:103
#, python-brace-format
@ -26,9 +26,8 @@ msgstr ""
#: plinth/container.py:140
#, fuzzy, python-brace-format
#| msgid "Service {service_name} is running"
msgid "Container {container_name} is running"
msgstr "Το πρόγραμμα {service_name} είναι ενεργοποιημένο"
msgstr "Το πρόγραμμα {container_name} είναι ενεργοποιημένο"
#: plinth/context_processors.py:21 plinth/views.py:175
msgid "FreedomBox"
@ -260,7 +259,7 @@ msgstr ""
#, fuzzy, python-brace-format
#| msgid "About {box_name}"
msgid "Go to {app_name}"
msgstr "Σχετικά με το {box_name}"
msgstr "Σχετικά με το {app_name}"
#: plinth/modules/backups/__init__.py:218
#, python-brace-format
@ -733,27 +732,17 @@ msgid "Restore data from"
msgstr "Επαναφορά δεδομένων από"
#: plinth/modules/backups/templates/backups_upload.html:17
#, fuzzy, python-format
#| msgid ""
#| "\n"
#| " Upload a backup file downloaded from another %(box_name)s to "
#| "restore its\n"
#| " contents. You can choose the apps you wish to restore after "
#| "uploading a\n"
#| " backup file.\n"
#| " "
#, python-format
msgid ""
"Upload a backup file downloaded from another %(box_name)s to restore its "
"contents. You can choose the apps you wish to restore after uploading a "
"backup file."
msgstr ""
"\n"
" Ανεβάστε ένα αντίγραφο ασφαλείας που κατεβάσατε από ένα άλλο "
"%(box_name)s για να επαναφέρετε τα\n"
"Ανεβάστε ένα αντίγραφο ασφαλείας που κατεβάσατε από ένα άλλο %(box_name)s "
"για να επαναφέρετε τα\n"
" περιεχόμενα. Μπορείτε να επιλέξετε τις εφαρμογές που θέλετε να επαναφέρετε "
"μετά την αποστολή ενός\n"
"αρχείου αντιγράφου ασφαλείας.\n"
" "
"αρχείου αντιγράφου ασφαλείας."
#: plinth/modules/backups/templates/backups_upload.html:31
#, python-format
@ -1351,7 +1340,7 @@ msgstr ""
#, fuzzy, python-format
#| msgid "Delete site %(site)s"
msgid "Delete library %(library)s"
msgstr "Διαγραφή ιστότοπου %(site)s"
msgstr "Διαγράψτε βιβλιοθήκη %(library)s"
#: plinth/modules/calibre/views.py:39
#, fuzzy
@ -1498,7 +1487,7 @@ msgid "Webserver Home Page"
msgstr "Αρχική σελίδα του διακομιστή διαδικτύου"
#: plinth/modules/config/forms.py:37
#, python-brace-format
#, 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 "
@ -1510,7 +1499,7 @@ msgstr ""
"{box_name} στο διαδίκτυο. Μια συνήθης χρήση είναι να ορίσετε το blog ή το "
"wiki σας. Να σημειωθεί ότι αφού καθορίσετε την αρχική σελίδα σε κάτι "
"διαφορετικό από την υπηρεσία {box_name} (Plinth), οι χρήστεσ πρέπει να "
"γράψουν /plinth ή /freedombox για να αποκτήσουν πρόσβαση σε αυτή."
"γράψουν /plinth ή /freedombox για να αποκτήσουν πρόσβαση σε αυτή {box_name}."
#: plinth/modules/config/forms.py:48
msgid "Show advanced apps and features"
@ -2271,7 +2260,7 @@ msgstr "Η εγκατάσταση απέτυχε."
#, fuzzy, python-format
#| msgid "Delete connection %(name)s"
msgid "Delete domain %(domain)s"
msgstr "Διαγραφή της σύνδεσης %(name)s"
msgstr "Διαγράψτε domain %(domain)s"
#: plinth/modules/dynamicdns/templates/dynamicdns.html:91
#, fuzzy
@ -2492,20 +2481,16 @@ msgid "Status"
msgstr "Κατάσταση"
#: plinth/modules/ejabberd/templates/ejabberd.html:23
#, fuzzy, python-format
#| msgid ""
#| "Your XMPP server domain is set to <b>%(domainname)s</b>. User IDs will "
#| "look like <i>username@%(domainname)s</i>. You can setup your domain on "
#| "the system <a href=\"%(index_url)s\">Configure</a> page."
#, python-format
msgid ""
"Your XMPP server domain is set to <b>%(domain_name)s</b>. User IDs will look "
"like <i>username@%(domain_name)s</i>. You can setup your domain on the "
"system <a href=\"%(names_url)s\">Name Services</a> page."
msgstr ""
"To όνομα διαδικτύου για το διακομιστή XMPP έχει ρυθμιστεί ως το "
"<b>%(domainname)s</b>. Ταυτότητες χρηστών θα είναι όπως: "
"<i>username@%(domainname)s</i>. Μπορείτε να ρυθμίσετε το όνομα της υπηρεσίας "
"στο <a href=\"%(index_url)s\">Ρυθμίστε</a> page."
"To όνομα διαδικτύου για το διακομιστή XMPP έχει ρυθμιστεί ως το <b>%"
"(domain_name)s</b>. Ταυτότητες χρηστών θα είναι όπως: <i>username@%"
"(domain_name)s</i>. Μπορείτε να ρυθμίσετε το όνομα της υπηρεσίας στο <a "
"href=\"%(names_url)s\">Ρυθμίστε</a> page."
#: plinth/modules/ejabberd/templates/ejabberd.html:30
#, fuzzy, python-format
@ -2517,10 +2502,8 @@ msgid ""
"Your XMPP server domain is not set. You can setup your domain on the system "
"<a href=\"%(names_url)s\">Name Services</a> page."
msgstr ""
"To όνομα διαδικτύου για το διακομιστή XMPP έχει ρυθμιστεί ως το "
"<b>%(domainname)s</b>. Ταυτότητες χρηστών θα είναι όπως: "
"<i>username@%(domainname)s</i>. Μπορείτε να ρυθμίσετε το όνομα της υπηρεσίας "
"στο <a href=\"%(index_url)s\">Ρυθμίστε</a> page."
"Το domain XMPP server σας δεν έχει οριστεί. Μπορείτε να ρυθμίσετε τον τομέα "
"σας στο σύστημα <a href=\"%(names_url)s\">Name Services</a> σελίδα."
#: plinth/modules/email/__init__.py:26
msgid ""
@ -2908,7 +2891,7 @@ msgstr "Δεν υπάρχουν διαθέσιμα αποθετήρια."
#, fuzzy, python-format
#| msgid "Go to site %(site)s"
msgid "Go to wiki %(wiki)s"
msgstr "Μεταβείτε στην τοποθεσία %(site)s"
msgstr "Μεταβείτε στην τοποθεσία %(wiki)s"
#: plinth/modules/featherwiki/templates/featherwiki_configure.html:43
#: plinth/modules/tiddlywiki/templates/tiddlywiki_configure.html:43
@ -2921,7 +2904,7 @@ msgstr ""
#, fuzzy, python-format
#| msgid "Delete site %(site)s"
msgid "Delete wiki %(wiki)s"
msgstr "Διαγραφή ιστότοπου %(site)s"
msgstr "Διαγραφή ιστότοπου %(wiki)s"
#: plinth/modules/featherwiki/templates/featherwiki_delete.html:12
#: plinth/modules/tiddlywiki/templates/tiddlywiki_delete.html:12
@ -3005,10 +2988,9 @@ msgstr "Απέτυχε η προσθήκη χρήστη στην ομάδα."
#: plinth/modules/featherwiki/views.py:138
#: plinth/modules/tiddlywiki/views.py:139
#, fuzzy, python-brace-format
#| msgid "Could not delete {name}: {error}"
#, python-brace-format
msgid "Could not delete {name}"
msgstr "Δεν ήταν δυνατή η διαγραφή του {name}: {error}"
msgstr "Δεν ήταν δυνατή η διαγραφή του {name}"
#: plinth/modules/firewall/__init__.py:25
#, python-brace-format
@ -3507,12 +3489,13 @@ msgstr ""
"%(box_name)s."
#: plinth/modules/help/templates/help_about.html:35
#, fuzzy, python-format
#| msgid "There is a new %(box_name)s version available."
#, python-format
msgid ""
"There is a new %(box_name)s version <a href=\"%(upgrades_url)s\">available</"
"a>."
msgstr "Υπάρχει μια νέα έκδοση %(box_name)s διαθέσιμη."
msgstr ""
"Υπάρχει μια νέα έκδοση %(box_name)s <a href=\"%(upgrades_url)s\">διαθέσιμη</"
"a>."
#: plinth/modules/help/templates/help_about.html:40
#: plinth/modules/upgrades/templates/upgrades_configure.html:42
@ -4281,7 +4264,7 @@ msgstr "Δεν υπάρχουν διαθέσιμα αποθετήρια."
#, fuzzy, python-format
#| msgid "Delete site %(site)s"
msgid "Delete package %(title)s"
msgstr "Διαγραφή ιστότοπου %(site)s"
msgstr "Διαγραφή ιστότοπου %(title)s"
#: plinth/modules/kiwix/views.py:49
msgid "Content package added."
@ -4401,15 +4384,12 @@ msgid "Obtain"
msgstr "Αποκτήσετε"
#: plinth/modules/letsencrypt/templates/letsencrypt.html:112
#, fuzzy, python-format
#| msgid ""
#| "No domains have been configured. <a href=\"%(config_url)s\">Configure "
#| "domains</a> to be able to obtain certificates for them."
#, python-format
msgid ""
"No domains have been configured. <a href=\"%(names_url)s\">Configure "
"domains</a> to be able to obtain certificates for them."
msgstr ""
"Δεν έχουν ρυθμιστεί ονόματα διαδικτύου <a href=\"%(config_url)s\">Ρυθμίσετε "
"Δεν έχουν ρυθμιστεί ονόματα διαδικτύου <a href=\"%(names_url)s\">Ρυθμίσετε "
"όνομα διαδικτύου</a> για να μπορείτε να αποκτήσετε πιστοποιητικά για αυτό."
#: plinth/modules/letsencrypt/views.py:40
@ -4422,10 +4402,9 @@ msgstr ""
"να διαρκέσει λίγα λεπτά για να τεθεί σε ισχύ."
#: plinth/modules/letsencrypt/views.py:46
#, fuzzy, python-brace-format
#| msgid "Failed to revoke certificate for domain {domain}: {error}"
#, python-brace-format
msgid "Failed to revoke certificate for domain {domain}"
msgstr "Απέτυχε η ανάκληση του πιστοποιητικού για το όνομα {domain}: {error}"
msgstr "Απέτυχε η ανάκληση του πιστοποιητικού για το όνομα {domain}"
#: plinth/modules/letsencrypt/views.py:59
#: plinth/modules/letsencrypt/views.py:77
@ -4435,10 +4414,9 @@ msgstr "Το πιστοποιητικό αποκτήθηκε με επιτυχί
#: plinth/modules/letsencrypt/views.py:64
#: plinth/modules/letsencrypt/views.py:82
#, fuzzy, python-brace-format
#| msgid "Failed to obtain certificate for domain {domain}: {error}"
#, python-brace-format
msgid "Failed to obtain certificate for domain {domain}"
msgstr "Αποτυχία λήψης πιστοποιητικού για το όνομα {domain} {error}"
msgstr "Αποτυχία λήψης πιστοποιητικού για το όνομα {domain}"
#: plinth/modules/letsencrypt/views.py:95
#, python-brace-format
@ -4446,10 +4424,9 @@ msgid "Certificate successfully deleted for domain {domain}"
msgstr "Το πιστοποιητικό διαγράφηκε επιτυχώς για το όνομα {domain}"
#: plinth/modules/letsencrypt/views.py:100
#, fuzzy, python-brace-format
#| msgid "Failed to delete certificate for domain {domain}: {error}"
#, python-brace-format
msgid "Failed to delete certificate for domain {domain}"
msgstr "Αποτυχία διαγραφής πιστοποιητικού για το όνομα {domain}: {error}"
msgstr "Αποτυχία διαγραφής πιστοποιητικού για το όνομα {domain}"
#: plinth/modules/matrixsynapse/__init__.py:26
msgid ""
@ -4554,22 +4531,12 @@ msgstr ""
"</em>."
#: plinth/modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:31
#, fuzzy
#| msgid ""
#| "\n"
#| " <strong>Warning!</strong> Changing the domain name after the "
#| "initial\n"
#| " setup is currently not supported.\n"
#| " "
msgid ""
"<strong>Warning!</strong> Changing the domain name after this step will "
"require uninstalling and reinstalling the app which will wipe app's data."
msgstr ""
"\n"
" το <strong>Προσοχη!</strong> Αλλαγή στο όνομα διαδικτύου, μετά την "
"αρχική\n"
"εγκατάσταση δεν υποστηρίζεται.\n"
" "
"το <strong>Προσοχη!</strong> Αλλαγή στο όνομα διαδικτύου, μετά την αρχική\n"
"εγκατάσταση δεν υποστηρίζεται."
#: plinth/modules/matrixsynapse/templates/matrix-synapse-pre-setup.html:42
#, python-format
@ -5151,10 +5118,9 @@ msgid "Created admin user: {username}"
msgstr "Μη έγκυρο όνομα χρήστη: {username}"
#: plinth/modules/miniflux/views.py:53
#, fuzzy, python-brace-format
#| msgid "An error occurred while creating the repository."
#, python-brace-format
msgid "An error occurred while creating the user: {error}."
msgstr "Παρουσιάστηκε σφάλμα κατά τη δημιουργία του αποθετηρίου."
msgstr "Παρουσιάστηκε σφάλμα κατά τη δημιουργία του αποθετηρίου: {error}."
#: plinth/modules/miniflux/views.py:70
#, fuzzy
@ -5172,7 +5138,7 @@ msgstr "Μη έγκυρο όνομα χρήστη: {username}"
#, fuzzy, python-brace-format
#| msgid "An error occurred during configuration."
msgid "An error occurred during password reset: {error}."
msgstr "Παρουσιάστηκε σφάλμα κατά τη ρύθμιση παραμέτρων."
msgstr "Παρουσιάστηκε σφάλμα κατά τη ρύθμιση παραμέτρων: {error}."
#: plinth/modules/mumble/__init__.py:26
msgid ""
@ -5788,7 +5754,7 @@ msgstr "Ανοιχτό"
#, fuzzy, python-brace-format
#| msgid "Use upstream bridges to connect to Tor network"
msgid "Specify how your {box_name} is connected to your network"
msgstr "Χρησιμοποιήστε εξωτερικές γέφυρες για να συνδεθείτε στο δίκτυο Tor"
msgstr "Καθορίστε πώς {box_name} είναι συνδεδεμένο με το δίκτυό σας"
#: plinth/modules/networks/forms.py:382
#, python-brace-format
@ -6270,7 +6236,7 @@ msgstr "Υποβολή"
#, fuzzy, python-format
#| msgid "Direct connection to the Internet."
msgid "How is Your %(box_name)s Connected to the Internet?"
msgstr "Άμεση σύνδεση στο Internet."
msgstr "Πώς είναι το %(box_name)s Συνδέεται με το Διαδίκτυο?"
#: plinth/modules/networks/templates/network_topology_content.html:16
#, python-format
@ -6378,7 +6344,7 @@ msgstr ""
#, fuzzy, python-format
#| msgid "Delete %(username)s"
msgid "Device: %(interface_name)s"
msgstr "Διαγραφή %(username)s"
msgstr "Συσκευή: %(interface_name)s"
#: plinth/modules/networks/templates/wifi_scan.html:27
#, fuzzy
@ -6864,12 +6830,7 @@ msgid "Profile"
msgstr "Προφίλ"
#: plinth/modules/openvpn/templates/openvpn.html:15
#, fuzzy, python-format
#| msgid ""
#| "To connect to %(box_name)s's VPN, you need to download a profile and feed "
#| "it to an OpenVPN client on your mobile or desktop machine. OpenVPN "
#| "Clients are available for most platforms. Click \"Learn more...\" above "
#| "for recommended clients and instructions on how to configure them."
#, python-format
msgid ""
"To connect to %(box_name)s's VPN, you need to download a profile and feed it "
"to an OpenVPN client on your mobile or desktop machine. OpenVPN Clients are "
@ -6878,17 +6839,16 @@ msgid ""
msgstr ""
"Για να συνδεθείτε στο VPN του %(box_name)s, πρέπει να κάνετε λήψη ενός "
"προφίλ και να το τροφοδοτήσετε σε ένα πρόγραμμα-πελάτη OpenVPN στον φορητό ή "
"επιτραπέζιο υπολογιστή σας. Οι υπολογιστές-πελάτες OpenVPN είναι διαθέσιμοι "
"για τις περισσότερες πλατφόρμες. Κάντε κλικ στην επιλογή \"Μάθετε "
"επιτραπέζιο υπολογιστή σας. Οι υπολογιστές-πελάτες OpenVPN είναι διαθέσιμοι "
"για τις περισσότερες πλατφόρμες.Κάντε κλικ στην επιλογή \"Μάθετε "
"περισσότερα...\" Προτεινόμενα προγράμματα-πελάτες και οδηγίες σχετικά με τον "
"τρόπο διαμόρφωσης τους."
#: plinth/modules/openvpn/templates/openvpn.html:24
#, fuzzy, python-format
#| msgid "Profile is specific to each user of %(box_name)s. Keep it a secret."
#, python-format
msgid "Profile is specific to each user of %(box_name)s. Keep it a secret."
msgstr ""
"Το προφίλ είναι συγκεκριμένο για κάθε χρήστη του %(box_name)s. Κρατήστε το "
"Το προφίλ είναι συγκεκριμένο για κάθε χρήστη του %(box_name)s. Κρατήστε το "
"μυστικό."
#: plinth/modules/openvpn/templates/openvpn.html:34
@ -7597,15 +7557,12 @@ msgstr ""
#: plinth/modules/rssbridge/__init__.py:24
#, fuzzy, python-brace-format
#| msgid ""
#| "When enabled, Tiny Tiny RSS can be accessed by any <a href=\"{users_url}"
#| "\">user with a {box_name} login</a>."
msgid ""
"When enabled, RSS-Bridge can be accessed by <a href=\"{users_url}\">any "
"user</a> belonging to the feed-reader group."
msgstr ""
"Όταν είναι ενεργοποιημένο, το Tiny Tiny RSS είναι προσβάσιμο από κάθε <a "
"href=\"{users_url}\">χρήστη με {box_name} πιστοποιητικά</a>."
"Όταν είναι ενεργοποιημένο, το Tiny RSS είναι προσβάσιμο από κάθε <a href=\""
"{users_url}\">χρήστη με πιστοποιητικά</a>."
#: plinth/modules/rssbridge/__init__.py:28
#, python-brace-format
@ -9690,7 +9647,7 @@ msgstr ""
#, fuzzy, python-format
#| msgid "Delete site %(site)s"
msgid "Released: %(date)s."
msgstr "Διαγραφή ιστότοπου %(site)s"
msgstr "Εκδόθηκε %(date)s"
#: plinth/modules/upgrades/templates/upgrades-dist-upgrade.html:91
#, fuzzy
@ -9982,16 +9939,14 @@ msgid "Invalid password."
msgstr "Εμφάνιση κωδικού"
#: plinth/modules/users/forms.py:213 plinth/modules/users/forms.py:439
#, fuzzy, python-brace-format
#| msgid "Creating LDAP user failed."
#, python-brace-format
msgid "Creating LDAP user failed: {error}"
msgstr "Η δημιουργία χρήστη LDAP απέτυχε."
msgstr "Η δημιουργία χρήστη LDAP απέτυχε: {error}."
#: plinth/modules/users/forms.py:225
#, fuzzy, python-brace-format
#| msgid "Failed to add new user to {group} group."
#, python-brace-format
msgid "Failed to add new user to {group} group: {error}"
msgstr "Απέτυχε η προσθήκη νέου χρήστη στην ομάδα {group}."
msgstr "Απέτυχε η προσθήκη νέου χρήστη στην ομάδα {group}: {error}."
#: plinth/modules/users/forms.py:241
msgid "Authorized SSH Keys"
@ -10051,10 +10006,9 @@ msgid "Changing LDAP user password failed."
msgstr "Η αλλαγή του κωδικού πρόσβασης χρήστη LDAP απέτυχε."
#: plinth/modules/users/forms.py:447
#, fuzzy, python-brace-format
#| msgid "Failed to add new user to admin group."
#, python-brace-format
msgid "Failed to add new user to admin group: {error}"
msgstr "Αποτυχία προσθήκης νέου χρήστη στην ομάδα διαχειριστών."
msgstr "Αποτυχία προσθήκης νέου χρήστη στην ομάδα διαχειριστών: {error}."
#: plinth/modules/users/forms.py:470
msgid "User account created, you are now logged in"
@ -10731,10 +10685,9 @@ msgid "Generic"
msgstr "Γενικός"
#: plinth/operation.py:120
#, fuzzy, python-brace-format
#| msgid "Error setting hostname: {exception}"
#, python-brace-format
msgid "Error: {name}: {exception}"
msgstr "Σφάλμα κατά τη ρύθμιση του ονόματος του υπολογιστή: {exception}"
msgstr "Σφάλμα κατά τη ρύθμιση του ονόματος του υπολογιστή {name}: {exception}"
#: plinth/operation.py:123
#, python-brace-format
@ -10790,22 +10743,21 @@ msgid "Updating app"
msgstr "Eνημερώνεται..."
#: plinth/setup.py:80
#, fuzzy, python-brace-format
#| msgid "Error installing application: {error}"
#, python-brace-format
msgid "Error installing app: {exception}"
msgstr "Σφάλμα κατά την εγκατάσταση της εφαρμογής: {error}"
msgstr "Σφάλμα κατά την εγκατάσταση της εφαρμογής: {exception}"
#: plinth/setup.py:82
#, fuzzy, python-brace-format
#| msgid "Error installing application: {error}"
msgid "Error repairing app: {exception}"
msgstr "Σφάλμα κατά την εγκατάσταση της εφαρμογής: {error}"
msgstr "Σφάλμα κατά την εγκατάσταση της εφαρμογής: {exception}"
#: plinth/setup.py:84
#, fuzzy, python-brace-format
#| msgid "Error installing application: {error}"
msgid "Error updating app: {exception}"
msgstr "Σφάλμα κατά την εγκατάσταση της εφαρμογής: {error}"
msgstr "Σφάλμα κατά την εγκατάσταση της εφαρμογής: {exception}"
#: plinth/setup.py:87
#, fuzzy
@ -10936,11 +10888,10 @@ msgid ""
"the <a href=\"%(logs_url)s\">logs</a> to the bug report."
msgstr ""
"Αυτό είναι ένα εσωτερικό σφάλμα και όχι κάτι που προκαλέσατε ή μπορείτε να "
"διορθώσετε. Αναφέρετε το σφάλμα στο <a href = \"https://salsa.debian.org/"
"freedombox-team/plinth/issues\">κατάλογος σφαλμάτων </a> ώστε να μπορούμε να "
"το διορθώσουμε. Επίσης, Παρακαλούμε επισυνάψτε το <a href = "
"\"%(status_log_url)s\">αρχείο καταγραφής κατάστασης </a> στην αναφορά "
"σφάλματος."
"διορθώσετε. Αναφέρετε το σφάλμα στο <a href=\"https://salsa.debian.org/"
"freedombox-team/freedombox/issues\">κατάλογος σφαλμάτων </a> ώστε να "
"μπορούμε να το διορθώσουμε. Επίσης, Παρακαλούμε επισυνάψτε το <a href=\"%"
"(logs_url)s\">αρχείο καταγραφής κατάστασης </a> στην αναφορά σφάλματος."
#: plinth/templates/app-header.html:26
msgid "Installation"
@ -11100,8 +11051,8 @@ msgid ""
"<em>%(service_name)s</em> is available only on internal networks or when the "
"client is connected to %(box_name)s through VPN."
msgstr ""
"Η υπηρεσία <em>%(service_name)s</em> είναι διαθέσιμη μόνο σε εσωτερικά "
"δίκτυα."
"Το <em>%(service_name)s</em> είναι διαθέσιμο μόνο σε εσωτερικά δίκτυα ή όταν "
"ο πελάτης συνδέεται με το %(box_name)s μέσω VPN."
#: plinth/templates/internal-zone.html:25
msgid "Currently there are no network interfaces configured as internal."
@ -11164,9 +11115,9 @@ msgid ""
"are not using the DMZ feature. You will need to set up port forwarding on "
"your router. You should forward the following ports for %(service_name)s:"
msgstr ""
"Αν το FreedomBox είναι πίσω από ένα router, θα πρέπει να ρυθμίσετε την "
"προώθηση των θυρών στο δρομολογητή σας. Θα πρέπει να προωθήσετε τις "
"ακόλουθες θύρες για %(service_name)s:"
"Αν το FreedomBox είναι <a href=\"%(networks_url)s\">πίσω από ένα router</a>, "
"θα πρέπει να ρυθμίσετε την προώθηση των θυρών στο δρομολογητή σας. Θα πρέπει "
"να προωθήσετε τις ακόλουθες θύρες για %(service_name)s:"
#: plinth/templates/port-forwarding-info.html:42
#, fuzzy
@ -11289,7 +11240,7 @@ msgstr "Εγκατάσταση"
#, fuzzy, python-format
#| msgid "Edit user %(username)s"
msgid "Uninstall App <em>%(app_name)s</em>?"
msgstr "Επεξεργασία χρήστη %(username)s"
msgstr "Εφαρμογή <em>%(app_name)s</em>?"
#: plinth/templates/uninstall.html:17
msgid ""

View File

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: FreedomBox UI\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-02-03 01:14+0000\n"
"PO-Revision-Date: 2026-02-04 06:01+0000\n"
"PO-Revision-Date: 2026-02-18 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.16-dev\n"
"X-Generator: Weblate 5.16.1-dev\n"
#: plinth/config.py:103
#, python-brace-format
@ -4767,8 +4767,8 @@ msgstr "Activer PVP"
#: plinth/modules/minetest/forms.py:26
msgid "Enabling Player Vs Player will allow players to damage other players."
msgstr ""
"Activer le paramètre joueur contre joueur (player versus player, PVP), "
"permettra aux joueurs dinfliger des dommages aux autres joueurs."
"Activer le mode joueur contre joueur (player versus player, PVP), permettra "
"aux joueurs dinfliger des dommages aux autres joueurs."
#: plinth/modules/minetest/forms.py:30
msgid "Enable damage"
@ -7187,7 +7187,7 @@ msgstr "Privoxy"
#: plinth/modules/privoxy/__init__.py:119
#, python-brace-format
msgid "Access {url} with proxy {proxy} on tcp{kind}"
msgstr "Accéder à lURL {url} avec le mandataire {proxy} sur tcp{kind}"
msgstr "Accéder à lURL {url} via le mandataire {proxy} sur tcp{kind}"
#: plinth/modules/privoxy/manifest.py:10
msgid "Ad blocker"
@ -7424,11 +7424,11 @@ msgid ""
"uncheck this option. When unchecked, a text entry field is added to the "
"login page so the user can specify to which account they wish to connect."
msgstr ""
"Lorsque l'option est activée, les utilisateurs ne peuvent que lire et "
"Lorsque loption est activée, les utilisateurs ne peuvent que lire et "
"envoyer des courriels par cette {box_name}. Si vous souhaitez utiliser "
"Roundcube avec un compte de courriel externe tel que GMail, vous devez "
"décocher cette option. Lorsqu'elle est décochée, un champ d'entrée de texte "
"est ajouté à la page de connexion pour que l'utilisateur puisse indiquer à "
"Roundcube avec un compte de courriel externe tel que Gmail, vous devez "
"décocher cette option. Lorsquelle est décochée, un champ supplémentaire est "
"ajouté au formulaire de connexion pour que lutilisateur puisse indiquer à "
"quel compte il veut se connecter."
#: plinth/modules/roundcube/manifest.py:23
@ -10258,7 +10258,7 @@ msgstr "Ajouter une connexion à un serveur"
#: plinth/modules/wireguard/templates/wireguard_add_client.html:21
msgid "IP address that will be assigned to this client"
msgstr "Adresse IP assignée à ce client :"
msgstr "Adresse IP assignée à ce client"
#: plinth/modules/wireguard/templates/wireguard_add_client.html:31
msgid "Add Client"

View File

@ -160,21 +160,19 @@ def copy_certificate(managing_app: str, source_private_key: str,
certificate_path.parent.mkdir(mode=0o755, parents=True, exist_ok=True)
# Private key is only accessible to the user owner
old_mask = os.umask(0o177)
shutil.copyfile(source_private_key_path, private_key_path)
with action_utils.umask(0o177):
shutil.copyfile(source_private_key_path, private_key_path)
if certificate_path != private_key_path:
# Certificate is only writable by the user owner
os.umask(0o133)
shutil.copyfile(source_certificate_path, certificate_path)
else:
# If private key and certificate are the same file, append one after
# the other.
source_certificate_bytes = source_certificate_path.read_bytes()
with private_key_path.open(mode='a+b') as file_handle:
file_handle.write(source_certificate_bytes)
os.umask(old_mask)
if certificate_path != private_key_path:
# Certificate is only writable by the user owner
with action_utils.umask(0o133):
shutil.copyfile(source_certificate_path, certificate_path)
else:
# If private key and certificate are the same file, append one
# after the other.
source_certificate_bytes = source_certificate_path.read_bytes()
with private_key_path.open(mode='a+b') as file_handle:
file_handle.write(source_certificate_bytes)
shutil.chown(certificate_path, user=user_owner, group=group_owner)
shutil.chown(private_key_path, user=user_owner, group=group_owner)

View File

@ -56,7 +56,7 @@ clients = [{
backup = {
'config': {
'files': ['/etc/mumble-server.ini']
'files': ['/etc/mumble/mumble-server.ini']
},
'data': {
'directories': ['/var/lib/mumble-server']

View File

@ -35,8 +35,8 @@ def check_setup() -> bool:
@privileged
def set_super_user_password(password: secret_str):
"""Set the superuser password with murmurd command."""
action_utils.run(['murmurd', '-readsupw'], input=password.encode(),
"""Set the superuser password with mumble-server command."""
action_utils.run(['mumble-server', '-readsupw'], input=password.encode(),
check=False)

View File

@ -11,3 +11,5 @@ def set_domain(domain_name: str):
"""Write a file containing domain name."""
domain_file = pathlib.Path('/var/lib/quassel/domain-freedombox')
domain_file.write_text(domain_name, encoding='utf-8')
# Ensure that that file is readable by non-privileged process.
domain_file.chmod(0o644)

View File

@ -14,6 +14,15 @@ PIP_OPTIONS=
dpkg --compare-versions 23 \<= $PIP_VERSION && PIP_OPTIONS=--break-system-packages
pip3 install $PIP_OPTIONS selenium==4.25.0 splinter==0.21.0 pytest-splinter pytest-reporter-html1
echo "Increasing systemd rate limits for starting units"
mkdir -p /etc/systemd/system.conf.d/
# Increase the start limit from 5 times in 10 seconds to 20 times in 10 seconds.
cat > /etc/systemd/system.conf.d/freedombox-functional-tests.conf << EOF
[Manager]
DefaultStartLimitBurst=20
EOF
systemctl daemon-reload
echo "Installing geckodriver"
(
DL_DIR=/tmp/gecko

View File

@ -4,6 +4,7 @@ Test module for key/value store.
"""
import json
import os
import pathlib
import subprocess
from unittest.mock import Mock, call, patch
@ -17,7 +18,7 @@ from plinth.action_utils import (get_addresses, get_hostname,
service_is_running, service_reload,
service_restart, service_start, service_stop,
service_try_reload_or_restart,
service_try_restart, service_unmask)
service_try_restart, service_unmask, umask)
UNKNOWN = 'unknowndeamon'
@ -298,3 +299,29 @@ def test_run_no_storage(subprocess_run):
subprocess_run.return_value.stderr = 'test-stderr'
run(['command', 'arg1', '--foo'], check=True)
def test_umask(tmp_path):
"""Test that setting umask works."""
def _asssert_umask(mask):
"""Assert the current umask."""
old_umask = os.umask(0)
assert old_umask == mask
os.umask(old_umask)
original_umask = os.umask(0o1)
with umask(0o033):
_asssert_umask(0o033)
file1 = tmp_path / 'file1'
file1.touch()
assert file1.stat().st_mode & 0o033 == 0
with umask(0o077):
_asssert_umask(0o77)
file2 = tmp_path / 'file2'
file2.touch()
assert file2.stat().st_mode & 0o077 == 0
_asssert_umask(0o033)
os.umask(original_umask)