From 3cf7e7c2c45c924d72eec3d0f4efe64bd47f619a Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Tue, 31 Jan 2023 09:39:37 -0500 Subject: [PATCH 01/26] matrixsynapse: Add python3-psycopg2 to packages python3-psycopg2 is a dependency of matrix-synapse package. It is also listed as a package to be installed by TT-RSS app. This is a quick fix to prevent accidental uninstall of matrix-synapse package, when TT-RSS app is uninstalled. Helps #2298. Tests: - Install Matrix Synapse. - Install TT-RSS, and then uninstall it. matrix-synapse package is still installed. Matrix Synapse diagnostics are passed. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- plinth/modules/matrixsynapse/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plinth/modules/matrixsynapse/__init__.py b/plinth/modules/matrixsynapse/__init__.py index d1043c19c..5ccad4940 100644 --- a/plinth/modules/matrixsynapse/__init__.py +++ b/plinth/modules/matrixsynapse/__init__.py @@ -70,8 +70,11 @@ class MatrixSynapseApp(app_module.App): clients=info.clients, login_required=True) self.add(shortcut) - packages = Packages('packages-matrixsynapse', - ['matrix-synapse', 'matrix-synapse-ldap3']) + # Include python3-psycopg2 to prevent accidental uninstall + # (see issue #2298). + packages = Packages( + 'packages-matrixsynapse', + ['matrix-synapse', 'matrix-synapse-ldap3', 'python3-psycopg2']) self.add(packages) firewall = Firewall('firewall-matrixsynapse', info.name, From 9cec959cd167b9c6d76f2479f221054014991c88 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Tue, 31 Jan 2023 09:43:01 -0500 Subject: [PATCH 02/26] searx: Add libjs-bootstrap to packages libjs-bootstrap is a transitive dependency of searx package. It is also listed as a package to be installed by Janus app. This is a quick fix to prevent accidental uninstall of searx package, when Janus app is uninstalled. Closes #2298. Tests: - Install Searx. - Install Janus, and then uninstall it. searx package is still installed. Searx diagnostics are passed. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- plinth/modules/searx/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plinth/modules/searx/__init__.py b/plinth/modules/searx/__init__.py index 9a51a9f2c..f0b0fafc9 100644 --- a/plinth/modules/searx/__init__.py +++ b/plinth/modules/searx/__init__.py @@ -57,7 +57,9 @@ class SearxApp(app_module.App): allowed_groups=list(groups)) self.add(shortcut) - packages = Packages('packages-searx', ['searx']) + # Include libjs-bootstrap to prevent accidental uninstall (see + # issue #2298). + packages = Packages('packages-searx', ['searx', 'libjs-bootstrap']) self.add(packages) firewall = Firewall('firewall-searx', info.name, From a179dd302e3481b69077ada06f6ab2a203fc8da7 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Tue, 31 Jan 2023 10:57:24 -0500 Subject: [PATCH 03/26] ikiwiki: Re-run setup for each site after restore Closes: #2028. Tests: - Backup and restore with no sites. - Backup and restore with one wiki and one blog. Confirm that login works for both sites. - Backup from one container, and restore in another container. Confirm that login works for both sites. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- plinth/modules/ikiwiki/__init__.py | 15 +++++++++++++-- plinth/modules/ikiwiki/privileged.py | 7 +++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/plinth/modules/ikiwiki/__init__.py b/plinth/modules/ikiwiki/__init__.py index 72ab46354..02faed584 100644 --- a/plinth/modules/ikiwiki/__init__.py +++ b/plinth/modules/ikiwiki/__init__.py @@ -74,8 +74,8 @@ class IkiwikiApp(app_module.App): groups=groups) self.add(users_and_groups) - backup_restore = BackupRestore('backup-restore-ikiwiki', - **manifest.backup) + backup_restore = IkiwikiBackupRestore('backup-restore-ikiwiki', + **manifest.backup) self.add(backup_restore) def post_init(self): @@ -111,3 +111,14 @@ class IkiwikiApp(app_module.App): super().setup(old_version) privileged.setup() self.enable() + + +class IkiwikiBackupRestore(BackupRestore): + """Component to handle Ikiwiki restore""" + + def restore_post(self, packet): + """Re-run setup for each wiki after restore.""" + super().restore_post(packet) + sites = privileged.get_sites() + for site in sites: + privileged.setup_site(site[0]) diff --git a/plinth/modules/ikiwiki/privileged.py b/plinth/modules/ikiwiki/privileged.py index 2b61e9e5c..8d127d1ef 100644 --- a/plinth/modules/ikiwiki/privileged.py +++ b/plinth/modules/ikiwiki/privileged.py @@ -78,6 +78,13 @@ def create_blog(blog_name: str, admin_name: str, admin_password: str): PERL_UNICODE='AS')) +@privileged +def setup_site(site_name: str): + """Run setup for a site.""" + setup_path = os.path.join(WIKI_PATH, site_name + '.setup') + subprocess.run(['ikiwiki', '-setup', setup_path], check=True) + + @privileged def delete(name: str): """Delete a wiki or blog.""" From b49afbc4ff3c363ff33dbcbb0383df6792af6405 Mon Sep 17 00:00:00 2001 From: nbenedek Date: Sun, 5 Feb 2023 01:12:57 +0100 Subject: [PATCH 04/26] email: Redirect to the app page if roundcube isn't installed If Roundcube is not yet installed and the user clicks on 'Launch web client', redirect to the installation page instead of Apache's Not Found page. Signed-off-by: nbenedek Reviewed-by: Sunil Mohan Adapa --- .../data/etc/apache2/conf-available/email-freedombox.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plinth/modules/email/data/etc/apache2/conf-available/email-freedombox.conf b/plinth/modules/email/data/etc/apache2/conf-available/email-freedombox.conf index 9f99790de..f402a3cb3 100644 --- a/plinth/modules/email/data/etc/apache2/conf-available/email-freedombox.conf +++ b/plinth/modules/email/data/etc/apache2/conf-available/email-freedombox.conf @@ -28,3 +28,9 @@ RewriteRule ^ /plinth/apps/email/config.xml [PT] + +# If Roundcube is not yet installed and the user clicks on 'Launch web client', +# redirect to the installation page instead of Apache's Not Found page. + + ErrorDocument 404 /plinth/apps/roundcube/ + From 982fec5a60bea8a643202d26a77ebd70cc79a87d Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sun, 5 Feb 2023 08:54:53 -0500 Subject: [PATCH 05/26] matrixsynapse: Use yaml.safe_load yaml.load() now requires a Loader= argument. yaml.safe_load() passes SafeLoader to yaml.load(). Fixes: #2315. Tests: - Matrix functional tests passed in testing container. - Matrix functional tests passed in stable container. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- plinth/modules/matrixsynapse/privileged.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plinth/modules/matrixsynapse/privileged.py b/plinth/modules/matrixsynapse/privileged.py index d8b98111e..f0620397c 100644 --- a/plinth/modules/matrixsynapse/privileged.py +++ b/plinth/modules/matrixsynapse/privileged.py @@ -49,7 +49,7 @@ def post_install(): # start with listener config from original homeserver.yaml with open(ORIG_CONF_PATH, encoding='utf-8') as orig_conf_file: - orig_config = yaml.load(orig_conf_file) + orig_config = yaml.safe_load(orig_conf_file) listeners = orig_config['listeners'] for listener in listeners: @@ -77,11 +77,11 @@ def public_registration(command: str) -> Optional[bool]: try: with open(REGISTRATION_CONF_PATH, encoding='utf-8') as reg_conf_file: - config = yaml.load(reg_conf_file) + config = yaml.safe_load(reg_conf_file) except FileNotFoundError: # Check if its set in original conffile. with open(ORIG_CONF_PATH, encoding='utf-8') as orig_conf_file: - orig_config = yaml.load(orig_conf_file) + orig_config = yaml.safe_load(orig_conf_file) config = { 'enable_registration': orig_config.get('enable_registration', False) From d0ea67cde6339457d85a2cbee45dfe1b15dac54a Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 2 Feb 2023 18:31:54 -0800 Subject: [PATCH 06/26] ejabberd: Fix making call connections when using TURN Closes: #2318. We currently set 'restricted: false' to both stun and turn server configuration in ejabberd. This works for stun but for turn, ejabberd assumes that authentication is not needed even though it is needed for our coturn setup. Drop the configuration option entirely as the desired values are already default in both stun and turn cases. Tests: - On a fresh setup, install coturn and ejabberd. In ejabberd.yaml, the stun/turn configuration does not have the restricted option. - On a container without the patch, install coturn and ejabberd. Configuration has restricted option. Apply the patch, ejabberd setup is updated and restricted option is removed. - Test that calls can't be made with TURN with 'restricted: false' set. Changing it to true for TURN configuration allows the calls to be established. Remove the restricted option entirely also works. This was tested by @Znoteer in #2318. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/ejabberd/__init__.py | 2 +- plinth/modules/ejabberd/privileged.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/plinth/modules/ejabberd/__init__.py b/plinth/modules/ejabberd/__init__.py index b4ebd11b1..2cc217a6f 100644 --- a/plinth/modules/ejabberd/__init__.py +++ b/plinth/modules/ejabberd/__init__.py @@ -50,7 +50,7 @@ class EjabberdApp(app_module.App): app_id = 'ejabberd' - _version = 5 + _version = 6 def __init__(self): """Create components for the app.""" diff --git a/plinth/modules/ejabberd/privileged.py b/plinth/modules/ejabberd/privileged.py index d0e9c3bd4..118ad299e 100644 --- a/plinth/modules/ejabberd/privileged.py +++ b/plinth/modules/ejabberd/privileged.py @@ -292,7 +292,6 @@ def _generate_service(uri: str) -> dict: "port": int(port), "type": typ, "transport": transport, - "restricted": False } From 0f484d7eaadba22652dd81d45a421961ec16486d Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 2 Feb 2023 12:58:33 -0800 Subject: [PATCH 07/26] snapshot: Fix issue with snapshot rollbacks Closes: #2144. - '--ambit' seems to a required argument if there is no default subvolume set on the filesystem. Add it to prevent error during rollback. - Description is not a required option for rollback (anymore?) and default descriptions for the two snapshots are more descriptive. Tests: - On a fresh vagrant machine, run snapshot rollback with the patch. It fails. With the patch, rollback succeeds. - The description created for the rollback is the default one 'rollback backup' and 'writable copy of #x'. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/snapshot/privileged.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plinth/modules/snapshot/privileged.py b/plinth/modules/snapshot/privileged.py index f0d18e24a..6facfd790 100644 --- a/plinth/modules/snapshot/privileged.py +++ b/plinth/modules/snapshot/privileged.py @@ -251,7 +251,12 @@ def kill_daemon(): @privileged def rollback(number: str): """Rollback to snapshot.""" - command = [ - 'snapper', 'rollback', '--description', 'created by rollback', number - ] + # "ambit" is not very well documented by snapper. Default ambit is "auto" + # which errors out if default subvolume is not yet set on the filesystem. + # If set, then it acts as "transactional" ambit if the default snapshot is + # readonly, otherwise it acts as "classic". The "classic" behavior is the + # one described snapper(8) man page for rollback behavior. The classic + # behavior when a snapshot number to rollback to is provided is the + # behavior that we desire. + command = ['snapper', '--ambit', 'classic', 'rollback', number] subprocess.run(command, check=True) From f7277cf465157dd08143f8a705ad83b3301c24ac Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 1 Feb 2023 05:21:13 -0800 Subject: [PATCH 08/26] snapshot: Fix mounting /.snapshots subvolume and use automounting Closes: #2085. - Read the list of snapshots and properly determine the full subvolume name to be used for mounting the .snapshots subvolume. - Use systemd .mount units instead of editing fstab. Fstab editing is dangerous and could result in system not booting properly. systemd units are better suited for tool based editing while /etc/fstab is recommended for humans. - Use automount feature provided by systemd using autofs to perform mounting. This means that the backing filesystem is only accessed and mounted when the mount point is accessed by a program. Parse errors in the mount/automount file and incorrect mount parameters are also tolerated well with failure to boot. Tests: - On a fresh Debian Bullseye install with btrfs. Install FreedomBox with the changes, create and delete manual snapshots. Rollback to a snapshot should also work. /.snapshots should contain all the files inside each of the snapshots. - After rebooting into a rolled back snapshot, create/delete and restore to a snapshot should work. /.snapshots should contain all the files inside each of the snapshots. - Introduce an error in .mount file such the mount operation will fail. Reboot the machine. Reboot is successful. /.snapshots is still mounted as autofs. Trying to access /.snapshots will result in error during mount operation. - On a vagrant box without changes. Install freedombox and ensure snapshot app setup has been run. This creates the /etc/fstab entry. Apply the patches. snapshot app will run and remove the mount line in /etc/fstab and create the .mount entry. /.snapshots is still mounted but not because of .automount. After reboot, /.snapshots is mounted with autofs and also with btrfs. Unmounting /.snapshots and then trying to run 'ls /.snapshots' will perform the mount again. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/snapshot/__init__.py | 2 +- plinth/modules/snapshot/privileged.py | 120 +++++++++++++++--- .../modules/snapshot/tests/test_privileged.py | 23 ++++ 3 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 plinth/modules/snapshot/tests/test_privileged.py diff --git a/plinth/modules/snapshot/__init__.py b/plinth/modules/snapshot/__init__.py index d1e6ddf4d..9c281aa9b 100644 --- a/plinth/modules/snapshot/__init__.py +++ b/plinth/modules/snapshot/__init__.py @@ -38,7 +38,7 @@ class SnapshotApp(app_module.App): app_id = 'snapshot' - _version = 4 + _version = 5 can_be_disabled = False diff --git a/plinth/modules/snapshot/privileged.py b/plinth/modules/snapshot/privileged.py index 6facfd790..4691003c4 100644 --- a/plinth/modules/snapshot/privileged.py +++ b/plinth/modules/snapshot/privileged.py @@ -2,16 +2,17 @@ """Configuration helper for filesystem snapshots.""" import os +import pathlib import signal import subprocess import augeas import dbus +from plinth import action_utils from plinth.actions import privileged FSTAB = '/etc/fstab' -AUG_FSTAB = '/files/etc/fstab' DEFAULT_FILE = '/etc/default/snapper' @@ -28,7 +29,11 @@ def setup(old_version: int): command = ['snapper', 'create-config', '/'] subprocess.run(command, check=True) - _add_fstab_entry('/') + if old_version and old_version <= 4: + _remove_fstab_entry('/') + + _add_automount_unit('/') + if old_version == 0: _set_default_config() elif old_version <= 3: @@ -96,37 +101,118 @@ def _set_default_config(): subprocess.run(command, check=True) -def _add_fstab_entry(mount_point): - """Add mountpoint for subvolumes.""" +def _remove_fstab_entry(mount_point): + """Remove mountpoint for subvolumes that was added by previous versions. + + The .snapshots mount is not needed, at least for the recent versions of + snapper. This removal code can be dropped after release of Debian Bullseye + + 1. + """ snapshots_mount_point = os.path.join(mount_point, '.snapshots') aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD) - aug.set('/augeas/load/Fstab/lens', 'Fstab.lns') - aug.set('/augeas/load/Fstab/incl[last() + 1]', FSTAB) + aug.transform('Fstab', '/etc/fstab') + aug.set('/augeas/context', '/files/etc/fstab') aug.load() spec = None - for entry in aug.match(AUG_FSTAB + '/*'): + for entry in aug.match('*'): entry_mount_point = aug.get(entry + '/file') - if entry_mount_point == snapshots_mount_point: - return - if entry_mount_point == mount_point and \ aug.get(entry + '/vfstype') == 'btrfs': spec = aug.get(entry + '/spec') if spec: - aug.set(AUG_FSTAB + '/01/spec', spec) - aug.set(AUG_FSTAB + '/01/file', snapshots_mount_point) - aug.set(AUG_FSTAB + '/01/vfstype', 'btrfs') - aug.set(AUG_FSTAB + '/01/opt', 'subvol') - aug.set(AUG_FSTAB + '/01/opt/value', '.snapshots') - aug.set(AUG_FSTAB + '/01/dump', '0') - aug.set(AUG_FSTAB + '/01/passno', '1') + for entry in aug.match('*'): + if (aug.get(entry + '/spec') == spec + and aug.get(entry + '/file') == snapshots_mount_point + and aug.get(entry + '/vfstype') == 'btrfs' + and aug.get(entry + '/opt') == 'subvol' + and aug.get(entry + '/opt/value') == '.snapshots'): + aug.remove(entry) + aug.save() +def _systemd_path_escape(path): + """Escape a string using systemd path rules.""" + process = subprocess.run(['systemd-escape', '--path', path], + stdout=subprocess.PIPE, check=True) + return process.stdout.decode().strip() + + +def _get_subvolume_path(mount_point): + """Return the subvolume path for .snapshots in a filesystem.""" + # -o causes the list of subvolumes directly under the given mount point + process = subprocess.run(['btrfs', 'subvolume', 'list', '-o', mount_point], + stdout=subprocess.PIPE, check=True) + for line in process.stdout.decode().splitlines(): + entry = line.split() + + # -o also causes the full path of the subvolume to be listed. This can + # -be used directly for mounting. + subvolume_path = entry[-1] + if '/' in subvolume_path: + path_parts = subvolume_path.split('/') + if len(path_parts) != 2 or path_parts[1] != '.snapshots': + continue + elif subvolume_path != '.snapshots': + continue + + return subvolume_path + + raise KeyError(f'.snapshots subvolume not found in {mount_point}') + + +def _add_automount_unit(mount_point): + """Add a systemd automount unit for mounting .snapshots subvolume.""" + aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + + augeas.Augeas.NO_MODL_AUTOLOAD) + aug.transform('Fstab', '/etc/fstab') + aug.set('/augeas/context', '/files/etc/fstab') + aug.load() + + what = None + for entry in aug.match('*'): + entry_mount_point = aug.get(entry + '/file') + if (entry_mount_point == mount_point + and aug.get(entry + '/vfstype') == 'btrfs'): + what = aug.get(entry + '/spec') + + snapshots_mount_point = os.path.join(mount_point, '.snapshots') + unit_name = _systemd_path_escape(snapshots_mount_point) + subvolume = _get_subvolume_path(mount_point) + mount_file = pathlib.Path(f'/etc/systemd/system/{unit_name}.mount') + mount_file.write_text(f'''# SPDX-License-Identifier: AGPL-3.0-or-later + +[Unit] +Description=Mount for Snapshots Subvolume (FreedomBox) +Documentation=man:snapper(8) + +[Mount] +What={what} +Where={snapshots_mount_point} +Type=btrfs +Options=subvol={subvolume} +''') + mount_file = pathlib.Path(f'/etc/systemd/system/{unit_name}.automount') + mount_file.write_text(f'''# SPDX-License-Identifier: AGPL-3.0-or-later + +[Unit] +Description=Automount for Snapshots Subvolume (FreedomBox) +Documentation=man:snapper(8) + +[Automount] +Where={snapshots_mount_point} + +[Install] +WantedBy=local-fs.target +''') + action_utils.service_daemon_reload() + action_utils.service_enable(f'{unit_name}.automount') + + def _parse_number(number): """Parse the char following the number and return status of snapshot.""" is_default = number[-1] in ('+', '*') diff --git a/plinth/modules/snapshot/tests/test_privileged.py b/plinth/modules/snapshot/tests/test_privileged.py new file mode 100644 index 000000000..fafbd3eb9 --- /dev/null +++ b/plinth/modules/snapshot/tests/test_privileged.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +Test module for privileged snapshot operations. +""" + +import pathlib + +import pytest + +from plinth.modules.snapshot import privileged + +systemctl_path = pathlib.Path('/usr/bin/systemctl') +systemd_installed = pytest.mark.skipif(not systemctl_path.exists(), + reason='systemd not available') + + +@pytest.mark.parametrize('input_path,escaped_path', + [('.snapshots', '\\x2esnapshots'), ('/', '-'), + ('/home/user', 'home-user'), (':_.', ':_.')]) +@systemd_installed +def test_systemd_path_escape(input_path, escaped_path): + """Test escaping systemd paths.""" + assert escaped_path == privileged._systemd_path_escape(input_path) From 0408998d968dab2546332cbebe30c7ead82ae8ce Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Tue, 7 Feb 2023 10:52:22 -0800 Subject: [PATCH 09/26] config: Drop RuntimeMaxUse=5% for journal logging Closes: #2313. systemd-journald does not (never did) accept size values given in percent of file system size. Only the defaults work with percent values. Hence our addition of RuntimeMaxUse= as percent value in configuration file did not work. systemd-journald outputs a warning to dmesg and ignores the value. We could change the value to fixed size. We would have to choose a value that works for systems with less memory (such as 1GiB) and that value would serve poorly for systems with more memory. Instead, leaving the default value of 10% for RuntimeMaxUse= might be better. Additional configuration of MaxFileSec=6h and MaxRetentionSec=2day would also ease the burden in most cases for the low memory devices. Considering that people did not report issues with status quo (where the value we have set did not work and default size was used) also suggests that default value will work. Further, /run filesystem itself seems to be allocated only 10% of available memory. Tests: - Without the patch, start a vagrant machine. Notice that dmesg shows the error mentioned in the issue #2313. Apply patch and restart the service. Setup is run for config app. The file /etc/systemd/journald.conf.d/50-freedombox.conf will no longer have the RuntimeMaxUse= directive. - After reboot, dmesg will no longer show the error. systemctl status systemd-journald shows that 10% of the size of /run is the max for journal file. - In config app page, setting various values of log persistence works. - On a fresh container with the patch, initial setup succeeds and journald.conf.d file is setup without the RuntimeMaxUse= directive. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/config/__init__.py | 4 +++- plinth/modules/config/privileged.py | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plinth/modules/config/__init__.py b/plinth/modules/config/__init__.py index c2ebd046a..293168da2 100644 --- a/plinth/modules/config/__init__.py +++ b/plinth/modules/config/__init__.py @@ -31,7 +31,7 @@ class ConfigApp(app_module.App): app_id = 'config' - _version = 4 + _version = 5 can_be_disabled = False @@ -79,6 +79,8 @@ class ConfigApp(app_module.App): if old_version <= 3: privileged.set_logging_mode('volatile') + elif old_version == 4: + privileged.set_logging_mode(privileged.get_logging_mode()) # systemd-journald is socket activated, it may not be running and it # does not support reload. diff --git a/plinth/modules/config/privileged.py b/plinth/modules/config/privileged.py index 24d38de49..ad5b7f58d 100644 --- a/plinth/modules/config/privileged.py +++ b/plinth/modules/config/privileged.py @@ -85,14 +85,13 @@ def set_logging_mode(mode: str): aug = load_augeas() aug.set('Journal/Storage', mode) if mode == 'volatile': - aug.set('Journal/RuntimeMaxUse', '5%') aug.set('Journal/MaxFileSec', '6h') aug.set('Journal/MaxRetentionSec', '2day') else: - aug.remove('Journal/RuntimeMaxUse') aug.remove('Journal/MaxFileSec') aug.remove('Journal/MaxRetentionSec') + aug.remove('Journal/RuntimeMaxUse') JOURNALD_FILE.parent.mkdir(exist_ok=True) aug.save() From 8dce51fa47c7455ed9d46d152866c74e22385553 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Wed, 8 Feb 2023 18:13:12 -0800 Subject: [PATCH 10/26] templates: Show better title for 404 page Closes: #2293. When running in develop mode, the title already contains 'Page not found'. Commit 0881dae66583304494e052dfaddb9e3a784d2994 already ensured that functional tests see this page title and treat it as 404 page. This change ensures that 404 is detected even when not running in develop mode. Tests: - Run freedombox as 'sudo --user=plinth ./run' without the --develop option. Install and disable JSXC. Visit the page /plinth/apps/jsxc/jsxc/. Notice that the page title is 'Page not found - FreedomBox'. - Functional tests for JSXC work even when service is running without --develop option. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/templates/404.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plinth/templates/404.html b/plinth/templates/404.html index 631dddfc4..96741161d 100644 --- a/plinth/templates/404.html +++ b/plinth/templates/404.html @@ -5,6 +5,14 @@ {% load i18n %} +{% block title %} + + {% blocktrans trimmed %} + Page not found - {{ box_name }} + {% endblocktrans %} + +{% endblock %} + {% block content %}

{% trans "404" %}

From 3667e934f12b0328d563fed492d7c05a39ae2921 Mon Sep 17 00:00:00 2001 From: Juan Date: Fri, 10 Feb 2023 11:47:24 +0000 Subject: [PATCH 11/26] Translated using Weblate (Spanish) Currently translated at 100.0% (1484 of 1484 strings) --- plinth/locale/es/LC_MESSAGES/django.po | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plinth/locale/es/LC_MESSAGES/django.po b/plinth/locale/es/LC_MESSAGES/django.po index dd12f5618..043c6ece9 100644 --- a/plinth/locale/es/LC_MESSAGES/django.po +++ b/plinth/locale/es/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-01-30 20:12-0500\n" -"PO-Revision-Date: 2023-01-01 18:49+0000\n" -"Last-Translator: gallegonovato \n" +"PO-Revision-Date: 2023-02-11 12:35+0000\n" +"Last-Translator: Juan \n" "Language-Team: Spanish \n" "Language: es\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.15.1-dev\n" +"X-Generator: Weblate 4.16-dev\n" #: doc/dev/_templates/layout.html:11 msgid "Page source" @@ -946,7 +946,8 @@ msgid "" msgstr "" "Actualmente, en {box_name}, BIND solo sirve para resolver las búsquedas DNS " "solicitadas por otros dispositivos conectados a su red local. Todavía no es " -"compatible con el servicio para compartir la conexión a Internet." +"compatible con el servicio para compartir la conexión a Internet desde " +"{box_name}." #: plinth/modules/bind/__init__.py:40 msgid "BIND" @@ -4593,7 +4594,7 @@ msgid "" "connect to %(box_name)s for their Internet connectivity." msgstr "" "Su %(box_name)s se conecta directamente a Internet y el resto de sus " -"dispositivos acceden a través de su {box_name}." +"dispositivos acceden a través de su %(box_name)s." #: plinth/modules/networks/templates/network_topology_main.html:34 #, python-format From 8f2520b327084f3127632e73d7798ac3b6e0d245 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 9 Feb 2023 08:42:41 -0800 Subject: [PATCH 12/26] backups: Allow selecting a single app from URL when creating backup Take app_id in a URL fragment and fill that as the default selected app in create backup form. This URL can be used in apps to create a backup link. Tests: - Visit /plinth/sys/backups/create/bepasty/. Only bepasty app will be selected. - Visit /plinth/sys/backups/create/foo/. No apps are selected. - Visit /plinth/sys/backups/create/. All apps are selected. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/backups/forms.py | 7 ++++--- plinth/modules/backups/urls.py | 4 ++-- plinth/modules/backups/views.py | 8 ++++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index ac70ad2fa..5b0d9c07f 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -110,9 +110,10 @@ class CreateArchiveForm(forms.Form): components = api.get_all_components_for_backup() choices = _get_app_choices(components) self.fields['selected_apps'].choices = choices - self.fields['selected_apps'].initial = [ - choice[0] for choice in choices - ] + if not self.initial or 'selected_apps' not in self.initial: + self.fields['selected_apps'].initial = [ + choice[0] for choice in choices + ] self.fields['repository'].choices = _get_repository_choices() diff --git a/plinth/modules/backups/urls.py b/plinth/modules/backups/urls.py index 4aace50db..d8876b27f 100644 --- a/plinth/modules/backups/urls.py +++ b/plinth/modules/backups/urls.py @@ -15,8 +15,8 @@ urlpatterns = [ re_path(r'^sys/backups/$', BackupsView.as_view(), name='index'), re_path(r'^sys/backups/(?P[^/]+)/schedule/$', ScheduleView.as_view(), name='schedule'), - re_path(r'^sys/backups/create/$', CreateArchiveView.as_view(), - name='create'), + re_path(r'^sys/backups/create/(?:(?P[1-9a-z\-_]+)/)?$', + CreateArchiveView.as_view(), name='create'), re_path(r'^sys/backups/(?P[^/]+)/download/(?P[^/]+)/$', DownloadArchiveView.as_view(), name='download'), re_path(r'^sys/backups/(?P[^/]+)/delete/(?P[^/]+)/$', diff --git a/plinth/modules/backups/views.py b/plinth/modules/backups/views.py index fdadea719..2dc0e7407 100644 --- a/plinth/modules/backups/views.py +++ b/plinth/modules/backups/views.py @@ -116,6 +116,14 @@ class CreateArchiveView(SuccessMessageMixin, FormView): context['title'] = _('Create a new backup') return context + def get_initial(self): + """Return initialization arguments to the form.""" + initial = super().get_initial() + if 'app_id' in self.kwargs: + initial['selected_apps'] = [self.kwargs['app_id']] + + return initial + def form_valid(self, form): """Create the archive on valid form submission.""" repository = get_instance(form.cleaned_data['repository']) From f1060fbf6b363ea6e2d2483a4fa1324f07dd2b26 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 9 Feb 2023 08:43:29 -0800 Subject: [PATCH 13/26] app: Add backup and restore menu items to toolbar menu Closes: #2316. Tests: - Go to to power app. There is no menu. - Go to firewall app. There is a menu item but no backup and restore items present. - Go to bepasty app. There are backup and restore menu items in menu. Clicking backup items takes us to create backup page with only Bepasty app selected. Signed-off-by: Sunil Mohan Adapa [jvalleroy: Fix CSS classes for new menu items] Signed-off-by: James Valleroy Reviewed-by: James Valleroy --- plinth/templates/toolbar.html | 14 +++++++++++++- plinth/views.py | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/plinth/templates/toolbar.html b/plinth/templates/toolbar.html index e935f7c5a..ffd03b7a1 100644 --- a/plinth/templates/toolbar.html +++ b/plinth/templates/toolbar.html @@ -21,7 +21,7 @@ {% endif %} - {% if has_diagnostics or show_uninstall %} + {% if has_diagnostics or show_uninstall or has_backup_restore %}