mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-27 10:44:33 +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 cfg
|
||||||
from plinth import module_loader
|
from plinth import module_loader
|
||||||
from plinth import service
|
from plinth import service
|
||||||
|
from plinth import setup
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -57,6 +58,9 @@ def parse_arguments():
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--no-daemon', action='store_true', default=cfg.no_daemon,
|
'--no-daemon', action='store_true', default=cfg.no_daemon,
|
||||||
help='do not start as a 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(
|
parser.add_argument(
|
||||||
'--diagnose', action='store_true', default=False,
|
'--diagnose', action='store_true', default=False,
|
||||||
help='run diagnostic tests and exit')
|
help='run diagnostic tests and exit')
|
||||||
@ -132,9 +136,7 @@ def setup_server():
|
|||||||
cherrypy.tree.mount(None, manual_url, config)
|
cherrypy.tree.mount(None, manual_url, config)
|
||||||
logger.debug('Serving manual images %s on %s', manual_dir, manual_url)
|
logger.debug('Serving manual images %s on %s', manual_dir, manual_url)
|
||||||
|
|
||||||
for module_import_path in module_loader.loaded_modules:
|
for module_name, module in module_loader.loaded_modules.items():
|
||||||
module = importlib.import_module(module_import_path)
|
|
||||||
module_name = module_import_path.split('.')[-1]
|
|
||||||
module_path = os.path.dirname(module.__file__)
|
module_path = os.path.dirname(module.__file__)
|
||||||
static_dir = os.path.join(module_path, 'static')
|
static_dir = os.path.join(module_path, 'static')
|
||||||
if not os.path.isdir(static_dir):
|
if not os.path.isdir(static_dir):
|
||||||
@ -258,6 +260,7 @@ def configure_django():
|
|||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'stronghold.middleware.LoginRequiredMiddleware',
|
'stronghold.middleware.LoginRequiredMiddleware',
|
||||||
'plinth.modules.first_boot.middleware.FirstBootMiddleware',
|
'plinth.modules.first_boot.middleware.FirstBootMiddleware',
|
||||||
|
'plinth.middleware.SetupMiddleware',
|
||||||
),
|
),
|
||||||
ROOT_URLCONF='plinth.urls',
|
ROOT_URLCONF='plinth.urls',
|
||||||
SECURE_PROXY_SSL_HEADER=secure_proxy_ssl_header,
|
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)
|
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():
|
def run_diagnostics_and_exit():
|
||||||
"""Run diagostics on all modules and exit."""
|
"""Run diagostics on all modules and exit."""
|
||||||
module = importlib.import_module('plinth.modules.diagnostics.diagnostics')
|
module = importlib.import_module('plinth.modules.diagnostics.diagnostics')
|
||||||
@ -315,6 +330,9 @@ def main():
|
|||||||
|
|
||||||
module_loader.load_modules()
|
module_loader.load_modules()
|
||||||
|
|
||||||
|
if arguments.setup:
|
||||||
|
run_setup_and_exit()
|
||||||
|
|
||||||
if arguments.diagnose:
|
if arguments.diagnose:
|
||||||
run_diagnostics_and_exit()
|
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):
|
def value(self, val):
|
||||||
"""Store the value of the key/value pair by JSON encoding it"""
|
"""Store the value of the key/value pair by JSON encoding it"""
|
||||||
self.value_json = json.dumps(val)
|
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
|
Discover, load and manage Plinth modules
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import collections
|
||||||
import django
|
import django
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
@ -27,11 +28,12 @@ import re
|
|||||||
|
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import urls
|
from plinth import urls
|
||||||
|
from plinth import setup
|
||||||
from plinth.signals import pre_module_loading, post_module_loading
|
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
|
_modules_to_load = None
|
||||||
|
|
||||||
|
|
||||||
@ -42,16 +44,18 @@ def load_modules():
|
|||||||
"""
|
"""
|
||||||
pre_module_loading.send_robust(sender="module_loader")
|
pre_module_loading.send_robust(sender="module_loader")
|
||||||
modules = {}
|
modules = {}
|
||||||
for module_name in get_modules_to_load():
|
for module_import_path in get_modules_to_load():
|
||||||
LOGGER.info('Importing %s', module_name)
|
logger.info('Importing %s', module_import_path)
|
||||||
|
module_name = module_import_path.split('.')[-1]
|
||||||
try:
|
try:
|
||||||
modules[module_name] = importlib.import_module(module_name)
|
modules[module_name] = importlib.import_module(module_import_path)
|
||||||
except Exception as exception:
|
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:
|
if cfg.debug:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
_include_module_urls(module_name)
|
_include_module_urls(module_import_path, module_name)
|
||||||
|
|
||||||
ordered_modules = []
|
ordered_modules = []
|
||||||
remaining_modules = dict(modules) # Make a copy
|
remaining_modules = dict(modules) # Make a copy
|
||||||
@ -64,14 +68,14 @@ def load_modules():
|
|||||||
_insert_modules(module_name, module, remaining_modules,
|
_insert_modules(module_name, module, remaining_modules,
|
||||||
ordered_modules)
|
ordered_modules)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOGGER.error('Unsatified dependency for module - %s',
|
logger.error('Unsatified dependency for module - %s',
|
||||||
module_name)
|
module_name)
|
||||||
|
|
||||||
LOGGER.debug('Module load order - %s', ordered_modules)
|
logger.debug('Module load order - %s', ordered_modules)
|
||||||
|
|
||||||
for module_name in ordered_modules:
|
for module_name in ordered_modules:
|
||||||
_initialize_module(modules[module_name])
|
_initialize_module(module_name, modules[module_name])
|
||||||
loaded_modules.append(module_name)
|
loaded_modules[module_name] = modules[module_name]
|
||||||
|
|
||||||
post_module_loading.send_robust(sender="module_loader")
|
post_module_loading.send_robust(sender="module_loader")
|
||||||
|
|
||||||
@ -94,7 +98,7 @@ def _insert_modules(module_name, module, remaining_modules, ordered_modules):
|
|||||||
try:
|
try:
|
||||||
module = remaining_modules.pop(dependency)
|
module = remaining_modules.pop(dependency)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
LOGGER.error('Not found or circular dependency - %s, %s',
|
logger.error('Not found or circular dependency - %s, %s',
|
||||||
module_name, dependency)
|
module_name, dependency)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@ -103,32 +107,34 @@ def _insert_modules(module_name, module, remaining_modules, ordered_modules):
|
|||||||
ordered_modules.append(module_name)
|
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"""
|
"""Include the module's URLs in global project URLs list"""
|
||||||
namespace = module_name.split('.')[-1]
|
url_module = module_import_path + '.urls'
|
||||||
url_module = module_name + '.urls'
|
|
||||||
try:
|
try:
|
||||||
urls.urlpatterns += [
|
urls.urlpatterns += [
|
||||||
django.conf.urls.url(
|
django.conf.urls.url(
|
||||||
r'', django.conf.urls.include(url_module, namespace))]
|
r'', django.conf.urls.include(url_module, module_name))]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
LOGGER.debug('No URLs for %s', module_name)
|
logger.debug('No URLs for %s', module_name)
|
||||||
if cfg.debug:
|
if cfg.debug:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def _initialize_module(module):
|
def _initialize_module(module_name, module):
|
||||||
"""Call initialization method in the module if it exists"""
|
"""Call initialization method in the module if it exists"""
|
||||||
|
# Perform setup related initialization on the module
|
||||||
|
setup.init(module_name, module)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
init = module.init
|
init = module.init
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
LOGGER.debug('No init() for module - %s', module.__name__)
|
logger.debug('No init() for module - %s', module.__name__)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
init()
|
init()
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
LOGGER.exception('Exception while running init for %s: %s',
|
logger.exception('Exception while running init for %s: %s',
|
||||||
module, exception)
|
module, exception)
|
||||||
if cfg.debug:
|
if cfg.debug:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@ -23,3 +23,7 @@ from . import apps
|
|||||||
from .apps import init
|
from .apps import init
|
||||||
|
|
||||||
__all__ = ['apps', '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 _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from plinth import actions
|
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
from plinth import service as service_module
|
||||||
|
from plinth.utils import format_lazy
|
||||||
|
|
||||||
# pylint: disable=C0103
|
# 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
|
service = None
|
||||||
|
|
||||||
@ -37,13 +53,16 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Intialize the service discovery module."""
|
"""Intialize the service discovery module."""
|
||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Service Discovery'), 'glyphicon-lamp',
|
menu.add_urlname(title, 'glyphicon-lamp', 'avahi:index', 950)
|
||||||
'avahi:index', 950)
|
|
||||||
|
|
||||||
global service # pylint: disable=W0603
|
global service # pylint: disable=W0603
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'avahi', _('Service Discovery'), ['mdns'],
|
'avahi', title, ['mdns'], is_external=False, enabled=is_enabled())
|
||||||
is_external=False, enabled=is_enabled())
|
|
||||||
|
|
||||||
|
def setup(helper, old_version=False):
|
||||||
|
"""Install and configure the module."""
|
||||||
|
helper.install(['avahi-daemon'])
|
||||||
|
|
||||||
|
|
||||||
def is_enabled():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,21 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -26,14 +26,12 @@ import logging
|
|||||||
|
|
||||||
from .forms import ServiceDiscoveryForm
|
from .forms import ServiceDiscoveryForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import avahi
|
from plinth.modules import avahi
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__) # pylint: disable=C0103
|
logger = logging.getLogger(__name__) # pylint: disable=C0103
|
||||||
|
|
||||||
|
|
||||||
@package.required(['avahi-daemon'])
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -50,7 +48,8 @@ def index(request):
|
|||||||
form = ServiceDiscoveryForm(initial=status, prefix='avahi')
|
form = ServiceDiscoveryForm(initial=status, prefix='avahi')
|
||||||
|
|
||||||
return TemplateResponse(request, 'avahi.html',
|
return TemplateResponse(request, 'avahi.html',
|
||||||
{'title': _('Service Discovery'),
|
{'title': avahi.title,
|
||||||
|
'description': avahi.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,8 @@ from .config import init
|
|||||||
|
|
||||||
__all__ = ['config', 'init']
|
__all__ = ['config', 'init']
|
||||||
|
|
||||||
depends = ['plinth.modules.system',
|
version = 1
|
||||||
'plinth.modules.firewall',
|
|
||||||
'plinth.modules.names']
|
is_essential = True
|
||||||
|
|
||||||
|
depends = ['system', 'firewall', 'names']
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import socket
|
|||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth.modules.firewall import firewall
|
from plinth.modules import firewall
|
||||||
from plinth.modules.names import SERVICES
|
from plinth.modules.names import SERVICES
|
||||||
from plinth.signals import pre_hostname_change, post_hostname_change
|
from plinth.signals import pre_hostname_change, post_hostname_change
|
||||||
from plinth.signals import domainname_change
|
from plinth.signals import domainname_change
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
URLs for the Configuration module
|
URLs for the Configuration module
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import config as views
|
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 _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from plinth import actions
|
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -36,13 +46,17 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Intialize the date/time module."""
|
"""Intialize the date/time module."""
|
||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Date & Time'), 'glyphicon-time',
|
menu.add_urlname(title, 'glyphicon-time', 'datetime:index', 900)
|
||||||
'datetime:index', 900)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'ntp', _('Network Time Server'),
|
'ntp', title, is_external=False, enabled=is_enabled())
|
||||||
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,16 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -26,18 +26,11 @@ import logging
|
|||||||
|
|
||||||
from .forms import DateTimeForm
|
from .forms import DateTimeForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import datetime
|
from plinth.modules import datetime
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -55,7 +48,8 @@ def index(request):
|
|||||||
form = DateTimeForm(initial=status, prefix='datetime')
|
form = DateTimeForm(initial=status, prefix='datetime')
|
||||||
|
|
||||||
return TemplateResponse(request, 'datetime.html',
|
return TemplateResponse(request, 'datetime.html',
|
||||||
{'title': _('Date & Time'),
|
{'title': datetime.title,
|
||||||
|
'description': datetime.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,20 @@ from plinth import cfg
|
|||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -35,13 +48,19 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the Deluge module."""
|
"""Initialize the Deluge module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('BitTorrent (Deluge)'), 'glyphicon-magnet',
|
menu.add_urlname(title, 'glyphicon-magnet', 'deluge:index', 200)
|
||||||
'deluge:index', 200)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'deluge', _('Deluge BitTorrent'), ['http', 'https'],
|
'deluge', title, ['http', 'https'], is_external=True,
|
||||||
is_external=True, enabled=is_enabled())
|
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,20 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -25,17 +25,9 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from .forms import DelugeForm
|
from .forms import DelugeForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import deluge
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -53,7 +45,8 @@ def index(request):
|
|||||||
form = DelugeForm(initial=status, prefix='deluge')
|
form = DelugeForm(initial=status, prefix='deluge')
|
||||||
|
|
||||||
return TemplateResponse(request, 'deluge.html',
|
return TemplateResponse(request, 'deluge.html',
|
||||||
{'title': _('BitTorrent (Deluge)'),
|
{'title': deluge.title,
|
||||||
|
'description': deluge.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -19,13 +19,30 @@
|
|||||||
Plinth module for system diagnostics
|
Plinth module for system diagnostics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import diagnostics
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .diagnostics import init
|
|
||||||
from plinth import action_utils
|
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():
|
def diagnose():
|
||||||
|
|||||||
@ -24,12 +24,11 @@ from django.http import Http404
|
|||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import importlib
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from plinth import cfg
|
|
||||||
from plinth import module_loader
|
from plinth import module_loader
|
||||||
|
from plinth.modules import diagnostics
|
||||||
|
|
||||||
|
|
||||||
logger = logging.Logger(__name__)
|
logger = logging.Logger(__name__)
|
||||||
@ -39,20 +38,14 @@ current_results = {}
|
|||||||
_running_task = None
|
_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):
|
def index(request):
|
||||||
"""Serve the index page"""
|
"""Serve the index page"""
|
||||||
if request.method == 'POST' and not _running_task:
|
if request.method == 'POST' and not _running_task:
|
||||||
_start_task()
|
_start_task()
|
||||||
|
|
||||||
return TemplateResponse(request, 'diagnostics.html',
|
return TemplateResponse(request, 'diagnostics.html',
|
||||||
{'title': _('System Diagnostics'),
|
{'title': diagnostics.title,
|
||||||
|
'description': diagnostics.description,
|
||||||
'is_running': _running_task is not None,
|
'is_running': _running_task is not None,
|
||||||
'results': current_results})
|
'results': current_results})
|
||||||
|
|
||||||
@ -60,19 +53,14 @@ def index(request):
|
|||||||
@require_POST
|
@require_POST
|
||||||
def module(request, module_name):
|
def module(request, module_name):
|
||||||
"""Return diagnostics for a particular module."""
|
"""Return diagnostics for a particular module."""
|
||||||
found = False
|
try:
|
||||||
for module_import_path in module_loader.loaded_modules:
|
module = module_loader.loaded_modules[module_name]
|
||||||
if module_name == module_import_path.split('.')[-1]:
|
except KeyError:
|
||||||
found = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
raise Http404('Module does not exist or not loaded')
|
raise Http404('Module does not exist or not loaded')
|
||||||
|
|
||||||
loaded_module = importlib.import_module(module_import_path)
|
|
||||||
results = []
|
results = []
|
||||||
if hasattr(loaded_module, 'diagnose'):
|
if hasattr(module, 'diagnose'):
|
||||||
results = loaded_module.diagnose()
|
results = module.diagnose()
|
||||||
|
|
||||||
return TemplateResponse(request, 'diagnostics_module.html',
|
return TemplateResponse(request, 'diagnostics_module.html',
|
||||||
{'title': _('Diagnostic Test'),
|
{'title': _('Diagnostic Test'),
|
||||||
@ -110,17 +98,15 @@ def run_on_all_modules():
|
|||||||
'progress_percentage': 0}
|
'progress_percentage': 0}
|
||||||
|
|
||||||
modules = []
|
modules = []
|
||||||
for module_import_path in module_loader.loaded_modules:
|
for module_name, module in module_loader.loaded_modules.items():
|
||||||
loaded_module = importlib.import_module(module_import_path)
|
if not hasattr(module, 'diagnose'):
|
||||||
if not hasattr(loaded_module, 'diagnose'):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
module_name = module_import_path.split('.')[-1]
|
modules.append((module_name, module))
|
||||||
modules.append((module_name, loaded_module))
|
|
||||||
current_results['results'][module_name] = None
|
current_results['results'][module_name] = None
|
||||||
|
|
||||||
current_results['modules'] = modules
|
current_results['modules'] = modules
|
||||||
for current_index, (module_name, loaded_module) in enumerate(modules):
|
for current_index, (module_name, module) in enumerate(modules):
|
||||||
current_results['results'][module_name] = loaded_module.diagnose()
|
current_results['results'][module_name] = module.diagnose()
|
||||||
current_results['progress_percentage'] = \
|
current_results['progress_percentage'] = \
|
||||||
int((current_index + 1) * 100 / len(modules))
|
int((current_index + 1) * 100 / len(modules))
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'app.html' %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -29,17 +29,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{% if not is_running %}
|
{% if not is_running %}
|
||||||
<form class="form form-diagnostics-button" method="post"
|
<form class="form form-diagnostics-button" method="post"
|
||||||
|
|||||||
@ -19,9 +19,42 @@
|
|||||||
Plinth module to configure ez-ipupdate client
|
Plinth module to configure ez-ipupdate client
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import dynamicdns
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .dynamicdns import init
|
|
||||||
|
|
||||||
__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 actions
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import package
|
from plinth.modules import dynamicdns
|
||||||
from plinth.utils import format_lazy
|
from plinth.utils import format_lazy
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -39,19 +39,11 @@ subsubmenu = [{'url': reverse_lazy('dynamicdns:index'),
|
|||||||
'text': ugettext_lazy('Status')}]
|
'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):
|
def index(request):
|
||||||
"""Serve Dynamic DNS page."""
|
"""Serve Dynamic DNS page."""
|
||||||
|
|
||||||
return TemplateResponse(request, 'dynamicdns.html',
|
return TemplateResponse(request, 'dynamicdns.html',
|
||||||
{'title': _('Dynamic DNS'),
|
{'title': dynamicdns.title,
|
||||||
|
'description': dynamicdns.description,
|
||||||
'subsubmenu': subsubmenu})
|
'subsubmenu': subsubmenu})
|
||||||
|
|
||||||
|
|
||||||
@ -198,7 +190,6 @@ class ConfigureForm(forms.Form):
|
|||||||
raise forms.ValidationError(_('Please provide a password'))
|
raise forms.ValidationError(_('Please provide a password'))
|
||||||
|
|
||||||
|
|
||||||
@package.required(['ez-ipupdate'])
|
|
||||||
def configure(request):
|
def configure(request):
|
||||||
"""Serve the configuration form."""
|
"""Serve the configuration form."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -219,7 +210,6 @@ def configure(request):
|
|||||||
'subsubmenu': subsubmenu})
|
'subsubmenu': subsubmenu})
|
||||||
|
|
||||||
|
|
||||||
@package.required(['ez-ipupdate'])
|
|
||||||
def statuspage(request):
|
def statuspage(request):
|
||||||
"""Serve the status page."""
|
"""Serve the status page."""
|
||||||
check_nat = actions.run('dynamicdns', ['get-nat'])
|
check_nat = actions.run('dynamicdns', ['get-nat'])
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -20,32 +20,7 @@
|
|||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
If you are looking for a free dynamic DNS account, you may find
|
If you are looking for a free dynamic DNS account, you may find
|
||||||
|
|||||||
@ -19,9 +19,119 @@
|
|||||||
Plinth module to configure a firewall
|
Plinth module to configure a firewall
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import firewall
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .firewall import init
|
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 %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -20,18 +20,7 @@
|
|||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<p>{% trans "Current status:" %}</p>
|
<p>{% trans "Current status:" %}</p>
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ URLs for the Firewall module
|
|||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import firewall as views
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
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
|
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
|
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
|
service = None
|
||||||
|
|
||||||
@ -35,13 +44,25 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the ikiwiki module."""
|
"""Initialize the ikiwiki module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Wiki and Blog (ikiwiki)'), 'glyphicon-edit',
|
menu.add_urlname(title, 'glyphicon-edit', 'ikiwiki:index', 1100)
|
||||||
'ikiwiki:index', 1100)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'ikiwiki', _('ikiwiki wikis and blogs'), ['http', 'https'],
|
'ikiwiki', title, ['http', 'https'], is_external=True,
|
||||||
is_external=True, enabled=is_enabled())
|
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,14 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<p>
|
|
||||||
{% blocktrans trimmed %}
|
|
||||||
When enabled, the blogs and wikis will be available
|
|
||||||
from <a href="/ikiwiki">/ikiwiki</a>.
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{% include "diagnostics_button.html" with module="ikiwiki" %}
|
{% 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 .forms import IkiwikiForm, IkiwikiCreateForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import action_utils
|
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import ikiwiki
|
from plinth.modules import ikiwiki
|
||||||
|
|
||||||
|
|
||||||
@ -40,20 +38,6 @@ subsubmenu = [{'url': reverse_lazy('ikiwiki:index'),
|
|||||||
'text': ugettext_lazy('Create')}]
|
'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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -70,7 +54,8 @@ def index(request):
|
|||||||
form = IkiwikiForm(initial=status, prefix='ikiwiki')
|
form = IkiwikiForm(initial=status, prefix='ikiwiki')
|
||||||
|
|
||||||
return TemplateResponse(request, 'ikiwiki.html',
|
return TemplateResponse(request, 'ikiwiki.html',
|
||||||
{'title': _('Wiki and Blog'),
|
{'title': ikiwiki.title,
|
||||||
|
'description': ikiwiki.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form,
|
'form': form,
|
||||||
'subsubmenu': subsubmenu})
|
'subsubmenu': subsubmenu})
|
||||||
|
|||||||
@ -20,20 +20,38 @@ Plinth module for using Let's Encrypt.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import json
|
|
||||||
|
|
||||||
from plinth import actions
|
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
|
||||||
from plinth.modules import names
|
from plinth.modules import names
|
||||||
|
from plinth.utils import format_lazy
|
||||||
|
|
||||||
|
|
||||||
depends = [
|
version = 1
|
||||||
'plinth.modules.apps',
|
|
||||||
'plinth.modules.names'
|
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
|
service = None
|
||||||
|
|
||||||
|
|
||||||
@ -44,6 +62,11 @@ def init():
|
|||||||
'glyphicon-lock', 'letsencrypt:index', 20)
|
'glyphicon-lock', 'letsencrypt:index', 20)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(helper, old_version=None):
|
||||||
|
"""Install and configure the module."""
|
||||||
|
helper.install(['letsencrypt'])
|
||||||
|
|
||||||
|
|
||||||
def diagnose():
|
def diagnose():
|
||||||
"""Run diagnostics and return the results."""
|
"""Run diagnostics and return the results."""
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -33,30 +33,7 @@
|
|||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
|
|||||||
@ -29,20 +29,20 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.errors import ActionError
|
from plinth.errors import ActionError
|
||||||
|
from plinth.modules import letsencrypt
|
||||||
from plinth.modules import names
|
from plinth.modules import names
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@package.required(['letsencrypt'])
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
|
|
||||||
return TemplateResponse(request, 'letsencrypt.html',
|
return TemplateResponse(request, 'letsencrypt.html',
|
||||||
{'title': _('Certificates (Let\'s Encrypt)'),
|
{'title': letsencrypt.title,
|
||||||
|
'description': letsencrypt.description,
|
||||||
'status': status})
|
'status': status})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,22 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from plinth import cfg
|
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():
|
def init():
|
||||||
@ -31,3 +46,8 @@ def init():
|
|||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Monkeysphere'), 'glyphicon-certificate',
|
menu.add_urlname(_('Monkeysphere'), 'glyphicon-certificate',
|
||||||
'monkeysphere:index', 970)
|
'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 %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -30,22 +30,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{% if running %}
|
{% if running %}
|
||||||
<p class="running-status-parent">
|
<p class="running-status-parent">
|
||||||
|
|||||||
@ -28,20 +28,20 @@ from django.views.decorators.http import require_POST
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
from plinth.modules import monkeysphere
|
||||||
from plinth.modules import names
|
from plinth.modules import names
|
||||||
|
|
||||||
publish_process = None
|
publish_process = None
|
||||||
|
|
||||||
|
|
||||||
@package.required(['monkeysphere'])
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
_collect_publish_result(request)
|
_collect_publish_result(request)
|
||||||
status = get_status()
|
status = get_status()
|
||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request, 'monkeysphere.html',
|
request, 'monkeysphere.html',
|
||||||
{'title': _('Monkeysphere'),
|
{'title': monkeysphere.title,
|
||||||
|
'description': monkeysphere.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'running': bool(publish_process)})
|
'running': bool(publish_process)})
|
||||||
|
|
||||||
|
|||||||
@ -21,13 +21,25 @@ Plinth module to configure Mumble server
|
|||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from plinth import actions
|
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -35,13 +47,17 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Intialize the Mumble module."""
|
"""Intialize the Mumble module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Voice Chat (Mumble)'), 'glyphicon-headphones',
|
menu.add_urlname(title, 'glyphicon-headphones', 'mumble:index', 900)
|
||||||
'mumble:index', 900)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'mumble-plinth', _('Mumble Voice Chat Server'),
|
'mumble-plinth', title, is_external=True, enabled=is_enabled())
|
||||||
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,25 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -26,18 +26,11 @@ import logging
|
|||||||
|
|
||||||
from .forms import MumbleForm
|
from .forms import MumbleForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import mumble
|
from plinth.modules import mumble
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -55,7 +48,8 @@ def index(request):
|
|||||||
form = MumbleForm(initial=status, prefix='mumble')
|
form = MumbleForm(initial=status, prefix='mumble')
|
||||||
|
|
||||||
return TemplateResponse(request, 'mumble.html',
|
return TemplateResponse(request, 'mumble.html',
|
||||||
{'title': _('Voice Chat (Mumble)'),
|
{'title': mumble.title,
|
||||||
|
'description': mumble.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,13 @@ SERVICES = (
|
|||||||
('ssh', _('SSH'), 22),
|
('ssh', _('SSH'), 22),
|
||||||
)
|
)
|
||||||
|
|
||||||
depends = ['plinth.modules.system']
|
version = 1
|
||||||
|
|
||||||
|
is_essential = True
|
||||||
|
|
||||||
|
depends = ['system']
|
||||||
|
|
||||||
|
title = _('Name Services')
|
||||||
|
|
||||||
domain_types = {}
|
domain_types = {}
|
||||||
domains = {}
|
domains = {}
|
||||||
@ -42,8 +48,7 @@ logger = logging.getLogger(__name__)
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the names module."""
|
"""Initialize the names module."""
|
||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Name Services'), 'glyphicon-tag',
|
menu.add_urlname(title, 'glyphicon-tag', 'names:index', 19)
|
||||||
'names:index', 19)
|
|
||||||
|
|
||||||
domain_added.connect(on_domain_added)
|
domain_added.connect(on_domain_added)
|
||||||
domain_removed.connect(on_domain_removed)
|
domain_removed.connect(on_domain_removed)
|
||||||
@ -54,6 +59,7 @@ def on_domain_added(sender, domain_type, name='', description='',
|
|||||||
"""Add domain to global list."""
|
"""Add domain to global list."""
|
||||||
if not domain_type:
|
if not domain_type:
|
||||||
return
|
return
|
||||||
|
|
||||||
domain_types[domain_type] = description
|
domain_types[domain_type] = description
|
||||||
|
|
||||||
if not name:
|
if not name:
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,9 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<h2>{{ title }}</h2>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-5">
|
<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 SERVICES, get_domain_types, get_description
|
||||||
from . import get_domain, get_services_status
|
from . import get_domain, get_services_status
|
||||||
|
from plinth.modules import names
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
@ -31,7 +32,7 @@ def index(request):
|
|||||||
status = get_status()
|
status = get_status()
|
||||||
|
|
||||||
return TemplateResponse(request, 'names.html',
|
return TemplateResponse(request, 'names.html',
|
||||||
{'title': _('Name Services'),
|
{'title': names.title,
|
||||||
'status': status})
|
'status': status})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,23 +19,37 @@
|
|||||||
Plinth module to interface with network-manager
|
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
|
from logging import Logger
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from . import networks
|
|
||||||
from .networks import init
|
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
|
from plinth import cfg
|
||||||
from plinth import network
|
from plinth import network
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['networks', 'init']
|
version = 1
|
||||||
|
|
||||||
depends = ['plinth.modules.system']
|
is_essential = True
|
||||||
|
|
||||||
|
depends = ['system']
|
||||||
|
|
||||||
|
title = _('Networks')
|
||||||
|
|
||||||
logger = Logger(__name__)
|
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():
|
def diagnose():
|
||||||
"""Run diagnostics and return the results."""
|
"""Run diagnostics and return the results."""
|
||||||
results = []
|
results = []
|
||||||
@ -44,8 +58,10 @@ def diagnose():
|
|||||||
addresses = _get_interface_addresses(interfaces)
|
addresses = _get_interface_addresses(interfaces)
|
||||||
|
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
results.append(action_utils.diagnose_port_listening(53, 'tcp', address))
|
results.append(
|
||||||
results.append(action_utils.diagnose_port_listening(53, 'udp', address))
|
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('4'))
|
||||||
results.append(_diagnose_dnssec('6'))
|
results.append(_diagnose_dnssec('6'))
|
||||||
|
|||||||
@ -25,9 +25,7 @@ from logging import Logger
|
|||||||
|
|
||||||
from .forms import (ConnectionTypeSelectForm, EthernetForm, PPPoEForm,
|
from .forms import (ConnectionTypeSelectForm, EthernetForm, PPPoEForm,
|
||||||
WifiForm)
|
WifiForm)
|
||||||
from plinth import cfg
|
|
||||||
from plinth import network
|
from plinth import network
|
||||||
from plinth import package
|
|
||||||
|
|
||||||
|
|
||||||
logger = Logger(__name__)
|
logger = Logger(__name__)
|
||||||
@ -40,14 +38,6 @@ subsubmenu = [{'url': reverse_lazy('networks:index'),
|
|||||||
'text': ugettext_lazy('Add Connection')}]
|
'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):
|
def index(request):
|
||||||
"""Show connection list."""
|
"""Show connection list."""
|
||||||
connections = network.get_connection_list()
|
connections = network.get_connection_list()
|
||||||
|
|||||||
@ -25,9 +25,25 @@ from plinth import actions
|
|||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -35,13 +51,16 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Intialize the OpenVPN module."""
|
"""Intialize the OpenVPN module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Virtual Private Network (OpenVPN)'), 'glyphicon-lock',
|
menu.add_urlname(title, 'glyphicon-lock', 'openvpn:index', 850)
|
||||||
'openvpn:index', 850)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'openvpn', _('OpenVPN'), ['openvpn'],
|
'openvpn', title, ['openvpn'], is_external=True, enabled=is_enabled())
|
||||||
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -30,21 +30,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{% if status.is_setup %}
|
{% if status.is_setup %}
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,6 @@ import logging
|
|||||||
|
|
||||||
from .forms import OpenVpnForm
|
from .forms import OpenVpnForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import openvpn
|
from plinth.modules import openvpn
|
||||||
from plinth.modules.config import config
|
from plinth.modules.config import config
|
||||||
|
|
||||||
@ -38,7 +37,6 @@ logger = logging.getLogger(__name__)
|
|||||||
setup_process = None
|
setup_process = None
|
||||||
|
|
||||||
|
|
||||||
@package.required(['openvpn', 'easy-rsa'])
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -59,7 +57,8 @@ def index(request):
|
|||||||
form = OpenVpnForm(initial=status, prefix='openvpn')
|
form = OpenVpnForm(initial=status, prefix='openvpn')
|
||||||
|
|
||||||
return TemplateResponse(request, 'openvpn.html',
|
return TemplateResponse(request, 'openvpn.html',
|
||||||
{'title': _('Virtual Private Network (OpenVPN)'),
|
{'title': openvpn.title,
|
||||||
|
'description': openvpn.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -19,14 +19,60 @@
|
|||||||
Plinth module to configure ownCloud
|
Plinth module to configure ownCloud
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import owncloud
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .owncloud import init
|
|
||||||
|
|
||||||
|
from plinth import actions
|
||||||
from plinth import action_utils
|
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():
|
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 %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,30 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{% include "diagnostics_button.html" with module="owncloud" %}
|
{% include "diagnostics_button.html" with module="owncloud" %}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ URLs for the ownCloud module
|
|||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import owncloud as views
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|||||||
@ -19,47 +19,15 @@
|
|||||||
Plinth module for configuring ownCloud.
|
Plinth module for configuring ownCloud.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from .forms import OwnCloudForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import cfg
|
from plinth.modules import owncloud
|
||||||
from plinth import package
|
|
||||||
from plinth import service as service_module
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
def index(request):
|
||||||
"""Serve the ownCloud configuration page"""
|
"""Serve the ownCloud configuration page"""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -77,14 +45,14 @@ def index(request):
|
|||||||
form = OwnCloudForm(initial=status, prefix='owncloud')
|
form = OwnCloudForm(initial=status, prefix='owncloud')
|
||||||
|
|
||||||
return TemplateResponse(request, 'owncloud.html',
|
return TemplateResponse(request, 'owncloud.html',
|
||||||
{'title': _('ownCloud'),
|
{'title': owncloud.title,
|
||||||
|
'description': owncloud.description,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|
||||||
def get_status():
|
def get_status():
|
||||||
"""Return the current status"""
|
"""Return the current status"""
|
||||||
output = actions.run('owncloud-setup', ['status'])
|
return {'enabled': owncloud.is_enabled()}
|
||||||
return {'enabled': 'enable' in output.split()}
|
|
||||||
|
|
||||||
|
|
||||||
def _apply_changes(request, old_status, new_status):
|
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
|
# Send a signal to other modules that the service is
|
||||||
# enabled/disabled
|
# 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 django.utils.translation import ugettext_lazy as _
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
|
from plinth.utils import format_lazy
|
||||||
|
|
||||||
from . import utils
|
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():
|
def init():
|
||||||
"""Intialize the PageKite module"""
|
"""Intialize the PageKite module"""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Public Visibility (PageKite)'),
|
menu.add_urlname(title, 'glyphicon-flag', 'pagekite:index', 800)
|
||||||
'glyphicon-flag', 'pagekite:index', 800)
|
|
||||||
|
|
||||||
# Register kite name with Name Services module.
|
# Register kite name with Name Services module.
|
||||||
utils.update_names_module(initial_registration=True)
|
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 %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -20,58 +20,7 @@
|
|||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-primary btn-md" href="{% url 'pagekite:configure' %}">
|
<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.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.http.response import HttpResponseRedirect
|
from django.http.response import HttpResponseRedirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import View, TemplateView
|
from django.views.generic import View, TemplateView
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
from plinth import package
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .forms import ConfigurationForm, StandardServiceForm, \
|
from .forms import ConfigurationForm, StandardServiceForm, \
|
||||||
AddCustomServiceForm, DeleteCustomServiceForm
|
AddCustomServiceForm, DeleteCustomServiceForm
|
||||||
|
from plinth.modules import pagekite
|
||||||
|
|
||||||
|
|
||||||
required_packages = ('pagekite',)
|
|
||||||
subsubmenu = [{'url': reverse_lazy('pagekite:index'),
|
subsubmenu = [{'url': reverse_lazy('pagekite:index'),
|
||||||
'text': _('About PageKite')},
|
'text': _('About PageKite')},
|
||||||
{'url': reverse_lazy('pagekite:configure'),
|
{'url': reverse_lazy('pagekite:configure'),
|
||||||
@ -43,7 +41,8 @@ subsubmenu = [{'url': reverse_lazy('pagekite:index'),
|
|||||||
def index(request):
|
def index(request):
|
||||||
"""Serve introduction page"""
|
"""Serve introduction page"""
|
||||||
return TemplateResponse(request, 'pagekite_introduction.html',
|
return TemplateResponse(request, 'pagekite_introduction.html',
|
||||||
{'title': _('Public Visibility (PageKite)'),
|
{'title': pagekite.title,
|
||||||
|
'description': pagekite.description,
|
||||||
'subsubmenu': subsubmenu})
|
'subsubmenu': subsubmenu})
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +58,6 @@ class ContextMixin(object):
|
|||||||
context['subsubmenu'] = subsubmenu
|
context['subsubmenu'] = subsubmenu
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@method_decorator(package.required(required_packages))
|
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
return super(ContextMixin, self).dispatch(*args, **kwargs)
|
return super(ContextMixin, self).dispatch(*args, **kwargs)
|
||||||
|
|
||||||
@ -81,8 +79,9 @@ class CustomServiceView(ContextMixin, TemplateView):
|
|||||||
unused, custom_services = utils.get_pagekite_services()
|
unused, custom_services = utils.get_pagekite_services()
|
||||||
for service in custom_services:
|
for service in custom_services:
|
||||||
service['form'] = AddCustomServiceForm(initial=service)
|
service['form'] = AddCustomServiceForm(initial=service)
|
||||||
context['custom_services'] = [utils.prepare_service_for_display(service)
|
context['custom_services'] = [
|
||||||
for service in custom_services]
|
utils.prepare_service_for_display(service)
|
||||||
|
for service in custom_services]
|
||||||
context.update(utils.get_kite_details())
|
context.update(utils.get_kite_details())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
@ -23,11 +23,20 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from plinth import cfg
|
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():
|
def init():
|
||||||
"""Initialize the power module."""
|
"""Initialize the power module."""
|
||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Power'), 'glyphicon-off',
|
menu.add_urlname(title, 'glyphicon-off', 'power:index', 1000)
|
||||||
'power:index', 1000)
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,13 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<h2>{{ title }}</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{% blocktrans trimmed %}Restart or shut down the system.{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-default btn-md" href="{% url 'power:restart' %}">
|
<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 django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
|
from plinth.modules import power
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve power controls page."""
|
"""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):
|
def restart(request):
|
||||||
|
|||||||
@ -20,15 +20,36 @@ Plinth module to configure Privoxy.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import json
|
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -36,13 +57,18 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Intialize the module."""
|
"""Intialize the module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Web Proxy (Privoxy)'), 'glyphicon-cloud-upload',
|
menu.add_urlname(title, 'glyphicon-cloud-upload', 'privoxy:index', 1000)
|
||||||
'privoxy:index', 1000)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'privoxy', _('Privoxy Web Proxy'),
|
'privoxy', title, is_external=False, enabled=is_enabled())
|
||||||
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():
|
def is_enabled():
|
||||||
@ -84,9 +110,7 @@ def diagnose_url_with_proxy():
|
|||||||
|
|
||||||
result = action_utils.diagnose_url(url, kind=address['kind'], env=env)
|
result = action_utils.diagnose_url(url, kind=address['kind'], env=env)
|
||||||
result[0] = _('Access {url} with proxy {proxy} on tcp{kind}') \
|
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)
|
results.append(result)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,29 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -26,19 +26,11 @@ import logging
|
|||||||
|
|
||||||
from .forms import PrivoxyForm
|
from .forms import PrivoxyForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import privoxy
|
from plinth.modules import privoxy
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -56,7 +48,8 @@ def index(request):
|
|||||||
form = PrivoxyForm(initial=status, prefix='privoxy')
|
form = PrivoxyForm(initial=status, prefix='privoxy')
|
||||||
|
|
||||||
return TemplateResponse(request, 'privoxy.html',
|
return TemplateResponse(request, 'privoxy.html',
|
||||||
{'title': _('Web Proxy (Privoxy)'),
|
{'title': privoxy.title,
|
||||||
|
'description': privoxy.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Plinth module for quassel.
|
Plinth module for Quassel.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -33,13 +55,17 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the quassel module."""
|
"""Initialize the quassel module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('IRC Client (Quassel)'), 'glyphicon-retweet',
|
menu.add_urlname(title, 'glyphicon-retweet', 'quassel:index', 730)
|
||||||
'quassel:index', 730)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'quassel-plinth', _('Quassel IRC Client'),
|
'quassel-plinth', title, is_external=True, enabled=is_enabled())
|
||||||
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,31 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -25,16 +25,9 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from .forms import QuasselForm
|
from .forms import QuasselForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import quassel
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -51,7 +44,8 @@ def index(request):
|
|||||||
form = QuasselForm(initial=status, prefix='quassel')
|
form = QuasselForm(initial=status, prefix='quassel')
|
||||||
|
|
||||||
return TemplateResponse(request, 'quassel.html',
|
return TemplateResponse(request, 'quassel.html',
|
||||||
{'title': _('IRC Client (Quassel)'),
|
{'title': quassel.title,
|
||||||
|
'description': quassel.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -21,11 +21,37 @@ Plinth module for repro.
|
|||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from plinth import actions
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -33,13 +59,19 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the repro module."""
|
"""Initialize the repro module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('SIP Server (repro)'), 'glyphicon-phone-alt',
|
menu.add_urlname(title, 'glyphicon-phone-alt', 'repro:index', 825)
|
||||||
'repro:index', 825)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'repro', _('repro SIP Server'), ['sip-plinth', 'sip-tls-plinth'],
|
'repro', title, ['sip-plinth', 'sip-tls-plinth'], is_external=True,
|
||||||
is_external=True, enabled=is_enabled())
|
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,39 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -25,17 +25,9 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from .forms import ReproForm
|
from .forms import ReproForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import repro
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -52,7 +44,8 @@ def index(request):
|
|||||||
form = ReproForm(initial=status, prefix='repro')
|
form = ReproForm(initial=status, prefix='repro')
|
||||||
|
|
||||||
return TemplateResponse(request, 'repro.html',
|
return TemplateResponse(request, 'repro.html',
|
||||||
{'title': _('SIP Server (repro)'),
|
{'title': repro.title,
|
||||||
|
'description': repro.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -22,24 +22,44 @@ Plinth module to configure reStore.
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from plinth import action_utils, cfg
|
from plinth import action_utils, cfg
|
||||||
from plinth import service as service_module
|
from plinth import service as service_module
|
||||||
|
from plinth.utils import format_lazy
|
||||||
|
|
||||||
service = None
|
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():
|
def init():
|
||||||
"""Initialize the reStore module."""
|
"""Initialize the reStore module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Unhosted Storage (reStore)'), 'glyphicon-hdd',
|
menu.add_urlname(title, 'glyphicon-hdd', 'restore:index', 750)
|
||||||
'restore:index', 750)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'node-restore', _('reStore'), ['http', 'https'],
|
'node-restore', title, ['http', 'https'], is_external=False,
|
||||||
is_external=False, enabled=is_enabled())
|
enabled=is_enabled())
|
||||||
|
|
||||||
|
|
||||||
|
def setup(helper, old_version=None):
|
||||||
|
"""Install and configure the module."""
|
||||||
|
helper.install(['node-restore'])
|
||||||
|
|
||||||
|
|
||||||
def is_enabled():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,27 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>Configuration</h3>
|
<h3>Configuration</h3>
|
||||||
|
|
||||||
|
|||||||
@ -24,11 +24,10 @@ from django.template.response import TemplateResponse
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from .forms import ReStoreForm
|
from .forms import ReStoreForm
|
||||||
from plinth import actions, package
|
from plinth import actions
|
||||||
from plinth.modules import restore
|
from plinth.modules import restore
|
||||||
|
|
||||||
|
|
||||||
@package.required(['node-restore'])
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -43,7 +42,8 @@ def index(request):
|
|||||||
form = ReStoreForm(initial=status, prefix='restore')
|
form = ReStoreForm(initial=status, prefix='restore')
|
||||||
|
|
||||||
return TemplateResponse(request, 'restore_index.html',
|
return TemplateResponse(request, 'restore_index.html',
|
||||||
{'title': _('Unhosted Storage (reStore)'),
|
{'title': restore.title,
|
||||||
|
'description': restore.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -24,17 +24,49 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
from plinth import cfg
|
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():
|
def init():
|
||||||
"""Intialize the module."""
|
"""Intialize the module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Email Client (Roundcube)'), 'glyphicon-envelope',
|
menu.add_urlname(title, 'glyphicon-envelope', 'roundcube:index', 600)
|
||||||
'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():
|
def is_enabled():
|
||||||
"""Return whether the module is enabled."""
|
"""Return whether the module is enabled."""
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,41 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{% include "diagnostics_button.html" with module="roundcube" %}
|
{% include "diagnostics_button.html" with module="roundcube" %}
|
||||||
|
|
||||||
|
|||||||
@ -26,24 +26,11 @@ import logging
|
|||||||
|
|
||||||
from .forms import RoundcubeForm
|
from .forms import RoundcubeForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import roundcube
|
from plinth.modules import roundcube
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -61,7 +48,8 @@ def index(request):
|
|||||||
form = RoundcubeForm(initial=status, prefix='roundcube')
|
form = RoundcubeForm(initial=status, prefix='roundcube')
|
||||||
|
|
||||||
return TemplateResponse(request, 'roundcube.html',
|
return TemplateResponse(request, 'roundcube.html',
|
||||||
{'title': _('Email Client (Roundcube)'),
|
{'title': roundcube.title,
|
||||||
|
'description': roundcube.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,20 @@ from plinth import cfg
|
|||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -34,13 +47,18 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the module."""
|
"""Initialize the module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Bookmarks (Shaarli)'), 'glyphicon-bookmark',
|
menu.add_urlname(title, 'glyphicon-bookmark', 'shaarli:index', 350)
|
||||||
'shaarli:index', 350)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'shaarli', _('Shaarli'), ['http', 'https'],
|
'shaarli', title, ['http', 'https'], is_external=True,
|
||||||
is_external=True, enabled=is_enabled())
|
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,21 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Configuration" %}</h3>
|
<h3>{% trans "Configuration" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -25,14 +25,9 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from .forms import ShaarliForm
|
from .forms import ShaarliForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import shaarli
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -49,7 +44,8 @@ def index(request):
|
|||||||
form = ShaarliForm(initial=status, prefix='shaarli')
|
form = ShaarliForm(initial=status, prefix='shaarli')
|
||||||
|
|
||||||
return TemplateResponse(request, 'shaarli.html',
|
return TemplateResponse(request, 'shaarli.html',
|
||||||
{'title': _('Bookmarks (Shaarli)'),
|
{'title': shaarli.title,
|
||||||
|
'description': shaarli.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -19,8 +19,29 @@
|
|||||||
Plinth module for system section page
|
Plinth module for system section page
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import system
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .system import init
|
|
||||||
|
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 %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -17,25 +17,3 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
{% endcomment %}
|
{% 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 django.conf.urls import url
|
||||||
|
|
||||||
from . import system as views
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|||||||
@ -16,18 +16,12 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from plinth import cfg
|
from plinth.modules import system
|
||||||
|
|
||||||
|
|
||||||
def init():
|
|
||||||
"""Initialize the system module"""
|
|
||||||
cfg.main_menu.add_urlname(_('System'), 'glyphicon-cog', 'system:index',
|
|
||||||
100)
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Serve the index page"""
|
"""Serve the index page"""
|
||||||
return TemplateResponse(request, 'system.html',
|
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
|
import augeas
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import glob
|
import glob
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
@ -32,7 +32,20 @@ from plinth.modules.names import SERVICES
|
|||||||
from plinth.signals import domain_added
|
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
|
socks_service = None
|
||||||
bridge_service = None
|
bridge_service = None
|
||||||
@ -45,8 +58,7 @@ APT_TOR_PREFIX = 'tor+'
|
|||||||
def init():
|
def init():
|
||||||
"""Initialize the module."""
|
"""Initialize the module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Anonymity Network (Tor)'), 'glyphicon-eye-close',
|
menu.add_urlname(title, 'glyphicon-eye-close', 'tor:index', 100)
|
||||||
'tor:index', 100)
|
|
||||||
|
|
||||||
global socks_service
|
global socks_service
|
||||||
socks_service = service_module.Service(
|
socks_service = service_module.Service(
|
||||||
@ -77,6 +89,17 @@ def init():
|
|||||||
services=hs_services)
|
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():
|
def is_enabled():
|
||||||
"""Return whether the module is enabled."""
|
"""Return whether the module is enabled."""
|
||||||
return action_utils.service_is_enabled('tor')
|
return action_utils.service_is_enabled('tor')
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -30,20 +30,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h3>{% trans "Status" %}</h3>
|
<h3>{% trans "Status" %}</h3>
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from .forms import TorForm
|
from .forms import TorForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.errors import ActionError
|
from plinth.errors import ActionError
|
||||||
from plinth.modules import tor
|
from plinth.modules import tor
|
||||||
from plinth.modules.names import SERVICES
|
from plinth.modules.names import SERVICES
|
||||||
@ -34,18 +33,6 @@ from plinth.signals import domain_added, domain_removed
|
|||||||
config_process = None
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
if config_process:
|
if config_process:
|
||||||
@ -65,7 +52,8 @@ def index(request):
|
|||||||
form = TorForm(initial=status, prefix='tor')
|
form = TorForm(initial=status, prefix='tor')
|
||||||
|
|
||||||
return TemplateResponse(request, 'tor.html',
|
return TemplateResponse(request, 'tor.html',
|
||||||
{'title': _('Tor Control Panel'),
|
{'title': tor.title,
|
||||||
|
'description': tor.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'config_running': bool(config_process),
|
'config_running': bool(config_process),
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|||||||
@ -20,6 +20,7 @@ Plinth module to configure Transmission server
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
import json
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
@ -27,7 +28,17 @@ from plinth import cfg
|
|||||||
from plinth import service as service_module
|
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
|
service = None
|
||||||
|
|
||||||
@ -35,13 +46,25 @@ service = None
|
|||||||
def init():
|
def init():
|
||||||
"""Intialize the Transmission module."""
|
"""Intialize the Transmission module."""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('BitTorrent (Transmission)'), 'glyphicon-save',
|
menu.add_urlname(title, 'glyphicon-save', 'transmission:index', 300)
|
||||||
'transmission:index', 300)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'transmission', _('Transmission BitTorrent'), ['http', 'https'],
|
'transmission', title, ['http', 'https'], is_external=True,
|
||||||
is_external=True, enabled=is_enabled())
|
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():
|
def is_enabled():
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,17 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
|
|||||||
@ -28,7 +28,6 @@ import socket
|
|||||||
|
|
||||||
from .forms import TransmissionForm
|
from .forms import TransmissionForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import transmission
|
from plinth.modules import transmission
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -36,17 +35,6 @@ logger = logging.getLogger(__name__)
|
|||||||
TRANSMISSION_CONFIG = '/etc/transmission-daemon/settings.json'
|
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):
|
def index(request):
|
||||||
"""Serve configuration page."""
|
"""Serve configuration page."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -64,7 +52,8 @@ def index(request):
|
|||||||
form = TransmissionForm(initial=status, prefix='transmission')
|
form = TransmissionForm(initial=status, prefix='transmission')
|
||||||
|
|
||||||
return TemplateResponse(request, 'transmission.html',
|
return TemplateResponse(request, 'transmission.html',
|
||||||
{'title': _('BitTorrent (Transmission)'),
|
{'title': transmission.title,
|
||||||
|
'description': transmission.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|||||||
@ -21,14 +21,32 @@ Plinth module for upgrades
|
|||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from plinth import actions
|
||||||
from plinth import cfg
|
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():
|
def init():
|
||||||
"""Initialize the module."""
|
"""Initialize the module."""
|
||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Software Upgrades'), 'glyphicon-refresh',
|
menu.add_urlname(title, 'glyphicon-refresh', 'upgrades:index', 21)
|
||||||
'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 %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -29,17 +29,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,9 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<h2>{{ title }}</h2>
|
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|||||||
@ -21,16 +21,14 @@ Plinth module for upgrades
|
|||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
from django.views.decorators.http import require_POST
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from .forms import ConfigureForm
|
from .forms import ConfigureForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.errors import ActionError
|
from plinth.errors import ActionError
|
||||||
|
from plinth.modules import upgrades
|
||||||
|
|
||||||
subsubmenu = [{'url': reverse_lazy('upgrades:index'),
|
subsubmenu = [{'url': reverse_lazy('upgrades:index'),
|
||||||
'text': ugettext_lazy('Automatic Upgrades')},
|
'text': ugettext_lazy('Automatic Upgrades')},
|
||||||
@ -41,12 +39,6 @@ LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
|
|||||||
LOCK_FILE = '/var/log/dpkg/lock'
|
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):
|
def index(request):
|
||||||
"""Serve the configuration form."""
|
"""Serve the configuration form."""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -63,10 +55,12 @@ def index(request):
|
|||||||
form = ConfigureForm(initial=status, prefix='upgrades')
|
form = ConfigureForm(initial=status, prefix='upgrades')
|
||||||
|
|
||||||
return TemplateResponse(request, 'upgrades_configure.html',
|
return TemplateResponse(request, 'upgrades_configure.html',
|
||||||
{'title': _('Automatic Upgrades'),
|
{'title': upgrades.title,
|
||||||
|
'description': upgrades.description,
|
||||||
'form': form,
|
'form': form,
|
||||||
'subsubmenu': subsubmenu})
|
'subsubmenu': subsubmenu})
|
||||||
|
|
||||||
|
|
||||||
def is_package_manager_busy():
|
def is_package_manager_busy():
|
||||||
"""Return whether a package manager is running."""
|
"""Return whether a package manager is running."""
|
||||||
try:
|
try:
|
||||||
@ -85,7 +79,6 @@ def get_log():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@package.required(['unattended-upgrades'], on_install=on_install)
|
|
||||||
def upgrade(request):
|
def upgrade(request):
|
||||||
"""Serve the upgrade page."""
|
"""Serve the upgrade page."""
|
||||||
is_busy = is_package_manager_busy()
|
is_busy = is_package_manager_busy()
|
||||||
@ -99,7 +92,8 @@ def upgrade(request):
|
|||||||
messages.error(request, _('Starting upgrade failed.'))
|
messages.error(request, _('Starting upgrade failed.'))
|
||||||
|
|
||||||
return TemplateResponse(request, 'upgrades.html',
|
return TemplateResponse(request, 'upgrades.html',
|
||||||
{'title': _('Package Upgrades'),
|
{'title': upgrades.title,
|
||||||
|
'description': upgrades.description,
|
||||||
'subsubmenu': subsubmenu,
|
'subsubmenu': subsubmenu,
|
||||||
'is_busy': is_busy,
|
'is_busy': is_busy,
|
||||||
'log': get_log()})
|
'log': get_log()})
|
||||||
|
|||||||
@ -20,21 +20,24 @@ Plinth module to manage users
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import json
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from plinth import cfg
|
from plinth import cfg
|
||||||
from plinth import actions
|
|
||||||
from plinth import action_utils
|
from plinth import action_utils
|
||||||
|
|
||||||
depends = ['plinth.modules.system']
|
version = 1
|
||||||
|
|
||||||
|
is_essential = True
|
||||||
|
|
||||||
|
depends = ['system']
|
||||||
|
|
||||||
|
title = _('Users and Groups')
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
"""Intialize the user module."""
|
"""Intialize the user module."""
|
||||||
menu = cfg.main_menu.get('system:index')
|
menu = cfg.main_menu.get('system:index')
|
||||||
menu.add_urlname(_('Users and Groups'), 'glyphicon-user', 'users:index',
|
menu.add_urlname(title, 'glyphicon-user', 'users:index', 15)
|
||||||
15)
|
|
||||||
|
|
||||||
|
|
||||||
def diagnose():
|
def diagnose():
|
||||||
|
|||||||
@ -20,7 +20,8 @@ Plinth module to configure XMPP server
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import json
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import action_utils
|
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
|
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
|
service = None
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
"""Initialize the XMPP module"""
|
"""Initialize the XMPP module"""
|
||||||
menu = cfg.main_menu.get('apps:index')
|
menu = cfg.main_menu.get('apps:index')
|
||||||
menu.add_urlname(_('Chat Server (XMPP)'), 'glyphicon-comment',
|
menu.add_urlname(title, 'glyphicon-comment', 'xmpp:index', 400)
|
||||||
'xmpp:index', 400)
|
|
||||||
|
|
||||||
global service
|
global service
|
||||||
service = service_module.Service(
|
service = service_module.Service(
|
||||||
'xmpp', _('Chat Server (XMPP)'),
|
'xmpp', title, ['xmpp-client', 'xmpp-server', 'xmpp-bosh'],
|
||||||
['xmpp-client', 'xmpp-server', 'xmpp-bosh'],
|
|
||||||
is_external=True, enabled=is_enabled())
|
is_external=True, enabled=is_enabled())
|
||||||
|
|
||||||
pre_hostname_change.connect(on_pre_hostname_change)
|
pre_hostname_change.connect(on_pre_hostname_change)
|
||||||
@ -52,6 +67,18 @@ def init():
|
|||||||
domainname_change.connect(on_domainname_change)
|
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():
|
def is_enabled():
|
||||||
"""Return whether the module is enabled."""
|
"""Return whether the module is enabled."""
|
||||||
return (action_utils.service_is_enabled('ejabberd') and
|
return (action_utils.service_is_enabled('ejabberd') and
|
||||||
@ -63,6 +90,12 @@ def is_running():
|
|||||||
return action_utils.service_is_running('ejabberd')
|
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):
|
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
||||||
"""
|
"""
|
||||||
Backup ejabberd database before hostname is changed.
|
Backup ejabberd database before hostname is changed.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "app.html" %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
#
|
#
|
||||||
# This file is part of Plinth.
|
# This file is part of Plinth.
|
||||||
@ -21,24 +21,7 @@
|
|||||||
{% load bootstrap %}
|
{% load bootstrap %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block configuration %}
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% url 'config:index' as index_url %}
|
{% url 'config:index' as index_url %}
|
||||||
|
|||||||
@ -23,39 +23,15 @@ from django.contrib import messages
|
|||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
import logging
|
import logging
|
||||||
import socket
|
|
||||||
|
|
||||||
from .forms import XmppForm
|
from .forms import XmppForm
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
from plinth import package
|
|
||||||
from plinth.modules import xmpp
|
from plinth.modules import xmpp
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def index(request):
|
||||||
"""Serve configuration page"""
|
"""Serve configuration page"""
|
||||||
status = get_status()
|
status = get_status()
|
||||||
@ -72,7 +48,8 @@ def index(request):
|
|||||||
form = XmppForm(initial=status, prefix='xmpp')
|
form = XmppForm(initial=status, prefix='xmpp')
|
||||||
|
|
||||||
return TemplateResponse(request, 'xmpp.html',
|
return TemplateResponse(request, 'xmpp.html',
|
||||||
{'title': _('Chat Server (XMPP)'),
|
{'title': xmpp.title,
|
||||||
|
'description': xmpp.description,
|
||||||
'status': status,
|
'status': status,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
@ -81,7 +58,7 @@ def get_status():
|
|||||||
"""Get the current settings."""
|
"""Get the current settings."""
|
||||||
status = {'enabled': xmpp.is_enabled(),
|
status = {'enabled': xmpp.is_enabled(),
|
||||||
'is_running': xmpp.is_running(),
|
'is_running': xmpp.is_running(),
|
||||||
'domainname': get_domainname()}
|
'domainname': xmpp.get_domainname()}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|||||||
@ -19,13 +19,9 @@
|
|||||||
Framework for installing and updating distribution packages
|
Framework for installing and updating distribution packages
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
import functools
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
|
||||||
|
|
||||||
import plinth
|
|
||||||
from plinth.utils import import_from_gi
|
from plinth.utils import import_from_gi
|
||||||
glib = import_from_gi('GLib', '2.0')
|
glib = import_from_gi('GLib', '2.0')
|
||||||
packagekit = import_from_gi('PackageKitGlib', '1.0')
|
packagekit = import_from_gi('PackageKitGlib', '1.0')
|
||||||
@ -46,19 +42,21 @@ class PackageException(Exception):
|
|||||||
self.error_string = error_string
|
self.error_string = error_string
|
||||||
self.error_details = error_details
|
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):
|
class Transaction(object):
|
||||||
"""Information about an ongoing transaction."""
|
"""Information about an ongoing transaction."""
|
||||||
|
|
||||||
def __init__(self, package_names, before_install=None, on_install=None):
|
def __init__(self, package_names):
|
||||||
"""Initialize transaction object.
|
"""Initialize transaction object.
|
||||||
|
|
||||||
Set most values to None until they are sent as progress update.
|
Set most values to None until they are sent as progress update.
|
||||||
"""
|
"""
|
||||||
self.package_names = package_names
|
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
|
# Progress
|
||||||
self.allow_cancel = None
|
self.allow_cancel = None
|
||||||
@ -74,10 +72,6 @@ class Transaction(object):
|
|||||||
self.download_size_remaining = None
|
self.download_size_remaining = None
|
||||||
self.speed = None
|
self.speed = None
|
||||||
|
|
||||||
# Completion
|
|
||||||
self.is_finished = False
|
|
||||||
self.exception = None
|
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
"""Return a identifier to use as a key in a map of transactions."""
|
"""Return a identifier to use as a key in a map of transactions."""
|
||||||
return frozenset(self.package_names)
|
return frozenset(self.package_names)
|
||||||
@ -89,43 +83,12 @@ class Transaction(object):
|
|||||||
self.package_names, self.allow_cancel, self.status_string,
|
self.package_names, self.allow_cancel, self.status_string,
|
||||||
self.percentage, self.package, self.item_progress)
|
self.percentage, self.package, self.item_progress)
|
||||||
|
|
||||||
def start_install(self):
|
def 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):
|
|
||||||
"""Run a PackageKit transaction to install given packages."""
|
"""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:
|
try:
|
||||||
self._do_install()
|
self._do_install()
|
||||||
except PackageException as exception:
|
|
||||||
self.finish(exception)
|
|
||||||
return
|
|
||||||
except glib.Error as exception:
|
except glib.Error as exception:
|
||||||
self.finish(PackageException(exception.message))
|
raise PackageException(exception.message) from exception
|
||||||
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()
|
|
||||||
|
|
||||||
def _do_install(self):
|
def _do_install(self):
|
||||||
"""Run a PackageKit transaction to install given packages.
|
"""Run a PackageKit transaction to install given packages.
|
||||||
@ -203,115 +166,3 @@ class Transaction(object):
|
|||||||
else:
|
else:
|
||||||
logger.info('Unhandle packagekit progress callback - %s, %s',
|
logger.info('Unhandle packagekit progress callback - %s, %s',
|
||||||
progress, progress_type)
|
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