mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
Use Django dispatcher instead of CherryPy dispatcher
This commit is big because anything small breaks the code. - Django dispatcher is based on regular expressions and does not need a tree structure - Reduces a lot of unnecessary dependencies among modules - Use Django sessions middlewear instead of CherryPy sessions - Introduce dependency based modules instead of numeric load order - Remove PagePlugin and simply use Django views - Eliminate page rendering wrappers in favor of Django context processors - Use custom auth for now until replaced by Django auth middlewear - Use Django templated 404 and 500 error pages
This commit is contained in:
parent
657bb11bbc
commit
58d13e3ed8
2
LICENSES
2
LICENSES
@ -52,7 +52,7 @@ specified and linked otherwise.
|
|||||||
- modules/first_boot/first_boot.py :: -
|
- modules/first_boot/first_boot.py :: -
|
||||||
- modules/help/help.py :: -
|
- modules/help/help.py :: -
|
||||||
- modules/lib/auth_page.py :: -
|
- modules/lib/auth_page.py :: -
|
||||||
- modules/lib/auth.py :: Derived from [[http://tools.cherrypy.org/wiki/AuthenticationAndAccessRestrictions][Arnar Birisson's CherryPy wiki code]].
|
- modules/lib/auth.py :: -
|
||||||
- modules/lib/user_store.py :: -
|
- modules/lib/user_store.py :: -
|
||||||
- modules/owncloud/owncloud.py :: -
|
- modules/owncloud/owncloud.py :: -
|
||||||
- modules/packages/packages.py :: -
|
- modules/packages/packages.py :: -
|
||||||
|
|||||||
6
Makefile
6
Makefile
@ -31,7 +31,7 @@ install: default apache-install freedombox-setup-install
|
|||||||
cp share/init.d/plinth $(DESTDIR)/etc/init.d
|
cp share/init.d/plinth $(DESTDIR)/etc/init.d
|
||||||
cp -a lib/* $(DESTDIR)/usr/lib
|
cp -a lib/* $(DESTDIR)/usr/lib
|
||||||
install plinth $(DESTDIR)/usr/bin/
|
install plinth $(DESTDIR)/usr/bin/
|
||||||
mkdir -p $(DESTDIR)/var/lib/plinth/cherrypy_sessions $(DESTDIR)/var/log/plinth $(DESTDIR)/var/run
|
mkdir -p $(DESTDIR)/var/lib/plinth/sessions $(DESTDIR)/var/log/plinth $(DESTDIR)/var/run
|
||||||
mkdir -p $(DESTDIR)/var/lib/plinth/data
|
mkdir -p $(DESTDIR)/var/lib/plinth/data
|
||||||
rm -f $(DESTDIR)/var/lib/plinth/users/sqlite3.distrib
|
rm -f $(DESTDIR)/var/lib/plinth/users/sqlite3.distrib
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ uninstall:
|
|||||||
$(DESTDIR)/usr/share/man/man1/plinth.1.gz $(DESTDIR)/var/run/plinth.pid
|
$(DESTDIR)/usr/share/man/man1/plinth.1.gz $(DESTDIR)/var/run/plinth.pid
|
||||||
|
|
||||||
dirs:
|
dirs:
|
||||||
@mkdir -p data/cherrypy_sessions
|
@mkdir -p data/sessions
|
||||||
|
|
||||||
config: Makefile
|
config: Makefile
|
||||||
@test -f plinth.config || cp plinth.sample.config plinth.config
|
@test -f plinth.config || cp plinth.sample.config plinth.config
|
||||||
@ -59,7 +59,7 @@ html:
|
|||||||
@$(MAKE) -s -C doc html
|
@$(MAKE) -s -C doc html
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f cherrypy.config data/cherrypy_sessions/*
|
@rm -f cherrypy.config data/sessions/*
|
||||||
@find . -name "*~" -exec rm {} \;
|
@find . -name "*~" -exec rm {} \;
|
||||||
@find . -name ".#*" -exec rm {} \;
|
@find . -name ".#*" -exec rm {} \;
|
||||||
@find . -name "#*" -exec rm {} \;
|
@find . -name "#*" -exec rm {} \;
|
||||||
|
|||||||
1
cfg.py
1
cfg.py
@ -32,7 +32,6 @@ pidfile = get_item(parser, 'Path', 'pidfile')
|
|||||||
host = get_item(parser, 'Network', 'host')
|
host = get_item(parser, 'Network', 'host')
|
||||||
port = int(get_item(parser, 'Network', 'port'))
|
port = int(get_item(parser, 'Network', 'port'))
|
||||||
|
|
||||||
html_root = None
|
|
||||||
main_menu = Menu()
|
main_menu = Menu()
|
||||||
|
|
||||||
if store_file.endswith(".sqlite3"):
|
if store_file.endswith(".sqlite3"):
|
||||||
|
|||||||
16
menu.py
16
menu.py
@ -1,5 +1,4 @@
|
|||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
import cherrypy
|
|
||||||
import cfg
|
import cfg
|
||||||
|
|
||||||
|
|
||||||
@ -59,16 +58,17 @@ class Menu(object):
|
|||||||
self.sort_items()
|
self.sort_items()
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def active_p(self):
|
def is_active(self, request_path):
|
||||||
"""Returns True if this menu item is active, otherwise False.
|
"""
|
||||||
|
Returns True if this menu item is active, otherwise False.
|
||||||
|
|
||||||
We can tell if a menu is active if the menu item points
|
We can tell if a menu is active if the menu item points
|
||||||
anywhere above url we are visiting in the url tree."""
|
anywhere above url we are visiting in the url tree.
|
||||||
return urlparse(cherrypy.url()).path.startswith(self.url)
|
"""
|
||||||
|
return request_path.startswith(self.url)
|
||||||
|
|
||||||
def active_item(self):
|
def active_item(self, request):
|
||||||
"""Return item list (e.g. submenu) of active menu item."""
|
"""Return item list (e.g. submenu) of active menu item."""
|
||||||
path = urlparse(cherrypy.url()).path
|
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if path.startswith(item.url):
|
if request.path.startswith(item.url):
|
||||||
return item
|
return item
|
||||||
|
|||||||
137
module_loader.py
Normal file
137
module_loader.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Discover, load and manage Plinth modules
|
||||||
|
"""
|
||||||
|
|
||||||
|
import django
|
||||||
|
import importlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
import cfg
|
||||||
|
import urls
|
||||||
|
|
||||||
|
|
||||||
|
def load_modules():
|
||||||
|
"""
|
||||||
|
Read names of enabled modules in modules/enabled directory and
|
||||||
|
import them from modules directory.
|
||||||
|
"""
|
||||||
|
module_names = []
|
||||||
|
modules = {}
|
||||||
|
for name in os.listdir('modules/enabled'):
|
||||||
|
full_name = 'modules.{module}'.format(module=name)
|
||||||
|
|
||||||
|
cfg.log.info('Importing {full_name}'.format(full_name=full_name))
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(full_name)
|
||||||
|
modules[name] = module
|
||||||
|
module_names.append(name)
|
||||||
|
except ImportError as exception:
|
||||||
|
cfg.log.error(
|
||||||
|
'Could not import modules/{module}: {exception}'
|
||||||
|
.format(module=name, exception=exception))
|
||||||
|
|
||||||
|
_include_module_urls(full_name)
|
||||||
|
|
||||||
|
ordered_modules = []
|
||||||
|
remaining_modules = dict(modules)
|
||||||
|
for module_name in modules:
|
||||||
|
if module_name not in remaining_modules:
|
||||||
|
continue
|
||||||
|
|
||||||
|
module = remaining_modules.pop(module_name)
|
||||||
|
try:
|
||||||
|
_insert_modules(module_name, module, remaining_modules,
|
||||||
|
ordered_modules)
|
||||||
|
except KeyError:
|
||||||
|
cfg.log.error('Unsatified dependency for module - %s' %
|
||||||
|
(module_name,))
|
||||||
|
|
||||||
|
cfg.log.debug('Module load order - %s' % ordered_modules)
|
||||||
|
|
||||||
|
for module_name in ordered_modules:
|
||||||
|
_initialize_module(modules[module_name])
|
||||||
|
|
||||||
|
|
||||||
|
def _insert_modules(module_name, module, remaining_modules, ordered_modules):
|
||||||
|
"""Insert modules into a list based on dependency order"""
|
||||||
|
if module_name in ordered_modules:
|
||||||
|
return
|
||||||
|
|
||||||
|
dependencies = []
|
||||||
|
try:
|
||||||
|
dependencies = module.DEPENDS
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for dependency in dependencies:
|
||||||
|
if dependency in ordered_modules:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
module = remaining_modules.pop(dependency)
|
||||||
|
except KeyError:
|
||||||
|
cfg.log.error('Not found or circular dependency - %s, %s' %
|
||||||
|
(module_name, dependency))
|
||||||
|
raise
|
||||||
|
|
||||||
|
_insert_modules(dependency, module, remaining_modules, ordered_modules)
|
||||||
|
|
||||||
|
ordered_modules.append(module_name)
|
||||||
|
|
||||||
|
|
||||||
|
def _include_module_urls(module_name):
|
||||||
|
"""Include the module's URLs in global project URLs list"""
|
||||||
|
url_module = module_name + '.urls'
|
||||||
|
try:
|
||||||
|
urls.urlpatterns += django.conf.urls.patterns(
|
||||||
|
'', django.conf.urls.url(
|
||||||
|
r'', django.conf.urls.include(url_module)))
|
||||||
|
except ImportError:
|
||||||
|
cfg.log.debug('No URLs for {module}'.format(module=module_name))
|
||||||
|
|
||||||
|
|
||||||
|
def _initialize_module(module):
|
||||||
|
"""Call initialization method in the module if it exists"""
|
||||||
|
try:
|
||||||
|
init = module.init
|
||||||
|
except AttributeError:
|
||||||
|
cfg.log.debug('No init() for module - {module}'
|
||||||
|
.format(module=module.__name__))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
init()
|
||||||
|
except Exception as exception:
|
||||||
|
cfg.log.error('Exception while running init for {module}: {exception}'
|
||||||
|
.format(module=module, exception=exception))
|
||||||
|
|
||||||
|
|
||||||
|
def get_template_directories():
|
||||||
|
"""Return the list of template directories"""
|
||||||
|
directory = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
core_directory = os.path.join(directory, 'templates')
|
||||||
|
|
||||||
|
directories = set((core_directory,))
|
||||||
|
for name in os.listdir('modules/enabled'):
|
||||||
|
directories.add(os.path.join('modules', name, 'templates'))
|
||||||
|
|
||||||
|
cfg.log.info('Template directories - %s' % directories)
|
||||||
|
|
||||||
|
return directories
|
||||||
@ -16,10 +16,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Plinth module for apps section page
|
Plinth module for Apps section page
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import apps
|
from . import apps
|
||||||
|
from .apps import init
|
||||||
|
|
||||||
|
__all__ = ['apps', 'init']
|
||||||
__all__ = ['apps']
|
|
||||||
|
|||||||
@ -1,19 +1,14 @@
|
|||||||
import cherrypy
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class Apps(PagePlugin):
|
def init():
|
||||||
def __init__(self):
|
"""Initailize the apps module"""
|
||||||
super(Apps, self).__init__()
|
cfg.main_menu.add_item("Apps", "icon-download-alt", "/apps", 80)
|
||||||
|
|
||||||
self.register_page("apps")
|
|
||||||
self.menu = cfg.main_menu.add_item("Apps", "icon-download-alt", "/apps", 80)
|
|
||||||
self.menu.add_item("Chat", "icon-comment", "/../jwchat", 30)
|
|
||||||
|
|
||||||
@cherrypy.expose
|
def index(request):
|
||||||
def index(self):
|
"""Serve the apps index page"""
|
||||||
return util.render_template(template='apps',
|
return TemplateResponse(request, 'apps.html', {'title': _('Applications')})
|
||||||
title=_('User Applications'))
|
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module for basic system configuration
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import config
|
from . import config
|
||||||
|
from .config import init
|
||||||
|
|
||||||
|
__all__ = ['config', 'init']
|
||||||
|
|
||||||
__all__ = ['config']
|
DEPENDS = ['system']
|
||||||
|
|||||||
@ -19,17 +19,16 @@
|
|||||||
Plinth module for configuring timezone, hostname etc.
|
Plinth module for configuring timezone, hostname etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
from ..lib.auth import require
|
from ..lib.auth import login_required
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import util
|
import util
|
||||||
|
|
||||||
|
|
||||||
@ -89,66 +88,66 @@ and must not be greater than 63 characters in length.'),
|
|||||||
return time_zones
|
return time_zones
|
||||||
|
|
||||||
|
|
||||||
class Configuration(PagePlugin):
|
def init():
|
||||||
"""System configuration page"""
|
"""Initialize the module"""
|
||||||
def __init__(self):
|
menu = cfg.main_menu.find('/sys')
|
||||||
super(Configuration, self).__init__()
|
menu.add_item(_('Configure'), 'icon-cog', '/sys/config', 10)
|
||||||
|
|
||||||
self.register_page('sys.config')
|
|
||||||
|
|
||||||
self.menu = cfg.html_root.sys.menu.add_item(_('Configure'), 'icon-cog',
|
@login_required
|
||||||
'/sys/config', 10)
|
def index(request):
|
||||||
|
"""Serve the configuration form"""
|
||||||
|
status = get_status()
|
||||||
|
|
||||||
@cherrypy.expose
|
form = None
|
||||||
@require()
|
messages = []
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the configuration form"""
|
|
||||||
status = self.get_status()
|
|
||||||
|
|
||||||
form = None
|
is_expert = cfg.users.expert(request=request)
|
||||||
messages = []
|
if request.method == 'POST' and is_expert:
|
||||||
|
form = ConfigurationForm(request.POST, prefix='configuration')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
|
if form.is_valid():
|
||||||
|
_apply_changes(status, form.cleaned_data, messages)
|
||||||
|
status = get_status()
|
||||||
|
form = ConfigurationForm(initial=status,
|
||||||
|
prefix='configuration')
|
||||||
|
else:
|
||||||
|
form = ConfigurationForm(initial=status, prefix='configuration')
|
||||||
|
|
||||||
if kwargs and cfg.users.expert():
|
return TemplateResponse(request, 'config.html',
|
||||||
form = ConfigurationForm(kwargs, prefix='configuration')
|
{'title': _('General Configuration'),
|
||||||
# pylint: disable-msg=E1101
|
'form': form,
|
||||||
if form.is_valid():
|
'messages_': messages,
|
||||||
self._apply_changes(status, form.cleaned_data, messages)
|
'is_expert': is_expert})
|
||||||
status = self.get_status()
|
|
||||||
form = ConfigurationForm(initial=status,
|
|
||||||
prefix='configuration')
|
def get_status():
|
||||||
|
"""Return the current status"""
|
||||||
|
return {'hostname': get_hostname(),
|
||||||
|
'time_zone': util.slurp('/etc/timezone').rstrip()}
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_changes(old_status, new_status, messages):
|
||||||
|
"""Apply the form changes"""
|
||||||
|
if old_status['hostname'] != new_status['hostname']:
|
||||||
|
if not set_hostname(new_status['hostname']):
|
||||||
|
messages.append(('error', _('Setting hostname failed')))
|
||||||
else:
|
else:
|
||||||
form = ConfigurationForm(initial=status, prefix='configuration')
|
|
||||||
|
|
||||||
return util.render_template(template='config',
|
|
||||||
title=_('General Configuration'),
|
|
||||||
form=form, messages=messages)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_status():
|
|
||||||
"""Return the current status"""
|
|
||||||
return {'hostname': get_hostname(),
|
|
||||||
'time_zone': util.slurp('/etc/timezone').rstrip()}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _apply_changes(old_status, new_status, messages):
|
|
||||||
"""Apply the form changes"""
|
|
||||||
if old_status['hostname'] != new_status['hostname']:
|
|
||||||
set_hostname(new_status['hostname'])
|
|
||||||
messages.append(('success', _('Hostname set')))
|
messages.append(('success', _('Hostname set')))
|
||||||
else:
|
else:
|
||||||
messages.append(('info', _('Hostname is unchanged')))
|
messages.append(('info', _('Hostname is unchanged')))
|
||||||
|
|
||||||
if old_status['time_zone'] != new_status['time_zone']:
|
if old_status['time_zone'] != new_status['time_zone']:
|
||||||
output, error = actions.superuser_run('timezone-change',
|
output, error = actions.superuser_run('timezone-change',
|
||||||
[new_status['time_zone']])
|
[new_status['time_zone']])
|
||||||
del output # Unused
|
del output # Unused
|
||||||
if error:
|
if error:
|
||||||
messages.append(('error',
|
messages.append(('error',
|
||||||
_('Error setting time zone - %s') % error))
|
_('Error setting time zone - %s') % error))
|
||||||
else:
|
|
||||||
messages.append(('success', _('Time zone set')))
|
|
||||||
else:
|
else:
|
||||||
messages.append(('info', _('Time zone is unchanged')))
|
messages.append(('success', _('Time zone set')))
|
||||||
|
else:
|
||||||
|
messages.append(('info', _('Time zone is unchanged')))
|
||||||
|
|
||||||
|
|
||||||
def set_hostname(hostname):
|
def set_hostname(hostname):
|
||||||
@ -165,6 +164,7 @@ def set_hostname(hostname):
|
|||||||
# don't persist/cache change unless it was saved successfuly
|
# don't persist/cache change unless it was saved successfuly
|
||||||
sys_store = util.filedict_con(cfg.store_file, 'sys')
|
sys_store = util.filedict_con(cfg.store_file, 'sys')
|
||||||
sys_store['hostname'] = hostname
|
sys_store['hostname'] = hostname
|
||||||
except OSError as exception:
|
except OSError:
|
||||||
raise cherrypy.HTTPError(500,
|
return False
|
||||||
'Updating hostname failed: %s' % exception)
|
|
||||||
|
return True
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
{% block main_block %}
|
{% block main_block %}
|
||||||
|
|
||||||
{% if cfg.users.expert %}
|
{% if is_expert %}
|
||||||
|
|
||||||
{% include 'messages.html' %}
|
{% include 'messages.html' %}
|
||||||
|
|
||||||
|
|||||||
@ -16,10 +16,12 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Plinth module to system diagnostics
|
Plinth module for system diagnostics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import diagnostics
|
from . import diagnostics
|
||||||
|
from .diagnostics import init
|
||||||
|
|
||||||
|
__all__ = ['diagnostics', 'init']
|
||||||
|
|
||||||
__all__ = ['diagnostics']
|
DEPENDS = ['system']
|
||||||
|
|||||||
@ -19,41 +19,32 @@
|
|||||||
Plinth module for running diagnostics
|
Plinth module for running diagnostics
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from ..lib.auth import require
|
from django.template.response import TemplateResponse
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
from ..lib.auth import login_required
|
||||||
|
|
||||||
|
|
||||||
class Diagnostics(PagePlugin):
|
def init():
|
||||||
order = 30
|
"""Initialize the module"""
|
||||||
def __init__(self):
|
menu = cfg.main_menu.find('/sys')
|
||||||
super(Diagnostics, self).__init__()
|
menu.add_item("Diagnostics", "icon-screenshot", "/sys/diagnostics", 30)
|
||||||
|
|
||||||
self.register_page("sys.diagnostics")
|
|
||||||
cfg.html_root.sys.menu.add_item("Diagnostics", "icon-screenshot", "/sys/diagnostics", 30)
|
|
||||||
|
|
||||||
@cherrypy.expose
|
@login_required
|
||||||
@require()
|
def index(request):
|
||||||
def index(self):
|
"""Serve the index page"""
|
||||||
return util.render_template(template='diagnostics',
|
return TemplateResponse(request, 'diagnostics.html',
|
||||||
title=_('System Diagnostics'))
|
{'title': _('System Diagnostics')})
|
||||||
|
|
||||||
class Test(PagePlugin):
|
|
||||||
order = 31
|
|
||||||
def __init__(self):
|
|
||||||
super(Test, self).__init__()
|
|
||||||
|
|
||||||
self.register_page("sys.diagnostics.test")
|
@login_required
|
||||||
|
def test(request):
|
||||||
@cherrypy.expose
|
"""Run diagnostics and the output page"""
|
||||||
@require()
|
output, error = actions.superuser_run("diagnostic-test")
|
||||||
def index(self):
|
return TemplateResponse(request, 'diagnostics_test.html',
|
||||||
output, error = actions.superuser_run("diagnostic-test")
|
{'title': _('Diagnostic Test'),
|
||||||
return util.render_template(template='diagnostics_test',
|
'diagnostics_output': output,
|
||||||
title=_('Diagnostic Test'),
|
'diagnostics_error': error})
|
||||||
diagnostics_output=output,
|
|
||||||
diagnostics_error=error)
|
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module for expert mode configuration
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import expert_mode
|
from . import expert_mode
|
||||||
|
from .expert_mode import init
|
||||||
|
|
||||||
|
__all__ = ['expert_mode', 'init']
|
||||||
|
|
||||||
__all__ = ['expert_mode']
|
DEPENDS = ['system']
|
||||||
|
|||||||
@ -1,80 +1,66 @@
|
|||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from ..lib.auth import require
|
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
from ..lib.auth import login_required
|
||||||
|
|
||||||
|
|
||||||
class ExpertsForm(forms.Form): # pylint: disable-msg=W0232
|
class ExpertsForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
"""Form to configure expert mode"""
|
"""Form to configure expert mode"""
|
||||||
|
|
||||||
expert_mode = forms.BooleanField(
|
expert_mode = forms.BooleanField(
|
||||||
label=_('Expert Mode'), required=False)
|
label=_('Expert Mode'), required=False)
|
||||||
|
|
||||||
# XXX: Only present due to issue with submitting empty form
|
|
||||||
dummy = forms.CharField(label='Dummy', initial='dummy',
|
def init():
|
||||||
widget=forms.HiddenInput())
|
"""Initialize the module"""
|
||||||
|
menu = cfg.main_menu.find('/sys')
|
||||||
|
menu.add_item(_('Expert Mode'), 'icon-cog', '/sys/expert', 10)
|
||||||
|
|
||||||
|
|
||||||
class Experts(PagePlugin):
|
@login_required
|
||||||
"""Expert forms page"""
|
def index(request):
|
||||||
order = 60
|
"""Serve the configuration form"""
|
||||||
|
status = get_status(request)
|
||||||
|
|
||||||
def __init__(self):
|
form = None
|
||||||
super(Experts, self).__init__()
|
messages = []
|
||||||
|
|
||||||
self.register_page('sys.expert')
|
if request.method == 'POST':
|
||||||
|
form = ExpertsForm(request.POST, prefix='experts')
|
||||||
cfg.html_root.sys.menu.add_item(_('Expert Mode'), 'icon-cog',
|
# pylint: disable-msg=E1101
|
||||||
'/sys/expert', 10)
|
if form.is_valid():
|
||||||
|
_apply_changes(request, form.cleaned_data, messages)
|
||||||
@cherrypy.expose
|
status = get_status(request)
|
||||||
@require()
|
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the configuration form"""
|
|
||||||
status = self.get_status()
|
|
||||||
|
|
||||||
cfg.log.info('Args - %s' % kwargs)
|
|
||||||
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = ExpertsForm(kwargs, prefix='experts')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._apply_changes(form.cleaned_data, messages)
|
|
||||||
status = self.get_status()
|
|
||||||
form = ExpertsForm(initial=status, prefix='experts')
|
|
||||||
else:
|
|
||||||
form = ExpertsForm(initial=status, prefix='experts')
|
form = ExpertsForm(initial=status, prefix='experts')
|
||||||
|
else:
|
||||||
|
form = ExpertsForm(initial=status, prefix='experts')
|
||||||
|
|
||||||
return util.render_template(template='expert_mode',
|
return TemplateResponse(request, 'expert_mode.html',
|
||||||
title=_('Expert Mode'), form=form,
|
{'title': _('Expert Mode'),
|
||||||
messages=messages)
|
'form': form,
|
||||||
|
'messages_': messages})
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_status():
|
|
||||||
"""Return the current status"""
|
|
||||||
return {'expert_mode': cfg.users.expert()}
|
|
||||||
|
|
||||||
@staticmethod
|
def get_status(request):
|
||||||
def _apply_changes(new_status, messages):
|
"""Return the current status"""
|
||||||
"""Apply expert mode configuration"""
|
return {'expert_mode': cfg.users.expert(request=request)}
|
||||||
message = ('info', _('Settings unchanged'))
|
|
||||||
|
|
||||||
user = cfg.users.current()
|
|
||||||
|
|
||||||
if new_status['expert_mode']:
|
def _apply_changes(request, new_status, messages):
|
||||||
if not 'expert' in user['groups']:
|
"""Apply expert mode configuration"""
|
||||||
user['groups'].append('expert')
|
message = ('info', _('Settings unchanged'))
|
||||||
message = ('success', _('Expert mode enabled'))
|
|
||||||
else:
|
|
||||||
if 'expert' in user['groups']:
|
|
||||||
user['groups'].remove('expert')
|
|
||||||
message = ('success', _('Expert mode disabled'))
|
|
||||||
|
|
||||||
cfg.users.set(user['username'], user)
|
user = cfg.users.current(request=request)
|
||||||
messages.append(message)
|
|
||||||
|
if new_status['expert_mode']:
|
||||||
|
if not 'expert' in user['groups']:
|
||||||
|
user['groups'].append('expert')
|
||||||
|
message = ('success', _('Expert mode enabled'))
|
||||||
|
else:
|
||||||
|
if 'expert' in user['groups']:
|
||||||
|
user['groups'].remove('expert')
|
||||||
|
message = ('success', _('Expert mode disabled'))
|
||||||
|
|
||||||
|
cfg.users.set(user['username'], user)
|
||||||
|
messages.append(message)
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to configure a firewall
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import firewall
|
from . import firewall
|
||||||
|
from .firewall import init
|
||||||
|
|
||||||
|
__all__ = ['firewall', 'init']
|
||||||
|
|
||||||
__all__ = ['firewall']
|
DEPENDS = ['system']
|
||||||
|
|||||||
@ -19,136 +19,134 @@
|
|||||||
Plinth module to configure a firewall
|
Plinth module to configure a firewall
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
from ..lib.auth import require
|
from ..lib.auth import login_required
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import service as service_module
|
import service as service_module
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class Firewall(PagePlugin):
|
def init():
|
||||||
"""Firewall menu entry and introduction page"""
|
"""Initailze firewall module"""
|
||||||
order = 40
|
menu = cfg.main_menu.find('/sys')
|
||||||
|
menu.add_item(_('Firewall'), 'icon-flag', '/sys/firewall', 50)
|
||||||
|
|
||||||
def __init__(self):
|
service_module.ENABLED.connect(on_service_enabled)
|
||||||
super(Firewall, self).__init__()
|
|
||||||
|
|
||||||
self.register_page('sys.firewall')
|
|
||||||
cfg.html_root.sys.menu.add_item(_('Firewall'), 'icon-flag',
|
|
||||||
'/sys/firewall', 50)
|
|
||||||
|
|
||||||
service_module.ENABLED.connect(self.on_service_enabled)
|
@login_required
|
||||||
|
def index(request):
|
||||||
|
"""Serve introcution page"""
|
||||||
|
if not get_installed_status():
|
||||||
|
return TemplateResponse(request, 'firewall.html',
|
||||||
|
{'title': _('Firewall'),
|
||||||
|
'firewall_status': 'not_installed'})
|
||||||
|
|
||||||
@cherrypy.expose
|
if not get_enabled_status():
|
||||||
@require()
|
return TemplateResponse(request, 'firewall.html',
|
||||||
def index(self):
|
{'title': _('Firewall'),
|
||||||
"""Serve introcution page"""
|
'firewall_status': 'not_running'})
|
||||||
if not self.get_installed_status():
|
|
||||||
return util.render_template(template='firewall',
|
|
||||||
title=_("Firewall"),
|
|
||||||
firewall_status='not_installed')
|
|
||||||
|
|
||||||
if not self.get_enabled_status():
|
internal_enabled_services = get_enabled_services(zone='internal')
|
||||||
return util.render_template(template='firewall',
|
external_enabled_services = get_enabled_services(zone='external')
|
||||||
title=_("Firewall"),
|
|
||||||
firewall_status='not_running')
|
|
||||||
|
|
||||||
internal_enabled_services = self.get_enabled_services(zone='internal')
|
return TemplateResponse(
|
||||||
external_enabled_services = self.get_enabled_services(zone='external')
|
request, 'firewall.html',
|
||||||
|
{'title': _('Firewall'),
|
||||||
|
'services': service_module.SERVICES.values(),
|
||||||
|
'internal_enabled_services': internal_enabled_services,
|
||||||
|
'external_enabled_services': external_enabled_services})
|
||||||
|
|
||||||
return util.render_template(
|
|
||||||
template='firewall', title=_('Firewall'),
|
|
||||||
services=service_module.SERVICES.values(),
|
|
||||||
internal_enabled_services=internal_enabled_services,
|
|
||||||
external_enabled_services=external_enabled_services)
|
|
||||||
|
|
||||||
def get_installed_status(self):
|
def get_installed_status():
|
||||||
"""Return whether firewall is installed"""
|
"""Return whether firewall is installed"""
|
||||||
output = self._run(['get-installed'], superuser=True)
|
output = _run(['get-installed'], superuser=True)
|
||||||
return output.split()[0] == 'installed'
|
return output.split()[0] == 'installed'
|
||||||
|
|
||||||
def get_enabled_status(self):
|
|
||||||
"""Return whether firewall is installed"""
|
|
||||||
output = self._run(['get-status'], superuser=True)
|
|
||||||
return output.split()[0] == 'running'
|
|
||||||
|
|
||||||
def get_enabled_services(self, zone):
|
def get_enabled_status():
|
||||||
"""Return the status of various services currently enabled"""
|
"""Return whether firewall is installed"""
|
||||||
output = self._run(['get-enabled-services', '--zone', zone],
|
output = _run(['get-status'], superuser=True)
|
||||||
superuser=True)
|
return output.split()[0] == 'running'
|
||||||
return output.split()
|
|
||||||
|
|
||||||
def add_service(self, port, zone):
|
|
||||||
"""Enable a service in firewall"""
|
|
||||||
self._run(['add-service', port, '--zone', zone], superuser=True)
|
|
||||||
|
|
||||||
def remove_service(self, port, zone):
|
def get_enabled_services(zone):
|
||||||
"""Remove a service in firewall"""
|
"""Return the status of various services currently enabled"""
|
||||||
self._run(['remove-service', port, '--zone', zone], superuser=True)
|
output = _run(['get-enabled-services', '--zone', zone], superuser=True)
|
||||||
|
return output.split()
|
||||||
|
|
||||||
def on_service_enabled(self, sender, service_id, enabled, **kwargs):
|
|
||||||
"""
|
|
||||||
Enable/disable firewall ports when a service is
|
|
||||||
enabled/disabled.
|
|
||||||
"""
|
|
||||||
del sender # Unused
|
|
||||||
del kwargs # Unused
|
|
||||||
|
|
||||||
internal_enabled_services = self.get_enabled_services(zone='internal')
|
def add_service(port, zone):
|
||||||
external_enabled_services = self.get_enabled_services(zone='external')
|
"""Enable a service in firewall"""
|
||||||
|
_run(['add-service', port, '--zone', zone], superuser=True)
|
||||||
|
|
||||||
cfg.log.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:
|
|
||||||
self.add_service(port, zone='internal')
|
|
||||||
|
|
||||||
if (service.is_external and
|
def remove_service(port, zone):
|
||||||
port not in external_enabled_services):
|
"""Remove a service in firewall"""
|
||||||
self.add_service(port, zone='external')
|
_run(['remove-service', port, '--zone', zone], superuser=True)
|
||||||
else:
|
|
||||||
# service already configured.
|
|
||||||
pass
|
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')
|
||||||
|
|
||||||
|
cfg.log.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:
|
else:
|
||||||
if port in internal_enabled_services:
|
# service already configured.
|
||||||
enabled_services_on_port = [
|
pass
|
||||||
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):
|
|
||||||
self.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):
|
|
||||||
self.remove_service(port, zone='external')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _run(arguments, superuser=False):
|
|
||||||
"""Run an given command and raise exception if there was an error"""
|
|
||||||
command = 'firewall'
|
|
||||||
|
|
||||||
cfg.log.info('Running command - %s, %s, %s' % (command, arguments,
|
|
||||||
superuser))
|
|
||||||
|
|
||||||
if superuser:
|
|
||||||
output, error = actions.superuser_run(command, arguments)
|
|
||||||
else:
|
else:
|
||||||
output, error = actions.run(command, arguments)
|
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 error:
|
if port in external_enabled_services:
|
||||||
raise Exception('Error setting/getting firewalld confguration - %s'
|
enabled_services_on_port = [
|
||||||
% error)
|
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')
|
||||||
|
|
||||||
return output
|
|
||||||
|
def _run(arguments, superuser=False):
|
||||||
|
"""Run an given command and raise exception if there was an error"""
|
||||||
|
command = 'firewall'
|
||||||
|
|
||||||
|
cfg.log.info('Running command - %s, %s, %s' % (command, arguments,
|
||||||
|
superuser))
|
||||||
|
|
||||||
|
if superuser:
|
||||||
|
output, error = actions.superuser_run(command, arguments)
|
||||||
|
else:
|
||||||
|
output, error = actions.run(command, arguments)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise Exception('Error setting/getting firewalld confguration - %s'
|
||||||
|
% error)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|||||||
@ -18,16 +18,16 @@ The Plinth first-connection process has several stages:
|
|||||||
4. The user interacts with the box normally.
|
4. The user interacts with the box normally.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django.http.response import HttpResponseRedirect
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
from ..lib.auth import add_user
|
from ..lib.auth import add_user
|
||||||
from ..config import config
|
from ..config import config
|
||||||
from withsqlite.withsqlite import sqlite_db
|
from withsqlite.withsqlite import sqlite_db
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
## TODO: flesh out these tests values
|
## TODO: flesh out these tests values
|
||||||
@ -66,132 +66,124 @@ below. This key should not be the same as your key because you are not your \
|
|||||||
FreedomBox!'))
|
FreedomBox!'))
|
||||||
|
|
||||||
|
|
||||||
class FirstBoot(PagePlugin):
|
def index(request):
|
||||||
"""First boot wizard"""
|
"""Serve the index first boot page"""
|
||||||
|
return state0(request)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(FirstBoot, self).__init__()
|
|
||||||
|
|
||||||
# this is the url this page will hang off of (/firstboot)
|
def generate_box_key():
|
||||||
self.register_page('firstboot')
|
"""Generate a box key"""
|
||||||
self.register_page('firstboot/state0')
|
return "fake key"
|
||||||
self.register_page('firstboot/state1')
|
|
||||||
|
|
||||||
@cherrypy.expose
|
|
||||||
def index(self, *args, **kwargs):
|
|
||||||
return self.state0(*args, **kwargs)
|
|
||||||
|
|
||||||
def generate_box_key(self):
|
def state0(request):
|
||||||
return "fake key"
|
"""
|
||||||
|
In this state, we do time config over HTTP, name the box and
|
||||||
|
server key selection.
|
||||||
|
|
||||||
@cherrypy.expose
|
All the parameters are form inputs. They get passed in when
|
||||||
def state0(self, **kwargs):
|
the form is submitted. This method checks the inputs and if
|
||||||
"""
|
they validate, uses them to take action. If they do not
|
||||||
In this state, we do time config over HTTP, name the box and
|
validate, it displays the form to give the user a chance to
|
||||||
server key selection.
|
input correct values. It might display an error message (in
|
||||||
|
the message parameter).
|
||||||
|
|
||||||
All the parameters are form inputs. They get passed in when
|
message is an optional string that we can display to the
|
||||||
the form is submitted. This method checks the inputs and if
|
user. It's a good place to put error messages.
|
||||||
they validate, uses them to take action. If they do not
|
"""
|
||||||
validate, it displays the form to give the user a chance to
|
try:
|
||||||
input correct values. It might display an error message (in
|
if _read_state() >= 5:
|
||||||
the message parameter).
|
return HttpResponseRedirect(cfg.server_dir)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
message is an optional string that we can display to the
|
## Until LDAP is in place, we'll put the box key in the cfg.store_file
|
||||||
user. It's a good place to put error messages.
|
status = get_state0()
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if FirstBoot._read_state() >= 5:
|
|
||||||
raise cherrypy.HTTPRedirect(cfg.server_dir, 302)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
## Until LDAP is in place, we'll put the box key in the cfg.store_file
|
form = None
|
||||||
status = self.get_state0()
|
messages = []
|
||||||
|
|
||||||
form = None
|
if request.method == 'POST':
|
||||||
messages = []
|
form = State0Form(request.POST, prefix='firstboot')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
|
if form.is_valid():
|
||||||
|
success = _apply_state0(status, form.cleaned_data, messages)
|
||||||
|
|
||||||
if kwargs:
|
if success:
|
||||||
form = State0Form(kwargs, prefix='firstboot')
|
# Everything is good, permanently mark and move to page 2
|
||||||
# pylint: disable-msg=E1101
|
_write_state(1)
|
||||||
if form.is_valid():
|
return HttpResponseRedirect(
|
||||||
success = self._apply_state0(status, form.cleaned_data,
|
cfg.server_dir + '/firstboot/state1')
|
||||||
messages)
|
else:
|
||||||
|
form = State0Form(initial=status, prefix='firstboot')
|
||||||
|
|
||||||
if success:
|
return TemplateResponse(request, 'firstboot_state0.html',
|
||||||
# Everything is good, permanently mark and move to page 2
|
{'title': _('First Boot!'),
|
||||||
FirstBoot._write_state(1)
|
'form': form,
|
||||||
raise cherrypy.HTTPRedirect('state1', 302)
|
'messages_': messages})
|
||||||
|
|
||||||
|
|
||||||
|
def get_state0():
|
||||||
|
"""Return the state for form state 0"""
|
||||||
|
with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as \
|
||||||
|
database:
|
||||||
|
return {'hostname': config.get_hostname(),
|
||||||
|
'box_key': database.get('box_key', None)}
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_state0(old_state, new_state, messages):
|
||||||
|
"""Apply changes in state 0 form"""
|
||||||
|
success = True
|
||||||
|
with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as \
|
||||||
|
database:
|
||||||
|
database['about'] = 'Information about this FreedomBox'
|
||||||
|
|
||||||
|
if new_state['box_key']:
|
||||||
|
database['box_key'] = new_state['box_key']
|
||||||
|
elif not old_state['box_key']:
|
||||||
|
database['box_key'] = generate_box_key()
|
||||||
|
|
||||||
|
if old_state['hostname'] != new_state['hostname']:
|
||||||
|
config.set_hostname(new_state['hostname'])
|
||||||
|
|
||||||
|
error = add_user(new_state['username'], new_state['password'],
|
||||||
|
'First user, please change', '', True)
|
||||||
|
if error:
|
||||||
|
messages.append(
|
||||||
|
('error', _('User account creation failed: %s') % error))
|
||||||
|
success = False
|
||||||
else:
|
else:
|
||||||
form = State0Form(initial=status, prefix='firstboot')
|
messages.append(('success', _('User account created')))
|
||||||
|
|
||||||
return util.render_template(template='firstboot_state0',
|
return success
|
||||||
title=_('First Boot!'), form=form,
|
|
||||||
messages=messages)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_state0():
|
|
||||||
"""Return the state for form state 0"""
|
|
||||||
with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as \
|
|
||||||
database:
|
|
||||||
return {'hostname': config.get_hostname(),
|
|
||||||
'box_key': database.get('box_key', None)}
|
|
||||||
|
|
||||||
def _apply_state0(self, old_state, new_state, messages):
|
def state1(request):
|
||||||
"""Apply changes in state 0 form"""
|
"""
|
||||||
success = True
|
State 1 is when we have a box name and key. In this state,
|
||||||
with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as \
|
our task is to provide a certificate and maybe to guide the
|
||||||
database:
|
user through installing it. We automatically move to State 2,
|
||||||
database['about'] = 'Information about this FreedomBox'
|
which is an HTTPS connection.
|
||||||
|
|
||||||
if new_state['box_key']:
|
TODO: HTTPS failure in State 2 should returns to state 1.
|
||||||
database['box_key'] = new_state['box_key']
|
"""
|
||||||
elif not old_state['box_key']:
|
# TODO complete first_boot handling
|
||||||
database['box_key'] = self.generate_box_key()
|
# Make sure the user is not stuck on a dead end for now.
|
||||||
|
_write_state(5)
|
||||||
|
|
||||||
if old_state['hostname'] != new_state['hostname']:
|
return TemplateResponse(request, 'firstboot_state1.html',
|
||||||
config.set_hostname(new_state['hostname'])
|
{'title': _('Installing the Certificate')})
|
||||||
|
|
||||||
error = add_user(new_state['username'], new_state['password'],
|
|
||||||
'First user, please change', '', True)
|
|
||||||
if error:
|
|
||||||
messages.append(
|
|
||||||
('error', _('User account creation failed: %s') % error))
|
|
||||||
success = False
|
|
||||||
else:
|
|
||||||
messages.append(('success', _('User account created')))
|
|
||||||
|
|
||||||
return success
|
def _read_state():
|
||||||
|
"""Read the current state from database"""
|
||||||
|
with sqlite_db(cfg.store_file, table='firstboot',
|
||||||
|
autocommit=True) as database:
|
||||||
|
return database['state']
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@cherrypy.expose
|
|
||||||
def state1():
|
|
||||||
"""
|
|
||||||
State 1 is when we have a box name and key. In this state,
|
|
||||||
our task is to provide a certificate and maybe to guide the
|
|
||||||
user through installing it. We automatically move to State 2,
|
|
||||||
which is an HTTPS connection.
|
|
||||||
|
|
||||||
TODO: HTTPS failure in State 2 should returns to state 1.
|
def _write_state(state):
|
||||||
"""
|
"""Write state to database"""
|
||||||
# TODO complete first_boot handling
|
with sqlite_db(cfg.store_file, table='firstboot',
|
||||||
# Make sure the user is not stuck on a dead end for now.
|
autocommit=True) as database:
|
||||||
FirstBoot._write_state(5)
|
database['state'] = state
|
||||||
|
|
||||||
return util.render_template(template='firstboot_state1',
|
|
||||||
title=_('Installing the Certificate'))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _read_state():
|
|
||||||
"""Read the current state from database"""
|
|
||||||
with sqlite_db(cfg.store_file, table='firstboot',
|
|
||||||
autocommit=True) as database:
|
|
||||||
return database['state']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _write_state(state):
|
|
||||||
"""Write state to database"""
|
|
||||||
with sqlite_db(cfg.store_file, table='firstboot',
|
|
||||||
autocommit=True) as database:
|
|
||||||
database['state'] = state
|
|
||||||
|
|||||||
@ -20,6 +20,6 @@ Plinth module for help pages
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import help # pylint: disable-msg=W0622
|
from . import help # pylint: disable-msg=W0622
|
||||||
|
from .help import init
|
||||||
|
|
||||||
|
__all__ = ['help', 'init']
|
||||||
__all__ = ['help']
|
|
||||||
|
|||||||
@ -1,49 +1,40 @@
|
|||||||
import os
|
import os
|
||||||
import cherrypy
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from plugin_mount import PagePlugin
|
from django.template.response import TemplateResponse
|
||||||
|
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class Help(PagePlugin):
|
def init():
|
||||||
order = 20 # order of running init in PagePlugins
|
"""Initialize the Help module"""
|
||||||
|
menu = cfg.main_menu.add_item(_('Documentation'), 'icon-book', '/help',
|
||||||
def __init__(self):
|
101)
|
||||||
super(Help, self).__init__()
|
menu.add_item(_("Where to Get Help"), "icon-search", "/help/index", 5)
|
||||||
|
menu.add_item(_('Developer\'s Manual'), 'icon-info-sign',
|
||||||
self.register_page("help")
|
'/help/view/plinth', 10)
|
||||||
self.menu = cfg.main_menu.add_item(_("Documentation"), "icon-book", "/help", 101)
|
menu.add_item(_('FAQ'), 'icon-question-sign', '/help/view/faq', 20)
|
||||||
self.menu.add_item(_("Where to Get Help"), "icon-search", "/help/index", 5)
|
menu.add_item(_('%s Wiki' % cfg.box_name), 'icon-pencil',
|
||||||
self.menu.add_item(_("Developer's Manual"), "icon-info-sign", "/help/view/plinth", 10)
|
'http://wiki.debian.org/FreedomBox', 30)
|
||||||
self.menu.add_item(_("FAQ"), "icon-question-sign", "/help/view/faq", 20)
|
menu.add_item(_('About'), 'icon-star', '/help/about', 100)
|
||||||
self.menu.add_item(_("%s Wiki" % cfg.box_name), "icon-pencil", "http://wiki.debian.org/FreedomBox", 30)
|
|
||||||
self.menu.add_item(_("About"), "icon-star", "/help/about", 100)
|
|
||||||
|
|
||||||
@cherrypy.expose
|
|
||||||
def index(self):
|
|
||||||
return util.render_template(template='help',
|
|
||||||
title=_('Documentation and FAQ'))
|
|
||||||
|
|
||||||
@cherrypy.expose
|
|
||||||
def about(self):
|
|
||||||
return util.render_template(
|
|
||||||
template='about',
|
|
||||||
title=_('About the {box_name}').format(box_name=cfg.box_name))
|
|
||||||
|
|
||||||
|
|
||||||
class View(PagePlugin):
|
def index(request):
|
||||||
def __init__(self):
|
"""Serve the index page"""
|
||||||
super(View, self).__init__()
|
return TemplateResponse(request, 'help.html',
|
||||||
|
{'title': _('Documentation and FAQ')})
|
||||||
|
|
||||||
self.register_page("help.view")
|
|
||||||
|
|
||||||
@cherrypy.expose
|
def about(request):
|
||||||
def default(self, page=''):
|
"""Serve the about page"""
|
||||||
if page not in ['design', 'plinth', 'hacking', 'faq']:
|
title = _('About the {box_name}').format(box_name=cfg.box_name)
|
||||||
raise cherrypy.HTTPError(404, "The path '/help/view/%s' was not found." % page)
|
return TemplateResponse(request, 'about.html', {'title': title})
|
||||||
|
|
||||||
with open(os.path.join("doc", "%s.part.html" % page), 'r') as IF:
|
|
||||||
main = IF.read()
|
def default(request, page=''):
|
||||||
return util.render_template(title=_("%s Documentation" %
|
"""Serve the documentation pages"""
|
||||||
cfg.product_name), main=main)
|
with open(os.path.join('doc', '%s.part.html' % page), 'r') as input_file:
|
||||||
|
main = input_file.read()
|
||||||
|
|
||||||
|
title = _('%s Documentation') % cfg.product_name
|
||||||
|
return TemplateResponse(request, 'login_nav.html',
|
||||||
|
{'title': title, 'main': main})
|
||||||
|
|||||||
@ -1,20 +1,13 @@
|
|||||||
# Form based authentication for CherryPy. Requires the
|
from django.http.response import HttpResponseRedirect
|
||||||
# Session tool to be loaded.
|
import functools
|
||||||
#
|
|
||||||
# Thanks for this code is owed to Arnar Birgisson -at - gmail.com. It
|
|
||||||
# is based on code he wrote that was retrieved from
|
|
||||||
# http://tools.cherrypy.org/wiki/AuthenticationAndAccessRestrictions
|
|
||||||
# on 1 February 2011.
|
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
import urllib
|
|
||||||
from passlib.hash import bcrypt
|
from passlib.hash import bcrypt
|
||||||
from passlib.exc import PasswordSizeError
|
from passlib.exc import PasswordSizeError
|
||||||
|
|
||||||
import cfg
|
import cfg
|
||||||
import random
|
|
||||||
from model import User
|
from model import User
|
||||||
|
|
||||||
cfg.session_key = '_cp_username'
|
cfg.session_key = '_username'
|
||||||
|
|
||||||
|
|
||||||
def add_user(username, passphrase, name='', email='', expert=False):
|
def add_user(username, passphrase, name='', email='', expert=False):
|
||||||
"""Add a new user with specified username and passphrase.
|
"""Add a new user with specified username and passphrase.
|
||||||
@ -49,6 +42,7 @@ def add_user(username, passphrase, name='', email='', expert=False):
|
|||||||
cfg.log(error)
|
cfg.log(error)
|
||||||
return error
|
return error
|
||||||
|
|
||||||
|
|
||||||
def check_credentials(username, passphrase):
|
def check_credentials(username, passphrase):
|
||||||
"""Verifies credentials for username and passphrase.
|
"""Verifies credentials for username and passphrase.
|
||||||
|
|
||||||
@ -87,79 +81,20 @@ def check_credentials(username, passphrase):
|
|||||||
|
|
||||||
if error:
|
if error:
|
||||||
cfg.log(error)
|
cfg.log(error)
|
||||||
|
|
||||||
return error
|
return error
|
||||||
|
|
||||||
def check_auth(*args, **kwargs):
|
|
||||||
"""A tool that looks in config for 'auth.require'. If found and it
|
|
||||||
is not None, a login is required and the entry is evaluated as a
|
|
||||||
list of conditions that the user must fulfill"""
|
|
||||||
conditions = cherrypy.request.config.get('auth.require', None)
|
|
||||||
# format GET params
|
|
||||||
get_params = urllib.quote(cherrypy.request.request_line.split()[1])
|
|
||||||
if conditions is not None:
|
|
||||||
username = cherrypy.session.get(cfg.session_key)
|
|
||||||
if username:
|
|
||||||
cherrypy.request.login = username
|
|
||||||
for condition in conditions:
|
|
||||||
# A condition is just a callable that returns true or false
|
|
||||||
if not condition():
|
|
||||||
# Send old page as from_page parameter
|
|
||||||
raise cherrypy.HTTPRedirect(cfg.server_dir + "/auth/login?from_page=%s" % get_params)
|
|
||||||
else:
|
|
||||||
# Send old page as from_page parameter
|
|
||||||
raise cherrypy.HTTPRedirect(cfg.server_dir + "/auth/login?from_page=%s" % get_params)
|
|
||||||
|
|
||||||
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)
|
# XXX: Only required until we start using Django authentication system properly
|
||||||
|
def login_required(func):
|
||||||
def require(*conditions):
|
"""A decorator to ensure that user is logged in before accessing a view"""
|
||||||
"""A decorator that appends conditions to the auth.require config
|
@functools.wraps(func)
|
||||||
variable."""
|
def wrapper(request, *args, **kwargs):
|
||||||
def decorate(f):
|
"""Check that user is logged in"""
|
||||||
if not hasattr(f, '_cp_config'):
|
if not request.session.get(cfg.session_key, None):
|
||||||
f._cp_config = dict()
|
return HttpResponseRedirect(
|
||||||
if 'auth.require' not in f._cp_config:
|
cfg.server_dir + "/auth/login?from_page=%s" % request.path)
|
||||||
f._cp_config['auth.require'] = []
|
|
||||||
f._cp_config['auth.require'].extend(conditions)
|
|
||||||
return f
|
|
||||||
return decorate
|
|
||||||
|
|
||||||
|
|
||||||
# Conditions are callables that return True
|
|
||||||
# if the user fulfills the conditions they define, False otherwise
|
|
||||||
#
|
|
||||||
# They can access the current username as cherrypy.request.login
|
|
||||||
#
|
|
||||||
# Define those at will however suits the application.
|
|
||||||
|
|
||||||
def member_of(groupname):
|
|
||||||
def check():
|
|
||||||
# replace with actual check if <username> is in <groupname>
|
|
||||||
return cherrypy.request.login == 'joe' and groupname == 'admin'
|
|
||||||
return check
|
|
||||||
|
|
||||||
def name_is(reqd_username):
|
|
||||||
return lambda: reqd_username == cherrypy.request.login
|
|
||||||
|
|
||||||
# These might be handy
|
|
||||||
|
|
||||||
def any_of(*conditions):
|
|
||||||
"""Returns True if any of the conditions match"""
|
|
||||||
def check():
|
|
||||||
for c in conditions:
|
|
||||||
if c():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
return check
|
|
||||||
|
|
||||||
# By default all conditions are required, but this might still be
|
|
||||||
# needed if you want to use it inside of an any_of(...) condition
|
|
||||||
def all_of(*conditions):
|
|
||||||
"""Returns True if all of the conditions match"""
|
|
||||||
def check():
|
|
||||||
for c in conditions:
|
|
||||||
if not c():
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
return check
|
|
||||||
|
|
||||||
|
return func(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|||||||
@ -2,19 +2,17 @@
|
|||||||
Controller to provide login and logout actions
|
Controller to provide login and logout actions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
import cfg
|
import cfg
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.http.response import HttpResponseRedirect
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import auth
|
from . import auth
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(forms.Form): # pylint: disable-msg=W0232
|
class LoginForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
"""Login form"""
|
"""Login form"""
|
||||||
from_page = forms.CharField(widget=forms.HiddenInput(), required=False)
|
|
||||||
|
|
||||||
username = forms.CharField(label=_('Username'))
|
username = forms.CharField(label=_('Username'))
|
||||||
password = forms.CharField(label=_('Passphrase'),
|
password = forms.CharField(label=_('Passphrase'),
|
||||||
widget=forms.PasswordInput())
|
widget=forms.PasswordInput())
|
||||||
@ -33,48 +31,37 @@ class LoginForm(forms.Form): # pylint: disable-msg=W0232
|
|||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class AuthController(PagePlugin):
|
def login(request):
|
||||||
"""Login and logout pages"""
|
"""Serve the login page"""
|
||||||
|
form = None
|
||||||
|
|
||||||
def __init__(self):
|
if request.method == 'POST':
|
||||||
super(AuthController, self).__init__()
|
form = LoginForm(request.POST, prefix='auth')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
|
if form.is_valid():
|
||||||
|
username = form.cleaned_data['username']
|
||||||
|
request.session[cfg.session_key] = username
|
||||||
|
return HttpResponseRedirect(_get_from_page(request))
|
||||||
|
else:
|
||||||
|
form = LoginForm(prefix='auth')
|
||||||
|
|
||||||
self.register_page('auth')
|
return TemplateResponse(request, 'form.html',
|
||||||
|
{'title': _('Login'),
|
||||||
|
'form': form,
|
||||||
|
'submit_text': _('Login')})
|
||||||
|
|
||||||
def on_login(self, username):
|
|
||||||
"""Called on successful login"""
|
|
||||||
|
|
||||||
def on_logout(self, username):
|
def logout(request):
|
||||||
"""Called on logout"""
|
"""Logout and redirect to origin page"""
|
||||||
|
try:
|
||||||
|
del request.session[cfg.session_key]
|
||||||
|
request.session.flush()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
@cherrypy.expose
|
return HttpResponseRedirect(_get_from_page(request))
|
||||||
def login(self, from_page=cfg.server_dir+"/", **kwargs):
|
|
||||||
"""Serve the login page"""
|
|
||||||
form = None
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = LoginForm(kwargs, prefix='auth')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
username = form.cleaned_data['username']
|
|
||||||
cherrypy.session[cfg.session_key] = username
|
|
||||||
cherrypy.request.login = username
|
|
||||||
self.on_login(username)
|
|
||||||
raise cherrypy.HTTPRedirect(from_page or
|
|
||||||
(cfg.server_dir + "/"))
|
|
||||||
else:
|
|
||||||
form = LoginForm(prefix='auth')
|
|
||||||
|
|
||||||
return util.render_template(template='form', title=_('Login'),
|
def _get_from_page(request):
|
||||||
form=form, submit_text=_('Login'))
|
"""Return the 'from page' of a request"""
|
||||||
|
return request.GET.get('from_page', cfg.server_dir + '/')
|
||||||
@cherrypy.expose
|
|
||||||
def logout(self, from_page=cfg.server_dir+"/"):
|
|
||||||
sess = cherrypy.session
|
|
||||||
username = sess.get(cfg.session_key, None)
|
|
||||||
sess[cfg.session_key] = None
|
|
||||||
if username:
|
|
||||||
cherrypy.request.login = None
|
|
||||||
self.on_logout(username)
|
|
||||||
|
|
||||||
raise cherrypy.HTTPRedirect(from_page or (cfg.server_dir + "/"))
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import cherrypy
|
|
||||||
import cfg
|
import cfg
|
||||||
from model import User
|
from model import User
|
||||||
from plugin_mount import UserStoreModule
|
from plugin_mount import UserStoreModule
|
||||||
@ -16,25 +15,36 @@ class UserStore(UserStoreModule, sqlite_db):
|
|||||||
def close(self):
|
def close(self):
|
||||||
self.__exit__(None,None,None)
|
self.__exit__(None,None,None)
|
||||||
|
|
||||||
def current(self, name=False):
|
def current(self, request=None, name=False):
|
||||||
"""Return current user, if there is one, else None.
|
"""Return current user, if there is one, else None.
|
||||||
If name = True, return the username instead of the user."""
|
If name = True, return the username instead of the user."""
|
||||||
try:
|
if not request:
|
||||||
username = cherrypy.session.get(cfg.session_key)
|
|
||||||
if name:
|
|
||||||
return username
|
|
||||||
else:
|
|
||||||
return self.get(username)
|
|
||||||
except AttributeError:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def expert(self, username=None):
|
try:
|
||||||
|
username = request.session[cfg.session_key]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if name:
|
||||||
|
return username
|
||||||
|
|
||||||
|
return self.get(username)
|
||||||
|
|
||||||
|
def expert(self, username=None, request=None):
|
||||||
|
"""Return whether the current or provided user is an expert"""
|
||||||
if not username:
|
if not username:
|
||||||
username = self.current(name=True)
|
if not request:
|
||||||
groups = self.attr(username,"groups")
|
return False
|
||||||
|
|
||||||
|
username = self.current(request=request, name=True)
|
||||||
|
|
||||||
|
groups = self.attr(username, 'groups')
|
||||||
|
|
||||||
if not groups:
|
if not groups:
|
||||||
return False
|
return False
|
||||||
return 'expert' in groups
|
|
||||||
|
return 'expert' in groups
|
||||||
|
|
||||||
def attr(self, username=None, field=None):
|
def attr(self, username=None, field=None):
|
||||||
return self.get(username)[field]
|
return self.get(username)[field]
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to configure ownCloud
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import owncloud
|
from . import owncloud
|
||||||
|
from .owncloud import init
|
||||||
|
|
||||||
|
__all__ = ['owncloud', 'init']
|
||||||
|
|
||||||
__all__ = ['owncloud']
|
DEPENDS = ['apps']
|
||||||
|
|||||||
@ -1,86 +1,81 @@
|
|||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from ..lib.auth import require
|
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
|
from ..lib.auth import login_required
|
||||||
import service
|
import service
|
||||||
import util
|
|
||||||
|
|
||||||
|
SERVICE = None
|
||||||
|
|
||||||
|
|
||||||
class OwnCloudForm(forms.Form): # pylint: disable-msg=W0232
|
class OwnCloudForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
"""ownCloud configuration form"""
|
"""ownCloud configuration form"""
|
||||||
enabled = forms.BooleanField(label=_('Enable ownCloud'), required=False)
|
enabled = forms.BooleanField(label=_('Enable ownCloud'), required=False)
|
||||||
|
|
||||||
# XXX: Only present due to issue with submitting empty form
|
|
||||||
dummy = forms.CharField(label='Dummy', initial='dummy',
|
def init():
|
||||||
widget=forms.HiddenInput())
|
"""Initialize the ownCloud module"""
|
||||||
|
menu = cfg.main_menu.find('/apps')
|
||||||
|
menu.add_item('Owncloud', 'icon-picture', '/apps/owncloud', 35)
|
||||||
|
|
||||||
|
status = get_status()
|
||||||
|
|
||||||
|
global SERVICE # pylint: disable-msg=W0603
|
||||||
|
SERVICE = service.Service('owncloud', _('ownCloud'), ['http', 'https'],
|
||||||
|
is_external=True, enabled=status['enabled'])
|
||||||
|
|
||||||
|
|
||||||
class OwnCloud(PagePlugin):
|
@login_required
|
||||||
"""ownCloud configuration page"""
|
def index(request):
|
||||||
order = 90
|
"""Serve the ownCloud configuration page"""
|
||||||
|
status = get_status()
|
||||||
|
|
||||||
def __init__(self):
|
form = None
|
||||||
super(OwnCloud, self).__init__()
|
messages = []
|
||||||
|
|
||||||
self.register_page('apps.owncloud')
|
if request.method == 'POST':
|
||||||
|
form = OwnCloudForm(request.POST, prefix='owncloud')
|
||||||
cfg.html_root.apps.menu.add_item('Owncloud', 'icon-picture',
|
# pylint: disable-msg=E1101
|
||||||
'/apps/owncloud', 35)
|
if form.is_valid():
|
||||||
|
_apply_changes(status, form.cleaned_data, messages)
|
||||||
status = self.get_status()
|
status = get_status()
|
||||||
self.service = service.Service('owncloud', _('ownCloud'),
|
|
||||||
['http', 'https'], is_external=True,
|
|
||||||
enabled=status['enabled'])
|
|
||||||
|
|
||||||
@cherrypy.expose
|
|
||||||
@require()
|
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the ownCloud configuration page"""
|
|
||||||
status = self.get_status()
|
|
||||||
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = OwnCloudForm(kwargs, prefix='owncloud')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._apply_changes(status, form.cleaned_data, messages)
|
|
||||||
status = self.get_status()
|
|
||||||
form = OwnCloudForm(initial=status, prefix='owncloud')
|
|
||||||
else:
|
|
||||||
form = OwnCloudForm(initial=status, prefix='owncloud')
|
form = OwnCloudForm(initial=status, prefix='owncloud')
|
||||||
|
else:
|
||||||
|
form = OwnCloudForm(initial=status, prefix='owncloud')
|
||||||
|
|
||||||
return util.render_template(template='owncloud', title=_('ownCloud'),
|
return TemplateResponse(request, 'owncloud.html',
|
||||||
form=form, messages=messages)
|
{'title': _('ownCloud'),
|
||||||
|
'form': form,
|
||||||
|
'messages_': messages})
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_status():
|
|
||||||
"""Return the current status"""
|
|
||||||
output, error = actions.run('owncloud-setup', 'status')
|
|
||||||
if error:
|
|
||||||
raise Exception('Error getting ownCloud status: %s' % error)
|
|
||||||
|
|
||||||
return {'enabled': 'enable' in output.split()}
|
def get_status():
|
||||||
|
"""Return the current status"""
|
||||||
|
output, error = actions.run('owncloud-setup', 'status')
|
||||||
|
if error:
|
||||||
|
raise Exception('Error getting ownCloud status: %s' % error)
|
||||||
|
|
||||||
def _apply_changes(self, old_status, new_status, messages):
|
return {'enabled': 'enable' in output.split()}
|
||||||
"""Apply the changes"""
|
|
||||||
if old_status['enabled'] == new_status['enabled']:
|
|
||||||
messages.append(('info', _('Setting unchanged')))
|
|
||||||
return
|
|
||||||
|
|
||||||
if new_status['enabled']:
|
|
||||||
messages.append(('success', _('ownCloud enabled')))
|
|
||||||
option = 'enable'
|
|
||||||
else:
|
|
||||||
messages.append(('success', _('ownCloud disabled')))
|
|
||||||
option = 'noenable'
|
|
||||||
|
|
||||||
actions.superuser_run('owncloud-setup', [option], async=True)
|
def _apply_changes(old_status, new_status, messages):
|
||||||
|
"""Apply the changes"""
|
||||||
|
if old_status['enabled'] == new_status['enabled']:
|
||||||
|
messages.append(('info', _('Setting unchanged')))
|
||||||
|
return
|
||||||
|
|
||||||
# Send a signal to other modules that the service is
|
if new_status['enabled']:
|
||||||
# enabled/disabled
|
messages.append(('success', _('ownCloud enabled')))
|
||||||
self.service.notify_enabled(self, new_status['enabled'])
|
option = 'enable'
|
||||||
|
else:
|
||||||
|
messages.append(('success', _('ownCloud disabled')))
|
||||||
|
option = 'noenable'
|
||||||
|
|
||||||
|
actions.superuser_run('owncloud-setup', [option], async=True)
|
||||||
|
|
||||||
|
# Send a signal to other modules that the service is
|
||||||
|
# enabled/disabled
|
||||||
|
SERVICE.notify_enabled(None, new_status['enabled'])
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to manage packages
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import packages
|
from . import packages
|
||||||
|
from .packages import init
|
||||||
|
|
||||||
|
__all__ = ['packages', 'init']
|
||||||
|
|
||||||
__all__ = ['packages']
|
DEPENDS = ['system']
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from ..lib.auth import require
|
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
from ..lib.auth import login_required
|
||||||
|
|
||||||
|
|
||||||
def get_modules_available():
|
def get_modules_available():
|
||||||
@ -28,10 +27,6 @@ def get_modules_enabled():
|
|||||||
|
|
||||||
class PackagesForm(forms.Form):
|
class PackagesForm(forms.Form):
|
||||||
"""Packages form"""
|
"""Packages form"""
|
||||||
# XXX: Only present due to issue with submitting empty form
|
|
||||||
dummy = forms.CharField(label='Dummy', initial='dummy',
|
|
||||||
widget=forms.HiddenInput())
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# pylint: disable-msg=E1002, E1101
|
# pylint: disable-msg=E1002, E1101
|
||||||
super(forms.Form, self).__init__(*args, **kwargs)
|
super(forms.Form, self).__init__(*args, **kwargs)
|
||||||
@ -44,89 +39,83 @@ class PackagesForm(forms.Form):
|
|||||||
label=label, required=False)
|
label=label, required=False)
|
||||||
|
|
||||||
|
|
||||||
class Packages(PagePlugin):
|
def init():
|
||||||
"""Package page"""
|
"""Initialize the Packages module"""
|
||||||
order = 20
|
menu = cfg.main_menu.find('/sys')
|
||||||
|
menu.add_item('Package Manager', 'icon-gift', '/sys/packages', 20)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Packages, self).__init__()
|
|
||||||
|
|
||||||
self.register_page('sys.packages')
|
@login_required
|
||||||
|
def index(request):
|
||||||
|
"""Serve the form"""
|
||||||
|
status = get_status()
|
||||||
|
|
||||||
cfg.html_root.sys.menu.add_item('Package Manager', 'icon-gift',
|
form = None
|
||||||
'/sys/packages', 20)
|
messages = []
|
||||||
|
|
||||||
@cherrypy.expose
|
if request.method == 'POST':
|
||||||
@require()
|
form = PackagesForm(request.POST, prefix='packages')
|
||||||
def index(self, **kwargs):
|
# pylint: disable-msg=E1101
|
||||||
"""Serve the form"""
|
if form.is_valid():
|
||||||
status = self.get_status()
|
_apply_changes(status, form.cleaned_data, messages)
|
||||||
|
status = get_status()
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = PackagesForm(kwargs, prefix='packages')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._apply_changes(status, form.cleaned_data, messages)
|
|
||||||
status = self.get_status()
|
|
||||||
form = PackagesForm(initial=status, prefix='packages')
|
|
||||||
else:
|
|
||||||
form = PackagesForm(initial=status, prefix='packages')
|
form = PackagesForm(initial=status, prefix='packages')
|
||||||
|
else:
|
||||||
|
form = PackagesForm(initial=status, prefix='packages')
|
||||||
|
|
||||||
return util.render_template(template='packages',
|
return TemplateResponse(request, 'packages.html',
|
||||||
title=_('Add/Remove Plugins'),
|
{'title': _('Add/Remove Plugins'),
|
||||||
form=form, messages=messages)
|
'form': form,
|
||||||
|
'messages_': messages})
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_status():
|
|
||||||
"""Return the current status"""
|
|
||||||
modules_available = get_modules_available()
|
|
||||||
modules_enabled = get_modules_enabled()
|
|
||||||
|
|
||||||
return {module + '_enabled': module in modules_enabled
|
def get_status():
|
||||||
for module in modules_available}
|
"""Return the current status"""
|
||||||
|
modules_available = get_modules_available()
|
||||||
|
modules_enabled = get_modules_enabled()
|
||||||
|
|
||||||
@staticmethod
|
return {module + '_enabled': module in modules_enabled
|
||||||
def _apply_changes(old_status, new_status, messages):
|
for module in modules_available}
|
||||||
"""Apply form changes"""
|
|
||||||
for field, enabled in new_status.items():
|
|
||||||
if not field.endswith('_enabled'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if old_status[field] == new_status[field]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
module = field.split('_enabled')[0]
|
def _apply_changes(old_status, new_status, messages):
|
||||||
if enabled:
|
"""Apply form changes"""
|
||||||
output, error = actions.superuser_run(
|
for field, enabled in new_status.items():
|
||||||
'module-manager', ['enable', cfg.python_root, module])
|
if not field.endswith('_enabled'):
|
||||||
del output # Unused
|
continue
|
||||||
|
|
||||||
# TODO: need to get plinth to load the module we just
|
if old_status[field] == new_status[field]:
|
||||||
# enabled
|
continue
|
||||||
if error:
|
|
||||||
messages.append(
|
module = field.split('_enabled')[0]
|
||||||
('error', _('Error enabling module - {module}').format(
|
if enabled:
|
||||||
module=module)))
|
output, error = actions.superuser_run(
|
||||||
else:
|
'module-manager', ['enable', cfg.python_root, module])
|
||||||
messages.append(
|
del output # Unused
|
||||||
('success', _('Module enabled - {module}').format(
|
|
||||||
|
# TODO: need to get plinth to load the module we just
|
||||||
|
# enabled
|
||||||
|
if error:
|
||||||
|
messages.append(
|
||||||
|
('error', _('Error enabling module - {module}').format(
|
||||||
|
module=module)))
|
||||||
|
else:
|
||||||
|
messages.append(
|
||||||
|
('success', _('Module enabled - {module}').format(
|
||||||
|
module=module)))
|
||||||
|
else:
|
||||||
|
output, error = actions.superuser_run(
|
||||||
|
'module-manager', ['disable', cfg.python_root, module])
|
||||||
|
del output # Unused
|
||||||
|
|
||||||
|
# TODO: need a smoother way for plinth to unload the
|
||||||
|
# module
|
||||||
|
if error:
|
||||||
|
messages.append(
|
||||||
|
('error',
|
||||||
|
_('Error disabling module - {module}').format(
|
||||||
module=module)))
|
module=module)))
|
||||||
else:
|
else:
|
||||||
output, error = actions.superuser_run(
|
messages.append(
|
||||||
'module-manager', ['disable', cfg.python_root, module])
|
('success', _('Module disabled - {module}').format(
|
||||||
del output # Unused
|
module=module)))
|
||||||
|
|
||||||
# TODO: need a smoother way for plinth to unload the
|
|
||||||
# module
|
|
||||||
if error:
|
|
||||||
messages.append(
|
|
||||||
('error',
|
|
||||||
_('Error disabling module - {module}').format(
|
|
||||||
module=module)))
|
|
||||||
else:
|
|
||||||
messages.append(
|
|
||||||
('success', _('Module disabled - {module}').format(
|
|
||||||
module=module)))
|
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to configure PageKite
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import pagekite
|
from . import pagekite
|
||||||
|
from .pagekite import init
|
||||||
|
|
||||||
|
__all__ = ['pagekite', 'init']
|
||||||
|
|
||||||
__all__ = ['pagekite']
|
DEPENDS = ['apps']
|
||||||
|
|||||||
@ -19,43 +19,38 @@
|
|||||||
Plinth module for configuring PageKite service
|
Plinth module for configuring PageKite service
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django.template import RequestContext
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
from ..lib.auth import require
|
from ..lib.auth import login_required
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class PageKite(PagePlugin):
|
def init():
|
||||||
"""PageKite menu entry and introduction page"""
|
"""Intialize the PageKite module"""
|
||||||
order = 60
|
menu = cfg.main_menu.find('/apps')
|
||||||
|
menu.add_item(_('Public Visibility (PageKite)'), 'icon-flag',
|
||||||
|
'/apps/pagekite', 50)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(PageKite, self).__init__()
|
|
||||||
|
|
||||||
self.register_page("apps.pagekite")
|
@login_required
|
||||||
cfg.html_root.apps.menu.add_item(
|
def index(request):
|
||||||
_("Public Visibility (PageKite)"), "icon-flag",
|
"""Serve introdution page"""
|
||||||
"/apps/pagekite", 50)
|
menu = {'title': _('PageKite'),
|
||||||
|
'items': [{'url': '/apps/pagekite/configure',
|
||||||
|
'text': _('Configure PageKite')}]}
|
||||||
|
|
||||||
@staticmethod
|
sidebar_right = render_to_string('menu_block.html', {'menu': menu},
|
||||||
@cherrypy.expose
|
RequestContext(request))
|
||||||
@require()
|
|
||||||
def index():
|
|
||||||
"""Serve introdution page"""
|
|
||||||
menu = {'title': _('PageKite'),
|
|
||||||
'items': [{'url': '/apps/pagekite/configure',
|
|
||||||
'text': _('Configure PageKite')}]}
|
|
||||||
sidebar_right = util.render_template(template='menu_block', menu=menu)
|
|
||||||
|
|
||||||
return util.render_template(template='pagekite_introduction',
|
return TemplateResponse(request, 'pagekite_introduction.html',
|
||||||
title=_("Public Visibility (PageKite)"),
|
{'title': _('Public Visibility (PageKite)'),
|
||||||
sidebar_right=sidebar_right)
|
'sidebar_right': sidebar_right})
|
||||||
|
|
||||||
|
|
||||||
class TrimmedCharField(forms.CharField):
|
class TrimmedCharField(forms.CharField):
|
||||||
@ -103,121 +98,114 @@ for your account if no secret is set on the kite'))
|
|||||||
https://pagekite.net/wiki/Howto/SshOverPageKite/">instructions</a>'))
|
https://pagekite.net/wiki/Howto/SshOverPageKite/">instructions</a>'))
|
||||||
|
|
||||||
|
|
||||||
class Configure(PagePlugin): # pylint: disable-msg=C0103
|
@login_required
|
||||||
"""Main configuration form"""
|
def configure(request):
|
||||||
order = 65
|
"""Serve the configuration form"""
|
||||||
|
status = get_status()
|
||||||
|
|
||||||
def __init__(self):
|
form = None
|
||||||
super(Configure, self).__init__()
|
messages = []
|
||||||
|
|
||||||
self.register_page("apps.pagekite.configure")
|
if request.method == 'POST':
|
||||||
|
form = ConfigureForm(request.POST, prefix='pagekite')
|
||||||
@cherrypy.expose
|
# pylint: disable-msg=E1101
|
||||||
@require()
|
if form.is_valid():
|
||||||
def index(self, **kwargs):
|
_apply_changes(status, form.cleaned_data, messages)
|
||||||
"""Serve the configuration form"""
|
status = get_status()
|
||||||
status = self.get_status()
|
|
||||||
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = ConfigureForm(kwargs, prefix='pagekite')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._apply_changes(status, form.cleaned_data, messages)
|
|
||||||
status = self.get_status()
|
|
||||||
form = ConfigureForm(initial=status, prefix='pagekite')
|
|
||||||
else:
|
|
||||||
form = ConfigureForm(initial=status, prefix='pagekite')
|
form = ConfigureForm(initial=status, prefix='pagekite')
|
||||||
|
else:
|
||||||
|
form = ConfigureForm(initial=status, prefix='pagekite')
|
||||||
|
|
||||||
return util.render_template(template='pagekite_configure',
|
return TemplateResponse(request, 'pagekite_configure.html',
|
||||||
title=_('Configure PageKite'), form=form,
|
{'title': _('Configure PageKite'),
|
||||||
messages=messages)
|
'form': form,
|
||||||
|
'messages_': messages})
|
||||||
|
|
||||||
def get_status(self):
|
|
||||||
"""
|
|
||||||
Return the current status of PageKite configuration by
|
|
||||||
executing various actions.
|
|
||||||
"""
|
|
||||||
status = {}
|
|
||||||
|
|
||||||
# Check if PageKite is installed
|
def get_status():
|
||||||
output = self._run(['get-installed'])
|
"""
|
||||||
cfg.log('Output - %s' % output)
|
Return the current status of PageKite configuration by
|
||||||
if output.split()[0] != 'installed':
|
executing various actions.
|
||||||
return None
|
"""
|
||||||
|
status = {}
|
||||||
|
|
||||||
# PageKite service enabled/disabled
|
# Check if PageKite is installed
|
||||||
output = self._run(['get-status'])
|
output = _run(['get-installed'])
|
||||||
status['enabled'] = (output.split()[0] == 'enabled')
|
cfg.log('Output - %s' % output)
|
||||||
|
if output.split()[0] != 'installed':
|
||||||
|
return None
|
||||||
|
|
||||||
# PageKite kite details
|
# PageKite service enabled/disabled
|
||||||
output = self._run(['get-kite'])
|
output = _run(['get-status'])
|
||||||
kite_details = output.split()
|
status['enabled'] = (output.split()[0] == 'enabled')
|
||||||
status['kite_name'] = kite_details[0]
|
|
||||||
status['kite_secret'] = kite_details[1]
|
|
||||||
|
|
||||||
# Service status
|
# PageKite kite details
|
||||||
status['service'] = {}
|
output = _run(['get-kite'])
|
||||||
for service in ('http', 'ssh'):
|
kite_details = output.split()
|
||||||
output = self._run(['get-service-status', service])
|
status['kite_name'] = kite_details[0]
|
||||||
status[service + '_enabled'] = (output.split()[0] == 'enabled')
|
status['kite_secret'] = kite_details[1]
|
||||||
|
|
||||||
return status
|
# Service status
|
||||||
|
status['service'] = {}
|
||||||
|
for service in ('http', 'ssh'):
|
||||||
|
output = _run(['get-service-status', service])
|
||||||
|
status[service + '_enabled'] = (output.split()[0] == 'enabled')
|
||||||
|
|
||||||
def _apply_changes(self, old_status, new_status, messages):
|
return status
|
||||||
"""Apply the changes to PageKite configuration"""
|
|
||||||
cfg.log.info('New status is - %s' % new_status)
|
|
||||||
|
|
||||||
if old_status != new_status:
|
|
||||||
self._run(['stop'])
|
|
||||||
|
|
||||||
if old_status['enabled'] != new_status['enabled']:
|
def _apply_changes(old_status, new_status, messages):
|
||||||
if new_status['enabled']:
|
"""Apply the changes to PageKite configuration"""
|
||||||
self._run(['set-status', 'enable'])
|
cfg.log.info('New status is - %s' % new_status)
|
||||||
messages.append(('success', _('PageKite enabled')))
|
|
||||||
else:
|
|
||||||
self._run(['set-status', 'disable'])
|
|
||||||
messages.append(('success', _('PageKite disabled')))
|
|
||||||
|
|
||||||
if old_status['kite_name'] != new_status['kite_name'] or \
|
if old_status != new_status:
|
||||||
old_status['kite_secret'] != new_status['kite_secret']:
|
_run(['stop'])
|
||||||
self._run(['set-kite', '--kite-name', new_status['kite_name'],
|
|
||||||
'--kite-secret', new_status['kite_secret']])
|
|
||||||
messages.append(('success', _('Kite details set')))
|
|
||||||
|
|
||||||
for service in ['http', 'ssh']:
|
if old_status['enabled'] != new_status['enabled']:
|
||||||
if old_status[service + '_enabled'] != \
|
if new_status['enabled']:
|
||||||
new_status[service + '_enabled']:
|
_run(['set-status', 'enable'])
|
||||||
if new_status[service + '_enabled']:
|
messages.append(('success', _('PageKite enabled')))
|
||||||
self._run(['set-service-status', service, 'enable'])
|
|
||||||
messages.append(('success', _('Service enabled: {service}')
|
|
||||||
.format(service=service)))
|
|
||||||
else:
|
|
||||||
self._run(['set-service-status', service, 'disable'])
|
|
||||||
messages.append(('success',
|
|
||||||
_('Service disabled: {service}')
|
|
||||||
.format(service=service)))
|
|
||||||
|
|
||||||
if old_status != new_status:
|
|
||||||
self._run(['start'])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _run(arguments, superuser=True):
|
|
||||||
"""Run an given command and raise exception if there was an error"""
|
|
||||||
command = 'pagekite-configure'
|
|
||||||
|
|
||||||
cfg.log.info('Running command - %s, %s, %s' % (command, arguments,
|
|
||||||
superuser))
|
|
||||||
|
|
||||||
if superuser:
|
|
||||||
output, error = actions.superuser_run(command, arguments)
|
|
||||||
else:
|
else:
|
||||||
output, error = actions.run(command, arguments)
|
_run(['set-status', 'disable'])
|
||||||
|
messages.append(('success', _('PageKite disabled')))
|
||||||
|
|
||||||
if error:
|
if old_status['kite_name'] != new_status['kite_name'] or \
|
||||||
raise Exception('Error setting/getting PageKite confguration - %s'
|
old_status['kite_secret'] != new_status['kite_secret']:
|
||||||
% error)
|
_run(['set-kite', '--kite-name', new_status['kite_name'],
|
||||||
|
'--kite-secret', new_status['kite_secret']])
|
||||||
|
messages.append(('success', _('Kite details set')))
|
||||||
|
|
||||||
return output
|
for service in ['http', 'ssh']:
|
||||||
|
if old_status[service + '_enabled'] != \
|
||||||
|
new_status[service + '_enabled']:
|
||||||
|
if new_status[service + '_enabled']:
|
||||||
|
_run(['set-service-status', service, 'enable'])
|
||||||
|
messages.append(('success', _('Service enabled: {service}')
|
||||||
|
.format(service=service)))
|
||||||
|
else:
|
||||||
|
_run(['set-service-status', service, 'disable'])
|
||||||
|
messages.append(('success',
|
||||||
|
_('Service disabled: {service}')
|
||||||
|
.format(service=service)))
|
||||||
|
|
||||||
|
if old_status != new_status:
|
||||||
|
_run(['start'])
|
||||||
|
|
||||||
|
|
||||||
|
def _run(arguments, superuser=True):
|
||||||
|
"""Run an given command and raise exception if there was an error"""
|
||||||
|
command = 'pagekite-configure'
|
||||||
|
|
||||||
|
cfg.log.info('Running command - %s, %s, %s' % (command, arguments,
|
||||||
|
superuser))
|
||||||
|
|
||||||
|
if superuser:
|
||||||
|
output, error = actions.superuser_run(command, arguments)
|
||||||
|
else:
|
||||||
|
output, error = actions.run(command, arguments)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise Exception('Error setting/getting PageKite confguration - %s'
|
||||||
|
% error)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|||||||
@ -20,6 +20,7 @@ Plinth module for system section page
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import system
|
from . import system
|
||||||
|
from system import init
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['system']
|
__all__ = ['system', 'init']
|
||||||
|
|||||||
@ -1,21 +1,15 @@
|
|||||||
import cherrypy
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from plugin_mount import PagePlugin
|
from django.template.response import TemplateResponse
|
||||||
|
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class Sys(PagePlugin):
|
def init():
|
||||||
order = 10
|
"""Initialize the system module"""
|
||||||
|
cfg.main_menu.add_item(_('System'), 'icon-cog', '/sys', 100)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Sys, self).__init__()
|
|
||||||
|
|
||||||
self.register_page("sys")
|
def index(request):
|
||||||
self.menu = cfg.main_menu.add_item(_("System"), "icon-cog", "/sys", 100)
|
"""Serve the index page"""
|
||||||
self.menu.add_item(_("Users and Groups"), "icon-user", "/sys/users", 15)
|
return TemplateResponse(request, 'system.html',
|
||||||
|
{'title': _('System Configuration')})
|
||||||
@cherrypy.expose
|
|
||||||
def index(self):
|
|
||||||
return util.render_template(template='system',
|
|
||||||
title=_("System Configuration"))
|
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to configure Tor
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import tor
|
from . import tor
|
||||||
|
from .tor import init
|
||||||
|
|
||||||
|
__all__ = ['tor', 'init']
|
||||||
|
|
||||||
__all__ = ['tor']
|
DEPENDS = ['apps']
|
||||||
|
|||||||
@ -19,38 +19,35 @@
|
|||||||
Plinth module for configuring Tor
|
Plinth module for configuring Tor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
from ..lib.auth import require
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
import util
|
from ..lib.auth import login_required
|
||||||
|
|
||||||
|
|
||||||
class Tor(PagePlugin):
|
def init():
|
||||||
order = 60 # order of running init in PagePlugins
|
"""Initialize the Tor module"""
|
||||||
|
menu = cfg.main_menu.find('/apps')
|
||||||
|
menu.add_item("Tor", "icon-eye-close", "/apps/tor", 30)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Tor, self).__init__()
|
|
||||||
|
|
||||||
self.register_page("apps.tor")
|
@login_required
|
||||||
cfg.html_root.apps.menu.add_item("Tor", "icon-eye-close", "/apps/tor",
|
def index(request):
|
||||||
30)
|
"""Service the index page"""
|
||||||
|
output, error = actions.superuser_run("tor-get-ports")
|
||||||
|
del error # Unused
|
||||||
|
|
||||||
@cherrypy.expose
|
port_info = output.split("\n")
|
||||||
@require()
|
tor_ports = {}
|
||||||
def index(self):
|
for line in port_info:
|
||||||
output, error = actions.superuser_run("tor-get-ports")
|
try:
|
||||||
port_info = output.split("\n")
|
(key, val) = line.split()
|
||||||
tor_ports = {}
|
tor_ports[key] = val
|
||||||
for line in port_info:
|
except ValueError:
|
||||||
try:
|
continue
|
||||||
(key, val) = line.split()
|
|
||||||
tor_ports[key] = val
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
return util.render_template(template='tor',
|
return TemplateResponse(request, 'tor.html',
|
||||||
title=_('Tor Control Panel'),
|
{'title': _('Tor Control Panel'),
|
||||||
tor_ports=tor_ports)
|
'tor_ports': tor_ports})
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to manage users
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import users
|
from . import users
|
||||||
|
from .users import init
|
||||||
|
|
||||||
|
__all__ = ['users', 'init']
|
||||||
|
|
||||||
__all__ = ['users']
|
DEPENDS = ['system']
|
||||||
|
|||||||
@ -1,36 +1,36 @@
|
|||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django.template import RequestContext
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from ..lib.auth import require, add_user
|
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import cfg
|
import cfg
|
||||||
|
from ..lib.auth import add_user, login_required
|
||||||
from model import User
|
from model import User
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
class Users(PagePlugin):
|
def init():
|
||||||
order = 20 # order of running init in PagePlugins
|
"""Intialize the module"""
|
||||||
|
menu = cfg.main_menu.find('/sys')
|
||||||
|
menu.add_item(_('Users and Groups'), 'icon-user', '/sys/users', 15)
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(Users, self).__init__()
|
|
||||||
|
|
||||||
self.register_page("sys.users")
|
@login_required
|
||||||
|
def index(request):
|
||||||
|
"""Return a rendered users page"""
|
||||||
|
menu = {'title': _('Users and Groups'),
|
||||||
|
'items': [{'url': '/sys/users/add',
|
||||||
|
'text': _('Add User')},
|
||||||
|
{'url': '/sys/users/edit',
|
||||||
|
'text': _('Edit Users')}]}
|
||||||
|
|
||||||
@staticmethod
|
sidebar_right = render_to_string('menu_block.html', {'menu': menu},
|
||||||
@cherrypy.expose
|
RequestContext(request))
|
||||||
@require()
|
|
||||||
def index():
|
return TemplateResponse(request, 'login_nav.html',
|
||||||
"""Return a rendered users page"""
|
{'title': _('Manage Users and Groups'),
|
||||||
menu = {'title': _('Users and Groups'),
|
'sidebar_right': sidebar_right})
|
||||||
'items': [{'url': '/sys/users/add',
|
|
||||||
'text': _('Add User')},
|
|
||||||
{'url': '/sys/users/edit',
|
|
||||||
'text': _('Edit Users')}]}
|
|
||||||
sidebar_right = util.render_template(template='menu_block',
|
|
||||||
menu=menu)
|
|
||||||
return util.render_template(title="Manage Users and Groups",
|
|
||||||
sidebar_right=sidebar_right)
|
|
||||||
|
|
||||||
|
|
||||||
class UserAddForm(forms.Form): # pylint: disable-msg=W0232
|
class UserAddForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
@ -50,48 +50,40 @@ and alphabet'),
|
|||||||
email = forms.EmailField(label=_('Email'), required=False)
|
email = forms.EmailField(label=_('Email'), required=False)
|
||||||
|
|
||||||
|
|
||||||
class UserAdd(PagePlugin):
|
@login_required
|
||||||
"""Add user page"""
|
def add(request):
|
||||||
order = 30
|
"""Serve the form"""
|
||||||
|
form = None
|
||||||
|
messages = []
|
||||||
|
|
||||||
def __init__(self):
|
if request.method == 'POST':
|
||||||
super(UserAdd, self).__init__()
|
form = UserAddForm(request.POST, prefix='user')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
self.register_page('sys.users.add')
|
if form.is_valid():
|
||||||
|
_add_user(form.cleaned_data, messages)
|
||||||
@cherrypy.expose
|
|
||||||
@require()
|
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the form"""
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = UserAddForm(kwargs, prefix='user')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._add_user(form.cleaned_data, messages)
|
|
||||||
form = UserAddForm(prefix='user')
|
|
||||||
else:
|
|
||||||
form = UserAddForm(prefix='user')
|
form = UserAddForm(prefix='user')
|
||||||
|
else:
|
||||||
|
form = UserAddForm(prefix='user')
|
||||||
|
|
||||||
return util.render_template(template='users_add', title=_('Add User'),
|
return TemplateResponse(request, 'users_add.html',
|
||||||
form=form, messages=messages)
|
{'title': _('Add User'),
|
||||||
|
'form': form,
|
||||||
|
'messages_': messages})
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _add_user(data, messages):
|
|
||||||
"""Add a user"""
|
|
||||||
if cfg.users.exists(data['username']):
|
|
||||||
messages.append(
|
|
||||||
('error', _('User "{username}" already exists').format(
|
|
||||||
username=data['username'])))
|
|
||||||
return
|
|
||||||
|
|
||||||
add_user(data['username'], data['password'], data['full_name'],
|
def _add_user(data, messages):
|
||||||
data['email'], False)
|
"""Add a user"""
|
||||||
|
if cfg.users.exists(data['username']):
|
||||||
messages.append(
|
messages.append(
|
||||||
('success', _('User "{username}" added').format(
|
('error', _('User "{username}" already exists').format(
|
||||||
username=data['username'])))
|
username=data['username'])))
|
||||||
|
return
|
||||||
|
|
||||||
|
add_user(data['username'], data['password'], data['full_name'],
|
||||||
|
data['email'], False)
|
||||||
|
messages.append(
|
||||||
|
('success', _('User "{username}" added').format(
|
||||||
|
username=data['username'])))
|
||||||
|
|
||||||
|
|
||||||
class UserEditForm(forms.Form): # pylint: disable-msg=W0232
|
class UserEditForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
@ -110,64 +102,56 @@ class UserEditForm(forms.Form): # pylint: disable-msg=W0232
|
|||||||
self.fields['delete_user_' + user['username']] = field
|
self.fields['delete_user_' + user['username']] = field
|
||||||
|
|
||||||
|
|
||||||
class UserEdit(PagePlugin):
|
@login_required
|
||||||
"""User edit page"""
|
def edit(request):
|
||||||
order = 35
|
"""Serve the edit form"""
|
||||||
|
form = None
|
||||||
|
messages = []
|
||||||
|
|
||||||
def __init__(self):
|
if request.method == 'POST':
|
||||||
super(UserEdit, self).__init__()
|
form = UserEditForm(request.POST, prefix='user')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
self.register_page('sys.users.edit')
|
if form.is_valid():
|
||||||
|
_apply_edit_changes(request, form.cleaned_data, messages)
|
||||||
@cherrypy.expose
|
|
||||||
@require()
|
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the form"""
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = UserEditForm(kwargs, prefix='user')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._apply_changes(form.cleaned_data, messages)
|
|
||||||
form = UserEditForm(prefix='user')
|
|
||||||
else:
|
|
||||||
form = UserEditForm(prefix='user')
|
form = UserEditForm(prefix='user')
|
||||||
|
else:
|
||||||
|
form = UserEditForm(prefix='user')
|
||||||
|
|
||||||
return util.render_template(template='users_edit',
|
return TemplateResponse(request, 'users_edit.html',
|
||||||
title=_('Edit or Delete User'),
|
{'title': _('Edit or Delete User'),
|
||||||
form=form, messages=messages)
|
'form': form,
|
||||||
|
'messages_': messages})
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _apply_changes(data, messages):
|
|
||||||
"""Apply form changes"""
|
|
||||||
for field, value in data.items():
|
|
||||||
if not value:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not field.startswith('delete_user_'):
|
def _apply_edit_changes(request, data, messages):
|
||||||
continue
|
"""Apply form changes"""
|
||||||
|
for field, value in data.items():
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
|
||||||
username = field.split('delete_user_')[1]
|
if not field.startswith('delete_user_'):
|
||||||
|
continue
|
||||||
|
|
||||||
cfg.log.info('%s asked to delete %s' %
|
username = field.split('delete_user_')[1]
|
||||||
(cherrypy.session.get(cfg.session_key), username))
|
|
||||||
|
|
||||||
if username == cfg.users.current(name=True):
|
requesting_user = request.session.get(cfg.session_key, None)
|
||||||
messages.append(
|
cfg.log.info('%s asked to delete %s' %
|
||||||
('error',
|
(requesting_user, username))
|
||||||
_('Can not delete current account - "%s"') % username))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not cfg.users.exists(username):
|
if username == cfg.users.current(request=request, name=True):
|
||||||
messages.append(('error',
|
messages.append(
|
||||||
_('User "%s" does not exist') % username))
|
('error',
|
||||||
continue
|
_('Can not delete current account - "%s"') % username))
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
if not cfg.users.exists(username):
|
||||||
cfg.users.remove(username)
|
messages.append(('error',
|
||||||
messages.append(('success', _('User "%s" deleted') % username))
|
_('User "%s" does not exist') % username))
|
||||||
except IOError as exception:
|
continue
|
||||||
messages.append(('error', _('Error deleting "%s" - %s') %
|
|
||||||
(username, exception)))
|
try:
|
||||||
|
cfg.users.remove(username)
|
||||||
|
messages.append(('success', _('User "%s" deleted') % username))
|
||||||
|
except IOError as exception:
|
||||||
|
messages.append(('error', _('Error deleting "%s" - %s') %
|
||||||
|
(username, exception)))
|
||||||
|
|||||||
@ -20,6 +20,8 @@ Plinth module to configure XMPP server
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from . import xmpp
|
from . import xmpp
|
||||||
|
from .xmpp import init
|
||||||
|
|
||||||
|
__all__ = ['xmpp', 'init']
|
||||||
|
|
||||||
__all__ = ['xmpp']
|
DEPENDS = ['apps']
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import cherrypy
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.template import RequestContext
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from ..lib.auth import require
|
|
||||||
from plugin_mount import PagePlugin
|
|
||||||
import cfg
|
|
||||||
import actions
|
import actions
|
||||||
|
import cfg
|
||||||
|
from ..lib.auth import login_required
|
||||||
import service
|
import service
|
||||||
import util
|
|
||||||
|
|
||||||
|
|
||||||
SIDE_MENU = {'title': _('XMPP'),
|
SIDE_MENU = {'title': _('XMPP'),
|
||||||
@ -16,38 +17,35 @@ SIDE_MENU = {'title': _('XMPP'),
|
|||||||
'text': 'Register XMPP Account'}]}
|
'text': 'Register XMPP Account'}]}
|
||||||
|
|
||||||
|
|
||||||
class XMPP(PagePlugin):
|
def init():
|
||||||
"""XMPP Page"""
|
"""Initialize the XMPP module"""
|
||||||
order = 60
|
menu = cfg.main_menu.find('/apps')
|
||||||
|
menu.add_item('Chat', 'icon-comment', '/../jwchat', 20)
|
||||||
|
menu.add_item('XMPP', 'icon-comment', '/apps/xmpp', 40)
|
||||||
|
|
||||||
def __init__(self):
|
service.Service(
|
||||||
super(XMPP, self).__init__()
|
'xmpp-client', _('Chat Server - client connections'),
|
||||||
|
is_external=True, enabled=True)
|
||||||
|
service.Service(
|
||||||
|
'xmpp-server', _('Chat Server - server connections'),
|
||||||
|
is_external=True, enabled=True)
|
||||||
|
service.Service(
|
||||||
|
'xmpp-bosh', _('Chat Server - web interface'), is_external=True,
|
||||||
|
enabled=True)
|
||||||
|
|
||||||
self.register_page('apps.xmpp')
|
|
||||||
cfg.html_root.apps.menu.add_item('XMPP', 'icon-comment',
|
|
||||||
'/apps/xmpp', 40)
|
|
||||||
|
|
||||||
self.client_service = service.Service(
|
@login_required
|
||||||
'xmpp-client', _('Chat Server - client connections'),
|
def index(request):
|
||||||
is_external=True, enabled=True)
|
"""Serve XMPP page"""
|
||||||
self.server_service = service.Service(
|
main = "<p>XMPP Server Accounts and Configuration</p>"
|
||||||
'xmpp-server', _('Chat Server - server connections'),
|
|
||||||
is_external=True, enabled=True)
|
|
||||||
self.bosh_service = service.Service(
|
|
||||||
'xmpp-bosh', _('Chat Server - web interface'), is_external=True,
|
|
||||||
enabled=True)
|
|
||||||
|
|
||||||
@staticmethod
|
sidebar_right = render_to_string('menu_block.html', {'menu': SIDE_MENU},
|
||||||
@cherrypy.expose
|
RequestContext(request))
|
||||||
@require()
|
|
||||||
def index():
|
|
||||||
"""Serve XMPP page"""
|
|
||||||
main = "<p>XMPP Server Accounts and Configuration</p>"
|
|
||||||
|
|
||||||
sidebar_right = util.render_template(template='menu_block',
|
return TemplateResponse(request, 'login_nav.html',
|
||||||
menu=SIDE_MENU)
|
{'title': _('XMPP Server'),
|
||||||
return util.render_template(title="XMPP Server", main=main,
|
'main': main,
|
||||||
sidebar_right=sidebar_right)
|
'sidebar_right': sidebar_right})
|
||||||
|
|
||||||
|
|
||||||
class ConfigureForm(forms.Form): # pylint: disable-msg=W0232
|
class ConfigureForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
@ -57,77 +55,65 @@ class ConfigureForm(forms.Form): # pylint: disable-msg=W0232
|
|||||||
help_text=_('When enabled, anyone who can reach this server will be \
|
help_text=_('When enabled, anyone who can reach this server will be \
|
||||||
allowed to register an account through an XMPP client'))
|
allowed to register an account through an XMPP client'))
|
||||||
|
|
||||||
# XXX: Only present due to issue with submitting empty form
|
|
||||||
dummy = forms.CharField(label='Dummy', initial='dummy',
|
|
||||||
widget=forms.HiddenInput())
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def configure(request):
|
||||||
|
"""Serve the configuration form"""
|
||||||
|
status = get_status()
|
||||||
|
|
||||||
class Configure(PagePlugin):
|
form = None
|
||||||
"""Configuration page"""
|
messages = []
|
||||||
order = 65
|
|
||||||
|
|
||||||
def __init__(self):
|
if request.method == 'POST':
|
||||||
super(Configure, self).__init__()
|
form = ConfigureForm(request.POST, prefix='xmpp')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
self.register_page('apps.xmpp.configure')
|
if form.is_valid():
|
||||||
|
_apply_changes(status, form.cleaned_data, messages)
|
||||||
@cherrypy.expose
|
status = get_status()
|
||||||
@require()
|
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the configuration form"""
|
|
||||||
status = self.get_status()
|
|
||||||
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = ConfigureForm(kwargs, prefix='xmpp')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._apply_changes(status, form.cleaned_data, messages)
|
|
||||||
status = self.get_status()
|
|
||||||
form = ConfigureForm(initial=status, prefix='xmpp')
|
|
||||||
else:
|
|
||||||
form = ConfigureForm(initial=status, prefix='xmpp')
|
form = ConfigureForm(initial=status, prefix='xmpp')
|
||||||
|
else:
|
||||||
|
form = ConfigureForm(initial=status, prefix='xmpp')
|
||||||
|
|
||||||
sidebar_right = util.render_template(template='menu_block',
|
sidebar_right = render_to_string('menu_block.html', {'menu': SIDE_MENU},
|
||||||
menu=SIDE_MENU)
|
RequestContext(request))
|
||||||
return util.render_template(template='xmpp_configure',
|
|
||||||
title=_('Configure XMPP Server'),
|
|
||||||
form=form, messages=messages,
|
|
||||||
sidebar_right=sidebar_right)
|
|
||||||
|
|
||||||
@staticmethod
|
return TemplateResponse(request, 'xmpp_configure.html',
|
||||||
def get_status():
|
{'title': _('Configure XMPP Server'),
|
||||||
"""Return the current status"""
|
'form': form,
|
||||||
output, error = actions.run('xmpp-setup', 'status')
|
'messages_': messages,
|
||||||
if error:
|
'sidebar_right': sidebar_right})
|
||||||
raise Exception('Error getting status: %s' % error)
|
|
||||||
|
|
||||||
return {'inband_enabled': 'inband_enable' in output.split()}
|
|
||||||
|
|
||||||
@staticmethod
|
def get_status():
|
||||||
def _apply_changes(old_status, new_status, messages):
|
"""Return the current status"""
|
||||||
"""Apply the form changes"""
|
output, error = actions.run('xmpp-setup', 'status')
|
||||||
cfg.log.info('Status - %s, %s' % (old_status, new_status))
|
if error:
|
||||||
|
raise Exception('Error getting status: %s' % error)
|
||||||
|
|
||||||
if old_status['inband_enabled'] == new_status['inband_enabled']:
|
return {'inband_enabled': 'inband_enable' in output.split()}
|
||||||
messages.append(('info', _('Setting unchanged')))
|
|
||||||
return
|
|
||||||
|
|
||||||
if new_status['inband_enabled']:
|
|
||||||
messages.append(('success', _('Inband registration enabled')))
|
|
||||||
option = 'inband_enable'
|
|
||||||
else:
|
|
||||||
messages.append(('success', _('Inband registration disabled')))
|
|
||||||
option = 'noinband_enable'
|
|
||||||
|
|
||||||
cfg.log.info('Option - %s' % option)
|
def _apply_changes(old_status, new_status, messages):
|
||||||
|
"""Apply the form changes"""
|
||||||
|
cfg.log.info('Status - %s, %s' % (old_status, new_status))
|
||||||
|
|
||||||
_output, error = actions.superuser_run('xmpp-setup', [option])
|
if old_status['inband_enabled'] == new_status['inband_enabled']:
|
||||||
del _output # Unused
|
messages.append(('info', _('Setting unchanged')))
|
||||||
if error:
|
return
|
||||||
raise Exception('Error running command - %s' % error)
|
|
||||||
|
if new_status['inband_enabled']:
|
||||||
|
messages.append(('success', _('Inband registration enabled')))
|
||||||
|
option = 'inband_enable'
|
||||||
|
else:
|
||||||
|
messages.append(('success', _('Inband registration disabled')))
|
||||||
|
option = 'noinband_enable'
|
||||||
|
|
||||||
|
cfg.log.info('Option - %s' % option)
|
||||||
|
|
||||||
|
_output, error = actions.superuser_run('xmpp-setup', [option])
|
||||||
|
del _output # Unused
|
||||||
|
if error:
|
||||||
|
raise Exception('Error running command - %s' % error)
|
||||||
|
|
||||||
|
|
||||||
class RegisterForm(forms.Form): # pylint: disable-msg=W0232
|
class RegisterForm(forms.Form): # pylint: disable-msg=W0232
|
||||||
@ -138,51 +124,43 @@ class RegisterForm(forms.Form): # pylint: disable-msg=W0232
|
|||||||
label=_('Password'), widget=forms.PasswordInput())
|
label=_('Password'), widget=forms.PasswordInput())
|
||||||
|
|
||||||
|
|
||||||
class Register(PagePlugin):
|
@login_required
|
||||||
"""User registration page"""
|
def register(request):
|
||||||
order = 65
|
"""Serve the registration form"""
|
||||||
|
form = None
|
||||||
|
messages = []
|
||||||
|
|
||||||
def __init__(self):
|
if request.method == 'POST':
|
||||||
super(Register, self).__init__()
|
form = RegisterForm(request.POST, prefix='xmpp')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
self.register_page('apps.xmpp.register')
|
if form.is_valid():
|
||||||
|
_register_user(form.cleaned_data, messages)
|
||||||
@cherrypy.expose
|
|
||||||
@require()
|
|
||||||
def index(self, **kwargs):
|
|
||||||
"""Serve the registration form"""
|
|
||||||
form = None
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
if kwargs:
|
|
||||||
form = RegisterForm(kwargs, prefix='xmpp')
|
|
||||||
# pylint: disable-msg=E1101
|
|
||||||
if form.is_valid():
|
|
||||||
self._register_user(form.cleaned_data, messages)
|
|
||||||
form = RegisterForm(prefix='xmpp')
|
|
||||||
else:
|
|
||||||
form = RegisterForm(prefix='xmpp')
|
form = RegisterForm(prefix='xmpp')
|
||||||
|
else:
|
||||||
|
form = RegisterForm(prefix='xmpp')
|
||||||
|
|
||||||
sidebar_right = util.render_template(template='menu_block',
|
sidebar_right = render_to_string('menu_block.html', {'menu': SIDE_MENU},
|
||||||
menu=SIDE_MENU)
|
RequestContext(request))
|
||||||
return util.render_template(template='xmpp_register',
|
|
||||||
title=_('Register XMPP Account'),
|
|
||||||
form=form, messages=messages,
|
|
||||||
sidebar_right=sidebar_right)
|
|
||||||
|
|
||||||
@staticmethod
|
return TemplateResponse(request, 'xmpp_register.html',
|
||||||
def _register_user(data, messages):
|
{'title': _('Register XMPP Account'),
|
||||||
"""Register a new XMPP user"""
|
'form': form,
|
||||||
output, error = actions.superuser_run(
|
'messages_': messages,
|
||||||
'xmpp-register', [data['username'], data['password']])
|
'sidebar_right': sidebar_right})
|
||||||
if error:
|
|
||||||
raise Exception('Error registering user - %s' % error)
|
|
||||||
|
|
||||||
if 'successfully registered' in output:
|
|
||||||
messages.append(('success',
|
def _register_user(data, messages):
|
||||||
_('Registered account for %s' %
|
"""Register a new XMPP user"""
|
||||||
data['username'])))
|
output, error = actions.superuser_run(
|
||||||
else:
|
'xmpp-register', [data['username'], data['password']])
|
||||||
messages.append(('error',
|
if error:
|
||||||
_('Failed to register account for %s: %s') %
|
raise Exception('Error registering user - %s' % error)
|
||||||
(data['username'], output)))
|
|
||||||
|
if 'successfully registered' in output:
|
||||||
|
messages.append(('success',
|
||||||
|
_('Registered account for %s' %
|
||||||
|
data['username'])))
|
||||||
|
else:
|
||||||
|
messages.append(('error',
|
||||||
|
_('Failed to register account for %s: %s') %
|
||||||
|
(data['username'], output)))
|
||||||
|
|||||||
209
plinth.py
209
plinth.py
@ -1,28 +1,22 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import os, stat, sys, argparse
|
import os, sys, argparse
|
||||||
from gettext import gettext as _
|
|
||||||
import cfg
|
import cfg
|
||||||
import django.conf
|
import django.conf
|
||||||
import importlib
|
import django.core.wsgi
|
||||||
if not os.path.join(cfg.file_root, "vendor") in sys.path:
|
if not os.path.join(cfg.file_root, "vendor") in sys.path:
|
||||||
sys.path.append(os.path.join(cfg.file_root, "vendor"))
|
sys.path.append(os.path.join(cfg.file_root, "vendor"))
|
||||||
import re
|
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
from cherrypy import _cpserver
|
from cherrypy import _cpserver
|
||||||
from cherrypy.process.plugins import Daemonizer
|
from cherrypy.process.plugins import Daemonizer
|
||||||
Daemonizer(cherrypy.engine).subscribe()
|
Daemonizer(cherrypy.engine).subscribe()
|
||||||
|
|
||||||
|
import module_loader
|
||||||
import plugin_mount
|
import plugin_mount
|
||||||
import service
|
import service
|
||||||
import util as u
|
|
||||||
|
|
||||||
from logger import Logger
|
from logger import Logger
|
||||||
#from modules.auth import AuthController, require, member_of, name_is
|
|
||||||
|
|
||||||
from withsqlite.withsqlite import sqlite_db
|
|
||||||
import socket
|
|
||||||
|
|
||||||
__version__ = "0.2.14"
|
__version__ = "0.2.14"
|
||||||
__author__ = "James Vasile"
|
__author__ = "James Vasile"
|
||||||
@ -32,79 +26,6 @@ __maintainer__ = "James Vasile"
|
|||||||
__email__ = "james@jamesvasile.com"
|
__email__ = "james@jamesvasile.com"
|
||||||
__status__ = "Development"
|
__status__ = "Development"
|
||||||
|
|
||||||
import urlparse
|
|
||||||
|
|
||||||
def error_page(status, dynamic_msg, stock_msg):
|
|
||||||
return u.render_template(template="err", title=status, main="<p>%s</p>%s" % (dynamic_msg, stock_msg))
|
|
||||||
|
|
||||||
def error_page_404(status, message, traceback, version):
|
|
||||||
return error_page(status, message, """<p>If you believe this
|
|
||||||
missing page should exist, please file a bug with either the Plinth
|
|
||||||
project (<a href="https://github.com/NickDaly/Plinth/issues">it has
|
|
||||||
an issue tracker</a>) or the people responsible for the module you
|
|
||||||
are trying to access.</p>
|
|
||||||
|
|
||||||
<p>Sorry for the mistake.</p>
|
|
||||||
""")
|
|
||||||
|
|
||||||
def error_page_500(status, message, traceback, version):
|
|
||||||
cfg.log.error("500 Internal Server Error. Trackback is above.")
|
|
||||||
more="""<p>This is an internal error and not something you caused
|
|
||||||
or can fix. Please report the error on the <a
|
|
||||||
href="https://github.com/jvasile/Plinth/issues">bug tracker</a> so
|
|
||||||
we can fix it.</p>"""
|
|
||||||
return error_page(status, message, "<p>%s</p><pre>%s</pre>" % (more, "\n".join(traceback.split("\n"))))
|
|
||||||
|
|
||||||
class Root(plugin_mount.PagePlugin):
|
|
||||||
@cherrypy.expose
|
|
||||||
def index(self):
|
|
||||||
## TODO: firstboot hijacking root should probably be in the firstboot module with a hook in plinth.py
|
|
||||||
with sqlite_db(cfg.store_file, table="firstboot") as db:
|
|
||||||
if not 'state' in db:
|
|
||||||
# if we created a new user db, make sure it can't be read by everyone
|
|
||||||
userdb_fname = '{}.sqlite3'.format(cfg.user_db)
|
|
||||||
os.chmod(userdb_fname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
|
|
||||||
# cherrypy.InternalRedirect throws a 301, causing the
|
|
||||||
# browser to cache the redirect, preventing the user from
|
|
||||||
# navigating to /plinth until the browser is restarted.
|
|
||||||
raise cherrypy.HTTPRedirect('firstboot', 307)
|
|
||||||
elif db['state'] < 5:
|
|
||||||
cfg.log("First Boot state = %d" % db['state'])
|
|
||||||
raise cherrypy.InternalRedirect('firstboot/state%d' % db['state'])
|
|
||||||
if cherrypy.session.get(cfg.session_key, None):
|
|
||||||
raise cherrypy.InternalRedirect('apps')
|
|
||||||
else:
|
|
||||||
raise cherrypy.InternalRedirect('help/about')
|
|
||||||
|
|
||||||
|
|
||||||
def load_modules():
|
|
||||||
"""
|
|
||||||
Read names of enabled modules in modules/enabled directory and
|
|
||||||
import them from modules directory.
|
|
||||||
"""
|
|
||||||
for name in os.listdir('modules/enabled'):
|
|
||||||
cfg.log.info('Importing modules/%s' % name)
|
|
||||||
try:
|
|
||||||
importlib.import_module('modules.{module}'.format(module=name))
|
|
||||||
except ImportError as exception:
|
|
||||||
cfg.log.error(
|
|
||||||
'Could not import modules/{module}: {exception}'
|
|
||||||
.format(module=name, exception=exception))
|
|
||||||
|
|
||||||
|
|
||||||
def get_template_directories():
|
|
||||||
"""Return the list of template directories"""
|
|
||||||
directory = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
core_directory = os.path.join(directory, 'templates')
|
|
||||||
|
|
||||||
directories = set((core_directory,))
|
|
||||||
for name in os.listdir('modules/enabled'):
|
|
||||||
directories.add(os.path.join('modules', name, 'templates'))
|
|
||||||
|
|
||||||
cfg.log.info('Template directories - %s' % directories)
|
|
||||||
|
|
||||||
return directories
|
|
||||||
|
|
||||||
|
|
||||||
def parse_arguments():
|
def parse_arguments():
|
||||||
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
|
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
|
||||||
@ -144,7 +65,13 @@ def set_config(args, element, default):
|
|||||||
# it wasn't in the config file, but set the default anyway.
|
# it wasn't in the config file, but set the default anyway.
|
||||||
setattr(cfg, element, default)
|
setattr(cfg, element, default)
|
||||||
|
|
||||||
def setup():
|
|
||||||
|
def setup_logging():
|
||||||
|
"""Setup logging framework"""
|
||||||
|
cfg.log = Logger()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_configuration():
|
||||||
cfg = parse_arguments()
|
cfg = parse_arguments()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -155,55 +82,91 @@ def setup():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
os.chdir(cfg.python_root)
|
os.chdir(cfg.python_root)
|
||||||
cherrypy.config.update({'error_page.404': error_page_404})
|
|
||||||
cherrypy.config.update({'error_page.500': error_page_500})
|
|
||||||
cfg.log = Logger()
|
|
||||||
load_modules()
|
|
||||||
cfg.html_root = Root()
|
|
||||||
|
|
||||||
cfg.users = plugin_mount.UserStoreModule.get_plugins()[0]
|
|
||||||
cfg.page_plugins = plugin_mount.PagePlugin.get_plugins()
|
|
||||||
cfg.log("Loaded %d page plugins" % len(cfg.page_plugins))
|
|
||||||
|
|
||||||
# Add an extra server
|
def setup_server():
|
||||||
server = _cpserver.Server()
|
"""Setup CherryPy server"""
|
||||||
server.socket_host = '127.0.0.1'
|
# Add an extra server
|
||||||
server.socket_port = 52854
|
server = _cpserver.Server()
|
||||||
server.subscribe()
|
server.socket_host = '127.0.0.1'
|
||||||
|
server.socket_port = 52854
|
||||||
|
server.subscribe()
|
||||||
|
|
||||||
# Configure default server
|
# Configure default server
|
||||||
cherrypy.config.update(
|
cherrypy.config.update(
|
||||||
{'server.socket_host': cfg.host,
|
{'server.socket_host': cfg.host,
|
||||||
'server.socket_port': cfg.port,
|
'server.socket_port': cfg.port,
|
||||||
'server.thread_pool':10,
|
'server.thread_pool': 10})
|
||||||
'tools.staticdir.root': cfg.file_root,
|
|
||||||
'tools.sessions.on':True,
|
application = django.core.wsgi.get_wsgi_application()
|
||||||
'tools.auth.on':True,
|
cherrypy.tree.graft(application, cfg.server_dir)
|
||||||
'tools.sessions.storage_type':"file",
|
|
||||||
'tools.sessions.timeout':90,
|
config = {
|
||||||
'tools.sessions.storage_path':"%s/cherrypy_sessions" % cfg.data_dir,})
|
'/': {'tools.staticdir.root': '%s/static' % cfg.file_root,
|
||||||
|
'tools.staticdir.on': True,
|
||||||
|
'tools.staticdir.dir': '.'}}
|
||||||
|
cherrypy.tree.mount(None, cfg.server_dir + '/static', config)
|
||||||
|
|
||||||
|
cherrypy.engine.signal_handler.subscribe()
|
||||||
|
|
||||||
|
|
||||||
|
def context_processor(request):
|
||||||
|
"""Add additional context values to RequestContext for use in templates"""
|
||||||
|
path_parts = request.path.split('/')
|
||||||
|
active_menu_urls = ['/'.join(path_parts[:length])
|
||||||
|
for length in xrange(1, len(path_parts))]
|
||||||
|
return {
|
||||||
|
'cfg': cfg,
|
||||||
|
'main_menu': cfg.main_menu,
|
||||||
|
'submenu': cfg.main_menu.active_item(request),
|
||||||
|
'request_path': request.path,
|
||||||
|
'basehref': cfg.server_dir,
|
||||||
|
'username': request.session.get(cfg.session_key, None),
|
||||||
|
'active_menu_urls': active_menu_urls
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def configure_django():
|
||||||
|
"""Setup Django configuration in the absense of .settings file"""
|
||||||
|
context_processors = [
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.core.context_processors.debug',
|
||||||
|
'django.core.context_processors.i18n',
|
||||||
|
'django.core.context_processors.media',
|
||||||
|
'django.core.context_processors.static',
|
||||||
|
'django.core.context_processors.tz',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'plinth.context_processor']
|
||||||
|
|
||||||
|
template_directories = module_loader.get_template_directories()
|
||||||
|
sessions_directory = os.path.join(cfg.data_dir, 'sessions')
|
||||||
|
django.conf.settings.configure(
|
||||||
|
DEBUG=False,
|
||||||
|
ALLOWED_HOSTS=['127.0.0.1', 'localhost'],
|
||||||
|
TEMPLATE_DIRS=template_directories,
|
||||||
|
INSTALLED_APPS=['bootstrapform'],
|
||||||
|
ROOT_URLCONF='urls',
|
||||||
|
SESSION_ENGINE='django.contrib.sessions.backends.file',
|
||||||
|
SESSION_FILE_PATH=sessions_directory,
|
||||||
|
STATIC_URL=cfg.server_dir + '/static/',
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS=context_processors)
|
||||||
|
|
||||||
config = {
|
|
||||||
'/': {'tools.staticdir.root': '%s/static' % cfg.file_root,
|
|
||||||
'tools.proxy.on': True,},
|
|
||||||
'/static': {'tools.staticdir.on': True,
|
|
||||||
'tools.staticdir.dir': "."},
|
|
||||||
'/favicon.ico':{'tools.staticfile.on': True,
|
|
||||||
'tools.staticfile.filename':
|
|
||||||
"%s/static/theme/favicon.ico" % cfg.file_root}}
|
|
||||||
cherrypy.tree.mount(cfg.html_root, cfg.server_dir, config=config)
|
|
||||||
cherrypy.engine.signal_handler.subscribe()
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Initialize basic services
|
"""Intialize and start the application"""
|
||||||
|
setup_logging()
|
||||||
|
|
||||||
service.init()
|
service.init()
|
||||||
|
|
||||||
setup()
|
setup_configuration()
|
||||||
|
|
||||||
# Configure Django
|
configure_django()
|
||||||
template_directories = get_template_directories()
|
|
||||||
django.conf.settings.configure(TEMPLATE_DIRS=template_directories,
|
module_loader.load_modules()
|
||||||
INSTALLED_APPS=['bootstrapform'])
|
|
||||||
|
cfg.users = plugin_mount.UserStoreModule.get_plugins()[0]
|
||||||
|
|
||||||
|
setup_server()
|
||||||
|
|
||||||
cherrypy.engine.start()
|
cherrypy.engine.start()
|
||||||
cherrypy.engine.block()
|
cherrypy.engine.block()
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
{% load static %}
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<!--[if lt IE 7 ]> <html class="ie ie6 no-js" lang="en"> <![endif]-->
|
<!--[if lt IE 7 ]> <html class="ie ie6 no-js" lang="en"> <![endif]-->
|
||||||
<!--[if IE 7 ]> <html class="ie ie7 no-js" lang="en"> <![endif]-->
|
<!--[if IE 7 ]> <html class="ie ie7 no-js" lang="en"> <![endif]-->
|
||||||
@ -29,20 +30,20 @@
|
|||||||
<title>{% if title %} {{ title }} {% else %} FreedomBox {% endif %}</title>
|
<title>{% if title %} {{ title }} {% else %} FreedomBox {% endif %}</title>
|
||||||
|
|
||||||
<!-- This is the traditional favicon. Size: 16x16 or 32x32, transparency is OK -->
|
<!-- This is the traditional favicon. Size: 16x16 or 32x32, transparency is OK -->
|
||||||
<link rel="shortcut icon" href="{{ basehref }}/static/theme/img/favicon.ico" />
|
<link rel="shortcut icon" href="{% static 'theme/img/favicon.ico' %}"/>
|
||||||
|
|
||||||
<!-- The is the icon for iOS's Web Clip. Size: 57x57 for older iPhones, 72x72 for iPads, 114x114 for iPhone4
|
<!-- The is the icon for iOS's Web Clip. Size: 57x57 for older iPhones, 72x72 for iPads, 114x114 for iPhone4
|
||||||
- To prevent iOS from applying its styles to the icon name it thusly: apple-touch-icon-precomposed.png
|
- To prevent iOS from applying its styles to the icon name it thusly: apple-touch-icon-precomposed.png
|
||||||
- Transparency is not recommended (iOS will put a black BG behind the icon) -->
|
- Transparency is not recommended (iOS will put a black BG behind the icon) -->
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="{{ basehref }}/static/theme/img/apple-touch-icon-57px-precomposed.png" />
|
<link rel="apple-touch-icon" sizes="57x57" href="{% static 'theme/img/apple-touch-icon-57px-precomposed.png' %}"/>
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="{{ basehref }}/static/theme/img/apple-touch-icon-72px-precomposed.png" />
|
<link rel="apple-touch-icon" sizes="72x72" href="{% static 'theme/img/apple-touch-icon-72px-precomposed.png' %}"/>
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="{{ basehref }}/static/theme/img/apple-touch-icon-114px-precomposed.png" />
|
<link rel="apple-touch-icon" sizes="114x114" href="{% static 'theme/img/apple-touch-icon-114px-precomposed.png' %}"/>
|
||||||
|
|
||||||
<!-- Bootstrap base CSS -->
|
<!-- Bootstrap base CSS -->
|
||||||
<link rel="stylesheet" href="{{ basehref }}/static/theme/css/bootstrap.min.css" />
|
<link rel="stylesheet" href="{% static 'theme/css/bootstrap.min.css' %}"/>
|
||||||
<!-- Bootstrap responsive CSS -->
|
<!-- Bootstrap responsive CSS -->
|
||||||
<link rel="stylesheet" href="{{ basehref }}/static/theme/css/bootstrap-responsive.min.css" />
|
<link rel="stylesheet" href="{% static 'theme/css/bootstrap-responsive.min.css' %}"/>
|
||||||
<link rel="stylesheet" href="{{ basehref }}/static/theme/css/plinth.css" />
|
<link rel="stylesheet" href="{% static 'theme/css/plinth.css' %}"/>
|
||||||
<!-- CSS from previous Plinth template, not sure what to keep yet -->
|
<!-- CSS from previous Plinth template, not sure what to keep yet -->
|
||||||
{{ css|safe }}
|
{{ css|safe }}
|
||||||
<!-- End Plinth CSS -->
|
<!-- End Plinth CSS -->
|
||||||
@ -57,7 +58,10 @@
|
|||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ basehref }}/" class="logo-top"><img src="{{ basehref }}/static/theme/img/freedombox-logo-32px.png" alt="FreedomBox" /></a><a class="brand" href="{{ basehref }}/">FreedomBox</a>
|
<a href="{{ basehref }}/" class="logo-top">
|
||||||
|
<img src="{% static 'theme/img/freedombox-logo-32px.png' %}" alt="FreedomBox" />
|
||||||
|
</a>
|
||||||
|
<a class="brand" href="{{ basehref }}/">FreedomBox</a>
|
||||||
{% block add_nav_and_login %}
|
{% block add_nav_and_login %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div><!--/.nav-collapse -->
|
</div><!--/.nav-collapse -->
|
||||||
@ -111,10 +115,6 @@
|
|||||||
free software offered to you under the terms of
|
free software offered to you under the terms of
|
||||||
the <a href="http://www.gnu.org/licenses/agpl.html" target="_blank">GNU Affero General Public
|
the <a href="http://www.gnu.org/licenses/agpl.html" target="_blank">GNU Affero General Public
|
||||||
License</a>, Version 3 or later. This Plinth theme was built by <a href="http://seandiggity.com" target="_blank">Sean "Diggity" O'Brien</a>.
|
License</a>, Version 3 or later. This Plinth theme was built by <a href="http://seandiggity.com" target="_blank">Sean "Diggity" O'Brien</a>.
|
||||||
</p>
|
|
||||||
<p>Current page: {{ current_url }}</p>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}</p>
|
{% endblock %}</p>
|
||||||
</footer>
|
</footer>
|
||||||
@ -123,11 +123,11 @@
|
|||||||
|
|
||||||
<!-- JavaScript <script> tags are placed at the end of the document to speed up initial page loads-->
|
<!-- JavaScript <script> tags are placed at the end of the document to speed up initial page loads-->
|
||||||
<!-- Local link to system Modernizr (includes HTML5 Shiv) -->
|
<!-- Local link to system Modernizr (includes HTML5 Shiv) -->
|
||||||
<script type="text/javascript" src="{{ basehref }}/static/theme/js/libs/modernizr.min.js"></script>
|
<script type="text/javascript" src="{% static 'theme/js/libs/modernizr.min.js' %}"></script>
|
||||||
<!-- Local link to system jQuery -->
|
<!-- Local link to system jQuery -->
|
||||||
<script type="text/javascript" src="{{ basehref }}/static/theme/js/libs/jquery.min.js"></script>
|
<script type="text/javascript" src="{% static 'theme/js/libs/jquery.min.js' %}"></script>
|
||||||
<!-- Local link to system Bootstrap JS -->
|
<!-- Local link to system Bootstrap JS -->
|
||||||
<script type="text/javascript" src="{{ basehref }}/static/theme/js/libs/bootstrap.min.js"></script>
|
<script type="text/javascript" src="{% static 'theme/js/libs/bootstrap.min.js' %}"></script>
|
||||||
|
|
||||||
{% block js_block %}
|
{% block js_block %}
|
||||||
{{ js|safe }}
|
{{ js|safe }}
|
||||||
|
|||||||
@ -5,8 +5,13 @@
|
|||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
{% for item in main_menu.items %}
|
{% for item in main_menu.items %}
|
||||||
<li class="{{ item.active_p|yesno:"active," }}">
|
{% if item.url in active_menu_urls %}
|
||||||
<a href="{{ item.url }}" class="{{ item.active_p|yesno:"active," }}">
|
<li class="active">
|
||||||
|
<a href="{{ item.url }}" class="active">
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ item.url }}">
|
||||||
|
{% endif %}
|
||||||
<span class="{{ item.icon }} icon-white nav-icon"></span>
|
<span class="{{ item.icon }} icon-white nav-icon"></span>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -21,16 +21,21 @@
|
|||||||
<li class="nav-header">Menu</li>
|
<li class="nav-header">Menu</li>
|
||||||
{% for item in menu.items %}
|
{% for item in menu.items %}
|
||||||
|
|
||||||
<li class="{{ item.active_p|yesno:"active," }}">
|
{% if item.url in active_menu_urls %}
|
||||||
<a href="{{ item.url }}" class="{{ item.active_p|yesno:"active," }}">
|
<li class="active">
|
||||||
<span class="{{ item.icon }} {{ item.active_p|yesno:"icon-white," }}
|
<a href="{{ item.url }}" class="active">
|
||||||
sidenav-icon"></span>
|
<span class="{{ item.icon }} icon-white sidenav-icon"></span>
|
||||||
{{ item.label }}
|
{% else %}
|
||||||
</a>
|
<li>
|
||||||
{% if item.items %}
|
<a href="{{ item.url }}">
|
||||||
{% include "menu.html" with menu=item %}
|
<span class="{{ item.icon }} sidenav-icon"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
{{ item.label }}
|
||||||
|
</a>
|
||||||
|
{% if item.items %}
|
||||||
|
{% include "menu.html" with menu=item %}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
#
|
#
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% for severity, message in messages %}
|
{% for severity, message in messages_ %}
|
||||||
<div class='alert alert-{{ severity }} alert-dismissable'>
|
<div class='alert alert-{{ severity }} alert-dismissable'>
|
||||||
<a class="close" data-dismiss="alert">×</a>
|
<a class="close" data-dismiss="alert">×</a>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
|
|||||||
52
views.py
Normal file
52
views.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Main Plinth views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.http.response import HttpResponseRedirect
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
|
||||||
|
import cfg
|
||||||
|
from withsqlite.withsqlite import sqlite_db
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
"""Serve the main index page"""
|
||||||
|
# TODO: Move firstboot handling to firstboot module somehow
|
||||||
|
with sqlite_db(cfg.store_file, table='firstboot') as database:
|
||||||
|
if not 'state' in database:
|
||||||
|
# If we created a new user db, make sure it can't be read by
|
||||||
|
# everyone
|
||||||
|
userdb_fname = '{}.sqlite3'.format(cfg.user_db)
|
||||||
|
os.chmod(userdb_fname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
|
||||||
|
# Permanent redirect causes the browser to cache the redirect,
|
||||||
|
# preventing the user from navigating to /plinth until the
|
||||||
|
# browser is restarted.
|
||||||
|
return HttpResponseRedirect(cfg.server_dir + '/firstboot')
|
||||||
|
|
||||||
|
if database['state'] < 5:
|
||||||
|
cfg.log('First boot state = %d' % database['state'])
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
cfg.server_dir + '/firstboot/state%d' % database['state'])
|
||||||
|
|
||||||
|
if request.session.get(cfg.session_key, None):
|
||||||
|
return HttpResponseRedirect(cfg.server_dir + '/apps')
|
||||||
|
|
||||||
|
return HttpResponseRedirect(cfg.server_dir + '/help/about')
|
||||||
Loading…
x
Reference in New Issue
Block a user