Commit 8b6656b6 authored by Jan Čermák's avatar Jan Čermák

automatic restarts and SMTP settings (WIP) - refs #2983 & refs #3104

parent 593a5bfc
......@@ -31,7 +31,7 @@ from utils.bottle_csrf import CSRFPlugin
from utils.routing import reverse
logger = logging.getLogger("admin")
logger = logging.getLogger(__name__)
app = Bottle()
......@@ -162,13 +162,32 @@ class MaintenanceConfigPage(ConfigPageMixin, MaintenanceHandler):
client.reboot()
bottle.redirect(reverse("config_page", page_name="maintenance"))
def _action_save_notifications(self):
if bottle.request.method != 'POST':
messages.error("Wrong HTTP method.")
bottle.redirect(reverse("config_page", page_name="maintenance"))
handler = NotificationsHandler(request.POST)
if handler.save():
messages.success(_("Configuration was successfully saved."))
bottle.redirect(reverse("config_page", page_name="maintenance"))
messages.warning(_("There were some errors in your input."))
return super(MaintenanceConfigPage, self).render(notifications_form=handler.form,
config_pages=config_page_map.display_names())
def call_action(self, action):
if action == "config-backup":
return self._action_config_backup()
elif action == "reboot":
return self._action_reboot()
elif action == "save_notifications":
return self._action_save_notifications()
raise ValueError("Unknown AJAX action.")
def render(self, **kwargs):
notifications_handler = NotificationsHandler(self.data)
return super(MaintenanceConfigPage, self).render(notifications_form=notifications_handler.form,
**kwargs)
def save(self, *args, **kwargs):
result = False
try:
......@@ -267,8 +286,7 @@ def config_page_post(page_name):
# only update is allowed
logger.debug("ajax request")
request.POST.pop("update", None)
return dict(html=config_page.render(is_xhr=True))
return config_page.render(is_xhr=True)
try:
if config_page.save():
bottle.redirect(request.fullpath)
......@@ -293,6 +311,31 @@ def config_action(page_name, action):
raise bottle.HTTPError(404, "Unknown action.")
@app.route("/<page_name:re:.+>/action/<action:re:.+>", method="POST")
@login_required
def config_action_post(page_name, action):
ConfigPage = get_config_page(page_name)
config_page = ConfigPage(request.POST)
if request.is_xhr:
# only update is allowed
request.POST.pop("update", None)
return config_page.render(is_xhr=True)
try:
result = config_page.call_action(action)
try:
if not result:
bottle.redirect(reverse("config_page", page_name=page_name))
except TypeError:
# raised by Validator - could happen when the form is posted with wrong fields
messages.error(_("Configuration could not be saved due to an internal error."))
logger.exception("Error when saving form.")
logger.warning("Form not saved.")
return result
except ValueError:
raise bottle.HTTPError(404, "Unknown action.")
@app.route("/<page_name:re:.+>/ajax")
@login_required
def config_ajax(page_name):
......
......@@ -16,7 +16,8 @@
from base import BaseConfigHandler, LanHandler, MaintenanceHandler, \
PasswordHandler, TimeHandler, WanHandler, WifiHandler, SystemPasswordHandler, \
DNSHandler
DNSHandler, NotificationsHandler
__all__ = ['BaseConfigHandler', 'LanHandler', 'MaintenanceHandler', 'PasswordHandler',
'TimeHandler', 'WanHandler', 'WifiHandler', 'SystemPasswordHandler', 'DNSHandler']
\ No newline at end of file
'TimeHandler', 'WanHandler', 'WifiHandler', 'SystemPasswordHandler', 'DNSHandler',
'NotificationsHandler']
\ No newline at end of file
......@@ -609,3 +609,120 @@ class MaintenanceHandler(BaseConfigHandler):
maintenance_form.add_callback(maintenance_form_cb)
return maintenance_form
class NotificationsHandler(BaseConfigHandler):
# {{ _("Notifications") }} - for translation
userfriendly_title = "Notifications"
def get_form(self):
notifications_form = fapi.ForisForm("notifications", self.data)
# basic settings
smtp = notifications_form.add_section(name="smtp", title=_("SMTP settings"))
smtp.add_field(Checkbox, name="enable_smtp", label=_("Enable SMTP"),
nuci_path="uci.user_notify.smtp.enable",
nuci_preproc=lambda val: bool(int(val.value)),
default=False)
smtp.add_field(Textbox, name="from", label=_("Sender address (From)"),
nuci_path="uci.user_notify.smtp.from",
required=True).requires("enable_smtp", True)
smtp.add_field(Textbox, name="server", label=_("Server address"),
nuci_path="uci.user_notify.smtp.server",
required=True).requires("enable_smtp", True)
smtp.add_field(Textbox, name="port", label=_("Server port"),
nuci_path="uci.user_notify.smtp.port",
required=True).requires("enable_smtp", True)
smtp.add_field(Textbox, name="username", label=_("Username"),
nuci_path="uci.user_notify.smtp.username",
required=True).requires("enable_smtp", True)
smtp.add_field(Textbox, name="password", label=_("Password"),
nuci_path="uci.user_notify.smtp.password",
required=True).requires("enable_smtp", True)
SECURITY_OPTIONS = (
("none", _("None")),
("ssl", _("SSL/TLS")),
("starttls", _("STARTTLS")),
)
smtp.add_field(Dropdown, name="security", label=_("Security"),
nuci_path="uci.user_notify.smtp.security",
args=SECURITY_OPTIONS, default="none",
required=True).requires("enable_smtp", True)
smtp.add_field(Textbox, name="to", label=_("To"),
nuci_path="uci.user_notify.smtp.to",
nuci_preproc=lambda x: " ".join(map(lambda value: value.content, x.children)),
required=True).requires("enable_smtp", True)
# notifications severity
notifications = notifications_form.add_section(name="notifications",
title=_("Notifications settings"))
SEVERITY_OPTIONS = (
(0, _("Never")),
(1, _("On errors and before restarts")),
(2, _("Errors, updates and other news")),
)
notifications.add_field(Dropdown, name="severity", label=_("Severity"),
nuci_path="uci.user_notify.notifications.severity",
args=SEVERITY_OPTIONS, default=0)\
.requires("enable_smtp", True)
# reboot time
reboot = notifications_form.add_section(name="reboot",
title=_("Automatic restarts"))
reboot.add_field(Textbox, name="delay", label=_("Delay"),
hint=_("Number of days after notification that must pass before automatic restart."),
nuci_path="uci.user_notify.reboot.delay",
validators=[validators.Integer()],
required=True)\
.requires("enable_smtp", True)
reboot.add_field(Textbox, name="reboot_hour", label=_("Reboot hour"),
nuci_path="uci.user_notify.reboot.reboot_hour",
validators=[validators.InRange(0, 23)],
required=True)
reboot.add_field(Textbox, name="reboot_minute", label=_("Reboot minute"),
nuci_path="uci.user_notify.reboot.reboot_minute",
validators=[validators.InRange(0, 59)],
required=True)
def notifications_form_cb(data):
uci = Uci()
user_notify = Config("user_notify")
uci.add(user_notify)
smtp = Section("smtp", "smtp")
user_notify.add(smtp)
smtp.add(Option("enable", data['enable_smtp']))
reboot = Section("reboot", "reboot")
user_notify.add(reboot)
reboot.add(Option("reboot_hour", data['reboot_hour']))
reboot.add(Option("reboot_minute", data['reboot_minute']))
if data['enable_smtp']:
# basic SMTP config
smtp.add(Option("from", data['from']))
smtp.add(Option("server", data['server']))
smtp.add(Option("port", data['port']))
smtp.add(Option("username", data['username']))
smtp.add(Option("password", data['password']))
smtp.add(Option("security", data['security']))
to = List("to")
for i, to_item in enumerate(data['to'].split(" ")):
to.add(Value(i, to_item))
smtp.add_replace(to)
#notifications section
notifications = Section("notifications", "notifications")
user_notify.add(notifications)
notifications.add(Option("severity", data['severity']))
#reboot section
reboot.add(Option("delay", data['delay']))
return "edit_config", uci
notifications_form.add_callback(notifications_form_cb)
return notifications_form
......@@ -64,6 +64,9 @@ h2
h3
+adjust-font-size-to(26px)
h4
+adjust-font-size-to(20px)
p
margin-bottom: 1.5em
......
......@@ -28,7 +28,7 @@
<h3>{{ trans("Configuration restore") }}</h3>
<p>{{ trans("To restore the configuration from a backup file, upload it using following form. Keep in mind that IP address of this device might change during the process, causing unavailability of this interface.") }}</p>
<form id="main-form" class="maintenance-form" action="{{ request.fullpath }}" method="post" enctype="multipart/form-data" autocomplete="off" novalidate>
<form id="restore-form" class="maintenance-form" action="{{ request.fullpath }}" method="post" enctype="multipart/form-data" autocomplete="off" novalidate>
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
%for field in form.active_fields:
%include _field field=field
......@@ -42,4 +42,19 @@
<div>
<a href="{{ url("config_action", page_name="maintenance", action="reboot") }}" class="button">{{ trans("Reboot") }}</a>
</div>
<h3>{{ trans("Notifications and reboots") }}</h3>
<p>{{ trans("This is the configuration of e-mail notifications and reboots.") }}</p>
<form id="notifications-form" class="maintenance-form" action="{{ url("config_action", page_name="maintenance", action="save_notifications") }}" method="post" enctype="multipart/form-data" autocomplete="off" novalidate>
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
%for section in notifications_form.sections:
%if section.active_fields:
<h4>{{ section.title }}</h4>
%for field in section.active_fields:
%include _field field=field
%end
%end
%end
<button type="submit" name="send" class="button">{{ trans("Update") }}</button>
</form>
</div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment