mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-02-04 08:13:38 +00:00
Merge branch 'better-setup'
This commit is contained in:
commit
77134cd55b
@ -33,6 +33,7 @@ from cherrypy.process.plugins import Daemonizer
|
||||
from plinth import cfg
|
||||
from plinth import module_loader
|
||||
from plinth import service
|
||||
from plinth import setup
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -57,6 +58,9 @@ def parse_arguments():
|
||||
parser.add_argument(
|
||||
'--no-daemon', action='store_true', default=cfg.no_daemon,
|
||||
help='do not start as a daemon')
|
||||
parser.add_argument(
|
||||
'--setup', action='store_true', default=False,
|
||||
help='run setup tasks on all essential modules and exit')
|
||||
parser.add_argument(
|
||||
'--diagnose', action='store_true', default=False,
|
||||
help='run diagnostic tests and exit')
|
||||
@ -132,9 +136,7 @@ def setup_server():
|
||||
cherrypy.tree.mount(None, manual_url, config)
|
||||
logger.debug('Serving manual images %s on %s', manual_dir, manual_url)
|
||||
|
||||
for module_import_path in module_loader.loaded_modules:
|
||||
module = importlib.import_module(module_import_path)
|
||||
module_name = module_import_path.split('.')[-1]
|
||||
for module_name, module in module_loader.loaded_modules.items():
|
||||
module_path = os.path.dirname(module.__file__)
|
||||
static_dir = os.path.join(module_path, 'static')
|
||||
if not os.path.isdir(static_dir):
|
||||
@ -258,6 +260,7 @@ def configure_django():
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'stronghold.middleware.LoginRequiredMiddleware',
|
||||
'plinth.modules.first_boot.middleware.FirstBootMiddleware',
|
||||
'plinth.middleware.SetupMiddleware',
|
||||
),
|
||||
ROOT_URLCONF='plinth.urls',
|
||||
SECURE_PROXY_SSL_HEADER=secure_proxy_ssl_header,
|
||||
@ -278,6 +281,18 @@ def configure_django():
|
||||
os.chmod(cfg.store_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
|
||||
|
||||
|
||||
def run_setup_and_exit():
|
||||
"""Run setup on all essential modules and exit."""
|
||||
error_code = 0
|
||||
try:
|
||||
setup.setup_all_modules(essential=True)
|
||||
except Exception as exception:
|
||||
logger.error('Error running setup - %s', exception)
|
||||
error_code = 1
|
||||
|
||||
sys.exit(error_code)
|
||||
|
||||
|
||||
def run_diagnostics_and_exit():
|
||||
"""Run diagostics on all modules and exit."""
|
||||
module = importlib.import_module('plinth.modules.diagnostics.diagnostics')
|
||||
@ -315,6 +330,9 @@ def main():
|
||||
|
||||
module_loader.load_modules()
|
||||
|
||||
if arguments.setup:
|
||||
run_setup_and_exit()
|
||||
|
||||
if arguments.diagnose:
|
||||
run_diagnostics_and_exit()
|
||||
|
||||
|
||||
75
plinth/middleware.py
Normal file
75
plinth/middleware.py
Normal file
@ -0,0 +1,75 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Django middleware to show pre-setup message and setup progress.
|
||||
"""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import resolve
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import logging
|
||||
|
||||
import plinth
|
||||
from plinth.package import PackageException
|
||||
from . import views
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SetupMiddleware(object):
|
||||
"""Show setup page or progress if setup is neccessary or running."""
|
||||
|
||||
@staticmethod
|
||||
def process_request(request):
|
||||
"""Handle a request as Django middleware request handler."""
|
||||
# Perform a URL resolution. This is slightly inefficient as
|
||||
# Django will do this resolution again.
|
||||
resolver_match = resolve(request.path_info)
|
||||
if not resolver_match.namespaces or not len(resolver_match.namespaces):
|
||||
# Requested URL does not belong to any application
|
||||
return
|
||||
|
||||
module_name = resolver_match.namespaces[0]
|
||||
module = plinth.module_loader.loaded_modules[module_name]
|
||||
|
||||
# Collect errors from any previous operations and show them
|
||||
if module.setup_helper.is_finished:
|
||||
exception = module.setup_helper.collect_result()
|
||||
if not exception:
|
||||
messages.success(request, _('Application installed.'))
|
||||
else:
|
||||
if isinstance(exception, PackageException):
|
||||
error_string = getattr(exception, 'error_string',
|
||||
str(exception))
|
||||
error_details = getattr(exception, 'error_details', '')
|
||||
message = _('Error installing application: {string} '
|
||||
'{details}').format(
|
||||
string=error_string, details=error_details)
|
||||
else:
|
||||
message = _('Error installing application: {error}') \
|
||||
.format(error=exception)
|
||||
|
||||
messages.error(request, message)
|
||||
|
||||
# Check if application is up-to-date
|
||||
if module.setup_helper.get_state() == 'up-to-date':
|
||||
return
|
||||
|
||||
view = views.SetupView.as_view()
|
||||
return view(request, setup_helper=module.setup_helper)
|
||||
22
plinth/migrations/0002_modulestore.py
Normal file
22
plinth/migrations/0002_modulestore.py
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.2 on 2016-02-10 12:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('plinth', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Module',
|
||||
fields=[
|
||||
('name', models.TextField(primary_key=True, serialize=False)),
|
||||
('setup_version', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -37,3 +37,9 @@ class KVStore(models.Model):
|
||||
def value(self, val):
|
||||
"""Store the value of the key/value pair by JSON encoding it"""
|
||||
self.value_json = json.dumps(val)
|
||||
|
||||
|
||||
class Module(models.Model):
|
||||
"""Model to store current setup versions of a module."""
|
||||
name = models.TextField(primary_key=True)
|
||||
setup_version = models.IntegerField()
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
Discover, load and manage Plinth modules
|
||||
"""
|
||||
|
||||
import collections
|
||||
import django
|
||||
import importlib
|
||||
import logging
|
||||
@ -27,11 +28,12 @@ import re
|
||||
|
||||
from plinth import cfg
|
||||
from plinth import urls
|
||||
from plinth import setup
|
||||
from plinth.signals import pre_module_loading, post_module_loading
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
loaded_modules = []
|
||||
loaded_modules = collections.OrderedDict()
|
||||
_modules_to_load = None
|
||||
|
||||
|
||||
@ -42,16 +44,18 @@ def load_modules():
|
||||
"""
|
||||
pre_module_loading.send_robust(sender="module_loader")
|
||||
modules = {}
|
||||
for module_name in get_modules_to_load():
|
||||
LOGGER.info('Importing %s', module_name)
|
||||
for module_import_path in get_modules_to_load():
|
||||
logger.info('Importing %s', module_import_path)
|
||||
module_name = module_import_path.split('.')[-1]
|
||||
try:
|
||||
modules[module_name] = importlib.import_module(module_name)
|
||||
modules[module_name] = importlib.import_module(module_import_path)
|
||||
except Exception as exception:
|
||||
LOGGER.exception('Could not import %s: %s', module_name, exception)
|
||||
logger.exception('Could not import %s: %s', module_import_path,
|
||||
exception)
|
||||
if cfg.debug:
|
||||
raise
|
||||
|
||||
_include_module_urls(module_name)
|
||||
_include_module_urls(module_import_path, module_name)
|
||||
|
||||
ordered_modules = []
|
||||
remaining_modules = dict(modules) # Make a copy
|
||||
@ -64,14 +68,14 @@ def load_modules():
|
||||
_insert_modules(module_name, module, remaining_modules,
|
||||
ordered_modules)
|
||||
except KeyError:
|
||||
LOGGER.error('Unsatified dependency for module - %s',
|
||||
logger.error('Unsatified dependency for module - %s',
|
||||
module_name)
|
||||
|
||||
LOGGER.debug('Module load order - %s', ordered_modules)
|
||||
logger.debug('Module load order - %s', ordered_modules)
|
||||
|
||||
for module_name in ordered_modules:
|
||||
_initialize_module(modules[module_name])
|
||||
loaded_modules.append(module_name)
|
||||
_initialize_module(module_name, modules[module_name])
|
||||
loaded_modules[module_name] = modules[module_name]
|
||||
|
||||
post_module_loading.send_robust(sender="module_loader")
|
||||
|
||||
@ -94,7 +98,7 @@ def _insert_modules(module_name, module, remaining_modules, ordered_modules):
|
||||
try:
|
||||
module = remaining_modules.pop(dependency)
|
||||
except KeyError:
|
||||
LOGGER.error('Not found or circular dependency - %s, %s',
|
||||
logger.error('Not found or circular dependency - %s, %s',
|
||||
module_name, dependency)
|
||||
raise
|
||||
|
||||
@ -103,32 +107,34 @@ def _insert_modules(module_name, module, remaining_modules, ordered_modules):
|
||||
ordered_modules.append(module_name)
|
||||
|
||||
|
||||
def _include_module_urls(module_name):
|
||||
def _include_module_urls(module_import_path, module_name):
|
||||
"""Include the module's URLs in global project URLs list"""
|
||||
namespace = module_name.split('.')[-1]
|
||||
url_module = module_name + '.urls'
|
||||
url_module = module_import_path + '.urls'
|
||||
try:
|
||||
urls.urlpatterns += [
|
||||
django.conf.urls.url(
|
||||
r'', django.conf.urls.include(url_module, namespace))]
|
||||
r'', django.conf.urls.include(url_module, module_name))]
|
||||
except ImportError:
|
||||
LOGGER.debug('No URLs for %s', module_name)
|
||||
logger.debug('No URLs for %s', module_name)
|
||||
if cfg.debug:
|
||||
raise
|
||||
|
||||
|
||||
def _initialize_module(module):
|
||||
def _initialize_module(module_name, module):
|
||||
"""Call initialization method in the module if it exists"""
|
||||
# Perform setup related initialization on the module
|
||||
setup.init(module_name, module)
|
||||
|
||||
try:
|
||||
init = module.init
|
||||
except AttributeError:
|
||||
LOGGER.debug('No init() for module - %s', module.__name__)
|
||||
logger.debug('No init() for module - %s', module.__name__)
|
||||
return
|
||||
|
||||
try:
|
||||
init()
|
||||
except Exception as exception:
|
||||
LOGGER.exception('Exception while running init for %s: %s',
|
||||
logger.exception('Exception while running init for %s: %s',
|
||||
module, exception)
|
||||
if cfg.debug:
|
||||
raise
|
||||
|
||||
@ -23,3 +23,7 @@ from . import apps
|
||||
from .apps import init
|
||||
|
||||
__all__ = ['apps', 'init']
|
||||
|
||||
version = 1
|
||||
|
||||
is_essential = 1
|
||||
|
||||
@ -20,16 +20,32 @@ Plinth module for service discovery.
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import subprocess
|
||||
|
||||
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
|
||||
|
||||
# pylint: disable=C0103
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['system']
|
||||
|
||||
title = _('Service Discovery')
|
||||
|
||||
description = [
|
||||
format_lazy(
|
||||
_('Service discovery allows other devices on the network to '
|
||||
'discover your {{ box_name }} and services running on it. It '
|
||||
'also allows {{ box_name }} to discover other devices and '
|
||||
'services running on your local network. Service discovery is '
|
||||
'not essential and works only on internal networks. It may be '
|
||||
'disabled to improve security especially when connecting to a '
|
||||
'hostile local network.'), box_name=_(cfg.box_name))
|
||||
]
|
||||
|
||||
service = None
|
||||
|
||||
@ -37,13 +53,16 @@ service = None
|
||||
def init():
|
||||
"""Intialize the service discovery module."""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Service Discovery'), 'glyphicon-lamp',
|
||||
'avahi:index', 950)
|
||||
menu.add_urlname(title, 'glyphicon-lamp', 'avahi:index', 950)
|
||||
|
||||
global service # pylint: disable=W0603
|
||||
service = service_module.Service(
|
||||
'avahi', _('Service Discovery'), ['mdns'],
|
||||
is_external=False, enabled=is_enabled())
|
||||
'avahi', title, ['mdns'], is_external=False, enabled=is_enabled())
|
||||
|
||||
|
||||
def setup(helper, old_version=False):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['avahi-daemon'])
|
||||
|
||||
|
||||
def is_enabled():
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "Service Discovery" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Service discovery allows other devices on the network to
|
||||
discover your {{ box_name }} and services running on it. It
|
||||
also allows {{ box_name }} to discover other devices and
|
||||
services running on your local network. Service discovery is
|
||||
not essential and works only on internal networks. It may be
|
||||
disabled to improve security especially when connecting to a
|
||||
hostile local network.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -26,14 +26,12 @@ import logging
|
||||
|
||||
from .forms import ServiceDiscoveryForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import avahi
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__) # pylint: disable=C0103
|
||||
|
||||
|
||||
@package.required(['avahi-daemon'])
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
@ -50,7 +48,8 @@ def index(request):
|
||||
form = ServiceDiscoveryForm(initial=status, prefix='avahi')
|
||||
|
||||
return TemplateResponse(request, 'avahi.html',
|
||||
{'title': _('Service Discovery'),
|
||||
{'title': avahi.title,
|
||||
'description': avahi.description,
|
||||
'status': status,
|
||||
'form': form})
|
||||
|
||||
|
||||
@ -24,6 +24,8 @@ from .config import init
|
||||
|
||||
__all__ = ['config', 'init']
|
||||
|
||||
depends = ['plinth.modules.system',
|
||||
'plinth.modules.firewall',
|
||||
'plinth.modules.names']
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['system', 'firewall', 'names']
|
||||
|
||||
@ -33,7 +33,7 @@ import socket
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth.modules.firewall import firewall
|
||||
from plinth.modules import firewall
|
||||
from plinth.modules.names import SERVICES
|
||||
from plinth.signals import pre_hostname_change, post_hostname_change
|
||||
from plinth.signals import domainname_change
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
URLs for the Configuration module
|
||||
"""
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import config as views
|
||||
|
||||
|
||||
@ -22,13 +22,23 @@ Plinth module to configure system date and time
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import subprocess
|
||||
|
||||
from plinth import actions
|
||||
from plinth import action_utils
|
||||
from plinth import cfg
|
||||
from plinth import service as service_module
|
||||
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['system']
|
||||
|
||||
title = _('Date & Time')
|
||||
|
||||
description = [
|
||||
_('Network time server is a program that maintians the system time '
|
||||
'in synchronization with servers on the Internet.')
|
||||
]
|
||||
|
||||
service = None
|
||||
|
||||
@ -36,13 +46,17 @@ service = None
|
||||
def init():
|
||||
"""Intialize the date/time module."""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Date & Time'), 'glyphicon-time',
|
||||
'datetime:index', 900)
|
||||
menu.add_urlname(title, 'glyphicon-time', 'datetime:index', 900)
|
||||
|
||||
global service
|
||||
service = service_module.Service(
|
||||
'ntp', _('Network Time Server'),
|
||||
is_external=False, enabled=is_enabled())
|
||||
'ntp', title, is_external=False, enabled=is_enabled())
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['ntp'])
|
||||
helper.call('post', service.notify_enabled, None, True)
|
||||
|
||||
|
||||
def is_enabled():
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -21,16 +21,7 @@
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Date & Time" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Network time server is a program that maintians the system time
|
||||
in synchronization with servers on the Internet.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -26,18 +26,11 @@ import logging
|
||||
|
||||
from .forms import DateTimeForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import datetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def on_install():
|
||||
"""Notify that the service is now enabled."""
|
||||
datetime.service.notify_enabled(None, True)
|
||||
|
||||
|
||||
@package.required(['ntp'], on_install=on_install)
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
@ -55,7 +48,8 @@ def index(request):
|
||||
form = DateTimeForm(initial=status, prefix='datetime')
|
||||
|
||||
return TemplateResponse(request, 'datetime.html',
|
||||
{'title': _('Date & Time'),
|
||||
{'title': datetime.title,
|
||||
'description': datetime.description,
|
||||
'status': status,
|
||||
'form': form})
|
||||
|
||||
|
||||
@ -27,7 +27,20 @@ from plinth import cfg
|
||||
from plinth import service as service_module
|
||||
|
||||
|
||||
depends = ['plinth.modules.apps']
|
||||
version = 1
|
||||
|
||||
depends = ['apps']
|
||||
|
||||
title = _('BitTorrent Web Client (Deluge)')
|
||||
|
||||
description = [
|
||||
_('Deluge is a BitTorrent client that features a Web UI.'),
|
||||
|
||||
_('When enabled, the Deluge web client will be available from '
|
||||
'<a href="/deluge">/deluge</a> path on the web server. The '
|
||||
'default password is \'deluge\', but you should log in and change '
|
||||
'it immediately after enabling this service.')
|
||||
]
|
||||
|
||||
service = None
|
||||
|
||||
@ -35,13 +48,19 @@ service = None
|
||||
def init():
|
||||
"""Initialize the Deluge module."""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(_('BitTorrent (Deluge)'), 'glyphicon-magnet',
|
||||
'deluge:index', 200)
|
||||
menu.add_urlname(title, 'glyphicon-magnet', 'deluge:index', 200)
|
||||
|
||||
global service
|
||||
service = service_module.Service(
|
||||
'deluge', _('Deluge BitTorrent'), ['http', 'https'],
|
||||
is_external=True, enabled=is_enabled())
|
||||
'deluge', title, ['http', 'https'], is_external=True,
|
||||
enabled=is_enabled())
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['deluged', 'deluge-web'])
|
||||
helper.call('post', actions.superuser_run, 'deluge', ['enable'])
|
||||
helper.call('post', service.notify_enabled, None, True)
|
||||
|
||||
|
||||
def is_enabled():
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -21,20 +21,7 @@
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "BitTorrent Web Client (Deluge)" %}</h2>
|
||||
|
||||
<p>{% trans "Deluge is a BitTorrent client that features a Web UI." %}</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
When enabled, the Deluge web client will be available from
|
||||
<a href="/deluge">/deluge</a> path on the web server. The
|
||||
default password is 'deluge', but you should log in and change
|
||||
it immediately after enabling this service.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -25,17 +25,9 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from .forms import DelugeForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import deluge
|
||||
|
||||
|
||||
def on_install():
|
||||
"""Tasks to run after package install."""
|
||||
actions.superuser_run('deluge', ['enable'])
|
||||
deluge.service.notify_enabled(None, True)
|
||||
|
||||
|
||||
@package.required(['deluged', 'deluge-web'], on_install=on_install)
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
@ -53,7 +45,8 @@ def index(request):
|
||||
form = DelugeForm(initial=status, prefix='deluge')
|
||||
|
||||
return TemplateResponse(request, 'deluge.html',
|
||||
{'title': _('BitTorrent (Deluge)'),
|
||||
{'title': deluge.title,
|
||||
'description': deluge.description,
|
||||
'status': status,
|
||||
'form': form})
|
||||
|
||||
|
||||
@ -19,13 +19,30 @@
|
||||
Plinth module for system diagnostics
|
||||
"""
|
||||
|
||||
from . import diagnostics
|
||||
from .diagnostics import init
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth import cfg
|
||||
|
||||
__all__ = ['diagnostics', 'init']
|
||||
version = 1
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
is_essential = True
|
||||
|
||||
title = _('Diagnostics')
|
||||
|
||||
description = [
|
||||
_('The system diagnostic test will run a number of checks on your '
|
||||
'system to confirm that applications and services are working as '
|
||||
'expected.')
|
||||
]
|
||||
|
||||
depends = ['system']
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize the module"""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(title, 'glyphicon-screenshot', 'diagnostics:index', 30)
|
||||
|
||||
|
||||
def diagnose():
|
||||
|
||||
@ -24,12 +24,11 @@ from django.http import Http404
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import importlib
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from plinth import cfg
|
||||
from plinth import module_loader
|
||||
from plinth.modules import diagnostics
|
||||
|
||||
|
||||
logger = logging.Logger(__name__)
|
||||
@ -39,20 +38,14 @@ current_results = {}
|
||||
_running_task = None
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize the module"""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Diagnostics'), 'glyphicon-screenshot',
|
||||
'diagnostics:index', 30)
|
||||
|
||||
|
||||
def index(request):
|
||||
"""Serve the index page"""
|
||||
if request.method == 'POST' and not _running_task:
|
||||
_start_task()
|
||||
|
||||
return TemplateResponse(request, 'diagnostics.html',
|
||||
{'title': _('System Diagnostics'),
|
||||
{'title': diagnostics.title,
|
||||
'description': diagnostics.description,
|
||||
'is_running': _running_task is not None,
|
||||
'results': current_results})
|
||||
|
||||
@ -60,19 +53,14 @@ def index(request):
|
||||
@require_POST
|
||||
def module(request, module_name):
|
||||
"""Return diagnostics for a particular module."""
|
||||
found = False
|
||||
for module_import_path in module_loader.loaded_modules:
|
||||
if module_name == module_import_path.split('.')[-1]:
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
try:
|
||||
module = module_loader.loaded_modules[module_name]
|
||||
except KeyError:
|
||||
raise Http404('Module does not exist or not loaded')
|
||||
|
||||
loaded_module = importlib.import_module(module_import_path)
|
||||
results = []
|
||||
if hasattr(loaded_module, 'diagnose'):
|
||||
results = loaded_module.diagnose()
|
||||
if hasattr(module, 'diagnose'):
|
||||
results = module.diagnose()
|
||||
|
||||
return TemplateResponse(request, 'diagnostics_module.html',
|
||||
{'title': _('Diagnostic Test'),
|
||||
@ -110,17 +98,15 @@ def run_on_all_modules():
|
||||
'progress_percentage': 0}
|
||||
|
||||
modules = []
|
||||
for module_import_path in module_loader.loaded_modules:
|
||||
loaded_module = importlib.import_module(module_import_path)
|
||||
if not hasattr(loaded_module, 'diagnose'):
|
||||
for module_name, module in module_loader.loaded_modules.items():
|
||||
if not hasattr(module, 'diagnose'):
|
||||
continue
|
||||
|
||||
module_name = module_import_path.split('.')[-1]
|
||||
modules.append((module_name, loaded_module))
|
||||
modules.append((module_name, module))
|
||||
current_results['results'][module_name] = None
|
||||
|
||||
current_results['modules'] = modules
|
||||
for current_index, (module_name, loaded_module) in enumerate(modules):
|
||||
current_results['results'][module_name] = loaded_module.diagnose()
|
||||
for current_index, (module_name, module) in enumerate(modules):
|
||||
current_results['results'][module_name] = module.diagnose()
|
||||
current_results['progress_percentage'] = \
|
||||
int((current_index + 1) * 100 / len(modules))
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends 'app.html' %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -29,17 +29,7 @@
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The system diagnostic test will run a number of checks on your
|
||||
system to confirm that applications and services are working as
|
||||
expected.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
{% if not is_running %}
|
||||
<form class="form form-diagnostics-button" method="post"
|
||||
|
||||
@ -19,9 +19,42 @@
|
||||
Plinth module to configure ez-ipupdate client
|
||||
"""
|
||||
|
||||
from . import dynamicdns
|
||||
from .dynamicdns import init
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
__all__ = ['dynamicdns', 'init']
|
||||
from plinth import cfg
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
depends = ['plinth.modules.apps']
|
||||
version = 1
|
||||
|
||||
depends = ['apps']
|
||||
|
||||
title = _('Dynamic DNS Client')
|
||||
|
||||
description = [
|
||||
format_lazy(
|
||||
_('If your internet provider changes your IP address periodic '
|
||||
'(i.e. every 24h) it may be hard for others to find you in the '
|
||||
'WEB. And for this reason nobody may find the services which are '
|
||||
'provided by {box_name}, such as ownCloud.'),
|
||||
box_name=_(cfg.box_name)),
|
||||
|
||||
_('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 an '
|
||||
'<a href=\'http://gnudip2.sourceforge.net/\' target=\'_blank\'> '
|
||||
'gnudip </a> server. Afterwards the Server will assign your DNS name '
|
||||
'with the new IP and if someone from the Internet asks for your DNS '
|
||||
'name he will get your current IP answered.')
|
||||
]
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize the module."""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(title, 'glyphicon-refresh', 'dynamicdns:index', 500)
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['ez-ipupdate'])
|
||||
|
||||
@ -25,7 +25,7 @@ import logging
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
from plinth.modules import dynamicdns
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -39,19 +39,11 @@ subsubmenu = [{'url': reverse_lazy('dynamicdns:index'),
|
||||
'text': ugettext_lazy('Status')}]
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize the dynamicdns module"""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(ugettext_lazy('Dynamic DNS'), 'glyphicon-refresh',
|
||||
'dynamicdns:index', 500)
|
||||
|
||||
|
||||
@package.required(['ez-ipupdate'])
|
||||
def index(request):
|
||||
"""Serve Dynamic DNS page."""
|
||||
|
||||
return TemplateResponse(request, 'dynamicdns.html',
|
||||
{'title': _('Dynamic DNS'),
|
||||
{'title': dynamicdns.title,
|
||||
'description': dynamicdns.description,
|
||||
'subsubmenu': subsubmenu})
|
||||
|
||||
|
||||
@ -198,7 +190,6 @@ class ConfigureForm(forms.Form):
|
||||
raise forms.ValidationError(_('Please provide a password'))
|
||||
|
||||
|
||||
@package.required(['ez-ipupdate'])
|
||||
def configure(request):
|
||||
"""Serve the configuration form."""
|
||||
status = get_status()
|
||||
@ -219,7 +210,6 @@ def configure(request):
|
||||
'subsubmenu': subsubmenu})
|
||||
|
||||
|
||||
@package.required(['ez-ipupdate'])
|
||||
def statuspage(request):
|
||||
"""Serve the status page."""
|
||||
check_nat = actions.run('dynamicdns', ['get-nat'])
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -20,32 +20,7 @@
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Dynamic DNS Client" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
If your internet provider changes your IP address periodic
|
||||
(i.e. every 24h) it may be hard for others to find you in the
|
||||
WEB. And for this reason nobody may find the services which are
|
||||
provided by {{ box_name }}, such as ownCloud.
|
||||
{% 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 an
|
||||
<a href='http://gnudip2.sourceforge.net/' target='_blank'> gnudip </a>
|
||||
server. Afterwards the Server will assign your DNS name with the
|
||||
new IP and if someone from the Internet asks for your DNS name
|
||||
he will get your current IP answered.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% block configuration %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
If you are looking for a free dynamic DNS account, you may find
|
||||
|
||||
@ -19,9 +19,119 @@
|
||||
Plinth module to configure a firewall
|
||||
"""
|
||||
|
||||
from . import firewall
|
||||
from .firewall import init
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import logging
|
||||
|
||||
__all__ = ['firewall', 'init']
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth.signals import service_enabled
|
||||
import plinth.service as service_module
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['system']
|
||||
|
||||
title = _('Firewall')
|
||||
|
||||
description = [
|
||||
format_lazy(
|
||||
_('Firewall is a security system that controls the incoming and '
|
||||
'outgoing network traffic on your {box_name}. Keeping a '
|
||||
'firewall enabled and properly configured reduces risk of '
|
||||
'security threat from the Internet.'), box_name=cfg.box_name)
|
||||
]
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def init():
|
||||
"""Initailze firewall module"""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(title, 'glyphicon-fire', 'firewall:index', 50)
|
||||
|
||||
service_enabled.connect(on_service_enabled)
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['firewalld'])
|
||||
|
||||
|
||||
def get_enabled_status():
|
||||
"""Return whether firewall is enabled"""
|
||||
output = _run(['get-status'], superuser=True)
|
||||
return output.split()[0] == 'running'
|
||||
|
||||
|
||||
def get_enabled_services(zone):
|
||||
"""Return the status of various services currently enabled"""
|
||||
output = _run(['get-enabled-services', '--zone', zone], superuser=True)
|
||||
return output.split()
|
||||
|
||||
|
||||
def add_service(port, zone):
|
||||
"""Enable a service in firewall"""
|
||||
_run(['add-service', port, '--zone', zone], superuser=True)
|
||||
|
||||
|
||||
def remove_service(port, zone):
|
||||
"""Remove a service in firewall"""
|
||||
_run(['remove-service', port, '--zone', zone], superuser=True)
|
||||
|
||||
|
||||
def on_service_enabled(sender, service_id, enabled, **kwargs):
|
||||
"""
|
||||
Enable/disable firewall ports when a service is
|
||||
enabled/disabled.
|
||||
"""
|
||||
del sender # Unused
|
||||
del kwargs # Unused
|
||||
|
||||
internal_enabled_services = get_enabled_services(zone='internal')
|
||||
external_enabled_services = get_enabled_services(zone='external')
|
||||
|
||||
LOGGER.info('Service enabled - %s, %s', service_id, enabled)
|
||||
service = service_module.services[service_id]
|
||||
for port in service.ports:
|
||||
if enabled:
|
||||
if port not in internal_enabled_services:
|
||||
add_service(port, zone='internal')
|
||||
|
||||
if (service.is_external and
|
||||
port not in external_enabled_services):
|
||||
add_service(port, zone='external')
|
||||
else:
|
||||
# service already configured.
|
||||
pass
|
||||
else:
|
||||
if port in internal_enabled_services:
|
||||
enabled_services_on_port = [
|
||||
service_.is_enabled()
|
||||
for service_ in service_module.services.values()
|
||||
if port in service_.ports and
|
||||
service_id != service_.service_id]
|
||||
if not any(enabled_services_on_port):
|
||||
remove_service(port, zone='internal')
|
||||
|
||||
if port in external_enabled_services:
|
||||
enabled_services_on_port = [
|
||||
service_.is_enabled()
|
||||
for service_ in service_module.services.values()
|
||||
if port in service_.ports and
|
||||
service_id != service_.service_id and
|
||||
service_.is_external]
|
||||
if not any(enabled_services_on_port):
|
||||
remove_service(port, zone='external')
|
||||
|
||||
|
||||
def _run(arguments, superuser=False):
|
||||
"""Run an given command and raise exception if there was an error"""
|
||||
command = 'firewall'
|
||||
|
||||
if superuser:
|
||||
return actions.superuser_run(command, arguments)
|
||||
else:
|
||||
return actions.run(command, arguments)
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Plinth module to configure a firewall
|
||||
"""
|
||||
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import logging
|
||||
|
||||
from plinth import actions
|
||||
from plinth import cfg
|
||||
from plinth import package
|
||||
from plinth.signals import service_enabled
|
||||
import plinth.service as service_module
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def init():
|
||||
"""Initailze firewall module"""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Firewall'), 'glyphicon-fire', 'firewall:index', 50)
|
||||
|
||||
service_enabled.connect(on_service_enabled)
|
||||
|
||||
|
||||
@package.required(['firewalld'])
|
||||
def index(request):
|
||||
"""Serve introcution page"""
|
||||
if not get_enabled_status():
|
||||
return TemplateResponse(request, 'firewall.html',
|
||||
{'title': _('Firewall'),
|
||||
'firewall_status': 'not_running'})
|
||||
|
||||
internal_enabled_services = get_enabled_services(zone='internal')
|
||||
external_enabled_services = get_enabled_services(zone='external')
|
||||
|
||||
return TemplateResponse(
|
||||
request, 'firewall.html',
|
||||
{'title': _('Firewall'),
|
||||
'services': list(service_module.services.values()),
|
||||
'internal_enabled_services': internal_enabled_services,
|
||||
'external_enabled_services': external_enabled_services})
|
||||
|
||||
|
||||
def get_enabled_status():
|
||||
"""Return whether firewall is enabled"""
|
||||
output = _run(['get-status'], superuser=True)
|
||||
return output.split()[0] == 'running'
|
||||
|
||||
|
||||
def get_enabled_services(zone):
|
||||
"""Return the status of various services currently enabled"""
|
||||
output = _run(['get-enabled-services', '--zone', zone], superuser=True)
|
||||
return output.split()
|
||||
|
||||
|
||||
def add_service(port, zone):
|
||||
"""Enable a service in firewall"""
|
||||
_run(['add-service', port, '--zone', zone], superuser=True)
|
||||
|
||||
|
||||
def remove_service(port, zone):
|
||||
"""Remove a service in firewall"""
|
||||
_run(['remove-service', port, '--zone', zone], superuser=True)
|
||||
|
||||
|
||||
def on_service_enabled(sender, service_id, enabled, **kwargs):
|
||||
"""
|
||||
Enable/disable firewall ports when a service is
|
||||
enabled/disabled.
|
||||
"""
|
||||
del sender # Unused
|
||||
del kwargs # Unused
|
||||
|
||||
internal_enabled_services = get_enabled_services(zone='internal')
|
||||
external_enabled_services = get_enabled_services(zone='external')
|
||||
|
||||
LOGGER.info('Service enabled - %s, %s', service_id, enabled)
|
||||
service = service_module.services[service_id]
|
||||
for port in service.ports:
|
||||
if enabled:
|
||||
if port not in internal_enabled_services:
|
||||
add_service(port, zone='internal')
|
||||
|
||||
if (service.is_external and
|
||||
port not in external_enabled_services):
|
||||
add_service(port, zone='external')
|
||||
else:
|
||||
# service already configured.
|
||||
pass
|
||||
else:
|
||||
if port in internal_enabled_services:
|
||||
enabled_services_on_port = [
|
||||
service_.is_enabled()
|
||||
for service_ in service_module.services.values()
|
||||
if port in service_.ports and
|
||||
service_id != service_.service_id]
|
||||
if not any(enabled_services_on_port):
|
||||
remove_service(port, zone='internal')
|
||||
|
||||
if port in external_enabled_services:
|
||||
enabled_services_on_port = [
|
||||
service_.is_enabled()
|
||||
for service_ in service_module.services.values()
|
||||
if port in service_.ports and
|
||||
service_id != service_.service_id and
|
||||
service_.is_external]
|
||||
if not any(enabled_services_on_port):
|
||||
remove_service(port, zone='external')
|
||||
|
||||
|
||||
def _run(arguments, superuser=False):
|
||||
"""Run an given command and raise exception if there was an error"""
|
||||
command = 'firewall'
|
||||
|
||||
if superuser:
|
||||
return actions.superuser_run(command, arguments)
|
||||
else:
|
||||
return actions.run(command, arguments)
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -20,18 +20,7 @@
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Firewall is a security system that controls the incoming and
|
||||
outgoing network traffic on your {{ box_name }}. Keeping a
|
||||
firewall enabled and properly configured reduces risk of
|
||||
security threat from the Internet.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<p>{% trans "Current status:" %}</p>
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ URLs for the Firewall module
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import firewall as views
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
45
plinth/modules/firewall/views.py
Normal file
45
plinth/modules/firewall/views.py
Normal file
@ -0,0 +1,45 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Plinth module to configure a firewall
|
||||
"""
|
||||
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
from plinth.modules import firewall
|
||||
import plinth.service as service_module
|
||||
|
||||
|
||||
def index(request):
|
||||
"""Serve introcution page"""
|
||||
if not firewall.get_enabled_status():
|
||||
return TemplateResponse(request, 'firewall.html',
|
||||
{'title': firewall.title,
|
||||
'description': firewall.description,
|
||||
'firewall_status': 'not_running'})
|
||||
|
||||
internal_enabled_services = firewall.get_enabled_services(zone='internal')
|
||||
external_enabled_services = firewall.get_enabled_services(zone='external')
|
||||
|
||||
return TemplateResponse(
|
||||
request, 'firewall.html',
|
||||
{'title': firewall.title,
|
||||
'description': firewall.description,
|
||||
'services': list(service_module.services.values()),
|
||||
'internal_enabled_services': internal_enabled_services,
|
||||
'external_enabled_services': external_enabled_services})
|
||||
@ -18,3 +18,7 @@
|
||||
"""
|
||||
Plinth module for first boot wizard
|
||||
"""
|
||||
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
@ -27,7 +27,16 @@ from plinth import cfg
|
||||
from plinth import service as service_module
|
||||
|
||||
|
||||
depends = ['plinth.modules.apps']
|
||||
version = 1
|
||||
|
||||
depends = ['apps']
|
||||
|
||||
title = _('Wiki and Blog (ikiwiki)')
|
||||
|
||||
description = [
|
||||
_('When enabled, the blogs and wikis will be available '
|
||||
'from <a href="/ikiwiki">/ikiwiki</a>.')
|
||||
]
|
||||
|
||||
service = None
|
||||
|
||||
@ -35,13 +44,25 @@ service = None
|
||||
def init():
|
||||
"""Initialize the ikiwiki module."""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(_('Wiki and Blog (ikiwiki)'), 'glyphicon-edit',
|
||||
'ikiwiki:index', 1100)
|
||||
menu.add_urlname(title, 'glyphicon-edit', 'ikiwiki:index', 1100)
|
||||
|
||||
global service
|
||||
service = service_module.Service(
|
||||
'ikiwiki', _('ikiwiki wikis and blogs'), ['http', 'https'],
|
||||
is_external=True, enabled=is_enabled())
|
||||
'ikiwiki', title, ['http', 'https'], is_external=True,
|
||||
enabled=is_enabled())
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['ikiwiki',
|
||||
'gcc',
|
||||
'libc6-dev',
|
||||
'libtimedate-perl',
|
||||
'libcgi-formbuilder-perl',
|
||||
'libcgi-session-perl',
|
||||
'libxml-writer-perl'])
|
||||
helper.call('post', actions.superuser_run, 'ikiwiki', ['setup'])
|
||||
helper.call('post', service.notify_enabled, None, True)
|
||||
|
||||
|
||||
def is_enabled():
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -21,14 +21,7 @@
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
When enabled, the blogs and wikis will be available
|
||||
from <a href="/ikiwiki">/ikiwiki</a>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
{% include "diagnostics_button.html" with module="ikiwiki" %}
|
||||
|
||||
|
||||
@ -27,8 +27,6 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from .forms import IkiwikiForm, IkiwikiCreateForm
|
||||
from plinth import actions
|
||||
from plinth import action_utils
|
||||
from plinth import package
|
||||
from plinth.modules import ikiwiki
|
||||
|
||||
|
||||
@ -40,20 +38,6 @@ subsubmenu = [{'url': reverse_lazy('ikiwiki:index'),
|
||||
'text': ugettext_lazy('Create')}]
|
||||
|
||||
|
||||
def on_install():
|
||||
"""Enable ikiwiki on install."""
|
||||
actions.superuser_run('ikiwiki', ['setup'])
|
||||
ikiwiki.service.notify_enabled(None, True)
|
||||
|
||||
|
||||
@package.required(['ikiwiki',
|
||||
'gcc',
|
||||
'libc6-dev',
|
||||
'libtimedate-perl',
|
||||
'libcgi-formbuilder-perl',
|
||||
'libcgi-session-perl',
|
||||
'libxml-writer-perl'],
|
||||
on_install=on_install)
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
@ -70,7 +54,8 @@ def index(request):
|
||||
form = IkiwikiForm(initial=status, prefix='ikiwiki')
|
||||
|
||||
return TemplateResponse(request, 'ikiwiki.html',
|
||||
{'title': _('Wiki and Blog'),
|
||||
{'title': ikiwiki.title,
|
||||
'description': ikiwiki.description,
|
||||
'status': status,
|
||||
'form': form,
|
||||
'subsubmenu': subsubmenu})
|
||||
|
||||
@ -20,20 +20,38 @@ Plinth module for using Let's Encrypt.
|
||||
"""
|
||||
|
||||
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.modules import names
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
|
||||
depends = [
|
||||
'plinth.modules.apps',
|
||||
'plinth.modules.names'
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['apps', 'names']
|
||||
|
||||
title = _('Certificates (Let\'s Encrypt)')
|
||||
|
||||
description = [
|
||||
format_lazy(
|
||||
_('A digital certficate allows users of a web service to verify the '
|
||||
'identity of the service and to securely communicate with it. '
|
||||
'{box_name} can automatically obtain and setup digital '
|
||||
'certificates for each available domain. It does so by proving '
|
||||
'itself to be the owner of a domain to Let\'s Encrypt, a '
|
||||
'certficate authority (CA).'), box_name=_(cfg.box_name)),
|
||||
|
||||
_('Let\'s Encrypt is a free, automated, and open certificate '
|
||||
'authority, run for the public’s benefit by the Internet Security '
|
||||
'Research Group (ISRG). Please read and agree with the '
|
||||
'<a href="https://letsencrypt.org/repository/">Let\'s Encrypt '
|
||||
'Subscriber Agreement</a> before using this service.')
|
||||
]
|
||||
|
||||
|
||||
service = None
|
||||
|
||||
|
||||
@ -44,6 +62,11 @@ def init():
|
||||
'glyphicon-lock', 'letsencrypt:index', 20)
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['letsencrypt'])
|
||||
|
||||
|
||||
def diagnose():
|
||||
"""Run diagnostics and return the results."""
|
||||
results = []
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -33,30 +33,7 @@
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Certificates (Let's Encrypt)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
A digital certficate allows users of a web service to verify the
|
||||
identity of the service and to securely communicate with it.
|
||||
{{ box_name }} can automatically obtain and setup digital
|
||||
certificates for each available domain. It does so by proving
|
||||
itself to be the owner of a domain to Let's Encrypt, a
|
||||
certficate authority (CA).
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Let's Encrypt is a free, automated, and open certificate
|
||||
authority, run for the public’s benefit by the Internet Security
|
||||
Research Group (ISRG). Please read and agree with the
|
||||
<a href="https://letsencrypt.org/repository/">Let's Encrypt
|
||||
Subscriber Agreement</a> before using this service.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
|
||||
@ -29,20 +29,20 @@ import json
|
||||
import logging
|
||||
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.errors import ActionError
|
||||
from plinth.modules import letsencrypt
|
||||
from plinth.modules import names
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@package.required(['letsencrypt'])
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
|
||||
return TemplateResponse(request, 'letsencrypt.html',
|
||||
{'title': _('Certificates (Let\'s Encrypt)'),
|
||||
{'title': letsencrypt.title,
|
||||
'description': letsencrypt.description,
|
||||
'status': status})
|
||||
|
||||
|
||||
|
||||
@ -23,7 +23,22 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from plinth import cfg
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
version = 1
|
||||
|
||||
depends = ['system']
|
||||
|
||||
title = _('Monkeysphere')
|
||||
|
||||
description = [
|
||||
_('With Monkeysphere, a PGP key can be generated for each configured '
|
||||
'domain serving SSH. The PGP public key can then be uploaded to the PGP '
|
||||
'keyservers. Users connecting to this machine through SSH can verify '
|
||||
'that they are connecting to the correct host. For users to trust the '
|
||||
'key, at least one person (usually the machine owner) must sign the key '
|
||||
'using the regular PGP key signing process. See the '
|
||||
'<a href="http://web.monkeysphere.info/getting-started-ssh/"> '
|
||||
'Monkeysphere SSH documentation</a> for more details.')
|
||||
]
|
||||
|
||||
|
||||
def init():
|
||||
@ -31,3 +46,8 @@ def init():
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Monkeysphere'), 'glyphicon-certificate',
|
||||
'monkeysphere:index', 970)
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['monkeysphere'])
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -30,22 +30,7 @@
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Monkeysphere" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
With Monkeysphere, a PGP key can be generated for each configured domain
|
||||
serving SSH. The PGP public key can then be uploaded to the PGP
|
||||
keyservers. Users connecting to this machine through SSH can verify that
|
||||
they are connecting to the correct host. For users to trust the key, at
|
||||
least one person (usually the machine owner) must sign the key using the
|
||||
regular PGP key signing process. See the
|
||||
<a href="http://web.monkeysphere.info/getting-started-ssh/">
|
||||
Monkeysphere SSH documentation</a> for more details.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
{% if running %}
|
||||
<p class="running-status-parent">
|
||||
|
||||
@ -28,20 +28,20 @@ from django.views.decorators.http import require_POST
|
||||
import json
|
||||
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import monkeysphere
|
||||
from plinth.modules import names
|
||||
|
||||
publish_process = None
|
||||
|
||||
|
||||
@package.required(['monkeysphere'])
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
_collect_publish_result(request)
|
||||
status = get_status()
|
||||
return TemplateResponse(
|
||||
request, 'monkeysphere.html',
|
||||
{'title': _('Monkeysphere'),
|
||||
{'title': monkeysphere.title,
|
||||
'description': monkeysphere.description,
|
||||
'status': status,
|
||||
'running': bool(publish_process)})
|
||||
|
||||
|
||||
@ -21,13 +21,25 @@ Plinth module to configure Mumble server
|
||||
|
||||
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 = _('Voice Chat (Mumble)')
|
||||
|
||||
description = [
|
||||
_('Mumble is an open source, low-latency, encrypted, high quality '
|
||||
'voice chat software.'),
|
||||
|
||||
_('You can connect to your Mumble server on the regular Mumble port '
|
||||
'64738. <a href="http://mumble.info">Clients</a> to connect to Mumble '
|
||||
'from your desktop and Android devices are available.')
|
||||
]
|
||||
|
||||
service = None
|
||||
|
||||
@ -35,13 +47,17 @@ service = None
|
||||
def init():
|
||||
"""Intialize the Mumble module."""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(_('Voice Chat (Mumble)'), 'glyphicon-headphones',
|
||||
'mumble:index', 900)
|
||||
menu.add_urlname(title, 'glyphicon-headphones', 'mumble:index', 900)
|
||||
|
||||
global service
|
||||
service = service_module.Service(
|
||||
'mumble-plinth', _('Mumble Voice Chat Server'),
|
||||
is_external=True, enabled=is_enabled())
|
||||
'mumble-plinth', title, is_external=True, enabled=is_enabled())
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(['mumble-server'])
|
||||
helper.call('post', service.notify_enabled, None, True)
|
||||
|
||||
|
||||
def is_enabled():
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -21,25 +21,7 @@
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Voice Chat (Mumble)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Mumble is an open source, low-latency, encrypted, high quality
|
||||
voice chat software.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can connect to your Mumble server on the regular Mumble port 64738.
|
||||
<a href="http://mumble.info">Clients</a> to connect to Mumble
|
||||
from your desktop and Android devices are available.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -26,18 +26,11 @@ import logging
|
||||
|
||||
from .forms import MumbleForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import mumble
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def on_install():
|
||||
"""Notify that the service is now enabled."""
|
||||
mumble.service.notify_enabled(None, True)
|
||||
|
||||
|
||||
@package.required(['mumble-server'], on_install=on_install)
|
||||
def index(request):
|
||||
"""Serve configuration page."""
|
||||
status = get_status()
|
||||
@ -55,7 +48,8 @@ def index(request):
|
||||
form = MumbleForm(initial=status, prefix='mumble')
|
||||
|
||||
return TemplateResponse(request, 'mumble.html',
|
||||
{'title': _('Voice Chat (Mumble)'),
|
||||
{'title': mumble.title,
|
||||
'description': mumble.description,
|
||||
'status': status,
|
||||
'form': form})
|
||||
|
||||
|
||||
@ -31,7 +31,13 @@ SERVICES = (
|
||||
('ssh', _('SSH'), 22),
|
||||
)
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['system']
|
||||
|
||||
title = _('Name Services')
|
||||
|
||||
domain_types = {}
|
||||
domains = {}
|
||||
@ -42,8 +48,7 @@ logger = logging.getLogger(__name__)
|
||||
def init():
|
||||
"""Initialize the names module."""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Name Services'), 'glyphicon-tag',
|
||||
'names:index', 19)
|
||||
menu.add_urlname(title, 'glyphicon-tag', 'names:index', 19)
|
||||
|
||||
domain_added.connect(on_domain_added)
|
||||
domain_removed.connect(on_domain_removed)
|
||||
@ -54,6 +59,7 @@ def on_domain_added(sender, domain_type, name='', description='',
|
||||
"""Add domain to global list."""
|
||||
if not domain_type:
|
||||
return
|
||||
|
||||
domain_types[domain_type] = description
|
||||
|
||||
if not name:
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
{% block configuration %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
|
||||
@ -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'))
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -30,21 +30,7 @@
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Virtual Private Network (OpenVPN)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
{% if status.is_setup %}
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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 <a href="/owncloud">/owncloud</a> 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():
|
||||
|
||||
30
plinth/modules/owncloud/forms.py
Normal file
30
plinth/modules/owncloud/forms.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
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)
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "File Hosting (ownCloud)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
When enabled, the ownCloud installation will be available
|
||||
from <a href="/owncloud">/owncloud</a> path on the web server.
|
||||
Visit this URL to set up the initial administration account for
|
||||
ownCloud.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
{% include "diagnostics_button.html" with module="owncloud" %}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ URLs for the ownCloud module
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import owncloud as views
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
@ -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'])
|
||||
@ -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 '
|
||||
'<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"""
|
||||
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'])
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<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>
|
||||
{% blocktrans trimmed %}
|
||||
{{ box_name }} is behind a restricted firewall.
|
||||
{% endblocktrans %}
|
||||
</li>
|
||||
|
||||
<li>
|
||||
{% blocktrans trimmed %}
|
||||
{{ box_name }} 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>{% trans "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>
|
||||
{% block configuration %}
|
||||
|
||||
<p>
|
||||
<a class="btn btn-primary btn-md" href="{% url 'pagekite:configure' %}">
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}Restart or shut down the system.{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<p>
|
||||
<a class="btn btn-default btn-md" href="{% url 'power:restart' %}">
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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 '
|
||||
'<a href="http://config.privoxy.org">http://config.privoxy.org/</a> '
|
||||
'or <a href="http://p.p">http://p.p</a>.'), 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
|
||||
|
||||
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "Web Proxy (Privoxy)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% 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
|
||||
<a href="http://config.privoxy.org">http://config.privoxy.org/</a>
|
||||
or <a href="http://p.p">http://p.p</a>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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 '
|
||||
'<a href="http://quassel-irc.org/downloads">desktop</a> and '
|
||||
'<a href="http://quasseldroid.iskrembilen.com/">mobile</a> 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():
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "IRC Client (Quassel)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can connect to your Quassel core on the default Quassel port
|
||||
4242. Clients to connect to Quassel from your
|
||||
<a href="http://quassel-irc.org/downloads">desktop</a> and
|
||||
<a href="http://quasseldroid.iskrembilen.com/">mobile</a> devices
|
||||
are available.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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 <a href="https://jitsi.org/">Jitsi</a> (for computers) and '
|
||||
'<a href="https://f-droid.org/repository/browse/?fdid=com.csipsimple"> '
|
||||
'CSipSimple</a> (for Android phones).'),
|
||||
|
||||
_('<strong>Note:</strong> Before using repro, domains and users will '
|
||||
'need to be configured using the <a href="/repro/domains.html">'
|
||||
'web-based configuration panel</a>. Users in the <em>admin</em> 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():
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "SIP Server (repro)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
To make SIP calls, a client application is needed. Available clients
|
||||
include <a href="https://jitsi.org/">Jitsi</a> (for computers) and
|
||||
<a href="https://f-droid.org/repository/browse/?fdid=com.csipsimple">
|
||||
CSipSimple</a> (for Android phones).
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
<strong>Note:</strong> Before using repro, domains and users will need
|
||||
to be configured using the <a href="/repro/domains.html">web-based
|
||||
configuration panel</a>. Users in the <em>admin</em> 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 %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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 <a href=\'https://unhosted.org/\'>'
|
||||
'unhosted</a> 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 '
|
||||
'<a href=\'/restore/\'>reStore web-interface</a>.')
|
||||
]
|
||||
|
||||
|
||||
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():
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "Unhosted Storage (reStore)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
reStore is a server for <a href='https://unhosted.org/'>unhosted</a>
|
||||
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 %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can create and edit accounts in the
|
||||
<a href='/restore/'>reStore web-interface</a>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>Configuration</h3>
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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 <a href="/roundcube">'
|
||||
'/roundcube</a>. 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 <code>imap.example.com'
|
||||
'</code>. For IMAP over SSL (recommended), fill the server field '
|
||||
'like <code>imaps://imap.example.com</code>.'),
|
||||
|
||||
_('For Gmail, username will be your Gmail address, password will be '
|
||||
'your Google account password and server will be '
|
||||
'<code>imaps://imap.gmail.com</code>. Note that you will also need '
|
||||
'to enable "Less secure apps" in your Google account settings '
|
||||
'(<a href="https://www.google.com/settings/security/lesssecureapps"'
|
||||
'>https://www.google.com/settings/security/lesssecureapps</a>).'),
|
||||
]
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "Email Client (Roundcube)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can access Roundcube from <a href="/roundcube">/roundcube</a>.
|
||||
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 <code>imap.example.com</code>. For
|
||||
IMAP over SSL (recommended), fill the server field like
|
||||
<code>imaps://imap.example.com</code>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
For Gmail, username will be your Gmail address, password will be
|
||||
your Google account password and server will be
|
||||
<code>imaps://imap.gmail.com</code>. Note that you will also need
|
||||
to enable "Less secure apps" in your Google account settings
|
||||
(<a href="https://www.google.com/settings/security/lesssecureapps"
|
||||
>https://www.google.com/settings/security/lesssecureapps</a>).
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
{% include "diagnostics_button.html" with module="roundcube" %}
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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 <a href="/shaarli">'
|
||||
'/shaarli</a> 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():
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "Bookmarks (Shaarli)" %}</h2>
|
||||
|
||||
<p>{% trans "Shaarli allows you to save and share bookmarks." %}</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
When enabled, Shaarli will be available from <a href="/shaarli">/shaarli</a>
|
||||
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 %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Configuration" %}</h3>
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
{% endcomment %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "System Configuration" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Here you can administrate the underlying system of your
|
||||
{{ box_name }}.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The options affect the {{ box_name }} at its most general level,
|
||||
so be careful!
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -21,7 +21,7 @@ URLs for the System module
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import system as views
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
@ -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})
|
||||
@ -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 <a href="https://www.torproject.org/">Tor '
|
||||
'Project</a> website. For best protection when web surfing, the '
|
||||
'Tor Project recommends that you use the '
|
||||
'<a href="https://www.torproject.org/download/download-easy.html.en">'
|
||||
'Tor Browser</a>.')
|
||||
]
|
||||
|
||||
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')
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -30,20 +30,7 @@
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Anonymity Network (Tor)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Tor is an anonymous communication system. You can learn more
|
||||
about it from the <a href="https://www.torproject.org/">Tor
|
||||
Project</a> website. For best protection when web surfing, the
|
||||
Tor Project recommends that you use the
|
||||
<a href="https://www.torproject.org/download/download-easy.html.en">
|
||||
Tor Browser</a>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<h3>{% trans "Status" %}</h3>
|
||||
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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():
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{% trans "BitTorrent (Transmission)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
BitTorrent is a peer-to-peer file sharing protocol.
|
||||
Transmission daemon handles Bitorrent file sharing. Note that
|
||||
BitTorrent is not anonymous.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@ -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})
|
||||
|
||||
|
||||
@ -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'])
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'base.html' %}
|
||||
{% extends 'app.html' %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -29,17 +29,7 @@
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
<h2>{{ title }}</h2>
|
||||
{% block configuration %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
@ -21,16 +21,14 @@ Plinth module for upgrades
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.views.decorators.http import require_POST
|
||||
import subprocess
|
||||
|
||||
from .forms import ConfigureForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.errors import ActionError
|
||||
from plinth.modules import upgrades
|
||||
|
||||
subsubmenu = [{'url': reverse_lazy('upgrades:index'),
|
||||
'text': ugettext_lazy('Automatic Upgrades')},
|
||||
@ -41,12 +39,6 @@ LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
|
||||
LOCK_FILE = '/var/log/dpkg/lock'
|
||||
|
||||
|
||||
def on_install():
|
||||
"""Enable automatic upgrades after install."""
|
||||
actions.superuser_run('upgrades', ['enable-auto'])
|
||||
|
||||
|
||||
@package.required(['unattended-upgrades'], on_install=on_install)
|
||||
def index(request):
|
||||
"""Serve the configuration form."""
|
||||
status = get_status()
|
||||
@ -63,10 +55,12 @@ def index(request):
|
||||
form = ConfigureForm(initial=status, prefix='upgrades')
|
||||
|
||||
return TemplateResponse(request, 'upgrades_configure.html',
|
||||
{'title': _('Automatic Upgrades'),
|
||||
{'title': upgrades.title,
|
||||
'description': upgrades.description,
|
||||
'form': form,
|
||||
'subsubmenu': subsubmenu})
|
||||
|
||||
|
||||
def is_package_manager_busy():
|
||||
"""Return whether a package manager is running."""
|
||||
try:
|
||||
@ -85,7 +79,6 @@ def get_log():
|
||||
return None
|
||||
|
||||
|
||||
@package.required(['unattended-upgrades'], on_install=on_install)
|
||||
def upgrade(request):
|
||||
"""Serve the upgrade page."""
|
||||
is_busy = is_package_manager_busy()
|
||||
@ -99,7 +92,8 @@ def upgrade(request):
|
||||
messages.error(request, _('Starting upgrade failed.'))
|
||||
|
||||
return TemplateResponse(request, 'upgrades.html',
|
||||
{'title': _('Package Upgrades'),
|
||||
{'title': upgrades.title,
|
||||
'description': upgrades.description,
|
||||
'subsubmenu': subsubmenu,
|
||||
'is_busy': is_busy,
|
||||
'log': get_log()})
|
||||
|
||||
@ -20,21 +20,24 @@ Plinth module to manage users
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
from plinth import cfg
|
||||
from plinth import actions
|
||||
from plinth import action_utils
|
||||
|
||||
depends = ['plinth.modules.system']
|
||||
version = 1
|
||||
|
||||
is_essential = True
|
||||
|
||||
depends = ['system']
|
||||
|
||||
title = _('Users and Groups')
|
||||
|
||||
|
||||
def init():
|
||||
"""Intialize the user module."""
|
||||
menu = cfg.main_menu.get('system:index')
|
||||
menu.add_urlname(_('Users and Groups'), 'glyphicon-user', 'users:index',
|
||||
15)
|
||||
menu.add_urlname(title, 'glyphicon-user', 'users:index', 15)
|
||||
|
||||
|
||||
def diagnose():
|
||||
|
||||
@ -20,7 +20,8 @@ Plinth module to configure XMPP server
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from plinth import actions
|
||||
from plinth import action_utils
|
||||
@ -30,21 +31,35 @@ from plinth.signals import pre_hostname_change, post_hostname_change
|
||||
from plinth.signals import domainname_change
|
||||
|
||||
|
||||
depends = ['plinth.modules.apps']
|
||||
version = 1
|
||||
|
||||
depends = ['apps']
|
||||
|
||||
title = _('Chat Server (XMPP)')
|
||||
|
||||
description = [
|
||||
_('XMPP is an open and standardized communication protocol. Here '
|
||||
'you can run and configure your XMPP server, called ejabberd.'),
|
||||
|
||||
_('To actually communicate, you can use the <a href=\'/jwchat\'>web '
|
||||
'client</a> or any other '
|
||||
'<a href=\'http://xmpp.org/xmpp-software/clients/\' target=\'_blank\''
|
||||
'>XMPP client</a>.')
|
||||
]
|
||||
|
||||
service = None
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize the XMPP module"""
|
||||
menu = cfg.main_menu.get('apps:index')
|
||||
menu.add_urlname(_('Chat Server (XMPP)'), 'glyphicon-comment',
|
||||
'xmpp:index', 400)
|
||||
menu.add_urlname(title, 'glyphicon-comment', 'xmpp:index', 400)
|
||||
|
||||
global service
|
||||
service = service_module.Service(
|
||||
'xmpp', _('Chat Server (XMPP)'),
|
||||
['xmpp-client', 'xmpp-server', 'xmpp-bosh'],
|
||||
'xmpp', title, ['xmpp-client', 'xmpp-server', 'xmpp-bosh'],
|
||||
is_external=True, enabled=is_enabled())
|
||||
|
||||
pre_hostname_change.connect(on_pre_hostname_change)
|
||||
@ -52,6 +67,18 @@ def init():
|
||||
domainname_change.connect(on_domainname_change)
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
domainname = get_domainname()
|
||||
logger.info('XMPP service domainname - %s', domainname)
|
||||
|
||||
helper.call('pre', actions.superuser_run, 'xmpp',
|
||||
['pre-install', '--domainname', domainname])
|
||||
helper.install(['jwchat', 'ejabberd'])
|
||||
helper.call('post', actions.superuser_run, 'xmpp', ['setup'])
|
||||
helper.call('post', service.notify_enabled, None, True)
|
||||
|
||||
|
||||
def is_enabled():
|
||||
"""Return whether the module is enabled."""
|
||||
return (action_utils.service_is_enabled('ejabberd') and
|
||||
@ -63,6 +90,12 @@ def is_running():
|
||||
return action_utils.service_is_running('ejabberd')
|
||||
|
||||
|
||||
def get_domainname():
|
||||
"""Return the domainname"""
|
||||
fqdn = socket.getfqdn()
|
||||
return '.'.join(fqdn.split('.')[1:])
|
||||
|
||||
|
||||
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
||||
"""
|
||||
Backup ejabberd database before hostname is changed.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
#
|
||||
# This file is part of Plinth.
|
||||
@ -21,24 +21,7 @@
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% trans "Chat Server (XMPP)" %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
XMPP is an open and standardized communication protocol. Here
|
||||
you can run and configure your XMPP server, called ejabberd.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
To actually communicate, you can use the <a href='/jwchat'>web
|
||||
client</a> or any other <a href='http://xmpp.org/xmpp-software/clients/'
|
||||
target='_blank'>XMPP client</a>.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% block configuration %}
|
||||
|
||||
<p>
|
||||
{% url 'config:index' as index_url %}
|
||||
|
||||
@ -23,39 +23,15 @@ from django.contrib import messages
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from .forms import XmppForm
|
||||
from plinth import actions
|
||||
from plinth import package
|
||||
from plinth.modules import xmpp
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_domainname():
|
||||
"""Return the domainname"""
|
||||
fqdn = socket.getfqdn()
|
||||
return '.'.join(fqdn.split('.')[1:])
|
||||
|
||||
|
||||
def before_install():
|
||||
"""Preseed debconf values before the packages are installed."""
|
||||
domainname = get_domainname()
|
||||
logger.info('XMPP service domainname - %s', domainname)
|
||||
actions.superuser_run('xmpp', ['pre-install', '--domainname', domainname])
|
||||
|
||||
|
||||
def on_install():
|
||||
"""Setup jwchat apache conf"""
|
||||
actions.superuser_run('xmpp', ['setup'])
|
||||
xmpp.service.notify_enabled(None, True)
|
||||
|
||||
|
||||
@package.required(['jwchat', 'ejabberd'],
|
||||
before_install=before_install,
|
||||
on_install=on_install)
|
||||
def index(request):
|
||||
"""Serve configuration page"""
|
||||
status = get_status()
|
||||
@ -72,7 +48,8 @@ def index(request):
|
||||
form = XmppForm(initial=status, prefix='xmpp')
|
||||
|
||||
return TemplateResponse(request, 'xmpp.html',
|
||||
{'title': _('Chat Server (XMPP)'),
|
||||
{'title': xmpp.title,
|
||||
'description': xmpp.description,
|
||||
'status': status,
|
||||
'form': form})
|
||||
|
||||
@ -81,7 +58,7 @@ def get_status():
|
||||
"""Get the current settings."""
|
||||
status = {'enabled': xmpp.is_enabled(),
|
||||
'is_running': xmpp.is_running(),
|
||||
'domainname': get_domainname()}
|
||||
'domainname': xmpp.get_domainname()}
|
||||
|
||||
return status
|
||||
|
||||
|
||||
@ -19,13 +19,9 @@
|
||||
Framework for installing and updating distribution packages
|
||||
"""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import ugettext as _
|
||||
import functools
|
||||
import logging
|
||||
import threading
|
||||
|
||||
import plinth
|
||||
from plinth.utils import import_from_gi
|
||||
glib = import_from_gi('GLib', '2.0')
|
||||
packagekit = import_from_gi('PackageKitGlib', '1.0')
|
||||
@ -46,19 +42,21 @@ class PackageException(Exception):
|
||||
self.error_string = error_string
|
||||
self.error_details = error_details
|
||||
|
||||
def __str__(self):
|
||||
"""Return the strin representation of the exception."""
|
||||
return 'PackageException(error_string="{0}", error_details="{1}")' \
|
||||
.format(self.error_string, self.error_details)
|
||||
|
||||
|
||||
class Transaction(object):
|
||||
"""Information about an ongoing transaction."""
|
||||
|
||||
def __init__(self, package_names, before_install=None, on_install=None):
|
||||
def __init__(self, package_names):
|
||||
"""Initialize transaction object.
|
||||
|
||||
Set most values to None until they are sent as progress update.
|
||||
"""
|
||||
self.package_names = package_names
|
||||
# XXX: This is hack, remove after implementing proper setup mechanism.
|
||||
self.before_install = before_install
|
||||
self.on_install = on_install
|
||||
|
||||
# Progress
|
||||
self.allow_cancel = None
|
||||
@ -74,10 +72,6 @@ class Transaction(object):
|
||||
self.download_size_remaining = None
|
||||
self.speed = None
|
||||
|
||||
# Completion
|
||||
self.is_finished = False
|
||||
self.exception = None
|
||||
|
||||
def get_id(self):
|
||||
"""Return a identifier to use as a key in a map of transactions."""
|
||||
return frozenset(self.package_names)
|
||||
@ -89,43 +83,12 @@ class Transaction(object):
|
||||
self.package_names, self.allow_cancel, self.status_string,
|
||||
self.percentage, self.package, self.item_progress)
|
||||
|
||||
def start_install(self):
|
||||
"""Start a PackageKit transaction to install given list of packages.
|
||||
|
||||
This operation is non-blocking at it spawns a new thread.
|
||||
"""
|
||||
thread = threading.Thread(target=self._install)
|
||||
thread.start()
|
||||
|
||||
def _install(self):
|
||||
def install(self):
|
||||
"""Run a PackageKit transaction to install given packages."""
|
||||
try:
|
||||
if self.before_install:
|
||||
self.before_install()
|
||||
except Exception as exception:
|
||||
logger.exception('Error during setup before install - %s',
|
||||
exception)
|
||||
self.finish(exception)
|
||||
return
|
||||
|
||||
try:
|
||||
self._do_install()
|
||||
except PackageException as exception:
|
||||
self.finish(exception)
|
||||
return
|
||||
except glib.Error as exception:
|
||||
self.finish(PackageException(exception.message))
|
||||
return
|
||||
|
||||
try:
|
||||
if self.on_install:
|
||||
self.on_install()
|
||||
except Exception as exception:
|
||||
logger.exception('Error during setup - %s', exception)
|
||||
self.finish(exception)
|
||||
return
|
||||
|
||||
self.finish()
|
||||
raise PackageException(exception.message) from exception
|
||||
|
||||
def _do_install(self):
|
||||
"""Run a PackageKit transaction to install given packages.
|
||||
@ -203,115 +166,3 @@ class Transaction(object):
|
||||
else:
|
||||
logger.info('Unhandle packagekit progress callback - %s, %s',
|
||||
progress, progress_type)
|
||||
|
||||
def finish(self, exception=None):
|
||||
"""Mark transaction as complected and store exception if any."""
|
||||
self.is_finished = True
|
||||
self.exception = exception
|
||||
|
||||
def collect_result(self):
|
||||
"""Retrieve the result of this transaction.
|
||||
|
||||
Also remove self from global transactions list.
|
||||
"""
|
||||
assert self.is_finished
|
||||
|
||||
del transactions[self.get_id()]
|
||||
return self.exception
|
||||
|
||||
|
||||
def required(package_names, before_install=None, on_install=None):
|
||||
"""Decorate a view to check and install required packages."""
|
||||
|
||||
def wrapper2(func):
|
||||
"""Return a function to check and install packages."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(request, *args, **kwargs):
|
||||
"""Check and install packages required by a view."""
|
||||
if not _should_show_install_view(request, package_names):
|
||||
return func(request, *args, **kwargs)
|
||||
|
||||
view = plinth.views.PackageInstallView.as_view()
|
||||
return view(request, package_names=package_names,
|
||||
before_install=before_install, on_install=on_install,
|
||||
*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return wrapper2
|
||||
|
||||
|
||||
def _should_show_install_view(request, package_names):
|
||||
"""Return whether the installation view should be shown."""
|
||||
transaction_id = frozenset(package_names)
|
||||
|
||||
# No transaction in progress
|
||||
if transaction_id not in transactions:
|
||||
is_installed = check_installed(package_names)
|
||||
return not is_installed
|
||||
|
||||
# Installing
|
||||
transaction = transactions[transaction_id]
|
||||
if not transaction.is_finished:
|
||||
return True
|
||||
|
||||
# Transaction finished, waiting to show the result
|
||||
exception = transaction.collect_result()
|
||||
if not exception:
|
||||
messages.success(request,
|
||||
_('Installed and configured packages successfully.'))
|
||||
return False
|
||||
else:
|
||||
error_string = getattr(exception, 'error_string', str(exception))
|
||||
error_details = getattr(exception, 'error_details', '')
|
||||
messages.error(request, _('Error installing packages: {string} {details}')
|
||||
.format(string=error_string, details=error_details))
|
||||
return True
|
||||
|
||||
|
||||
def check_installed(package_names):
|
||||
"""Return a boolean installed status of package.
|
||||
|
||||
This operation is blocking and waits until the check is finished.
|
||||
"""
|
||||
def _callback(progress, progress_type, user_data):
|
||||
"""Process progress updates on package resolve operation."""
|
||||
pass
|
||||
|
||||
client = packagekit.Client()
|
||||
response = client.resolve(packagekit.FilterEnum.INSTALLED,
|
||||
tuple(package_names) + (None, ), None,
|
||||
_callback, None)
|
||||
|
||||
installed_package_names = []
|
||||
for package in response.get_package_array():
|
||||
if package.get_info() == packagekit.InfoEnum.INSTALLED:
|
||||
installed_package_names.append(package.get_name())
|
||||
|
||||
packages_resolved[package.get_name()] = package
|
||||
|
||||
# When package names could not be resolved
|
||||
for package_name in package_names:
|
||||
if package_name not in packages_resolved:
|
||||
packages_resolved[package_name] = None
|
||||
|
||||
return set(installed_package_names) == set(package_names)
|
||||
|
||||
|
||||
def is_installing(package_names):
|
||||
"""Return whether a set of packages are currently being installed."""
|
||||
return frozenset(package_names) in transactions
|
||||
|
||||
|
||||
def start_install(package_names, before_install=None, on_install=None):
|
||||
"""Start a PackageKit transaction to install given list of packages.
|
||||
|
||||
This operation is non-blocking at it spawns a new thread.
|
||||
"""
|
||||
transaction = Transaction(package_names,
|
||||
before_install=before_install,
|
||||
on_install=on_install)
|
||||
transactions[frozenset(package_names)] = transaction
|
||||
|
||||
transaction.start_install()
|
||||
|
||||
160
plinth/setup.py
Normal file
160
plinth/setup.py
Normal file
@ -0,0 +1,160 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Plinth module with utilites for performing application setup operations.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from . import package
|
||||
import plinth
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Helper(object):
|
||||
"""Helper routines for modules to show progress."""
|
||||
|
||||
def __init__(self, module_name, module):
|
||||
"""Initialize the object."""
|
||||
self.module_name = module_name
|
||||
self.module = module
|
||||
self.current_operation = None
|
||||
self.is_finished = None
|
||||
self.exception = None
|
||||
|
||||
def run_in_thread(self):
|
||||
"""Execute the setup process in a thread."""
|
||||
thread = threading.Thread(target=self._run)
|
||||
thread.start()
|
||||
|
||||
def _run(self):
|
||||
"""Collect exceptions when running in a thread."""
|
||||
try:
|
||||
self.run()
|
||||
except Exception as exception:
|
||||
self.exception = exception
|
||||
|
||||
def collect_result(self):
|
||||
"""Return the exception if any."""
|
||||
exception = self.exception
|
||||
self.exception = None
|
||||
self.is_finished = None
|
||||
return exception
|
||||
|
||||
def run(self):
|
||||
"""Execute the setup process."""
|
||||
# Setup for the module is already running
|
||||
if self.current_operation:
|
||||
return
|
||||
|
||||
current_version = self.get_setup_version()
|
||||
if current_version >= self.module.version:
|
||||
return
|
||||
|
||||
self.exception = None
|
||||
self.current_operation = None
|
||||
self.is_finished = False
|
||||
try:
|
||||
if hasattr(self.module, 'setup'):
|
||||
logger.info('Running module setup - %s', self.module_name)
|
||||
self.module.setup(self, old_version=current_version)
|
||||
else:
|
||||
logger.info('Module does not require setup - %s',
|
||||
self.module_name)
|
||||
except Exception as exception:
|
||||
logger.exception('Error running setup - %s', exception)
|
||||
raise exception
|
||||
else:
|
||||
self.set_setup_version(self.module.version)
|
||||
finally:
|
||||
self.is_finished = True
|
||||
self.current_operation = None
|
||||
|
||||
def install(self, package_names):
|
||||
"""Install a set of packages marking progress."""
|
||||
logger.info('Running install for module - %s, packages - %s',
|
||||
self.module_name, package_names)
|
||||
transaction = package.Transaction(package_names)
|
||||
self.current_operation = {
|
||||
'step': 'install',
|
||||
'transaction': transaction,
|
||||
}
|
||||
|
||||
transaction.install()
|
||||
|
||||
def call(self, step, method, *args, **kwargs):
|
||||
"""Call an arbitrary method during setup and note down its stage."""
|
||||
logger.info('Running step for module - %s, step - %s',
|
||||
self.module_name, step)
|
||||
self.current_operation = {'step': step}
|
||||
return method(*args, **kwargs)
|
||||
|
||||
def get_state(self):
|
||||
"""Return whether the module is not setup or needs upgrade."""
|
||||
current_version = self.get_setup_version()
|
||||
if current_version and self.module.version <= current_version:
|
||||
return 'up-to-date'
|
||||
|
||||
# If a module need installing/updating but no setup method is
|
||||
# available, then automatically set version.
|
||||
#
|
||||
# Minor violation of 'get' only discipline for convenience.
|
||||
if not hasattr(self.module, 'setup'):
|
||||
self.set_setup_version(self.module.version)
|
||||
return 'up-to-date'
|
||||
|
||||
if not current_version:
|
||||
return 'needs-setup'
|
||||
else:
|
||||
return 'needs-update'
|
||||
|
||||
def get_setup_version(self):
|
||||
"""Return the setup version of a module."""
|
||||
# XXX: Optimize version gets
|
||||
from . import models
|
||||
|
||||
try:
|
||||
module_entry = models.Module.objects.get(pk=self.module_name)
|
||||
return module_entry.setup_version
|
||||
except models.Module.DoesNotExist:
|
||||
return 0
|
||||
|
||||
def set_setup_version(self, version):
|
||||
"""Set a module's setup version."""
|
||||
from . import models
|
||||
|
||||
models.Module.objects.update_or_create(
|
||||
pk=self.module_name, defaults={'setup_version': version})
|
||||
|
||||
|
||||
def init(module_name, module):
|
||||
"""Create a setup helper for a module for later use."""
|
||||
if not hasattr(module, 'setup_helper'):
|
||||
module.setup_helper = Helper(module_name, module)
|
||||
|
||||
|
||||
def setup_all_modules(essential=False):
|
||||
"""Run setup on all essential modules and exit."""
|
||||
logger.info('Running setup for all modules, essential - %s', essential)
|
||||
for module_name, module in plinth.module_loader.loaded_modules.items():
|
||||
if essential and not getattr(module, 'is_essential', False):
|
||||
continue
|
||||
|
||||
module.setup_helper.run()
|
||||
33
plinth/templates/app.html
Normal file
33
plinth/templates/app.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% 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 i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ title }}</h2>
|
||||
|
||||
{% for paragraph in description %}
|
||||
<p>{{ paragraph|safe }}</p>
|
||||
{% endfor %}
|
||||
|
||||
{% block configuration %}
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user