diff --git a/plinth/modules/names/views.py b/plinth/modules/names/views.py
index c95ea0b00..d8daecd1f 100644
--- a/plinth/modules/names/views.py
+++ b/plinth/modules/names/views.py
@@ -24,6 +24,7 @@ from django.utils.translation import ugettext as _
from . import SERVICES, get_domain_types, get_description
from . import get_domain, get_services_status
+from plinth.modules import names
def index(request):
@@ -31,7 +32,7 @@ def index(request):
status = get_status()
return TemplateResponse(request, 'names.html',
- {'title': _('Name Services'),
+ {'title': names.title,
'status': status})
diff --git a/plinth/modules/networks/__init__.py b/plinth/modules/networks/__init__.py
index a3a679823..d72425a19 100644
--- a/plinth/modules/networks/__init__.py
+++ b/plinth/modules/networks/__init__.py
@@ -19,23 +19,37 @@
Plinth module to interface with network-manager
"""
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext_lazy as _
from logging import Logger
import subprocess
-from . import networks
-from .networks import init
from plinth import action_utils
+from plinth import cfg
from plinth import network
-__all__ = ['networks', 'init']
+version = 1
-depends = ['plinth.modules.system']
+is_essential = True
+
+depends = ['system']
+
+title = _('Networks')
logger = Logger(__name__)
+def init():
+ """Initialize the Networks module."""
+ menu = cfg.main_menu.get('system:index')
+ menu.add_urlname(title, 'glyphicon-signal', 'networks:index', 18)
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['network-manager'])
+
+
def diagnose():
"""Run diagnostics and return the results."""
results = []
@@ -44,8 +58,10 @@ def diagnose():
addresses = _get_interface_addresses(interfaces)
for address in addresses:
- results.append(action_utils.diagnose_port_listening(53, 'tcp', address))
- results.append(action_utils.diagnose_port_listening(53, 'udp', address))
+ results.append(
+ action_utils.diagnose_port_listening(53, 'tcp', address))
+ results.append(
+ action_utils.diagnose_port_listening(53, 'udp', address))
results.append(_diagnose_dnssec('4'))
results.append(_diagnose_dnssec('6'))
diff --git a/plinth/modules/networks/networks.py b/plinth/modules/networks/networks.py
index 88034ee9f..522b08aa1 100644
--- a/plinth/modules/networks/networks.py
+++ b/plinth/modules/networks/networks.py
@@ -25,9 +25,7 @@ from logging import Logger
from .forms import (ConnectionTypeSelectForm, EthernetForm, PPPoEForm,
WifiForm)
-from plinth import cfg
from plinth import network
-from plinth import package
logger = Logger(__name__)
@@ -40,14 +38,6 @@ subsubmenu = [{'url': reverse_lazy('networks:index'),
'text': ugettext_lazy('Add Connection')}]
-def init():
- """Initialize the Networks module."""
- menu = cfg.main_menu.get('system:index')
- menu.add_urlname(ugettext_lazy('Networks'), 'glyphicon-signal',
- 'networks:index', 18)
-
-
-@package.required(['network-manager'])
def index(request):
"""Show connection list."""
connections = network.get_connection_list()
diff --git a/plinth/modules/openvpn/__init__.py b/plinth/modules/openvpn/__init__.py
index 20be7aabe..c035abb99 100644
--- a/plinth/modules/openvpn/__init__.py
+++ b/plinth/modules/openvpn/__init__.py
@@ -25,9 +25,25 @@ from plinth import actions
from plinth import action_utils
from plinth import cfg
from plinth import service as service_module
+from plinth.utils import format_lazy
-depends = ['plinth.modules.apps']
+version = 1
+
+depends = ['apps']
+
+title = _('Virtual Private Network (OpenVPN)')
+
+description = [
+ format_lazy(
+ _('Virtual Private Network (VPN) is a technique for securely '
+ 'connecting two devices in order to access resources of a '
+ 'private network. While you are away from home, you can connect '
+ 'to your {box_name} in order to join your home network and '
+ 'access private/internal services provided by {box_name}. '
+ 'You can also access the rest of the Internet via {box_name} '
+ 'for added security and anonymity.'), box_name=_(cfg.box_name))
+]
service = None
@@ -35,13 +51,16 @@ service = None
def init():
"""Intialize the OpenVPN module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Virtual Private Network (OpenVPN)'), 'glyphicon-lock',
- 'openvpn:index', 850)
+ menu.add_urlname(title, 'glyphicon-lock', 'openvpn:index', 850)
global service
service = service_module.Service(
- 'openvpn', _('OpenVPN'), ['openvpn'],
- is_external=True, enabled=is_enabled())
+ 'openvpn', title, ['openvpn'], is_external=True, enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['openvpn', 'easy-rsa'])
def is_enabled():
diff --git a/plinth/modules/openvpn/templates/openvpn.html b/plinth/modules/openvpn/templates/openvpn.html
index d2ff0c5d8..422a9e69f 100644
--- a/plinth/modules/openvpn/templates/openvpn.html
+++ b/plinth/modules/openvpn/templates/openvpn.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -30,21 +30,7 @@
{% endblock %}
-{% block content %}
-
-
{% trans "Virtual Private Network (OpenVPN)" %}
-
-
- {% blocktrans trimmed %}
- Virtual Private Network (VPN) is a technique for securely
- connecting two devices in order to access resources of a
- private network. While you are away from home, you can connect
- to your {{ box_name }} in order to join your home network and
- access private/internal services provided by {{ box_name }}.
- You can also access the rest of the Internet via {{ box_name }}
- for added security and anonymity.
- {% endblocktrans %}
-
+{% block configuration %}
{% if status.is_setup %}
diff --git a/plinth/modules/openvpn/views.py b/plinth/modules/openvpn/views.py
index f4cd85ed8..ea1c6aefa 100644
--- a/plinth/modules/openvpn/views.py
+++ b/plinth/modules/openvpn/views.py
@@ -29,7 +29,6 @@ import logging
from .forms import OpenVpnForm
from plinth import actions
-from plinth import package
from plinth.modules import openvpn
from plinth.modules.config import config
@@ -38,7 +37,6 @@ logger = logging.getLogger(__name__)
setup_process = None
-@package.required(['openvpn', 'easy-rsa'])
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -59,7 +57,8 @@ def index(request):
form = OpenVpnForm(initial=status, prefix='openvpn')
return TemplateResponse(request, 'openvpn.html',
- {'title': _('Virtual Private Network (OpenVPN)'),
+ {'title': openvpn.title,
+ 'description': openvpn.description,
'status': status,
'form': form})
diff --git a/plinth/modules/owncloud/__init__.py b/plinth/modules/owncloud/__init__.py
index e48fcae2d..c5fbdd59a 100644
--- a/plinth/modules/owncloud/__init__.py
+++ b/plinth/modules/owncloud/__init__.py
@@ -19,14 +19,60 @@
Plinth module to configure ownCloud
"""
-from . import owncloud
-from .owncloud import init
+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
-__all__ = ['owncloud', 'init']
+version = 1
-depends = ['plinth.modules.apps']
+depends = ['apps']
+
+title = _('File Hosting (ownCloud)')
+
+description = [
+ _('ownCloud gives you universal access to your files through a web '
+ 'interface or WebDAV. It also provides a platform to easily view '
+ '& sync your contacts, calendars and bookmarks across all your '
+ 'devices and enables basic editing right on the web. Installation '
+ 'has minimal server requirements, doesn\'t need special '
+ 'permissions and is quick. ownCloud is extendable via a simple '
+ 'but powerful API for applications and plugins.'),
+
+ _('When enabled, the ownCloud installation will be available '
+ 'from
/owncloud path on the web server. '
+ 'Visit this URL to set up the initial administration account for '
+ 'ownCloud.')
+]
+
+service = None
+
+
+def init():
+ """Initialize the ownCloud module"""
+ menu = cfg.main_menu.get('apps:index')
+ menu.add_urlname(title, 'glyphicon-picture', 'owncloud:index', 700)
+
+ global service
+ service = service_module.Service(
+ 'owncloud', title, ['http', 'https'], is_external=True,
+ enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['postgresql', 'php5-pgsql', 'owncloud', 'php-dropbox',
+ 'php-google-api-php-client'])
+ helper.call('post', actions.superuser_run, 'owncloud-setup', ['enable'])
+ helper.call('post', service.notify_enabled, None, True)
+
+
+def is_enabled():
+ """Return whether the module is enabled."""
+ output = actions.run('owncloud-setup', ['status'])
+ return 'enable' in output.split()
def diagnose():
diff --git a/plinth/modules/owncloud/forms.py b/plinth/modules/owncloud/forms.py
new file mode 100644
index 000000000..3ea4c6f8c
--- /dev/null
+++ b/plinth/modules/owncloud/forms.py
@@ -0,0 +1,30 @@
+#
+# 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
.
+#
+
+"""
+Forms for configuring ownCloud.
+"""
+
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+
+
+class OwnCloudForm(forms.Form): # pylint: disable-msg=W0232
+ """ownCloud configuration form"""
+ enabled = forms.BooleanField(
+ label=_('Enable ownCloud'),
+ required=False)
diff --git a/plinth/modules/owncloud/templates/owncloud.html b/plinth/modules/owncloud/templates/owncloud.html
index 410071f08..266967d3d 100644
--- a/plinth/modules/owncloud/templates/owncloud.html
+++ b/plinth/modules/owncloud/templates/owncloud.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,30 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "File Hosting (ownCloud)" %}
-
-
- {% blocktrans trimmed %}
- ownCloud gives you universal access to your files through a web
- interface or WebDAV. It also provides a platform to easily view
- & sync your contacts, calendars and bookmarks across all your
- devices and enables basic editing right on the web. Installation
- has minimal server requirements, doesn't need special
- permissions and is quick. ownCloud is extendable via a simple
- but powerful API for applications and plugins.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- When enabled, the ownCloud installation will be available
- from /owncloud path on the web server.
- Visit this URL to set up the initial administration account for
- ownCloud.
- {% endblocktrans %}
-
+{% block configuration %}
{% include "diagnostics_button.html" with module="owncloud" %}
diff --git a/plinth/modules/owncloud/urls.py b/plinth/modules/owncloud/urls.py
index d69ac14ef..338ad6cc5 100644
--- a/plinth/modules/owncloud/urls.py
+++ b/plinth/modules/owncloud/urls.py
@@ -21,7 +21,7 @@ URLs for the ownCloud module
from django.conf.urls import url
-from . import owncloud as views
+from . import views
urlpatterns = [
diff --git a/plinth/modules/owncloud/owncloud.py b/plinth/modules/owncloud/views.py
similarity index 62%
rename from plinth/modules/owncloud/owncloud.py
rename to plinth/modules/owncloud/views.py
index f510965d6..b2e77077b 100644
--- a/plinth/modules/owncloud/owncloud.py
+++ b/plinth/modules/owncloud/views.py
@@ -19,47 +19,15 @@
Plinth module for configuring ownCloud.
"""
-from django import forms
from django.contrib import messages
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _
+from .forms import OwnCloudForm
from plinth import actions
-from plinth import cfg
-from plinth import package
-from plinth import service as service_module
+from plinth.modules import owncloud
-service = None
-
-
-class OwnCloudForm(forms.Form): # pylint: disable-msg=W0232
- """ownCloud configuration form"""
- enabled = forms.BooleanField(label=_('Enable ownCloud'), required=False)
-
-
-def init():
- """Initialize the ownCloud module"""
- menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('File Hosting (ownCloud)'), 'glyphicon-picture',
- 'owncloud:index', 700)
-
- status = get_status()
-
- global service # pylint: disable-msg=W0603
- service = service_module.Service(
- 'owncloud', _('ownCloud'), ['http', 'https'], is_external=True,
- enabled=status['enabled'])
-
-
-def on_install():
- """Tasks to run after package install."""
- actions.superuser_run('owncloud-setup', ['enable'])
- service.notify_enabled(None, True)
-
-
-@package.required(['postgresql', 'php5-pgsql', 'owncloud', 'php-dropbox',
- 'php-google-api-php-client'], on_install=on_install)
def index(request):
"""Serve the ownCloud configuration page"""
status = get_status()
@@ -77,14 +45,14 @@ def index(request):
form = OwnCloudForm(initial=status, prefix='owncloud')
return TemplateResponse(request, 'owncloud.html',
- {'title': _('ownCloud'),
+ {'title': owncloud.title,
+ 'description': owncloud.description,
'form': form})
def get_status():
"""Return the current status"""
- output = actions.run('owncloud-setup', ['status'])
- return {'enabled': 'enable' in output.split()}
+ return {'enabled': owncloud.is_enabled()}
def _apply_changes(request, old_status, new_status):
@@ -104,4 +72,4 @@ def _apply_changes(request, old_status, new_status):
# Send a signal to other modules that the service is
# enabled/disabled
- service.notify_enabled(None, new_status['enabled'])
+ owncloud.service.notify_enabled(None, new_status['enabled'])
diff --git a/plinth/modules/pagekite/__init__.py b/plinth/modules/pagekite/__init__.py
index b83c7e5ae..6862c1b68 100644
--- a/plinth/modules/pagekite/__init__.py
+++ b/plinth/modules/pagekite/__init__.py
@@ -21,19 +21,59 @@ 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
-__all__ = ['init']
+version = 1
-depends = ['plinth.modules.apps', 'plinth.modules.names']
+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"""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Public Visibility (PageKite)'),
- 'glyphicon-flag', 'pagekite:index', 800)
+ menu.add_urlname(title, 'glyphicon-flag', 'pagekite:index', 800)
# Register kite name with Name Services module.
utils.update_names_module(initial_registration=True)
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['pagekite'])
diff --git a/plinth/modules/pagekite/templates/pagekite_introduction.html b/plinth/modules/pagekite/templates/pagekite_introduction.html
index 92682064d..0961c1923 100644
--- a/plinth/modules/pagekite/templates/pagekite_introduction.html
+++ b/plinth/modules/pagekite/templates/pagekite_introduction.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -20,58 +20,7 @@
{% load i18n %}
-{% block content %}
-
-
- {% 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 %}
-
-
-
- -
- {% blocktrans trimmed %}
- {{ box_name }} is behind a restricted firewall.
- {% endblocktrans %}
-
-
- -
- {% blocktrans trimmed %}
- {{ box_name }} 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 %}
-
-
- - {% trans "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 %}
-
+{% block configuration %}
diff --git a/plinth/modules/pagekite/views.py b/plinth/modules/pagekite/views.py
index 99fe0fd46..583f072dc 100644
--- a/plinth/modules/pagekite/views.py
+++ b/plinth/modules/pagekite/views.py
@@ -18,18 +18,16 @@
from django.core.urlresolvers import reverse, reverse_lazy
from django.http.response import HttpResponseRedirect
from django.template.response import TemplateResponse
-from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.generic import View, TemplateView
from django.views.generic.edit import FormView
-from plinth import package
from . import utils
from .forms import ConfigurationForm, StandardServiceForm, \
AddCustomServiceForm, DeleteCustomServiceForm
+from plinth.modules import pagekite
-required_packages = ('pagekite',)
subsubmenu = [{'url': reverse_lazy('pagekite:index'),
'text': _('About PageKite')},
{'url': reverse_lazy('pagekite:configure'),
@@ -43,7 +41,8 @@ subsubmenu = [{'url': reverse_lazy('pagekite:index'),
def index(request):
"""Serve introduction page"""
return TemplateResponse(request, 'pagekite_introduction.html',
- {'title': _('Public Visibility (PageKite)'),
+ {'title': pagekite.title,
+ 'description': pagekite.description,
'subsubmenu': subsubmenu})
@@ -59,7 +58,6 @@ class ContextMixin(object):
context['subsubmenu'] = subsubmenu
return context
- @method_decorator(package.required(required_packages))
def dispatch(self, *args, **kwargs):
return super(ContextMixin, self).dispatch(*args, **kwargs)
@@ -81,8 +79,9 @@ class CustomServiceView(ContextMixin, TemplateView):
unused, custom_services = utils.get_pagekite_services()
for service in custom_services:
service['form'] = AddCustomServiceForm(initial=service)
- context['custom_services'] = [utils.prepare_service_for_display(service)
- for service in custom_services]
+ context['custom_services'] = [
+ utils.prepare_service_for_display(service)
+ for service in custom_services]
context.update(utils.get_kite_details())
return context
diff --git a/plinth/modules/power/__init__.py b/plinth/modules/power/__init__.py
index f8f4c4e56..9cf5d0aef 100644
--- a/plinth/modules/power/__init__.py
+++ b/plinth/modules/power/__init__.py
@@ -23,11 +23,20 @@ from django.utils.translation import ugettext_lazy as _
from plinth import cfg
-depends = ['plinth.modules.system']
+version = 1
+
+is_essential = True
+
+depends = ['system']
+
+title = _('Power')
+
+description = [
+ _('Restart or shut down the system.')
+]
def init():
"""Initialize the power module."""
menu = cfg.main_menu.get('system:index')
- menu.add_urlname(_('Power'), 'glyphicon-off',
- 'power:index', 1000)
+ menu.add_urlname(title, 'glyphicon-off', 'power:index', 1000)
diff --git a/plinth/modules/power/templates/power.html b/plinth/modules/power/templates/power.html
index 642aea911..89553d389 100644
--- a/plinth/modules/power/templates/power.html
+++ b/plinth/modules/power/templates/power.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,13 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
- {{ title }}
-
-
- {% blocktrans trimmed %}Restart or shut down the system.{% endblocktrans %}
-
+{% block configuration %}
diff --git a/plinth/modules/power/views.py b/plinth/modules/power/views.py
index e1d835f6a..ae18d4506 100644
--- a/plinth/modules/power/views.py
+++ b/plinth/modules/power/views.py
@@ -26,11 +26,14 @@ from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
from plinth import actions
+from plinth.modules import power
def index(request):
"""Serve power controls page."""
- return TemplateResponse(request, 'power.html', {'title': _('Power')})
+ return TemplateResponse(request, 'power.html',
+ {'title': power.title,
+ 'description': power.description})
def restart(request):
diff --git a/plinth/modules/privoxy/__init__.py b/plinth/modules/privoxy/__init__.py
index 545020a0f..eaf0d20d1 100644
--- a/plinth/modules/privoxy/__init__.py
+++ b/plinth/modules/privoxy/__init__.py
@@ -20,15 +20,36 @@ Plinth module to configure Privoxy.
"""
from django.utils.translation import ugettext_lazy as _
-import json
from plinth import actions
from plinth import action_utils
from plinth import cfg
from plinth import service as service_module
+from plinth.utils import format_lazy
-depends = ['plinth.modules.apps']
+version = 1
+
+is_essential = False
+
+depends = ['apps']
+
+title = _('Web Proxy (Privoxy)')
+
+description = [
+ _('Privoxy is a non-caching web proxy with advanced filtering '
+ 'capabilities for enhancing privacy, modifying web page data and '
+ 'HTTP headers, controlling access, and removing ads and other '
+ 'obnoxious Internet junk. '),
+
+ format_lazy(
+ _('You can use Privoxy by modifying your browser proxy settings to '
+ 'your {box_name} hostname (or IP address) with port 8118. '
+ 'While using Privoxy, you can see its configuration details and '
+ 'documentation at '
+ 'http://config.privoxy.org/ '
+ 'or http://p.p.'), box_name=_(cfg.box_name))
+]
service = None
@@ -36,13 +57,18 @@ service = None
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Web Proxy (Privoxy)'), 'glyphicon-cloud-upload',
- 'privoxy:index', 1000)
+ menu.add_urlname(title, 'glyphicon-cloud-upload', 'privoxy:index', 1000)
global service
service = service_module.Service(
- 'privoxy', _('Privoxy Web Proxy'),
- is_external=False, enabled=is_enabled())
+ 'privoxy', title, is_external=False, enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['privoxy'])
+ helper.call('post', actions.superuser_run, 'privoxy', ['setup'])
+ helper.call('post', service.notify_enabled, None, True)
def is_enabled():
@@ -84,9 +110,7 @@ def diagnose_url_with_proxy():
result = action_utils.diagnose_url(url, kind=address['kind'], env=env)
result[0] = _('Access {url} with proxy {proxy} on tcp{kind}') \
- .format(url=url, proxy=proxy, kind=address['kind'])
+ .format(url=url, proxy=proxy, kind=address['kind'])
results.append(result)
return results
-
-
diff --git a/plinth/modules/privoxy/templates/privoxy.html b/plinth/modules/privoxy/templates/privoxy.html
index 4a4f82c00..d80099eb4 100644
--- a/plinth/modules/privoxy/templates/privoxy.html
+++ b/plinth/modules/privoxy/templates/privoxy.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,29 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "Web Proxy (Privoxy)" %}
-
-
- {% blocktrans trimmed %}
- Privoxy is a non-caching web proxy with advanced filtering
- capabilities for enhancing privacy, modifying web page data and
- HTTP headers, controlling access, and removing ads and other
- obnoxious Internet junk.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- You can use Privoxy by modifying your browser proxy settings to
- your {{ box_name }} hostname (or IP address) with port 8118.
- While using Privoxy, you can see its configuration details and
- documentation at
- http://config.privoxy.org/
- or http://p.p.
- {% endblocktrans %}
-
+{% block configuration %}
{% trans "Status" %}
diff --git a/plinth/modules/privoxy/views.py b/plinth/modules/privoxy/views.py
index 47bb844c7..853f8709f 100644
--- a/plinth/modules/privoxy/views.py
+++ b/plinth/modules/privoxy/views.py
@@ -26,19 +26,11 @@ import logging
from .forms import PrivoxyForm
from plinth import actions
-from plinth import package
from plinth.modules import privoxy
logger = logging.getLogger(__name__)
-def on_install():
- """Notify that the service is now enabled."""
- actions.superuser_run('privoxy', ['setup'])
- privoxy.service.notify_enabled(None, True)
-
-
-@package.required(['privoxy'], on_install=on_install)
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -56,7 +48,8 @@ def index(request):
form = PrivoxyForm(initial=status, prefix='privoxy')
return TemplateResponse(request, 'privoxy.html',
- {'title': _('Web Proxy (Privoxy)'),
+ {'title': privoxy.title,
+ 'description': privoxy.description,
'status': status,
'form': form})
diff --git a/plinth/modules/quassel/__init__.py b/plinth/modules/quassel/__init__.py
index 3e8c1b03a..c30f0abcc 100644
--- a/plinth/modules/quassel/__init__.py
+++ b/plinth/modules/quassel/__init__.py
@@ -16,7 +16,7 @@
#
"""
-Plinth module for quassel.
+Plinth module for Quassel.
"""
from django.utils.translation import ugettext_lazy as _
@@ -24,8 +24,30 @@ 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.utils import format_lazy
-depends = ['plinth.modules.apps']
+version = 1
+
+depends = ['apps']
+
+title = _('IRC Client (Quassel)')
+
+description = [
+ format_lazy(
+ _('Quassel is an IRC application that is split into two parts, a '
+ '"core" and a "client". This allows the core to remain connected '
+ 'to IRC servers, and to continue receiving messages, even when '
+ 'the client is disconnected. {box_name} can run the Quassel '
+ 'core service keeping you always online and one or more Quassel '
+ 'clients from a desktop or a mobile can be used to connect and '
+ 'disconnect from it.'), box_name=_(cfg.box_name)),
+
+ _('You can connect to your Quassel core on the default Quassel port '
+ '4242. Clients to connect to Quassel from your '
+ 'desktop and '
+ 'mobile devices '
+ 'are available.')
+]
service = None
@@ -33,13 +55,17 @@ service = None
def init():
"""Initialize the quassel module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('IRC Client (Quassel)'), 'glyphicon-retweet',
- 'quassel:index', 730)
+ menu.add_urlname(title, 'glyphicon-retweet', 'quassel:index', 730)
global service
service = service_module.Service(
- 'quassel-plinth', _('Quassel IRC Client'),
- is_external=True, enabled=is_enabled())
+ 'quassel-plinth', title, is_external=True, enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['quassel-core'])
+ helper.call('post', service.notify_enabled, None, True)
def is_enabled():
diff --git a/plinth/modules/quassel/templates/quassel.html b/plinth/modules/quassel/templates/quassel.html
index c2f47ce0c..227769463 100644
--- a/plinth/modules/quassel/templates/quassel.html
+++ b/plinth/modules/quassel/templates/quassel.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,31 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "IRC Client (Quassel)" %}
-
-
- {% blocktrans trimmed %}
- Quassel is an IRC application that is split into two parts, a
- "core" and a "client". This allows the core to remain connected
- to IRC servers, and to continue receiving messages, even when
- the client is disconnected. {{ box_name }} can run the Quassel
- core service keeping you always online and one or more Quassel
- clients from a desktop or a mobile can be used to connect and
- disconnect from it.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- You can connect to your Quassel core on the default Quassel port
- 4242. Clients to connect to Quassel from your
- desktop and
- mobile devices
- are available.
- {% endblocktrans %}
-
+{% block configuration %}
{% trans "Status" %}
diff --git a/plinth/modules/quassel/views.py b/plinth/modules/quassel/views.py
index 3918c849b..2adee5260 100644
--- a/plinth/modules/quassel/views.py
+++ b/plinth/modules/quassel/views.py
@@ -25,16 +25,9 @@ from django.utils.translation import ugettext as _
from .forms import QuasselForm
from plinth import actions
-from plinth import package
from plinth.modules import quassel
-def on_install():
- """Notify that the service is now enabled."""
- quassel.service.notify_enabled(None, True)
-
-
-@package.required(['quassel-core'], on_install=on_install)
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -51,7 +44,8 @@ def index(request):
form = QuasselForm(initial=status, prefix='quassel')
return TemplateResponse(request, 'quassel.html',
- {'title': _('IRC Client (Quassel)'),
+ {'title': quassel.title,
+ 'description': quassel.description,
'status': status,
'form': form})
diff --git a/plinth/modules/repro/__init__.py b/plinth/modules/repro/__init__.py
index 7517052f8..0a72adcf8 100644
--- a/plinth/modules/repro/__init__.py
+++ b/plinth/modules/repro/__init__.py
@@ -21,11 +21,37 @@ Plinth module for repro.
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
-depends = ['plinth.modules.apps']
+version = 1
+
+depends = ['apps']
+
+title = _('SIP Server (repro)')
+
+description = [
+ _('repro provides various SIP services that a SIP softphone can utilize '
+ 'to provide audio and video calls as well as presence and instant '
+ 'messaging. repro provides a server and SIP user accounts that clients '
+ 'can use to let their presence known. It also acts as a proxy to '
+ 'federate SIP communications to other servers on the Internet similar '
+ 'to email.'),
+
+ _('To make SIP calls, a client application is needed. Available clients '
+ 'include
Jitsi (for computers) and '
+ '
'
+ 'CSipSimple (for Android phones).'),
+
+ _('
Note: Before using repro, domains and users will '
+ 'need to be configured using the
'
+ 'web-based configuration panel. Users in the
admin group '
+ 'will be able to log in to the repro configuration panel. After setting '
+ 'the domain, it is required to restart the repro service. Disable the '
+ 'service and re-enable it.'),
+]
service = None
@@ -33,13 +59,19 @@ service = None
def init():
"""Initialize the repro module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('SIP Server (repro)'), 'glyphicon-phone-alt',
- 'repro:index', 825)
+ menu.add_urlname(title, 'glyphicon-phone-alt', 'repro:index', 825)
global service
service = service_module.Service(
- 'repro', _('repro SIP Server'), ['sip-plinth', 'sip-tls-plinth'],
- is_external=True, enabled=is_enabled())
+ 'repro', title, ['sip-plinth', 'sip-tls-plinth'], is_external=True,
+ enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['repro'])
+ helper.call('post', actions.superuser_run, 'repro', ['setup'])
+ helper.call('post', service.notify_enabled, None, True)
def is_enabled():
diff --git a/plinth/modules/repro/templates/repro.html b/plinth/modules/repro/templates/repro.html
index 1d4b2fe7d..df5109cb0 100644
--- a/plinth/modules/repro/templates/repro.html
+++ b/plinth/modules/repro/templates/repro.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,39 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "SIP Server (repro)" %}
-
-
- {% blocktrans trimmed %}
- repro provides various SIP services that a SIP softphone can utilize to
- provide audio and video calls as well as presence and instant messaging.
- repro provides a server and SIP user accounts that clients can use to let
- their presence known. It also acts as a proxy to federate SIP
- communications to other servers on the Internet similar to email.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- To make SIP calls, a client application is needed. Available clients
- include Jitsi (for computers) and
-
- CSipSimple (for Android phones).
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- Note: Before using repro, domains and users will need
- to be configured using the web-based
- configuration panel. Users in the admin group will be able
- to log in to the repro configuration panel. After setting the domain, it
- is required to restart the repro service. Disable the service and
- re-enable it.
- {% endblocktrans %}
-
+{% block configuration %}
{% trans "Status" %}
diff --git a/plinth/modules/repro/views.py b/plinth/modules/repro/views.py
index 40392f48a..407a96b0c 100644
--- a/plinth/modules/repro/views.py
+++ b/plinth/modules/repro/views.py
@@ -25,17 +25,9 @@ from django.utils.translation import ugettext as _
from .forms import ReproForm
from plinth import actions
-from plinth import package
from plinth.modules import repro
-def on_install():
- """Notify that the service is now enabled."""
- actions.superuser_run('repro', ['setup'])
- repro.service.notify_enabled(None, True)
-
-
-@package.required(['repro'], on_install=on_install)
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -52,7 +44,8 @@ def index(request):
form = ReproForm(initial=status, prefix='repro')
return TemplateResponse(request, 'repro.html',
- {'title': _('SIP Server (repro)'),
+ {'title': repro.title,
+ 'description': repro.description,
'status': status,
'form': form})
diff --git a/plinth/modules/restore/__init__.py b/plinth/modules/restore/__init__.py
index 21bb030d4..dff18b3d4 100644
--- a/plinth/modules/restore/__init__.py
+++ b/plinth/modules/restore/__init__.py
@@ -22,24 +22,44 @@ Plinth module to configure reStore.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, cfg
from plinth import service as service_module
+from plinth.utils import format_lazy
service = None
-__all__ = ['init']
+version = 1
-depends = ['plinth.modules.apps']
+depends = ['apps']
+
+title = _('Unhosted Storage (reStore)')
+
+description = [
+ format_lazy(
+ _('reStore is a server for
'
+ 'unhosted web applications. The idea is to uncouple web '
+ 'applications from data. No matter where a web application is '
+ 'served from, the data can be stored on an unhosted storage '
+ 'server of user\'s choice. With reStore, your {box_name} becomes '
+ 'your unhosted storage server.'), box_name=_(cfg.box_name)),
+
+ _('You can create and edit accounts in the '
+ '
reStore web-interface.')
+]
def init():
"""Initialize the reStore module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Unhosted Storage (reStore)'), 'glyphicon-hdd',
- 'restore:index', 750)
+ menu.add_urlname(title, 'glyphicon-hdd', 'restore:index', 750)
global service
service = service_module.Service(
- 'node-restore', _('reStore'), ['http', 'https'],
- is_external=False, enabled=is_enabled())
+ 'node-restore', title, ['http', 'https'], is_external=False,
+ enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['node-restore'])
def is_enabled():
diff --git a/plinth/modules/restore/templates/restore_index.html b/plinth/modules/restore/templates/restore_index.html
index 8c9402ba7..4148a047c 100644
--- a/plinth/modules/restore/templates/restore_index.html
+++ b/plinth/modules/restore/templates/restore_index.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,27 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "Unhosted Storage (reStore)" %}
-
-
- {% blocktrans trimmed %}
- reStore is a server for unhosted
- web applications. The idea is to uncouple web applications from
- data. No matter where a web application is served from, the
- data can be stored on an unhosted storage server of user's
- choice. With reStore, your {{ box_name }} becomes your
- unhosted storage server.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- You can create and edit accounts in the
- reStore web-interface.
- {% endblocktrans %}
-
+{% block configuration %}
Configuration
diff --git a/plinth/modules/restore/views.py b/plinth/modules/restore/views.py
index e0c75fbd6..0e91904a4 100644
--- a/plinth/modules/restore/views.py
+++ b/plinth/modules/restore/views.py
@@ -24,11 +24,10 @@ from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
from .forms import ReStoreForm
-from plinth import actions, package
+from plinth import actions
from plinth.modules import restore
-@package.required(['node-restore'])
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -43,7 +42,8 @@ def index(request):
form = ReStoreForm(initial=status, prefix='restore')
return TemplateResponse(request, 'restore_index.html',
- {'title': _('Unhosted Storage (reStore)'),
+ {'title': restore.title,
+ 'description': restore.description,
'status': status,
'form': form})
diff --git a/plinth/modules/roundcube/__init__.py b/plinth/modules/roundcube/__init__.py
index 5d9bd1e40..1c11596af 100644
--- a/plinth/modules/roundcube/__init__.py
+++ b/plinth/modules/roundcube/__init__.py
@@ -24,17 +24,49 @@ 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
-depends = ['plinth.modules.apps']
+version = 1
+
+depends = ['apps']
+
+title = _('Email Client (Roundcube)')
+
+description = [
+ _('Roundcube webmail is a browser-based multilingual IMAP '
+ 'client with an application-like user interface. It provides '
+ 'full functionality you expect from an email client, including '
+ 'MIME support, address book, folder manipulation, message '
+ 'searching and spell checking.'),
+
+ _('You can access Roundcube from
'
+ '/roundcube. Provide the username and password of the email '
+ 'account you wish to access followed by the domain name of the '
+ 'IMAP server for your email provider, like
imap.example.com'
+ '. For IMAP over SSL (recommended), fill the server field '
+ 'like
imaps://imap.example.com.'),
+
+ _('For Gmail, username will be your Gmail address, password will be '
+ 'your Google account password and server will be '
+ '
imaps://imap.gmail.com. Note that you will also need '
+ 'to enable "Less secure apps" in your Google account settings '
+ '(
https://www.google.com/settings/security/lesssecureapps).'),
+]
def init():
"""Intialize the module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Email Client (Roundcube)'), 'glyphicon-envelope',
- 'roundcube:index', 600)
+ menu.add_urlname(title, 'glyphicon-envelope', 'roundcube:index', 600)
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.call('pre', actions.superuser_run, 'roundcube', ['pre-install'])
+ helper.install(['sqlite3', 'roundcube', 'roundcube-sqlite3'])
+ helper.call('pre', actions.superuser_run, 'roundcube', ['setup'])
+
def is_enabled():
"""Return whether the module is enabled."""
diff --git a/plinth/modules/roundcube/templates/roundcube.html b/plinth/modules/roundcube/templates/roundcube.html
index fea5d2c92..6028f7aba 100644
--- a/plinth/modules/roundcube/templates/roundcube.html
+++ b/plinth/modules/roundcube/templates/roundcube.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,41 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "Email Client (Roundcube)" %}
-
-
- {% blocktrans trimmed %}
- Roundcube webmail is a browser-based multilingual IMAP client
- with an application-like user interface. It provides full
- functionality you expect from an email client, including MIME
- support, address book, folder manipulation, message searching
- and spell checking.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- You can access Roundcube from /roundcube.
- Provide the username and password of the email account you wish
- to access followed by the domain name of the IMAP server for
- your email provider, like imap.example.com. For
- IMAP over SSL (recommended), fill the server field like
- imaps://imap.example.com.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- For Gmail, username will be your Gmail address, password will be
- your Google account password and server will be
- imaps://imap.gmail.com. Note that you will also need
- to enable "Less secure apps" in your Google account settings
- (https://www.google.com/settings/security/lesssecureapps).
- {% endblocktrans %}
-
+{% block configuration %}
{% include "diagnostics_button.html" with module="roundcube" %}
diff --git a/plinth/modules/roundcube/views.py b/plinth/modules/roundcube/views.py
index 1790d8868..610fdb8c0 100644
--- a/plinth/modules/roundcube/views.py
+++ b/plinth/modules/roundcube/views.py
@@ -26,24 +26,11 @@ import logging
from .forms import RoundcubeForm
from plinth import actions
-from plinth import package
from plinth.modules import roundcube
logger = logging.getLogger(__name__)
-def before_install():
- """Preseed debconf values before the packages are installed."""
- actions.superuser_run('roundcube', ['pre-install'])
-
-
-def on_install():
- """Setup Roundcube Apache configuration."""
- actions.superuser_run('roundcube', ['setup'])
-
-
-@package.required(['sqlite3', 'roundcube', 'roundcube-sqlite3'],
- before_install=before_install, on_install=on_install)
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -61,7 +48,8 @@ def index(request):
form = RoundcubeForm(initial=status, prefix='roundcube')
return TemplateResponse(request, 'roundcube.html',
- {'title': _('Email Client (Roundcube)'),
+ {'title': roundcube.title,
+ 'description': roundcube.description,
'status': status,
'form': form})
diff --git a/plinth/modules/shaarli/__init__.py b/plinth/modules/shaarli/__init__.py
index e23af9186..2210da998 100644
--- a/plinth/modules/shaarli/__init__.py
+++ b/plinth/modules/shaarli/__init__.py
@@ -26,7 +26,20 @@ from plinth import cfg
from plinth import service as service_module
-depends = ['plinth.modules.apps']
+version = 1
+
+depends = ['apps']
+
+title = _('Bookmarks (Shaarli)')
+
+description = [
+ _('Shaarli allows you to save and share bookmarks.'),
+
+ _('When enabled, Shaarli will be available from
'
+ '/shaarli path on the web server. Note that Shaarli only supports a '
+ 'single user account, which you will need to setup on the initial '
+ 'visit.'),
+]
service = None
@@ -34,13 +47,18 @@ service = None
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Bookmarks (Shaarli)'), 'glyphicon-bookmark',
- 'shaarli:index', 350)
+ menu.add_urlname(title, 'glyphicon-bookmark', 'shaarli:index', 350)
global service
service = service_module.Service(
- 'shaarli', _('Shaarli'), ['http', 'https'],
- is_external=True, enabled=is_enabled())
+ 'shaarli', title, ['http', 'https'], is_external=True,
+ enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['shaarli'])
+ helper.call('post', service.notify_enabled, None, True)
def is_enabled():
diff --git a/plinth/modules/shaarli/templates/shaarli.html b/plinth/modules/shaarli/templates/shaarli.html
index 391c25466..9a6c55b0a 100644
--- a/plinth/modules/shaarli/templates/shaarli.html
+++ b/plinth/modules/shaarli/templates/shaarli.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,21 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "Bookmarks (Shaarli)" %}
-
-
{% trans "Shaarli allows you to save and share bookmarks." %}
-
-
- {% blocktrans trimmed %}
-
- When enabled, Shaarli will be available from /shaarli
- path on the web server. Note that Shaarli only supports a single
- user account, which you will need to setup on the initial visit.
-
- {% endblocktrans %}
-
+{% block configuration %}
{% trans "Configuration" %}
diff --git a/plinth/modules/shaarli/views.py b/plinth/modules/shaarli/views.py
index 6182db539..de174faa7 100644
--- a/plinth/modules/shaarli/views.py
+++ b/plinth/modules/shaarli/views.py
@@ -25,14 +25,9 @@ from django.utils.translation import ugettext as _
from .forms import ShaarliForm
from plinth import actions
-from plinth import package
from plinth.modules import shaarli
-def on_install():
- """Notify that the service is now enabled."""
- shaarli.service.notify_enabled(None, True)
-@package.required(['shaarli'], on_install=on_install)
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -49,7 +44,8 @@ def index(request):
form = ShaarliForm(initial=status, prefix='shaarli')
return TemplateResponse(request, 'shaarli.html',
- {'title': _('Bookmarks (Shaarli)'),
+ {'title': shaarli.title,
+ 'description': shaarli.description,
'status': status,
'form': form})
diff --git a/plinth/modules/system/__init__.py b/plinth/modules/system/__init__.py
index ec0f9e62d..7b75d3976 100644
--- a/plinth/modules/system/__init__.py
+++ b/plinth/modules/system/__init__.py
@@ -19,8 +19,29 @@
Plinth module for system section page
"""
-from . import system
-from .system import init
+from django.utils.translation import ugettext_lazy as _
+
+from plinth import cfg
+from plinth.utils import format_lazy
-__all__ = ['system', 'init']
+version = 1
+
+is_essential = 1
+
+title = _('System Configuration')
+
+description = [
+ format_lazy(
+ _('Here you can administrate the underlying system of your '
+ '{box_name}.'), box_name=_(cfg.box_name)),
+
+ format_lazy(
+ _('The options affect the {box_name} at its most general level, '
+ 'so be careful!'), box_name=_(cfg.box_name))
+]
+
+
+def init():
+ """Initialize the system module"""
+ cfg.main_menu.add_urlname(title, 'glyphicon-cog', 'system:index', 100)
diff --git a/plinth/modules/system/templates/system.html b/plinth/modules/system/templates/system.html
index e8c2a46f3..a60745d8f 100644
--- a/plinth/modules/system/templates/system.html
+++ b/plinth/modules/system/templates/system.html
@@ -1,4 +1,4 @@
-{% extends 'base.html' %}
+{% extends 'app.html' %}
{% comment %}
#
# This file is part of Plinth.
@@ -17,25 +17,3 @@
# along with this program. If not, see
.
#
{% endcomment %}
-
-{% load i18n %}
-
-{% block content %}
-
-
{% trans "System Configuration" %}
-
-
- {% blocktrans trimmed %}
- Here you can administrate the underlying system of your
- {{ box_name }}.
- {% endblocktrans %}
-
-
-
- {% blocktrans trimmed %}
- The options affect the {{ box_name }} at its most general level,
- so be careful!
- {% endblocktrans %}
-
-
-{% endblock %}
diff --git a/plinth/modules/system/urls.py b/plinth/modules/system/urls.py
index aceecc21d..8e75a6af5 100644
--- a/plinth/modules/system/urls.py
+++ b/plinth/modules/system/urls.py
@@ -21,7 +21,7 @@ URLs for the System module
from django.conf.urls import url
-from . import system as views
+from . import views
urlpatterns = [
diff --git a/plinth/modules/system/system.py b/plinth/modules/system/views.py
similarity index 73%
rename from plinth/modules/system/system.py
rename to plinth/modules/system/views.py
index c7a68b7f9..bc4dd1953 100644
--- a/plinth/modules/system/system.py
+++ b/plinth/modules/system/views.py
@@ -16,18 +16,12 @@
#
from django.template.response import TemplateResponse
-from django.utils.translation import ugettext_lazy as _
-from plinth import cfg
-
-
-def init():
- """Initialize the system module"""
- cfg.main_menu.add_urlname(_('System'), 'glyphicon-cog', 'system:index',
- 100)
+from plinth.modules import system
def index(request):
"""Serve the index page"""
return TemplateResponse(request, 'system.html',
- {'title': _('System Configuration')})
+ {'title': system.title,
+ 'description': system.description})
diff --git a/plinth/modules/tor/__init__.py b/plinth/modules/tor/__init__.py
index bd7ef694e..e12773198 100644
--- a/plinth/modules/tor/__init__.py
+++ b/plinth/modules/tor/__init__.py
@@ -20,7 +20,7 @@ Plinth module to configure Tor.
"""
import augeas
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext_lazy as _
import glob
import itertools
@@ -32,7 +32,20 @@ from plinth.modules.names import SERVICES
from plinth.signals import domain_added
-depends = ['plinth.modules.apps', 'plinth.modules.names']
+version = 1
+
+depends = ['apps', 'names']
+
+title = _('Anonymity Network (Tor)')
+
+description = [
+ _('Tor is an anonymous communication system. You can learn more '
+ 'about it from the
Tor '
+ 'Project website. For best protection when web surfing, the '
+ 'Tor Project recommends that you use the '
+ '
'
+ 'Tor Browser.')
+]
socks_service = None
bridge_service = None
@@ -45,8 +58,7 @@ APT_TOR_PREFIX = 'tor+'
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('Anonymity Network (Tor)'), 'glyphicon-eye-close',
- 'tor:index', 100)
+ menu.add_urlname(title, 'glyphicon-eye-close', 'tor:index', 100)
global socks_service
socks_service = service_module.Service(
@@ -77,6 +89,17 @@ def init():
services=hs_services)
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['tor', 'tor-geoipdb', 'torsocks', 'obfs4proxy',
+ 'apt-transport-tor'])
+ helper.call('post', actions.superuser_run, 'tor', ['setup'])
+ helper.call('post', actions.superuser_run, 'tor',
+ ['configure', '--apt-transport-tor', 'enable'])
+ helper.call('post', socks_service.notify_enabled, None, True)
+ helper.call('post', bridge_service.notify_enabled, None, True)
+
+
def is_enabled():
"""Return whether the module is enabled."""
return action_utils.service_is_enabled('tor')
diff --git a/plinth/modules/tor/templates/tor.html b/plinth/modules/tor/templates/tor.html
index 2b1b0f07e..9b6db3ed5 100644
--- a/plinth/modules/tor/templates/tor.html
+++ b/plinth/modules/tor/templates/tor.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -30,20 +30,7 @@
{% endblock %}
-{% block content %}
-
-
{% trans "Anonymity Network (Tor)" %}
-
-
- {% blocktrans trimmed %}
- Tor is an anonymous communication system. You can learn more
- about it from the Tor
- Project website. For best protection when web surfing, the
- Tor Project recommends that you use the
-
- Tor Browser.
- {% endblocktrans %}
-
+{% block configuration %}
{% trans "Status" %}
diff --git a/plinth/modules/tor/views.py b/plinth/modules/tor/views.py
index 120e42133..58e513a1f 100644
--- a/plinth/modules/tor/views.py
+++ b/plinth/modules/tor/views.py
@@ -25,7 +25,6 @@ from django.utils.translation import ugettext_lazy as _
from .forms import TorForm
from plinth import actions
-from plinth import package
from plinth.errors import ActionError
from plinth.modules import tor
from plinth.modules.names import SERVICES
@@ -34,18 +33,6 @@ from plinth.signals import domain_added, domain_removed
config_process = None
-def on_install():
- """Setup Tor configuration as soon as it is installed."""
- actions.superuser_run('tor', ['setup'])
- actions.superuser_run('tor',
- ['configure', '--apt-transport-tor', 'enable'])
- tor.socks_service.notify_enabled(None, True)
- tor.bridge_service.notify_enabled(None, True)
-
-
-@package.required(['tor', 'tor-geoipdb', 'torsocks', 'obfs4proxy',
- 'apt-transport-tor'],
- on_install=on_install)
def index(request):
"""Serve configuration page."""
if config_process:
@@ -65,7 +52,8 @@ def index(request):
form = TorForm(initial=status, prefix='tor')
return TemplateResponse(request, 'tor.html',
- {'title': _('Tor Control Panel'),
+ {'title': tor.title,
+ 'description': tor.description,
'status': status,
'config_running': bool(config_process),
'form': form})
diff --git a/plinth/modules/transmission/__init__.py b/plinth/modules/transmission/__init__.py
index b57797b99..aed1380ed 100644
--- a/plinth/modules/transmission/__init__.py
+++ b/plinth/modules/transmission/__init__.py
@@ -20,6 +20,7 @@ Plinth module to configure Transmission server
"""
from django.utils.translation import ugettext_lazy as _
+import json
from plinth import actions
from plinth import action_utils
@@ -27,7 +28,17 @@ from plinth import cfg
from plinth import service as service_module
-depends = ['plinth.modules.apps']
+version = 1
+
+depends = ['apps']
+
+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.')
+]
service = None
@@ -35,13 +46,25 @@ service = None
def init():
"""Intialize the Transmission module."""
menu = cfg.main_menu.get('apps:index')
- menu.add_urlname(_('BitTorrent (Transmission)'), 'glyphicon-save',
- 'transmission:index', 300)
+ menu.add_urlname(title, 'glyphicon-save', 'transmission:index', 300)
global service
service = service_module.Service(
- 'transmission', _('Transmission BitTorrent'), ['http', 'https'],
- is_external=True, enabled=is_enabled())
+ 'transmission', title, ['http', 'https'], is_external=True,
+ enabled=is_enabled())
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['transmission-daemon'])
+
+ new_configuration = {'rpc-whitelist-enabled': False}
+ helper.call('post', actions.superuser_run, 'transmission',
+ ['merge-configuration'],
+ input=json.dumps(new_configuration).encode())
+
+ helper.call('post', actions.superuser_run, 'transmission', ['enable'])
+ helper.call('post', service.notify_enabled, None, True)
def is_enabled():
diff --git a/plinth/modules/transmission/templates/transmission.html b/plinth/modules/transmission/templates/transmission.html
index de67c4908..0adf4648f 100644
--- a/plinth/modules/transmission/templates/transmission.html
+++ b/plinth/modules/transmission/templates/transmission.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,17 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{% trans "BitTorrent (Transmission)" %}
-
-
- {% blocktrans trimmed %}
- BitTorrent is a peer-to-peer file sharing protocol.
- Transmission daemon handles Bitorrent file sharing. Note that
- BitTorrent is not anonymous.
- {% endblocktrans %}
-
+{% block configuration %}
{% blocktrans trimmed %}
diff --git a/plinth/modules/transmission/views.py b/plinth/modules/transmission/views.py
index 213e69da8..c65c81013 100644
--- a/plinth/modules/transmission/views.py
+++ b/plinth/modules/transmission/views.py
@@ -28,7 +28,6 @@ import socket
from .forms import TransmissionForm
from plinth import actions
-from plinth import package
from plinth.modules import transmission
logger = logging.getLogger(__name__)
@@ -36,17 +35,6 @@ logger = logging.getLogger(__name__)
TRANSMISSION_CONFIG = '/etc/transmission-daemon/settings.json'
-def on_install():
- """Enable transmission as soon as it is installed."""
- new_configuration = {'rpc-whitelist-enabled': False}
- actions.superuser_run('transmission', ['merge-configuration'],
- input=json.dumps(new_configuration).encode())
-
- actions.superuser_run('transmission', ['enable'])
- transmission.service.notify_enabled(None, True)
-
-
-@package.required(['transmission-daemon'], on_install=on_install)
def index(request):
"""Serve configuration page."""
status = get_status()
@@ -64,7 +52,8 @@ def index(request):
form = TransmissionForm(initial=status, prefix='transmission')
return TemplateResponse(request, 'transmission.html',
- {'title': _('BitTorrent (Transmission)'),
+ {'title': transmission.title,
+ 'description': transmission.description,
'status': status,
'form': form})
diff --git a/plinth/modules/upgrades/__init__.py b/plinth/modules/upgrades/__init__.py
index 28b9f7c4a..795b88e1b 100644
--- a/plinth/modules/upgrades/__init__.py
+++ b/plinth/modules/upgrades/__init__.py
@@ -21,14 +21,32 @@ Plinth module for upgrades
from django.utils.translation import ugettext_lazy as _
+from plinth import actions
from plinth import cfg
-depends = ['plinth.modules.system']
+version = 1
+
+is_essential = 1
+
+depends = ['system']
+
+title = _('Software Upgrades')
+
+description = [
+ _('Upgrades install the latest software and security updates. When '
+ 'automatic upgrades are enabled, upgrades are automatically run every '
+ 'night. You don\'t normally need to start the upgrade process.')
+]
def init():
"""Initialize the module."""
menu = cfg.main_menu.get('system:index')
- menu.add_urlname(_('Software Upgrades'), 'glyphicon-refresh',
- 'upgrades:index', 21)
+ menu.add_urlname(title, 'glyphicon-refresh', 'upgrades:index', 21)
+
+
+def setup(helper, old_version=None):
+ """Install and configure the module."""
+ helper.install(['unattended-upgrades'])
+ helper.call('post', actions.superuser_run, 'upgrades', ['enable-auto'])
diff --git a/plinth/modules/upgrades/templates/upgrades.html b/plinth/modules/upgrades/templates/upgrades.html
index 15b0167b2..4cc47be98 100644
--- a/plinth/modules/upgrades/templates/upgrades.html
+++ b/plinth/modules/upgrades/templates/upgrades.html
@@ -1,4 +1,4 @@
-{% extends 'base.html' %}
+{% extends 'app.html' %}
{% comment %}
#
# This file is part of Plinth.
@@ -29,17 +29,7 @@
{% endblock %}
-{% block content %}
-
-
{{ title }}
-
-
- {% blocktrans trimmed %}
- Upgrades install the latest software and security updates. When automatic
- upgrades are enabled, upgrades are automatically run every night. You
- don't normally need to start the upgrade process.
- {% endblocktrans %}
-
+{% block configuration %}
{% blocktrans trimmed %}
diff --git a/plinth/modules/upgrades/templates/upgrades_configure.html b/plinth/modules/upgrades/templates/upgrades_configure.html
index 1ee94ec32..4a4e1c3fe 100644
--- a/plinth/modules/upgrades/templates/upgrades_configure.html
+++ b/plinth/modules/upgrades/templates/upgrades_configure.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "app.html" %}
{% comment %}
#
# This file is part of Plinth.
@@ -21,9 +21,7 @@
{% load bootstrap %}
{% load i18n %}
-{% block content %}
-
-
{{ title }}
+{% block configuration %}
-
- {% else %}
-
- {% for key, transaction in transactions.items %}
-
- {% blocktrans trimmed with package_names=transaction.package_names|join:", " status=transaction.status_string %}
- Installing {{ package_names }}: {{ status }}
- {% endblocktrans %}
-
-
-
-
- {% blocktrans trimmed with percentage=transaction.percentage %}
- {{ percentage }}% complete
- {% endblocktrans %}
-
-
-
- {% endfor %}
-
- {% endif %}
-
-{% endblock %}
diff --git a/plinth/templates/setup.html b/plinth/templates/setup.html
new file mode 100644
index 000000000..4cd25ee0f
--- /dev/null
+++ b/plinth/templates/setup.html
@@ -0,0 +1,101 @@
+{% 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 page_head %}
+
+ {% if setup_helper.current_operation %}
+
+ {% endif %}
+
+{% endblock %}
+
+
+{% block content %}
+
+
{% trans "Installation" %}: {{ setup_helper.module.title }}
+
+ {% for paragraph in setup_helper.module.description %}
+
{{ paragraph|safe }}
+ {% endfor %}
+
+ {% if not setup_helper.current_operation %}
+
+ {% if setup_helper.get_state == 'needs-setup' %}
+
+ {% blocktrans trimmed %}
+ Install this application?
+ {% endblocktrans %}
+
+
+
+ {% elif setup_helper.get_state == 'needs-update' %}
+
+ {% blocktrans trimmed %}
+ This application needs an update. Update now?
+ {% endblocktrans %}
+
+
+
+ {% endif %}
+
+ {% else %}
+
+ {% if setup_helper.current_operation.step == 'pre' %}
+ {% trans "Performing pre-install operation" %}
+ {% elif setup_helper.current_operation.step == 'post' %}
+ {% trans "Performing post-install operation" %}
+ {% elif setup_helper.current_operation.step == 'install' %}
+ {% with transaction=setup_helper.current_operation.transaction %}
+
+ {% blocktrans trimmed with package_names=transaction.package_names|join:", " status=transaction.status_string %}
+ Installing {{ package_names }}: {{ status }}
+ {% endblocktrans %}
+
+
+
+
+ {% blocktrans trimmed with percentage=transaction.percentage %}
+ {{ percentage }}% complete
+ {% endblocktrans %}
+
+
+
+ {% endwith %}
+ {% endif %}
+
+ {% endif %}
+
+{% endblock %}
diff --git a/plinth/urls.py b/plinth/urls.py
index 5ab1c866e..44be0a509 100644
--- a/plinth/urls.py
+++ b/plinth/urls.py
@@ -19,10 +19,9 @@
Django URLconf file containing all urls
"""
-from django.conf.urls import patterns, url
+from django.conf.urls import url
+from . import views
-
-urlpatterns = patterns( # pylint: disable-msg=C0103
- 'plinth.views',
- url(r'^$', 'index', name='index')
-)
+urlpatterns = [
+ url(r'^$', views.index, name='index')
+]
diff --git a/plinth/views.py b/plinth/views.py
index cfbbd608b..a5b9a7680 100644
--- a/plinth/views.py
+++ b/plinth/views.py
@@ -22,8 +22,7 @@ Main Plinth views
from django.core.urlresolvers import reverse
from django.http.response import HttpResponseRedirect
from django.views.generic import TemplateView
-
-from plinth import package as package_module
+import time
def index(request):
@@ -31,33 +30,26 @@ def index(request):
return HttpResponseRedirect(reverse('apps:index'))
-class PackageInstallView(TemplateView):
- """View to prompt and install packages."""
- template_name = 'package_install.html'
+class SetupView(TemplateView):
+ """View to prompt and setup applications."""
+ template_name = 'setup.html'
def get_context_data(self, **kwargs):
"""Return the context data rendering the template."""
- context = super(PackageInstallView, self).get_context_data(**kwargs)
-
- if 'packages_names' not in context:
- context['package_names'] = self.kwargs.get('package_names', [])
- context['packages'] = {
- package_name: package_module.packages_resolved[package_name]
- for package_name in context['package_names']}
- context['is_installing'] = \
- package_module.is_installing(context['package_names'])
- context['transactions'] = package_module.transactions
-
+ context = super(SetupView, self).get_context_data(**kwargs)
+ context['setup_helper'] = self.kwargs['setup_helper']
return context
def post(self, *args, **kwargs):
- """Handle installing packages
+ """Handle installing/upgrading applications.
- Start the package installation, and refresh the page every x seconds to
- keep displaying PackageInstallView.get() with the installation status.
+ Start the application setup, and refresh the page every few
+ seconds to keep displaying the status.
"""
- package_module.start_install(
- self.kwargs['package_names'],
- before_install=self.kwargs.get('before_install'),
- on_install=self.kwargs.get('on_install'))
+ self.kwargs['setup_helper'].run_in_thread()
+
+ # Give a moment for the setup process to start and show
+ # meaningful status.
+ time.sleep(1)
+
return self.render_to_response(self.get_context_data())