mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-27 10:44:33 +00:00
Convert config page to Django form
This commit is contained in:
parent
0bd1798baf
commit
6ebfe23c4b
@ -20,6 +20,8 @@ Plinth module for configuring timezone, hostname etc.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
from django import forms
|
||||||
|
from django.core import validators
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
try:
|
try:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
@ -30,61 +32,119 @@ import socket
|
|||||||
|
|
||||||
import actions
|
import actions
|
||||||
import cfg
|
import cfg
|
||||||
from forms import Form
|
|
||||||
from modules.auth import require
|
from modules.auth import require
|
||||||
from plugin_mount import PagePlugin, FormPlugin
|
from plugin_mount import PagePlugin
|
||||||
import util
|
import util
|
||||||
|
|
||||||
|
|
||||||
class Config(PagePlugin):
|
class TrimmedCharField(forms.CharField):
|
||||||
|
"""Trim the contents of a CharField"""
|
||||||
|
def clean(self, value):
|
||||||
|
"""Clean and validate the field value"""
|
||||||
|
if value:
|
||||||
|
value = value.strip()
|
||||||
|
|
||||||
|
return super(TrimmedCharField, self).clean(value)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurationForm(forms.Form):
|
||||||
|
"""Main system configuration form"""
|
||||||
|
time_zone = forms.ChoiceField(
|
||||||
|
label=_('Time Zone'),
|
||||||
|
help_text=_('Set your timezone to get accurate timestamps. \
|
||||||
|
This information will be used to set your systemwide timezone'))
|
||||||
|
|
||||||
|
# We're more conservative than RFC 952 and RFC 1123
|
||||||
|
hostname = TrimmedCharField(
|
||||||
|
label=_('Hostname'),
|
||||||
|
help_text=_('Your hostname is the local name by which other machines \
|
||||||
|
on your LAN can reach you. It must be alphanumeric, start with an alphabet \
|
||||||
|
and must not be greater than 63 characters in length.'),
|
||||||
|
validators=[
|
||||||
|
validators.RegexValidator(r'^[a-zA-Z][a-zA-Z0-9]{,62}$',
|
||||||
|
_('Invalid hostname'))])
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# pylint: disable-msg=E1101, W0233
|
||||||
|
forms.Form.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['time_zone'].choices = [(zone, zone)
|
||||||
|
for zone in self.get_time_zones()]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_time_zones():
|
||||||
|
"""Return list of available time zones"""
|
||||||
|
# XXX: Get rid of a custom file and read the values from /usr
|
||||||
|
module_file = __file__
|
||||||
|
if module_file.endswith(".pyc"):
|
||||||
|
module_file = module_file[:-1]
|
||||||
|
|
||||||
|
module_dir = os.path.dirname(os.path.realpath(module_file))
|
||||||
|
time_zones_file = os.path.join(module_dir, 'time_zones')
|
||||||
|
return json.loads(util.slurp(time_zones_file))
|
||||||
|
|
||||||
|
|
||||||
|
class Configuration(PagePlugin):
|
||||||
"""System configuration page"""
|
"""System configuration page"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
del args # Unused
|
del args # Unused
|
||||||
del kwargs # Unused
|
del kwargs # Unused
|
||||||
|
|
||||||
self.register_page("sys.config")
|
self.register_page('sys.config')
|
||||||
|
|
||||||
self.menu = cfg.html_root.sys.menu.add_item(_('Configure'), 'icon-cog',
|
self.menu = cfg.html_root.sys.menu.add_item(_('Configure'), 'icon-cog',
|
||||||
'/sys/config', 10)
|
'/sys/config', 10)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@require()
|
@require()
|
||||||
def index(self):
|
def index(self, **kwargs):
|
||||||
"""Serve configuration page"""
|
"""Serve the configuration form"""
|
||||||
parts = self.forms('/sys/config')
|
status = self.get_status()
|
||||||
parts['title'] = _("Configure this {box_name}") \
|
|
||||||
.format(box_name=cfg.box_name)
|
|
||||||
|
|
||||||
return util.render_template(**parts) # pylint: disable-msg=W0142
|
form = None
|
||||||
|
messages = []
|
||||||
|
|
||||||
|
if kwargs and cfg.users.expert():
|
||||||
|
form = ConfigurationForm(kwargs, prefix='configuration')
|
||||||
|
# pylint: disable-msg=E1101
|
||||||
|
if form.is_valid():
|
||||||
|
self._apply_changes(status, form.cleaned_data, messages)
|
||||||
|
status = self.get_status()
|
||||||
|
form = ConfigurationForm(initial=status,
|
||||||
|
prefix='configuration')
|
||||||
|
else:
|
||||||
|
form = ConfigurationForm(initial=status, prefix='configuration')
|
||||||
|
|
||||||
def valid_hostname(name):
|
return util.render_template(template='config',
|
||||||
"""
|
title=_('General Configuration'),
|
||||||
Return '' if name is a valid hostname by our standards (not just
|
form=form, messages=messages)
|
||||||
by RFC 952 and RFC 1123. We're more conservative than the
|
|
||||||
standard. If hostname isn't valid, return message explaining why.
|
|
||||||
"""
|
|
||||||
message = ''
|
|
||||||
if len(name) > 63:
|
|
||||||
message += "<br />Hostname too long (max is 63 characters)"
|
|
||||||
|
|
||||||
if not util.is_alphanumeric(name):
|
@staticmethod
|
||||||
message += "<br />Hostname must be alphanumeric"
|
def get_status():
|
||||||
|
"""Return the current status"""
|
||||||
|
return {'hostname': socket.gethostname(),
|
||||||
|
'time_zone': util.slurp('/etc/timezone').rstrip()}
|
||||||
|
|
||||||
if not name[0] in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
|
@staticmethod
|
||||||
message += "<br />Hostname must start with a letter"
|
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')))
|
||||||
|
else:
|
||||||
|
messages.append(('info', _('Hostname is unchanged')))
|
||||||
|
|
||||||
return message
|
if old_status['time_zone'] != new_status['time_zone']:
|
||||||
|
output, error = actions.superuser_run('timezone-change',
|
||||||
|
[new_status['time_zone']])
|
||||||
def get_hostname():
|
del output # Unused
|
||||||
"""Return the current hostname of the system"""
|
if error:
|
||||||
return socket.gethostname()
|
messages.append(('error',
|
||||||
|
_('Error setting time zone - %s') % error))
|
||||||
|
else:
|
||||||
def get_time_zone():
|
messages.append(('success', _('Time zone set')))
|
||||||
"""Return currently set system's timezone"""
|
else:
|
||||||
return util.slurp('/etc/timezone').rstrip()
|
messages.append(('info', _('Time zone is unchanged')))
|
||||||
|
|
||||||
|
|
||||||
def set_hostname(hostname):
|
def set_hostname(hostname):
|
||||||
@ -104,82 +164,3 @@ def set_hostname(hostname):
|
|||||||
except OSError as exception:
|
except OSError as exception:
|
||||||
raise cherrypy.HTTPError(500,
|
raise cherrypy.HTTPError(500,
|
||||||
'Updating hostname failed: %s' % exception)
|
'Updating hostname failed: %s' % exception)
|
||||||
|
|
||||||
class general(FormPlugin, PagePlugin):
|
|
||||||
"""Form to update hostname and time zone"""
|
|
||||||
url = ["/sys/config"]
|
|
||||||
order = 30
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def help(*args, **kwargs):
|
|
||||||
"""Build and return the help content area"""
|
|
||||||
del args # Unused
|
|
||||||
del kwargs # Unused
|
|
||||||
|
|
||||||
return _('''
|
|
||||||
<p>Set your timezone to get accurate timestamps. {product} will use
|
|
||||||
this information to set your {box}'s systemwide timezone.</p>''').format(
|
|
||||||
product=cfg.product_name, box=cfg.box_name)
|
|
||||||
|
|
||||||
def main(self, message='', time_zone=None, **kwargs):
|
|
||||||
"""Build and return the main content area which is the form"""
|
|
||||||
del kwargs # Unused
|
|
||||||
|
|
||||||
if not cfg.users.expert():
|
|
||||||
return _('''
|
|
||||||
<p>Only members of the expert group are allowed to see and modify the system
|
|
||||||
setup.</p>''')
|
|
||||||
|
|
||||||
if not time_zone:
|
|
||||||
time_zone = get_time_zone()
|
|
||||||
|
|
||||||
# Get the list of supported timezones and the index in that
|
|
||||||
# list of the current one
|
|
||||||
module_file = __file__
|
|
||||||
if module_file.endswith(".pyc"):
|
|
||||||
module_file = module_file[:-1]
|
|
||||||
module_dir = os.path.dirname(os.path.realpath(module_file))
|
|
||||||
time_zones_file = os.path.join(module_dir, 'time_zones')
|
|
||||||
time_zones = json.loads(util.slurp(time_zones_file))
|
|
||||||
try:
|
|
||||||
time_zone_id = time_zones.index(time_zone)
|
|
||||||
except ValueError:
|
|
||||||
cfg.log.critical("Unknown Time Zone: %s" % time_zone)
|
|
||||||
raise cherrypy.HTTPError(500, "Unknown Time Zone: %s" % time_zone)
|
|
||||||
|
|
||||||
# And now, the form.
|
|
||||||
form = Form(title=_("General Config"),
|
|
||||||
action=cfg.server_dir + "/sys/config/general/index",
|
|
||||||
name="config_general_form",
|
|
||||||
message=message)
|
|
||||||
form.html(self.help())
|
|
||||||
form.dropdown(_("Time Zone"), name="time_zone", vals=time_zones,
|
|
||||||
select=time_zone_id)
|
|
||||||
form.html('''
|
|
||||||
<p>Your hostname is the local name by which other machines on your LAN
|
|
||||||
can reach you.</p>''')
|
|
||||||
form.text_input('Hostname', name='hostname', value=get_hostname())
|
|
||||||
form.submit(_("Submit"))
|
|
||||||
|
|
||||||
return form.render()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def process_form(time_zone='', hostname='', *args, **kwargs):
|
|
||||||
"""Handle form submission"""
|
|
||||||
del args # Unused
|
|
||||||
del kwargs # Unused
|
|
||||||
|
|
||||||
message = ''
|
|
||||||
if hostname != get_hostname():
|
|
||||||
msg = valid_hostname(hostname)
|
|
||||||
if msg == '':
|
|
||||||
set_hostname(hostname)
|
|
||||||
else:
|
|
||||||
message += msg
|
|
||||||
|
|
||||||
time_zone = time_zone.strip()
|
|
||||||
if time_zone != get_time_zone():
|
|
||||||
cfg.log.info("Setting timezone to %s" % time_zone)
|
|
||||||
actions.superuser_run("timezone-change", [time_zone])
|
|
||||||
|
|
||||||
return message or "Settings updated."
|
|
||||||
|
|||||||
28
modules/installed/system/templates/config.html
Normal file
28
modules/installed/system/templates/config.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{% extends "login_nav.html" %}
|
||||||
|
{% load bootstrap %}
|
||||||
|
|
||||||
|
{% block main_block %}
|
||||||
|
|
||||||
|
{% if cfg.users.expert %}
|
||||||
|
|
||||||
|
{% for severity, message in messages %}
|
||||||
|
<div class='alert alert-{{ severity }}'>{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{{ form|bootstrap }}
|
||||||
|
|
||||||
|
<input type="submit" class="btn-primary" value="Submit"/>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<p>Only members of the expert group are allowed to see and modify
|
||||||
|
the system setup.</p>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
Loading…
x
Reference in New Issue
Block a user