Verified Commit 9181ded2 authored by Štěpán Henek's avatar Štěpán Henek 🌩

guide integration: with fake messages

parent 6ba4b7aa
......@@ -199,6 +199,15 @@ def reboot():
bottle.redirect(reverse("/"))
@login_required
def leave_guide():
current_state.backend.perform("web", "update_guide", {
"enabled": False,
"workflow": current_state.guide.workflow,
})
bottle.redirect(reverse("/"))
def ping():
res = bottle.response.copy(cls=bottle.HTTPResponse)
res.content_type = 'application/json'
......@@ -231,6 +240,7 @@ def init_default_app(index, include_static=False):
app.route("/lang/<lang:re:\w{2}>", name="change_lang", callback=change_lang)
app.route("/logout", name="logout", callback=logout)
app.route("/reboot", name="reboot", callback=reboot)
app.route("/leave_guide", method="POST", name="leave_guide", callback=leave_guide)
if include_static:
app.route('/static/<filename:re:.*>', name="static", callback=static)
app.route("/js/<filename:re:.*>", name="render_js", callback=render_js)
......
......@@ -633,6 +633,10 @@ def index():
@login_required
def config_page_get(page_name):
# redirect in case that guide is not passed
if current_state.guide.enabled and page_name not in current_state.guide.available_tabs:
bottle.redirect(reverse("config_page", page_name=current_state.guide.current))
bottle.SimpleTemplate.defaults['active_config_page_key'] = page_name
ConfigPage = get_config_page(page_name)
config_page = ConfigPage()
......
#
# Foris - web administration interface for OpenWrt based on NETCONF
# Copyright (C) 2017 CZ.NIC, z.s.p.o. <http://www.nic.cz>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from foris.utils.translators import _
class StandardMessages(object):
MESSAGES_CURRENT_MAP = {
"password": [
_(
"Welcome to Foris configuration. Bla bla bla"
),
],
"wan": [
_(
"In order to access the internet you need to configure your WAN device. Bla bla bla"
),
],
"time": [
_("In order to verify TLS certificates validity your time has to be properly set. ..."),
],
"dns": [
_("To resolve ip addresses to hostnames ..."),
],
}
MESSAGES_MAP = {
"password": [
MESSAGES_CURRENT_MAP["password"][0],
_("Your password seems to be set. You may proceed to the next step"),
],
"wan": [
MESSAGES_CURRENT_MAP["wan"][0],
_(
"You've configured your wan interface. "
"Try to run connection test to see whether it is working properly"
),
],
"time": [
MESSAGES_CURRENT_MAP["time"][0],
_(
"Your timezone seems to be set. Please make sure that the time is correct "
" because bla bla bla"
)
],
"dns": [
MESSAGES_CURRENT_MAP["dns"][0],
_(
"You've updated your dns configuration. "
"Try to run connection test to see whether your dns is properly set."
),
],
"updater": [
_("To keep your software up-to-date you need to configure updater first."),
],
}
@staticmethod
def get(step, current):
if current and step in StandardMessages.MESSAGES_CURRENT_MAP:
return StandardMessages.MESSAGES_CURRENT_MAP[step]
return StandardMessages.MESSAGES_MAP[step]
class Guide(object):
WORKFLOW_STANDARD = "standard"
STEP_PASSWORD = "password"
STEP_WAN = "wan"
STEP_TIME = "time"
STEP_DNS = "dns"
STEP_UPDATER = "updater"
STANDARD_STEPS = (
STEP_PASSWORD,
STEP_WAN,
STEP_TIME,
STEP_DNS,
STEP_UPDATER,
)
def __init__(self, backend_data):
self.enabled = backend_data["enabled"]
self.workflow = backend_data["workflow"]
self.current = self._get_current(backend_data["passed"])
if not self.current: # All required steps are passed -> disable guide
self.enabled = False
def _get_current(self, passed):
if self.workflow == Guide.WORKFLOW_STANDARD:
for step in Guide.STANDARD_STEPS:
if step not in passed:
return step
return None
raise NotImplementedError("unknown workflow %s" % self.workflow)
@property
def available_tabs(self):
if self.workflow == Guide.WORKFLOW_STANDARD:
return Guide.STANDARD_STEPS[:Guide.STANDARD_STEPS.index(self.current) + 1]
raise NotImplementedError("unknown workflow %s" % self.workflow)
def is_available(self, step):
if not self.enabled:
return True
return step in self.available_tabs
def message(self, step=None):
if not self.enabled:
return None
if self.workflow == Guide.WORKFLOW_STANDARD:
step = step if step else self.current
return StandardMessages.get(step, step == self.current)
raise NotImplementedError("unknown workflow %s" % self.workflow)
......@@ -68,4 +68,7 @@ class BackendData(object):
# update whether password is set
current_state.update_password_set(data["password_ready"])
# initialize guide
current_state.update_guide(data["guide"])
return self.app(environ, start_response)
......@@ -80,5 +80,10 @@ class ForisState(object):
logger.debug("setting password_set=%s" % password_set)
self.password_set = password_set
def update_guide(self, guide_data):
from foris.guide import Guide
logger.debug("setting guide_data (%s)" % guide_data)
self.guide = Guide(guide_data)
current_state = ForisState()
......@@ -119,6 +119,29 @@ body
text-align: center
vertical-align: middle
#guide-box
+rounded-borders
padding: 1em 1em 1em 1em
display: block
background-color: $message-color-info
color: white
.guide-title
font-weight: bold
text-transform: uppercase
.guide-buttons
text-align: right
.button
background-color: #fff
color: $message-color-info
font-weight: bold
font-family: $base-font
form
display: inline
#reboot-required-notice
+rounded-borders
padding: 1em 1em 1em 1em
......
......@@ -26,7 +26,7 @@ input.grayed, input[disabled]
input[type="file"]
border: none
.wizard-form, .config-form, .maintenance-form, .dns-form
.wizard-form, .config-form, .maintenance-form
+pie-clearfix
margin-bottom: 1em
......
......@@ -32,6 +32,23 @@
</div>
<div id="content-wrap">
<div id="content">
%if foris_info.guide.enabled:
<div id="guide-box">
<p class="guide-title">{{ trans("foris guide") }}</p>
%for msg in foris_info.guide.message(active_config_page_key):
<p>{{ msg }}</p>
%end
<div class="guide-buttons">
<form method="post" action="{{ url("leave_guide") }}">
<input type="hidden" name="csrf_token" value="{{ get_csrf_token() }}">
<button type="submit" name="target" class="button" value="save">{{ trans("Leave Guided Mode") }}</button>
</form>
%if foris_info.guide.current != active_config_page_key:
<a class="button" href="{{ url("config_page", page_name=foris_info.guide.current) }}">{{ trans("Next step") }} ➡</a>
%end
</div>
</div>
%end
<div id="network-restart-notice" class="main-warning-notice">
<img src="{{ static("img/loader.gif") }}" alt="{{ trans("Network is restarting...") }}" title="{{ trans("Network is restarting...") }}">
<span>{{ trans("Your network is being restarted. Wait, please...") }}</span>
......@@ -61,11 +78,16 @@
<nav>
<ul>
%for slug, config_page, menu_tag in config_pages.menu_list():
%if foris_info.guide.is_available(slug):
<li{{! ' class="active"' if defined("active_config_page_key") and slug == active_config_page_key else "" }}>
<a href="{{ url("config_page", page_name=slug) }}">{{ trans(config_page.userfriendly_title) }}
<span title="{{ menu_tag["hint"]}}" style="{{"" if menu_tag["show"] else "display: none" }}" id="{{ slug }}_menu_tag" class="menu-tag">{{! menu_tag["text"] }}</span>
% show = menu_tag["show"] or foris_info.guide.enabled and slug == foris_info.guide.current
<span title="{{ menu_tag["hint"]}}" style="{{"" if show else "display: none" }}" id="{{ slug }}_menu_tag" class="menu-tag">
{{ "⬅" if foris_info.guide.enabled and slug == foris_info.guide.current else menu_tag["text"] }}
</span>
</a>
</li>
%end
%end
</ul>
</nav>
......
......@@ -23,12 +23,15 @@
%if not contract_valid():
<p>{{! trans("In rare cases ISP's have improperly configured network which interferes with DNSSEC validation. If you experience problems with DNS, you can <strong>temporarily</strong> disable DNSSEC validation to determine the source of the problem. However, keep in mind that without DNSSEC validation, you are vulnerable to DNS spoofing attacks! Therefore we <strong>recommend keeping DNSSEC turned on</strong> and resolving the situation with your ISP as this is a serious flaw on their side.") }}</p>
%end
<form id="main-form" class="dns-form" action="{{ request.fullpath }}" method="post" enctype="multipart/form-data" autocomplete="off" novalidate>
<form id="main-form" class="config-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.tpl", field=field)
%end
<button type="submit" name="send" class="button">{{ trans("Save") }}</button>
<div class="form-buttons">
<a href="{{ request.fullpath }}" class="button grayed">{{ trans("Discard changes") }}</a>
<button type="submit" name="send" class="button">{{ trans("Save changes") }}</button>
</div>
</form>
<h2>{{ trans("Connection test") }}</h2>
......
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