From f419c285968a25e9f58b25bbc777d8c591b727d2 Mon Sep 17 00:00:00 2001 From: fonfon Date: Wed, 27 Apr 2016 10:41:40 +0000 Subject: [PATCH] Services: Explicitly use ServiceForm and ServiceView - adapted all modules to not use views.ConfigurationView anymore - removed templates that are not needed anymore - no more implicit 'enabled' and 'get_status' functions in __init__.py files - (more coherent/explicit use of Django functionality) --- actions/tor | 5 +- plinth/forms.py | 8 ++ plinth/modules/avahi/__init__.py | 16 +-- plinth/modules/avahi/templates/avahi.html | 49 ------- plinth/modules/avahi/urls.py | 4 +- plinth/modules/datetime/__init__.py | 21 --- plinth/modules/datetime/forms.py | 8 +- .../modules/datetime/templates/datetime.html | 33 +---- plinth/modules/datetime/urls.py | 5 +- plinth/modules/datetime/views.py | 33 +++-- plinth/modules/deluge/__init__.py | 34 ++--- plinth/modules/deluge/templates/deluge.html | 51 ------- plinth/modules/deluge/urls.py | 10 +- .../dynamicdns/templates/dynamicdns.html | 31 +++- plinth/modules/ikiwiki/__init__.py | 29 ++-- plinth/modules/ikiwiki/templates/ikiwiki.html | 39 ------ plinth/modules/ikiwiki/urls.py | 2 +- plinth/modules/ikiwiki/views.py | 18 ++- plinth/modules/minetest/__init__.py | 27 ++-- .../modules/minetest/templates/minetest.html | 52 ------- plinth/modules/minetest/urls.py | 5 +- plinth/modules/mumble/__init__.py | 29 ++-- plinth/modules/mumble/templates/mumble.html | 52 ------- plinth/modules/mumble/urls.py | 5 +- plinth/modules/openvpn/__init__.py | 8 +- plinth/modules/pagekite/__init__.py | 34 ----- .../templates/pagekite_introduction.html | 51 ++++++- plinth/modules/pagekite/views.py | 1 - plinth/modules/privoxy/__init__.py | 17 +-- plinth/modules/privoxy/templates/privoxy.html | 51 ------- plinth/modules/privoxy/urls.py | 5 +- plinth/modules/quassel/__init__.py | 31 ++-- plinth/modules/quassel/templates/quassel.html | 52 ------- plinth/modules/quassel/urls.py | 5 +- plinth/modules/radicale/__init__.py | 38 +++-- .../modules/radicale/templates/radicale.html | 52 ------- plinth/modules/radicale/urls.py | 5 +- plinth/modules/repro/__init__.py | 21 +-- plinth/modules/repro/templates/repro.html | 51 ------- plinth/modules/repro/urls.py | 5 +- plinth/modules/restore/__init__.py | 16 +-- plinth/modules/restore/urls.py | 9 +- plinth/modules/roundcube/__init__.py | 25 ++-- .../roundcube/templates/roundcube.html | 39 ------ plinth/modules/roundcube/urls.py | 11 +- plinth/modules/shaarli/__init__.py | 21 +-- plinth/modules/shaarli/templates/shaarli.html | 37 ----- plinth/modules/shaarli/urls.py | 10 +- plinth/modules/tor/__init__.py | 110 +-------------- plinth/modules/tor/utils.py | 132 ++++++++++++++++++ plinth/modules/tor/views.py | 9 +- plinth/modules/transmission/__init__.py | 38 ++--- plinth/modules/transmission/forms.py | 4 +- .../transmission/templates/transmission.html | 57 -------- plinth/modules/transmission/urls.py | 5 +- plinth/modules/transmission/views.py | 27 +++- plinth/modules/ttrss/__init__.py | 26 ++-- plinth/modules/ttrss/templates/ttrss.html | 52 ------- plinth/modules/ttrss/urls.py | 11 +- plinth/modules/upgrades/__init__.py | 24 ++-- plinth/modules/upgrades/urls.py | 4 +- plinth/modules/upgrades/views.py | 61 ++++---- plinth/modules/xmpp/__init__.py | 37 ++--- plinth/modules/xmpp/templates/xmpp.html | 35 +---- plinth/modules/xmpp/urls.py | 5 +- .../apache_service.html} | 23 +-- plinth/templates/service.html | 67 +++++++++ plinth/views.py | 65 +++++++++ 68 files changed, 704 insertions(+), 1249 deletions(-) delete mode 100644 plinth/modules/avahi/templates/avahi.html delete mode 100644 plinth/modules/deluge/templates/deluge.html delete mode 100644 plinth/modules/ikiwiki/templates/ikiwiki.html delete mode 100644 plinth/modules/minetest/templates/minetest.html delete mode 100644 plinth/modules/mumble/templates/mumble.html delete mode 100644 plinth/modules/privoxy/templates/privoxy.html delete mode 100644 plinth/modules/quassel/templates/quassel.html delete mode 100644 plinth/modules/radicale/templates/radicale.html delete mode 100644 plinth/modules/repro/templates/repro.html delete mode 100644 plinth/modules/roundcube/templates/roundcube.html delete mode 100644 plinth/modules/shaarli/templates/shaarli.html create mode 100644 plinth/modules/tor/utils.py delete mode 100644 plinth/modules/transmission/templates/transmission.html delete mode 100644 plinth/modules/ttrss/templates/ttrss.html rename plinth/{modules/restore/templates/restore.html => templates/apache_service.html} (70%) create mode 100644 plinth/templates/service.html diff --git a/actions/tor b/actions/tor index a421e30e5..3b8f48fb7 100755 --- a/actions/tor +++ b/actions/tor @@ -31,8 +31,9 @@ import socket import time from plinth import action_utils -from plinth.modules.tor import is_enabled, is_running, get_augeas, \ - get_real_apt_uri_path, iter_apt_uris, APT_TOR_PREFIX +from plinth.modules.tor import is_enabled, is_running, APT_TOR_PREFIX +from plinth.modules.tor.utils import get_real_apt_uri_path, iter_apt_uris, \ + get_augeas SERVICE_FILE = '/etc/firewalld/services/tor-{0}.xml' TOR_CONFIG = '/files/etc/tor/torrc' diff --git a/plinth/forms.py b/plinth/forms.py index f554bcfc8..896581975 100644 --- a/plinth/forms.py +++ b/plinth/forms.py @@ -23,8 +23,16 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +# TODO: remove this form once owncloud is removed (it's the last using it). class ConfigurationForm(forms.Form): """Generic configuration form for simple modules.""" enabled = forms.BooleanField( label=_('Enable application'), required=False) + + +class ServiceForm(forms.Form): + """Generic configuration form for a service.""" + is_enabled = forms.BooleanField( + label=_('Enable application'), + required=False) diff --git a/plinth/modules/avahi/__init__.py b/plinth/modules/avahi/__init__.py index bf68b952e..1954106fc 100644 --- a/plinth/modules/avahi/__init__.py +++ b/plinth/modules/avahi/__init__.py @@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import cfg from plinth import service as service_module from plinth.utils import format_lazy +from plinth.views import ServiceView # pylint: disable=C0103 @@ -66,15 +67,6 @@ def setup(helper, old_version=False): helper.install(['avahi-daemon']) -def get_status(): - """Get the current settings from server.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() +class AvahiServiceView(ServiceView): + service_id = managed_services[0] + description = description diff --git a/plinth/modules/avahi/templates/avahi.html b/plinth/modules/avahi/templates/avahi.html deleted file mode 100644 index 193a1ac07..000000000 --- a/plinth/modules/avahi/templates/avahi.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Service discovery server is running" %} - {% else %} - - {% trans "Service discovery server is not running" %} - {% endif %} -

- -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/avahi/urls.py b/plinth/modules/avahi/urls.py index 9a86c45f6..4e6032499 100644 --- a/plinth/modules/avahi/urls.py +++ b/plinth/modules/avahi/urls.py @@ -21,10 +21,10 @@ URLs for the service discovery module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.avahi import AvahiServiceView urlpatterns = [ - url(r'^sys/avahi/$', ConfigurationView.as_view(module_name='avahi'), + url(r'^sys/avahi/$', AvahiServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/datetime/__init__.py b/plinth/modules/datetime/__init__.py index 8ba219ab0..ad9ff12d0 100644 --- a/plinth/modules/datetime/__init__.py +++ b/plinth/modules/datetime/__init__.py @@ -60,27 +60,6 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings from server.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running(), - 'time_zone': get_current_time_zone()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() - - -def get_current_time_zone(): - """Get current time zone.""" - time_zone = open('/etc/timezone').read().rstrip() - return time_zone or 'none' - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/datetime/forms.py b/plinth/modules/datetime/forms.py index 794a4a56c..8558911b3 100644 --- a/plinth/modules/datetime/forms.py +++ b/plinth/modules/datetime/forms.py @@ -24,13 +24,11 @@ from django.utils.translation import ugettext_lazy as _ import glob import re +from plinth.forms import ServiceForm -class DateTimeForm(forms.Form): + +class DateTimeForm(ServiceForm): """Date/time configuration form.""" - enabled = forms.BooleanField( - label=_('Enable network time'), - required=False) - time_zone = forms.ChoiceField( label=_('Time Zone'), help_text=_('Set your time zone to get accurate timestamps. \ diff --git a/plinth/modules/datetime/templates/datetime.html b/plinth/modules/datetime/templates/datetime.html index d0a7aa407..2e8d24d04 100644 --- a/plinth/modules/datetime/templates/datetime.html +++ b/plinth/modules/datetime/templates/datetime.html @@ -1,4 +1,4 @@ -{% extends "app.html" %} +{% extends "service.html" %} {% comment %} # # This file is part of Plinth. @@ -18,33 +18,8 @@ # {% endcomment %} -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Network time server is running" %} - {% else %} - - {% trans "Network time server is not running" %} - {% endif %} +{% block diagnostics %} +

+ {% include "diagnostics_button.html" with module="datetime" %}

- {% include "diagnostics_button.html" with module="datetime" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- {% endblock %} diff --git a/plinth/modules/datetime/urls.py b/plinth/modules/datetime/urls.py index 641417844..c0d40cbde 100644 --- a/plinth/modules/datetime/urls.py +++ b/plinth/modules/datetime/urls.py @@ -21,10 +21,9 @@ URLs for the date and time module from django.conf.urls import url -from plinth.views import ConfigurationView +from .views import DateTimeServiceView urlpatterns = [ - url(r'^sys/datetime/$', ConfigurationView.as_view(module_name='datetime'), - name='index'), + url(r'^sys/datetime/$', DateTimeServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/datetime/views.py b/plinth/modules/datetime/views.py index 343b96b8e..4606e6cb7 100644 --- a/plinth/modules/datetime/views.py +++ b/plinth/modules/datetime/views.py @@ -25,31 +25,34 @@ import logging from .forms import DateTimeForm from plinth import actions -from plinth import views from plinth.modules import datetime +from plinth.views import ServiceView logger = logging.getLogger(__name__) -class ConfigurationView(views.ConfigurationView): - """Serve configuration page.""" +class DateTimeServiceView(ServiceView): + description = datetime.description form_class = DateTimeForm + service_id = datetime.managed_services[0] + template_name = "datetime.html" - def apply_changes(self, old_status, new_status): - """Apply the changes.""" - modified = False + def get_initial(self): + return {'is_enabled': self.service.is_enabled(), + 'is_running': self.service.is_running(), + 'time_zone': self.get_current_time_zone()} - if old_status['enabled'] != new_status['enabled']: - if new_status['enabled']: - datetime.service.enable() - else: - datetime.service.disable() - modified = True - messages.success(self.request, _('Configuration updated')) + def get_current_time_zone(self): + """Get current time zone.""" + time_zone = open('/etc/timezone').read().rstrip() + return time_zone or 'none' + + def form_valid(self, form): + old_status = form.initial + new_status = form.cleaned_data if old_status['time_zone'] != new_status['time_zone'] and \ new_status['time_zone'] != 'none': - modified = True try: actions.superuser_run( 'timezone-change', [new_status['time_zone']]) @@ -60,4 +63,4 @@ class ConfigurationView(views.ConfigurationView): else: messages.success(self.request, _('Time zone set')) - return modified + return super().form_valid(form) diff --git a/plinth/modules/deluge/__init__.py b/plinth/modules/deluge/__init__.py index ba313605d..53274a77c 100644 --- a/plinth/modules/deluge/__init__.py +++ b/plinth/modules/deluge/__init__.py @@ -20,7 +20,6 @@ Plinth module to configure a Deluge web client. """ from django.utils.translation import ugettext_lazy as _ -from functools import partial from plinth import actions from plinth import action_utils @@ -32,6 +31,10 @@ version = 1 depends = ['apps'] +service = None + +managed_services = ['deluge-web'] + title = _('BitTorrent Web Client (Deluge)') description = [ @@ -43,10 +46,6 @@ description = [ 'it immediately after enabling this service.') ] -service = None - -managed_services = ['deluge-web'] - def init(): """Initialize the Deluge module.""" @@ -56,7 +55,7 @@ def init(): global service service = service_module.Service( managed_services[0], title, ports=['http', 'https'], is_external=True, - is_enabled=is_enabled, enable=_enable, disable=_disable) + is_enabled=is_enabled, enable=enable, disable=disable) def setup(helper, old_version=None): @@ -66,23 +65,20 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - def is_enabled(): """Return whether the module is enabled.""" return (action_utils.webserver_is_enabled('deluge-plinth') and action_utils.service_is_enabled('deluge-web')) -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('deluge', [sub_command]) - service.notify_enabled(None, should_enable) +def enable(): + """Enable the module.""" + actions.superuser_run('deluge', ['enable']) + + +def disable(): + """Disable the module.""" + actions.superuser_run('deluge', ['disable']) def diagnose(): @@ -95,7 +91,3 @@ def diagnose(): 'https://{host}/deluge', extra_options=['--no-check-certificate'])) return results - - -_enable = partial(enable, True) -_disable = partial(enable, False) diff --git a/plinth/modules/deluge/templates/deluge.html b/plinth/modules/deluge/templates/deluge.html deleted file mode 100644 index 019c91b64..000000000 --- a/plinth/modules/deluge/templates/deluge.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "deluge-web is running" %} - {% else %} - - {% trans "deluge-web is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="deluge" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/deluge/urls.py b/plinth/modules/deluge/urls.py index 4aa3712fd..d6dd6ae40 100644 --- a/plinth/modules/deluge/urls.py +++ b/plinth/modules/deluge/urls.py @@ -21,10 +21,14 @@ URLs for the Deluge module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules import deluge +from plinth.views import ServiceView urlpatterns = [ - url(r'^apps/deluge/$', ConfigurationView.as_view(module_name='deluge'), - name='index'), + url(r'^apps/deluge/$', ServiceView.as_view( + description=deluge.description, + diagnostics_module_name="deluge", + service_id=deluge.managed_services[0] + ), name='index'), ] diff --git a/plinth/modules/dynamicdns/templates/dynamicdns.html b/plinth/modules/dynamicdns/templates/dynamicdns.html index 2e2455394..200d251bc 100644 --- a/plinth/modules/dynamicdns/templates/dynamicdns.html +++ b/plinth/modules/dynamicdns/templates/dynamicdns.html @@ -1,4 +1,4 @@ -{% extends "app.html" %} +{% extends "base.html" %} {% comment %} # # This file is part of Plinth. @@ -20,7 +20,33 @@ {% load i18n %} -{% block configuration %} +{% block content %} +

{{ title }}

+ + {% for paragraph in description %} +

{{ paragraph|safe }}

+ {% endfor %} + +

+ {% blocktrans trimmed %} + If your Internet provider changes your IP address periodically + (i.e. every 24h), it may be hard for others to find you on the + Internet. This will prevent others from finding services which are + provided by this {box_name}. + {% endblocktrans %} +

+

+ {% blocktrans trimmed %} + The solution is to assign a DNS name to your IP address and + update the DNS name every time your IP is changed by your + Internet provider. Dynamic DNS allows you to push your current + public IP address to a + + GnuDIP server. Afterwards, the server will assign your DNS name + to the new IP, and if someone from the Internet asks for your DNS + name, they will get a response with your current IP address. + {% endblocktrans %} +

{% blocktrans trimmed %} If you are looking for a free dynamic DNS account, you may find @@ -39,4 +65,5 @@ and TCP port 443 (HTTPS). {% endblocktrans %}

+ {% endblock %} diff --git a/plinth/modules/ikiwiki/__init__.py b/plinth/modules/ikiwiki/__init__.py index 588bb1ba3..f793503de 100644 --- a/plinth/modules/ikiwiki/__init__.py +++ b/plinth/modules/ikiwiki/__init__.py @@ -20,7 +20,6 @@ Plinth module to configure ikiwiki """ from django.utils.translation import ugettext_lazy as _ -from functools import partial from plinth import actions from plinth import action_utils @@ -32,6 +31,8 @@ version = 1 depends = ['apps'] +service = None + title = _('Wiki and Blog (ikiwiki)') description = [ @@ -39,8 +40,6 @@ description = [ 'from /ikiwiki.') ] -service = None - def init(): """Initialize the ikiwiki module.""" @@ -50,7 +49,7 @@ def init(): global service service = service_module.Service( 'ikiwiki', title, ports=['http', 'https'], is_external=True, - is_enabled=is_enabled, enable=_enable, disable=_disable) + is_enabled=is_enabled, enable=enable, disable=disable) def setup(helper, old_version=None): @@ -66,21 +65,19 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current setting.""" - return {'enabled': is_enabled()} - - def is_enabled(): """Return whether the module is enabled.""" return action_utils.webserver_is_enabled('ikiwiki-plinth') -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('ikiwiki', [sub_command]) - service.notify_enabled(None, should_enable) +def enable(): + """Enable the module.""" + actions.superuser_run('ikiwiki', ['enable']) + + +def disable(): + """Enable the module.""" + actions.superuser_run('ikiwiki', ['disable']) def diagnose(): @@ -91,7 +88,3 @@ def diagnose(): 'https://{host}/ikiwiki', extra_options=['--no-check-certificate'])) return results - - -_enable = partial(enable, True) -_disable = partial(enable, False) diff --git a/plinth/modules/ikiwiki/templates/ikiwiki.html b/plinth/modules/ikiwiki/templates/ikiwiki.html deleted file mode 100644 index e08738cf7..000000000 --- a/plinth/modules/ikiwiki/templates/ikiwiki.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - - {% include "diagnostics_button.html" with module="ikiwiki" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/ikiwiki/urls.py b/plinth/modules/ikiwiki/urls.py index 65854ae88..64ac25abd 100644 --- a/plinth/modules/ikiwiki/urls.py +++ b/plinth/modules/ikiwiki/urls.py @@ -26,7 +26,7 @@ from . import views urlpatterns = [ url(r'^apps/ikiwiki/$', - views.ConfigurationView.as_view(module_name='ikiwiki'), name='index'), + views.IkiwikiServiceView.as_view(), name='index'), url(r'^apps/ikiwiki/manage/$', views.manage, name='manage'), url(r'^apps/ikiwiki/(?P[\w.@+-]+)/delete/$', views.delete, name='delete'), diff --git a/plinth/modules/ikiwiki/views.py b/plinth/modules/ikiwiki/views.py index df806bdb6..0f1dffecb 100644 --- a/plinth/modules/ikiwiki/views.py +++ b/plinth/modules/ikiwiki/views.py @@ -25,9 +25,11 @@ from django.shortcuts import redirect from django.template.response import TemplateResponse from django.utils.translation import ugettext as _, ugettext_lazy -from .forms import IkiwikiCreateForm from plinth import actions from plinth import views +from plinth.modules import ikiwiki + +from .forms import IkiwikiCreateForm subsubmenu = [{'url': reverse_lazy('ikiwiki:index'), @@ -38,14 +40,18 @@ subsubmenu = [{'url': reverse_lazy('ikiwiki:index'), 'text': ugettext_lazy('Create')}] -class ConfigurationView(views.ConfigurationView): +class IkiwikiServiceView(views.ServiceView): """Serve configuration page.""" + service_id = "ikiwiki" + template_name = "apache_service.html" + diagnostics_module_name = "ikiwiki" + description = ikiwiki.description + def get_context_data(self, **kwargs): """Return the context data for rendering the template view.""" - if 'subsubmenu' not in kwargs: - kwargs['subsubmenu'] = subsubmenu - - return super().get_context_data(**kwargs) + context = super().get_context_data(**kwargs) + context['subsubmenu'] = subsubmenu + return context def manage(request): diff --git a/plinth/modules/minetest/__init__.py b/plinth/modules/minetest/__init__.py index d628f5b65..4eb04f273 100644 --- a/plinth/modules/minetest/__init__.py +++ b/plinth/modules/minetest/__init__.py @@ -25,12 +25,17 @@ from plinth import action_utils from plinth import cfg from plinth import service as service_module from plinth.utils import format_lazy +from plinth.views import ServiceView version = 1 depends = ['apps'] +service = None + +managed_services = ['minetest-server'] + title = _('Block Sandbox (Minetest)') description = [ @@ -42,11 +47,6 @@ description = [ 'is needed.'), box_name=_(cfg.box_name)), ] -service = None - -managed_services = ['minetest-server'] - - def init(): """Initialize the module.""" menu = cfg.main_menu.get('apps:index') @@ -63,18 +63,11 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current service status.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() +class MinetestServiceView(ServiceView): + service_id = managed_services[0] + template_name = "apache_service.html" + diagnostics_module_name = "minetest" + description = description def diagnose(): diff --git a/plinth/modules/minetest/templates/minetest.html b/plinth/modules/minetest/templates/minetest.html deleted file mode 100644 index 327a91434..000000000 --- a/plinth/modules/minetest/templates/minetest.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Minetest server is running" %} - {% else %} - - {% trans "Minetest server is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="minetest" %} - - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/minetest/urls.py b/plinth/modules/minetest/urls.py index a32f5a7c5..2ffa4dc44 100644 --- a/plinth/modules/minetest/urls.py +++ b/plinth/modules/minetest/urls.py @@ -21,10 +21,9 @@ URLs for the minetest module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.minetest import MinetestServiceView urlpatterns = [ - url(r'^apps/minetest/$', ConfigurationView.as_view(module_name='minetest'), - name='index'), + url(r'^apps/minetest/$', MinetestServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/mumble/__init__.py b/plinth/modules/mumble/__init__.py index 426d2f11c..ff5ea1dba 100644 --- a/plinth/modules/mumble/__init__.py +++ b/plinth/modules/mumble/__init__.py @@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import action_utils from plinth import cfg from plinth import service as service_module +from plinth.views import ServiceView version = 1 @@ -32,6 +33,10 @@ depends = ['apps'] title = _('Voice Chat (Mumble)') +service = None + +managed_services = ['mumble-server'] + description = [ _('Mumble is an open source, low-latency, encrypted, high quality ' 'voice chat software.'), @@ -41,10 +46,6 @@ description = [ 'from your desktop and Android devices are available.') ] -service = None - -managed_services = ['mumble-server'] - def init(): """Intialize the Mumble module.""" @@ -56,26 +57,18 @@ def init(): managed_services[0], title, is_external=True) +class MumbleServiceView(ServiceView): + service_id = managed_services[0] + diagnostics_module_name = "mumble" + description = description + + def setup(helper, old_version=None): """Install and configure the module.""" helper.install(['mumble-server']) helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings from server.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/mumble/templates/mumble.html b/plinth/modules/mumble/templates/mumble.html deleted file mode 100644 index 96745dfd8..000000000 --- a/plinth/modules/mumble/templates/mumble.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Mumble server is running" %} - {% else %} - - {% trans "Mumble server is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="mumble" %} - - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/mumble/urls.py b/plinth/modules/mumble/urls.py index 77aa97da5..3abd8506d 100644 --- a/plinth/modules/mumble/urls.py +++ b/plinth/modules/mumble/urls.py @@ -21,10 +21,9 @@ URLs for the Mumble module from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.mumble import MumbleServiceView urlpatterns = [ - url(r'^apps/mumble/$', ConfigurationView.as_view(module_name='mumble'), - name='index'), + url(r'^apps/mumble/$', MumbleServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/openvpn/__init__.py b/plinth/modules/openvpn/__init__.py index 7e8a8e26e..fc1a16f6d 100644 --- a/plinth/modules/openvpn/__init__.py +++ b/plinth/modules/openvpn/__init__.py @@ -32,6 +32,10 @@ version = 1 depends = ['apps'] +service = None + +managed_services = ['openvpn@freedombox'] + title = _('Virtual Private Network (OpenVPN)') description = [ @@ -45,10 +49,6 @@ description = [ 'for added security and anonymity.'), box_name=_(cfg.box_name)) ] -service = None - -managed_services = ['openvpn@freedombox'] - def init(): """Intialize the OpenVPN module.""" diff --git a/plinth/modules/pagekite/__init__.py b/plinth/modules/pagekite/__init__.py index 6862c1b68..c08ce4f44 100644 --- a/plinth/modules/pagekite/__init__.py +++ b/plinth/modules/pagekite/__init__.py @@ -21,7 +21,6 @@ Plinth module to configure PageKite from django.utils.translation import ugettext_lazy as _ from plinth import cfg -from plinth.utils import format_lazy from . import utils @@ -31,39 +30,6 @@ depends = ['apps', 'names'] title = _('Public Visibility (PageKite)') -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 ' - 'need this if your {box_name} services are unreachable from ' - 'the rest of the Internet. This includes the following ' - 'situations:'), box_name=_(cfg.box_name)), - - format_lazy( - _('{box_name} is behind a restricted firewall.'), - box_name=_(cfg.box_name)), - - format_lazy( - _('{box_name} is connected to a (wireless) router which you ' - 'don\'t control.'), box_name=_(cfg.box_name)), - - _('Your ISP does not provide you an external IP address and ' - 'instead provides Internet connection through NAT.'), - - _('Your ISP does not provide you a static IP address and your IP ' - 'address changes evertime you connect to Internet.'), - - _('Your ISP limits incoming connections.'), - - format_lazy( - _('PageKite works around NAT, firewalls and IP-address limitations ' - 'by using a combination of tunnels and reverse proxies. You can ' - 'use any pagekite service provider, for example ' - 'pagekite.net. In future it ' - 'might be possible to use your buddy\'s {box_name} for this.'), - box_name=_(cfg.box_name)) -] - def init(): """Intialize the PageKite module""" diff --git a/plinth/modules/pagekite/templates/pagekite_introduction.html b/plinth/modules/pagekite/templates/pagekite_introduction.html index 0961c1923..2f21aadf1 100644 --- a/plinth/modules/pagekite/templates/pagekite_introduction.html +++ b/plinth/modules/pagekite/templates/pagekite_introduction.html @@ -1,4 +1,4 @@ -{% extends "app.html" %} +{% extends "base.html" %} {% comment %} # # This file is part of Plinth. @@ -20,7 +20,54 @@ {% load i18n %} -{% block configuration %} +{% block content %} +

{{ title }}

+ +

+ {% blocktrans trimmed %} + PageKite is a system for exposing {box_name} services when + you don\'t have a direct connection to the Internet. You only + need this if your {{ box_name }} services are unreachable from + the rest of the Internet. This includes the following + situations: + {% endblocktrans %} +

+
    +
  • + {{ box_name }} {% trans "is behind a restricted firewall." %} +
  • +
  • + {{ box_name }} + {% blocktrans trimmed %} + is connected to a (wireless) router which you don't control. + {% endblocktrans %} +
  • +
  • + {% blocktrans trimmed %} + Your ISP does not provide you an external IP address and + instead provides Internet connection through NAT. + {% endblocktrans %} +
  • +
  • + {% blocktrans trimmed %} + Your ISP does not provide you a static IP address and your IP + address changes evertime you connect to Internet. + {% endblocktrans %} +
  • +
  • + Your ISP limits incoming connections. +
  • +
+ +

+ {% blocktrans trimmed %} + PageKite works around NAT, firewalls and IP-address limitations + by using a combination of tunnels and reverse proxies. You can + use any pagekite service provider, for example + pagekite.net. In future it + might be possible to use your buddy's {{ box_name }} for this. + {% endblocktrans %} +

diff --git a/plinth/modules/pagekite/views.py b/plinth/modules/pagekite/views.py index 583f072dc..4c170bf55 100644 --- a/plinth/modules/pagekite/views.py +++ b/plinth/modules/pagekite/views.py @@ -42,7 +42,6 @@ def index(request): """Serve introduction page""" return TemplateResponse(request, 'pagekite_introduction.html', {'title': pagekite.title, - 'description': pagekite.description, 'subsubmenu': subsubmenu}) diff --git a/plinth/modules/privoxy/__init__.py b/plinth/modules/privoxy/__init__.py index 3a4e47734..c1335cb1f 100644 --- a/plinth/modules/privoxy/__init__.py +++ b/plinth/modules/privoxy/__init__.py @@ -26,6 +26,7 @@ from plinth import action_utils from plinth import cfg from plinth import service as service_module from plinth.utils import format_lazy +from plinth.views import ServiceView version = 1 @@ -73,18 +74,10 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings from server.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() +class PrivoxyServiceView(ServiceView): + service_id = managed_services[0] + diagnostics_module_name = 'privoxy' + description = description def diagnose(): diff --git a/plinth/modules/privoxy/templates/privoxy.html b/plinth/modules/privoxy/templates/privoxy.html deleted file mode 100644 index d80099eb4..000000000 --- a/plinth/modules/privoxy/templates/privoxy.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Privoxy is running" %} - {% else %} - - {% trans "Privoxy is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="privoxy" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/privoxy/urls.py b/plinth/modules/privoxy/urls.py index a4a60ca39..7341c3c42 100644 --- a/plinth/modules/privoxy/urls.py +++ b/plinth/modules/privoxy/urls.py @@ -21,10 +21,9 @@ URLs for the Privoxy module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.privoxy import PrivoxyServiceView urlpatterns = [ - url(r'^apps/privoxy/$', ConfigurationView.as_view(module_name='privoxy'), - name='index'), + url(r'^apps/privoxy/$', PrivoxyServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/quassel/__init__.py b/plinth/modules/quassel/__init__.py index d01ffcb41..2b5ab8c78 100644 --- a/plinth/modules/quassel/__init__.py +++ b/plinth/modules/quassel/__init__.py @@ -25,11 +25,16 @@ from plinth import action_utils from plinth import cfg from plinth import service as service_module from plinth.utils import format_lazy +from plinth.views import ServiceView version = 1 depends = ['apps'] +service = None + +managed_services = ['quasselcore'] + title = _('IRC Client (Quassel)') description = [ @@ -49,10 +54,6 @@ description = [ 'are available.') ] -service = None - -managed_services = ['quasselcore'] - def init(): """Initialize the quassel module.""" @@ -61,7 +62,13 @@ def init(): global service service = service_module.Service( - managed_services[0], title, description=description, is_external=True) + managed_services[0], title, is_external=True) + + +class QuasselServiceView(ServiceView): + service_id = managed_services[0] + diagnostics_module_name = "quassel" + description = description def setup(helper, old_version=None): @@ -70,20 +77,6 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current service status.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/quassel/templates/quassel.html b/plinth/modules/quassel/templates/quassel.html deleted file mode 100644 index 227769463..000000000 --- a/plinth/modules/quassel/templates/quassel.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Quassel core service is running" %} - {% else %} - - {% trans "Quassel core service is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="quassel" %} - - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/quassel/urls.py b/plinth/modules/quassel/urls.py index 3fef76f7f..650129d66 100644 --- a/plinth/modules/quassel/urls.py +++ b/plinth/modules/quassel/urls.py @@ -21,10 +21,9 @@ URLs for the quassel module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.quassel import QuasselServiceView urlpatterns = [ - url(r'^apps/quassel/$', ConfigurationView.as_view(module_name='quassel'), - name='index'), + url(r'^apps/quassel/$', QuasselServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/radicale/__init__.py b/plinth/modules/radicale/__init__.py index 6f595eff3..f103aff19 100644 --- a/plinth/modules/radicale/__init__.py +++ b/plinth/modules/radicale/__init__.py @@ -19,8 +19,6 @@ Plinth module for radicale. """ -from functools import partial - from django.utils.translation import ugettext_lazy as _ from plinth import actions @@ -28,12 +26,17 @@ from plinth import action_utils from plinth import cfg from plinth import service as service_module from plinth.utils import format_lazy +from plinth.views import ServiceView version = 1 depends = ['apps'] +service = None + +managed_services = ['radicale'] + title = _('Calendar and Addressbook (Radicale)') description = [ @@ -46,10 +49,6 @@ description = [ 'login.'), box_name=_(cfg.box_name)), ] -service = None - -managed_services = ['radicale'] - def init(): """Initialize the radicale module.""" @@ -59,7 +58,13 @@ def init(): global service service = service_module.Service( managed_services[0], title, ports=['http', 'https'], is_external=True, - enable=_enable, disable=_disable) + enable=enable, disable=disable) + + +class RadicaleServiceView(ServiceView): + service_id = managed_services[0] + diagnostics_module_name = 'radicale' + description = description def setup(helper, old_version=None): @@ -69,17 +74,14 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current service status.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} +def enable(): + """Enable the module.""" + actions.superuser_run('radicale', ['enable']) -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('radicale', [sub_command]) - service.notify_enabled(None, should_enable) +def disable(): + """Disable the module.""" + actions.superuser_run('radicale', ['disable']) def diagnose(): @@ -92,7 +94,3 @@ def diagnose(): 'https://{host}/radicale', extra_options=['--no-check-certificate'])) return results - - -_enable = partial(enable, True) -_disable = partial(enable, False) diff --git a/plinth/modules/radicale/templates/radicale.html b/plinth/modules/radicale/templates/radicale.html deleted file mode 100644 index 088981c63..000000000 --- a/plinth/modules/radicale/templates/radicale.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Radicale service is running" %} - {% else %} - - {% trans "Radicale service is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="radicale" %} - - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/radicale/urls.py b/plinth/modules/radicale/urls.py index 55f492662..5a6c873fc 100644 --- a/plinth/modules/radicale/urls.py +++ b/plinth/modules/radicale/urls.py @@ -21,10 +21,9 @@ URLs for the radicale module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.radicale import RadicaleServiceView urlpatterns = [ - url(r'^apps/radicale/$', ConfigurationView.as_view(module_name='radicale'), - name='index'), + url(r'^apps/radicale/$', RadicaleServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/repro/__init__.py b/plinth/modules/repro/__init__.py index 2ce9faa73..85a25b99f 100644 --- a/plinth/modules/repro/__init__.py +++ b/plinth/modules/repro/__init__.py @@ -25,6 +25,7 @@ from plinth import actions from plinth import action_utils from plinth import cfg from plinth import service as service_module +from plinth.views import ServiceView version = 1 @@ -69,6 +70,12 @@ def init(): is_external=True) +class ReproServiceView(ServiceView): + service_id = managed_services[0] + diagnostics_module_name = "repro" + description = description + + def setup(helper, old_version=None): """Install and configure the module.""" helper.install(['repro']) @@ -76,20 +83,6 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current service status.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/repro/templates/repro.html b/plinth/modules/repro/templates/repro.html deleted file mode 100644 index df5109cb0..000000000 --- a/plinth/modules/repro/templates/repro.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "repro service is running" %} - {% else %} - - {% trans "repro service is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="repro" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/repro/urls.py b/plinth/modules/repro/urls.py index aeff7f51a..5ca3a301a 100644 --- a/plinth/modules/repro/urls.py +++ b/plinth/modules/repro/urls.py @@ -21,10 +21,9 @@ URLs for the repro module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.repro import ReproServiceView urlpatterns = [ - url(r'^apps/repro/$', ConfigurationView.as_view(module_name='repro'), - name='index'), + url(r'^apps/repro/$', ReproServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/restore/__init__.py b/plinth/modules/restore/__init__.py index 0ec2a20b3..0ce855940 100644 --- a/plinth/modules/restore/__init__.py +++ b/plinth/modules/restore/__init__.py @@ -57,23 +57,9 @@ def init(): global service service = service_module.Service( - managed_services[0], title, ports=['http', 'https'], is_external=False, - description=description) + managed_services[0], title, ports=['http', 'https'], is_external=False) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(['node-restore']) - - -def get_status(): - """Get the current settings.""" - return {'enabled': service.is_enabled()} - - -def enable(should_enable): - """Enable/disable the module.""" - if should_enable: - service.enable() - else: - service.disable() diff --git a/plinth/modules/restore/urls.py b/plinth/modules/restore/urls.py index c51116f8f..e68aa4662 100644 --- a/plinth/modules/restore/urls.py +++ b/plinth/modules/restore/urls.py @@ -21,10 +21,13 @@ URLs for the reStore module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.views import ServiceView +from plinth.modules import restore urlpatterns = [ - url(r'^apps/restore/$', ConfigurationView.as_view(module_name='restore'), - name='index'), + url(r'^apps/restore/$', ServiceView.as_view( + service_id=restore.managed_services[0], + description=restore.description + ), name='index'), ] diff --git a/plinth/modules/roundcube/__init__.py b/plinth/modules/roundcube/__init__.py index 9e86c6821..58e2fda28 100644 --- a/plinth/modules/roundcube/__init__.py +++ b/plinth/modules/roundcube/__init__.py @@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth import action_utils from plinth import cfg +from plinth import service as service_module version = 1 @@ -54,12 +55,19 @@ description = [ '>https://www.google.com/settings/security/lesssecureapps
).'), ] +service = None + def init(): """Intialize the module.""" menu = cfg.main_menu.get('apps:index') menu.add_urlname(title, 'glyphicon-envelope', 'roundcube:index', 600) + global service + service = service_module.Service( + 'roundcube', title, ports=['http', 'https'], is_external=True, + is_enabled=is_enabled, enable=enable, disable=disable) + def setup(helper, old_version=None): """Install and configure the module.""" @@ -68,20 +76,19 @@ def setup(helper, old_version=None): helper.call('post', actions.superuser_run, 'roundcube', ['setup']) -def get_status(): - """Get the current status.""" - return {'enabled': is_enabled()} - - def is_enabled(): """Return whether the module is enabled.""" return action_utils.webserver_is_enabled('roundcube') -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('roundcube', [sub_command]) +def enable(): + """Enable the module.""" + actions.superuser_run('roundcube', ['enable']) + + +def disable(): + """Enable the module.""" + actions.superuser_run('roundcube', ['disable']) def diagnose(): diff --git a/plinth/modules/roundcube/templates/roundcube.html b/plinth/modules/roundcube/templates/roundcube.html deleted file mode 100644 index 6028f7aba..000000000 --- a/plinth/modules/roundcube/templates/roundcube.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - - {% include "diagnostics_button.html" with module="roundcube" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/roundcube/urls.py b/plinth/modules/roundcube/urls.py index a307cce07..be4edd836 100644 --- a/plinth/modules/roundcube/urls.py +++ b/plinth/modules/roundcube/urls.py @@ -21,10 +21,15 @@ URLs for the Roundcube module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.views import ServiceView +from plinth.modules import roundcube urlpatterns = [ - url(r'^apps/roundcube/$', - ConfigurationView.as_view(module_name='roundcube'), name='index'), + url(r'^apps/roundcube/$', ServiceView.as_view( + service_id="roundcube", + diagnostics_module_name="roundcube", + description=roundcube.description, + template_name="apache_service.html" + ), name='index'), ] diff --git a/plinth/modules/shaarli/__init__.py b/plinth/modules/shaarli/__init__.py index febc84776..0fdcecfa6 100644 --- a/plinth/modules/shaarli/__init__.py +++ b/plinth/modules/shaarli/__init__.py @@ -19,8 +19,6 @@ Plinth module to configure Shaarli. """ -from functools import partial - from django.utils.translation import ugettext_lazy as _ from plinth import actions @@ -55,7 +53,7 @@ def init(): global service service = service_module.Service( 'shaarli', title, ports=['http', 'https'], is_external=True, - is_enabled=is_enabled, enable=_enable, disable=_disable) + is_enabled=is_enabled, enable=enable, disable=disable) def setup(helper, old_version=None): @@ -64,22 +62,17 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings.""" - return {'enabled': service.is_enabled()} - def is_enabled(): """Return whether the module is enabled.""" return action_utils.webserver_is_enabled('shaarli') -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('shaarli', [sub_command]) - service.notify_enabled(None, should_enable) +def enable(): + """Enable the module.""" + actions.superuser_run('shaarli', ['enable']) -_enable = partial(enable, True) -_disable = partial(enable, False) +def disable(): + """Enable the module.""" + actions.superuser_run('shaarli', ['disable']) diff --git a/plinth/modules/shaarli/templates/shaarli.html b/plinth/modules/shaarli/templates/shaarli.html deleted file mode 100644 index 9a6c55b0a..000000000 --- a/plinth/modules/shaarli/templates/shaarli.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/shaarli/urls.py b/plinth/modules/shaarli/urls.py index b75250a3d..236c00ee8 100644 --- a/plinth/modules/shaarli/urls.py +++ b/plinth/modules/shaarli/urls.py @@ -21,10 +21,14 @@ URLs for the Shaarli module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.views import ServiceView +from plinth.modules import shaarli urlpatterns = [ - url(r'^apps/shaarli/$', ConfigurationView.as_view(module_name='shaarli'), - name='index'), + url(r'^apps/shaarli/$', ServiceView.as_view( + service_id="shaarli", + description=shaarli.description, + template_name="apache_service.html" + ), name='index'), ] diff --git a/plinth/modules/tor/__init__.py b/plinth/modules/tor/__init__.py index 71bef6a0b..78fcd7b68 100644 --- a/plinth/modules/tor/__init__.py +++ b/plinth/modules/tor/__init__.py @@ -19,10 +19,7 @@ Plinth module to configure Tor. """ -import augeas from django.utils.translation import ugettext_lazy as _ -import glob -import itertools import json from plinth import actions @@ -32,6 +29,8 @@ from plinth import service as service_module from plinth.modules.names import SERVICES from plinth.signals import domain_added, domain_removed +from . import utils as tor_utils + version = 1 @@ -73,7 +72,7 @@ def init(): is_external=True, is_enabled=is_enabled, is_running=is_running) # Register hidden service name with Name Services module. - hs_info = get_hs() + hs_info = tor_utils.get_hs() hostname = hs_info['hostname'] hs_virtports = [port['virtport'] for port in hs_info['ports']] @@ -108,7 +107,7 @@ def setup(helper, old_version=None): def update_hidden_service_domain(status=None): """Update HS domain with Name Services module.""" if not status: - status = get_status() + status = tor_utils.get_status() domain_removed.send_robust( sender='tor', domain_type='hiddenservice') @@ -131,107 +130,6 @@ def is_running(): return action_utils.service_is_running('tor') -def get_status(): - """Return current Tor status.""" - output = actions.superuser_run('tor', ['get-ports']) - ports = json.loads(output) - - hs_info = get_hs() - hs_services = [] - hs_virtports = [port['virtport'] for port in hs_info['ports']] - for service_type in SERVICES: - if str(service_type[2]) in hs_virtports: - hs_services.append(service_type[0]) - - return {'enabled': is_enabled(), - 'is_running': is_running(), - 'ports': ports, - 'hs_enabled': hs_info['enabled'], - 'hs_status': hs_info['status'], - 'hs_hostname': hs_info['hostname'], - 'hs_ports': hs_info['ports'], - 'hs_services': hs_services, - 'apt_transport_tor_enabled': is_apt_transport_tor_enabled()} - - -def get_hs(): - """Return hidden service status.""" - output = actions.superuser_run('tor', ['get-hs']) - return json.loads(output) - - -def get_augeas(): - """Return an instance of Augeaus for processing APT configuration.""" - aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + - augeas.Augeas.NO_MODL_AUTOLOAD) - aug.set('/augeas/load/Aptsources/lens', 'Aptsources.lns') - aug.set('/augeas/load/Aptsources/incl[last() + 1]', '/etc/apt/sources.list') - aug.set('/augeas/load/Aptsources/incl[last() + 1]', - '/etc/apt/sources.list.d/*.list') - aug.load() - - # Currently, augeas does not handle Deb822 format, it error out. - if aug.match('/augeas/files/etc/apt/sources.list/error') or \ - aug.match('/augeas/files/etc/apt/sources.list.d//error'): - raise Exception('Error parsing sources list') - - # Starting with Apt 1.1, /etc/apt/sources.list.d/*.sources will - # contain files with Deb822 format. If they are found, error out - # for now. XXX: Provide proper support Deb822 format with a new - # Augeas lens. - if glob.glob('/etc/apt/sources.list.d/*.sources'): - raise Exception('Can not handle Deb822 source files') - - return aug - - -def iter_apt_uris(aug): - """Iterate over all the APT source URIs.""" - return itertools.chain.from_iterable([aug.match(path) - for path in APT_SOURCES_URI_PATHS]) - - -def get_real_apt_uri_path(aug, path): - """Return the actual path which contains APT URL. - - XXX: This is a workaround for Augeas bug parsing Apt source files - with '[options]'. Remove this workaround after Augeas lens is - fixed. - """ - uri = aug.get(path) - if uri[0] == '[': - parent_path = path.rsplit('/', maxsplit=1)[0] - skipped = False - for child_path in aug.match(parent_path + '/*')[1:]: - if skipped: - return child_path - - value = aug.get(child_path) - if value[-1] == ']': - skipped = True - - return path - - -def is_apt_transport_tor_enabled(): - """Return whether APT is set to download packages over Tor.""" - try: - aug = get_augeas() - except Exception: - # If there was an error with parsing or there are Deb822 - # files. - return False - - for uri_path in iter_apt_uris(aug): - uri_path = get_real_apt_uri_path(aug, uri_path) - uri = aug.get(uri_path) - if not uri.startswith(APT_TOR_PREFIX) and \ - (uri.startswith('http://') or uri.startswith('https://')): - return False - - return True - - def diagnose(): """Run diagnostics and return the results.""" results = [] diff --git a/plinth/modules/tor/utils.py b/plinth/modules/tor/utils.py new file mode 100644 index 000000000..7f7e649b1 --- /dev/null +++ b/plinth/modules/tor/utils.py @@ -0,0 +1,132 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Tor utility functions +""" + +import augeas +import glob +import itertools +import json + +from plinth import actions +from plinth.modules import tor +from plinth.modules.names import SERVICES + + +def get_hs(): + """Return hidden service status.""" + output = actions.superuser_run('tor', ['get-hs']) + return json.loads(output) + + +def get_status(): + """Return current Tor status.""" + output = actions.superuser_run('tor', ['get-ports']) + ports = json.loads(output) + + hs_info = get_hs() + hs_services = [] + hs_virtports = [port['virtport'] for port in hs_info['ports']] + for service_type in SERVICES: + if str(service_type[2]) in hs_virtports: + hs_services.append(service_type[0]) + + return {'enabled': tor.is_enabled(), + 'is_running': tor.is_running(), + 'ports': ports, + 'hs_enabled': hs_info['enabled'], + 'hs_status': hs_info['status'], + 'hs_hostname': hs_info['hostname'], + 'hs_ports': hs_info['ports'], + 'hs_services': hs_services, + 'apt_transport_tor_enabled': \ + _is_apt_transport_tor_enabled() + } + + +def iter_apt_uris(aug): + """Iterate over all the APT source URIs.""" + return itertools.chain.from_iterable([aug.match(path) for \ + path in tor.APT_SOURCES_URI_PATHS]) + + +def get_real_apt_uri_path(aug, path): + """Return the actual path which contains APT URL. + + XXX: This is a workaround for Augeas bug parsing Apt source files + with '[options]'. Remove this workaround after Augeas lens is + fixed. + """ + uri = aug.get(path) + if uri[0] == '[': + parent_path = path.rsplit('/', maxsplit=1)[0] + skipped = False + for child_path in aug.match(parent_path + '/*')[1:]: + if skipped: + return child_path + + value = aug.get(child_path) + if value[-1] == ']': + skipped = True + + return path + + +def get_augeas(): + """Return an instance of Augeaus for processing APT configuration.""" + aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + + augeas.Augeas.NO_MODL_AUTOLOAD) + aug.set('/augeas/load/Aptsources/lens', 'Aptsources.lns') + aug.set('/augeas/load/Aptsources/incl[last() + 1]', '/etc/apt/sources.list') + aug.set('/augeas/load/Aptsources/incl[last() + 1]', + '/etc/apt/sources.list.d/*.list') + aug.load() + + # Currently, augeas does not handle Deb822 format, it error out. + if aug.match('/augeas/files/etc/apt/sources.list/error') or \ + aug.match('/augeas/files/etc/apt/sources.list.d//error'): + raise Exception('Error parsing sources list') + + # Starting with Apt 1.1, /etc/apt/sources.list.d/*.sources will + # contain files with Deb822 format. If they are found, error out + # for now. XXX: Provide proper support Deb822 format with a new + # Augeas lens. + if glob.glob('/etc/apt/sources.list.d/*.sources'): + raise Exception('Can not handle Deb822 source files') + + return aug + + +def _is_apt_transport_tor_enabled(): + """Return whether APT is set to download packages over Tor.""" + try: + aug = get_augeas() + except Exception: + # If there was an error with parsing or there are Deb822 + # files. + return False + + for uri_path in iter_apt_uris(aug): + uri_path = get_real_apt_uri_path(aug, uri_path) + uri = aug.get(uri_path) + if not uri.startswith(tor.APT_TOR_PREFIX) and \ + (uri.startswith('http://') or uri.startswith('https://')): + return False + + return True diff --git a/plinth/modules/tor/views.py b/plinth/modules/tor/views.py index fc9f42f2b..34bfdf1c3 100644 --- a/plinth/modules/tor/views.py +++ b/plinth/modules/tor/views.py @@ -18,16 +18,17 @@ """ Plinth module for configuring Tor. """ - from django.contrib import messages from django.template.response import TemplateResponse from django.utils.translation import ugettext_lazy as _ +from . import utils as tor_utils from .forms import TorForm from plinth import actions from plinth.errors import ActionError from plinth.modules import tor + config_process = None @@ -36,7 +37,7 @@ def index(request): if config_process: _collect_config_result(request) - status = tor.get_status() + status = tor_utils.get_status() form = None if request.method == 'POST': @@ -44,7 +45,7 @@ def index(request): # pylint: disable=E1101 if form.is_valid(): _apply_changes(request, status, form.cleaned_data) - status = tor.get_status() + status = tor_utils.get_status() form = TorForm(initial=status, prefix='tor') else: form = TorForm(initial=status, prefix='tor') @@ -109,7 +110,7 @@ def _collect_config_result(request): if return_code is None: return - status = tor.get_status() + status = tor_utils.get_status() tor.socks_service.notify_enabled(None, status['enabled']) tor.bridge_service.notify_enabled(None, status['enabled']) diff --git a/plinth/modules/transmission/__init__.py b/plinth/modules/transmission/__init__.py index 255063f21..c46210f2d 100644 --- a/plinth/modules/transmission/__init__.py +++ b/plinth/modules/transmission/__init__.py @@ -20,9 +20,7 @@ Plinth module to configure Transmission server """ from django.utils.translation import ugettext_lazy as _ -from functools import partial import json -import socket from plinth import actions from plinth import action_utils @@ -39,7 +37,8 @@ title = _('BitTorrent (Transmission)') description = [ _('BitTorrent is a peer-to-peer file sharing protocol. ' 'Transmission daemon handles Bitorrent file sharing. Note that ' - 'BitTorrent is not anonymous.') + 'BitTorrent is not anonymous.'), + _('Access the web interface at /transmission.') ] service = None @@ -57,8 +56,7 @@ def init(): global service service = service_module.Service( managed_services[0], title, ports=['http', 'https'], is_external=True, - is_enabled=is_enabled, enable=_enable, disable=_disable, - description=description) + is_enabled=is_enabled, enable=enable, disable=disable) def setup(helper, old_version=None): @@ -74,30 +72,20 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings from Transmission server.""" - configuration = open(TRANSMISSION_CONFIG, 'r').read() - status = json.loads(configuration) - status = {key.translate(str.maketrans({'-': '_'})): value - for key, value in status.items()} - status['enabled'] = is_enabled() - status['is_running'] = service.is_running() - status['hostname'] = socket.gethostname() - - return status - - def is_enabled(): """Return whether the module is enabled.""" return (action_utils.service_is_enabled('transmission-daemon') and action_utils.webserver_is_enabled('transmission-plinth')) -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('transmission', [sub_command]) - service.notify_enabled(None, should_enable) +def enable(): + """Enable the module.""" + actions.superuser_run('transmission', ['enable']) + + +def disable(): + """Enable the module.""" + actions.superuser_run('transmission', ['disable']) def diagnose(): @@ -111,7 +99,3 @@ def diagnose(): extra_options=['--no-check-certificate'])) return results - - -_enable = partial(enable, True) -_disable = partial(enable, False) diff --git a/plinth/modules/transmission/forms.py b/plinth/modules/transmission/forms.py index 6eaa42928..d9ade27bf 100644 --- a/plinth/modules/transmission/forms.py +++ b/plinth/modules/transmission/forms.py @@ -22,10 +22,10 @@ Plinth module for configuring Transmission. from django import forms from django.utils.translation import ugettext_lazy as _ -from plinth.forms import ConfigurationForm +from plinth.forms import ServiceForm -class TransmissionForm(ConfigurationForm): # pylint: disable=W0232 +class TransmissionForm(ServiceForm): # pylint: disable=W0232 """Transmission configuration form""" download_dir = forms.CharField( label=_('Download directory'), diff --git a/plinth/modules/transmission/templates/transmission.html b/plinth/modules/transmission/templates/transmission.html deleted file mode 100644 index 0adf4648f..000000000 --- a/plinth/modules/transmission/templates/transmission.html +++ /dev/null @@ -1,57 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

- {% blocktrans trimmed %} - Access the web interface at /transmission. - {% endblocktrans %} -

- -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Transmission daemon is running" %} - {% else %} - - {% trans "Transmission daemon is not running." %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="transmission" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/transmission/urls.py b/plinth/modules/transmission/urls.py index 1c0a7dddb..c7c046bc3 100644 --- a/plinth/modules/transmission/urls.py +++ b/plinth/modules/transmission/urls.py @@ -21,11 +21,10 @@ URLs for the Transmission module. from django.conf.urls import url -from .views import TransmissionConfigurationView +from .views import TransmissionServiceView urlpatterns = [ url(r'^apps/transmission/$', - TransmissionConfigurationView.as_view(module_name='transmission'), - name='index'), + TransmissionServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/transmission/views.py b/plinth/modules/transmission/views.py index 30dafc9f4..781c87e97 100644 --- a/plinth/modules/transmission/views.py +++ b/plinth/modules/transmission/views.py @@ -23,21 +23,39 @@ from django.contrib import messages from django.utils.translation import ugettext as _ import json import logging +import socket from .forms import TransmissionForm from plinth import actions from plinth import views +from plinth.modules import transmission logger = logging.getLogger(__name__) -class TransmissionConfigurationView(views.ConfigurationView): +class TransmissionServiceView(views.ServiceView): """Serve configuration page.""" + description = transmission.description + diagnostics_module_name = 'transmission' form_class = TransmissionForm + service_id = transmission.managed_services[0] - def apply_changes(self, old_status, new_status): + def get_initial(self): + """Get the current settings from Transmission server.""" + configuration = open(transmission.TRANSMISSION_CONFIG, 'r').read() + status = json.loads(configuration) + status = {key.translate(str.maketrans({'-': '_'})): value + for key, value in status.items()} + status['is_enabled'] = self.service.is_enabled() + status['is_running'] = self.service.is_running() + status['hostname'] = socket.gethostname() + + return status + + def form_valid(self, form): """Apply the changes submitted in the form.""" - modified = super().apply_changes(old_status, new_status) + old_status = form.initial + new_status = form.cleaned_data if old_status['download_dir'] != new_status['download_dir'] or \ old_status['rpc_username'] != new_status['rpc_username'] or \ @@ -51,6 +69,5 @@ class TransmissionConfigurationView(views.ConfigurationView): actions.superuser_run('transmission', ['merge-configuration'], input=json.dumps(new_configuration).encode()) messages.success(self.request, _('Configuration updated')) - return True - return modified + return super().form_valid(form) diff --git a/plinth/modules/ttrss/__init__.py b/plinth/modules/ttrss/__init__.py index 42d240e5d..ba163efc6 100644 --- a/plinth/modules/ttrss/__init__.py +++ b/plinth/modules/ttrss/__init__.py @@ -20,7 +20,6 @@ Plinth module to configure Tiny Tiny RSS. """ from django.utils.translation import ugettext_lazy as _ -from functools import partial from plinth import actions from plinth import action_utils @@ -56,7 +55,7 @@ def init(): global service service = service_module.Service( managed_services[0], title, ports=['http', 'https'], is_external=True, - is_enabled=is_enabled, enable=_enable, disable=_disable) + is_enabled=is_enabled, enable=enable, disable=disable) def setup(helper, old_version=None): @@ -67,23 +66,20 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings.""" - return {'enabled': is_enabled(), - 'is_running': service.is_running()} - - def is_enabled(): """Return whether the module is enabled.""" return (action_utils.service_is_enabled('tt-rss') and action_utils.webserver_is_enabled('tt-rss-plinth')) -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('ttrss', [sub_command]) - service.notify_enabled(None, should_enable) +def enable(): + """Enable the module.""" + actions.superuser_run('ttrss', ['enable']) + + +def disable(): + """Enable the module.""" + actions.superuser_run('ttrss', ['disable']) def diagnose(): @@ -94,7 +90,3 @@ def diagnose(): 'https://{host}/tt-rss', extra_options=['--no-check-certificate'])) return results - - -_enable = partial(enable, True) -_disable = partial(enable, False) diff --git a/plinth/modules/ttrss/templates/ttrss.html b/plinth/modules/ttrss/templates/ttrss.html deleted file mode 100644 index 4cd57ebac..000000000 --- a/plinth/modules/ttrss/templates/ttrss.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "app.html" %} -{% comment %} -# -# This file is part of Plinth. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -{% endcomment %} - -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "Tiny Tiny RSS feed update service is running" %} - {% else %} - - {% trans "Tiny Tiny RSS feed update service is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="ttrss" %} - - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- -{% endblock %} diff --git a/plinth/modules/ttrss/urls.py b/plinth/modules/ttrss/urls.py index d758af068..34413e569 100644 --- a/plinth/modules/ttrss/urls.py +++ b/plinth/modules/ttrss/urls.py @@ -21,10 +21,15 @@ URLs for the Tiny Tiny RSS module. from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.views import ServiceView +from plinth.modules import ttrss urlpatterns = [ - url(r'^apps/ttrss/$', ConfigurationView.as_view(module_name='ttrss'), - name='index'), + url(r'^apps/ttrss/$', ServiceView.as_view( + service_id=ttrss.managed_services[0], + diagnostics_module_name="ttrss", + description=ttrss.description, + template_name="apache_service.html" + ), name='index'), ] diff --git a/plinth/modules/upgrades/__init__.py b/plinth/modules/upgrades/__init__.py index e8e84ac38..afecac836 100644 --- a/plinth/modules/upgrades/__init__.py +++ b/plinth/modules/upgrades/__init__.py @@ -23,6 +23,7 @@ from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth import cfg +from plinth import service as service_module version = 1 @@ -39,11 +40,17 @@ description = [ 'night. You don\'t normally need to start the upgrade process.') ] +service = None + def init(): """Initialize the module.""" menu = cfg.main_menu.get('system:index') menu.add_urlname(title, 'glyphicon-refresh', 'upgrades:index', 21) + global service + service = service_module.Service( + 'auto-upgrades', title, is_external=False, is_enabled=is_enabled, + enable=enable, disable=disable) def setup(helper, old_version=None): @@ -52,18 +59,17 @@ def setup(helper, old_version=None): helper.call('post', actions.superuser_run, 'upgrades', ['enable-auto']) -def get_status(): - """Return the current status.""" - return {'auto_upgrades_enabled': 'is_enabled'} - - def is_enabled(): """Return whether the module is enabled.""" output = actions.run('upgrades', ['check-auto']) return 'True' in output.split() -def enable(should_enable): - """Enable/disable the module.""" - option = 'enable-auto' if should_enable else 'disable-auto' - actions.superuser_run('upgrades', [option]) +def enable(): + """Enable the module.""" + actions.superuser_run('upgrades', ['enable-auto']) + + +def disable(): + """Disable the module.""" + actions.superuser_run('upgrades', ['disable-auto']) diff --git a/plinth/modules/upgrades/urls.py b/plinth/modules/upgrades/urls.py index eba0e70b8..afc0576e0 100644 --- a/plinth/modules/upgrades/urls.py +++ b/plinth/modules/upgrades/urls.py @@ -25,7 +25,7 @@ from . import views urlpatterns = [ - url(r'^sys/upgrades/$', - views.ConfigurationView.as_view(module_name='upgrades'), name='index'), + url(r'^sys/upgrades/$', views.UpgradesConfigurationView.as_view(), + name='index'), url(r'^sys/upgrades/upgrade/$', views.upgrade, name='upgrade'), ] diff --git a/plinth/modules/upgrades/views.py b/plinth/modules/upgrades/views.py index 37b63bc82..857fb2070 100644 --- a/plinth/modules/upgrades/views.py +++ b/plinth/modules/upgrades/views.py @@ -23,11 +23,11 @@ from django.contrib import messages from django.core.urlresolvers import reverse_lazy from django.template.response import TemplateResponse from django.utils.translation import ugettext as _, ugettext_lazy +from django.views.generic.edit import FormView import subprocess from .forms import ConfigureForm from plinth import actions -from plinth import views from plinth.errors import ActionError from plinth.modules import upgrades @@ -40,43 +40,52 @@ LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log' LOCK_FILE = '/var/log/dpkg/lock' -class ConfigurationView(views.ConfigurationView): +class UpgradesConfigurationView(FormView): """Serve configuration page.""" form_class = ConfigureForm + success_url = reverse_lazy('upgrades:index') + template_name = "upgrades_configure.html" - def get_context_data(self, **kwargs): + def get_context_data(self, *args, **kwargs): """Return the context data for rendering the template view.""" - if 'subsubmenu' not in kwargs: - kwargs['subsubmenu'] = subsubmenu + context = super().get_context_data(*args, **kwargs) + context['subsubmenu'] = subsubmenu + context['title'] = upgrades.title + context['description'] = upgrades.description + return context - return super().get_context_data(**kwargs) + def get_initial(self): + return {'auto_upgrades_enabled': upgrades.service.is_enabled()} - def get_template_names(self): - """Return the list of template names for the view.""" - return ['upgrades_configure.html'] - - def apply_changes(self, old_status, new_status): + def form_valid(self, form): """Apply the form changes.""" + old_status = form.initial + new_status = form.cleaned_data + if old_status['auto_upgrades_enabled'] \ - == new_status['auto_upgrades_enabled']: - return False + != new_status['auto_upgrades_enabled']: - try: - upgrades.enable(new_status['auto_upgrades_enabled']) - except ActionError as exception: - error = exception.args[2] - messages.error( - self.request, - _('Error when configuring unattended-upgrades: {error}') - .format(error=error)) - return True + try: + if new_status['auto_upgrades_enabled']: + upgrades.service.enable() + else: + upgrades.service.disable() + except ActionError as exception: + error = exception.args[2] + messages.error( + self.request, + _('Error when configuring unattended-upgrades: {error}') + .format(error=error)) - if new_status['auto_upgrades_enabled']: - messages.success(self.request, _('Automatic upgrades enabled')) + if new_status['auto_upgrades_enabled']: + messages.success(self.request, _('Automatic upgrades enabled')) + else: + messages.success(self.request, _('Automatic upgrades disabled')) else: - messages.success(self.request, _('Automatic upgrades disabled')) + messages.info(self.request, _('Settings unchanged')) + + return super().form_valid(form) - return True def is_package_manager_busy(): diff --git a/plinth/modules/xmpp/__init__.py b/plinth/modules/xmpp/__init__.py index 8602b7146..dd97dbe76 100644 --- a/plinth/modules/xmpp/__init__.py +++ b/plinth/modules/xmpp/__init__.py @@ -20,7 +20,6 @@ Plinth module to configure XMPP server """ from django.utils.translation import ugettext_lazy as _ -from functools import partial import logging import socket @@ -28,6 +27,7 @@ from plinth import actions from plinth import action_utils from plinth import cfg from plinth import service as service_module +from plinth.views import ServiceView from plinth.signals import pre_hostname_change, post_hostname_change from plinth.signals import domainname_change @@ -63,8 +63,7 @@ def init(): global service service = service_module.Service( 'ejabberd', title, ports=['xmpp-client', 'xmpp-server', 'xmpp-bosh'], - is_external=True, is_enabled=is_enabled, enable=_enable, - disable=_disable) + is_external=True, is_enabled=is_enabled, enable=enable, disable=disable) pre_hostname_change.connect(on_pre_hostname_change) post_hostname_change.connect(on_post_hostname_change) @@ -83,11 +82,16 @@ def setup(helper, old_version=None): helper.call('post', service.notify_enabled, None, True) -def get_status(): - """Get the current settings.""" - return {'enabled': service.is_enabled(), - 'is_running': service.is_running(), - 'domainname': get_domainname()} +class EjabberdServiceView(ServiceView): + service_id = managed_services[0] + template_name = "xmpp.html" + description = description + diagnostics_module_name = "xmpp" + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context['domainname'] = get_domainname() + return context def is_enabled(): @@ -102,11 +106,14 @@ def get_domainname(): return '.'.join(fqdn.split('.')[1:]) -def enable(should_enable): - """Enable/disable the module.""" - sub_command = 'enable' if should_enable else 'disable' - actions.superuser_run('xmpp', [sub_command]) - service.notify_enabled(None, should_enable) +def enable(): + """Enable the module.""" + actions.superuser_run('xmpp', ['enable']) + + +def disable(): + """Enable the module.""" + actions.superuser_run('xmpp', ['disable']) def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs): @@ -163,7 +170,3 @@ def diagnose(): results.extend(action_utils.diagnose_url_on_all('http://{host}/jwchat')) return results - - -_enable = partial(enable, True) -_disable = partial(enable, False) diff --git a/plinth/modules/xmpp/templates/xmpp.html b/plinth/modules/xmpp/templates/xmpp.html index 31e387be2..702a883e7 100644 --- a/plinth/modules/xmpp/templates/xmpp.html +++ b/plinth/modules/xmpp/templates/xmpp.html @@ -1,4 +1,4 @@ -{% extends "app.html" %} +{% extends "service.html" %} {% comment %} # # This file is part of Plinth. @@ -18,10 +18,13 @@ # {% endcomment %} -{% load bootstrap %} {% load i18n %} -{% block configuration %} +{% block description %} + + {% for paragraph in description %} +

{{ paragraph|safe }}

+ {% endfor %}

{% url 'config:index' as index_url %} @@ -37,30 +40,4 @@ {% trans "Launch web client" %}

- -

{% trans "Status" %}

- -

- {% if status.is_running %} - - {% trans "ejabberd is running" %} - {% else %} - - {% trans "ejabberd is not running" %} - {% endif %} -

- - {% include "diagnostics_button.html" with module="xmpp" %} - -

{% trans "Configuration" %}

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
- {% endblock %} diff --git a/plinth/modules/xmpp/urls.py b/plinth/modules/xmpp/urls.py index 36e86a7c3..b3efc5252 100644 --- a/plinth/modules/xmpp/urls.py +++ b/plinth/modules/xmpp/urls.py @@ -21,10 +21,9 @@ URLs for the XMPP module from django.conf.urls import url -from plinth.views import ConfigurationView +from plinth.modules.xmpp import EjabberdServiceView urlpatterns = [ - url(r'^apps/xmpp/$', ConfigurationView.as_view(module_name='xmpp'), - name='index'), + url(r'^apps/xmpp/$', EjabberdServiceView.as_view(), name='index'), ] diff --git a/plinth/modules/restore/templates/restore.html b/plinth/templates/apache_service.html similarity index 70% rename from plinth/modules/restore/templates/restore.html rename to plinth/templates/apache_service.html index 4148a047c..991405822 100644 --- a/plinth/modules/restore/templates/restore.html +++ b/plinth/templates/apache_service.html @@ -1,4 +1,4 @@ -{% extends "app.html" %} +{% extends "service.html" %} {% comment %} # # This file is part of Plinth. @@ -18,20 +18,11 @@ # {% endcomment %} -{% load bootstrap %} -{% load i18n %} - -{% block configuration %} - -

Configuration

- -
- {% csrf_token %} - - {{ form|bootstrap }} - - -
+{% comment %} +# The status block uses is_running, but apache services by default don't +# provide this method. We could use is_enabled instead, but this information is +# available in the enable/disable checkbox already. So omit it altogether. +{% endcomment %} +{% block status %} {% endblock %} diff --git a/plinth/templates/service.html b/plinth/templates/service.html new file mode 100644 index 000000000..92940e757 --- /dev/null +++ b/plinth/templates/service.html @@ -0,0 +1,67 @@ +{% extends "base.html" %} +{% comment %} +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +{% endcomment %} + +{% load bootstrap %} +{% load i18n %} + +{% block content %} + {% block pagetitle %} +

{{ service.name }}

+ {% endblock %} + + {% block description %} + {% for paragraph in description %} +

{{ paragraph|safe }}

+ {% endfor %} + {% endblock %} + + {% block status %} +

Status

+

+ {% if service.is_running %} + + Service {{ service.name }} {% trans "is running" %} + {% else %} + + Service {{ service.name }} {% trans "is not running" %} + {% endif %} +

+ {% endblock %} + + {% block diagnostics %} + {% if diagnostics_module_name %} + {% include "diagnostics_button.html" with module=diagnostics_module_name %} + {% endif %} + {% endblock %} + + {% block configuration %} +

Configuration

+ +
+ {% csrf_token %} + + {{ form|bootstrap }} + + +
+ {% endblock %} + +{% endblock %} diff --git a/plinth/views.py b/plinth/views.py index 669506d0c..efcad2f9a 100644 --- a/plinth/views.py +++ b/plinth/views.py @@ -37,6 +37,71 @@ def index(request): return HttpResponseRedirect(reverse('apps:index')) +class ServiceView(FormView): + """A generic view for configuring simple modules.""" + service_id = None + form_class = forms.ServiceForm + template_name = 'service.html' + # Set diagnostics_module_name to the module name to show diagnostics button + diagnostics_module_name = "" + # List of paragraphs describing the service + description = "" + + @property + def success_url(self): + return self.request.path + + @property + def service(self): + if hasattr(self, '_service'): + return self._service + + if not self.service_id: + raise ImproperlyConfigured("missing attribute: 'service_id'") + service = plinth.service.services.get(self.service_id, None) + if service is None: + message = "Could not find service %s" % self.service_id + raise ImproperlyConfigured(message) + self._service = service + return service + + def get_initial(self): + """Return the status of the module to fill in the form.""" + return {'is_enabled': self.service.is_enabled(), + 'is_running': self.service.is_running()} + + def form_valid(self, form): + """Enable/disable a service and set messages.""" + old_status = form.initial + new_status = form.cleaned_data + + if old_status['is_enabled'] == new_status['is_enabled']: + # TODO: find a more reliable/official way to check whether the + # request has messages attached. + if not self.request._messages._queued_messages: + messages.info(self.request, _('Setting unchanged')) + else: + if new_status['is_enabled']: + self.service.enable() + messages.success(self.request, _('Application enabled')) + else: + self.service.disable() + messages.success(self.request, _('Application disabled')) + + return super().form_valid(form) + + def get_context_data(self, *args, **kwargs): + """Add service to the context data.""" + context = super().get_context_data(*args, **kwargs) + context['service'] = self.service + if self.diagnostics_module_name: + context['diagnostics_module_name'] = self.diagnostics_module_name + if self.description: + context['description'] = self.description + return context + + +# TODO: remove this view once owncloud is gone. class ConfigurationView(FormView): """A generic view for configuring simple modules.""" form_class = forms.ConfigurationForm