From b576a77c3d1dc804256045db4a33930d0c0ad01f Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Mon, 10 Feb 2020 17:42:42 -0800 Subject: [PATCH] app: Refactor all apps to use the Info component - Remove the need to pass all the individual information elements to the AppView separately. This eliminates many issues with elements that were mistakenly not sent to AppView. Also reduces a lot of code duplication. - Create App classes for power and sso for consistency. Reviewed-by: James Valleroy --- doc/dev/tutorial/view.rst | 2 - plinth/modules/apache/__init__.py | 4 ++ plinth/modules/avahi/__init__.py | 18 ++++---- plinth/modules/backups/__init__.py | 16 ++++--- plinth/modules/backups/forms.py | 4 +- plinth/modules/backups/views.py | 4 +- plinth/modules/bind/__init__.py | 18 ++++---- plinth/modules/bind/views.py | 4 +- plinth/modules/cockpit/__init__.py | 37 ++++++++-------- plinth/modules/cockpit/views.py | 7 --- plinth/modules/config/__init__.py | 15 ++++--- plinth/modules/config/views.py | 3 -- plinth/modules/coquelicot/__init__.py | 33 +++++++------- plinth/modules/coquelicot/views.py | 8 +--- plinth/modules/datetime/__init__.py | 15 ++++--- plinth/modules/datetime/views.py | 4 -- plinth/modules/deluge/__init__.py | 40 ++++++++--------- plinth/modules/deluge/views.py | 10 +---- plinth/modules/diagnostics/__init__.py | 15 ++++--- plinth/modules/diagnostics/diagnostics.py | 4 +- plinth/modules/diaspora/__init__.py | 31 ++++++------- plinth/modules/diaspora/views.py | 6 +-- plinth/modules/dynamicdns/__init__.py | 15 ++++--- plinth/modules/dynamicdns/views.py | 14 ++---- plinth/modules/ejabberd/__init__.py | 34 +++++++-------- plinth/modules/ejabberd/views.py | 5 --- plinth/modules/firewall/__init__.py | 14 +++--- plinth/modules/firewall/views.py | 3 -- plinth/modules/gitweb/__init__.py | 37 ++++++++-------- plinth/modules/gitweb/views.py | 7 +-- plinth/modules/help/__init__.py | 9 +++- plinth/modules/i2p/__init__.py | 37 ++++++++-------- plinth/modules/i2p/views.py | 17 ++------ plinth/modules/ikiwiki/__init__.py | 34 +++++++-------- plinth/modules/ikiwiki/views.py | 12 +---- plinth/modules/infinoted/__init__.py | 40 ++++++++--------- plinth/modules/jsxc/__init__.py | 33 +++++++------- plinth/modules/jsxc/views.py | 6 +-- plinth/modules/letsencrypt/__init__.py | 22 +++++----- plinth/modules/letsencrypt/views.py | 4 +- plinth/modules/matrixsynapse/__init__.py | 38 ++++++++-------- plinth/modules/matrixsynapse/views.py | 12 +---- plinth/modules/mediawiki/__init__.py | 35 +++++++-------- plinth/modules/mediawiki/views.py | 5 --- plinth/modules/minetest/__init__.py | 34 +++++++-------- plinth/modules/minetest/views.py | 7 +-- plinth/modules/minidlna/__init__.py | 32 +++++++------- plinth/modules/minidlna/views.py | 5 --- plinth/modules/mldonkey/__init__.py | 39 ++++++++--------- plinth/modules/mldonkey/urls.py | 14 +++--- plinth/modules/monkeysphere/__init__.py | 18 ++++---- plinth/modules/monkeysphere/views.py | 8 ++-- plinth/modules/mumble/__init__.py | 34 +++++++-------- plinth/modules/mumble/views.py | 8 +--- plinth/modules/names/__init__.py | 15 ++++--- plinth/modules/names/views.py | 11 ++--- plinth/modules/networks/__init__.py | 14 +++--- plinth/modules/networks/networks.py | 21 ++++----- plinth/modules/openvpn/__init__.py | 32 +++++++------- plinth/modules/openvpn/views.py | 6 +-- plinth/modules/pagekite/__init__.py | 22 +++++----- plinth/modules/pagekite/views.py | 6 +-- plinth/modules/power/__init__.py | 27 ++++++++++-- plinth/modules/power/views.py | 15 +++---- plinth/modules/privoxy/__init__.py | 33 ++++++-------- plinth/modules/quassel/__init__.py | 36 ++++++++------- plinth/modules/quassel/views.py | 5 --- plinth/modules/radicale/__init__.py | 38 ++++++++-------- plinth/modules/radicale/views.py | 8 +--- plinth/modules/roundcube/__init__.py | 34 +++++++-------- plinth/modules/roundcube/urls.py | 11 ++--- plinth/modules/samba/__init__.py | 30 ++++++------- plinth/modules/samba/views.py | 3 -- plinth/modules/searx/__init__.py | 31 ++++++------- plinth/modules/searx/views.py | 5 --- plinth/modules/security/__init__.py | 11 ++--- plinth/modules/security/views.py | 3 +- plinth/modules/shaarli/__init__.py | 36 +++++++-------- plinth/modules/shaarli/urls.py | 10 ++--- plinth/modules/shadowsocks/__init__.py | 31 ++++++------- plinth/modules/shadowsocks/views.py | 5 --- plinth/modules/sharing/__init__.py | 16 ++++--- plinth/modules/sharing/views.py | 5 +-- plinth/modules/snapshot/__init__.py | 14 +++--- plinth/modules/snapshot/views.py | 18 +++----- plinth/modules/ssh/__init__.py | 14 +++--- plinth/modules/ssh/views.py | 3 -- plinth/modules/sso/__init__.py | 18 ++++++-- plinth/modules/sso/views.py | 5 ++- plinth/modules/storage/__init__.py | 14 +++--- plinth/modules/storage/views.py | 3 -- plinth/modules/syncthing/__init__.py | 38 ++++++++-------- plinth/modules/syncthing/urls.py | 12 ++--- plinth/modules/tahoe/__init__.py | 53 ++++++++++++----------- plinth/modules/tahoe/views.py | 9 +--- plinth/modules/tor/__init__.py | 24 +++++----- plinth/modules/tor/views.py | 6 +-- plinth/modules/transmission/__init__.py | 38 ++++++++-------- plinth/modules/transmission/views.py | 6 --- plinth/modules/ttrss/__init__.py | 37 ++++++++-------- plinth/modules/ttrss/urls.py | 10 +---- plinth/modules/upgrades/__init__.py | 14 +++--- plinth/modules/upgrades/views.py | 3 -- plinth/modules/users/__init__.py | 14 +++--- plinth/modules/users/views.py | 5 +-- plinth/modules/wireguard/__init__.py | 29 +++++++------ plinth/modules/wireguard/views.py | 3 -- plinth/templates/app.html | 4 +- plinth/templates/header.html | 18 ++++---- plinth/templates/setup.html | 6 +-- plinth/templates/toolbar.html | 6 +-- plinth/views.py | 29 +++---------- 112 files changed, 862 insertions(+), 1041 deletions(-) diff --git a/doc/dev/tutorial/view.rst b/doc/dev/tutorial/view.rst index 4e04cfb7e..1ad7a5ac6 100644 --- a/doc/dev/tutorial/view.rst +++ b/doc/dev/tutorial/view.rst @@ -100,8 +100,6 @@ view to show the app page for our app. In ``views.py``, let us add a view. class TransmissionAppView(views.AppView): """Serve configuration page.""" - name = transmission.name - description = transmission.description app_id = 'transmission' The base view :class:`~plinth.views.AppView` takes care of a lot of details for diff --git a/plinth/modules/apache/__init__.py b/plinth/modules/apache/__init__.py index bb47a30d6..29d81a2e4 100644 --- a/plinth/modules/apache/__init__.py +++ b/plinth/modules/apache/__init__.py @@ -48,6 +48,10 @@ class ApacheApp(app_module.App): """Create components for the app.""" super().__init__() + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential) + self.add(info) + web_server_ports = Firewall('firewall-web', _('Web Server'), ports=['http', 'https'], is_external=True) self.add(web_server_ports) diff --git a/plinth/modules/avahi/__init__.py b/plinth/modules/avahi/__init__.py index d58de5b9e..18a536a18 100644 --- a/plinth/modules/avahi/__init__.py +++ b/plinth/modules/avahi/__init__.py @@ -43,9 +43,7 @@ managed_services = ['avahi-daemon'] managed_packages = ['avahi-daemon', 'avahi-utils'] -name = _('Service Discovery') - -description = [ +_description = [ format_lazy( _('Service discovery allows other devices on the network to ' 'discover your {box_name} and services running on it. It ' @@ -69,7 +67,14 @@ class AvahiApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-avahi', name, None, 'fa-compass', + + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Service Discovery'), icon='fa-compass', + description=_description, + manual_page='ServiceDiscovery') + self.add(info) + + menu_item = menu.Menu('menu-avahi', info.name, None, info.icon, 'avahi:index', parent_url_name='system') self.add(menu_item) @@ -78,7 +83,7 @@ class AvahiApp(app_module.App): can_have_certificate=False) self.add(domain_type) - firewall = Firewall('firewall-avahi', name, ports=['mdns'], + firewall = Firewall('firewall-avahi', info.name, ports=['mdns'], is_external=False) self.add(firewall) @@ -123,6 +128,3 @@ def on_post_hostname_change(sender, old_hostname, new_hostname, **kwargs): class AvahiAppView(AppView): app_id = 'avahi' - name = name - description = description - manual_page = manual_page diff --git a/plinth/modules/backups/__init__.py b/plinth/modules/backups/__init__.py index d355848b0..49520e746 100644 --- a/plinth/modules/backups/__init__.py +++ b/plinth/modules/backups/__init__.py @@ -37,16 +37,12 @@ version = 2 managed_packages = ['borgbackup', 'sshfs'] -name = _('Backups') - depends = ['storage'] -description = [ +_description = [ _('Backups allows creating and managing backup archives.'), ] -manual_page = 'Backups' - MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/' # session variable name that stores when a backup file should be deleted SESSION_PATH_VARIABLE = 'fbx-backups-upload-path' @@ -62,7 +58,13 @@ class BackupsApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-backups', name, None, 'fa-files-o', + info = app_module.Info(app_id=self.app_id, version=version, + depends=depends, name=_('Backups'), + icon='fa-files-o', description=_description, + manual_page='Backups') + self.add(info) + + menu_item = menu.Menu('menu-backups', info.name, None, info.icon, 'backups:index', parent_url_name='system') self.add(menu_item) @@ -96,7 +98,7 @@ def _backup_handler(packet, encryption_passphrase=None): manifests = { 'apps': [{ 'name': app.name, - 'version': app.app.version, + 'version': app.app.app.info.version, 'backup': app.manifest } for app in packet.apps] } diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index 23697bf14..e4532ddd7 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -43,10 +43,10 @@ def _get_app_choices(apps): """Return a list of check box multiple choices from list of apps.""" choices = [] for app in apps: - name = app.app.name + name = app.app.app.info.name if not app.has_data: name = ugettext('{app} (No data to backup)').format( - app=app.app.name) + app=app.app.app.info.name) choices.append((app.name, name)) diff --git a/plinth/modules/backups/views.py b/plinth/modules/backups/views.py index b2715cd2b..dbe3a20fc 100644 --- a/plinth/modules/backups/views.py +++ b/plinth/modules/backups/views.py @@ -54,9 +54,7 @@ class IndexView(TemplateView): def get_context_data(self, **kwargs): """Return additional context for rendering the template.""" context = super().get_context_data(**kwargs) - context['name'] = backups.name - context['description'] = backups.description - context['manual_page'] = backups.manual_page + context['app_info'] = backups.app.info context['repositories'] = [ repository.get_view_content() for repository in get_repositories() ] diff --git a/plinth/modules/bind/__init__.py b/plinth/modules/bind/__init__.py index 353fc27a3..9b5d68f4b 100644 --- a/plinth/modules/bind/__init__.py +++ b/plinth/modules/bind/__init__.py @@ -36,15 +36,11 @@ from .manifest import backup # noqa, pylint: disable=unused-import version = 2 -name = _('BIND') - -short_description = _('Domain Name Server') - managed_services = ['bind9'] managed_packages = ['bind9'] -description = [ +_description = [ _('BIND enables you to publish your Domain Name System (DNS) information ' 'on the Internet, and to resolve DNS queries for your user devices on ' 'your network.'), @@ -97,12 +93,18 @@ class BindApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-bind', name, short_description, - 'fa-globe-w', 'bind:index', + info = app_module.Info(app_id=self.app_id, version=version, + name=_('BIND'), icon='fa-globe-w', + short_description=_('Domain Name Server'), + description=_description) + self.add(info) + + menu_item = menu.Menu('menu-bind', info.name, info.short_description, + info.icon, 'bind:index', parent_url_name='system') self.add(menu_item) - firewall = Firewall('firewall-bind', name, ports=['dns'], + firewall = Firewall('firewall-bind', info.name, ports=['dns'], is_external=False) self.add(firewall) diff --git a/plinth/modules/bind/views.py b/plinth/modules/bind/views.py index 8649b7c01..ba2ad65a4 100644 --- a/plinth/modules/bind/views.py +++ b/plinth/modules/bind/views.py @@ -25,15 +25,13 @@ from plinth import actions from plinth.views import AppView from plinth.modules import bind, names -from . import description, get_config, name, port_forwarding_info +from . import get_config, port_forwarding_info from .forms import BindForm class BindAppView(AppView): # pylint: disable=too-many-ancestors """A specialized view for configuring Bind.""" app_id = 'bind' - name = name - description = description show_status_block = True form_class = BindForm template_name = 'bind.html' diff --git a/plinth/modules/cockpit/__init__.py b/plinth/modules/cockpit/__init__.py index df667496b..0475f1b65 100644 --- a/plinth/modules/cockpit/__init__.py +++ b/plinth/modules/cockpit/__init__.py @@ -42,13 +42,7 @@ managed_services = ['cockpit.socket'] managed_packages = ['cockpit'] -name = _('Cockpit') - -icon_filename = 'cockpit' - -short_description = _('Server Administration') - -description = [ +_description = [ format_lazy( _('Cockpit is a server manager that makes it easy to administer ' 'GNU/Linux servers via a web browser. On a {box_name}, controls ' @@ -65,8 +59,6 @@ description = [ ' of the URL.')), ] -manual_page = 'Cockpit' - app = None @@ -78,19 +70,28 @@ class CockpitApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-cockpit', name, short_description, - 'fa-wrench', 'cockpit:index', - parent_url_name='system') + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Cockpit'), + icon='fa-wrench', icon_filename='cockpit', + short_description=_('Server Administration'), + description=_description, manual_page='Cockpit', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-cockpit', info.name, + info.short_description, info.icon, + 'cockpit:index', parent_url_name='system') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-cockpit', name, - short_description=short_description, - icon='cockpit', url='/_cockpit/', - clients=clients, login_required=True) + shortcut = frontpage.Shortcut('shortcut-cockpit', info.name, + short_description=info.short_description, + icon=info.icon_filename, + url='/_cockpit/', clients=info.clients, + login_required=True) self.add(shortcut) - firewall = Firewall('firewall-cockpit', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-cockpit', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-cockpit', 'cockpit-freedombox', diff --git a/plinth/modules/cockpit/views.py b/plinth/modules/cockpit/views.py index a7ca6fa53..cf21bc286 100644 --- a/plinth/modules/cockpit/views.py +++ b/plinth/modules/cockpit/views.py @@ -17,21 +17,14 @@ """ Views for the Cockpit module """ -from plinth.modules.cockpit import (clients, description, icon_filename, - manual_page, name) from plinth.modules.cockpit.utils import get_origin_domains, load_augeas from plinth.views import AppView class CockpitAppView(AppView): app_id = 'cockpit' - name = name - description = description show_status_block = True - clients = clients - manual_page = manual_page template_name = 'cockpit.html' - icon_filename = icon_filename def get_context_data(self, *args, **kwargs): context = super().get_context_data(**kwargs) diff --git a/plinth/modules/config/__init__.py b/plinth/modules/config/__init__.py index a633689d4..d16ba4f3f 100644 --- a/plinth/modules/config/__init__.py +++ b/plinth/modules/config/__init__.py @@ -34,17 +34,13 @@ version = 2 is_essential = True -name = _('General Configuration') - -description = [ +_description = [ _('Here you can set some general configuration options ' 'like hostname, domain name, webserver home page etc.') ] depends = ['firewall', 'names'] -manual_page = 'Configure' - APACHE_CONF_ENABLED_DIR = '/etc/apache2/conf-enabled' APACHE_HOMEPAGE_CONF_FILE_NAME = 'freedombox-apache-homepage.conf' APACHE_HOMEPAGE_CONFIG = os.path.join(APACHE_CONF_ENABLED_DIR, @@ -64,7 +60,14 @@ class ConfigApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-config', _('Configure'), None, 'fa-cog', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, depends=depends, + name=_('General Configuration'), icon='fa-cog', + description=_description, + manual_page='Configure') + self.add(info) + + menu_item = menu.Menu('menu-config', _('Configure'), None, info.icon, 'config:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/config/views.py b/plinth/modules/config/views.py index 7318d0f4a..efc11a50e 100644 --- a/plinth/modules/config/views.py +++ b/plinth/modules/config/views.py @@ -35,11 +35,8 @@ LOGGER = logging.getLogger(__name__) class ConfigAppView(views.AppView): """Serve configuration page.""" - name = config.name - description = config.description form_class = ConfigurationForm app_id = 'config' - manual_page = config.manual_page show_status_block = False def get_initial(self): diff --git a/plinth/modules/coquelicot/__init__.py b/plinth/modules/coquelicot/__init__.py index 37878db47..787d468a9 100644 --- a/plinth/modules/coquelicot/__init__.py +++ b/plinth/modules/coquelicot/__init__.py @@ -29,19 +29,13 @@ from plinth.modules.firewall.components import Firewall from .manifest import backup, clients # noqa, pylint: disable=unused-import -clients = clients - version = 1 managed_services = ['coquelicot'] managed_packages = ['coquelicot'] -name = _('Coquelicot') - -short_description = _('File Sharing') - -description = [ +_description = [ _('Coquelicot is a "one-click" file sharing web application with a focus ' 'on protecting users\' privacy. It is best used for quickly sharing a ' 'single file. '), @@ -51,8 +45,6 @@ description = [ 'The default upload password is "test".') ] -manual_page = 'Coquelicot' - app = None @@ -64,18 +56,27 @@ class CoquelicotApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-coquelicot', name, short_description, - 'coquelicot', 'coquelicot:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Coquelicot'), + icon_filename='coquelicot', + short_description=_('File Sharing'), + description=_description, + manual_page='Coquelicot', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-coquelicot', info.name, + info.short_description, info.icon_filename, + 'coquelicot:index', parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-coquelicot', name, - short_description=short_description, + shortcut = frontpage.Shortcut('shortcut-coquelicot', info.name, + short_description=info.short_description, icon='coquelicot', url='/coquelicot', - clients=clients, login_required=True) + clients=info.clients, + login_required=True) self.add(shortcut) - firewall = Firewall('firewall-coquelicot', name, + firewall = Firewall('firewall-coquelicot', info.name, ports=['http', 'https'], is_external=True) self.add(firewall) diff --git a/plinth/modules/coquelicot/views.py b/plinth/modules/coquelicot/views.py index 619ccc1b0..74ca36997 100644 --- a/plinth/modules/coquelicot/views.py +++ b/plinth/modules/coquelicot/views.py @@ -23,22 +23,16 @@ 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, - name) +from plinth.modules.coquelicot import get_current_max_file_size from .forms import CoquelicotForm class CoquelicotAppView(views.AppView): """Serve configuration page.""" - clients = clients - name = name - description = description app_id = 'coquelicot' form_class = CoquelicotForm show_status_block = True - manual_page = manual_page def get_initial(self): """Return the status of the service to fill in the form.""" diff --git a/plinth/modules/datetime/__init__.py b/plinth/modules/datetime/__init__.py index abce4f889..c881b10b7 100644 --- a/plinth/modules/datetime/__init__.py +++ b/plinth/modules/datetime/__init__.py @@ -36,15 +36,11 @@ managed_services = ['systemd-timesyncd'] managed_packages = [] -name = _('Date & Time') - -description = [ +_description = [ _('Network time server is a program that maintains the system time ' 'in synchronization with servers on the Internet.') ] -manual_page = 'DateTime' - app = None @@ -56,7 +52,14 @@ class DateTimeApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-datetime', name, None, 'fa-clock-o', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, + name=_('Date & Time'), icon='fa-clock-o', + description=_description, + manual_page='DateTime') + self.add(info) + + menu_item = menu.Menu('menu-datetime', info.name, None, info.icon, 'datetime:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/datetime/views.py b/plinth/modules/datetime/views.py index 519ee72fb..b90fda355 100644 --- a/plinth/modules/datetime/views.py +++ b/plinth/modules/datetime/views.py @@ -24,7 +24,6 @@ from django.contrib import messages from django.utils.translation import ugettext as _ from plinth import actions -from plinth.modules import datetime from plinth.views import AppView from .forms import DateTimeForm @@ -33,11 +32,8 @@ logger = logging.getLogger(__name__) class DateTimeAppView(AppView): - name = datetime.name - description = datetime.description form_class = DateTimeForm app_id = 'datetime' - manual_page = datetime.manual_page def get_initial(self): status = super().get_initial() diff --git a/plinth/modules/deluge/__init__.py b/plinth/modules/deluge/__init__.py index 5e3981d3c..aa199d13d 100644 --- a/plinth/modules/deluge/__init__.py +++ b/plinth/modules/deluge/__init__.py @@ -26,7 +26,7 @@ from plinth import frontpage, menu 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, add_user_to_share_group +from plinth.modules.users import add_user_to_share_group, register_group from .manifest import backup, clients # noqa, pylint: disable=unused-import @@ -36,13 +36,7 @@ managed_services = ['deluged', 'deluge-web'] managed_packages = ['deluged', 'deluge-web'] -name = _('Deluge') - -icon_filename = 'deluge' - -short_description = _('BitTorrent Web Client') - -description = [ +_description = [ _('Deluge is a BitTorrent client that features a Web UI.'), _('The default password is \'deluge\', but you should log in and ' 'change it immediately after enabling this service.') @@ -52,10 +46,6 @@ group = ('bit-torrent', _('Download files using BitTorrent applications')) reserved_usernames = ['debian-deluged'] -clients = clients - -manual_page = 'Deluge' - app = None @@ -67,18 +57,28 @@ class DelugeApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-deluge', name, short_description, 'deluge', - 'deluge:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Deluge'), icon_filename='deluge', + short_description=_('BitTorrent Web Client'), + description=_description, manual_page='Deluge', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-deluge', info.name, info.short_description, + info.icon_filename, 'deluge:index', + parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut( - 'shortcut-deluge', name, short_description=short_description, - url='/deluge', icon=icon_filename, clients=clients, - login_required=True, allowed_groups=[group[0]]) + shortcut = frontpage.Shortcut('shortcut-deluge', info.name, + short_description=info.short_description, + url='/deluge', icon=info.icon_filename, + clients=info.clients, + login_required=True, + allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-deluge', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-deluge', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-deluge', 'deluge-plinth', diff --git a/plinth/modules/deluge/views.py b/plinth/modules/deluge/views.py index 1a15149a9..9d5f8e4fe 100644 --- a/plinth/modules/deluge/views.py +++ b/plinth/modules/deluge/views.py @@ -25,21 +25,15 @@ from django.contrib import messages from django.utils.translation import ugettext as _ from plinth import actions, views -from plinth.modules import deluge from .forms import DelugeForm class DelugeAppView(views.AppView): """Serve configuration page.""" - clients = deluge.clients - name = deluge.name - description = deluge.description diagnostics_module_name = 'deluge' form_class = DelugeForm app_id = 'deluge' - manual_page = deluge.manual_page - icon_filename = deluge.icon_filename def get_initial(self): """Get current Deluge server settings.""" @@ -62,8 +56,8 @@ class DelugeAppView(views.AppView): 'download_location', new_status['storage_path'] ] - actions.superuser_run( - 'deluge', ['set-configuration'] + new_configuration) + actions.superuser_run('deluge', ['set-configuration'] + + new_configuration) messages.success(self.request, _('Configuration updated')) return super().form_valid(form) diff --git a/plinth/modules/diagnostics/__init__.py b/plinth/modules/diagnostics/__init__.py index 9122aaf8d..4af769665 100644 --- a/plinth/modules/diagnostics/__init__.py +++ b/plinth/modules/diagnostics/__init__.py @@ -30,16 +30,12 @@ version = 1 is_essential = True -name = _('Diagnostics') - -description = [ +_description = [ _('The system diagnostic test will run a number of checks on your ' 'system to confirm that applications and services are working as ' 'expected.') ] -manual_page = 'Diagnostics' - app = None @@ -51,7 +47,14 @@ class DiagnosticsApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-diagnostics', name, None, 'fa-heartbeat', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, + name=_('Diagnostics'), icon='fa-heartbeat', + description=_description, + manual_page='Diagnostics') + self.add(info) + + menu_item = menu.Menu('menu-diagnostics', info.name, None, info.icon, 'diagnostics:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/diagnostics/diagnostics.py b/plinth/modules/diagnostics/diagnostics.py index 0b637527c..217bbb552 100644 --- a/plinth/modules/diagnostics/diagnostics.py +++ b/plinth/modules/diagnostics/diagnostics.py @@ -45,10 +45,8 @@ def index(request): return TemplateResponse( request, 'diagnostics.html', { - 'name': diagnostics.name, - 'description': diagnostics.description, + 'app_info': diagnostics.app.info, 'is_running': _running_task is not None, - 'manual_page': diagnostics.manual_page, 'results': current_results }) diff --git a/plinth/modules/diaspora/__init__.py b/plinth/modules/diaspora/__init__.py index 4a9a9501b..f7f547d7a 100644 --- a/plinth/modules/diaspora/__init__.py +++ b/plinth/modules/diaspora/__init__.py @@ -51,15 +51,11 @@ def get_configured_domain_name(): version = 1 -name = _('diaspora*') - -short_description = _('Federated Social Network') - managed_services = ['diaspora'] managed_packages = ['diaspora'] -description = [ +_description = [ _('diaspora* is a decentralized social network where you can store ' 'and control your own data.'), format_lazy( @@ -71,7 +67,6 @@ description = [ ] from .manifest import clients # noqa pylint:disable=E402 isort:skip -clients = clients app = None @@ -84,19 +79,25 @@ class DiasporaApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-diaspora', name, short_description, - 'diaspora', 'diaspora:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('diaspora*'), icon_filename='diaspora', + short_description=_('Federated Social Network'), + description=_description, clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-diaspora', info.name, + info.short_description, info.icon_filename, + 'diaspora:index', parent_url_name='apps') self.add(menu_item) - shortcut = Shortcut('shortcut-diaspora', name, - short_description=short_description, - icon='diaspora', url=None, clients=clients, - login_required=True) + shortcut = Shortcut('shortcut-diaspora', info.name, + short_description=info.short_description, + icon=info.icon_filename, url=None, + clients=info.clients, login_required=True) self.add(shortcut) - firewall = Firewall('firewall-diaspora', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-diaspora', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-diaspora', 'diaspora-plinth') diff --git a/plinth/modules/diaspora/views.py b/plinth/modules/diaspora/views.py index fd17defc1..03598977d 100644 --- a/plinth/modules/diaspora/views.py +++ b/plinth/modules/diaspora/views.py @@ -35,8 +35,8 @@ class DiasporaSetupView(FormView): """Show diaspora setup page.""" template_name = 'diaspora-pre-setup.html' form_class = DomainSelectionForm - description = diaspora.description - title = diaspora.name + description = diaspora.app.info.description + title = diaspora.app.info.name success_url = reverse_lazy('diaspora:index') def form_valid(self, form): @@ -60,7 +60,6 @@ class DiasporaAppView(AppView): form_class = DiasporaAppForm app_id = 'diaspora' template_name = 'diaspora-post-setup.html' - name = diaspora.name def dispatch(self, request, *args, **kwargs): if not diaspora.is_setup(): @@ -70,7 +69,6 @@ class DiasporaAppView(AppView): def get_context_data(self, *args, **kwargs): context = super().get_context_data(**kwargs) context['domain_name'] = diaspora.get_configured_domain_name() - context['clients'] = diaspora.clients return context def get_initial(self): diff --git a/plinth/modules/dynamicdns/__init__.py b/plinth/modules/dynamicdns/__init__.py index c96a7dc52..80ccbecc0 100644 --- a/plinth/modules/dynamicdns/__init__.py +++ b/plinth/modules/dynamicdns/__init__.py @@ -37,9 +37,7 @@ depends = ['names'] managed_packages = ['ez-ipupdate'] -name = _('Dynamic DNS Client') - -description = [ +_description = [ format_lazy( _('If your Internet provider changes your IP address periodically ' '(i.e. every 24h), it may be hard for others to find you on the ' @@ -57,8 +55,6 @@ description = [ reserved_usernames = ['ez-ipupd'] -manual_page = 'DynamicDNS' - app = None @@ -70,7 +66,14 @@ class DynamicDNSApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-dynamicdns', name, None, 'fa-refresh', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, depends=depends, + name=_('Dynamic DNS Client'), icon='fa-refresh', + description=_description, + manual_page='DynamicDNS') + self.add(info) + + menu_item = menu.Menu('menu-dynamicdns', info.name, None, info.icon, 'dynamicdns:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/dynamicdns/views.py b/plinth/modules/dynamicdns/views.py index b00df9b18..8f2c80a82 100644 --- a/plinth/modules/dynamicdns/views.py +++ b/plinth/modules/dynamicdns/views.py @@ -52,10 +52,8 @@ def index(request): """Serve Dynamic DNS page.""" return TemplateResponse( request, 'dynamicdns.html', { - 'title': dynamicdns.name, - 'name': dynamicdns.name, - 'description': dynamicdns.description, - 'manual_page': dynamicdns.manual_page, + 'app_info': dynamicdns.app.info, + 'title': dynamicdns.app.info.name, 'subsubmenu': subsubmenu }) @@ -77,9 +75,7 @@ def configure(request): return TemplateResponse( request, 'dynamicdns_configure.html', { 'title': _('Configure Dynamic DNS'), - 'name': dynamicdns.name, - 'description': dynamicdns.description, - 'manual_page': dynamicdns.manual_page, + 'app_info': dynamicdns.app.info, 'form': form, 'subsubmenu': subsubmenu }) @@ -103,9 +99,7 @@ def statuspage(request): return TemplateResponse( request, 'dynamicdns_status.html', { 'title': _('Dynamic DNS Status'), - 'name': dynamicdns.name, - 'description': dynamicdns.description, - 'manual_page': dynamicdns.manual_page, + 'app_info': dynamicdns.app.info, 'no_nat': no_nat, 'nat_unchecked': nat_unchecked, 'timer': timer, diff --git a/plinth/modules/ejabberd/__init__.py b/plinth/modules/ejabberd/__init__.py index f92a05227..9439dce70 100644 --- a/plinth/modules/ejabberd/__init__.py +++ b/plinth/modules/ejabberd/__init__.py @@ -47,13 +47,7 @@ managed_packages = ['ejabberd'] managed_paths = [pathlib.Path('/etc/ejabberd/')] -name = _('ejabberd') - -icon_filename = 'ejabberd' - -short_description = _('Chat Server') - -description = [ +_description = [ _('XMPP is an open and standardized communication protocol. Here ' 'you can run and configure your XMPP server, called ejabberd.'), format_lazy( @@ -66,12 +60,8 @@ description = [ jsxc_url=reverse_lazy('jsxc:index')) ] -clients = clients - reserved_usernames = ['ejabberd'] -manual_page = 'ejabberd' - port_forwarding_info = [ ('TCP', 5222), ('TCP', 5269), @@ -91,19 +81,27 @@ class EjabberdApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-ejabberd', name, short_description, - 'ejabberd', 'ejabberd:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('ejabberd'), icon_filename='ejabberd', + short_description=_('Chat Server'), + description=_description, + manual_page='ejabberd', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-ejabberd', info.name, + info.short_description, info.icon_filename, + 'ejabberd:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-ejabberd', name, short_description=short_description, - icon=icon_filename, description=description, - configure_url=reverse_lazy('ejabberd:index'), clients=clients, + 'shortcut-ejabberd', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('ejabberd:index'), clients=info.clients, login_required=True) self.add(shortcut) - firewall = Firewall('firewall-ejabberd', name, + firewall = Firewall('firewall-ejabberd', info.name, ports=['xmpp-client', 'xmpp-server', 'xmpp-bosh'], is_external=True) self.add(firewall) diff --git a/plinth/modules/ejabberd/views.py b/plinth/modules/ejabberd/views.py index 24f86e781..f7817d4b8 100644 --- a/plinth/modules/ejabberd/views.py +++ b/plinth/modules/ejabberd/views.py @@ -32,12 +32,8 @@ class EjabberdAppView(AppView): """Show ejabberd as a service.""" app_id = 'ejabberd' template_name = 'ejabberd.html' - name = ejabberd.name - description = ejabberd.description form_class = EjabberdForm - manual_page = ejabberd.manual_page port_forwarding_info = ejabberd.port_forwarding_info - icon_filename = ejabberd.icon_filename def get_initial(self): initdict = super().get_initial() @@ -49,7 +45,6 @@ class EjabberdAppView(AppView): context = super().get_context_data(*args, **kwargs) domains = ejabberd.get_domains() context['domainname'] = domains[0] if domains else None - context['clients'] = ejabberd.clients return context def form_valid(self, form): diff --git a/plinth/modules/firewall/__init__.py b/plinth/modules/firewall/__init__.py index 846423891..eb3c84007 100644 --- a/plinth/modules/firewall/__init__.py +++ b/plinth/modules/firewall/__init__.py @@ -36,9 +36,7 @@ managed_packages = ['firewalld', 'nftables'] managed_services = ['firewalld'] -name = _('Firewall') - -description = [ +_description = [ format_lazy( _('Firewall is a security system that controls the incoming and ' 'outgoing network traffic on your {box_name}. Keeping a ' @@ -46,8 +44,6 @@ description = [ 'security threat from the Internet.'), box_name=cfg.box_name) ] -manual_page = 'Firewall' - _port_details = {} app = None @@ -61,7 +57,13 @@ class FirewallApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-firewall', name, None, 'fa-shield', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Firewall'), + icon='fa-shield', description=_description, + manual_page='Firewall') + self.add(info) + + menu_item = menu.Menu('menu-firewall', info.name, None, info.icon, 'firewall:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/firewall/views.py b/plinth/modules/firewall/views.py index ded469986..e00a35ec2 100644 --- a/plinth/modules/firewall/views.py +++ b/plinth/modules/firewall/views.py @@ -26,10 +26,7 @@ from . import components class FirewallAppView(views.AppView): """Serve firewall index page.""" - name = firewall.name - description = firewall.description app_id = 'firewall' - manual_page = firewall.manual_page template_name = 'firewall.html' def get_context_data(self, *args, **kwargs): diff --git a/plinth/modules/gitweb/__init__.py b/plinth/modules/gitweb/__init__.py index b4e5e43b0..bedea32d9 100644 --- a/plinth/modules/gitweb/__init__.py +++ b/plinth/modules/gitweb/__init__.py @@ -35,19 +35,11 @@ from .forms import is_repo_url from .manifest import (GIT_REPO_PATH, # noqa, pylint: disable=unused-import backup, clients) -clients = clients - version = 1 managed_packages = ['gitweb', 'highlight'] -name = _('Gitweb') - -icon_filename = 'gitweb' - -short_description = _('Simple Git Hosting') - -description = [ +_description = [ _('Git is a distributed version-control system for tracking changes in ' 'source code during software development. Gitweb provides a web ' 'interface to Git repositories. You can browse history and content of ' @@ -59,8 +51,6 @@ description = [ 'Git tutorial.') ] -manual_page = 'GitWeb' - group = ('git-access', _('Read-write access to Git repositories')) app = None @@ -77,19 +67,28 @@ class GitwebApp(app_module.App): self.repos = [] - menu_item = menu.Menu('menu-gitweb', name, short_description, 'gitweb', - 'gitweb:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Gitweb'), icon_filename='gitweb', + short_description=_('Simple Git Hosting'), + description=_description, manual_page='GitWeb', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-gitweb', info.name, info.short_description, + info.icon_filename, 'gitweb:index', + parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-gitweb', name, - short_description=short_description, - icon=icon_filename, url='/gitweb/', - clients=clients, login_required=True, + shortcut = frontpage.Shortcut('shortcut-gitweb', info.name, + short_description=info.short_description, + icon=info.icon_filename, url='/gitweb/', + clients=info.clients, + login_required=True, allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-gitweb', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-gitweb', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-gitweb', 'gitweb-freedombox', diff --git a/plinth/modules/gitweb/views.py b/plinth/modules/gitweb/views.py index d2aab09dd..afbc2f45d 100644 --- a/plinth/modules/gitweb/views.py +++ b/plinth/modules/gitweb/views.py @@ -37,14 +37,9 @@ from .forms import CreateRepoForm, EditRepoForm class GitwebAppView(views.AppView): """Serve configuration page.""" - clients = gitweb.clients - name = gitweb.name - description = gitweb.description app_id = 'gitweb' show_status_block = False template_name = 'gitweb_configure.html' - icon_filename = gitweb.icon_filename - manual_page = gitweb.manual_page def get_context_data(self, *args, **kwargs): """Add repositories to the context data.""" @@ -163,6 +158,6 @@ def delete(request, name): return redirect(reverse_lazy('gitweb:index')) return TemplateResponse(request, 'gitweb_delete.html', { - 'title': gitweb.name, + 'title': gitweb.app.info.name, 'name': name }) diff --git a/plinth/modules/help/__init__.py b/plinth/modules/help/__init__.py index c1f215a32..3ccb7acf7 100644 --- a/plinth/modules/help/__init__.py +++ b/plinth/modules/help/__init__.py @@ -20,8 +20,8 @@ FreedomBox app for help pages. from django.utils.translation import ugettext_lazy as _ +from plinth import app as app_module from plinth import menu -from plinth.app import App version = 1 @@ -29,7 +29,7 @@ is_essential = True app = None -class HelpApp(App): +class HelpApp(app_module.App): """FreedomBox app for showing help.""" app_id = 'help' @@ -37,6 +37,11 @@ class HelpApp(App): def __init__(self): """Create components for the app.""" super().__init__() + + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential) + self.add(info) + menu_item = menu.Menu('menu-help', _('Documentation'), None, 'fa-book', 'help:index', parent_url_name='index') self.add(menu_item) diff --git a/plinth/modules/i2p/__init__.py b/plinth/modules/i2p/__init__.py index b5a4e5b74..548807907 100644 --- a/plinth/modules/i2p/__init__.py +++ b/plinth/modules/i2p/__init__.py @@ -39,13 +39,7 @@ managed_services = [service_name] managed_packages = ['i2p'] -name = _('I2P') - -icon_filename = 'i2p' - -short_description = _('Anonymity Network') - -description = [ +_description = [ _('The Invisible Internet Project is an anonymous network layer intended ' 'to protect communication from censorship and surveillance. I2P ' 'provides anonymity by sending encrypted traffic through a ' @@ -56,12 +50,8 @@ description = [ 'configuration process.') ] -clients = clients - group = ('i2p', _('Manage I2P application')) -manual_page = 'I2P' - port_forwarding_info = [ ('TCP', 4444), ('TCP', 4445), @@ -85,19 +75,28 @@ class I2PApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-i2p', name, short_description, 'i2p', - 'i2p:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('I2P'), icon_filename='i2p', + short_description=_('Anonymity Network'), + description=_description, manual_page='I2P', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-i2p', info.name, info.short_description, + info.icon_filename, 'i2p:index', + parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-i2p', name, - short_description=short_description, - icon=icon_filename, url='/i2p/', - clients=clients, login_required=True, + shortcut = frontpage.Shortcut('shortcut-i2p', info.name, + short_description=info.short_description, + icon=info.icon_filename, url='/i2p/', + clients=info.clients, + login_required=True, allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-i2p-web', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-i2p-web', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) firewall = Firewall('firewall-i2p-proxies', _('I2P Proxy'), diff --git a/plinth/modules/i2p/views.py b/plinth/modules/i2p/views.py index c40856fa7..cbdd40925 100644 --- a/plinth/modules/i2p/views.py +++ b/plinth/modules/i2p/views.py @@ -41,20 +41,14 @@ subsubmenu = [{ class I2PAppView(AppView): """Serve configuration page.""" app_id = 'i2p' - clients = i2p.clients - name = i2p.name - description = i2p.description show_status_block = True template_name = 'i2p.html' - icon_filename = i2p.icon_filename def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" context = super().get_context_data(**kwargs) - context['title'] = i2p.name - context['description'] = i2p.description - context['clients'] = i2p.clients - context['manual_page'] = i2p.manual_page + context['title'] = i2p.app.info.name + context['app_info'] = i2p.app.info context['subsubmenu'] = subsubmenu context['port_forwarding_info'] = i2p.port_forwarding_info return context @@ -69,11 +63,8 @@ class ServiceBaseView(TemplateView): def get_context_data(self, **kwargs): """Add context data for template.""" context = super().get_context_data(**kwargs) - context['title'] = i2p.name - context['name'] = i2p.name - context['description'] = i2p.description - context['clients'] = i2p.clients - context['manual_page'] = i2p.manual_page + context['title'] = i2p.app.info.name + context['app_info'] = i2p.app.info context['subsubmenu'] = subsubmenu context['is_enabled'] = i2p.app.is_enabled() context['service_title'] = self.service_title diff --git a/plinth/modules/ikiwiki/__init__.py b/plinth/modules/ikiwiki/__init__.py index 213a58c58..5a19b2bbe 100644 --- a/plinth/modules/ikiwiki/__init__.py +++ b/plinth/modules/ikiwiki/__init__.py @@ -38,13 +38,7 @@ managed_packages = [ 'libsearch-xapian-perl', 'libimage-magick-perl' ] -name = _('ikiwiki') - -icon_filename = 'ikiwiki' - -short_description = _('Wiki and Blog') - -description = [ +_description = [ _('ikiwiki is a simple wiki and blog application. It supports ' 'several lightweight markup languages, including Markdown, and ' 'common blogging functionality such as comments and RSS feeds.'), @@ -57,12 +51,8 @@ description = [ users_url=reverse_lazy('users:index')) ] -clients = clients - group = ('wiki', _('View and edit wiki applications')) -manual_page = 'Ikiwiki' - app = None @@ -74,15 +64,22 @@ class IkiwikiApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-ikiwiki', name, short_description, - 'ikiwiki', 'ikiwiki:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('ikiwiki'), icon_filename='ikiwiki', + short_description=_('Wiki and Blog'), + description=_description, manual_page='Ikiwiki', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-ikiwiki', info.name, + info.short_description, info.icon_filename, + 'ikiwiki:index', parent_url_name='apps') self.add(menu_item) self.refresh_sites() - firewall = Firewall('firewall-ikiwiki', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-ikiwiki', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-ikiwiki', 'ikiwiki-plinth', @@ -92,8 +89,9 @@ class IkiwikiApp(app_module.App): def add_shortcut(self, site, title): """Add an ikiwiki shortcut to frontpage.""" shortcut = frontpage.Shortcut('shortcut-ikiwiki-' + site, title, - icon=icon_filename, - url='/ikiwiki/' + site, clients=clients) + icon=self.info.icon_filename, + url='/ikiwiki/' + site, + clients=self.info.clients) self.add(shortcut) return shortcut diff --git a/plinth/modules/ikiwiki/views.py b/plinth/modules/ikiwiki/views.py index fae514969..e577b8a61 100644 --- a/plinth/modules/ikiwiki/views.py +++ b/plinth/modules/ikiwiki/views.py @@ -33,13 +33,8 @@ from .forms import IkiwikiCreateForm class IkiwikiAppView(views.AppView): """Serve configuration page.""" app_id = 'ikiwiki' - name = ikiwiki.name - description = ikiwiki.description show_status_block = False template_name = 'ikiwiki_configure.html' - manual_page = ikiwiki.manual_page - clients = ikiwiki.clients - icon_filename = ikiwiki.icon_filename def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" @@ -77,11 +72,8 @@ def create(request): return TemplateResponse( request, 'ikiwiki_create.html', { - 'title': ikiwiki.name, - 'clients': ikiwiki.clients, - 'description': ikiwiki.description, + 'title': ikiwiki.app.info.name, 'form': form, - 'manual_page': ikiwiki.manual_page, 'is_enabled': ikiwiki.app.is_enabled(), }) @@ -134,6 +126,6 @@ def delete(request, name): return redirect(reverse_lazy('ikiwiki:index')) return TemplateResponse(request, 'ikiwiki_delete.html', { - 'title': ikiwiki.name, + 'title': ikiwiki.app.info.name, 'name': title }) diff --git a/plinth/modules/infinoted/__init__.py b/plinth/modules/infinoted/__init__.py index 12b50d21a..525134be9 100644 --- a/plinth/modules/infinoted/__init__.py +++ b/plinth/modules/infinoted/__init__.py @@ -37,15 +37,7 @@ managed_services = ['infinoted'] managed_packages = ['infinoted'] -manual_page = 'Infinoted' - -name = _('infinoted') - -icon_filename = 'infinoted' - -short_description = _('Gobby Server') - -description = [ +_description = [ _('infinoted is a server for Gobby, a collaborative text editor.'), format_lazy( _('To use it, download Gobby, ' @@ -54,8 +46,6 @@ description = [ box_name=_(cfg.box_name)), ] -clients = clients - port_forwarding_info = [('TCP', 6523)] app = None @@ -69,19 +59,27 @@ class InfinotedApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-infinoted', name, short_description, - 'infinoted', 'infinoted:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('infinoted'), icon_filename='infinoted', + short_description=_('Gobby Server'), + description=_description, + manual_page='Infinoted', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-infinoted', info.name, + info.short_description, info.icon_filename, + 'infinoted:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-infinoted', name, short_description=short_description, - icon=icon_filename, description=description, - configure_url=reverse_lazy('infinoted:index'), clients=clients, - login_required=False) + 'shortcut-infinoted', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('infinoted:index'), + clients=info.clients, login_required=False) self.add(shortcut) - firewall = Firewall('firewall-infinoted', name, + firewall = Firewall('firewall-infinoted', info.name, ports=['infinoted-plinth'], is_external=True) self.add(firewall) @@ -102,11 +100,7 @@ def init(): class InfinotedAppView(AppView): app_id = 'infinoted' - name = name - description = description - clients = clients port_forwarding_info = port_forwarding_info - icon_filename = icon_filename def setup(helper, old_version=None): diff --git a/plinth/modules/jsxc/__init__.py b/plinth/modules/jsxc/__init__.py index 3ec4756ca..36b5a0a1b 100644 --- a/plinth/modules/jsxc/__init__.py +++ b/plinth/modules/jsxc/__init__.py @@ -33,19 +33,11 @@ version = 1 managed_packages = ['libjs-jsxc'] -name = _('JSXC') - -icon_filename = 'jsxc' - -short_description = _('Chat Client') - -description = [ +_description = [ _('JSXC is a web client for XMPP. Typically it is used with an XMPP ' 'server running locally.'), ] -clients = clients - logger = logging.getLogger(__name__) app = None @@ -59,19 +51,26 @@ class JSXCApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-jsxc', name, short_description, 'jsxc', - 'jsxc:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('JSXC'), icon_filename='jsxc', + short_description=_('Chat Client'), + description=_description, clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-jsxc', info.name, info.short_description, + info.icon_filename, 'jsxc:index', + parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-jsxc', name=name, - short_description=short_description, - icon=icon_filename, + shortcut = frontpage.Shortcut('shortcut-jsxc', name=info.name, + short_description=info.short_description, + icon=info.icon_filename, url=reverse_lazy('jsxc:jsxc'), - clients=clients) + clients=info.clients) self.add(shortcut) - firewall = Firewall('firewall-jsxc', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-jsxc', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) diff --git a/plinth/modules/jsxc/views.py b/plinth/modules/jsxc/views.py index c2db27497..2cf4624ad 100644 --- a/plinth/modules/jsxc/views.py +++ b/plinth/modules/jsxc/views.py @@ -20,7 +20,7 @@ Views for the JSXC module from django.views.generic import TemplateView -from plinth.modules import config, jsxc +from plinth.modules import config from plinth.views import AppView @@ -28,11 +28,7 @@ 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 - icon_filename = jsxc.icon_filename class JsxcView(TemplateView): diff --git a/plinth/modules/letsencrypt/__init__.py b/plinth/modules/letsencrypt/__init__.py index 2eb1ca45d..9f0180b22 100644 --- a/plinth/modules/letsencrypt/__init__.py +++ b/plinth/modules/letsencrypt/__init__.py @@ -45,11 +45,7 @@ depends = ['names'] managed_packages = ['certbot'] -name = _('Let\'s Encrypt') - -short_description = _('Certificates') - -description = [ +_description = [ format_lazy( _('A digital certificate allows users of a web service to verify the ' 'identity of the service and to securely communicate with it. ' @@ -64,8 +60,6 @@ description = [ 'Subscriber Agreement before using this service.') ] -manual_page = 'LetsEncrypt' - LIVE_DIRECTORY = '/etc/letsencrypt/live/' CERTIFICATE_CHECK_DELAY = 120 logger = logging.getLogger(__name__) @@ -81,9 +75,17 @@ class LetsEncryptApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-letsencrypt', name, short_description, - 'fa-lock', 'letsencrypt:index', - parent_url_name='system') + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, depends=depends, + name=_('Let\'s Encrypt'), icon='fa-lock', + short_description=_('Certificates'), + description=_description, + manual_page='LetsEncrypt') + self.add(info) + + menu_item = menu.Menu('menu-letsencrypt', info.name, + info.short_description, info.icon, + 'letsencrypt:index', parent_url_name='system') self.add(menu_item) def diagnose(self): diff --git a/plinth/modules/letsencrypt/views.py b/plinth/modules/letsencrypt/views.py index 0b0ed16a7..130686915 100644 --- a/plinth/modules/letsencrypt/views.py +++ b/plinth/modules/letsencrypt/views.py @@ -39,10 +39,8 @@ def index(request): return TemplateResponse( request, 'letsencrypt.html', { 'app_id': 'letsencrypt', - 'name': letsencrypt.name, - 'description': letsencrypt.description, + 'app_info': letsencrypt.app.info, 'status': status, - 'manual_page': letsencrypt.manual_page, 'has_diagnostics': True, 'is_enabled': letsencrypt.app.is_enabled(), }) diff --git a/plinth/modules/matrixsynapse/__init__.py b/plinth/modules/matrixsynapse/__init__.py index 9f0c05a6b..72ac05c5e 100644 --- a/plinth/modules/matrixsynapse/__init__.py +++ b/plinth/modules/matrixsynapse/__init__.py @@ -45,13 +45,7 @@ managed_packages = ['matrix-synapse', 'matrix-synapse-ldap3'] managed_paths = [pathlib.Path('/etc/matrix-synapse/')] -name = _('Matrix Synapse') - -icon_filename = 'matrixsynapse' - -short_description = _('Chat Server') - -description = [ +_description = [ _('Matrix is an new ' 'ecosystem for open, federated instant messaging and VoIP. Synapse is a ' 'server implementing the Matrix protocol. It provides chat groups, ' @@ -65,10 +59,6 @@ description = [ 'client is recommended.') ] -clients = clients - -manual_page = 'MatrixSynapse' - port_forwarding_info = [('TCP', 8448)] logger = logging.getLogger(__name__) @@ -87,20 +77,28 @@ class MatrixSynapseApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-matrixsynapse', name, short_description, - 'matrixsynapse', 'matrixsynapse:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Matrix Synapse'), + icon_filename='matrixsynapse', + short_description=_('Chat Server'), + description=_description, + manual_page='MatrixSynapse', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-matrixsynapse', info.name, + info.short_description, 'matrixsynapse', + 'matrixsynapse:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-matrixsynapse', name, - short_description=short_description, icon=icon_filename, - description=description, - configure_url=reverse_lazy('matrixsynapse:index'), clients=clients, - login_required=True) + 'shortcut-matrixsynapse', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('matrixsynapse:index'), + clients=info.clients, login_required=True) self.add(shortcut) - firewall = Firewall('firewall-matrixsynapse', name, + firewall = Firewall('firewall-matrixsynapse', info.name, ports=['matrix-synapse-plinth'], is_external=True) self.add(firewall) diff --git a/plinth/modules/matrixsynapse/views.py b/plinth/modules/matrixsynapse/views.py index 7d21eab5f..0ce480a27 100644 --- a/plinth/modules/matrixsynapse/views.py +++ b/plinth/modules/matrixsynapse/views.py @@ -38,8 +38,6 @@ class SetupView(FormView): template_name = 'matrix-synapse-pre-setup.html' form_class = DomainSelectionForm success_url = reverse_lazy('matrixsynapse:index') - icon_filename = matrixsynapse.icon_filename - title = matrixsynapse.name def form_valid(self, form): """Handle valid form submission.""" @@ -50,9 +48,8 @@ class SetupView(FormView): """Provide context data to the template.""" context = super().get_context_data(**kwargs) - context['name'] = matrixsynapse.name - context['description'] = matrixsynapse.description - context['icon_filename'] = matrixsynapse.icon_filename + context['title'] = matrixsynapse.app.info.name + context['app_info'] = matrixsynapse.app.info context['domain_names'] = names.components.DomainName.list_names( 'matrix-synapse-plinth') @@ -63,11 +60,8 @@ class MatrixSynapseAppView(AppView): """Show matrix-synapse service page.""" app_id = 'matrixsynapse' template_name = 'matrix-synapse.html' - name = matrixsynapse.name - description = matrixsynapse.description form_class = MatrixSynapseForm port_forwarding_info = matrixsynapse.port_forwarding_info - icon_filename = matrixsynapse.icon_filename def dispatch(self, request, *args, **kwargs): """Redirect to setup page if setup is not done yet.""" @@ -80,8 +74,6 @@ class MatrixSynapseAppView(AppView): """Add additional context data for template.""" context = super().get_context_data(*args, **kwargs) context['domain_name'] = matrixsynapse.get_configured_domain_name() - context['clients'] = matrixsynapse.clients - context['manual_page'] = matrixsynapse.manual_page context['certificate_status'] = matrixsynapse.get_certificate_status() return context diff --git a/plinth/modules/mediawiki/__init__.py b/plinth/modules/mediawiki/__init__.py index b7546a84e..474b671ef 100644 --- a/plinth/modules/mediawiki/__init__.py +++ b/plinth/modules/mediawiki/__init__.py @@ -37,13 +37,7 @@ managed_packages = ['mediawiki', 'imagemagick', 'php-sqlite3'] managed_services = ['mediawiki-jobrunner'] -name = _('MediaWiki') - -icon_filename = 'mediawiki' - -short_description = _('Wiki') - -description = [ +_description = [ _('MediaWiki is the wiki engine that powers Wikipedia and other WikiMedia ' 'projects. A wiki engine is a program for creating a collaboratively ' 'edited website. You can use MediaWiki to host a wiki-like website, ' @@ -58,10 +52,6 @@ description = [ 'logged in can make changes to the content.') ] -manual_page = 'MediaWiki' - -clients = clients - app = None @@ -75,18 +65,25 @@ class MediaWikiApp(app_module.App): super().__init__() self._private_mode = True - menu_item = menu.Menu('menu-mediawiki', name, short_description, - 'mediawiki', 'mediawiki:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('MediaWiki'), icon_filename='mediawiki', + short_description=_('Wiki'), + description=_description, + manual_page='MediaWiki', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-mediawiki', info.name, + info.short_description, info.icon_filename, + 'mediawiki:index', parent_url_name='apps') self.add(menu_item) - shortcut = Shortcut('shortcut-mediawiki', name, - short_description=short_description, - icon=icon_filename, url='/mediawiki', - clients=clients, login_required=True) + shortcut = Shortcut('shortcut-mediawiki', info.name, + short_description=info.short_description, + icon=info.icon_filename, url='/mediawiki', + clients=info.clients, login_required=True) self.add(shortcut) - firewall = Firewall('firewall-mediawiki', name, + firewall = Firewall('firewall-mediawiki', info.name, ports=['http', 'https'], is_external=True) self.add(firewall) diff --git a/plinth/modules/mediawiki/views.py b/plinth/modules/mediawiki/views.py index 940a0b8e1..7f907750e 100644 --- a/plinth/modules/mediawiki/views.py +++ b/plinth/modules/mediawiki/views.py @@ -35,15 +35,10 @@ logger = logging.getLogger(__name__) class MediaWikiAppView(views.AppView): """App configuration page.""" - clients = mediawiki.clients - name = mediawiki.name - description = mediawiki.description app_id = 'mediawiki' form_class = MediaWikiForm - manual_page = mediawiki.manual_page show_status_block = False template_name = 'mediawiki.html' - icon_filename = mediawiki.icon_filename def get_initial(self): """Return the values to fill in the form.""" diff --git a/plinth/modules/minetest/__init__.py b/plinth/modules/minetest/__init__.py index 8cebad289..c6ebc54d3 100644 --- a/plinth/modules/minetest/__init__.py +++ b/plinth/modules/minetest/__init__.py @@ -47,13 +47,7 @@ mods = [ managed_packages = ['minetest-server'] + mods -name = _('Minetest') - -icon_filename = 'minetest' - -short_description = _('Block Sandbox') - -description = [ +_description = [ format_lazy( _('Minetest is a multiplayer infinite-world block sandbox. This ' 'module enables the Minetest server to be run on this ' @@ -62,10 +56,6 @@ description = [ 'is needed.'), box_name=_(cfg.box_name)), ] -clients = clients - -manual_page = 'Minetest' - port_forwarding_info = [('UDP', 30000)] reserved_usernames = ['Debian-minetest'] @@ -84,19 +74,27 @@ class MinetestApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-minetest', name, short_description, - 'minetest', 'minetest:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Minetest'), icon_filename='minetest', + short_description=_('Block Sandbox'), + description=_description, + manual_page='Minetest', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-minetest', info.name, + info.short_description, info.icon_filename, + 'minetest:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-minetest', name, short_description=short_description, - icon=icon_filename, description=description, - configure_url=reverse_lazy('minetest:index'), clients=clients, + 'shortcut-minetest', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('minetest:index'), clients=info.clients, login_required=False) self.add(shortcut) - firewall = Firewall('firewall-minetest', name, + firewall = Firewall('firewall-minetest', info.name, ports=['minetest-plinth'], is_external=True) self.add(firewall) diff --git a/plinth/modules/minetest/views.py b/plinth/modules/minetest/views.py index f436c5ec9..acfea7d82 100644 --- a/plinth/modules/minetest/views.py +++ b/plinth/modules/minetest/views.py @@ -25,22 +25,17 @@ from plinth import actions from plinth.modules import minetest, names from plinth.views import AppView -from . import description, get_configuration +from . import get_configuration from .forms import MinetestForm class MinetestAppView(AppView): # pylint: disable=too-many-ancestors """A specialized view for configuring minetest.""" app_id = 'minetest' - name = minetest.name - description = description show_status_block = True template_name = 'minetest.html' form_class = MinetestForm - clients = minetest.clients - manual_page = minetest.manual_page port_forwarding_info = minetest.port_forwarding_info - icon_filename = minetest.icon_filename def get_initial(self): """Return the values to fill in the form.""" diff --git a/plinth/modules/minidlna/__init__.py b/plinth/modules/minidlna/__init__.py index a969fe349..4e4fa2c54 100644 --- a/plinth/modules/minidlna/__init__.py +++ b/plinth/modules/minidlna/__init__.py @@ -30,17 +30,11 @@ from .manifest import backup, clients # noqa version = 1 -name = 'minidlna' - -icon_filename = name - managed_packages = ['minidlna'] managed_services = ['minidlna'] -short_description = _('Simple Media Server') - -description = [ +_description = [ _('MiniDLNA is a simple media server software, with the aim of being ' 'fully compliant with DLNA/UPnP-AV clients. ' 'The MiniDNLA daemon serves media files ' @@ -51,8 +45,6 @@ description = [ 'such as PS3 and Xbox 360) or applications such as totem and Kodi.') ] -clients = clients - group = ('minidlna', _('Media streaming server')) app = None @@ -65,24 +57,30 @@ class MiniDLNAApp(app_module.App): def __init__(self): """Initialize the app components""" super().__init__() + info = app_module.Info(app_id=self.app_id, version=version, + name='minidlna', icon_filename='minidlna', + short_description=_('Simple Media Server'), + description=_description, clients=clients) + self.add(info) + menu_item = menu.Menu( 'menu-minidlna', - name=name, - short_description=short_description, + name=info.name, + short_description=info.short_description, url_name='minidlna:index', parent_url_name='apps', - icon=icon_filename, + icon=info.icon_filename, ) - firewall = Firewall('firewall-minidlna', name, ports=['minidlna'], + firewall = Firewall('firewall-minidlna', info.name, ports=['minidlna'], is_external=False) webserver = Webserver('webserver-minidlna', 'minidlna-freedombox', urls=['http://localhost:8200/']) shortcut = frontpage.Shortcut( 'shortcut-minidlna', - name, - short_description=short_description, - description=description, - icon=icon_filename, + info.name, + short_description=info.short_description, + description=info.description, + icon=info.icon_filename, url='/_minidlna/', login_required=True, allowed_groups=[group[0]], diff --git a/plinth/modules/minidlna/views.py b/plinth/modules/minidlna/views.py index bf426939d..a8407d3de 100644 --- a/plinth/modules/minidlna/views.py +++ b/plinth/modules/minidlna/views.py @@ -23,7 +23,6 @@ from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from plinth import actions -from plinth.modules import minidlna from plinth.views import AppView from .forms import MiniDLNAServerForm @@ -31,11 +30,7 @@ from .forms import MiniDLNAServerForm class MiniDLNAAppView(AppView): app_id = 'minidlna' - name = minidlna.name - description = minidlna.description form_class = MiniDLNAServerForm - icon_filename = minidlna.icon_filename - clients = minidlna.clients def get_initial(self): """Initial form value as found in the minidlna.conf""" diff --git a/plinth/modules/mldonkey/__init__.py b/plinth/modules/mldonkey/__init__.py index c12b86e41..e5c903abd 100644 --- a/plinth/modules/mldonkey/__init__.py +++ b/plinth/modules/mldonkey/__init__.py @@ -37,13 +37,7 @@ managed_services = ['mldonkey-server'] managed_packages = ['mldonkey-server'] -name = _('MLDonkey') - -icon_filename = 'mldonkey' - -short_description = _('Peer-to-peer File Sharing') - -description = [ +_description = [ _('MLDonkey is a peer-to-peer file sharing application used to exchange ' 'large files. It can participate in multiple peer-to-peer networks ' 'including eDonkey, Kademlia, Overnet, BitTorrent and DirectConnect.'), @@ -56,14 +50,10 @@ description = [ 'directory.'), box_name=cfg.box_name) ] -clients = clients - reserved_usernames = ['mldonkey'] group = ('ed2k', _('Download files using eDonkey applications')) -manual_page = 'MLDonkey' - app = None @@ -75,20 +65,27 @@ class MLDonkeyApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-mldonkey', name, short_description, - 'mldonkey', 'mldonkey:index', - parent_url_name='apps') + info = app_module.Info( + app_id=self.app_id, version=version, name=_('MLDonkey'), + icon_filename='mldonkey', + short_description=_('Peer-to-peer File Sharing'), + description=_description, manual_page='MLDonkey', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-mldonkey', info.name, + info.short_description, info.icon_filename, + 'mldonkey:index', parent_url_name='apps') self.add(menu_item) - shortcuts = frontpage.Shortcut('shortcut-mldonkey', name, - short_description=short_description, - icon=icon_filename, url='/mldonkey/', - login_required=True, clients=clients, - allowed_groups=[group[0]]) + shortcuts = frontpage.Shortcut( + 'shortcut-mldonkey', info.name, + short_description=info.short_description, icon=info.icon_filename, + url='/mldonkey/', login_required=True, clients=info.clients, + allowed_groups=[group[0]]) self.add(shortcuts) - firewall = Firewall('firewall-mldonkey', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-mldonkey', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-mldonkey', 'mldonkey-freedombox', diff --git a/plinth/modules/mldonkey/urls.py b/plinth/modules/mldonkey/urls.py index 71f84f5b6..139eb0e11 100644 --- a/plinth/modules/mldonkey/urls.py +++ b/plinth/modules/mldonkey/urls.py @@ -20,16 +20,12 @@ URLs for the mldonkey module. from django.conf.urls import url -from plinth.modules import mldonkey from plinth.views import AppView urlpatterns = [ - url( - r'^apps/mldonkey/$', - AppView.as_view(app_id='mldonkey', name=mldonkey.name, - description=mldonkey.description, - clients=mldonkey.clients, - manual_page=mldonkey.manual_page, - show_status_block=True, - icon_filename=mldonkey.icon_filename), name='index'), + url(r'^apps/mldonkey/$', + AppView.as_view( + app_id='mldonkey', + show_status_block=True, + ), name='index'), ] diff --git a/plinth/modules/monkeysphere/__init__.py b/plinth/modules/monkeysphere/__init__.py index de05d6538..a27c1e171 100644 --- a/plinth/modules/monkeysphere/__init__.py +++ b/plinth/modules/monkeysphere/__init__.py @@ -29,9 +29,7 @@ version = 1 managed_packages = ['monkeysphere'] -name = _('Monkeysphere') - -description = [ +_description = [ _('With Monkeysphere, an OpenPGP key can be generated for each configured ' 'domain serving SSH. The OpenPGP public key can then be uploaded to the ' 'OpenPGP keyservers. Users connecting to this machine through SSH can ' @@ -50,8 +48,6 @@ description = [ 'website.') ] -manual_page = "Monkeysphere" - reserved_usernames = ['monkeysphere'] app = None @@ -65,9 +61,15 @@ class MonkeysphereApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-monkeysphere', name, None, - 'fa-certificate', 'monkeysphere:index', - parent_url_name='system', advanced=True) + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Monkeysphere'), icon='fa-certificate', + description=_description, + manual_page='Monkeysphere') + self.add(info) + + menu_item = menu.Menu('menu-monkeysphere', info.name, None, info.icon, + 'monkeysphere:index', parent_url_name='system', + advanced=True) self.add(menu_item) diff --git a/plinth/modules/monkeysphere/views.py b/plinth/modules/monkeysphere/views.py index 18755c12d..f4c2247fc 100644 --- a/plinth/modules/monkeysphere/views.py +++ b/plinth/modules/monkeysphere/views.py @@ -39,11 +39,9 @@ def index(request): status = get_status() return TemplateResponse( request, 'monkeysphere.html', { - 'title': monkeysphere.name, - 'name': monkeysphere.name, - 'description': monkeysphere.description, + 'app_info': monkeysphere.app.info, + 'title': monkeysphere.app.info.name, 'status': status, - 'manual_page': monkeysphere.manual_page, 'running': bool(publish_process) }) @@ -67,7 +65,7 @@ def import_key(request, ssh_fingerprint): def details(request, fingerprint): """Get details for an OpenPGP key.""" return TemplateResponse(request, 'monkeysphere_details.html', { - 'title': monkeysphere.name, + 'title': monkeysphere.app.info.name, 'key': get_key(fingerprint) }) diff --git a/plinth/modules/mumble/__init__.py b/plinth/modules/mumble/__init__.py index 57252f842..ee14b24ac 100644 --- a/plinth/modules/mumble/__init__.py +++ b/plinth/modules/mumble/__init__.py @@ -30,17 +30,11 @@ from .manifest import backup, clients # noqa, pylint: disable=unused-import version = 1 -name = _('Mumble') - -icon_filename = 'mumble' - -short_description = _('Voice Chat') - managed_services = ['mumble-server'] managed_packages = ['mumble-server'] -description = [ +_description = [ _('Mumble is an open source, low-latency, encrypted, high quality ' 'voice chat software.'), _('You can connect to your Mumble server on the regular Mumble port ' @@ -48,12 +42,8 @@ description = [ 'from your desktop and Android devices are available.') ] -clients = clients - reserved_usernames = ['mumble-server'] -manual_page = 'Mumble' - port_forwarding_info = [ ('TCP', 64738), ('UDP', 64738), @@ -70,18 +60,26 @@ class MumbleApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-mumble', name, short_description, 'mumble', - 'mumble:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Mumble'), icon_filename='mumble', + short_description=_('Voice Chat'), + description=_description, manual_page='Mumble', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-mumble', info.name, info.short_description, + 'mumble', 'mumble:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-mumble', name, short_description=short_description, - icon=icon_filename, description=description, - configure_url=reverse_lazy('mumble:index'), clients=clients) + 'shortcut-mumble', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('mumble:index'), clients=info.clients) self.add(shortcut) - firewall = Firewall('firewall-mumble', name, ports=['mumble-plinth'], - is_external=True) + firewall = Firewall('firewall-mumble', info.name, + ports=['mumble-plinth'], is_external=True) self.add(firewall) daemon = Daemon( diff --git a/plinth/modules/mumble/views.py b/plinth/modules/mumble/views.py index 363856fc3..0541f65f0 100644 --- a/plinth/modules/mumble/views.py +++ b/plinth/modules/mumble/views.py @@ -18,20 +18,14 @@ from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from plinth import actions -from plinth.modules.mumble import (clients, description, icon_filename, - manual_page, name, port_forwarding_info) +from plinth.modules.mumble import port_forwarding_info from plinth.modules.mumble.forms import MumbleForm from plinth.views import AppView class MumbleAppView(AppView): app_id = 'mumble' - name = name - description = description - clients = clients - manual_page = manual_page port_forwarding_info = port_forwarding_info - icon_filename = icon_filename form_class = MumbleForm def form_valid(self, form): diff --git a/plinth/modules/names/__init__.py b/plinth/modules/names/__init__.py index 59b0a434e..b5087f333 100644 --- a/plinth/modules/names/__init__.py +++ b/plinth/modules/names/__init__.py @@ -34,13 +34,9 @@ version = 1 is_essential = True -name = _('Name Services') - logger = logging.getLogger(__name__) -manual_page = 'NameServices' - -description = [ +_description = [ format_lazy( _('Name Services provides an overview of the ways {box_name} can be ' 'reached from the public Internet: domain name, Tor onion service, ' @@ -60,7 +56,14 @@ class NamesApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-names', name, None, 'fa-tags', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, + name=_('Name Services'), icon='fa-tags', + description=_description, + manual_page='NameServices') + self.add(info) + + menu_item = menu.Menu('menu-names', info.name, None, info.icon, 'names:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/names/views.py b/plinth/modules/names/views.py index 2781436ed..a3d00e88e 100644 --- a/plinth/modules/names/views.py +++ b/plinth/modules/names/views.py @@ -29,13 +29,10 @@ def index(request): """Serve name services page.""" status = get_status() - return TemplateResponse( - request, 'names.html', { - 'name': names.name, - 'description': names.description, - 'manual_page': names.manual_page, - 'status': status - }) + return TemplateResponse(request, 'names.html', { + 'app_info': names.app.info, + 'status': status + }) def get_status(): diff --git a/plinth/modules/networks/__init__.py b/plinth/modules/networks/__init__.py index fcca30165..bc9237c01 100644 --- a/plinth/modules/networks/__init__.py +++ b/plinth/modules/networks/__init__.py @@ -39,9 +39,7 @@ first_boot_steps = [{ 'order': 4, }] -name = _('Networks') - -description = [ +_description = [ _('Configure network devices. Connect to the Internet via Ethernet, Wi-Fi ' 'or PPPoE. Share that connection with other devices on the network.'), _('Devices administered through other methods may not be available for ' @@ -50,8 +48,6 @@ description = [ logger = Logger(__name__) -manual_page = 'Networks' - app = None ROUTER_CONFIGURATION_TYPE_KEY = 'networks_router_configuration_type' @@ -65,7 +61,13 @@ class NetworksApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-networks', name, None, 'fa-signal', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Networks'), + icon='fa-signal', description=_description, + manual_page='Networks') + self.add(info) + + menu_item = menu.Menu('menu-networks', info.name, None, info.icon, 'networks:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/networks/networks.py b/plinth/modules/networks/networks.py index 937fc68af..29ca23b79 100644 --- a/plinth/modules/networks/networks.py +++ b/plinth/modules/networks/networks.py @@ -24,11 +24,11 @@ from django.urls import reverse_lazy from django.utils.translation import ugettext as _ from django.views.decorators.http import require_POST -from plinth import network, kvstore -from plinth.modules import networks, first_boot +from plinth import kvstore, network +from plinth.modules import first_boot, networks from .forms import (ConnectionTypeSelectForm, EthernetForm, GenericForm, - PPPoEForm, WifiForm, RouterConfigurationWizardForm) + PPPoEForm, RouterConfigurationWizardForm, WifiForm) logger = logging.getLogger(__name__) @@ -40,10 +40,8 @@ def index(request): return TemplateResponse( request, 'connections_list.html', { 'app_id': 'networks', + 'app_info': networks.app.info, 'title': _('Network Connections'), - 'name': networks.name, - 'description': networks.description, - 'manual_page': networks.manual_page, 'has_diagnostics': True, 'is_enabled': True, 'connections': connections @@ -431,10 +429,8 @@ def router_configuration_help_page(request): if form.is_valid(): logger.info('Updating router configuration setup with value: %s' % request.POST['router_config']) - kvstore.set( - networks.ROUTER_CONFIGURATION_TYPE_KEY, - request.POST['router_config'] - ) + kvstore.set(networks.ROUTER_CONFIGURATION_TYPE_KEY, + request.POST['router_config']) if is_firstboot: resp = reverse_lazy(first_boot.next_step()) else: @@ -446,8 +442,9 @@ def router_configuration_help_page(request): else: html = "router_configuration_update.html" initial = { - "router_config": kvstore.get_default( - networks.ROUTER_CONFIGURATION_TYPE_KEY, 'dmz'), + "router_config": + kvstore.get_default(networks.ROUTER_CONFIGURATION_TYPE_KEY, + 'dmz'), } template_kwargs = { 'form': RouterConfigurationWizardForm(initial=initial), diff --git a/plinth/modules/openvpn/__init__.py b/plinth/modules/openvpn/__init__.py index 1a9fa4ff3..04da07f33 100644 --- a/plinth/modules/openvpn/__init__.py +++ b/plinth/modules/openvpn/__init__.py @@ -36,13 +36,7 @@ managed_services = ['openvpn-server@freedombox'] managed_packages = ['openvpn', 'easy-rsa'] -name = _('OpenVPN') - -icon_filename = 'openvpn' - -short_description = _('Virtual Private Network') - -description = [ +_description = [ format_lazy( _('Virtual Private Network (VPN) is a technique for securely ' 'connecting two devices in order to access resources of a ' @@ -53,14 +47,10 @@ description = [ 'for added security and anonymity.'), box_name=_(cfg.box_name)) ] -manual_page = 'OpenVPN' - port_forwarding_info = [('UDP', 1194)] app = None -clients = clients - class OpenVPNApp(app_module.App): """FreedomBox app for OpenVPN.""" @@ -70,9 +60,16 @@ class OpenVPNApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-openvpn', name, short_description, - 'openvpn', 'openvpn:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('OpenVPN'), icon_filename='openvpn', + short_description=_('Virtual Private Network'), + description=_description, manual_page='OpenVPN', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-openvpn', info.name, + info.short_description, info.icon_filename, + 'openvpn:index', parent_url_name='apps') self.add(menu_item) download_profile = \ @@ -80,12 +77,13 @@ class OpenVPNApp(app_module.App): 'Download Profile'), link=reverse_lazy('openvpn:profile')) shortcut = frontpage.Shortcut( - 'shortcut-openvpn', name, short_description=short_description, - icon=icon_filename, description=description + [download_profile], + 'shortcut-openvpn', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description + [download_profile], configure_url=reverse_lazy('openvpn:index'), login_required=True) self.add(shortcut) - firewall = Firewall('firewall-openvpn', name, ports=['openvpn'], + firewall = Firewall('firewall-openvpn', info.name, ports=['openvpn'], is_external=True) self.add(firewall) diff --git a/plinth/modules/openvpn/views.py b/plinth/modules/openvpn/views.py index 65a0b7f18..3d578c856 100644 --- a/plinth/modules/openvpn/views.py +++ b/plinth/modules/openvpn/views.py @@ -59,10 +59,7 @@ def index(request): return TemplateResponse( request, 'openvpn.html', { 'app_id': 'openvpn', - 'clients': openvpn.clients, - 'name': openvpn.name, - 'description': openvpn.description, - 'manual_page': openvpn.manual_page, + 'app_info': openvpn.app.info, 'port_forwarding_info': openvpn.port_forwarding_info, 'status': status, 'form': form, @@ -70,7 +67,6 @@ def index(request): 'is_running': status['is_running'], 'has_diagnostics': True, 'is_enabled': status['enabled'], - 'icon_filename': openvpn.icon_filename }) diff --git a/plinth/modules/pagekite/__init__.py b/plinth/modules/pagekite/__init__.py index 38abdad52..114b35818 100644 --- a/plinth/modules/pagekite/__init__.py +++ b/plinth/modules/pagekite/__init__.py @@ -36,11 +36,7 @@ managed_services = ['pagekite'] managed_packages = ['pagekite'] -name = _('PageKite') - -short_description = _('Public Visibility') - -description = [ +_description = [ format_lazy( _('PageKite is a system for exposing {box_name} services when ' 'you don\'t have a direct connection to the Internet. You only ' @@ -66,8 +62,6 @@ description = [ box_name=_(cfg.box_name)) ] -manual_page = 'PageKite' - app = None @@ -79,9 +73,17 @@ class PagekiteApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-pagekite', name, short_description, - 'fa-flag', 'pagekite:index', - parent_url_name='system') + info = app_module.Info(app_id=self.app_id, version=version, + depends=depends, name=_('PageKite'), + icon='fa-flag', + short_description=_('Public Visibility'), + description=_description, + manual_page='PageKite') + self.add(info) + + menu_item = menu.Menu('menu-pagekite', info.name, + info.short_description, info.icon, + 'pagekite:index', parent_url_name='system') self.add(menu_item) domain_type = DomainType('domain-type-pagekite', _('PageKite Domain'), diff --git a/plinth/modules/pagekite/views.py b/plinth/modules/pagekite/views.py index 0045145e4..b0d1ba723 100644 --- a/plinth/modules/pagekite/views.py +++ b/plinth/modules/pagekite/views.py @@ -35,10 +35,8 @@ class ContextMixin(object): def get_context_data(self, **kwargs): """Use self.title and the module-level subsubmenu""" context = super(ContextMixin, self).get_context_data(**kwargs) - context['title'] = pagekite.name - context['name'] = pagekite.name - context['description'] = pagekite.description - context['manual_page'] = pagekite.manual_page + context['app_info'] = pagekite.app.info + context['title'] = pagekite.app.info.name context['is_enabled'] = pagekite.app.is_enabled() return context diff --git a/plinth/modules/power/__init__.py b/plinth/modules/power/__init__.py index 8a61adf5f..e85315386 100644 --- a/plinth/modules/power/__init__.py +++ b/plinth/modules/power/__init__.py @@ -20,19 +20,38 @@ FreedomBox app for power controls. from django.utils.translation import ugettext_lazy as _ +from plinth import app as app_module + from .manifest import backup # noqa, pylint: disable=unused-import version = 1 is_essential = True -name = _('Power') +_description = [_('Restart or shut down the system.')] -description = [_('Restart or shut down the system.')] +app = None -manual_page = 'Power' + +class PowerApp(app_module.App): + """FreedomBox app for power controls.""" + + app_id = 'power' + + def __init__(self): + """Create components for the app.""" + super().__init__() + + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Power'), + description=_description, manual_page='Power') + self.add(info) + + # not in menu, see issue #834 def init(): """Initialize the power module.""" - pass # not in menu, see issue #834 + global app + app = PowerApp() + app.set_enabled(True) diff --git a/plinth/modules/power/views.py b/plinth/modules/power/views.py index 260df6878..62de95604 100644 --- a/plinth/modules/power/views.py +++ b/plinth/modules/power/views.py @@ -22,7 +22,6 @@ from django.forms import Form from django.shortcuts import redirect from django.template.response import TemplateResponse from django.urls import reverse -from django.utils.translation import ugettext as _ from plinth import actions from plinth.modules import power @@ -32,10 +31,8 @@ def index(request): """Serve power controls page.""" return TemplateResponse( request, 'power.html', { - 'title': power.name, - 'name': power.name, - 'description': power.description, - 'manual_page': power.manual_page, + 'title': power.app.info.name, + 'app_info': power.app.info, 'pkg_manager_is_busy': _is_pkg_manager_busy() }) @@ -52,9 +49,9 @@ def restart(request): return TemplateResponse( request, 'power_restart.html', { - 'title': _('Power'), + 'title': power.app.info.name, 'form': form, - 'manual_page': power.manual_page, + 'manual_page': power.app.info.manual_page, 'pkg_manager_is_busy': _is_pkg_manager_busy() }) @@ -71,9 +68,9 @@ def shutdown(request): return TemplateResponse( request, 'power_shutdown.html', { - 'title': _('Power'), + 'title': power.app.info.name, 'form': form, - 'manual_page': power.manual_page, + 'manual_page': power.app.info.manual_page, 'pkg_manager_is_busy': _is_pkg_manager_busy() }) diff --git a/plinth/modules/privoxy/__init__.py b/plinth/modules/privoxy/__init__.py index f276c9c2d..1ffb16a70 100644 --- a/plinth/modules/privoxy/__init__.py +++ b/plinth/modules/privoxy/__init__.py @@ -40,13 +40,7 @@ managed_services = ['privoxy'] managed_packages = ['privoxy'] -name = _('Privoxy') - -icon_filename = 'privoxy' - -short_description = _('Web Proxy') - -description = [ +_description = [ _('Privoxy is a non-caching web proxy with advanced filtering ' 'capabilities for enhancing privacy, modifying web page data and ' 'HTTP headers, controlling access, and removing ads and other ' @@ -63,8 +57,6 @@ description = [ reserved_usernames = ['privoxy'] -manual_page = 'Privoxy' - app = None @@ -76,18 +68,25 @@ class PrivoxyApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-privoxy', name, short_description, - 'privoxy', 'privoxy:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Privoxy'), icon_filename='privoxy', + short_description=_('Web Proxy'), + description=_description, manual_page='Privoxy') + self.add(info) + + menu_item = menu.Menu('menu-privoxy', info.name, + info.short_description, info.icon_filename, + 'privoxy:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-privoxy', name, short_description=short_description, - icon=icon_filename, description=description, + 'shortcut-privoxy', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, configure_url=reverse_lazy('privoxy:index'), login_required=True) self.add(shortcut) - firewall = Firewall('firewall-privoxy', name, ports=['privoxy'], + firewall = Firewall('firewall-privoxy', info.name, ports=['privoxy'], is_external=False) self.add(firewall) @@ -122,10 +121,6 @@ def setup(helper, old_version=None): class PrivoxyAppView(AppView): app_id = 'privoxy' - name = name - description = description - manual_page = manual_page - icon_filename = icon_filename def diagnose_url_with_proxy(): diff --git a/plinth/modules/quassel/__init__.py b/plinth/modules/quassel/__init__.py index 2b4401de8..26e5b003d 100644 --- a/plinth/modules/quassel/__init__.py +++ b/plinth/modules/quassel/__init__.py @@ -42,13 +42,7 @@ managed_packages = ['quassel-core'] managed_paths = [pathlib.Path('/var/lib/quassel/')] -name = _('Quassel') - -icon_filename = 'quassel' - -short_description = _('IRC Client') - -description = [ +_description = [ format_lazy( _('Quassel is an IRC application that is split into two parts, a ' '"core" and a "client". This allows the core to remain connected ' @@ -64,12 +58,8 @@ description = [ 'are available.'), ] -clients = clients - reserved_usernames = ['quasselcore'] -manual_page = 'Quassel' - port_forwarding_info = [('TCP', 4242)] app = None @@ -83,20 +73,28 @@ class QuasselApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-quassel', name, short_description, - 'quassel', 'quassel:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Quassel'), icon_filename='quassel', + short_description=_('IRC Client'), + description=_description, manual_page='Quassel', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-quassel', info.name, + info.short_description, info.icon_filename, + 'quassel:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-quassel', name, short_description=short_description, - icon=icon_filename, description=description, - configure_url=reverse_lazy('quassel:index'), clients=clients, + 'shortcut-quassel', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('quassel:index'), clients=info.clients, login_required=True) self.add(shortcut) - firewall = Firewall('firewall-quassel', name, ports=['quassel-plinth'], - is_external=True) + firewall = Firewall('firewall-quassel', info.name, + ports=['quassel-plinth'], is_external=True) self.add(firewall) letsencrypt = LetsEncrypt( diff --git a/plinth/modules/quassel/views.py b/plinth/modules/quassel/views.py index e929fc88e..f5f294bce 100644 --- a/plinth/modules/quassel/views.py +++ b/plinth/modules/quassel/views.py @@ -23,13 +23,8 @@ from .forms import QuasselForm class QuasselAppView(AppView): app_id = 'quassel' - name = quassel.name - description = quassel.description - clients = quassel.clients - manual_page = quassel.manual_page port_forwarding_info = quassel.port_forwarding_info form_class = QuasselForm - icon_filename = quassel.icon_filename def get_initial(self): """Return the values to fill in the form.""" diff --git a/plinth/modules/radicale/__init__.py b/plinth/modules/radicale/__init__.py index e8be8078e..d94b65c47 100644 --- a/plinth/modules/radicale/__init__.py +++ b/plinth/modules/radicale/__init__.py @@ -42,13 +42,7 @@ managed_services = ['radicale'] managed_packages = ['radicale', 'uwsgi', 'uwsgi-plugin-python3'] -name = _('Radicale') - -icon_filename = 'radicale' - -short_description = _('Calendar and Addressbook') - -description = [ +_description = [ format_lazy( _('Radicale is a CalDAV and CardDAV server. It allows synchronization ' 'and sharing of scheduling and contact data. To use Radicale, a ' @@ -60,12 +54,8 @@ description = [ 'contacts, which must be done using a separate client.'), ] -clients = clients - reserved_usernames = ['radicale'] -manual_page = 'Radicale' - logger = logging.getLogger(__name__) CONFIG_FILE = '/etc/radicale/config' @@ -83,19 +73,27 @@ class RadicaleApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-radicale', name, short_description, - 'radicale', 'radicale:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Radicale'), icon_filename='radicale', + short_description=_('Calendar and Addressbook'), + description=_description, + manual_page='Radicale', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-radicale', info.name, + info.short_description, info.icon_filename, + 'radicale:index', parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-radicale', name, - short_description=short_description, - icon=icon_filename, url='/radicale/', - clients=clients, login_required=True) + shortcut = frontpage.Shortcut('shortcut-radicale', info.name, + short_description=info.short_description, + icon=info.icon_filename, + url='/radicale/', clients=info.clients, + login_required=True) self.add(shortcut) - firewall = Firewall('firewall-radicale', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-radicale', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = RadicaleWebserver('webserver-radicale', None, diff --git a/plinth/modules/radicale/views.py b/plinth/modules/radicale/views.py index f638022d7..0b3880025 100644 --- a/plinth/modules/radicale/views.py +++ b/plinth/modules/radicale/views.py @@ -22,22 +22,16 @@ from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from plinth import actions -from plinth.modules import radicale from plinth.views import AppView -from . import description, get_rights_value +from . import get_rights_value from .forms import RadicaleForm class RadicaleAppView(AppView): """A specialized view for configuring radicale service.""" - clients = radicale.clients - name = radicale.name - description = description form_class = RadicaleForm app_id = 'radicale' - manual_page = radicale.manual_page - icon_filename = radicale.icon_filename def get_initial(self): """Return the values to fill in the form.""" diff --git a/plinth/modules/roundcube/__init__.py b/plinth/modules/roundcube/__init__.py index b88102d6f..4c4349296 100644 --- a/plinth/modules/roundcube/__init__.py +++ b/plinth/modules/roundcube/__init__.py @@ -32,13 +32,7 @@ version = 1 managed_packages = ['sqlite3', 'roundcube', 'roundcube-sqlite3'] -name = _('Roundcube') - -icon_filename = 'roundcube' - -short_description = _('Email Client') - -description = [ +_description = [ _('Roundcube webmail is a browser-based multilingual IMAP ' 'client with an application-like user interface. It provides ' 'full functionality you expect from an email client, including ' @@ -58,8 +52,6 @@ description = [ '>https://www.google.com/settings/security/lesssecureapps).'), ] -clients = clients - manual_page = 'Roundcube' app = None @@ -73,18 +65,26 @@ class RoundcubeApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-roundcube', name, short_description, - 'roundcube', 'roundcube:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Roundcube'), icon_filename='roundcube', + short_description=_('Email Client'), + description=_description, + manual_page='Roundcube', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-roundcube', info.name, + info.short_description, info.icon_filename, + 'roundcube:index', parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-roundcube', name, - short_description=short_description, - icon=icon_filename, url='/roundcube/', - clients=clients, login_required=True) + shortcut = frontpage.Shortcut('shortcut-roundcube', info.name, + short_description=info.short_description, + icon=info.icon_filename, + url='/roundcube/', clients=info.clients, + login_required=True) self.add(shortcut) - firewall = Firewall('firewall-roundcube', name, + firewall = Firewall('firewall-roundcube', info.name, ports=['http', 'https'], is_external=True) self.add(firewall) diff --git a/plinth/modules/roundcube/urls.py b/plinth/modules/roundcube/urls.py index ba3bda5fc..ac3a33343 100644 --- a/plinth/modules/roundcube/urls.py +++ b/plinth/modules/roundcube/urls.py @@ -20,15 +20,10 @@ URLs for the Roundcube module. from django.conf.urls import url -from plinth.modules import roundcube from plinth.views import AppView urlpatterns = [ - url( - r'^apps/roundcube/$', - AppView.as_view(app_id='roundcube', name=roundcube.name, - description=roundcube.description, - show_status_block=False, clients=roundcube.clients, - manual_page=roundcube.manual_page, - icon_filename=roundcube.icon_filename), name='index'), + url(r'^apps/roundcube/$', + AppView.as_view(app_id='roundcube', show_status_block=False), + name='index'), ] diff --git a/plinth/modules/samba/__init__.py b/plinth/modules/samba/__init__.py index c93d5688f..49f2d9ffe 100644 --- a/plinth/modules/samba/__init__.py +++ b/plinth/modules/samba/__init__.py @@ -43,13 +43,7 @@ managed_services = ['smbd', 'nmbd'] managed_packages = ['samba', 'acl'] -name = _('Samba') - -icon_filename = 'samba' - -short_description = _('File Sharing') - -description = [ +_description = [ _('Samba allows to share files and folders between FreedomBox and ' 'other computers in your local network.'), format_lazy( @@ -67,8 +61,6 @@ description = [ group = ('freedombox-share', _('Access to the private shares')) -clients = clients - app = None @@ -80,18 +72,26 @@ class SambaApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-samba', name, short_description, 'samba', - 'samba:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Samba'), icon_filename='samba', + short_description=_('File Sharing'), + description=_description, clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-samba', info.name, info.short_description, + info.icon_filename, 'samba:index', + parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-samba', name, short_description=short_description, - icon=icon_filename, description=description, - configure_url=reverse_lazy('samba:index'), clients=clients, + 'shortcut-samba', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, + configure_url=reverse_lazy('samba:index'), clients=info.clients, login_required=True, allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-samba', name, ports=['samba']) + firewall = Firewall('firewall-samba', info.name, ports=['samba']) self.add(firewall) daemon = Daemon( diff --git a/plinth/modules/samba/views.py b/plinth/modules/samba/views.py index f3828d4ad..a51fbc72e 100644 --- a/plinth/modules/samba/views.py +++ b/plinth/modules/samba/views.py @@ -37,11 +37,8 @@ logger = logging.getLogger(__name__) class SambaAppView(views.AppView): """Samba sharing basic configuration.""" - name = samba.name - description = samba.description app_id = 'samba' template_name = 'samba.html' - icon_filename = samba.icon_filename def get_context_data(self, *args, **kwargs): """Return template context data.""" diff --git a/plinth/modules/searx/__init__.py b/plinth/modules/searx/__init__.py index 70c738ee4..0a4cde1b9 100644 --- a/plinth/modules/searx/__init__.py +++ b/plinth/modules/searx/__init__.py @@ -32,19 +32,11 @@ from plinth.modules.users import register_group from .manifest import (PUBLIC_ACCESS_SETTING_FILE, # noqa, pylint: disable=unused-import backup, clients) -clients = clients - version = 3 managed_packages = ['searx', 'uwsgi', 'uwsgi-plugin-python3'] -name = _('Searx') - -icon_filename = 'searx' - -short_description = _('Web Search') - -description = [ +_description = [ _('Searx is a privacy-respecting Internet metasearch engine. ' 'It aggregrates and displays results from multiple search engines.'), _('Searx can be used to avoid tracking and profiling by search engines. ' @@ -66,19 +58,28 @@ class SearxApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-searx', name, short_description, 'searx', - 'searx:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Searx'), icon_filename='searx', + short_description=_('Web Search'), + description=_description, manual_page='Searx', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-searx', info.name, info.short_description, + info.icon_filename, 'searx:index', + parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-searx', name, short_description=short_description, - icon=icon_filename, url='/searx/', clients=clients, + 'shortcut-searx', info.name, + short_description=info.short_description, icon=info.icon_filename, + url='/searx/', clients=info.clients, login_required=(not is_public_access_enabled()), allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-searx', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-searx', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-searx', 'searx-freedombox', diff --git a/plinth/modules/searx/views.py b/plinth/modules/searx/views.py index 2921ee2ac..16f3ed96e 100644 --- a/plinth/modules/searx/views.py +++ b/plinth/modules/searx/views.py @@ -30,14 +30,9 @@ from .forms import SearxForm class SearxAppView(views.AppView): """Serve configuration page.""" - clients = searx.clients - name = searx.name - description = searx.description app_id = 'searx' form_class = SearxForm show_status_block = False - manual_page = searx.manual_page - icon_filename = searx.icon_filename def get_initial(self): """Return the status of the service to fill in the form.""" diff --git a/plinth/modules/security/__init__.py b/plinth/modules/security/__init__.py index 859c6dd1d..b17df5082 100644 --- a/plinth/modules/security/__init__.py +++ b/plinth/modules/security/__init__.py @@ -35,14 +35,10 @@ version = 6 is_essential = True -name = _('Security') - managed_packages = ['fail2ban', 'debsecan'] managed_services = ['fail2ban'] -manual_page = 'Security' - ACCESS_CONF_FILE = '/etc/security/access.d/50freedombox.conf' ACCESS_CONF_FILE_OLD = '/etc/security/access.conf' ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx plinth (admin) (sudo):ALL' @@ -60,7 +56,12 @@ class SecurityApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-security', name, None, 'fa-lock', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Security'), + icon='fa-lock', manual_page='Security') + self.add(info) + + menu_item = menu.Menu('menu-security', info.name, None, info.icon, 'security:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/security/views.py b/plinth/modules/security/views.py index d64eafde5..02577e1f4 100644 --- a/plinth/modules/security/views.py +++ b/plinth/modules/security/views.py @@ -44,8 +44,7 @@ def index(request): form = SecurityForm(initial=status, prefix='security') return TemplateResponse(request, 'security.html', { - 'name': _('Security'), - 'manual_page': security.manual_page, + 'app_info': security.app.info, 'form': form, }) diff --git a/plinth/modules/shaarli/__init__.py b/plinth/modules/shaarli/__init__.py index dbe8b6f6d..75fdae980 100644 --- a/plinth/modules/shaarli/__init__.py +++ b/plinth/modules/shaarli/__init__.py @@ -31,11 +31,7 @@ version = 1 managed_packages = ['shaarli'] -name = _('Shaarli') - -short_description = _('Bookmarks') - -description = [ +_description = [ _('Shaarli allows you to save and share bookmarks.'), _('When enabled, Shaarli will be available from /shaarli path on the web server. Note that ' @@ -43,10 +39,6 @@ description = [ 'setup on the initial visit.'), ] -clients = clients - -manual_page = 'Shaarli' - app = None @@ -58,19 +50,27 @@ class ShaarliApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-shaarli', name, short_description, - 'shaarli', 'shaarli:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Shaarli'), icon_filename='shaarli', + short_description=_('Bookmarks'), + description=_description, manual_page='Shaarli', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-shaarli', info.name, + info.short_description, info.icon_filename, + 'shaarli:index', parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-shaarli', name, - short_description=short_description, - icon='shaarli', url='/shaarli', - clients=clients, login_required=True) + shortcut = frontpage.Shortcut('shortcut-shaarli', info.name, + short_description=info.short_description, + icon=info.icon_filename, url='/shaarli', + clients=info.clients, + login_required=True) self.add(shortcut) - firewall = Firewall('firewall-shaarli', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-shaarli', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-shaarli', 'shaarli') diff --git a/plinth/modules/shaarli/urls.py b/plinth/modules/shaarli/urls.py index 5bfaa73a5..8b7971563 100644 --- a/plinth/modules/shaarli/urls.py +++ b/plinth/modules/shaarli/urls.py @@ -20,14 +20,10 @@ URLs for the Shaarli module. from django.conf.urls import url -from plinth.modules import shaarli from plinth.views import AppView urlpatterns = [ - 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'), + url(r'^apps/shaarli/$', + AppView.as_view(app_id='shaarli', show_status_block=False), + name='index'), ] diff --git a/plinth/modules/shadowsocks/__init__.py b/plinth/modules/shadowsocks/__init__.py index 5b31e0ffd..ed59929c4 100644 --- a/plinth/modules/shadowsocks/__init__.py +++ b/plinth/modules/shadowsocks/__init__.py @@ -32,17 +32,11 @@ from .manifest import backup # noqa, pylint: disable=unused-import version = 1 -name = _('Shadowsocks') - -icon_filename = 'shadowsocks' - -short_description = _('Socks5 Proxy') - managed_services = ['shadowsocks-libev-local@freedombox'] managed_packages = ['shadowsocks-libev'] -description = [ +_description = [ _('Shadowsocks is a lightweight and secure SOCKS5 proxy, designed to ' 'protect your Internet traffic. It can be used to bypass Internet ' 'filtering and censorship.'), @@ -56,8 +50,6 @@ description = [ 'device, browser or application to http://freedombox_address:1080/') ] -manual_page = 'Shadowsocks' - app = None @@ -69,19 +61,28 @@ class ShadowsocksApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-shadowsocks', name, short_description, - 'shadowsocks', 'shadowsocks:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Shadowsocks'), + icon_filename='shadowsocks', + short_description=_('Socks5 Proxy'), + description=_description, + manual_page='Shadowsocks') + self.add(info) + + menu_item = menu.Menu('menu-shadowsocks', info.name, + info.short_description, info.icon_filename, + 'shadowsocks:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-shadowsocks', name, short_description=short_description, - icon=icon_filename, description=description, + 'shortcut-shadowsocks', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, configure_url=reverse_lazy('shadowsocks:index'), login_required=True) self.add(shortcut) - firewall = Firewall('firewall-shadowsocks', name, + firewall = Firewall('firewall-shadowsocks', info.name, ports=['shadowsocks-local-plinth'], is_external=False) self.add(firewall) diff --git a/plinth/modules/shadowsocks/views.py b/plinth/modules/shadowsocks/views.py index e3590d60c..2d095e3ab 100644 --- a/plinth/modules/shadowsocks/views.py +++ b/plinth/modules/shadowsocks/views.py @@ -25,7 +25,6 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions, views from plinth.errors import ActionError -from plinth.modules import shadowsocks from .forms import ShadowsocksForm @@ -34,10 +33,6 @@ class ShadowsocksAppView(views.AppView): """Configuration view for Shadowsocks local socks5 proxy.""" app_id = 'shadowsocks' form_class = ShadowsocksForm - name = shadowsocks.name - description = shadowsocks.description - manual_page = shadowsocks.manual_page - icon_filename = shadowsocks.icon_filename def get_initial(self, *args, **kwargs): """Get initial values for form.""" diff --git a/plinth/modules/sharing/__init__.py b/plinth/modules/sharing/__init__.py index 25a905cfb..14f539e7a 100644 --- a/plinth/modules/sharing/__init__.py +++ b/plinth/modules/sharing/__init__.py @@ -31,9 +31,7 @@ from .manifest import backup # noqa, pylint: disable=unused-import version = 1 -name = _('Sharing') - -description = [ +_description = [ format_lazy( _('Sharing allows you to share files and folders on your {box_name} ' 'over the web with chosen groups of users.'), @@ -42,8 +40,6 @@ description = [ app = None -icon_filename = 'sharing' - class SharingApp(app_module.App): """FreedomBox app for sharing files.""" @@ -53,8 +49,14 @@ class SharingApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-sharing', name, None, 'sharing', - 'sharing:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Sharing'), icon_filename='sharing', + description=_description) + self.add(info) + + menu_item = menu.Menu('menu-sharing', info.name, None, + info.icon_filename, 'sharing:index', + parent_url_name='apps') self.add(menu_item) diff --git a/plinth/modules/sharing/views.py b/plinth/modules/sharing/views.py index fb07caf48..94cd7c0cb 100644 --- a/plinth/modules/sharing/views.py +++ b/plinth/modules/sharing/views.py @@ -39,10 +39,9 @@ class IndexView(TemplateView): def get_context_data(self, **kwargs): """Return additional context for rendering the template.""" context = super().get_context_data(**kwargs) - context['title'] = sharing.name - context['description'] = sharing.description + context['title'] = sharing.app.info.name + context['app_info'] = sharing.app.info context['shares'] = sharing.list_shares() - context['icon_filename'] = sharing.icon_filename return context diff --git a/plinth/modules/snapshot/__init__.py b/plinth/modules/snapshot/__init__.py index 82501aa0f..7d1d00e16 100644 --- a/plinth/modules/snapshot/__init__.py +++ b/plinth/modules/snapshot/__init__.py @@ -34,9 +34,7 @@ version = 4 managed_packages = ['snapper'] -name = _('Storage Snapshots') - -description = [ +_description = [ _('Snapshots allows creating and managing btrfs file system snapshots. ' 'These can be used to roll back the system to a previously known ' 'good state in case of unwanted changes to the system.'), @@ -50,8 +48,6 @@ description = [ 'they can only be stored on the same partition. ') ] -manual_page = 'Snapshots' - DEFAULT_FILE = '/etc/default/snapper' fs_types_supported = ['btrfs'] @@ -67,7 +63,13 @@ class SnapshotApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-snapshot', name, None, 'fa-film', + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Storage Snapshots'), icon='fa-film', + description=_description, + manual_page='Snapshots') + self.add(info) + + menu_item = menu.Menu('menu-snapshot', info.name, None, info.icon, 'snapshot:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/snapshot/views.py b/plinth/modules/snapshot/views.py index 92f303c7c..f314eedc8 100644 --- a/plinth/modules/snapshot/views.py +++ b/plinth/modules/snapshot/views.py @@ -50,12 +50,10 @@ subsubmenu = [ def not_supported_view(request): """Show that snapshots are not supported on the system.""" template_data = { - 'title': snapshot_module.name, - 'name': snapshot_module.name, - 'description': snapshot_module.description, + 'app_info': snapshot_module.app.info, + 'title': snapshot_module.app.info.name, 'fs_type': storage.get_filesystem_type(), 'fs_types_supported': snapshot_module.fs_types_supported, - 'manual_page': snapshot_module.manual_page, } return TemplateResponse(request, 'snapshot_not_supported.html', template_data) @@ -78,10 +76,8 @@ def index(request): return TemplateResponse( request, 'snapshot.html', { - 'title': snapshot_module.name, - 'name': snapshot_module.name, - 'description': snapshot_module.description, - 'manual_page': snapshot_module.manual_page, + 'app_info': snapshot_module.app.info, + 'title': snapshot_module.app.info, 'subsubmenu': subsubmenu, 'form': form }) @@ -109,10 +105,8 @@ def manage(request): return TemplateResponse( request, 'snapshot_manage.html', { - 'title': snapshot_module.name, - 'name': snapshot_module.name, - 'description': snapshot_module.description, - 'manual_page': snapshot_module.manual_page, + 'title': snapshot_module.app.info.name, + 'app_info': snapshot_module.app.info, 'snapshots': snapshots, 'has_deletable_snapshots': has_deletable_snapshots, 'subsubmenu': subsubmenu, diff --git a/plinth/modules/ssh/__init__.py b/plinth/modules/ssh/__init__.py index 5a079d64b..d8e88ec5f 100644 --- a/plinth/modules/ssh/__init__.py +++ b/plinth/modules/ssh/__init__.py @@ -40,9 +40,7 @@ managed_services = ['ssh'] managed_packages = ['openssh-server'] -name = _('Secure Shell (SSH) Server') - -description = [ +_description = [ _('A Secure Shell server uses the secure shell protocol to accept ' 'connections from remote computers. An authorized remote computer ' 'can perform administration tasks, copy files or run other services ' @@ -62,11 +60,17 @@ class SSHApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-ssh', name, None, 'fa-terminal', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, + name=_('Secure Shell (SSH) Server'), + icon='fa-terminal', description=_description) + self.add(info) + + menu_item = menu.Menu('menu-ssh', info.name, None, info.icon, 'ssh:index', parent_url_name='system') self.add(menu_item) - firewall = Firewall('firewall-ssh', name, ports=['ssh'], + firewall = Firewall('firewall-ssh', info.name, ports=['ssh'], is_external=True) self.add(firewall) diff --git a/plinth/modules/ssh/views.py b/plinth/modules/ssh/views.py index fc913085d..5dd7162ea 100644 --- a/plinth/modules/ssh/views.py +++ b/plinth/modules/ssh/views.py @@ -30,9 +30,6 @@ from .forms import SSHServerForm class SshAppView(AppView): app_id = 'ssh' - name = ssh.name - description = ssh.description - port_forwarding_info = ssh.port_forwarding_info template_name = 'ssh.html' form_class = SSHServerForm diff --git a/plinth/modules/sso/__init__.py b/plinth/modules/sso/__init__.py index 3e3bef6f1..67609fd1d 100644 --- a/plinth/modules/sso/__init__.py +++ b/plinth/modules/sso/__init__.py @@ -18,17 +18,17 @@ FreedomBox app to configure Single Sign On services. """ -from plinth import actions from django.utils.translation import ugettext_lazy as _ +from plinth import actions +from plinth import app as app_module + version = 1 is_essential = True depends = ['security', 'apache'] -name = _('Single Sign On') - managed_packages = [ 'libapache2-mod-auth-pubtkt', 'openssl', @@ -37,6 +37,18 @@ managed_packages = [ ] +class SSOApp(app_module.App): + """FreedomBox app for single sign on.""" + app_id = 'sso' + + def __init__(self): + """Create components for the app.""" + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, depends=depends, + name=_('Single Sign On')) + self.add(info) + + def setup(helper, old_version=None): """Install the required packages""" helper.install(managed_packages) diff --git a/plinth/modules/sso/views.py b/plinth/modules/sso/views.py index dda518f9b..a02564b55 100644 --- a/plinth/modules/sso/views.py +++ b/plinth/modules/sso/views.py @@ -27,6 +27,7 @@ from axes.decorators import axes_form_invalid from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.views import LoginView, LogoutView from django.http import HttpResponseRedirect + from plinth import actions, utils, web_framework from .forms import AuthenticationForm, CaptchaAuthenticationForm @@ -82,8 +83,8 @@ class CaptchaLoginView(LoginView): form_class = CaptchaAuthenticationForm def dispatch(self, request, *args, **kwargs): - response = super(CaptchaLoginView, self).dispatch( - request, *args, **kwargs) + response = super(CaptchaLoginView, + self).dispatch(request, *args, **kwargs) if not request.POST: return response diff --git a/plinth/modules/storage/__init__.py b/plinth/modules/storage/__init__.py index 2f59410f3..e0e725725 100644 --- a/plinth/modules/storage/__init__.py +++ b/plinth/modules/storage/__init__.py @@ -36,13 +36,11 @@ from .manifest import backup # noqa, pylint: disable=unused-import version = 4 -name = _('Storage') - managed_services = ['freedombox-udiskie'] managed_packages = ['parted', 'udiskie', 'gir1.2-udisks-2.0'] -description = [ +_description = [ format_lazy( _('This module allows you to manage storage media attached to your ' '{box_name}. You can view the storage media currently in use, mount ' @@ -52,8 +50,6 @@ description = [ logger = logging.getLogger(__name__) -manual_page = 'Storage' - is_essential = True app = None @@ -67,7 +63,13 @@ class StorageApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-storage', name, None, 'fa-hdd-o', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Storage'), + icon='fa-hdd-o', description=_description, + manual_page='Storage') + self.add(info) + + menu_item = menu.Menu('menu-storage', info.name, None, info.icon, 'storage:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/storage/views.py b/plinth/modules/storage/views.py index c3c0b2fb6..dd2b672b8 100644 --- a/plinth/modules/storage/views.py +++ b/plinth/modules/storage/views.py @@ -39,9 +39,6 @@ logger = logging.getLogger(__name__) class StorageAppView(views.AppView): """Show storage information.""" - name = storage.name - description = storage.description - manual_page = storage.manual_page app_id = 'storage' template_name = 'storage.html' show_status_block = False diff --git a/plinth/modules/syncthing/__init__.py b/plinth/modules/syncthing/__init__.py index ab6f29198..bfb06ccb2 100644 --- a/plinth/modules/syncthing/__init__.py +++ b/plinth/modules/syncthing/__init__.py @@ -37,13 +37,7 @@ managed_services = ['syncthing@syncthing'] managed_packages = ['syncthing'] -name = _('Syncthing') - -icon_filename = 'syncthing' - -short_description = _('File Synchronization') - -description = [ +_description = [ _('Syncthing is an application to synchronize files across multiple ' 'devices, e.g. your desktop computer and mobile phone. Creation, ' 'modification, or deletion of files on one device will be automatically ' @@ -58,12 +52,8 @@ description = [ 'users belonging to the "admin" group.'), box_name=_(cfg.box_name)), ] -clients = clients - group = ('syncthing', _('Administer Syncthing application')) -manual_page = 'Syncthing' - app = None @@ -75,23 +65,31 @@ class SyncthingApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-syncthing', name, short_description, - 'syncthing', 'syncthing:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Syncthing'), icon_filename='syncthing', + short_description=_('File Synchronization'), + description=_description, + manual_page='Syncthing', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-syncthing', info.name, + info.short_description, info.icon_filename, + 'syncthing:index', parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-syncthing', name, - short_description=short_description, - icon=icon_filename, url='/syncthing/', - clients=clients, login_required=True, + shortcut = frontpage.Shortcut('shortcut-syncthing', info.name, + short_description=info.short_description, + icon=info.icon_filename, + url='/syncthing/', clients=info.clients, + login_required=True, allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-syncthing-web', name, + firewall = Firewall('firewall-syncthing-web', info.name, ports=['http', 'https'], is_external=True) self.add(firewall) - firewall = Firewall('firewall-syncthing-ports', name, + firewall = Firewall('firewall-syncthing-ports', info.name, ports=['syncthing'], is_external=True) self.add(firewall) diff --git a/plinth/modules/syncthing/urls.py b/plinth/modules/syncthing/urls.py index f02869180..a33b06805 100644 --- a/plinth/modules/syncthing/urls.py +++ b/plinth/modules/syncthing/urls.py @@ -20,16 +20,10 @@ URLs for the Syncthing module. from django.conf.urls import url -from plinth.modules import syncthing from plinth.views import AppView urlpatterns = [ - url( - r'^apps/syncthing/$', - AppView.as_view(app_id='syncthing', name=syncthing.name, - description=syncthing.description, - clients=syncthing.clients, - manual_page=syncthing.manual_page, - icon_filename=syncthing.icon_filename, - show_status_block=True), name='index'), + url(r'^apps/syncthing/$', + AppView.as_view(app_id='syncthing', show_status_block=True), + name='index'), ] diff --git a/plinth/modules/tahoe/__init__.py b/plinth/modules/tahoe/__init__.py index 7d8724ffd..f0a355c12 100644 --- a/plinth/modules/tahoe/__init__.py +++ b/plinth/modules/tahoe/__init__.py @@ -21,6 +21,7 @@ FreedomBox app to configure Tahoe-LAFS. import json import os +from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ from plinth import actions @@ -40,11 +41,16 @@ managed_services = ['tahoe-lafs'] managed_packages = ['tahoe-lafs'] -name = _('Tahoe-LAFS') - -icon_filename = 'tahoe-lafs' - -short_description = _('Distributed File Storage') +_description = [ + _('Tahoe-LAFS is a decentralized secure file storage system. ' + 'It uses provider independent security to store files over a ' + 'distributed network of storage nodes. Even if some of the nodes fail, ' + 'your files can be retrieved from the remaining nodes.'), + format_lazy( + _('This {box_name} hosts a storage node and an introducer by default. ' + 'Additional introducers can be added, which will introduce this ' + 'node to the other storage nodes.'), box_name=_(cfg.box_name)), +] port_forwarding_info = [ ('TCP', 3456), @@ -71,19 +77,28 @@ class TahoeApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-tahoe', name, short_description, - 'tahoe-lafs', 'tahoe:index', + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Tahoe-LAFS'), + icon_filename='tahoe-lafs', + short_description=_('Distributed File Storage'), + description=_description) + + self.add(info) + + menu_item = menu.Menu('menu-tahoe', info.name, info.short_description, + info.icon_filename, 'tahoe:index', parent_url_name='apps', advanced=True) self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-tahoe', name, - short_description=short_description, - icon=icon_filename, url=None, - login_required=True) + shortcut = frontpage.Shortcut( + 'shortcut-tahoe', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, url=None, + configure_url=reverse_lazy('tahoe:index'), login_required=True) self.add(shortcut) - firewall = Firewall('firewall-tahoe', name, ports=['tahoe-plinth'], - is_external=True) + firewall = Firewall('firewall-tahoe', info.name, + ports=['tahoe-plinth'], is_external=True) self.add(firewall) webserver = Webserver('webserver-tahoe', 'tahoe-plinth') @@ -130,18 +145,6 @@ def get_configured_domain_name(): return dnf.read().rstrip() -description = [ - _('Tahoe-LAFS is a decentralized secure file storage system. ' - 'It uses provider independent security to store files over a ' - 'distributed network of storage nodes. Even if some of the nodes fail, ' - 'your files can be retrieved from the remaining nodes.'), - format_lazy( - _('This {box_name} hosts a storage node and an introducer by default. ' - 'Additional introducers can be added, which will introduce this ' - 'node to the other storage nodes.'), box_name=_(cfg.box_name)), -] - - def init(): """Initialize the module.""" global app diff --git a/plinth/modules/tahoe/views.py b/plinth/modules/tahoe/views.py index 1d490f9fe..072024f81 100644 --- a/plinth/modules/tahoe/views.py +++ b/plinth/modules/tahoe/views.py @@ -30,8 +30,6 @@ class TahoeSetupView(FormView): """Show tahoe-lafs setup page.""" template_name = 'tahoe-pre-setup.html' form_class = DomainSelectionForm - description = tahoe.description - title = tahoe.name success_url = reverse_lazy('tahoe:index') def form_valid(self, form): @@ -41,8 +39,8 @@ class TahoeSetupView(FormView): def get_context_data(self, *args, **kwargs): context = super().get_context_data(**kwargs) - context['description'] = self.description - context['title'] = self.title + context['description'] = tahoe.app.info.description + context['title'] = tahoe.app.info.name context['domain_names'] = names.components.DomainName.list_names( 'tahoe-plinth') @@ -53,10 +51,7 @@ class TahoeAppView(AppView): """Show tahoe-lafs service page.""" app_id = 'tahoe' template_name = 'tahoe-post-setup.html' - name = tahoe.name - description = tahoe.description port_forwarding_info = tahoe.port_forwarding_info - icon_filename = tahoe.icon_filename def dispatch(self, request, *args, **kwargs): if not tahoe.is_setup(): diff --git a/plinth/modules/tor/__init__.py b/plinth/modules/tor/__init__.py index 0940affae..27dab4b8f 100644 --- a/plinth/modules/tor/__init__.py +++ b/plinth/modules/tor/__init__.py @@ -44,11 +44,7 @@ managed_packages = [ managed_services = ['tor@plinth'] -name = _('Tor') - -short_description = _('Anonymity Network') - -description = [ +_description = [ _('Tor is an anonymous communication system. You can learn more ' 'about it from the Tor ' 'Project website. For best protection when web surfing, the ' @@ -57,16 +53,10 @@ description = [ 'Tor Browser.') ] -clients = clients - reserved_usernames = ['debian-tor'] -manual_page = 'Tor' - app = None -icon_filename = 'tor' - class TorApp(app_module.App): """FreedomBox app for Tor.""" @@ -76,8 +66,16 @@ class TorApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-tor', name, short_description, 'tor', - 'tor:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Tor'), icon_filename='tor', + short_description=_('Anonymity Network'), + description=_description, manual_page='Tor', + clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-tor', info.name, info.short_description, + info.icon_filename, 'tor:index', + parent_url_name='apps') self.add(menu_item) domain_type = DomainType('domain-type-tor', _('Tor Onion Service'), diff --git a/plinth/modules/tor/views.py b/plinth/modules/tor/views.py index 59751e5f0..c12e32038 100644 --- a/plinth/modules/tor/views.py +++ b/plinth/modules/tor/views.py @@ -53,10 +53,7 @@ def index(request): return TemplateResponse( request, 'tor.html', { 'app_id': 'tor', - 'name': tor.name, - 'description': tor.description, - 'clients': tor.clients, - 'manual_page': tor.manual_page, + 'app_info': tor.app.info, 'status': status, 'config_running': bool(config_process), 'form': form, @@ -65,7 +62,6 @@ def index(request): 'is_enabled': status['enabled'], 'show_status_block': True, 'is_running': status['is_running'], - 'icon_filename': tor.icon_filename, }) diff --git a/plinth/modules/transmission/__init__.py b/plinth/modules/transmission/__init__.py index 128e2fb34..080e45c1a 100644 --- a/plinth/modules/transmission/__init__.py +++ b/plinth/modules/transmission/__init__.py @@ -38,26 +38,16 @@ managed_services = ['transmission-daemon'] managed_packages = ['transmission-daemon'] -name = _('Transmission') - -icon_filename = 'transmission' - -short_description = _('BitTorrent Web Client') - -description = [ +_description = [ _('BitTorrent is a peer-to-peer file sharing protocol. ' 'Transmission daemon handles Bitorrent file sharing. Note that ' 'BitTorrent is not anonymous.'), ] -clients = clients - reserved_usernames = ['debian-transmission'] group = ('bit-torrent', _('Download files using BitTorrent applications')) -manual_page = 'Transmission' - app = None @@ -69,19 +59,27 @@ class TransmissionApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-transmission', name, short_description, - 'transmission', 'transmission:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Transmission'), + icon_filename='transmission', + short_description=_('BitTorrent Web Client'), + description=_description, + manual_page='Transmission', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-transmission', info.name, + info.short_description, info.icon_filename, + 'transmission:index', parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-transmission', name, - short_description=short_description, - icon=icon_filename, url='/transmission', - clients=clients, login_required=True, - allowed_groups=[group[0]]) + shortcut = frontpage.Shortcut( + 'shortcut-transmission', info.name, + short_description=info.short_description, icon=info.icon_filename, + url='/transmission', clients=info.clients, login_required=True, + allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-transmission', name, + firewall = Firewall('firewall-transmission', info.name, ports=['http', 'https'], is_external=True) self.add(firewall) diff --git a/plinth/modules/transmission/views.py b/plinth/modules/transmission/views.py index ff768aa88..c1a5aab43 100644 --- a/plinth/modules/transmission/views.py +++ b/plinth/modules/transmission/views.py @@ -27,7 +27,6 @@ from django.contrib import messages from django.utils.translation import ugettext as _ from plinth import actions, views -from plinth.modules import transmission from .forms import TransmissionForm @@ -36,13 +35,8 @@ logger = logging.getLogger(__name__) class TransmissionAppView(views.AppView): """Serve configuration page.""" - clients = transmission.clients - name = transmission.name - description = transmission.description form_class = TransmissionForm app_id = 'transmission' - manual_page = transmission.manual_page - icon_filename = transmission.icon_filename def get_initial(self): """Get the current settings from Transmission server.""" diff --git a/plinth/modules/ttrss/__init__.py b/plinth/modules/ttrss/__init__.py index 14caf41d5..8c695803f 100644 --- a/plinth/modules/ttrss/__init__.py +++ b/plinth/modules/ttrss/__init__.py @@ -40,13 +40,7 @@ managed_packages = [ 'tt-rss', 'postgresql', 'dbconfig-pgsql', 'php-pgsql', 'python3-psycopg2' ] -name = _('Tiny Tiny RSS') - -icon_filename = 'ttrss' - -short_description = _('News Feed Reader') - -description = [ +_description = [ _('Tiny Tiny RSS is a news feed (RSS/Atom) reader and aggregator, ' 'designed to allow reading news from any location, while feeling as ' 'close to a real desktop application as possible.'), @@ -60,12 +54,8 @@ description = [ '/tt-rss-app for connecting.')) ] -clients = clients - group = ('feed-reader', _('Read and subscribe to news feeds')) -manual_page = 'TinyTinyRSS' - app = None @@ -77,19 +67,28 @@ class TTRSSApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-ttrss', name, short_description, 'ttrss', - 'ttrss:index', parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('Tiny Tiny RSS'), icon_filename='ttrss', + short_description=_('News Feed Reader'), + description=_description, + manual_page='TinyTinyRSS', clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-ttrss', info.name, info.short_description, + info.icon_filename, 'ttrss:index', + parent_url_name='apps') self.add(menu_item) - shortcut = frontpage.Shortcut('shortcut-ttrss', name, - short_description=short_description, - icon=icon_filename, url='/tt-rss', - clients=clients, login_required=True, + shortcut = frontpage.Shortcut('shortcut-ttrss', info.name, + short_description=info.short_description, + icon=info.icon_filename, url='/tt-rss', + clients=info.clients, + login_required=True, allowed_groups=[group[0]]) self.add(shortcut) - firewall = Firewall('firewall-ttrss', name, ports=['http', 'https'], - is_external=True) + firewall = Firewall('firewall-ttrss', info.name, + ports=['http', 'https'], is_external=True) self.add(firewall) webserver = Webserver('webserver-ttrss', 'tt-rss-plinth', diff --git a/plinth/modules/ttrss/urls.py b/plinth/modules/ttrss/urls.py index 04f9f33a6..b9e44659b 100644 --- a/plinth/modules/ttrss/urls.py +++ b/plinth/modules/ttrss/urls.py @@ -20,15 +20,9 @@ URLs for the Tiny Tiny RSS module. from django.conf.urls import url -from plinth.modules import ttrss from plinth.views import AppView urlpatterns = [ - url( - r'^apps/ttrss/$', - AppView.as_view(app_id='ttrss', name=ttrss.name, - description=ttrss.description, clients=ttrss.clients, - icon_filename=ttrss.icon_filename, - manual_page=ttrss.manual_page, show_status_block=True), - name='index'), + url(r'^apps/ttrss/$', + AppView.as_view(app_id='ttrss', show_status_block=True), name='index'), ] diff --git a/plinth/modules/upgrades/__init__.py b/plinth/modules/upgrades/__init__.py index 4bf593a10..01ed0fa6f 100644 --- a/plinth/modules/upgrades/__init__.py +++ b/plinth/modules/upgrades/__init__.py @@ -34,14 +34,10 @@ is_essential = True managed_packages = ['unattended-upgrades'] -name = _('Update') - -description = [ +_description = [ _('Check for and apply the latest software and security updates.') ] -manual_page = 'Upgrades' - app = None @@ -53,7 +49,13 @@ class UpgradesApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-upgrades', name, None, 'fa-refresh', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, name=_('Update'), + icon='fa-refresh', description=_description, + manual_page='Upgrades') + self.add(info) + + menu_item = menu.Menu('menu-upgrades', info.name, None, info.icon, 'upgrades:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/upgrades/views.py b/plinth/modules/upgrades/views.py index 0b3b7e48e..bfd4a6f06 100644 --- a/plinth/modules/upgrades/views.py +++ b/plinth/modules/upgrades/views.py @@ -38,9 +38,6 @@ class UpgradesConfigurationView(AppView): success_url = reverse_lazy('upgrades:index') template_name = "upgrades_configure.html" app_id = 'upgrades' - name = upgrades.name - description = upgrades.description - manual_page = upgrades.manual_page show_status_block = False def get_initial(self): diff --git a/plinth/modules/users/__init__.py b/plinth/modules/users/__init__.py index c27378769..60535823b 100644 --- a/plinth/modules/users/__init__.py +++ b/plinth/modules/users/__init__.py @@ -48,9 +48,7 @@ first_boot_steps = [ }, ] -name = _('Users and Groups') - -description = [ +_description = [ _('Create and managed user accounts. These accounts serve as centralized ' 'authentication mechanism for most apps. Some apps further require a ' 'user account to be part of a group to authorize the user to access the ' @@ -62,8 +60,6 @@ description = [ box_name=_(cfg.box_name)) ] -manual_page = 'Users' - # All FreedomBox user groups groups = dict() @@ -78,7 +74,13 @@ class UsersApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-users', name, None, 'fa-users', + info = app_module.Info(app_id=self.app_id, version=version, + is_essential=is_essential, + name=_('Users and Groups'), icon='fa-users', + description=_description, manual_page='Users') + self.add(info) + + menu_item = menu.Menu('menu-users', info.name, None, info.icon, 'users:index', parent_url_name='system') self.add(menu_item) diff --git a/plinth/modules/users/views.py b/plinth/modules/users/views.py index 7168a7bb3..0b13e3a26 100644 --- a/plinth/modules/users/views.py +++ b/plinth/modules/users/views.py @@ -29,7 +29,7 @@ from django.views.generic.edit import (CreateView, DeleteView, FormView, from plinth import actions from plinth.errors import ActionError -from plinth.modules import first_boot, users +from plinth.modules import first_boot from plinth.utils import is_user_admin from plinth.views import AppView @@ -72,11 +72,8 @@ class UserList(AppView, ContextMixin, django.views.generic.ListView): model = User template_name = 'users_list.html' title = ugettext_lazy('Users') - name = users.name - description = users.description app_id = 'users' show_status_block = False - manual_page = users.manual_page def get_context_data(self, *args, **kwargs): context = super(UserList, self).get_context_data(*args, **kwargs) diff --git a/plinth/modules/wireguard/__init__.py b/plinth/modules/wireguard/__init__.py index b359865a5..3d9d97ae8 100644 --- a/plinth/modules/wireguard/__init__.py +++ b/plinth/modules/wireguard/__init__.py @@ -35,11 +35,7 @@ version = 1 managed_packages = ['wireguard'] -name = _('WireGuard') - -short_description = _('Virtual Private Network') - -description = [ +_description = [ _('WireGuard is a fast, modern, secure VPN tunnel.'), format_lazy( _('It can be used to connect to a VPN provider which supports ' @@ -52,8 +48,6 @@ description = [ box_name=_(cfg.box_name)) ] -clients = clients - port_forwarding_info = [('UDP', 51820)] app = None @@ -69,19 +63,26 @@ class WireguardApp(app_module.App): def __init__(self): """Create components for the app.""" super().__init__() - menu_item = menu.Menu('menu-wireguard', name, short_description, - 'wireguard', 'wireguard:index', - parent_url_name='apps') + info = app_module.Info(app_id=self.app_id, version=version, + name=_('WireGuard'), icon_filename='wireguard', + short_description=_('Virtual Private Network'), + description=_description, clients=clients) + self.add(info) + + menu_item = menu.Menu('menu-wireguard', info.name, + info.short_description, info.icon_filename, + 'wireguard:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( - 'shortcut-wireguard', name, short_description=short_description, - icon='wireguard', description=description, + 'shortcut-wireguard', info.name, + short_description=info.short_description, icon=info.icon_filename, + description=info.description, configure_url=reverse_lazy('wireguard:index'), login_required=True, - clients=clients) + clients=info.clients) self.add(shortcut) - firewall = Firewall('firewall-wireguard', name, + firewall = Firewall('firewall-wireguard', info.name, ports=['wireguard-freedombox'], is_external=True) self.add(firewall) diff --git a/plinth/modules/wireguard/views.py b/plinth/modules/wireguard/views.py index b02ae65f1..84dcd89a6 100644 --- a/plinth/modules/wireguard/views.py +++ b/plinth/modules/wireguard/views.py @@ -39,9 +39,6 @@ from . import forms, utils class WireguardView(AppView): """Serve configuration page.""" app_id = 'wireguard' - clients = wireguard.clients - name = wireguard.name - description = wireguard.description diagnostics_module_name = 'wireguard' show_status_block = False template_name = 'wireguard.html' diff --git a/plinth/templates/app.html b/plinth/templates/app.html index f658f62c0..9ca014028 100644 --- a/plinth/templates/app.html +++ b/plinth/templates/app.html @@ -27,7 +27,7 @@ {% block content %} - {% include "header.html" with icon_filename=icon_filename name=name description=description manual_page=manual_page %} + {% include "header.html" %} {% include "toolbar.html" with enabled=is_enabled %} @@ -41,7 +41,7 @@ {% if show_status_block %}

{% trans "Status" %}

- {% with service_name=name %} + {% with service_name=app_info.name %} {% if is_running %} {% blocktrans trimmed %} diff --git a/plinth/templates/header.html b/plinth/templates/header.html index 51ff130ac..f508dd92d 100644 --- a/plinth/templates/header.html +++ b/plinth/templates/header.html @@ -21,11 +21,11 @@ {% load i18n %} {% load static %} -

+
- {% if icon_filename %} + {% if app_info.icon_filename %} - {{ name  }} + {{ app_info.name  }} {% endif %} @@ -33,9 +33,9 @@
{% block pagetitle %} {% if setup %} -

{% trans "Installation" %}: {{ short_description|default:'' }} ({{ name }})

+

{% trans "Installation" %}: {{ app_info.short_description|default:'' }} ({{ app_info.name }})

{% else %} -

{{ name }}

+

{{ app_info.name }}

{% endif %} {% endblock %}
@@ -47,15 +47,15 @@
- {% block description %} - {% for paragraph in description %} + {% block app_info.description %} + {% for paragraph in app_info.description %}

{{ paragraph|safe }}

{% endfor %} {% endblock %} - {% if manual_page %} + {% if app_info.manual_page %}

- + {% trans 'Learn more...' %}

diff --git a/plinth/templates/setup.html b/plinth/templates/setup.html index 9de74ce09..57f00e143 100644 --- a/plinth/templates/setup.html +++ b/plinth/templates/setup.html @@ -36,9 +36,9 @@ {% block content %} - {% include "header.html" with icon_filename=setup_helper.module.icon_filename name=setup_helper.module.name description=setup_helper.module.description short_description=setup_helper.module.short_description manual_page=setup_helper.module.manual_page setup=True %} + {% include "header.html" with setup=True %} - {% include "toolbar.html" with clients=setup_helper.module.clients %} + {% include "toolbar.html" %} {% if setup_state == 'up-to-date' %} @@ -138,4 +138,4 @@ {% endif %} {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/plinth/templates/toolbar.html b/plinth/templates/toolbar.html index 79563c635..03bc0f3a3 100644 --- a/plinth/templates/toolbar.html +++ b/plinth/templates/toolbar.html @@ -27,8 +27,8 @@
diff --git a/plinth/views.py b/plinth/views.py index f54f2b2c8..8e4c7f7b7 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -31,8 +31,7 @@ from django.views.generic import TemplateView from django.views.generic.edit import FormView from stronghold.decorators import public -from plinth import package -from plinth.app import App +from plinth import app, package from plinth.daemon import app_is_running from plinth.modules.config import get_advanced_mode from plinth.translation import get_language_from_request, set_language @@ -111,10 +110,6 @@ class LanguageSelectionView(FormView): class AppView(FormView): """A generic view for configuring simple apps.""" - clients = [] - name = None - # List of paragraphs describing the service - description = "" form_class = forms.AppForm # Display the 'status' block of the app.html template # This block uses information from service.is_running. This method is @@ -122,18 +117,13 @@ class AppView(FormView): show_status_block = True app_id = None template_name = 'app.html' - manual_page = '' port_forwarding_info = None - icon_filename = '' def __init__(self, *args, **kwargs): """Initialize 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 @@ -144,7 +134,7 @@ class AppView(FormView): if not self.app_id: raise ImproperlyConfigured('Missing attribute: app_id') - return App.get(self.app_id) + return app.App.get(self.app_id) def _get_common_status(self): """Return the status needed for form and template. @@ -187,17 +177,13 @@ class AppView(FormView): """Add service to the context data.""" context = super().get_context_data(*args, **kwargs) context.update(self._get_common_status()) - context['app'] = self.app + context['app'] = self.app # XXX: Remove this for template security context['app_id'] = self.app.app_id context['is_running'] = app_is_running(self.app) - context['clients'] = self.clients - context['name'] = self.name - context['description'] = self.description + context['app_info'] = self.app.info context['has_diagnostics'] = self.app.has_diagnostics() context['show_status_block'] = self.show_status_block - context['manual_page'] = self.manual_page context['port_forwarding_info'] = self.port_forwarding_info - context['icon_filename'] = self.icon_filename from plinth.modules.firewall.components import Firewall context['firewall'] = self.app.get_components_of_type(Firewall) @@ -208,15 +194,13 @@ class AppView(FormView): class SetupView(TemplateView): """View to prompt and setup applications.""" template_name = 'setup.html' - name = 'None' - # List of paragraphs describing the service - description = [] def get_context_data(self, **kwargs): """Return the context data rendering the template.""" context = super(SetupView, self).get_context_data(**kwargs) setup_helper = self.kwargs['setup_helper'] context['setup_helper'] = setup_helper + context['app_info'] = setup_helper.module.app.info # Reuse the value of setup_state throughout the view for consistency. context['setup_state'] = setup_helper.get_state() @@ -227,9 +211,6 @@ class SetupView(TemplateView): context[ 'package_manager_is_busy'] = package.is_package_manager_busy() - context['name'] = self.name - context['description'] = self.description - return context def dispatch(self, request, *args, **kwargs):