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)
This commit is contained in:
fonfon 2016-04-27 10:41:40 +00:00 committed by James Valleroy
parent a9528c56d9
commit f419c28596
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
68 changed files with 704 additions and 1249 deletions

View File

@ -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'

View File

@ -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)

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Service discovery server is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Service discovery server is not running" %}
{% endif %}
</p>
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 = []

View File

@ -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. \

View File

@ -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 %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Network time server is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Network time server is not running" %}
{% endif %}
{% block diagnostics %}
<p>
{% include "diagnostics_button.html" with module="datetime" %}
</p>
{% include "diagnostics_button.html" with module="datetime" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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)

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "deluge-web is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "deluge-web is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="deluge" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 %}
<h2>{{ title }}</h2>
{% for paragraph in description %}
<p>{{ paragraph|safe }}</p>
{% endfor %}
<p>
{% 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 %}
</p>
<p>
{% 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
<a href='http://gnudip2.sourceforge.net/' target='_blank'>
GnuDIP</a> 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 %}
</p>
<p>
{% 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 %}
</p>
{% endblock %}

View File

@ -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 <a href="/ikiwiki">/ikiwiki</a>.')
]
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)

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
{% include "diagnostics_button.html" with module="ikiwiki" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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<name>[\w.@+-]+)/delete/$', views.delete,
name='delete'),

View File

@ -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):

View File

@ -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():

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Minetest server is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Minetest server is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="minetest" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 = []

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Mumble server is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Mumble server is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="mumble" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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."""

View File

@ -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 '
'<a href="https://pagekite.net">pagekite.net</a>. 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"""

View File

@ -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 %}
<h2>{{ title }}</h2>
<p>
{% 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 %}
</p>
<ul>
<li>
{{ box_name }} {% trans "is behind a restricted firewall." %}
</li>
<li>
{{ box_name }}
{% blocktrans trimmed %}
is connected to a (wireless) router which you don't control.
{% endblocktrans %}
</li>
<li>
{% blocktrans trimmed %}
Your ISP does not provide you an external IP address and
instead provides Internet connection through NAT.
{% endblocktrans %}
</li>
<li>
{% blocktrans trimmed %}
Your ISP does not provide you a static IP address and your IP
address changes evertime you connect to Internet.
{% endblocktrans %}
</li>
<li>
Your ISP limits incoming connections.
</li>
</ul>
<p>
{% 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
<a href="https://pagekite.net">pagekite.net</a>. In future it
might be possible to use your buddy's {{ box_name }} for this.
{% endblocktrans %}
</p>
<p>
<a class="btn btn-primary btn-md" href="{% url 'pagekite:configure' %}">

View File

@ -42,7 +42,6 @@ def index(request):
"""Serve introduction page"""
return TemplateResponse(request, 'pagekite_introduction.html',
{'title': pagekite.title,
'description': pagekite.description,
'subsubmenu': subsubmenu})

View File

@ -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():

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Privoxy is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Privoxy is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="privoxy" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 = []

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Quassel core service is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Quassel core service is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="quassel" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Radicale service is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Radicale service is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="radicale" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 = []

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "repro service is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "repro service is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="repro" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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()

View File

@ -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'),
]

View File

@ -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</a>).'),
]
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():

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
{% include "diagnostics_button.html" with module="roundcube" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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'])

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 = []

132
plinth/modules/tor/utils.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#
"""
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

View File

@ -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'])

View File

@ -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 <a href="/transmission">/transmission</a>.')
]
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)

View File

@ -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'),

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<p>
{% blocktrans trimmed %}
Access the web interface at <a href="/transmission">/transmission</a>.
{% endblocktrans %}
</p>
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class='running-status active'></span>
{% trans "Transmission daemon is running" %}
{% else %}
<span class='running-status inactive'></span>
{% trans "Transmission daemon is not running." %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="transmission" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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)

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block configuration %}
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "Tiny Tiny RSS feed update service is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "Tiny Tiny RSS feed update service is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="ttrss" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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'])

View File

@ -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'),
]

View File

@ -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():

View File

@ -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)

View File

@ -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 %}
<p>{{ paragraph|safe }}</p>
{% endfor %}
<p>
{% url 'config:index' as index_url %}
@ -37,30 +40,4 @@
<a href='/jwchat' target='_blank' class='btn btn-primary'>
{% trans "Launch web client" %}</a>
</p>
<h3>{% trans "Status" %}</h3>
<p class="running-status-parent">
{% if status.is_running %}
<span class="running-status active"></span>
{% trans "ejabberd is running" %}
{% else %}
<span class="running-status inactive"></span>
{% trans "ejabberd is not running" %}
{% endif %}
</p>
{% include "diagnostics_button.html" with module="xmpp" %}
<h3>{% trans "Configuration" %}</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}

View File

@ -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'),
]

View File

@ -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 %}
<h3>Configuration</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% 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 %}

View File

@ -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 <http://www.gnu.org/licenses/>.
#
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% block content %}
{% block pagetitle %}
<h2>{{ service.name }}</h2>
{% endblock %}
{% block description %}
{% for paragraph in description %}
<p>{{ paragraph|safe }}</p>
{% endfor %}
{% endblock %}
{% block status %}
<h3>Status</h3>
<p class="running-status-parent">
{% if service.is_running %}
<span class="running-status active"></span>
Service <i>{{ service.name }}</i> {% trans "is running" %}
{% else %}
<span class="running-status inactive"></span>
Service <i>{{ service.name }}</i> {% trans "is not running" %}
{% endif %}
</p>
{% endblock %}
{% block diagnostics %}
{% if diagnostics_module_name %}
{% include "diagnostics_button.html" with module=diagnostics_module_name %}
{% endif %}
{% endblock %}
{% block configuration %}
<h3>Configuration</h3>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary"
value="{% trans "Update setup" %}"/>
</form>
{% endblock %}
{% endblock %}

View File

@ -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