diff --git a/actions/auth-pubtkt b/actions/auth-pubtkt index e06415d28..c24492315 100755 --- a/actions/auth-pubtkt +++ b/actions/auth-pubtkt @@ -27,8 +27,6 @@ import os from OpenSSL import crypto -from plinth import action_utils - KEYS_DIRECTORY = '/etc/apache2/auth-pubtkt-keys' diff --git a/actions/cockpit b/actions/cockpit index b900eb454..513d33528 100755 --- a/actions/cockpit +++ b/actions/cockpit @@ -37,8 +37,6 @@ def parse_arguments(): help='Setup Cockpit configuration') subparser.add_argument('domain_names', nargs='*', help='Domain names to be allowed') - subparsers.add_parser('enable', help='Enable Cockpit') - subparsers.add_parser('disable', help='Disable Cockpit') subparser = subparsers.add_parser( 'add-domain', help='Allow a new domain to be origin for Cockpit\'s WebSocket') @@ -67,16 +65,6 @@ def subcommand_setup(arguments): action_utils.service_restart('cockpit.socket') -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('cockpit.socket') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('cockpit.socket') - - def _get_origin_domains(aug): """Return the list of allowed origin domains.""" origins = aug.get('/files' + CONFIG_FILE + '/WebService/Origins') diff --git a/actions/coquelicot b/actions/coquelicot index 7f1fca3eb..835d50db8 100755 --- a/actions/coquelicot +++ b/actions/coquelicot @@ -39,8 +39,6 @@ def parse_arguments(): subparsers.add_parser('setup', help='Post-installation operations for coquelicot') - subparsers.add_parser('enable', help='Enable coquelicot') - subparsers.add_parser('disable', help='Disable coquelicot') subparsers.add_parser( 'set-upload-password', @@ -70,16 +68,6 @@ def subcommand_setup(_): action_utils.service_restart('coquelicot') -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('coquelicot') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('coquelicot') - - def subcommand_set_upload_password(arguments): """Set a new upload password for Coquelicot.""" upload_password = ''.join(sys.stdin) diff --git a/actions/deluge b/actions/deluge index 9b7885d57..b282dc425 100755 --- a/actions/deluge +++ b/actions/deluge @@ -52,25 +52,13 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('enable', help='Enable deluge-web site') - subparsers.add_parser('disable', help='Disable deluge-web site') + subparsers.add_parser('setup', help='Setup deluge') subparsers.required = True return parser.parse_args() -def subcommand_enable(_): - """Enable deluge-web site and start deluge-web.""" - setup() - action_utils.service_enable('deluge-web') - - -def subcommand_disable(_): - """Disable deluge-web site and stop deluge-web.""" - action_utils.service_disable('deluge-web') - - -def setup(): +def subcommand_setup(_): """Perform initial setup for deluge-web.""" if not os.path.isfile(SYSTEMD_SERVICE_PATH): with open(SYSTEMD_SERVICE_PATH, 'w') as file_handle: diff --git a/actions/diaspora b/actions/diaspora index 79321735a..4a4795bae 100755 --- a/actions/diaspora +++ b/actions/diaspora @@ -38,8 +38,6 @@ def parse_arguments(): 'pre-install', help='Preseed debconf values before packages are installed.') - subparsers.add_parser('enable', help='Enable diaspora* web server') - subparsers.add_parser('disable', help='Disable diaspora* web server') subparsers.add_parser('enable-user-registrations', help='Allow users to' \ 'sign up to this diaspora* pod without an invitation.') subparsers.add_parser('disable-user-registrations', help='Allow only, ' \ @@ -148,16 +146,6 @@ def subcommand_pre_install(_): subprocess.check_output(['debconf-set-selections'], input=preset) -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('diaspora') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('diaspora') - - def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() diff --git a/actions/ejabberd b/actions/ejabberd index e44e2f031..7700206d5 100755 --- a/actions/ejabberd +++ b/actions/ejabberd @@ -58,9 +58,6 @@ def parse_arguments(): # Setup ejabberd configuration subparsers.add_parser('setup', help='Setup ejabberd configuration') - subparsers.add_parser('enable', help='Enable XMPP service') - subparsers.add_parser('disable', help='Disable XMPP service') - # Prepare ejabberd for hostname change pre_hostname_change = subparsers.add_parser( 'pre-change-hostname', help='Prepare ejabberd for nodename change') @@ -162,16 +159,6 @@ def upgrade_config(): ruamel.yaml.round_trip_dump(conf, file_handle) -def subcommand_enable(_): - """Enable XMPP service""" - action_utils.service_enable('ejabberd') - - -def subcommand_disable(_): - """Disable XMPP service""" - action_utils.service_disable('ejabberd') - - def subcommand_pre_change_hostname(arguments): """Prepare ejabberd for hostname change""" if not shutil.which('ejabberdctl'): diff --git a/actions/i2p b/actions/i2p index f53e87c38..ed5296970 100755 --- a/actions/i2p +++ b/actions/i2p @@ -22,7 +22,7 @@ Wrapper to list and handle system services import argparse import os -from plinth import action_utils, cfg +from plinth import cfg from plinth.modules.i2p.helpers import RouterEditor, TunnelEditor cfg.read() @@ -36,9 +36,6 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('enable', help='enable i2p service') - subparsers.add_parser('disable', help='disable i2p service') - subparser = subparsers.add_parser( 'add-favorite', help='Add an eepsite to the list of favorites') subparser.add_argument('--name', help='Name of the entry', required=True) @@ -58,16 +55,6 @@ def parse_arguments(): return parser.parse_args() -def subcommand_enable(_): - """Enable I2P service.""" - action_utils.service_enable('i2p') - - -def subcommand_disable(_): - """Disable I2P service.""" - action_utils.service_disable('i2p') - - def subcommand_set_tunnel_property(arguments): """Modify the configuration file for a certain tunnel.""" editor = TunnelEditor() diff --git a/actions/ikiwiki b/actions/ikiwiki index 7fed468da..e753a684f 100755 --- a/actions/ikiwiki +++ b/actions/ikiwiki @@ -25,8 +25,6 @@ import shutil import subprocess import sys -from plinth import action_utils - SETUP_WIKI = '/etc/ikiwiki/plinth-wiki.setup' SETUP_BLOG = '/etc/ikiwiki/plinth-blog.setup' SITE_PATH = '/var/www/ikiwiki' diff --git a/actions/matrixsynapse b/actions/matrixsynapse index 3c153cefd..7a689bd61 100755 --- a/actions/matrixsynapse +++ b/actions/matrixsynapse @@ -39,8 +39,6 @@ def parse_arguments(): subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') subparsers.add_parser('post-install', help='Perform post install steps') - subparsers.add_parser('enable', help='Enable matrix-synapse service') - subparsers.add_parser('disable', help='Disable matrix-synapse service') help_pubreg = 'Enable/Disable/Status public user registration.' pubreg = subparsers.add_parser('public-registration', help=help_pubreg) pubreg.add_argument('command', choices=('enable', 'disable', 'status'), @@ -166,17 +164,6 @@ def subcommand_setup(arguments): action_utils.dpkg_reconfigure('matrix-synapse', {'server-name': domain_name}) _update_tls_certificate() - subcommand_enable(arguments) - - -def subcommand_enable(_): - """Enable service.""" - action_utils.service_enable('matrix-synapse') - - -def subcommand_disable(_): - """Disable service.""" - action_utils.service_disable('matrix-synapse') def subcommand_public_registration(argument): diff --git a/actions/mediawiki b/actions/mediawiki index f4d76f40d..fe2306bf1 100755 --- a/actions/mediawiki +++ b/actions/mediawiki @@ -25,7 +25,6 @@ import subprocess import sys import tempfile -from plinth import action_utils from plinth.utils import generate_password, grep MAINTENANCE_SCRIPTS_DIR = "/usr/share/mediawiki/maintenance" @@ -38,8 +37,6 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('enable', help='Enable MediaWiki') - subparsers.add_parser('disable', help='Disable MediaWiki') subparsers.add_parser('setup', help='Setup MediaWiki') subparsers.add_parser('update', help='Run MediaWiki update script') @@ -116,16 +113,6 @@ def subcommand_update(_): subprocess.check_call(['php', update_script]) -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('mediawiki-jobrunner') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('mediawiki-jobrunner') - - def subcommand_public_registrations(arguments): """Enable or Disable public registrations for MediaWiki.""" diff --git a/actions/mldonkey b/actions/mldonkey index 502e8ffb4..942dadeb1 100755 --- a/actions/mldonkey +++ b/actions/mldonkey @@ -23,8 +23,6 @@ Configuration helper for mldonkey. import argparse import subprocess -from plinth import action_utils - def parse_arguments(): """Return parsed command line arguments as dictionary.""" @@ -32,8 +30,6 @@ def parse_arguments(): subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') subparsers.add_parser('pre-install', help='Perform pre-install operations') - subparsers.add_parser('enable', help='Enable mldonkey') - subparsers.add_parser('disable', help='Disable mldonkey') subparsers.required = True return parser.parse_args() @@ -46,16 +42,6 @@ def subcommand_pre_install(_): ], input=b'mldonkey-server mldonkey-server/launch_at_startup boolean true') -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('mldonkey-server') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('mldonkey-server') - - def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() diff --git a/actions/radicale b/actions/radicale index 0fc97e689..f92c6f5fc 100755 --- a/actions/radicale +++ b/actions/radicale @@ -44,8 +44,8 @@ def parse_arguments(): subparsers.add_parser('setup', help='Setup Radicale configuration') subparsers.add_parser('migrate', help='Migrate config to radicale 2.x') - subparsers.add_parser('enable', help='Enable Radicale service') - subparsers.add_parser('disable', help='Disable Radicale service') + subparsers.add_parser('fix-collections', + help='Ensure collections path exists') configure = subparsers.add_parser('configure', help='Configure various options') configure.add_argument('--rights_type', @@ -77,8 +77,6 @@ def subcommand_setup(_): aug.save() - subcommand_enable(None) - def subcommand_migrate(_): """Migrate from radicale 1.x to 2.x.""" @@ -121,23 +119,11 @@ def subcommand_configure(arguments): action_utils.service_try_restart('radicale') -def subcommand_enable(_): - """Start service.""" - if radicale.get_package_version() >= radicale.VERSION_2: - # Workaround for bug in radicale's uwsgi script (#919339) - if not os.path.exists(COLLECTIONS_PATH): - os.makedirs(COLLECTIONS_PATH) - action_utils.service_disable('radicale') - else: - action_utils.service_enable('radicale') - action_utils.service_restart('radicale') - - -def subcommand_disable(_): - """Stop service.""" - package_version = radicale.get_package_version() - if package_version and package_version < radicale.VERSION_2: - action_utils.service_disable('radicale') +def subcommand_fix_collections(_): + """Fix collections path to work around a bug.""" + # Workaround for bug in radicale's uwsgi script (#919339) + if not os.path.exists(COLLECTIONS_PATH): + os.makedirs(COLLECTIONS_PATH) def load_augeas(): diff --git a/actions/syncthing b/actions/syncthing index f113e6e8a..6fcfdbd24 100755 --- a/actions/syncthing +++ b/actions/syncthing @@ -26,22 +26,19 @@ import pwd import shutil import subprocess -from plinth import action_utils - def parse_arguments(): """Return parsed command line arguments as dictionary.""" parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('enable', help='Enable Syncthing') - subparsers.add_parser('disable', help='Disable Syncthing') + subparsers.add_parser('setup', help='Setup Syncthing') subparsers.required = True return parser.parse_args() -def setup(): +def subcommand_setup(_): """Actions to be performed before installing Syncthing""" data_dir = '/var/lib/syncthing' @@ -66,17 +63,6 @@ def setup(): shutil.chown(data_dir, user='syncthing', group='syncthing') -def subcommand_enable(_): - """Enable web configuration and reload.""" - setup() - action_utils.service_enable('syncthing@syncthing') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('syncthing@syncthing') - - def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() diff --git a/actions/tahoe-lafs b/actions/tahoe-lafs index 829072eeb..5630d05a0 100755 --- a/actions/tahoe-lafs +++ b/actions/tahoe-lafs @@ -30,7 +30,6 @@ import subprocess import augeas import ruamel.yaml -from plinth import action_utils from plinth.modules.tahoe import (introducer_furl_file, introducer_name, introducers_file, storage_node_name, tahoe_home) @@ -47,8 +46,6 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('enable', help='Enable Tahoe-LAFS') - subparsers.add_parser('disable', help='Disable Tahoe-LAFS') setup = subparsers.add_parser('setup', help='Set domain name for Tahoe-LAFS') setup.add_argument('--domain-name', @@ -224,16 +221,6 @@ def subcommand_get_local_introducer(_): print(json.dumps((introducer_name, furl))) -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('tahoe-lafs') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('tahoe-lafs') - - def restart_storage_node(): """Called after exiting context of editing introducers file.""" try: diff --git a/actions/tor b/actions/tor index b03b71b62..96ec812a0 100755 --- a/actions/tor +++ b/actions/tor @@ -20,19 +20,19 @@ Configuration helper for the Tor service """ import argparse -import augeas import codecs import json import os import re import socket -import time import subprocess +import time + +import augeas from plinth import action_utils -from plinth.modules.tor.utils import get_real_apt_uri_path, iter_apt_uris, \ - get_augeas, is_running, is_enabled, \ - APT_TOR_PREFIX +from plinth.modules.tor.utils import (APT_TOR_PREFIX, get_augeas, + get_real_apt_uri_path, iter_apt_uris) SERVICE_FILE = '/etc/firewalld/services/tor-{0}.xml' TOR_CONFIG = '/files/etc/tor/instances/plinth/torrc' @@ -212,7 +212,8 @@ def subcommand_configure(arguments): def subcommand_restart(_): """Restart Tor.""" - if is_enabled() and is_running(): + if (action_utils.service_is_enabled('tor@plinth', strict_check=True) + and action_utils.service_is_running('tor@plinth')): action_utils.service_restart('tor@plinth') aug = augeas_load() @@ -527,8 +528,8 @@ def _update_ports(): def augeas_load(): """Initialize Augeas.""" - aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + - augeas.Augeas.NO_MODL_AUTOLOAD) + aug = augeas.Augeas( + flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD) aug.set('/augeas/load/Tor/lens', 'Tor.lns') aug.set('/augeas/load/Tor/incl[last() + 1]', '/etc/tor/instances/plinth/torrc') diff --git a/actions/transmission b/actions/transmission index 6445aa036..fa6418d68 100755 --- a/actions/transmission +++ b/actions/transmission @@ -15,7 +15,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Configuration helper for Transmission daemon. """ @@ -26,7 +25,6 @@ import sys from plinth import action_utils - TRANSMISSION_CONFIG = '/etc/transmission-daemon/settings.json' @@ -35,10 +33,8 @@ def parse_arguments(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - subparsers.add_parser('enable', help='Enable Transmission service') - subparsers.add_parser('disable', help='Disable Transmission service') - subparsers.add_parser( - 'get-configuration', help='Return the current configuration') + subparsers.add_parser('get-configuration', + help='Return the current configuration') subparsers.add_parser( 'merge-configuration', help='Merge JSON configuration from stdin with existing') @@ -47,16 +43,6 @@ def parse_arguments(): return parser.parse_args() -def subcommand_enable(_): - """Start Transmission service.""" - action_utils.service_enable('transmission-daemon') - - -def subcommand_disable(_): - """Stop Transmission service.""" - action_utils.service_disable('transmission-daemon') - - def subcommand_get_configuration(_): """Return the current configuration in JSON format.""" configuration = open(TRANSMISSION_CONFIG, 'r').read() diff --git a/actions/ttrss b/actions/ttrss index d519f565a..012f662e6 100755 --- a/actions/ttrss +++ b/actions/ttrss @@ -40,8 +40,7 @@ def parse_arguments(): subparsers.add_parser('pre-setup', help='Perform pre-setup operations') subparsers.add_parser('setup', help='Setup Tiny Tiny RSS configuration') - subparsers.add_parser('enable', help='Enable Tiny Tiny RSS site') - subparsers.add_parser('disable', help='Disable Tiny Tiny RSS site') + subparsers.add_parser('enable-api-access', help='Enable Tiny Tiny RSS API') subparsers.add_parser('dump-database', help='Dump database to file') subparsers.add_parser('restore-database', help='Restore database from file') @@ -84,7 +83,7 @@ def subcommand_setup(_): action_utils.service_restart('tt-rss') -def enable_api_access(): +def subcommand_enable_api_access(_): """Enable API access so that tt-rss can be accessed through mobile app.""" import psycopg2 # Only available post installation @@ -111,17 +110,6 @@ def enable_api_access(): connection.close() -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('tt-rss') - enable_api_access() - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('tt-rss') - - def subcommand_dump_database(_): """Dump database to file.""" os.makedirs(os.path.dirname(DB_BACKUP_FILE), exist_ok=True) diff --git a/actions/udiskie b/actions/udiskie deleted file mode 100755 index 2b249a163..000000000 --- a/actions/udiskie +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python3 -# -*- mode: python -*- -# -# This file is part of FreedomBox. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -""" -Configuration helper for udiskie. -""" - -import argparse - -from plinth import action_utils - - -def parse_arguments(): - """Return parsed command line arguments as dictionary.""" - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') - - subparsers.add_parser('enable', help='Enable udiskie') - subparsers.add_parser('disable', help='Disable udiskie') - - subparsers.required = True - return parser.parse_args() - - -def subcommand_enable(_): - """Enable web configuration and reload.""" - action_utils.service_enable('freedombox-udiskie') - - -def subcommand_disable(_): - """Disable web configuration and reload.""" - action_utils.service_disable('freedombox-udiskie') - - -def main(): - """Parse arguments and perform all duties.""" - arguments = parse_arguments() - - subcommand = arguments.subcommand.replace('-', '_') - subcommand_method = globals()['subcommand_' + subcommand] - subcommand_method(arguments) - - -if __name__ == '__main__': - main() diff --git a/plinth/__main__.py b/plinth/__main__.py index bd65554ff..39392fcb5 100644 --- a/plinth/__main__.py +++ b/plinth/__main__.py @@ -24,7 +24,7 @@ import sys import axes from . import (__version__, cfg, dbus, frontpage, log, menu, module_loader, - service, setup, web_framework, web_server) + setup, web_framework, web_server) axes.default_app_config = "plinth.axes_app_config.AppConfig" precedence_commandline_arguments = ["server_dir", "develop"] diff --git a/plinth/app.py b/plinth/app.py index e17a65d2e..60411a980 100644 --- a/plinth/app.py +++ b/plinth/app.py @@ -64,6 +64,12 @@ class App: """Return a component given the component's ID.""" return self.components[component_id] + def get_components_of_type(self, component_type): + """Return all components of a given type.""" + for component in self.components.values(): + if isinstance(component, component_type): + yield component + def enable(self): """Enable all the components of the app.""" for component in self.components.values(): @@ -75,7 +81,10 @@ class App: component.disable() def is_enabled(self): - """Return whether all the leader components are enabled.""" + """Return whether all the leader components are enabled. + + Return True when there are no leader components. + """ return all((component.is_enabled() for component in self.components.values() if component.is_leader)) diff --git a/plinth/daemon.py b/plinth/daemon.py new file mode 100644 index 000000000..604502d64 --- /dev/null +++ b/plinth/daemon.py @@ -0,0 +1,65 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Component for managing a background daemon or any systemd unit. +""" + +from plinth import action_utils, actions, app + + +class Daemon(app.LeaderComponent): + """Component to manage a background daemon or any systemd unit.""" + + def __init__(self, component_id, unit, strict_check=False): + """Initialize a new daemon component. + + 'component_id' must be a unique string across all apps and components + of a app. Conventionally starts with 'daemon-'. + + 'unit' must the name of systemd unit that this component should manage. + + """ + super().__init__(component_id) + + self.unit = unit + self.strict_check = strict_check + + def is_enabled(self): + """Return if the daemon/unit is enabled.""" + return action_utils.service_is_enabled(self.unit, + strict_check=self.strict_check) + + def enable(self): + """Run operations to enable the daemon/unit.""" + actions.superuser_run('service', ['enable', self.unit]) + + def disable(self): + """Run operations to disable the daemon/unit.""" + actions.superuser_run('service', ['disable', self.unit]) + + def is_running(self): + """Return whether the daemon/unit is running.""" + return action_utils.service_is_running(self.unit) + + +def app_is_running(app_): + """Return whether all the daemons in the app are running.""" + for component in app_.components.values(): + if hasattr(component, 'is_running') and not component.is_running(): + return False + + return True diff --git a/plinth/forms.py b/plinth/forms.py index a315b331f..af56214ea 100644 --- a/plinth/forms.py +++ b/plinth/forms.py @@ -26,15 +26,15 @@ from django.conf import settings from django.forms import CheckboxInput from django.utils import translation from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ from django.utils.translation import get_language_info +from django.utils.translation import ugettext_lazy as _ import plinth from plinth import utils -class ServiceForm(forms.Form): - """Generic configuration form for a service.""" +class AppForm(forms.Form): + """Generic configuration form for an app.""" is_enabled = forms.BooleanField( label=_('Enable application'), required=False) diff --git a/plinth/modules/avahi/__init__.py b/plinth/modules/avahi/__init__.py index e20177a96..d01bc6a2f 100644 --- a/plinth/modules/avahi/__init__.py +++ b/plinth/modules/avahi/__init__.py @@ -23,10 +23,10 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth import app as app_module from plinth import cfg, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup @@ -53,8 +53,6 @@ description = [ 'hostile local network.'), box_name=_(cfg.box_name)) ] -service = None - manual_page = 'ServiceDiscovery' app = None @@ -76,6 +74,9 @@ class AvahiApp(app_module.App): is_external=False) self.add(firewall) + daemon = Daemon('daemon-avahi', managed_services[0]) + self.add(daemon) + def init(): """Intialize the service discovery module.""" @@ -83,9 +84,6 @@ def init(): app = AvahiApp() app.set_enabled(True) - global service # pylint: disable=W0603 - service = service_module.Service(managed_services[0], name) - def setup(helper, old_version=None): """Install and configure the module.""" @@ -97,7 +95,8 @@ def setup(helper, old_version=None): ['reload', 'avahi-daemon']) -class AvahiServiceView(ServiceView): - service_id = managed_services[0] +class AvahiAppView(AppView): + app_id = 'avahi' + name = name description = description manual_page = manual_page diff --git a/plinth/modules/avahi/urls.py b/plinth/modules/avahi/urls.py index 4f35c7574..685d49446 100644 --- a/plinth/modules/avahi/urls.py +++ b/plinth/modules/avahi/urls.py @@ -14,17 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the service discovery module. """ from django.conf.urls import url -from plinth.modules.avahi import AvahiServiceView - +from plinth.modules.avahi import AvahiAppView urlpatterns = [ - url(r'^sys/avahi/$', AvahiServiceView.as_view(), - name='index'), + url(r'^sys/avahi/$', AvahiAppView.as_view(), name='index'), ] diff --git a/plinth/modules/backups/__init__.py b/plinth/modules/backups/__init__.py index 2d32de583..fb10323a0 100644 --- a/plinth/modules/backups/__init__.py +++ b/plinth/modules/backups/__init__.py @@ -41,8 +41,6 @@ description = [ _('Backups allows creating and managing backup archives.'), ] -service = None - manual_page = 'Backups' MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/' @@ -74,9 +72,8 @@ def init(): global app app = BackupsApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): app.set_enabled(True) diff --git a/plinth/modules/backups/templates/backups.html b/plinth/modules/backups/templates/backups.html index 0472214e0..27cfc1233 100644 --- a/plinth/modules/backups/templates/backups.html +++ b/plinth/modules/backups/templates/backups.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/backups/templates/backups_form.html b/plinth/modules/backups/templates/backups_form.html index 386d69534..9d466d606 100644 --- a/plinth/modules/backups/templates/backups_form.html +++ b/plinth/modules/backups/templates/backups_form.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/backups/templates/backups_upload.html b/plinth/modules/backups/templates/backups_upload.html index 0623caf99..319bdd964 100644 --- a/plinth/modules/backups/templates/backups_upload.html +++ b/plinth/modules/backups/templates/backups_upload.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/bind/__init__.py b/plinth/modules/bind/__init__.py index 02bc421cd..8b76ccb98 100644 --- a/plinth/modules/bind/__init__.py +++ b/plinth/modules/bind/__init__.py @@ -25,7 +25,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -37,8 +37,6 @@ name = _('BIND') short_description = _('Domain Name Server') -service = None - managed_services = ['bind9'] managed_packages = ['bind9'] @@ -104,26 +102,23 @@ class BindApp(app_module.App): is_external=False) self.add(firewall) + daemon = Daemon('daemon-bind', managed_services[0]) + self.add(daemon) + def init(): """Intialize the BIND module.""" global app app = BindApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name) - app.set_enabled(True) # XXX: Perform better check + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) helper.call('post', actions.superuser_run, 'bind', ['setup']) helper.call('post', app.enable) @@ -133,18 +128,6 @@ def force_upgrade(helper, _packages): helper.install(managed_packages, force_configuration='old') -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/bind/forms.py b/plinth/modules/bind/forms.py index e6924cd39..955ed4db3 100644 --- a/plinth/modules/bind/forms.py +++ b/plinth/modules/bind/forms.py @@ -22,7 +22,7 @@ from django import forms from django.core.validators import validate_ipv46_address from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm def validate_ips(ips): @@ -31,7 +31,7 @@ def validate_ips(ips): validate_ipv46_address(ip_addr) -class BindForm(ServiceForm): +class BindForm(AppForm): """BIND configuration form""" forwarders = forms.CharField( label=_('Forwarders'), required=False, validators=[validate_ips], diff --git a/plinth/modules/bind/urls.py b/plinth/modules/bind/urls.py index 14489b6f8..b679873ac 100644 --- a/plinth/modules/bind/urls.py +++ b/plinth/modules/bind/urls.py @@ -20,6 +20,8 @@ URLs for the BIND module. from django.conf.urls import url -from plinth.modules.bind.views import BindServiceView +from plinth.modules.bind.views import BindAppView -urlpatterns = [url(r'^sys/bind/$', BindServiceView.as_view(), name='index'), ] +urlpatterns = [ + url(r'^sys/bind/$', BindAppView.as_view(), name='index'), +] diff --git a/plinth/modules/bind/views.py b/plinth/modules/bind/views.py index 5dfbeafcd..e394b396a 100644 --- a/plinth/modules/bind/views.py +++ b/plinth/modules/bind/views.py @@ -22,16 +22,17 @@ from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from plinth import actions -from plinth.views import ServiceView +from plinth.views import AppView -from . import description, get_config, managed_services, port_forwarding_info +from . import description, get_config, name, port_forwarding_info from .forms import BindForm -class BindServiceView(ServiceView): # pylint: disable=too-many-ancestors +class BindAppView(AppView): # pylint: disable=too-many-ancestors """A specialized view for configuring Bind.""" - service_id = managed_services[0] - diagnostics_module_name = "bind" + app_id = 'bind' + diagnostics_module_name = 'bind' + name = name description = description show_status_block = True form_class = BindForm diff --git a/plinth/modules/cockpit/__init__.py b/plinth/modules/cockpit/__init__.py index e99e801a9..7bd47a4a4 100644 --- a/plinth/modules/cockpit/__init__.py +++ b/plinth/modules/cockpit/__init__.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules import names from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -58,8 +58,6 @@ description = [ users_url=reverse_lazy('users:index')), ] -service = None - manual_page = 'Cockpit' app = None @@ -91,21 +89,18 @@ class CockpitApp(app_module.App): webserver = Webserver('webserver-cockpit', 'cockpit-freedombox') self.add(webserver) + daemon = Daemon('daemon-cockpit', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" global app app = CockpitApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) domain_added.connect(on_domain_added) domain_removed.connect(on_domain_removed) @@ -120,32 +115,9 @@ def setup(helper, old_version=None): for domain in domains_of_a_type ] helper.call('post', actions.superuser_run, 'cockpit', ['setup'] + domains) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return (app.is_enabled() - and action_utils.service_is_running('cockpit.socket')) - - -def enable(): - """Enable the module.""" - actions.superuser_run('cockpit', ['enable']) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('cockpit', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/cockpit/urls.py b/plinth/modules/cockpit/urls.py index 0cdea5e49..2abb14456 100644 --- a/plinth/modules/cockpit/urls.py +++ b/plinth/modules/cockpit/urls.py @@ -20,17 +20,15 @@ URLs for Cockpit module. from django.conf.urls import url -from plinth.views import ServiceView from plinth.modules import cockpit +from plinth.views import AppView urlpatterns = [ - url(r'^sys/cockpit/$', - ServiceView.as_view( - service_id=cockpit.managed_services[0], - diagnostics_module_name='cockpit', - description=cockpit.description, - show_status_block=True, - clients=cockpit.clients, - manual_page=cockpit.manual_page), - name='index'), + url( + r'^sys/cockpit/$', + AppView.as_view(app_id='cockpit', name=cockpit.name, + diagnostics_module_name='cockpit', + description=cockpit.description, + show_status_block=True, clients=cockpit.clients, + manual_page=cockpit.manual_page), name='index'), ] diff --git a/plinth/modules/coquelicot/__init__.py b/plinth/modules/coquelicot/__init__.py index d3d3eb9ce..1db51146d 100644 --- a/plinth/modules/coquelicot/__init__.py +++ b/plinth/modules/coquelicot/__init__.py @@ -23,7 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -51,8 +51,6 @@ description = [ 'The default upload password is "test".') ] -service = None - manual_page = 'Coquelicot' app = None @@ -84,58 +82,27 @@ class CoquelicotApp(app_module.App): webserver = Webserver('webserver-coquelicot', 'coquelicot-freedombox') self.add(webserver) + daemon = Daemon('daemon-coquelicot', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" global app app = CoquelicotApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'coquelicot', ['setup']) - helper.call('post', actions.superuser_run, 'coquelicot', ['enable']) - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) helper.call('post', app.enable) -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running('coquelicot') - - -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled('coquelicot') and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('coquelicot', ['enable']) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('coquelicot', ['disable']) - app.disable() - - def get_current_max_file_size(): """Get the current value of maximum file size.""" size = actions.superuser_run('coquelicot', ['get-max-file-size']) diff --git a/plinth/modules/coquelicot/forms.py b/plinth/modules/coquelicot/forms.py index f1b610e0c..5a0db44ca 100644 --- a/plinth/modules/coquelicot/forms.py +++ b/plinth/modules/coquelicot/forms.py @@ -21,10 +21,10 @@ Plinth form for configuring Coquelicot. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm -class CoquelicotForm(ServiceForm): # pylint: disable=W0232 +class CoquelicotForm(AppForm): # pylint: disable=W0232 """Coquelicot configuration form.""" upload_password = forms.CharField( label=_('Upload Password'), diff --git a/plinth/modules/coquelicot/urls.py b/plinth/modules/coquelicot/urls.py index 020d092cb..fbb78b8eb 100644 --- a/plinth/modules/coquelicot/urls.py +++ b/plinth/modules/coquelicot/urls.py @@ -20,8 +20,8 @@ URLs for the coquelicot module. from django.conf.urls import url -from .views import CoquelicotServiceView +from .views import CoquelicotAppView urlpatterns = [ - url(r'^apps/coquelicot/$', CoquelicotServiceView.as_view(), name='index'), + url(r'^apps/coquelicot/$', CoquelicotAppView.as_view(), name='index'), ] diff --git a/plinth/modules/coquelicot/views.py b/plinth/modules/coquelicot/views.py index 9fdb9e737..4eaa91e6b 100644 --- a/plinth/modules/coquelicot/views.py +++ b/plinth/modules/coquelicot/views.py @@ -24,17 +24,19 @@ from django.utils.translation import ugettext as _ from plinth import actions, views from plinth.errors import ActionError from plinth.modules.coquelicot import (clients, description, - get_current_max_file_size, manual_page) + get_current_max_file_size, manual_page, + name) from .forms import CoquelicotForm -class CoquelicotServiceView(views.ServiceView): +class CoquelicotAppView(views.AppView): """Serve configuration page.""" clients = clients + name = name description = description diagnostics_module_name = 'coquelicot' - service_id = 'coquelicot' + app_id = 'coquelicot' form_class = CoquelicotForm show_status_block = True manual_page = manual_page diff --git a/plinth/modules/datetime/__init__.py b/plinth/modules/datetime/__init__.py index 6b8c6f208..a6f5974e4 100644 --- a/plinth/modules/datetime/__init__.py +++ b/plinth/modules/datetime/__init__.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import app as app_module from plinth import menu -from plinth import service as service_module +from plinth.daemon import Daemon from .manifest import backup @@ -45,8 +45,6 @@ description = [ manual_page = 'DateTime' -service = None - app = None @@ -62,6 +60,9 @@ class DateTimeApp(app_module.App): 'datetime:index', parent_url_name='system') self.add(menu_item) + daemon = Daemon('daemon-datetime', managed_services[0]) + self.add(daemon) + def init(): """Intialize the date/time module.""" @@ -69,18 +70,9 @@ def init(): app = DateTimeApp() app.set_enabled(True) - global service - setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name) - def setup(helper, old_version=None): """Install and configure the module.""" - global service - if service is None: - service = service_module.Service(managed_services[0], name) - service.enable() helper.call('post', app.enable) diff --git a/plinth/modules/datetime/forms.py b/plinth/modules/datetime/forms.py index 365b6cc94..654f2a4c5 100644 --- a/plinth/modules/datetime/forms.py +++ b/plinth/modules/datetime/forms.py @@ -14,23 +14,22 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Forms for configuring date and time """ -from django import forms -from django.utils.translation import ugettext_lazy as _ import logging import subprocess -from plinth.forms import ServiceForm +from django import forms +from django.utils.translation import ugettext_lazy as _ +from plinth.forms import AppForm logger = logging.getLogger(__name__) -class DateTimeForm(ServiceForm): +class DateTimeForm(AppForm): """Date/time configuration form.""" time_zone = forms.ChoiceField( label=_('Time Zone'), @@ -41,8 +40,7 @@ class DateTimeForm(ServiceForm): """Initialize the date/time form.""" forms.Form.__init__(self, *args, **kwargs) - time_zone_options = [(zone, zone) - for zone in self.get_time_zones()] + time_zone_options = [(zone, zone) for zone in self.get_time_zones()] # Show not-set option only when time zone is not set current_time_zone = self.initial.get('time_zone') if current_time_zone == 'none': diff --git a/plinth/modules/datetime/urls.py b/plinth/modules/datetime/urls.py index d0f732580..2ca059443 100644 --- a/plinth/modules/datetime/urls.py +++ b/plinth/modules/datetime/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the date and time module """ from django.conf.urls import url -from .views import DateTimeServiceView - +from .views import DateTimeAppView urlpatterns = [ - url(r'^sys/datetime/$', DateTimeServiceView.as_view(), name='index'), + url(r'^sys/datetime/$', DateTimeAppView.as_view(), name='index'), ] diff --git a/plinth/modules/datetime/views.py b/plinth/modules/datetime/views.py index e00093d43..a51906bed 100644 --- a/plinth/modules/datetime/views.py +++ b/plinth/modules/datetime/views.py @@ -25,26 +25,25 @@ from django.utils.translation import ugettext as _ from plinth import actions from plinth.modules import datetime -from plinth.views import ServiceView +from plinth.views import AppView from .forms import DateTimeForm logger = logging.getLogger(__name__) -class DateTimeServiceView(ServiceView): +class DateTimeAppView(AppView): + name = datetime.name description = datetime.description form_class = DateTimeForm - service_id = datetime.managed_services[0] - diagnostics_module_name = "datetime" + app_id = 'datetime' + diagnostics_module_name = 'datetime' manual_page = datetime.manual_page def get_initial(self): - return { - 'is_enabled': self.service.is_enabled(), - 'is_running': self.service.is_running(), - 'time_zone': self.get_current_time_zone() - } + status = super().get_initial() + status['time_zone'] = self.get_current_time_zone() + return status def get_current_time_zone(self): """Get current time zone.""" @@ -61,9 +60,10 @@ class DateTimeServiceView(ServiceView): actions.superuser_run('timezone-change', [new_status['time_zone']]) except Exception as exception: - messages.error(self.request, - _('Error setting time zone: {exception}') - .format(exception=exception)) + messages.error( + self.request, + _('Error setting time zone: {exception}').format( + exception=exception)) else: messages.success(self.request, _('Time zone set')) diff --git a/plinth/modules/deluge/__init__.py b/plinth/modules/deluge/__init__.py index c31bec212..708bf12d9 100644 --- a/plinth/modules/deluge/__init__.py +++ b/plinth/modules/deluge/__init__.py @@ -23,7 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -32,8 +32,6 @@ from .manifest import backup, clients version = 2 -service = None - managed_services = ['deluge-web'] managed_packages = ['deluged', 'deluge-web'] @@ -86,6 +84,9 @@ class DelugeApp(app_module.App): webserver = Webserver('webserver-deluge', 'deluge-plinth') self.add(webserver) + daemon = Daemon('daemon-deluge', managed_services[0]) + self.add(daemon) + def init(): """Initialize the Deluge module.""" @@ -93,45 +94,18 @@ def init(): app = DelugeApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - helper.call('post', actions.superuser_run, 'deluge', ['enable']) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) + helper.call('post', actions.superuser_run, 'deluge', ['setup']) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return (app.is_enabled() and action_utils.service_is_enabled('deluge-web')) - - -def enable(): - """Enable the module.""" - actions.superuser_run('deluge', ['enable']) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('deluge', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/deluge/urls.py b/plinth/modules/deluge/urls.py index e5b8b3123..67cbb9c26 100644 --- a/plinth/modules/deluge/urls.py +++ b/plinth/modules/deluge/urls.py @@ -21,12 +21,13 @@ URLs for the Deluge module. from django.conf.urls import url from plinth.modules import deluge -from plinth.views import ServiceView +from plinth.views import AppView urlpatterns = [ - url(r'^apps/deluge/$', - ServiceView.as_view( - description=deluge.description, diagnostics_module_name="deluge", - clients=deluge.clients, service_id=deluge.managed_services[0], - manual_page=deluge.manual_page), name='index'), + url( + r'^apps/deluge/$', + AppView.as_view(name=deluge.name, description=deluge.description, + diagnostics_module_name='deluge', + clients=deluge.clients, app_id='deluge', + manual_page=deluge.manual_page), name='index'), ] diff --git a/plinth/modules/diagnostics/templates/diagnostics.html b/plinth/modules/diagnostics/templates/diagnostics.html index 324dbf26c..a923b0e1f 100644 --- a/plinth/modules/diagnostics/templates/diagnostics.html +++ b/plinth/modules/diagnostics/templates/diagnostics.html @@ -1,4 +1,4 @@ -{% extends 'simple_service.html' %} +{% extends 'simple_app.html' %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/diaspora/__init__.py b/plinth/modules/diaspora/__init__.py index 2e5e4f128..f8854399e 100644 --- a/plinth/modules/diaspora/__init__.py +++ b/plinth/modules/diaspora/__init__.py @@ -22,7 +22,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.errors import DomainNotRegisteredError from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -55,8 +55,6 @@ name = _('diaspora*') short_description = _('Federated Social Network') -service = None - managed_services = ['diaspora'] managed_packages = ['diaspora'] @@ -103,6 +101,9 @@ class DiasporaApp(app_module.App): webserver = Webserver('webserver-diaspora', 'diaspora-plinth') self.add(webserver) + daemon = Daemon('daemon-diaspora', managed_services[0]) + self.add(daemon) + class Shortcut(frontpage.Shortcut): """Frontpage shortcut to use configured domain name for URL.""" @@ -118,15 +119,9 @@ def init(): global app app = DiasporaApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -139,31 +134,9 @@ def setup(helper, old_version=None): def setup_domain_name(domain_name): actions.superuser_run('diaspora', ['setup', '--domain-name', domain_name]) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) app.enable() -def is_enabled(): - """Return whether the module is enabled.""" - return app.is_enabled() - - -def enable(): - """Enable the module.""" - actions.superuser_run('diaspora', ['enable']) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('diaspora', ['disable']) - app.disable() - - def is_user_registrations_enabled(): """Return whether user registrations are enabled""" with open('/etc/diaspora/diaspora.yml') as f: diff --git a/plinth/modules/diaspora/forms.py b/plinth/modules/diaspora/forms.py index 3adb0e261..2e97ae7e6 100644 --- a/plinth/modules/diaspora/forms.py +++ b/plinth/modules/diaspora/forms.py @@ -21,12 +21,10 @@ Forms for configuring diaspora* from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm -class DiasporaServiceForm(ServiceForm): +class DiasporaAppForm(AppForm): """Service Form with additional fields for diaspora*""" is_user_registrations_enabled = forms.BooleanField( - label=_('Enable new user registrations'), - required=False - ) + label=_('Enable new user registrations'), required=False) diff --git a/plinth/modules/diaspora/templates/diaspora-post-setup.html b/plinth/modules/diaspora/templates/diaspora-post-setup.html index cea3442e8..cc0b8c79d 100644 --- a/plinth/modules/diaspora/templates/diaspora-post-setup.html +++ b/plinth/modules/diaspora/templates/diaspora-post-setup.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/diaspora/urls.py b/plinth/modules/diaspora/urls.py index 302707d0f..201234727 100644 --- a/plinth/modules/diaspora/urls.py +++ b/plinth/modules/diaspora/urls.py @@ -14,18 +14,15 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the diaspora module """ from django.conf.urls import url -from .views import DiasporaSetupView, DiasporaServiceView +from .views import DiasporaAppView, DiasporaSetupView urlpatterns = [ - url(r'^apps/diaspora/setup$', DiasporaSetupView.as_view(), - name='setup'), - url(r'^apps/diaspora/$', DiasporaServiceView.as_view(), - name='index') + url(r'^apps/diaspora/setup$', DiasporaSetupView.as_view(), name='setup'), + url(r'^apps/diaspora/$', DiasporaAppView.as_view(), name='index') ] diff --git a/plinth/modules/diaspora/views.py b/plinth/modules/diaspora/views.py index b7b5f3e12..b8fcfac6c 100644 --- a/plinth/modules/diaspora/views.py +++ b/plinth/modules/diaspora/views.py @@ -27,9 +27,9 @@ from django.views.generic import FormView from plinth.forms import DomainSelectionForm from plinth.modules import diaspora from plinth.utils import get_domain_names -from plinth.views import ServiceView +from plinth.views import AppView -from .forms import DiasporaServiceForm +from .forms import DiasporaAppForm class DiasporaSetupView(FormView): @@ -55,12 +55,13 @@ class DiasporaSetupView(FormView): return context -class DiasporaServiceView(ServiceView): +class DiasporaAppView(AppView): """Show diaspora service page.""" - form_class = DiasporaServiceForm - service_id = diaspora.managed_services[0] + form_class = DiasporaAppForm + app_id = 'diaspora' template_name = 'diaspora-post-setup.html' diagnostics_module_name = 'diaspora' + name = diaspora.name def dispatch(self, request, *args, **kwargs): if not diaspora.is_setup(): @@ -75,14 +76,10 @@ class DiasporaServiceView(ServiceView): def get_initial(self): """Return the status of the service to fill in the form.""" - return { - 'is_enabled': - self.service.is_enabled(), - 'is_user_registrations_enabled': - diaspora.is_user_registrations_enabled(), - 'is_running': - self.service.is_running() - } + status = super().get_initial() + status['is_user_registrations_enabled'] = \ + diaspora.is_user_registrations_enabled() + return status def form_valid(self, form): """Enable/disable user registrations""" diff --git a/plinth/modules/dynamicdns/templates/dynamicdns.html b/plinth/modules/dynamicdns/templates/dynamicdns.html index e15861018..1e8ba3a41 100644 --- a/plinth/modules/dynamicdns/templates/dynamicdns.html +++ b/plinth/modules/dynamicdns/templates/dynamicdns.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/dynamicdns/templates/dynamicdns_configure.html b/plinth/modules/dynamicdns/templates/dynamicdns_configure.html index 2a8115658..1ac338efb 100644 --- a/plinth/modules/dynamicdns/templates/dynamicdns_configure.html +++ b/plinth/modules/dynamicdns/templates/dynamicdns_configure.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/dynamicdns/templates/dynamicdns_status.html b/plinth/modules/dynamicdns/templates/dynamicdns_status.html index 69a25321c..de96582e1 100644 --- a/plinth/modules/dynamicdns/templates/dynamicdns_status.html +++ b/plinth/modules/dynamicdns/templates/dynamicdns_status.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/dynamicdns/views.py b/plinth/modules/dynamicdns/views.py index 737650549..625db4884 100644 --- a/plinth/modules/dynamicdns/views.py +++ b/plinth/modules/dynamicdns/views.py @@ -39,13 +39,15 @@ EMPTYSTRING = 'none' subsubmenu = [{ 'url': reverse_lazy('dynamicdns:index'), 'text': ugettext_lazy('About') -}, { - 'url': reverse_lazy('dynamicdns:configure'), - 'text': ugettext_lazy('Configure') -}, { - 'url': reverse_lazy('dynamicdns:statuspage'), - 'text': ugettext_lazy('Status') -}] +}, + { + 'url': reverse_lazy('dynamicdns:configure'), + 'text': ugettext_lazy('Configure') + }, + { + 'url': reverse_lazy('dynamicdns:statuspage'), + 'text': ugettext_lazy('Status') + }] def index(request): diff --git a/plinth/modules/ejabberd/__init__.py b/plinth/modules/ejabberd/__init__.py index 4a556a979..1852e9b89 100644 --- a/plinth/modules/ejabberd/__init__.py +++ b/plinth/modules/ejabberd/__init__.py @@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules import config from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -63,8 +63,6 @@ clients = clients reserved_usernames = ['ejabberd'] -service = None - manual_page = 'ejabberd' port_forwarding_info = [ @@ -106,20 +104,18 @@ class EjabberdApp(app_module.App): webserver = Webserver('webserver-ejabberd', 'jwchat-plinth') self.add(webserver) + daemon = Daemon('daemon-ejabberd', managed_services[0]) + self.add(daemon) + def init(): """Initialize the ejabberd module""" global app app = EjabberdApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('ejabberd', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) pre_hostname_change.connect(on_pre_hostname_change) post_hostname_change.connect(on_post_hostname_change) @@ -135,31 +131,9 @@ def setup(helper, old_version=None): ['pre-install', '--domainname', domainname]) helper.install(managed_packages) helper.call('post', actions.superuser_run, 'ejabberd', ['setup']) - global service - if service is None: - service = service_module.Service('ejabberd', name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return action_utils.service_is_enabled('ejabberd') - - -def enable(): - """Enable the module.""" - actions.superuser_run('ejabberd', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('ejabberd', ['disable']) - app.disable() - - def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs): """ Backup ejabberd database before hostname is changed. diff --git a/plinth/modules/ejabberd/forms.py b/plinth/modules/ejabberd/forms.py index ea788c2b0..b20c930b4 100644 --- a/plinth/modules/ejabberd/forms.py +++ b/plinth/modules/ejabberd/forms.py @@ -14,27 +14,25 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Forms for configuring Ejabberd. """ -from plinth import forms as plinthForms -from django import forms as djangoForms +from django import forms from django.utils.translation import ugettext_lazy as _ from plinth import cfg +from plinth.forms import AppForm from plinth.utils import format_lazy -class EjabberdForm(plinthForms.ServiceForm): +class EjabberdForm(AppForm): """Ejabberd configuration form.""" - MAM_enabled = djangoForms.BooleanField( - label=_('Enable Message Archive Management'), - required=False, - help_text=format_lazy(_( - 'If enabled, your {box_name} will store chat message histories. ' - 'This allows synchronization of conversations between multiple ' - 'clients, and reading the history of a multi-user chat room. ' - 'It depends on the client settings whether the histories are ' - 'stored as plain text or encrypted.'), box_name=_(cfg.box_name))) + MAM_enabled = forms.BooleanField( + label=_('Enable Message Archive Management'), required=False, + help_text=format_lazy( + _('If enabled, your {box_name} will store chat message histories. ' + 'This allows synchronization of conversations between multiple ' + 'clients, and reading the history of a multi-user chat room. ' + 'It depends on the client settings whether the histories are ' + 'stored as plain text or encrypted.'), box_name=_(cfg.box_name))) diff --git a/plinth/modules/ejabberd/templates/ejabberd.html b/plinth/modules/ejabberd/templates/ejabberd.html index 0938c0a01..c6b69389c 100644 --- a/plinth/modules/ejabberd/templates/ejabberd.html +++ b/plinth/modules/ejabberd/templates/ejabberd.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/ejabberd/urls.py b/plinth/modules/ejabberd/urls.py index ded518137..dbb8ddd7e 100644 --- a/plinth/modules/ejabberd/urls.py +++ b/plinth/modules/ejabberd/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URL for the Ejabberd module """ from django.conf.urls import url -from .views import EjabberdServiceView - +from .views import EjabberdAppView urlpatterns = [ - url(r'^apps/ejabberd/$', EjabberdServiceView.as_view(), name='index') + url(r'^apps/ejabberd/$', EjabberdAppView.as_view(), name='index') ] diff --git a/plinth/modules/ejabberd/views.py b/plinth/modules/ejabberd/views.py index 896014829..bb5d033d6 100644 --- a/plinth/modules/ejabberd/views.py +++ b/plinth/modules/ejabberd/views.py @@ -23,15 +23,16 @@ from django.utils.translation import ugettext as _ from plinth import actions from plinth.modules import config, ejabberd -from plinth.views import ServiceView +from plinth.views import AppView from .forms import EjabberdForm -class EjabberdServiceView(ServiceView): +class EjabberdAppView(AppView): """Show ejabberd as a service.""" - service_id = ejabberd.managed_services[0] + app_id = 'ejabberd' template_name = 'ejabberd.html' + name = ejabberd.name description = ejabberd.description diagnostics_module_name = 'ejabberd' form_class = EjabberdForm @@ -64,11 +65,9 @@ class EjabberdServiceView(ServiceView): messages.info(self.request, _('Setting unchanged')) elif not app_same: if new_status['is_enabled']: - self.service.enable() - messages.success(self.request, _('Application enabled')) + self.app.enable() else: - self.service.disable() - messages.success(self.request, _('Application disabled')) + self.app.disable() if not mam_same: # note ejabberd action "enable" or "disable" restarts, if running @@ -81,7 +80,7 @@ class EjabberdServiceView(ServiceView): messages.success(self.request, _('Message Archive Management disabled')) - return super(ServiceView, self).form_valid(form) + return super().form_valid(form) def is_MAM_enabled(self): """Return whether Message Archive Management (MAM) is enabled.""" diff --git a/plinth/modules/firewall/components.py b/plinth/modules/firewall/components.py index 525a3f1d0..8d0a7040c 100644 --- a/plinth/modules/firewall/components.py +++ b/plinth/modules/firewall/components.py @@ -104,3 +104,8 @@ class Firewall(app.FollowerComponent): ] if not any(enabled_components_on_port): firewall.remove_service(port, zone='external') + + @staticmethod + def get_internal_interfaces(): + """Returns a list of interfaces in a firewall zone.""" + return firewall.get_interfaces('internal') diff --git a/plinth/modules/firewall/templates/firewall.html b/plinth/modules/firewall/templates/firewall.html index b5ee99a8e..86a9b94e3 100644 --- a/plinth/modules/firewall/templates/firewall.html +++ b/plinth/modules/firewall/templates/firewall.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/firewall/views.py b/plinth/modules/firewall/views.py index 2e85e1f08..a8890e3a5 100644 --- a/plinth/modules/firewall/views.py +++ b/plinth/modules/firewall/views.py @@ -20,7 +20,6 @@ FreedomBox app to configure a firewall. from django.template.response import TemplateResponse -import plinth.service as service_module from plinth.modules import firewall from . import components diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index 005f3bae6..b6f25eab3 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -23,7 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.i2p.resources import FAVORITES @@ -58,8 +58,6 @@ clients = clients group = ('i2p', _('Manage I2P application')) -service = None - manual_page = 'I2P' port_forwarding_info = [ @@ -107,6 +105,9 @@ class I2PApp(app_module.App): webserver = Webserver('webserver-i2p', 'i2p-freedombox') self.add(webserver) + daemon = Daemon('daemon-i2p', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" @@ -114,21 +115,16 @@ def init(): app = I2PApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - helper.call('post', disable) + helper.call('post', app.disable) # Add favorites to the configuration for fav in FAVORITES: args = [ @@ -152,38 +148,9 @@ def setup(helper, old_version=None): 'set-tunnel-property', '--name', tunnel, '--property', 'interface', '--value', '0.0.0.0' ]) - helper.call('post', enable) - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - helper.call('post', app.enable) -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running('i2p') - - -def is_enabled(): - """Return whether the module is enabled.""" - return action_utils.service_is_enabled('i2p') and app.is_enabled() - - -def enable(): - """Enable the module.""" - actions.superuser_run('i2p', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('i2p', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/i2p/templates/i2p.html b/plinth/modules/i2p/templates/i2p.html index adf5b2f2e..4eda093dc 100644 --- a/plinth/modules/i2p/templates/i2p.html +++ b/plinth/modules/i2p/templates/i2p.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. @@ -26,8 +26,8 @@ {% if show_status_block %}

{% trans "Status" %}

- {% with service_name=service.name %} - {% if service.is_running %} + {% with service_name=name %} + {% if is_running %} {% blocktrans trimmed %} Service {{ service_name }} is running. @@ -45,7 +45,7 @@ {% block diagnostics %} {% if diagnostics_module_name %} - {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=service.is_enabled %} + {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=is_enabled %} {% endif %} {% endblock %} diff --git a/plinth/modules/i2p/templates/i2p_service.html b/plinth/modules/i2p/templates/i2p_service.html index 364ba0f38..7ddb303c7 100644 --- a/plinth/modules/i2p/templates/i2p_service.html +++ b/plinth/modules/i2p/templates/i2p_service.html @@ -1,4 +1,22 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} +{% comment %} +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +{% endcomment %} {% load i18n %} diff --git a/plinth/modules/i2p/urls.py b/plinth/modules/i2p/urls.py index 8a62a9f76..3229d89df 100644 --- a/plinth/modules/i2p/urls.py +++ b/plinth/modules/i2p/urls.py @@ -23,7 +23,8 @@ from django.conf.urls import url from plinth.modules.i2p import views urlpatterns = [ - url(r'^apps/i2p/$', views.I2PServiceView.as_view(), name='index'), + url(r'^apps/i2p/$', views.I2PAppView.as_view(), name='index'), url(r'^apps/i2p/tunnels/?$', views.TunnelsView.as_view(), name='tunnels'), - url(r'^apps/i2p/torrents/?$', views.TorrentsView.as_view(), name='torrents'), + url(r'^apps/i2p/torrents/?$', views.TorrentsView.as_view(), + name='torrents'), ] diff --git a/plinth/modules/i2p/views.py b/plinth/modules/i2p/views.py index b7e2013c9..68ec2d517 100644 --- a/plinth/modules/i2p/views.py +++ b/plinth/modules/i2p/views.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext_lazy from django.views.generic import TemplateView import plinth.modules.i2p as i2p -from plinth.views import ServiceView +from plinth.views import AppView subsubmenu = [{ 'url': reverse_lazy('i2p:index'), @@ -39,10 +39,11 @@ subsubmenu = [{ }] -class I2PServiceView(ServiceView): +class I2PAppView(AppView): """Serve configuration page.""" - service_id = i2p.service_name + app_id = 'i2p' clients = i2p.clients + name = i2p.name description = i2p.description diagnostics_module_name = i2p.service_name show_status_block = True @@ -74,6 +75,7 @@ class ServiceBaseView(TemplateView): context['clients'] = i2p.clients context['manual_page'] = i2p.manual_page context['subsubmenu'] = subsubmenu + context['is_enabled'] = i2p.app.is_enabled() context['service_title'] = self.service_title context['service_path'] = self.service_path context['service_description'] = self.service_description diff --git a/plinth/modules/ikiwiki/__init__.py b/plinth/modules/ikiwiki/__init__.py index 51bf9c667..18c25c815 100644 --- a/plinth/modules/ikiwiki/__init__.py +++ b/plinth/modules/ikiwiki/__init__.py @@ -24,7 +24,6 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -39,8 +38,6 @@ managed_packages = [ 'libsearch-xapian-perl', 'libimage-magick-perl' ] -service = None - name = _('ikiwiki') short_description = _('Wiki and Blog') @@ -114,43 +111,18 @@ def init(): app = IkiwikiApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('ikiwiki', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'ikiwiki', ['setup']) - global service - if service is None: - service = service_module.Service('ikiwiki', name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return app.is_enabled() - - -def enable(): - """Enable the module.""" - app.enable() - - -def disable(): - """Enable the module.""" - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/ikiwiki/templates/ikiwiki_configure.html b/plinth/modules/ikiwiki/templates/ikiwiki_configure.html index 8b085396e..56ce04cff 100644 --- a/plinth/modules/ikiwiki/templates/ikiwiki_configure.html +++ b/plinth/modules/ikiwiki/templates/ikiwiki_configure.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. @@ -26,7 +26,7 @@ {% block diagnostics %} {% if diagnostics_module_name %} - {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=service.is_enabled %} + {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=is_enabled %} {% endif %} {% endblock %} diff --git a/plinth/modules/ikiwiki/templates/ikiwiki_create.html b/plinth/modules/ikiwiki/templates/ikiwiki_create.html index 64eb28ba4..35d1839c0 100644 --- a/plinth/modules/ikiwiki/templates/ikiwiki_create.html +++ b/plinth/modules/ikiwiki/templates/ikiwiki_create.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/ikiwiki/templates/ikiwiki_manage.html b/plinth/modules/ikiwiki/templates/ikiwiki_manage.html index 8850d435b..00c3881e1 100644 --- a/plinth/modules/ikiwiki/templates/ikiwiki_manage.html +++ b/plinth/modules/ikiwiki/templates/ikiwiki_manage.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/ikiwiki/urls.py b/plinth/modules/ikiwiki/urls.py index 195e5f198..57bed0f7f 100644 --- a/plinth/modules/ikiwiki/urls.py +++ b/plinth/modules/ikiwiki/urls.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the ikiwiki module """ @@ -23,10 +22,8 @@ from django.conf.urls import url from . import views - urlpatterns = [ - url(r'^apps/ikiwiki/$', - views.IkiwikiServiceView.as_view(), name='index'), + url(r'^apps/ikiwiki/$', views.IkiwikiAppView.as_view(), name='index'), url(r'^apps/ikiwiki/manage/$', views.manage, name='manage'), url(r'^apps/ikiwiki/(?P[\w.@+-]+)/delete/$', views.delete, name='delete'), diff --git a/plinth/modules/ikiwiki/views.py b/plinth/modules/ikiwiki/views.py index 2a4ee0399..e230aae4c 100644 --- a/plinth/modules/ikiwiki/views.py +++ b/plinth/modules/ikiwiki/views.py @@ -42,13 +42,14 @@ subsubmenu = [{ }] -class IkiwikiServiceView(views.ServiceView): +class IkiwikiAppView(views.AppView): """Serve configuration page.""" - service_id = "ikiwiki" + app_id = 'ikiwiki' + name = ikiwiki.name description = ikiwiki.description - diagnostics_module_name = "ikiwiki" + diagnostics_module_name = 'ikiwiki' show_status_block = False - template_name = "ikiwiki_configure.html" + template_name = 'ikiwiki_configure.html' def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" @@ -72,7 +73,8 @@ def manage(request): 'description': ikiwiki.description, 'manual_page': ikiwiki.manual_page, 'subsubmenu': subsubmenu, - 'sites': sites + 'sites': sites, + 'is_enabled': ikiwiki.app.is_enabled(), }) @@ -108,6 +110,7 @@ def create(request): 'form': form, 'manual_page': ikiwiki.manual_page, 'subsubmenu': subsubmenu, + 'is_enabled': ikiwiki.app.is_enabled(), }) diff --git a/plinth/modules/infinoted/__init__.py b/plinth/modules/infinoted/__init__.py index 39014c3d1..329c961af 100644 --- a/plinth/modules/infinoted/__init__.py +++ b/plinth/modules/infinoted/__init__.py @@ -24,17 +24,15 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup, clients version = 1 -service = None - managed_services = ['infinoted'] managed_packages = ['infinoted'] @@ -83,24 +81,24 @@ class InfinotedApp(app_module.App): ports=['infinoted-plinth'], is_external=True) self.add(firewall) + daemon = Daemon('daemon-infinoted', managed_services[0]) + self.add(daemon) + def init(): """Initialize the infinoted module.""" global app app = InfinotedApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) -class InfinotedServiceView(ServiceView): - service_id = managed_services[0] - diagnostics_module_name = "infinoted" +class InfinotedAppView(AppView): + app_id = 'infinoted' + diagnostics_module_name = 'infinoted' + name = name description = description clients = clients port_forwarding_info = port_forwarding_info @@ -110,26 +108,9 @@ def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'infinoted', ['setup']) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - helper.call('post', app.enable) -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/infinoted/urls.py b/plinth/modules/infinoted/urls.py index e35be81e2..64a6e2188 100644 --- a/plinth/modules/infinoted/urls.py +++ b/plinth/modules/infinoted/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the infinoted module. """ from django.conf.urls import url -from plinth.modules.infinoted import InfinotedServiceView - +from plinth.modules.infinoted import InfinotedAppView urlpatterns = [ - url(r'^apps/infinoted/$', InfinotedServiceView.as_view(), name='index'), + url(r'^apps/infinoted/$', InfinotedAppView.as_view(), name='index'), ] diff --git a/plinth/modules/jsxc/__init__.py b/plinth/modules/jsxc/__init__.py index 4891b4c1c..cd1036e7b 100644 --- a/plinth/modules/jsxc/__init__.py +++ b/plinth/modules/jsxc/__init__.py @@ -25,7 +25,6 @@ from django.utils.translation import ugettext_lazy as _ from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module from plinth.modules.firewall.components import Firewall from .manifest import backup, clients @@ -45,8 +44,6 @@ description = [ clients = clients -service = None - logger = logging.getLogger(__name__) app = None @@ -79,36 +76,12 @@ def init(): global app app = JSXCApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('jsxc', name, is_enabled=is_enabled, - enable=enable, disable=disable) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - - global service - if not service: - service = service_module.Service('jsxc', name, is_enabled=is_enabled, - enable=enable, disable=disable) - helper.call('post', app.enable) - - -def is_enabled(): - """Return whether the module is enabled.""" - setup_helper = globals()['setup_helper'] - return setup_helper.get_state() != 'needs-setup' - - -def enable(): - app.enable() - - -def disable(): - app.disable() diff --git a/plinth/modules/jsxc/templates/jsxc.html b/plinth/modules/jsxc/templates/jsxc.html index 6f0da05a0..2cb34f379 100644 --- a/plinth/modules/jsxc/templates/jsxc.html +++ b/plinth/modules/jsxc/templates/jsxc.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/jsxc/urls.py b/plinth/modules/jsxc/urls.py index 0c53d14ff..19ff648d0 100644 --- a/plinth/modules/jsxc/urls.py +++ b/plinth/modules/jsxc/urls.py @@ -20,9 +20,9 @@ URLs for the JSXC module from django.conf.urls import url -from .views import JSXCServiceView, JsxcView +from .views import JSXCAppView, JsxcView urlpatterns = [ - url(r'^apps/jsxc/$', JSXCServiceView.as_view(), name='index'), + url(r'^apps/jsxc/$', JSXCAppView.as_view(), name='index'), url(r'^apps/jsxc/jsxc/$', JsxcView.as_view(), name='jsxc') ] diff --git a/plinth/modules/jsxc/views.py b/plinth/modules/jsxc/views.py index bc006410f..5dc674424 100644 --- a/plinth/modules/jsxc/views.py +++ b/plinth/modules/jsxc/views.py @@ -18,18 +18,19 @@ Views for the JSXC module """ -from django.views.generic import TemplateView from django.utils.decorators import method_decorator +from django.views.generic import TemplateView from stronghold.decorators import public from plinth.modules import config, jsxc -from plinth.views import ServiceView +from plinth.views import AppView -class JSXCServiceView(ServiceView): - """Show ejabberd as a service.""" - service_id = 'jsxc' +class JSXCAppView(AppView): + """Show ejabberd as an app.""" + app_id = 'jsxc' template_name = 'jsxc.html' + name = jsxc.name description = jsxc.description show_status_block = False clients = jsxc.clients diff --git a/plinth/modules/letsencrypt/__init__.py b/plinth/modules/letsencrypt/__init__.py index a19d79608..207136dbe 100644 --- a/plinth/modules/letsencrypt/__init__.py +++ b/plinth/modules/letsencrypt/__init__.py @@ -60,8 +60,6 @@ description = [ 'Subscriber Agreement before using this service.') ] -service = None - manual_page = 'LetsEncrypt' MODULES_WITH_HOOKS = ['ejabberd', 'matrixsynapse'] diff --git a/plinth/modules/letsencrypt/templates/letsencrypt.html b/plinth/modules/letsencrypt/templates/letsencrypt.html index 3ea9e6958..b9c160cba 100644 --- a/plinth/modules/letsencrypt/templates/letsencrypt.html +++ b/plinth/modules/letsencrypt/templates/letsencrypt.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/matrixsynapse/__init__.py b/plinth/modules/matrixsynapse/__init__.py index b7974a227..0db44e08a 100644 --- a/plinth/modules/matrixsynapse/__init__.py +++ b/plinth/modules/matrixsynapse/__init__.py @@ -28,7 +28,7 @@ from ruamel.yaml.util import load_yaml_guess_indent from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -60,8 +60,6 @@ description = [ clients = clients -service = None - manual_page = 'MatrixSynapse' port_forwarding_info = [('TCP', 8448)] @@ -103,31 +101,23 @@ class MatrixSynapseApp(app_module.App): 'matrix-synapse-plinth') self.add(webserver) + daemon = Daemon('daemon-matrixsynapse', managed_services[0]) + self.add(daemon) + def init(): """Initialize the matrix-synapse module.""" global app app = MatrixSynapseApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('matrix-synapse', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service('matrix-synapse', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - helper.call('post', actions.superuser_run, 'matrixsynapse', ['post-install']) helper.call('post', app.enable) @@ -138,24 +128,6 @@ def is_setup(): return os.path.exists(SERVER_NAME_PATH) -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled('matrix-synapse') - and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('matrixsynapse', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('matrixsynapse', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/matrixsynapse/forms.py b/plinth/modules/matrixsynapse/forms.py index 7c30f3914..6c180f8f2 100644 --- a/plinth/modules/matrixsynapse/forms.py +++ b/plinth/modules/matrixsynapse/forms.py @@ -14,21 +14,19 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Forms for the Matrix Synapse module. """ from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm + +from plinth.forms import AppForm -class MatrixSynapseForm(ServiceForm): +class MatrixSynapseForm(AppForm): enable_public_registration = forms.BooleanField( - label=_('Enable Public Registration'), - required=False, - help_text=_( + label=_('Enable Public Registration'), required=False, help_text=_( 'Enabling public registration means that anyone on the Internet ' 'can register a new account on your Matrix server. Disable this ' 'if you only want existing users to be able to use it.')) diff --git a/plinth/modules/matrixsynapse/templates/matrix-synapse.html b/plinth/modules/matrixsynapse/templates/matrix-synapse.html index 9a4b832ae..70674c193 100644 --- a/plinth/modules/matrixsynapse/templates/matrix-synapse.html +++ b/plinth/modules/matrixsynapse/templates/matrix-synapse.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/matrixsynapse/urls.py b/plinth/modules/matrixsynapse/urls.py index 7b175e009..788854856 100644 --- a/plinth/modules/matrixsynapse/urls.py +++ b/plinth/modules/matrixsynapse/urls.py @@ -14,17 +14,16 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the matrix-synapse module. """ from django.conf.urls import url -from .views import SetupView, MatrixSynapseServiceView +from .views import MatrixSynapseAppView, SetupView urlpatterns = [ url(r'^apps/matrixsynapse/setup/$', SetupView.as_view(), name='setup'), - url(r'^apps/matrixsynapse/$', MatrixSynapseServiceView.as_view(), + url(r'^apps/matrixsynapse/$', MatrixSynapseAppView.as_view(), name='index'), ] diff --git a/plinth/modules/matrixsynapse/views.py b/plinth/modules/matrixsynapse/views.py index 1e2751586..8d73f20ae 100644 --- a/plinth/modules/matrixsynapse/views.py +++ b/plinth/modules/matrixsynapse/views.py @@ -28,7 +28,7 @@ from plinth import actions from plinth.forms import DomainSelectionForm from plinth.modules import matrixsynapse from plinth.utils import get_domain_names -from plinth.views import ServiceView +from plinth.views import AppView from . import get_public_registration_status, has_valid_certificate from .forms import MatrixSynapseForm @@ -59,10 +59,11 @@ class SetupView(FormView): return context -class MatrixSynapseServiceView(ServiceView): +class MatrixSynapseAppView(AppView): """Show matrix-synapse service page.""" - service_id = matrixsynapse.managed_services[0] + app_id = 'matrixsynapse' template_name = 'matrix-synapse.html' + name = matrixsynapse.name description = matrixsynapse.description diagnostics_module_name = 'matrixsynapse' form_class = MatrixSynapseForm @@ -107,11 +108,9 @@ class MatrixSynapseServiceView(ServiceView): messages.info(self.request, _('Setting unchanged')) elif not app_same: if new_config['is_enabled']: - self.service.enable() - messages.success(self.request, _('Application enabled')) + self.app.enable() else: - self.service.disable() - messages.success(self.request, _('Application disabled')) + self.app.disable() if not pubreg_same: # note action public-registration restarts, if running now @@ -126,4 +125,4 @@ class MatrixSynapseServiceView(ServiceView): messages.success(self.request, _('Public registration disabled')) - return super(ServiceView, self).form_valid(form) + return super().form_valid(form) diff --git a/plinth/modules/mediawiki/__init__.py b/plinth/modules/mediawiki/__init__.py index 9eb220af1..5e60a58c2 100644 --- a/plinth/modules/mediawiki/__init__.py +++ b/plinth/modules/mediawiki/__init__.py @@ -23,7 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -33,6 +33,8 @@ version = 6 managed_packages = ['mediawiki', 'imagemagick', 'php-sqlite3'] +managed_services = ['mediawiki-jobrunner'] + name = _('MediaWiki') short_description = _('Wiki') @@ -52,8 +54,6 @@ description = [ 'logged in can make changes to the content.') ] -service = None - manual_page = 'MediaWiki' clients = clients @@ -93,6 +93,9 @@ class MediaWikiApp(app_module.App): 'mediawiki-freedombox') self.add(webserver) + daemon = Daemon('daemon-mediawiki', managed_services[0]) + self.add(daemon) + class Shortcut(frontpage.Shortcut): """Frontpage shortcut for only logged users when in private mode.""" @@ -108,14 +111,9 @@ def init(): global app app = MediaWikiApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('mediawiki', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -123,32 +121,9 @@ def setup(helper, old_version=None): helper.install(managed_packages) helper.call('setup', actions.superuser_run, 'mediawiki', ['setup']) helper.call('update', actions.superuser_run, 'mediawiki', ['update']) - helper.call('enable', actions.superuser_run, 'mediawiki', ['enable']) - global service - if service is None: - service = service_module.Service('mediawiki', name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return app.is_enabled() - - -def enable(): - """Enable the module.""" - actions.superuser_run('mediawiki', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('mediawiki', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/mediawiki/forms.py b/plinth/modules/mediawiki/forms.py index d4a5343c6..671e4340e 100644 --- a/plinth/modules/mediawiki/forms.py +++ b/plinth/modules/mediawiki/forms.py @@ -21,10 +21,10 @@ FreedomBox app for configuring MediaWiki. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm -class MediaWikiForm(ServiceForm): # pylint: disable=W0232 +class MediaWikiForm(AppForm): # pylint: disable=W0232 """MediaWiki configuration form.""" password = forms.CharField( label=_('Administrator Password'), help_text=_( diff --git a/plinth/modules/mediawiki/templates/mediawiki.html b/plinth/modules/mediawiki/templates/mediawiki.html index 66c041a61..4dbdc6776 100644 --- a/plinth/modules/mediawiki/templates/mediawiki.html +++ b/plinth/modules/mediawiki/templates/mediawiki.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/mediawiki/urls.py b/plinth/modules/mediawiki/urls.py index b7b2ac33c..64dd1556a 100644 --- a/plinth/modules/mediawiki/urls.py +++ b/plinth/modules/mediawiki/urls.py @@ -20,8 +20,8 @@ URLs for the mediawiki module. from django.conf.urls import url -from .views import MediaWikiServiceView +from .views import MediaWikiAppView urlpatterns = [ - url(r'^apps/mediawiki/$', MediaWikiServiceView.as_view(), name='index'), + url(r'^apps/mediawiki/$', MediaWikiAppView.as_view(), name='index'), ] diff --git a/plinth/modules/mediawiki/views.py b/plinth/modules/mediawiki/views.py index 9a927dd54..07f7615e0 100644 --- a/plinth/modules/mediawiki/views.py +++ b/plinth/modules/mediawiki/views.py @@ -26,19 +26,19 @@ from django.utils.translation import ugettext as _ from plinth import actions, views from plinth.modules import mediawiki -from . import (is_enabled, is_private_mode_enabled, - is_public_registration_enabled) +from . import is_private_mode_enabled, is_public_registration_enabled from .forms import MediaWikiForm logger = logging.getLogger(__name__) -class MediaWikiServiceView(views.ServiceView): - """Serve configuration page.""" +class MediaWikiAppView(views.AppView): + """App configuration page.""" clients = mediawiki.clients + name = mediawiki.name description = mediawiki.description diagnostics_module_name = 'mediawiki' - service_id = 'mediawiki' + app_id = 'mediawiki' form_class = MediaWikiForm manual_page = mediawiki.manual_page show_status_block = False @@ -75,11 +75,9 @@ class MediaWikiServiceView(views.ServiceView): messages.info(self.request, _('Setting unchanged')) elif not app_same: if new_config['is_enabled']: - self.service.enable() - messages.success(self.request, _('Application enabled')) + self.app.enable() else: - self.service.disable() - messages.success(self.request, _('Application disabled')) + self.app.disable() if not pub_reg_same: # note action public-registration restarts, if running now diff --git a/plinth/modules/minetest/__init__.py b/plinth/modules/minetest/__init__.py index ccce4a7bd..1252ce3df 100644 --- a/plinth/modules/minetest/__init__.py +++ b/plinth/modules/minetest/__init__.py @@ -22,10 +22,10 @@ import augeas from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from plinth import action_utils, actions +from plinth import action_utils from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -33,8 +33,6 @@ from .manifest import backup, clients version = 2 -service = None - managed_services = ['minetest-server'] mods = [ @@ -102,43 +100,26 @@ class MinetestApp(app_module.App): ports=['minetest-plinth'], is_external=True) self.add(firewall) + daemon = Daemon('daemon-minetest', managed_services[0]) + self.add(daemon) + def init(): """Initialize the module.""" global app app = MinetestApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) helper.call('post', app.enable) -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/minetest/forms.py b/plinth/modules/minetest/forms.py index b6467e86c..31afa21a7 100644 --- a/plinth/modules/minetest/forms.py +++ b/plinth/modules/minetest/forms.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Forms for minetest module. """ @@ -22,34 +21,29 @@ Forms for minetest module. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm -class MinetestForm(ServiceForm): +class MinetestForm(AppForm): """Minetest configuration form""" max_players = forms.IntegerField( - label=_('Maximum number of players'), - required=True, - min_value=1, - max_value=100, - help_text=_('You can change the maximum number of players playing ' - 'minetest at a single instance of time.')) + label=_('Maximum number of players'), required=True, min_value=1, + max_value=100, help_text=_( + 'You can change the maximum number of players playing ' + 'minetest at a single instance of time.')) creative_mode = forms.BooleanField( - label=_('Enable creative mode'), - required=False, + label=_('Enable creative mode'), required=False, help_text=_('Creative mode changes the rules of the game to make it ' 'more suitable for creative gameplay, rather than ' 'challenging "survival" gameplay.')) enable_pvp = forms.BooleanField( - label=_('Enable PVP'), - required=False, + label=_('Enable PVP'), required=False, help_text=_('Enabling Player Vs Player will allow players to damage ' 'other players.')) enable_damage = forms.BooleanField( - label=_('Enable damage'), - required=False, + label=_('Enable damage'), required=False, help_text=_('When disabled, players cannot die or receive damage of ' 'any kind.')) diff --git a/plinth/modules/minetest/templates/minetest.html b/plinth/modules/minetest/templates/minetest.html index 1e54a88e1..4a0fc1acc 100644 --- a/plinth/modules/minetest/templates/minetest.html +++ b/plinth/modules/minetest/templates/minetest.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/minetest/urls.py b/plinth/modules/minetest/urls.py index b5327e670..91d2b8f42 100644 --- a/plinth/modules/minetest/urls.py +++ b/plinth/modules/minetest/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the minetest module. """ from django.conf.urls import url -from plinth.modules.minetest.views import MinetestServiceView - +from plinth.modules.minetest.views import MinetestAppView urlpatterns = [ - url(r'^apps/minetest/$', MinetestServiceView.as_view(), name='index'), + url(r'^apps/minetest/$', MinetestAppView.as_view(), name='index'), ] diff --git a/plinth/modules/minetest/views.py b/plinth/modules/minetest/views.py index 11efe629b..0414d6250 100644 --- a/plinth/modules/minetest/views.py +++ b/plinth/modules/minetest/views.py @@ -23,16 +23,17 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth.modules import minetest, names -from plinth.views import ServiceView +from plinth.views import AppView -from . import description, get_configuration, managed_services +from . import description, get_configuration from .forms import MinetestForm -class MinetestServiceView(ServiceView): # pylint: disable=too-many-ancestors +class MinetestAppView(AppView): # pylint: disable=too-many-ancestors """A specialized view for configuring minetest.""" - service_id = managed_services[0] - diagnostics_module_name = "minetest" + app_id = 'minetest' + diagnostics_module_name = 'minetest' + name = minetest.name description = description show_status_block = True template_name = 'minetest.html' diff --git a/plinth/modules/mldonkey/__init__.py b/plinth/modules/mldonkey/__init__.py index aa54901e0..99b82be0f 100644 --- a/plinth/modules/mldonkey/__init__.py +++ b/plinth/modules/mldonkey/__init__.py @@ -23,7 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -60,8 +60,6 @@ reserved_usernames = ['mldonkey'] group = ('ed2k', _('Download files using eDonkey applications')) -service = None - manual_page = 'MLDonkey' app = None @@ -93,6 +91,9 @@ class MLDonkeyApp(app_module.App): webserver = Webserver('webserver-mldonkey', 'mldonkey-freedombox') self.add(webserver) + daemon = Daemon('daemon-mldonkey', managed_services[0]) + self.add(daemon) + def init(): """Initialize the MLDonkey module.""" @@ -100,53 +101,18 @@ def init(): app = MLDonkeyApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.call('pre', actions.superuser_run, 'mldonkey', ['pre-install']) helper.install(managed_packages) - helper.call('post', actions.superuser_run, 'mldonkey', ['enable']) - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) helper.call('post', app.enable) -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running('mldonkey-server') - - -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled('mldonkey-server') - and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('mldonkey', ['enable']) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('mldonkey', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/mldonkey/urls.py b/plinth/modules/mldonkey/urls.py index f39d53cce..5b5e18483 100644 --- a/plinth/modules/mldonkey/urls.py +++ b/plinth/modules/mldonkey/urls.py @@ -21,16 +21,15 @@ URLs for the mldonkey module. from django.conf.urls import url from plinth.modules import mldonkey -from plinth.views import ServiceView +from plinth.views import AppView urlpatterns = [ url( r'^apps/mldonkey/$', - ServiceView.as_view(service_id=mldonkey.managed_services[0], - diagnostics_module_name='mldonkey', - description=mldonkey.description, - clients=mldonkey.clients, - manual_page=mldonkey.manual_page, - show_status_block=True), + AppView.as_view( + app_id='mldonkey', name=mldonkey.name, + diagnostics_module_name='mldonkey', + description=mldonkey.description, clients=mldonkey.clients, + manual_page=mldonkey.manual_page, show_status_block=True), name='index'), ] diff --git a/plinth/modules/monkeysphere/templates/monkeysphere.html b/plinth/modules/monkeysphere/templates/monkeysphere.html index 9e593c8e2..3f56b7ed0 100644 --- a/plinth/modules/monkeysphere/templates/monkeysphere.html +++ b/plinth/modules/monkeysphere/templates/monkeysphere.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/mumble/__init__.py b/plinth/modules/mumble/__init__.py index 64f409ea4..aa61f6001 100644 --- a/plinth/modules/mumble/__init__.py +++ b/plinth/modules/mumble/__init__.py @@ -21,12 +21,12 @@ FreedomBox app to configure Mumble server. from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from plinth import action_utils, actions +from plinth import action_utils from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup, clients @@ -36,8 +36,6 @@ name = _('Mumble') short_description = _('Voice Chat') -service = None - managed_services = ['mumble-server'] managed_packages = ['mumble-server'] @@ -86,25 +84,24 @@ class MumbleApp(app_module.App): is_external=True) self.add(firewall) + daemon = Daemon('daemon-mumble', managed_services[0]) + self.add(daemon) + def init(): """Intialize the Mumble module.""" global app app = MumbleApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) -class MumbleServiceView(ServiceView): - service_id = managed_services[0] - diagnostics_module_name = "mumble" +class MumbleAppView(AppView): + app_id = 'mumble' + diagnostics_module_name = 'mumble' + name = name description = description clients = clients manual_page = manual_page @@ -114,25 +111,9 @@ class MumbleServiceView(ServiceView): def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) helper.call('post', app.enable) -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/mumble/urls.py b/plinth/modules/mumble/urls.py index 398e87b19..78580d0b7 100644 --- a/plinth/modules/mumble/urls.py +++ b/plinth/modules/mumble/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the Mumble module """ from django.conf.urls import url -from plinth.modules.mumble import MumbleServiceView - +from plinth.modules.mumble import MumbleAppView urlpatterns = [ - url(r'^apps/mumble/$', MumbleServiceView.as_view(), name='index'), + url(r'^apps/mumble/$', MumbleAppView.as_view(), name='index'), ] diff --git a/plinth/modules/names/templates/names.html b/plinth/modules/names/templates/names.html index 638d3a471..df791e519 100644 --- a/plinth/modules/names/templates/names.html +++ b/plinth/modules/names/templates/names.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/openvpn/__init__.py b/plinth/modules/openvpn/__init__.py index f51f6a7dd..97e11217e 100644 --- a/plinth/modules/openvpn/__init__.py +++ b/plinth/modules/openvpn/__init__.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -32,8 +32,6 @@ from .manifest import backup version = 3 -service = None - managed_services = ['openvpn-server@freedombox'] managed_packages = ['openvpn', 'easy-rsa'] @@ -87,31 +85,26 @@ class OpenVPNApp(app_module.App): is_external=True) self.add(firewall) + daemon = Daemon('daemon-openvpn', managed_services[0]) + self.add(daemon) + def init(): """Initialize the OpenVPN module.""" global app app = OpenVPNApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name) - - if service.is_enabled() and is_setup(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled() \ + and is_setup(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'openvpn', ['upgrade']) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - - if service.is_enabled() and is_setup(): + if app.is_enabled() and is_setup(): helper.call('post', app.enable) @@ -120,18 +113,6 @@ def is_setup(): return actions.superuser_run('openvpn', ['is-setup']).strip() == 'true' -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" return [action_utils.diagnose_port_listening(1194, 'udp4')] diff --git a/plinth/modules/openvpn/templates/openvpn.html b/plinth/modules/openvpn/templates/openvpn.html index a5f02e122..b4e15a755 100644 --- a/plinth/modules/openvpn/templates/openvpn.html +++ b/plinth/modules/openvpn/templates/openvpn.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/openvpn/views.py b/plinth/modules/openvpn/views.py index f44ddd491..fd2b36f5b 100644 --- a/plinth/modules/openvpn/views.py +++ b/plinth/modules/openvpn/views.py @@ -27,7 +27,7 @@ from django.template.response import TemplateResponse from django.utils.translation import ugettext as _ from django.views.decorators.http import require_POST -from plinth import actions +from plinth import actions, daemon from plinth.modules import config, openvpn from .forms import OpenVpnForm @@ -75,7 +75,7 @@ def setup(request): setup_process = actions.superuser_run('openvpn', ['setup'], run_in_background=True) - openvpn.enable() + openvpn.app.enable() return redirect('openvpn:index') @@ -105,8 +105,8 @@ def get_status(): return { 'is_setup': openvpn.is_setup(), 'setup_running': bool(setup_process), - 'enabled': openvpn.service.is_enabled(), - 'is_running': openvpn.service.is_running() + 'enabled': openvpn.app.is_enabled(), + 'is_running': daemon.app_is_running(openvpn.app) } @@ -136,9 +136,9 @@ def _apply_changes(request, old_status, new_status): if old_status['enabled'] != new_status['enabled']: if new_status['enabled']: - openvpn.enable() + openvpn.app.enable() else: - openvpn.disable() + openvpn.app.disable() modified = True diff --git a/plinth/modules/pagekite/__init__.py b/plinth/modules/pagekite/__init__.py index eb78b329e..10c2cdf9c 100644 --- a/plinth/modules/pagekite/__init__.py +++ b/plinth/modules/pagekite/__init__.py @@ -92,16 +92,17 @@ class PagekiteApp(app_module.App): parent_url_name='system') self.add(menu_item) + # XXX: Add pagekite daemon component and simplify action script + def init(): """Intialize the PageKite module""" global app app = PagekiteApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - app.set_enabled(True) # XXX: Perform more proper check + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) # Register kite name with Name Services module. utils.update_names_module(initial_registration=True) diff --git a/plinth/modules/pagekite/templates/pagekite_base.html b/plinth/modules/pagekite/templates/pagekite_base.html index 0215e7fb3..ad517a9cd 100644 --- a/plinth/modules/pagekite/templates/pagekite_base.html +++ b/plinth/modules/pagekite/templates/pagekite_base.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/pagekite/utils.py b/plinth/modules/pagekite/utils.py index 4b1facd46..b85ba2db3 100644 --- a/plinth/modules/pagekite/utils.py +++ b/plinth/modules/pagekite/utils.py @@ -15,13 +15,13 @@ # along with this program. If not, see . # -from django.utils.translation import ugettext_lazy as _ import json import logging import os -from plinth import actions -from plinth import action_utils +from django.utils.translation import ugettext_lazy as _ + +from plinth import action_utils, actions from plinth.signals import domain_added, domain_removed LOGGER = logging.getLogger(__name__) @@ -36,8 +36,9 @@ KITE_SECRET = '@kitesecret' CONF_PATH = '/files/etc/pagekite.d' # Parameters that get stored in configuration service_on entries -SERVICE_PARAMS = ['protocol', 'kitename', 'backend_host', 'backend_port', - 'secret'] +SERVICE_PARAMS = [ + 'protocol', 'kitename', 'backend_host', 'backend_port', 'secret' +] # Predefined services are used to build the PredefinedServiceForm # @@ -45,35 +46,47 @@ SERVICE_PARAMS = ['protocol', 'kitename', 'backend_host', 'backend_port', # still recognizes when you try to add a service equal to a predefined one PREDEFINED_SERVICES = { 'http': { - 'params': {'protocol': 'http', - 'kitename': KITE_NAME, - 'backend_port': '80', - 'backend_host': BACKEND_HOST, - 'secret': KITE_SECRET}, - 'label': _('Web Server (HTTP)'), - 'help_text': _('Site will be available at ' - 'http://{0}'), + 'params': { + 'protocol': 'http', + 'kitename': KITE_NAME, + 'backend_port': '80', + 'backend_host': BACKEND_HOST, + 'secret': KITE_SECRET + }, + 'label': + _('Web Server (HTTP)'), + 'help_text': + _('Site will be available at ' + 'http://{0}'), }, 'https': { - 'params': {'protocol': 'https', - 'kitename': KITE_NAME, - 'backend_port': '443', - 'backend_host': BACKEND_HOST, - 'secret': KITE_SECRET}, - 'label': _('Web Server (HTTPS)'), - 'help_text': _('Site will be available at ' - 'https://{0}'), + 'params': { + 'protocol': 'https', + 'kitename': KITE_NAME, + 'backend_port': '443', + 'backend_host': BACKEND_HOST, + 'secret': KITE_SECRET + }, + 'label': + _('Web Server (HTTPS)'), + 'help_text': + _('Site will be available at ' + 'https://{0}'), }, 'ssh': { - 'params': {'protocol': 'raw/22', - 'kitename': KITE_NAME, - 'backend_port': '22', - 'backend_host': BACKEND_HOST, - 'secret': KITE_SECRET}, - 'label': _('Secure Shell (SSH)'), - 'help_text': _('See SSH client setup ' - 'instructions') + 'params': { + 'protocol': 'raw/22', + 'kitename': KITE_NAME, + 'backend_port': '22', + 'backend_host': BACKEND_HOST, + 'secret': KITE_SECRET + }, + 'label': + _('Secure Shell (SSH)'), + 'help_text': + _('See SSH client setup ' + 'instructions') }, } @@ -81,8 +94,7 @@ PREDEFINED_SERVICES = { def get_kite_details(): output = run(['get-kite']) kite_details = output.split() - return {'kite_name': kite_details[0], - 'kite_secret': kite_details[1]} + return {'kite_name': kite_details[0], 'kite_secret': kite_details[1]} def get_pagekite_config(): @@ -259,8 +271,9 @@ def update_names_module(initial_registration=False, enabled=None, if enabled: # Get enabled services and kite name services = get_pagekite_services()[0] - enabled_services = [service for service in services if - services[service]] + enabled_services = [ + service for service in services if services[service] + ] if kite_name is None: try: kite_name = get_kite_details()['kite_name'] @@ -271,9 +284,9 @@ def update_names_module(initial_registration=False, enabled=None, kite_name = None if initial_registration or (enabled and kite_name): - domain_added.send_robust( - sender='pagekite', domain_type='pagekite', name=kite_name, - description=_('Pagekite'), services=enabled_services) + domain_added.send_robust(sender='pagekite', domain_type='pagekite', + name=kite_name, description=_('Pagekite'), + services=enabled_services) if __name__ == "__main__": diff --git a/plinth/modules/pagekite/views.py b/plinth/modules/pagekite/views.py index d4f16f01f..342c146be 100644 --- a/plinth/modules/pagekite/views.py +++ b/plinth/modules/pagekite/views.py @@ -34,13 +34,15 @@ from .forms import (AddCustomServiceForm, ConfigurationForm, subsubmenu = [{ 'url': reverse_lazy('pagekite:index'), 'text': _('Configure') -}, { - 'url': reverse_lazy('pagekite:standard-services'), - 'text': _('Standard Services') -}, { - 'url': reverse_lazy('pagekite:custom-services'), - 'text': _('Custom Services') -}] +}, + { + 'url': reverse_lazy('pagekite:standard-services'), + 'text': _('Standard Services') + }, + { + 'url': reverse_lazy('pagekite:custom-services'), + 'text': _('Custom Services') + }] class ContextMixin(object): diff --git a/plinth/modules/power/templates/power.html b/plinth/modules/power/templates/power.html index 981983c2f..49e963907 100644 --- a/plinth/modules/power/templates/power.html +++ b/plinth/modules/power/templates/power.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/privoxy/__init__.py b/plinth/modules/privoxy/__init__.py index a18d1b08c..5b4ca64d9 100644 --- a/plinth/modules/privoxy/__init__.py +++ b/plinth/modules/privoxy/__init__.py @@ -24,10 +24,10 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup @@ -60,8 +60,6 @@ description = [ reserved_usernames = ['privoxy'] -service = None - manual_page = 'Privoxy' app = None @@ -90,48 +88,31 @@ class PrivoxyApp(app_module.App): is_external=False) self.add(firewall) + daemon = Daemon('daemon-privoxy', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" global app app = PrivoxyApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.call('pre', actions.superuser_run, 'privoxy', ['pre-install']) helper.install(managed_packages) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) helper.call('post', app.enable) -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - -class PrivoxyServiceView(ServiceView): - service_id = managed_services[0] +class PrivoxyAppView(AppView): + app_id = 'privoxy' diagnostics_module_name = 'privoxy' + name = name description = description manual_page = manual_page diff --git a/plinth/modules/privoxy/urls.py b/plinth/modules/privoxy/urls.py index 2f7b81fc7..6887b281e 100644 --- a/plinth/modules/privoxy/urls.py +++ b/plinth/modules/privoxy/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the Privoxy module. """ from django.conf.urls import url -from plinth.modules.privoxy import PrivoxyServiceView - +from plinth.modules.privoxy import PrivoxyAppView urlpatterns = [ - url(r'^apps/privoxy/$', PrivoxyServiceView.as_view(), name='index'), + url(r'^apps/privoxy/$', PrivoxyAppView.as_view(), name='index'), ] diff --git a/plinth/modules/quassel/__init__.py b/plinth/modules/quassel/__init__.py index 4fcad5621..782ceb196 100644 --- a/plinth/modules/quassel/__init__.py +++ b/plinth/modules/quassel/__init__.py @@ -21,20 +21,18 @@ FreedomBox app for Quassel. from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ -from plinth import action_utils, actions +from plinth import action_utils from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup, clients version = 1 -service = None - managed_services = ['quasselcore'] managed_packages = ['quassel-core'] @@ -94,25 +92,24 @@ class QuasselApp(app_module.App): is_external=True) self.add(firewall) + daemon = Daemon('daemon-quassel', managed_services[0]) + self.add(daemon) + def init(): """Initialize the quassel module.""" global app app = QuasselApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) -class QuasselServiceView(ServiceView): - service_id = managed_services[0] - diagnostics_module_name = "quassel" +class QuasselAppView(AppView): + app_id = 'quassel' + diagnostics_module_name = 'quassel' + name = name description = description clients = clients manual_page = manual_page @@ -122,25 +119,9 @@ class QuasselServiceView(ServiceView): def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) helper.call('post', app.enable) -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/quassel/urls.py b/plinth/modules/quassel/urls.py index 98b1b67bf..a2dc6f9c0 100644 --- a/plinth/modules/quassel/urls.py +++ b/plinth/modules/quassel/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the quassel module. """ from django.conf.urls import url -from plinth.modules.quassel import QuasselServiceView - +from plinth.modules.quassel import QuasselAppView urlpatterns = [ - url(r'^apps/quassel/$', QuasselServiceView.as_view(), name='index'), + url(r'^apps/quassel/$', QuasselAppView.as_view(), name='index'), ] diff --git a/plinth/modules/radicale/__init__.py b/plinth/modules/radicale/__init__.py index dfe208a92..52807e79c 100644 --- a/plinth/modules/radicale/__init__.py +++ b/plinth/modules/radicale/__init__.py @@ -29,7 +29,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Uwsgi, Webserver from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -38,8 +38,6 @@ from .manifest import backup, clients version = 2 -service = None - managed_services = ['radicale'] managed_packages = ['radicale', 'uwsgi', 'uwsgi-plugin-python3'] @@ -104,6 +102,9 @@ class RadicaleApp(app_module.App): uwsgi = RadicaleUwsgi('uwsgi-radicale', 'radicale') self.add(uwsgi) + daemon = RadicaleDaemon('daemon-radicale', managed_services[0]) + self.add(daemon) + class RadicaleWebserver(Webserver): """Webserver enable/disable behavior specific for radicale.""" @@ -137,6 +138,7 @@ class RadicaleUwsgi(Uwsgi): """Enable the uWSGI configuration if version >=2.""" package_version = get_package_version() if package_version and package_version >= VERSION_2: + actions.superuser_run('radicale', ['fix-collections']) super().enable() def disable(self): @@ -146,20 +148,50 @@ class RadicaleUwsgi(Uwsgi): super().disable() +class RadicaleDaemon(Daemon): + """Daemon enable/disable behavior specific for radicale.""" + + @staticmethod + def _is_old_radicale(): + """Return whether radicale is less than version 2.""" + package_version = get_package_version() + return package_version and package_version < VERSION_2 + + def is_enabled(self): + """Return whether daemon is enabled if version < 2.""" + if self._is_old_radicale(): + return super().is_enabled() + + return True + + def enable(self): + """Enable the daemon if version < 2.""" + if self._is_old_radicale(): + super().enable() + else: + super().disable() + + def disable(self): + """Disable the daemon if version < 2.""" + if self._is_old_radicale(): + super().disable() + + def is_running(self): + """Return whether daemon is enabled if version < 2.""" + if self._is_old_radicale(): + return super().is_running() + + return True + + def init(): """Initialize the radicale module.""" global app app = RadicaleApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -188,11 +220,6 @@ def setup(helper, old_version=None): helper.install(managed_packages) helper.call('post', actions.superuser_run, 'radicale', ['setup']) - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) helper.call('post', app.enable) @@ -208,24 +235,6 @@ def get_package_version(): return LV(package_version) -def is_running(): - """Return whether the service is running.""" - if get_package_version() < VERSION_2: - return action_utils.service_is_running('radicale') - - return app.is_enabled() - - -def is_enabled(): - """Return whether the module is enabled.""" - package_version = get_package_version() - daemon_enabled = True - if package_version and package_version < VERSION_2: - daemon_enabled = action_utils.service_is_enabled('radicale') - - return app.is_enabled() and daemon_enabled - - def enable(): """Enable the module.""" actions.superuser_run('radicale', ['enable']) diff --git a/plinth/modules/radicale/forms.py b/plinth/modules/radicale/forms.py index 8d3178e9f..029fc6663 100644 --- a/plinth/modules/radicale/forms.py +++ b/plinth/modules/radicale/forms.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Forms for radicale module. """ @@ -23,22 +22,26 @@ from django import forms from django.utils.translation import ugettext_lazy as _ from plinth import cfg -from plinth.forms import ServiceForm +from plinth.forms import AppForm from plinth.utils import format_lazy CHOICES = [ - ('owner_only', _('Only the owner of a calendar/addressbook can view or ' - 'make changes.')), - ('owner_write', format_lazy( - _('Any user with a {box_name} login can view any calendar/addressbook' - ', but only the owner can make changes.'), box_name=_(cfg.box_name))), - ('authenticated', format_lazy( - _('Any user with a {box_name} login can view or make changes' - ' to any calendar/addressbook.'), box_name=_(cfg.box_name))), + ('owner_only', + _('Only the owner of a calendar/addressbook can view or ' + 'make changes.')), + ('owner_write', + format_lazy( + _('Any user with a {box_name} login can view any calendar/addressbook' + ', but only the owner can make changes.'), + box_name=_(cfg.box_name))), + ('authenticated', + format_lazy( + _('Any user with a {box_name} login can view or make changes' + ' to any calendar/addressbook.'), box_name=_(cfg.box_name))), ] -class RadicaleForm(ServiceForm): +class RadicaleForm(AppForm): """Specialized configuration form for radicale service.""" access_rights = forms.ChoiceField(choices=CHOICES, required=True, widget=forms.RadioSelect()) diff --git a/plinth/modules/radicale/urls.py b/plinth/modules/radicale/urls.py index e3d3068d6..0601736fc 100644 --- a/plinth/modules/radicale/urls.py +++ b/plinth/modules/radicale/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the radicale module. """ from django.conf.urls import url -from .views import RadicaleServiceView - +from .views import RadicaleAppView urlpatterns = [ - url(r'^apps/radicale/$', RadicaleServiceView.as_view(), name='index'), + url(r'^apps/radicale/$', RadicaleAppView.as_view(), name='index'), ] diff --git a/plinth/modules/radicale/views.py b/plinth/modules/radicale/views.py index 62ab7b2d4..70f174c84 100644 --- a/plinth/modules/radicale/views.py +++ b/plinth/modules/radicale/views.py @@ -23,19 +23,20 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth.modules import radicale -from plinth.views import ServiceView +from plinth.views import AppView -from . import description, get_rights_value, managed_services +from . import description, get_rights_value from .forms import RadicaleForm -class RadicaleServiceView(ServiceView): +class RadicaleAppView(AppView): """A specialized view for configuring radicale service.""" clients = radicale.clients + name = radicale.name description = description diagnostics_module_name = 'radicale' form_class = RadicaleForm - service_id = managed_services[0] + app_id = 'radicale' manual_page = radicale.manual_page def get_initial(self): diff --git a/plinth/modules/repro/__init__.py b/plinth/modules/repro/__init__.py index ec64b4d8f..1ba7b4477 100644 --- a/plinth/modules/repro/__init__.py +++ b/plinth/modules/repro/__init__.py @@ -24,10 +24,10 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup, clients @@ -64,8 +64,6 @@ clients = clients reserved_usernames = ['repro'] -service = None - manual_page = 'Repro' port_forwarding_info = [('UDP', '1024-65535')] @@ -100,27 +98,26 @@ class ReproApp(app_module.App): webserver = Webserver('webserver-repro', 'repro-plinth') self.add(webserver) + daemon = Daemon('daemon-repro', managed_services[0]) + self.add(daemon) + def init(): """Initialize the repro module.""" global app app = ReproApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) - - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) -class ReproServiceView(ServiceView): +class ReproAppView(AppView): clients = clients + name = name description = description - diagnostics_module_name = "repro" - service_id = managed_services[0] + diagnostics_module_name = 'repro' + app_id = 'repro' manual_page = manual_page port_forwarding_info = port_forwarding_info @@ -129,25 +126,9 @@ def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'repro', ['setup']) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - enable=enable, disable=disable) helper.call('post', app.enable) -def enable(): - """Enable the module.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/repro/urls.py b/plinth/modules/repro/urls.py index 5072790ab..9433098ff 100644 --- a/plinth/modules/repro/urls.py +++ b/plinth/modules/repro/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the repro module. """ from django.conf.urls import url -from plinth.modules.repro import ReproServiceView - +from plinth.modules.repro import ReproAppView urlpatterns = [ - url(r'^apps/repro/$', ReproServiceView.as_view(), name='index'), + url(r'^apps/repro/$', ReproAppView.as_view(), name='index'), ] diff --git a/plinth/modules/restore/__init__.py b/plinth/modules/restore/__init__.py index dab62552d..2f6dfd4bc 100644 --- a/plinth/modules/restore/__init__.py +++ b/plinth/modules/restore/__init__.py @@ -22,7 +22,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import app as app_module from plinth import cfg, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -54,8 +54,6 @@ clients = clients reserved_usernames = ['node-restore'] -service = None - app = None @@ -76,21 +74,21 @@ class RestoreApp(app_module.App): is_external=True) self.add(firewall) + daemon = Daemon('daemon-restore', managed_services[0]) + self.add(daemon) + def init(): """Initialize the reStore module.""" global app app = RestoreApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.enable() def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service(managed_services[0], name) + helper.call('post', app.enable) diff --git a/plinth/modules/restore/urls.py b/plinth/modules/restore/urls.py index 78547f0a9..1d8288de7 100644 --- a/plinth/modules/restore/urls.py +++ b/plinth/modules/restore/urls.py @@ -20,14 +20,13 @@ URLs for the reStore module. from django.conf.urls import url -from plinth.views import ServiceView from plinth.modules import restore +from plinth.views import AppView urlpatterns = [ - url(r'^apps/restore/$', - ServiceView.as_view( - service_id=restore.managed_services[0], - description=restore.description, - clients=restore.clients), - name='index'), + url( + r'^apps/restore/$', + AppView.as_view(app_id='restore', name=restore.name, + description=restore.description, + clients=restore.clients), name='index'), ] diff --git a/plinth/modules/roundcube/__init__.py b/plinth/modules/roundcube/__init__.py index dc49478ce..45f646fe4 100644 --- a/plinth/modules/roundcube/__init__.py +++ b/plinth/modules/roundcube/__init__.py @@ -23,7 +23,6 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -59,8 +58,6 @@ description = [ clients = clients -service = None - manual_page = 'Roundcube' app = None @@ -98,15 +95,9 @@ def init(): global app app = RoundcubeApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('roundcube', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -114,29 +105,9 @@ def setup(helper, old_version=None): helper.call('pre', actions.superuser_run, 'roundcube', ['pre-install']) helper.install(managed_packages) helper.call('post', actions.superuser_run, 'roundcube', ['setup']) - global service - if service is None: - service = service_module.Service('roundcube', name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return app.is_enabled() - - -def enable(): - """Enable the module.""" - app.enable() - - -def disable(): - """Enable the module.""" - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/roundcube/urls.py b/plinth/modules/roundcube/urls.py index ebc730e76..a9c29fda5 100644 --- a/plinth/modules/roundcube/urls.py +++ b/plinth/modules/roundcube/urls.py @@ -21,15 +21,14 @@ URLs for the Roundcube module. from django.conf.urls import url from plinth.modules import roundcube -from plinth.views import ServiceView +from plinth.views import AppView urlpatterns = [ - url(r'^apps/roundcube/$', - ServiceView.as_view( - service_id="roundcube", - diagnostics_module_name="roundcube", - description=roundcube.description, - show_status_block=False, - manual_page=roundcube.manual_page, - ), name='index'), + url( + r'^apps/roundcube/$', + AppView.as_view(app_id='roundcube', name=roundcube.name, + diagnostics_module_name='roundcube', + description=roundcube.description, + show_status_block=False, + manual_page=roundcube.manual_page), name='index'), ] diff --git a/plinth/modules/searx/__init__.py b/plinth/modules/searx/__init__.py index 9b8d6a1c0..3fd554568 100644 --- a/plinth/modules/searx/__init__.py +++ b/plinth/modules/searx/__init__.py @@ -25,7 +25,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Uwsgi, Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -36,8 +36,6 @@ clients = clients version = 3 -managed_services = ['searx'] - managed_packages = ['searx', 'uwsgi', 'uwsgi-plugin-python3'] name = _('Searx') @@ -53,8 +51,6 @@ description = [ group = ('web-search', _('Search the web')) -service = None - manual_page = 'Searx' app = None @@ -119,15 +115,9 @@ def init(): app = SearxApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -135,18 +125,10 @@ def setup(helper, old_version=None): helper.install(managed_packages) helper.call('post', actions.superuser_run, 'searx', ['setup']) if not old_version or old_version < 3: - helper.call('post', actions.superuser_run, 'searx', ['enable']) helper.call('post', actions.superuser_run, 'searx', ['disable-public-access']) + helper.call('post', app.enable) app.set_shortcut_login_required(True) - app.enable() - - global service - if service is None: - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - helper.call('post', app.enable) def get_safe_search_setting(): @@ -160,21 +142,6 @@ def is_public_access_enabled(): return os.path.exists(PUBLIC_ACCESS_SETTING_FILE) -def is_enabled(): - """Return whether the module is enabled.""" - return app.is_enabled() - - -def enable(): - """Enable the module.""" - app.enable() - - -def disable(): - """Disable the module.""" - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/searx/forms.py b/plinth/modules/searx/forms.py index 68c66177c..60821f3b3 100644 --- a/plinth/modules/searx/forms.py +++ b/plinth/modules/searx/forms.py @@ -21,10 +21,10 @@ Django form for configuring Searx. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm -class SearxForm(ServiceForm): +class SearxForm(AppForm): """Searx configuration form.""" safe_search = forms.ChoiceField( label=_('Safe Search'), help_text=_( diff --git a/plinth/modules/searx/urls.py b/plinth/modules/searx/urls.py index 66307c8e9..c837e8142 100644 --- a/plinth/modules/searx/urls.py +++ b/plinth/modules/searx/urls.py @@ -20,8 +20,8 @@ URLs for the Searx module. from django.conf.urls import url -from .views import SearxServiceView +from .views import SearxAppView urlpatterns = [ - url(r'^apps/searx/$', SearxServiceView.as_view(), name='index'), + url(r'^apps/searx/$', SearxAppView.as_view(), name='index'), ] diff --git a/plinth/modules/searx/views.py b/plinth/modules/searx/views.py index aacf1f9a0..d4b5b48f3 100644 --- a/plinth/modules/searx/views.py +++ b/plinth/modules/searx/views.py @@ -23,29 +23,28 @@ from django.utils.translation import ugettext as _ from plinth import actions, views from plinth.errors import ActionError -from plinth.modules.searx import (clients, description, disable_public_access, - enable_public_access, - get_safe_search_setting, is_enabled, - is_public_access_enabled, manual_page) +from plinth.modules import searx from .forms import SearxForm -class SearxServiceView(views.ServiceView): +class SearxAppView(views.AppView): """Serve configuration page.""" - clients = clients - description = description + clients = searx.clients + name = searx.name + description = searx.description diagnostics_module_name = 'searx' - service_id = 'searx' + app_id = 'searx' form_class = SearxForm show_status_block = False - manual_page = manual_page + manual_page = searx.manual_page def get_initial(self): """Return the status of the service to fill in the form.""" initial = super().get_initial() - initial['safe_search'] = get_safe_search_setting() - initial['public_access'] = is_public_access_enabled() and is_enabled() + initial['safe_search'] = searx.get_safe_search_setting() + initial['public_access'] = searx.is_public_access_enabled() and \ + searx.app.is_enabled() return initial def form_valid(self, form): @@ -65,9 +64,9 @@ class SearxServiceView(views.ServiceView): if old_data['public_access'] != form_data['public_access']: try: if form_data['public_access']: - enable_public_access() + searx.enable_public_access() else: - disable_public_access() + searx.disable_public_access() messages.success(self.request, _('Configuration updated.')) except ActionError: messages.error(self.request, diff --git a/plinth/modules/security/templates/security.html b/plinth/modules/security/templates/security.html index f3b84f011..3e4a999c0 100644 --- a/plinth/modules/security/templates/security.html +++ b/plinth/modules/security/templates/security.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/shaarli/__init__.py b/plinth/modules/shaarli/__init__.py index 4c0069dfd..e7871af5a 100644 --- a/plinth/modules/shaarli/__init__.py +++ b/plinth/modules/shaarli/__init__.py @@ -22,7 +22,6 @@ from django.utils.translation import ugettext_lazy as _ from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall @@ -46,8 +45,6 @@ description = [ clients = clients -service = None - manual_page = 'Shaarli' app = None @@ -85,38 +82,12 @@ def init(): global app app = ShaarliApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service('shaarli', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - global service - if service is None: - service = service_module.Service('shaarli', name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) - - -def is_enabled(): - """Return whether the module is enabled.""" - return app.is_enabled() - - -def enable(): - """Enable the module.""" - app.enable() - - -def disable(): - """Enable the module.""" - app.disable() diff --git a/plinth/modules/shaarli/urls.py b/plinth/modules/shaarli/urls.py index 09478b1d5..5bfaa73a5 100644 --- a/plinth/modules/shaarli/urls.py +++ b/plinth/modules/shaarli/urls.py @@ -21,12 +21,13 @@ URLs for the Shaarli module. from django.conf.urls import url from plinth.modules import shaarli -from plinth.views import ServiceView +from plinth.views import AppView urlpatterns = [ - url(r'^apps/shaarli/$', - ServiceView.as_view( - service_id="shaarli", description=shaarli.description, - show_status_block=False, manual_page=shaarli.manual_page), - name='index'), + url( + r'^apps/shaarli/$', + AppView.as_view(app_id='shaarli', name=shaarli.name, + description=shaarli.description, + show_status_block=False, + manual_page=shaarli.manual_page), name='index'), ] diff --git a/plinth/modules/shadowsocks/__init__.py b/plinth/modules/shadowsocks/__init__.py index ecdab7d9a..aa41082db 100644 --- a/plinth/modules/shadowsocks/__init__.py +++ b/plinth/modules/shadowsocks/__init__.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -36,8 +36,6 @@ name = _('Shadowsocks') short_description = _('Socks5 Proxy') -service = None - managed_services = ['shadowsocks-libev-local@freedombox'] managed_packages = ['shadowsocks-libev'] @@ -86,58 +84,27 @@ class ShadowsocksApp(app_module.App): is_external=False) self.add(firewall) + daemon = Daemon('daemon-shadowsocks', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" global app app = ShadowsocksApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service( - 'shadowsocks', name, is_enabled=is_enabled, is_running=is_running, - enable=enable, disable=disable) - - if service.is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', actions.superuser_run, 'shadowsocks', ['setup']) - global service - if service is None: - service = service_module.Service( - 'shadowsocks', name, is_enabled=is_enabled, is_running=is_running, - enable=enable, disable=disable) - helper.call('post', app.enable) -def is_enabled(): - """Return whether service is enabled.""" - return action_utils.service_is_enabled(managed_services[0]) - - -def is_running(): - """Return whether service is running.""" - return action_utils.service_is_running(managed_services[0]) - - -def enable(): - """Enable service.""" - actions.superuser_run('service', ['enable', managed_services[0]]) - app.enable() - - -def disable(): - """Disable service.""" - actions.superuser_run('service', ['disable', managed_services[0]]) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/shadowsocks/forms.py b/plinth/modules/shadowsocks/forms.py index b06f6e08b..761a56cc2 100644 --- a/plinth/modules/shadowsocks/forms.py +++ b/plinth/modules/shadowsocks/forms.py @@ -21,26 +21,20 @@ FreedomBox app for configuring Shadowsocks. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm from plinth.utils import format_lazy -METHODS = [ - ('chacha20-ietf-poly1305', - format_lazy('chacha20-ietf-poly1305 ({})', _('Recommended'))), - ('aes-256-gcm', format_lazy('aes-256-gcm ({})', _('Recommended'))), - ('aes-192-gcm', 'aes-192-gcm'), - ('aes-128-gcm', 'aes-128-gcm'), - ('aes-128-ctr', 'aes-128-ctr'), - ('aes-192-ctr', 'aes-192-ctr'), - ('aes-256-ctr', 'aes-256-ctr'), - ('aes-128-cfb', 'aes-128-cfb'), - ('aes-192-cfb', 'aes-192-cfb'), - ('aes-256-cfb', 'aes-256-cfb'), - ('camellia-128-cfb', 'camellia-128-cfb'), - ('camellia-192-cfb', 'camellia-192-cfb'), - ('camellia-256-cfb', 'camellia-256-cfb'), - ('chacha20-ietf', 'chacha20-ietf') -] +METHODS = [('chacha20-ietf-poly1305', + format_lazy('chacha20-ietf-poly1305 ({})', _('Recommended'))), + ('aes-256-gcm', format_lazy('aes-256-gcm ({})', _('Recommended'))), + ('aes-192-gcm', 'aes-192-gcm'), ('aes-128-gcm', 'aes-128-gcm'), + ('aes-128-ctr', 'aes-128-ctr'), ('aes-192-ctr', 'aes-192-ctr'), + ('aes-256-ctr', 'aes-256-ctr'), ('aes-128-cfb', 'aes-128-cfb'), + ('aes-192-cfb', 'aes-192-cfb'), ('aes-256-cfb', 'aes-256-cfb'), + ('camellia-128-cfb', 'camellia-128-cfb'), + ('camellia-192-cfb', 'camellia-192-cfb'), + ('camellia-256-cfb', 'camellia-256-cfb'), + ('chacha20-ietf', 'chacha20-ietf')] class TrimmedCharField(forms.CharField): @@ -54,24 +48,19 @@ class TrimmedCharField(forms.CharField): return super(TrimmedCharField, self).clean(value) -class ShadowsocksForm(ServiceForm): +class ShadowsocksForm(AppForm): """Shadowsocks configuration form""" server = TrimmedCharField( - label=_('Server'), - help_text=_('Server hostname or IP address')) + label=_('Server'), help_text=_('Server hostname or IP address')) server_port = forms.IntegerField( - label=_('Server port'), - min_value=0, - max_value=65535, + label=_('Server port'), min_value=0, max_value=65535, help_text=_('Server port number')) password = forms.CharField( - label=_('Password'), - help_text=_('Password used to encrypt data. ' - 'Must match server password.')) + label=_('Password'), help_text=_('Password used to encrypt data. ' + 'Must match server password.')) method = forms.ChoiceField( - label=_('Method'), - choices=METHODS, + label=_('Method'), choices=METHODS, help_text=_('Encryption method. Must match setting on server.')) diff --git a/plinth/modules/shadowsocks/urls.py b/plinth/modules/shadowsocks/urls.py index bad1efa80..7551edad2 100644 --- a/plinth/modules/shadowsocks/urls.py +++ b/plinth/modules/shadowsocks/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the Shadowsocks module. """ from django.conf.urls import url -from .views import ShadowsocksServiceView +from .views import ShadowsocksAppView urlpatterns = [ - url(r'^apps/shadowsocks/$', ShadowsocksServiceView.as_view(), - name='index'), + url(r'^apps/shadowsocks/$', ShadowsocksAppView.as_view(), name='index'), ] diff --git a/plinth/modules/shadowsocks/views.py b/plinth/modules/shadowsocks/views.py index 282b81d8a..d9e13928d 100644 --- a/plinth/modules/shadowsocks/views.py +++ b/plinth/modules/shadowsocks/views.py @@ -30,30 +30,29 @@ from plinth.modules import shadowsocks from .forms import ShadowsocksForm -class ShadowsocksServiceView(views.ServiceView): +class ShadowsocksAppView(views.AppView): """Configuration view for Shadowsocks local socks5 proxy.""" - service_id = 'shadowsocks' + app_id = 'shadowsocks' diagnostics_module_name = 'shadowsocks' form_class = ShadowsocksForm + name = shadowsocks.name description = shadowsocks.description manual_page = shadowsocks.manual_page def get_initial(self, *args, **kwargs): """Get initial values for form.""" + status = super().get_initial() try: configuration = actions.superuser_run('shadowsocks', ['get-config']) - status = json.loads(configuration) + status.update(json.loads(configuration)) except ActionError: - status = { + status.update({ 'server': '', 'server_port': 8388, 'password': '', 'method': 'chacha20-ietf-poly1305', - } - - status['is_enabled'] = self.service.is_enabled() - status['is_running'] = self.service.is_running() + }) return status diff --git a/plinth/modules/snapshot/__init__.py b/plinth/modules/snapshot/__init__.py index 66f982c6c..2c1c81e0b 100644 --- a/plinth/modules/snapshot/__init__.py +++ b/plinth/modules/snapshot/__init__.py @@ -50,8 +50,6 @@ description = [ 'they can only be stored on the same partition. ') ] -service = None - manual_page = 'Snapshots' DEFAULT_FILE = '/etc/default/snapper' @@ -79,10 +77,9 @@ def init(): global app app = SnapshotApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - app.set_enabled(True) # XXX: Perform better checks + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def is_supported(): diff --git a/plinth/modules/snapshot/templates/snapshot.html b/plinth/modules/snapshot/templates/snapshot.html index 79e55a02f..e1543326c 100644 --- a/plinth/modules/snapshot/templates/snapshot.html +++ b/plinth/modules/snapshot/templates/snapshot.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/snapshot/templates/snapshot_manage.html b/plinth/modules/snapshot/templates/snapshot_manage.html index a8a24d0ba..4af1e3ae2 100644 --- a/plinth/modules/snapshot/templates/snapshot_manage.html +++ b/plinth/modules/snapshot/templates/snapshot_manage.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/snapshot/templates/snapshot_not_supported.html b/plinth/modules/snapshot/templates/snapshot_not_supported.html index f555a0d08..5d97732ff 100644 --- a/plinth/modules/snapshot/templates/snapshot_not_supported.html +++ b/plinth/modules/snapshot/templates/snapshot_not_supported.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/ssh/__init__.py b/plinth/modules/ssh/__init__.py index dabf47083..56019ad1a 100644 --- a/plinth/modules/ssh/__init__.py +++ b/plinth/modules/ssh/__init__.py @@ -23,9 +23,9 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth import app as app_module from plinth import menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall -from plinth.views import ServiceView +from plinth.views import AppView from .manifest import backup @@ -46,8 +46,6 @@ description = [ 'using such connections.') ] -service = None - port_forwarding_info = [('TCP', 22)] app = None @@ -69,6 +67,9 @@ class SSHApp(app_module.App): is_external=True) self.add(firewall) + daemon = Daemon('daemon-ssh', managed_services[0]) + self.add(daemon) + def init(): """Intialize the ssh module.""" @@ -76,16 +77,14 @@ def init(): app = SSHApp() app.set_enabled(True) - global service - service = service_module.Service(managed_services[0], name) - def setup(helper, old_version=None): """Configure the module.""" actions.superuser_run('ssh', ['setup']) -class SshServiceView(ServiceView): - service_id = managed_services[0] +class SshAppView(AppView): + app_id = 'ssh' + name = name description = description port_forwarding_info = port_forwarding_info diff --git a/plinth/modules/ssh/urls.py b/plinth/modules/ssh/urls.py index 19fd8c144..e8b1264d3 100644 --- a/plinth/modules/ssh/urls.py +++ b/plinth/modules/ssh/urls.py @@ -14,16 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the Secure Shell Server module. """ from django.conf.urls import url -from plinth.modules.ssh import SshServiceView - +from plinth.modules.ssh import SshAppView urlpatterns = [ - url(r'^sys/ssh/$', SshServiceView.as_view(), name='index'), + url(r'^sys/ssh/$', SshAppView.as_view(), name='index'), ] diff --git a/plinth/modules/storage/__init__.py b/plinth/modules/storage/__init__.py index 4330fca7c..03e4664c3 100644 --- a/plinth/modules/storage/__init__.py +++ b/plinth/modules/storage/__init__.py @@ -25,9 +25,8 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module -from plinth import cfg, menu -from plinth import service as service_module -from plinth import utils +from plinth import cfg, menu, utils +from plinth.daemon import Daemon from plinth.errors import PlinthError from plinth.utils import format_lazy, import_from_gi @@ -47,8 +46,6 @@ description = [ box_name=_(cfg.box_name)) ] -service = None - logger = logging.getLogger(__name__) manual_page = 'Storage' @@ -70,6 +67,9 @@ class StorageApp(app_module.App): 'storage:index', parent_url_name='system') self.add(menu_item) + daemon = Daemon('daemon-udiskie', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" @@ -271,37 +271,10 @@ def get_error_message(error): return message -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running('freedombox-udiskie') - - -def is_enabled(): - """Return whether the module is enabled.""" - return action_utils.service_is_enabled('freedombox-udiskie') - - -def enable(): - """Enable the module.""" - actions.superuser_run('udiskie', ['enable']) - app.enable() - - -def disable(): - """Disable the module.""" - actions.superuser_run('udiskie', ['disable']) - app.disable() - - def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages, skip_recommends=True) - helper.call('post', actions.superuser_run, 'udiskie', ['enable']) - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) + helper.call('post', app.enable) disks = get_disks() root_device = get_root_device(disks) if is_expandable(root_device): diff --git a/plinth/modules/syncthing/__init__.py b/plinth/modules/syncthing/__init__.py index 94eb60639..da5d3609e 100644 --- a/plinth/modules/syncthing/__init__.py +++ b/plinth/modules/syncthing/__init__.py @@ -23,7 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -63,8 +63,6 @@ clients = clients group = ('syncthing', _('Administer Syncthing application')) -service = None - manual_page = 'Syncthing' app = None @@ -96,6 +94,9 @@ class SyncthingApp(app_module.App): webserver = Webserver('webserver-syncthing', 'syncthing-plinth') self.add(webserver) + daemon = Daemon('daemon-syncthing', managed_services[0]) + self.add(daemon) + def init(): """Intialize the module.""" @@ -103,52 +104,18 @@ def init(): app = SyncthingApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) - helper.call('post', actions.superuser_run, 'syncthing', ['enable']) - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) + helper.call('post', actions.superuser_run, 'syncthing', ['setup']) helper.call('post', app.enable) -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running('syncthing@syncthing') - - -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled('syncthing@syncthing') - and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('syncthing', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('syncthing', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/syncthing/urls.py b/plinth/modules/syncthing/urls.py index 71154f50e..63a7bee1e 100644 --- a/plinth/modules/syncthing/urls.py +++ b/plinth/modules/syncthing/urls.py @@ -21,12 +21,13 @@ URLs for the Syncthing module. from django.conf.urls import url from plinth.modules import syncthing -from plinth.views import ServiceView +from plinth.views import AppView urlpatterns = [ - url(r'^apps/syncthing/$', - ServiceView.as_view( - service_id=syncthing.managed_services[0], + url( + r'^apps/syncthing/$', + AppView.as_view( + app_id='syncthing', name=syncthing.name, diagnostics_module_name='syncthing', description=syncthing.description, clients=syncthing.clients, manual_page=syncthing.manual_page, show_status_block=True), diff --git a/plinth/modules/tahoe/__init__.py b/plinth/modules/tahoe/__init__.py index 8f5cf34c2..5dac44462 100644 --- a/plinth/modules/tahoe/__init__.py +++ b/plinth/modules/tahoe/__init__.py @@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.utils import format_lazy @@ -44,8 +44,6 @@ name = _('Tahoe-LAFS') short_description = _('Distributed File Storage') -service = None - port_forwarding_info = [ ('TCP', 3456), ('TCP', 5678), @@ -88,6 +86,9 @@ class TahoeApp(app_module.App): webserver = Webserver('webserver-tahoe', 'tahoe-plinth') self.add(webserver) + daemon = Daemon('daemon-tahoe', managed_services[0]) + self.add(daemon) + class Shortcut(frontpage.Shortcut): """Frontpage shortcut to use configured domain name for URL.""" @@ -131,15 +132,10 @@ def init(): global app app = TahoeApp() - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup' and is_setup(): - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and is_setup() \ + and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -151,44 +147,14 @@ def post_setup(configured_domain_name): """Actions to be performed after installing tahoe-lafs package.""" actions.superuser_run('tahoe-lafs', ['setup', '--domain-name', configured_domain_name]) - actions.superuser_run('tahoe-lafs', ['enable']) actions.run_as_user('tahoe-lafs', ['create-introducer'], become_user='tahoe-lafs') actions.run_as_user('tahoe-lafs', ['create-storage-node'], become_user='tahoe-lafs') actions.superuser_run('tahoe-lafs', ['autostart']) - - global service - if service is None: - service = service_module.Service( - managed_services[0], name, is_enabled=is_enabled, enable=enable, - disable=disable, is_running=is_running) app.enable() -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running(managed_services[0]) - - -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled(managed_services[0]) - and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('tahoe-lafs', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('tahoe-lafs', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" return [ diff --git a/plinth/modules/tahoe/templates/tahoe-post-setup.html b/plinth/modules/tahoe/templates/tahoe-post-setup.html index 1cf79dae4..bd3a511a8 100644 --- a/plinth/modules/tahoe/templates/tahoe-post-setup.html +++ b/plinth/modules/tahoe/templates/tahoe-post-setup.html @@ -1,4 +1,4 @@ -{% extends "service.html" %} +{% extends "app.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/tahoe/urls.py b/plinth/modules/tahoe/urls.py index a74c0b23c..784068e27 100644 --- a/plinth/modules/tahoe/urls.py +++ b/plinth/modules/tahoe/urls.py @@ -21,7 +21,7 @@ URLs for the Tahoe-LAFS module. from django.conf.urls import url from . import views -from .views import TahoeServiceView, TahoeSetupView +from .views import TahoeAppView, TahoeSetupView urlpatterns = [ url(r'^apps/tahoe/setup/$', TahoeSetupView.as_view(), name='setup'), @@ -29,5 +29,5 @@ urlpatterns = [ name='add-introducer'), url(r'^apps/tahoe/remove_introducer/(?P[0-9a-zA-Z_]+)/$', views.remove_introducer, name='remove-introducer'), - url(r'^apps/tahoe/$', TahoeServiceView.as_view(), name='index') + url(r'^apps/tahoe/$', TahoeAppView.as_view(), name='index') ] diff --git a/plinth/modules/tahoe/views.py b/plinth/modules/tahoe/views.py index af042a0ba..e9612f1b5 100644 --- a/plinth/modules/tahoe/views.py +++ b/plinth/modules/tahoe/views.py @@ -24,7 +24,7 @@ from django.views.generic import FormView from plinth.forms import DomainSelectionForm from plinth.modules import tahoe from plinth.utils import get_domain_names -from plinth.views import ServiceView +from plinth.views import AppView class TahoeSetupView(FormView): @@ -49,10 +49,11 @@ class TahoeSetupView(FormView): return context -class TahoeServiceView(ServiceView): +class TahoeAppView(AppView): """Show tahoe-lafs service page.""" - service_id = tahoe.managed_services[0] + app_id = 'tahoe' template_name = 'tahoe-post-setup.html' + name = tahoe.name description = tahoe.description diagnostics_module_name = 'tahoe' port_forwarding_info = tahoe.port_forwarding_info diff --git a/plinth/modules/tor/__init__.py b/plinth/modules/tor/__init__.py index 0b80074ca..823051cb2 100644 --- a/plinth/modules/tor/__init__.py +++ b/plinth/modules/tor/__init__.py @@ -25,7 +25,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.firewall.components import Firewall from plinth.modules.names import SERVICES from plinth.signals import domain_added, domain_removed @@ -41,6 +41,8 @@ managed_packages = [ 'tor', 'tor-geoipdb', 'torsocks', 'obfs4proxy', 'apt-transport-tor' ] +managed_services = ['tor@plinth'] + name = _('Tor') short_description = _('Anonymity Network') @@ -58,9 +60,6 @@ clients = clients reserved_usernames = ['debian-tor'] -socks_service = None -bridge_service = None - manual_page = 'Tor' app = None @@ -87,6 +86,9 @@ class TorApp(app_module.App): 'tor-obfs4'], is_external=True) self.add(firewall) + daemon = Daemon('daemon-tor', managed_services[0], strict_check=True) + self.add(daemon) + def init(): """Initialize the module.""" @@ -97,20 +99,9 @@ def init(): needs_setup = setup_helper.get_state() == 'needs-setup' if not needs_setup: - if utils.is_enabled(): + if app.is_enabled(): app.set_enabled(True) - global socks_service - socks_service = service_module.Service( - 'tor-socks', _('Tor Socks Proxy'), is_enabled=utils.is_enabled, - is_running=utils.is_running) - - global bridge_service - bridge_service = service_module.Service('tor-bridge', - _('Tor Bridge Relay'), - is_enabled=utils.is_enabled, - is_running=utils.is_running) - # Register hidden service name with Name Services module. status = utils.get_status() hostname = status['hs_hostname'] @@ -141,44 +132,10 @@ def setup(helper, old_version=None): helper.call('post', actions.superuser_run, 'tor', ['configure', '--apt-transport-tor', 'enable']) - global socks_service - if socks_service is None: - socks_service = service_module.Service('tor-socks', - _('Tor Anonymity Network'), - is_enabled=utils.is_enabled, - is_running=utils.is_running) - - global bridge_service - if bridge_service is None: - bridge_service = service_module.Service('tor-bridge', - _('Tor Bridge Relay'), - is_enabled=utils.is_enabled, - is_running=utils.is_running) - helper.call('post', update_hidden_service_domain) helper.call('post', app.enable) -def enable(): - """Enable the app. - - XXX: Currently performs only partial activities while the rest happens - elsewhere. - - """ - app.enable() - - -def disable(): - """Enable the app. - - XXX: Currently performs only partial activities while the rest happens - elsewhere. - - """ - app.disable() - - def update_hidden_service_domain(status=None): """Update HS domain with Name Services module.""" if not status: diff --git a/plinth/modules/tor/templates/tor.html b/plinth/modules/tor/templates/tor.html index 8d7250011..3f369c7aa 100644 --- a/plinth/modules/tor/templates/tor.html +++ b/plinth/modules/tor/templates/tor.html @@ -1,4 +1,4 @@ -{% extends "simple_service.html" %} +{% extends "simple_app.html" %} {% comment %} # # This file is part of FreedomBox. @@ -83,7 +83,7 @@ {% endif %} - {% include "internal-zone.html" with service=socks_service %} + {% include "internal-zone.html" %}

{% trans "Configuration" %}

diff --git a/plinth/modules/tor/utils.py b/plinth/modules/tor/utils.py index a87ffad4d..dc93d42b5 100644 --- a/plinth/modules/tor/utils.py +++ b/plinth/modules/tor/utils.py @@ -14,36 +14,26 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ Tor utility functions """ -import augeas import glob import itertools import json -from plinth import actions -from plinth import action_utils -from plinth.modules.names import SERVICES +import augeas +from plinth import actions +from plinth.daemon import app_is_running +from plinth.modules import tor +from plinth.modules.names import SERVICES APT_SOURCES_URI_PATHS = ('/files/etc/apt/sources.list/*/uri', '/files/etc/apt/sources.list.d/*/*/uri') APT_TOR_PREFIX = 'tor+' -def is_enabled(): - """Return whether the module is enabled.""" - return action_utils.service_is_enabled('tor@plinth', strict_check=True) - - -def is_running(): - """Return whether the service is running.""" - return action_utils.service_is_running('tor@plinth') - - def get_status(): """Return current Tor status.""" output = actions.superuser_run('tor', ['get-status']) @@ -57,32 +47,34 @@ def get_status(): hs_services.append(service_type[0]) # Filter out obfs3/4 ports when bridge relay is disabled - ports = {service_type: port - for service_type, port in status['ports'].items() - if service_type not in ['obfs4', 'obfs3'] or - status['bridge_relay_enabled']} + ports = { + service_type: port + for service_type, port in status['ports'].items() + if service_type not in ['obfs4', 'obfs3'] + or status['bridge_relay_enabled'] + } - return {'enabled': is_enabled(), - 'is_running': is_running(), - 'use_upstream_bridges': status['use_upstream_bridges'], - 'upstream_bridges': status['upstream_bridges'], - 'relay_enabled': status['relay_enabled'], - 'bridge_relay_enabled': status['bridge_relay_enabled'], - 'ports': ports, - 'hs_enabled': hs_info['enabled'], - 'hs_status': hs_info['status'], - 'hs_hostname': hs_info['hostname'], - 'hs_ports': hs_info['ports'], - 'hs_services': hs_services, - 'apt_transport_tor_enabled': - is_apt_transport_tor_enabled() - } + return { + 'enabled': tor.app.is_enabled(), + 'is_running': app_is_running(tor.app), + 'use_upstream_bridges': status['use_upstream_bridges'], + 'upstream_bridges': status['upstream_bridges'], + 'relay_enabled': status['relay_enabled'], + 'bridge_relay_enabled': status['bridge_relay_enabled'], + 'ports': ports, + 'hs_enabled': hs_info['enabled'], + 'hs_status': hs_info['status'], + 'hs_hostname': hs_info['hostname'], + 'hs_ports': hs_info['ports'], + 'hs_services': hs_services, + 'apt_transport_tor_enabled': is_apt_transport_tor_enabled() + } def iter_apt_uris(aug): """Iterate over all the APT source URIs.""" - return itertools.chain.from_iterable([aug.match(path) - for path in APT_SOURCES_URI_PATHS]) + return itertools.chain.from_iterable( + [aug.match(path) for path in APT_SOURCES_URI_PATHS]) def get_real_apt_uri_path(aug, path): @@ -109,8 +101,8 @@ def get_real_apt_uri_path(aug, path): def get_augeas(): """Return an instance of Augeaus for processing APT configuration.""" - aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + - augeas.Augeas.NO_MODL_AUTOLOAD) + aug = augeas.Augeas( + flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD) aug.set('/augeas/load/Aptsources/lens', 'Aptsources.lns') aug.set('/augeas/load/Aptsources/incl[last() + 1]', '/etc/apt/sources.list') diff --git a/plinth/modules/tor/views.py b/plinth/modules/tor/views.py index a6a3da4a0..be1ecc4e4 100644 --- a/plinth/modules/tor/views.py +++ b/plinth/modules/tor/views.py @@ -19,11 +19,12 @@ FreedomBox app for configuring Tor. """ from django.contrib import messages from django.template.response import TemplateResponse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext as _ from plinth import actions from plinth.errors import ActionError from plinth.modules import tor +from plinth.modules.firewall.components import Firewall from . import utils as tor_utils from .forms import TorForm @@ -58,7 +59,7 @@ def index(request): 'status': status, 'config_running': bool(config_process), 'form': form, - 'socks_service': tor.socks_service + 'firewall': tor.app.get_components_of_type(Firewall) }) @@ -124,10 +125,11 @@ def __apply_changes(request, old_status, new_status): if old_status['enabled'] != new_status['enabled']: arg_value = 'enable' if new_status['enabled'] else 'disable' arguments.extend(['--service', arg_value]) + # XXX: Perform app enable/disable within the background process if new_status['enabled']: - tor.enable() + tor.app.enable() else: - tor.disable() + tor.app.disable() config_process = actions.superuser_run( 'tor', ['configure'] + arguments, run_in_background=True) diff --git a/plinth/modules/transmission/__init__.py b/plinth/modules/transmission/__init__.py index 689a0152e..be5c47b55 100644 --- a/plinth/modules/transmission/__init__.py +++ b/plinth/modules/transmission/__init__.py @@ -25,7 +25,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -55,8 +55,6 @@ reserved_usernames = ['debian-transmission'] group = ('bit-torrent', _('Download files using BitTorrent applications')) -service = None - manual_page = 'Transmission' app = None @@ -88,6 +86,9 @@ class TransmissionApp(app_module.App): webserver = Webserver('webserver-transmission', 'transmission-plinth') self.add(webserver) + daemon = Daemon('daemon-transmission', managed_services[0]) + self.add(daemon) + def init(): """Initialize the Transmission module.""" @@ -95,15 +96,9 @@ def init(): app = TransmissionApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -118,33 +113,9 @@ def setup(helper, old_version=None): ['merge-configuration'], input=json.dumps(new_configuration).encode()) - helper.call('post', actions.superuser_run, 'transmission', ['enable']) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled('transmission-daemon') - and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('transmission', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('transmission', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/transmission/forms.py b/plinth/modules/transmission/forms.py index 590bc6f65..da8f8bcf8 100644 --- a/plinth/modules/transmission/forms.py +++ b/plinth/modules/transmission/forms.py @@ -21,10 +21,10 @@ FreedomBox app for configuring Transmission. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ServiceForm +from plinth.forms import AppForm -class TransmissionForm(ServiceForm): # pylint: disable=W0232 +class TransmissionForm(AppForm): # pylint: disable=W0232 """Transmission configuration form""" download_dir = forms.CharField( label=_('Download directory'), diff --git a/plinth/modules/transmission/urls.py b/plinth/modules/transmission/urls.py index 2aec2935b..94d4d38ad 100644 --- a/plinth/modules/transmission/urls.py +++ b/plinth/modules/transmission/urls.py @@ -14,17 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # - """ URLs for the Transmission module. """ from django.conf.urls import url -from .views import TransmissionServiceView - +from .views import TransmissionAppView urlpatterns = [ - url(r'^apps/transmission/$', - TransmissionServiceView.as_view(), name='index'), + url(r'^apps/transmission/$', TransmissionAppView.as_view(), name='index'), ] diff --git a/plinth/modules/transmission/views.py b/plinth/modules/transmission/views.py index b3e1443a7..4a7a73edb 100644 --- a/plinth/modules/transmission/views.py +++ b/plinth/modules/transmission/views.py @@ -33,28 +33,28 @@ from .forms import TransmissionForm logger = logging.getLogger(__name__) -class TransmissionServiceView(views.ServiceView): +class TransmissionAppView(views.AppView): """Serve configuration page.""" clients = transmission.clients + name = transmission.name description = transmission.description diagnostics_module_name = 'transmission' form_class = TransmissionForm - service_id = transmission.managed_services[0] + app_id = 'transmission' manual_page = transmission.manual_page def get_initial(self): """Get the current settings from Transmission server.""" + status = super().get_initial() configuration = actions.superuser_run('transmission', ['get-configuration']) - status = json.loads(configuration) - status = { + configuration = json.loads(configuration) + status.update({ key.translate(str.maketrans({ '-': '_' })): value - for key, value in status.items() - } - status['is_enabled'] = self.service.is_enabled() - status['is_running'] = self.service.is_running() + for key, value in configuration.items() + }) status['hostname'] = socket.gethostname() return status diff --git a/plinth/modules/ttrss/__init__.py b/plinth/modules/ttrss/__init__.py index b51f21f77..7e25e0a1e 100644 --- a/plinth/modules/ttrss/__init__.py +++ b/plinth/modules/ttrss/__init__.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils, actions from plinth import app as app_module from plinth import cfg, frontpage, menu -from plinth import service as service_module +from plinth.daemon import Daemon from plinth.modules.apache.components import Webserver from plinth.modules.firewall.components import Firewall from plinth.modules.users import register_group @@ -62,8 +62,6 @@ clients = clients group = ('feed-reader', _('Read and subscribe to news feeds')) -service = None - manual_page = 'TinyTinyRSS' app = None @@ -94,6 +92,14 @@ class TTRSSApp(app_module.App): webserver = Webserver('webserver-ttrss', 'tt-rss-plinth') self.add(webserver) + daemon = Daemon('daemon-ttrss', managed_services[0]) + self.add(daemon) + + def enable(self): + """Enable components and API access.""" + super().enable() + actions.superuser_run('ttrss', ['enable-api-access']) + def init(): """Intialize the module.""" @@ -101,15 +107,9 @@ def init(): app = TTRSSApp() register_group(group) - global service setup_helper = globals()['setup_helper'] - if setup_helper.get_state() != 'needs-setup': - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) - - if is_enabled(): - app.set_enabled(True) + if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): + app.set_enabled(True) def setup(helper, old_version=None): @@ -117,12 +117,6 @@ def setup(helper, old_version=None): helper.call('pre', actions.superuser_run, 'ttrss', ['pre-setup']) helper.install(managed_packages) helper.call('post', actions.superuser_run, 'ttrss', ['setup']) - helper.call('post', actions.superuser_run, 'ttrss', ['enable']) - global service - if service is None: - service = service_module.Service(managed_services[0], name, - is_enabled=is_enabled, enable=enable, - disable=disable) helper.call('post', app.enable) @@ -141,23 +135,6 @@ def force_upgrade(helper, packages): actions.superuser_run('ttrss', ['setup']) -def is_enabled(): - """Return whether the module is enabled.""" - return (action_utils.service_is_enabled('tt-rss') and app.is_enabled()) - - -def enable(): - """Enable the module.""" - actions.superuser_run('ttrss', ['enable']) - app.enable() - - -def disable(): - """Enable the module.""" - actions.superuser_run('ttrss', ['disable']) - app.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/ttrss/urls.py b/plinth/modules/ttrss/urls.py index e85d8163d..7321e3216 100644 --- a/plinth/modules/ttrss/urls.py +++ b/plinth/modules/ttrss/urls.py @@ -21,13 +21,14 @@ URLs for the Tiny Tiny RSS module. from django.conf.urls import url from plinth.modules import ttrss -from plinth.views import ServiceView +from plinth.views import AppView urlpatterns = [ - url(r'^apps/ttrss/$', - ServiceView.as_view( - service_id=ttrss.managed_services[0], - diagnostics_module_name="ttrss", description=ttrss.description, - clients=ttrss.clients, manual_page=ttrss.manual_page, - show_status_block=True), name='index'), + url( + r'^apps/ttrss/$', + AppView.as_view(app_id='ttrss', name=ttrss.name, + diagnostics_module_name='ttrss', + description=ttrss.description, clients=ttrss.clients, + manual_page=ttrss.manual_page, show_status_block=True), + name='index'), ] diff --git a/plinth/modules/upgrades/__init__.py b/plinth/modules/upgrades/__init__.py index 27d3c16ab..8270c382e 100644 --- a/plinth/modules/upgrades/__init__.py +++ b/plinth/modules/upgrades/__init__.py @@ -23,7 +23,6 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth import app as app_module from plinth import menu -from plinth import service as service_module from .manifest import backup @@ -39,8 +38,6 @@ description = [ _('Check for and apply the latest software and security updates.') ] -service = None - manual_page = 'Upgrades' app = None @@ -65,11 +62,6 @@ def init(): app = UpgradesApp() app.set_enabled(True) - global service - service = service_module.Service('auto-upgrades', name, - is_enabled=is_enabled, enable=enable, - disable=disable) - def setup(helper, old_version=None): """Install and configure the module.""" diff --git a/plinth/modules/upgrades/templates/upgrades.html b/plinth/modules/upgrades/templates/upgrades.html index a08381c9a..86aea4f3d 100644 --- a/plinth/modules/upgrades/templates/upgrades.html +++ b/plinth/modules/upgrades/templates/upgrades.html @@ -1,4 +1,4 @@ -{% extends 'service-subsubmenu.html' %} +{% extends 'app-subsubmenu.html' %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/upgrades/templates/upgrades_configure.html b/plinth/modules/upgrades/templates/upgrades_configure.html index 6d162c2e8..bbe32f5fb 100644 --- a/plinth/modules/upgrades/templates/upgrades_configure.html +++ b/plinth/modules/upgrades/templates/upgrades_configure.html @@ -1,4 +1,4 @@ -{% extends "service-subsubmenu.html" %} +{% extends "app-subsubmenu.html" %} {% comment %} # # This file is part of FreedomBox. diff --git a/plinth/modules/upgrades/views.py b/plinth/modules/upgrades/views.py index 2c8a3f2f2..45761df7a 100644 --- a/plinth/modules/upgrades/views.py +++ b/plinth/modules/upgrades/views.py @@ -34,10 +34,11 @@ from .forms import ConfigureForm subsubmenu = [{ 'url': reverse_lazy('upgrades:index'), 'text': ugettext_lazy('Auto-update') -}, { - 'url': reverse_lazy('upgrades:upgrade'), - 'text': ugettext_lazy('Manual update') -}] +}, + { + 'url': reverse_lazy('upgrades:upgrade'), + 'text': ugettext_lazy('Manual update') + }] class UpgradesConfigurationView(FormView): @@ -56,7 +57,7 @@ class UpgradesConfigurationView(FormView): return context def get_initial(self): - return {'auto_upgrades_enabled': upgrades.service.is_enabled()} + return {'auto_upgrades_enabled': upgrades.is_enabled()} def form_valid(self, form): """Apply the form changes.""" @@ -68,15 +69,15 @@ class UpgradesConfigurationView(FormView): try: if new_status['auto_upgrades_enabled']: - upgrades.service.enable() + upgrades.enable() else: - upgrades.service.disable() + upgrades.disable() except ActionError as exception: error = exception.args[2] messages.error( self.request, - _('Error when configuring unattended-upgrades: {error}') - .format(error=error)) + _('Error when configuring unattended-upgrades: {error}'). + format(error=error)) if new_status['auto_upgrades_enabled']: messages.success(self.request, _('Automatic upgrades enabled')) diff --git a/plinth/service.py b/plinth/service.py deleted file mode 100644 index 38b35e176..000000000 --- a/plinth/service.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# This file is part of FreedomBox. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -""" -Framework for working with servers and their services. -""" - -import collections - -from plinth import action_utils, actions - -services = {} - - -class Service(): - """ - Representation of an application service provided by the machine - containing information such as current status. - - - service_id: unique service name. If possible this should be the name of - the service's systemd unit file (without the extension). - - name: service name as to be displayed in the GUI - - is_enabled (optional): Boolean or a method returning Boolean - - enable (optional): method - - disable (optional): method - - is_running (optional): Boolean or a method returning Boolean - """ - - def __init__(self, service_id, name, is_enabled=None, enable=None, - disable=None, is_running=None): - if is_enabled is None: - is_enabled = self._default_is_enabled - - self.service_id = service_id - self.name = name - self._is_enabled = is_enabled - self._enable = enable - self._disable = disable - self._is_running = is_running - - # Maintain a complete list of services - assert service_id not in services - services[service_id] = self - - def enable(self): - if self._enable is None: - actions.superuser_run('service', ['enable', self.service_id]) - else: - self._call_or_return(self._enable) - - def disable(self): - if self._disable is None: - actions.superuser_run('service', ['disable', self.service_id]) - else: - self._call_or_return(self._disable) - - def is_enabled(self): - """Return whether the service is enabled.""" - # TODO: we could cache the service state if we only use this service - # interface to change service status. - return self._call_or_return(self._is_enabled) - - def is_running(self): - """Return whether the service is running.""" - if self._is_running is None: - return action_utils.service_is_running(self.service_id) - - return self._call_or_return(self._is_running) - - @staticmethod - def _call_or_return(obj): - """Calls obj if it's callable, returns it if it's Boolean.""" - if isinstance(obj, collections.abc.Callable): - return obj() - - if isinstance(obj, bool): - return obj - - message = 'obj is expected to be callable or a boolean.' - raise ValueError(message) - - def _default_is_enabled(self): - """Returns is_enabled relying on a correct service_id""" - return action_utils.service_is_enabled(self.service_id) - - @staticmethod - def get_internal_interfaces(): - """Returns a list of interfaces in a firewall zone.""" - from plinth.modules import firewall - return firewall.get_interfaces('internal') diff --git a/plinth/templates/service-subsubmenu.html b/plinth/templates/app-subsubmenu.html similarity index 95% rename from plinth/templates/service-subsubmenu.html rename to plinth/templates/app-subsubmenu.html index 778d32ae3..0757a5a1a 100644 --- a/plinth/templates/service-subsubmenu.html +++ b/plinth/templates/app-subsubmenu.html @@ -49,7 +49,7 @@

{% endif %} - {% include "clients.html" with clients=clients enabled=service.is_enabled %} + {% include "clients.html" with clients=clients enabled=is_enabled %} {% block subsubmenu %} {% if subsubmenu %} diff --git a/plinth/templates/service.html b/plinth/templates/app.html similarity index 85% rename from plinth/templates/service.html rename to plinth/templates/app.html index 72431dc3d..d8b8e8122 100644 --- a/plinth/templates/service.html +++ b/plinth/templates/app.html @@ -18,7 +18,7 @@ # {% endcomment %} -{# Template to display/configure a Service, used by views.ServiceView #} +{# Template to display/configure an App, used by views.AppView #} {% load bootstrap %} {% load i18n %} @@ -27,7 +27,7 @@ {% block content %} {% block pagetitle %} -

{{ service.name }}

+

{{ name }}

{% endblock %} {% block description %} @@ -44,14 +44,14 @@

{% endif %} - {% include "clients.html" with clients=clients enabled=service.is_enabled %} + {% include "clients.html" with clients=clients enabled=is_enabled %} {% block status %} {% if show_status_block %}

{% trans "Status" %}

- {% with service_name=service.name %} - {% if service.is_running %} + {% with service_name=name %} + {% if is_running %} {% blocktrans trimmed %} Service {{ service_name }} is running. @@ -69,13 +69,13 @@ {% block diagnostics %} {% if diagnostics_module_name %} - {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=service.is_enabled %} + {% include "diagnostics_button.html" with module=diagnostics_module_name enabled=is_enabled %} {% endif %} {% endblock %} {% include "internal-zone.html" %} - {% include "port-forwarding-info.html" with service_name=service.name %} + {% include "port-forwarding-info.html" with service_name=name %} {% block configuration %}

{% trans "Configuration" %}

diff --git a/plinth/templates/index.html b/plinth/templates/index.html index dcad14631..35e7073c6 100644 --- a/plinth/templates/index.html +++ b/plinth/templates/index.html @@ -52,7 +52,7 @@ {% endfor %} {% endblock %} - {% include "clients.html" with clients=clients enabled=service.is_enabled %} + {% include "clients.html" with clients=selected_shortcut.clients enabled=True %} {% if user.is_authenticated and user_is_admin and selected_shortcut.configure_url %} diff --git a/plinth/templates/internal-zone.html b/plinth/templates/internal-zone.html index 0d8ecaa57..a5931264c 100644 --- a/plinth/templates/internal-zone.html +++ b/plinth/templates/internal-zone.html @@ -20,22 +20,24 @@ {% load i18n %} {% block internal_zone_warning %} - {% if not service.is_external %} -
- {% blocktrans trimmed with service_name=service.name %} - {{ service_name }} is available only on internal networks. - {% endblocktrans %} -

- {% with interfaces=service.get_internal_interfaces %} - {% if not interfaces %} - {% trans "Currently there are no network interfaces configured as internal." %} - {% else %} - {% blocktrans trimmed with interface_list=interfaces|join:", " %} - Currently the following network interfaces are configured as internal: {{ interface_list }} - {% endblocktrans %} - {% endif %} - {% endwith %} -

-
- {% endif %} + {% for component in firewall %} + {% if not component.is_external %} +
+ {% blocktrans trimmed with service_name=component.name %} + {{ service_name }} is available only on internal networks. + {% endblocktrans %} +

+ {% with interfaces=component.get_internal_interfaces %} + {% if not interfaces %} + {% trans "Currently there are no network interfaces configured as internal." %} + {% else %} + {% blocktrans trimmed with interface_list=interfaces|join:", " %} + Currently the following network interfaces are configured as internal: {{ interface_list }} + {% endblocktrans %} + {% endif %} + {% endwith %} +

+
+ {% endif %} + {% endfor %} {% endblock %} diff --git a/plinth/templates/simple_service.html b/plinth/templates/simple_app.html similarity index 100% rename from plinth/templates/simple_service.html rename to plinth/templates/simple_app.html diff --git a/plinth/tests/test_app.py b/plinth/tests/test_app.py index 815b3d482..3afd40caa 100644 --- a/plinth/tests/test_app.py +++ b/plinth/tests/test_app.py @@ -95,6 +95,25 @@ def test_get_component(app_with_components): app.get_component('x-invalid-component') +def test_get_components_of_type(app_with_components): + """Test retrieving list of components of a given type.""" + app = app_with_components + components = app.get_components_of_type(FollowerComponent) + follower_components = [ + app.components['test-follower-1'], + app.components['test-follower-2'], + app.components['test-leader-1'], + app.components['test-leader-2'], + ] + assert list(components) == follower_components + components = app.get_components_of_type(LeaderTest) + leader_components = [ + app.components['test-leader-1'], + app.components['test-leader-2'], + ] + assert list(components) == leader_components + + def test_app_enable(app_with_components): """Test that enabling an app enables components.""" app_with_components.disable() @@ -136,6 +155,12 @@ def test_app_is_enabled(app_with_components): assert app.is_enabled() +def test_app_is_enabled_with_no_leader_components(): + """When there are not leader components, app.is_enabled() returns True.""" + app = TestApp() + assert app.is_enabled() + + def test_app_set_enabled(app_with_components): """Test that setting enabled effects only followers.""" app = app_with_components diff --git a/plinth/tests/test_daemon.py b/plinth/tests/test_daemon.py new file mode 100644 index 000000000..4e448348a --- /dev/null +++ b/plinth/tests/test_daemon.py @@ -0,0 +1,119 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Test module for component managing system daemons and other systemd units. +""" + +from unittest.mock import Mock, call, patch + +import pytest + +from plinth.app import App, FollowerComponent +from plinth.daemon import Daemon, app_is_running + + +@pytest.fixture(name='daemon') +def fixture_daemon(): + """Create a test daemon object.""" + return Daemon('test-daemon', 'test-unit') + + +def test_initialization(): + """Test that component is initialized properly.""" + with pytest.raises(ValueError): + Daemon(None, None) + + daemon = Daemon('test-daemon', 'test-unit') + assert daemon.component_id == 'test-daemon' + assert daemon.unit == 'test-unit' + assert not daemon.strict_check + + daemon = Daemon('test-daemon', 'test-unit', strict_check=True) + assert daemon.strict_check + + +@patch('plinth.action_utils.service_is_enabled') +def test_is_enabled(service_is_enabled, daemon): + """Test that daemon enabled check works.""" + service_is_enabled.return_value = True + assert daemon.is_enabled() + service_is_enabled.assert_has_calls( + [call('test-unit', strict_check=False)]) + + service_is_enabled.return_value = False + assert not daemon.is_enabled() + + service_is_enabled.reset_mock() + daemon.strict_check = True + daemon.is_enabled() + service_is_enabled.assert_has_calls([call('test-unit', strict_check=True)]) + + +@patch('plinth.actions.superuser_run') +def test_enable(superuser_run, daemon): + """Test that enabling the daemon works.""" + daemon.enable() + superuser_run.assert_has_calls([call('service', ['enable', 'test-unit'])]) + + +@patch('plinth.actions.superuser_run') +def test_disable(superuser_run, daemon): + """Test that disabling the daemon works.""" + daemon.disable() + superuser_run.assert_has_calls([call('service', ['disable', 'test-unit'])]) + + +@patch('plinth.action_utils.service_is_running') +def test_is_running(service_is_running, daemon): + """Test that checking that the daemon is running works.""" + service_is_running.return_value = True + assert daemon.is_running() + service_is_running.assert_has_calls([call('test-unit')]) + + service_is_running.return_value = False + assert not daemon.is_running() + + +@patch('plinth.action_utils.service_is_running') +def test_app_is_running(service_is_running): + """Test that checking whether app is running works.""" + daemon1 = Daemon('test-daemon-1', 'test-unit-1') + daemon2 = FollowerComponent('test-daemon-2', 'test-unit-2') + daemon2.is_running = Mock() + + follower1 = FollowerComponent('test-follower-1') + + class TestApp(App): + """Test app""" + app_id = 'test-app' + + app = TestApp() + app.add(daemon1) + app.add(daemon2) + app.add(follower1) + + service_is_running.return_value = True + daemon2.is_running.return_value = False + assert not app_is_running(app) + + service_is_running.return_value = False + daemon2.is_running.return_value = False + assert not app_is_running(app) + + service_is_running.return_value = True + daemon2.is_running.return_value = True + assert app_is_running(app) diff --git a/plinth/views.py b/plinth/views.py index 97bcb0e46..b1ddf259e 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -30,8 +30,9 @@ from django.views.generic import TemplateView from django.views.generic.edit import FormView from stronghold.decorators import public -import plinth from plinth import package +from plinth.app import App +from plinth.daemon import app_is_running from plinth.modules.config import get_advanced_mode from plinth.modules.storage import views as disk_views from plinth.translation import get_language_from_request, set_language @@ -106,47 +107,60 @@ class LanguageSelectionView(FormView): return reverse('index') -class ServiceView(FormView): - """A generic view for configuring simple services.""" +class AppView(FormView): + """A generic view for configuring simple apps.""" clients = [] # Set diagnostics_module_name to the module name to show diagnostics button diagnostics_module_name = "" + name = None # List of paragraphs describing the service description = "" - form_class = forms.ServiceForm - # Display the 'status' block of the service.html template + form_class = forms.AppForm + # Display the 'status' block of the app.html template # This block uses information from service.is_running. This method is # optional, so allow not showing this block here. show_status_block = True - service_id = None - template_name = 'service.html' - manual_page = "" + app_id = None + template_name = 'app.html' + manual_page = '' port_forwarding_info = None + def __init__(self, *args, **kwargs): + """Intialize the view.""" + super().__init__(*args, **kwargs) + self._common_status = None + + if not self.name: + raise ImproperlyConfigured('Missing name attribute') + @property def success_url(self): return self.request.path @property - def service(self): - if hasattr(self, '_service'): - return self._service + def app(self): + """Return the app for which this view is configured.""" + if not self.app_id: + raise ImproperlyConfigured('Missing attribute: app_id') - if not self.service_id: - raise ImproperlyConfigured("missing attribute: 'service_id'") - service = plinth.service.services.get(self.service_id, None) - if service is None: - message = "Could not find service %s" % self.service_id - raise ImproperlyConfigured(message) - self._service = service - return service + return App.get(self.app_id) + + def _get_common_status(self): + """Return the status needed for form and template. + + Avoid multiple queries to expensive operations such as + app.is_enabled(). + + """ + if self._common_status: + return self._common_status + + self._common_status = {'is_enabled': self.app.is_enabled()} + return self._common_status def get_initial(self): - """Return the status of the service to fill in the form.""" - return { - 'is_enabled': self.service.is_enabled(), - 'is_running': self.service.is_running() - } + """Return the status of the app to fill in the form.""" + return self._get_common_status() def form_valid(self, form): """Enable/disable a service and set messages.""" @@ -160,10 +174,10 @@ class ServiceView(FormView): messages.info(self.request, _('Setting unchanged')) else: if new_status['is_enabled']: - self.service.enable() + self.app.enable() messages.success(self.request, _('Application enabled')) else: - self.service.disable() + self.app.disable() messages.success(self.request, _('Application disabled')) return super().form_valid(form) @@ -171,13 +185,20 @@ class ServiceView(FormView): def get_context_data(self, *args, **kwargs): """Add service to the context data.""" context = super().get_context_data(*args, **kwargs) - context['service'] = self.service + context.update(self._get_common_status()) + context['app'] = self.app + context['is_running'] = app_is_running(self.app) context['clients'] = self.clients context['diagnostics_module_name'] = self.diagnostics_module_name + context['name'] = self.name context['description'] = self.description context['show_status_block'] = self.show_status_block context['manual_page'] = self.manual_page context['port_forwarding_info'] = self.port_forwarding_info + + from plinth.modules.firewall.components import Firewall + context['firewall'] = self.app.get_components_of_type(Firewall) + return context