Add sidebar and command bar to base template

Modify the base template to add the sidebar and command bar, implemented
just in the scopes view.

This patch also modifies the "actions/mode.html" template to be shown in
the scopes page. Any other action that should be inside the scopes
should do the same, add the scopes and clients to the template context
and use the "scopes.html" as base in those actions.

The notification has been also changed to use a toast notification
instead of the usual alert to avoid changing the layout on error.
multi-ogserver
Daniel García Moreno 2021-06-08 11:33:30 +02:00
parent db29b306aa
commit 22dcea19ff
6 changed files with 134 additions and 91 deletions

View File

@ -6,3 +6,11 @@ html, body {
width: 100%;
height: 100% !important;
}
#sidebar .list-group-item {
background-color: transparent;
}
.toast {
margin: 10px;
}

View File

@ -1,4 +1,4 @@
{% extends 'base.html' %}
{% extends 'scopes.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block content %}
@ -20,4 +20,3 @@
extra_classes="m-5") }}
{% endblock %}

View File

@ -17,24 +17,25 @@
<div class="main d-flex flex-column align-items-stretch h-100">
{% include 'nav.html' %}
{% block nav %}{% endblock %}
{% block flash %}
{% for category, message in get_flashed_messages(with_categories=True) %}
{% if category == 'info' %}
<div class="alert alert-info alert-dismissible fade show m-1" role="alert">
{% elif category == 'error' %}
<div class="alert alert-danger alert-dismissible fade show m-1" role="alert">
{% else %}
<div class="alert alert-warning alert-dismissible fade show m-1" role="alert">
{% endif %}
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">
<span aria-hidden="true">&times;</span>
</button>
<div class="container-fluid flex-grow-1">
{% block container %}
<div class="row h-100">
{# The sidebar is not visible on index #}
{% if request.endpoint != "index" %}
<div id="sidebar" class="bg-light col-md-3 col-lg-2">
{% block sidebar %}{% endblock %}
</div>
{% else %}
{% endif %}
<div id="content" class="col">
<div id="commands" class="py-2">{% block commands %}{% endblock %}</div>
<div class="container">
{% block content %}{% endblock %}
</div>
</div>
</div>
{% endfor %}
{% endblock %}
<div id="content" class="container-fluid flex-grow-1">{% block content %}{% endblock %}</div>
{% endblock %}
</div>
{% block footer %}
<footer class="footer navbar-inverse bg-dark flex-shrink-0" role="contentinfo">
@ -52,5 +53,26 @@
<script src="{{ url_for('static', filename='AdminLTE/plugins/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static', filename='AdminLTE/dist/js/adminlte.min.js') }}"></script>
<script>
// error messages
{% for category, message in get_flashed_messages(with_categories=True) %}
let bgclass = 'bg-success';
{% if category == 'info' %}
bgclass = 'bg-info';
{% elif category == 'error' %}
bgclass = 'bg-danger';
{% else %}
bgclass = 'bg-warning';
{% endif %}
$(document).Toasts('create', {
class: bgclass,
position: 'topLeft',
autohide: true,
delay: 5000,
title: '{{ message }}',
})
{% endfor %}
</script>
</body>
</html>

View File

@ -6,13 +6,25 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item {% if request.endpoint == "index" %}active{% endif %}">
<a class="nav-link" href="{{ url_for('index') }}">{{ _('Home') }}<span class="sr-only">(current)</span></a>
</li>
{% if current_user.is_authenticated %}
<li class="nav-item {% if request.endpoint == "index" %}active{% endif %}">
<a class="nav-link" href="{{ url_for('index') }}">{{ _('Dashboard') }}<span class="sr-only">(current)</span></a>
</li>
<li class="nav-item {% if request.endpoint == "scopes" %}active{% endif %}">
<a class="nav-link" href="{{ url_for('scopes') }}">{{ _('Scopes') }}</a>
</li>
<li class="nav-item {% if request.endpoint == "commands" %}active{% endif %}">
<a class="nav-link" href="#">{{ _('Commands') }}</a>
</li>
<li class="nav-item {% if request.endpoint == "images" %}active{% endif %}">
<a class="nav-link" href="#">{{ _('Images') }}</a>
</li>
<li class="nav-item {% if request.endpoint == "tasks" %}active{% endif %}">
<a class="nav-link" href="#">{{ _('Tasks') }}</a>
</li>
<li class="nav-item {% if request.endpoint == "schedule" %}active{% endif %}">
<a class="nav-link" href="#">{{ _('Schedule') }}</a>
</li>
{% endif %}
</ul>

View File

@ -3,10 +3,10 @@
{% macro print_scopes_tree(scopes) -%}
<ul class="list-group list-group-flush mx-5">
<ul class="list-group list-group-flush">
{% for scope in scopes %}
<li class="list-group-item state--{{ scope['state'] | lower }}">
<input class="form-check-input" type="checkbox"
<input class="form-check-input" type="checkbox" form="scopesForm"
value="{{ " ".join(scope["ip"]) }}"
name="{{ scope["name"] }}_{{ scope["id"] }}">
{{ scope["name"] }}
@ -20,51 +20,47 @@
{% endmacro %}
{% block content %}
<form>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{{ print_scopes_tree(scopes["scope"]) }}
<div class="dropdown mt-2">
<button class="btn btn-primary dropdown-toggle" type="button"
id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
{{ _('Actions') }}
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<input class="dropdown-item" type="submit" value="{{ _('Power on (WoL)') }}"
formaction="{{ url_for('action_wol') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Power off') }}"
formaction="{{ url_for('action_poweroff') }}" formmethod="post">
<input class="dropdown-item" type="submit" value="{{ _('Reboot') }}"
formaction="{{ url_for('action_reboot') }}" formmethod="post">
<input class="dropdown-item" type="submit" value="{{ _('Refresh') }}"
formaction="{{ url_for('action_refresh') }}" formmethod="post">
<input class="dropdown-item" type="submit" value="{{ _('Hardware') }}"
formaction="{{ url_for('action_hardware') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Software') }}"
formaction="{{ url_for('action_software') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Start session') }}"
formaction="{{ url_for('action_session') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Restore Image') }}"
formaction="{{ url_for('action_image_restore') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Partition & Format') }}"
formaction="{{ url_for('action_setup_show') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Client details') }}"
formaction="{{ url_for('action_client_info') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Add client') }}"
formaction="{{ url_for('action_client_add') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Create image') }}"
formaction="{{ url_for('action_image_create') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Set boot mode') }}"
formaction="{{ url_for('action_mode') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Add room') }}"
formaction="{{ url_for('action_room_add') }}" formmethod="get">
<input class="dropdown-item" type="submit" value="{{ _('Delete room') }}"
formaction="{{ url_for('action_room_delete') }}" formmethod="get">
</div>
</div>
{% block container %}
<form id="scopesForm">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>
{{ super() }}
</form>
{% endblock %}
{% block sidebar %}
{{ print_scopes_tree(scopes["scope"]) }}
{% endblock %}
{% block commands %}
<input class="btn btn-light" type="submit" value="{{ _('Power on (WoL)') }}"
form="scopesForm" formaction="{{ url_for('action_wol') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Power off') }}"
form="scopesForm" formaction="{{ url_for('action_poweroff') }}" formmethod="post">
<input class="btn btn-light" type="submit" value="{{ _('Reboot') }}"
form="scopesForm" formaction="{{ url_for('action_reboot') }}" formmethod="post">
<input class="btn btn-light" type="submit" value="{{ _('Refresh') }}"
form="scopesForm" formaction="{{ url_for('action_refresh') }}" formmethod="post">
<input class="btn btn-light" type="submit" value="{{ _('Hardware') }}"
form="scopesForm" formaction="{{ url_for('action_hardware') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Software') }}"
form="scopesForm" formaction="{{ url_for('action_software') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Start session') }}"
form="scopesForm" formaction="{{ url_for('action_session') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Restore Image') }}"
form="scopesForm" formaction="{{ url_for('action_image_restore') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Partition & Format') }}"
form="scopesForm" formaction="{{ url_for('action_setup_show') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Client details') }}"
form="scopesForm" formaction="{{ url_for('action_client_info') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Add client') }}"
form="scopesForm" formaction="{{ url_for('action_client_add') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Create image') }}"
form="scopesForm" formaction="{{ url_for('action_image_create') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Set boot mode') }}"
form="scopesForm" formaction="{{ url_for('action_mode') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Add room') }}"
form="scopesForm" formaction="{{ url_for('action_room_add') }}" formmethod="get">
<input class="btn btn-light" type="submit" value="{{ _('Delete room') }}"
form="scopesForm" formaction="{{ url_for('action_room_delete') }}" formmethod="get">
{% endblock %}

View File

@ -105,6 +105,30 @@ def parse_scopes_from_tree(tree, scope_type):
scopes += parse_scopes_from_tree(scope, scope_type)
return scopes
def add_state_and_ips(scope, clients):
if 'ip' in scope:
filtered_client = filter(lambda x: x['addr']==scope['ip'], clients)
client = next(filtered_client, False)
if client:
scope['state'] = client['state']
else:
scope['state'] = 'OFF'
scope['ip'] = [scope['ip']]
else:
scope['ip'] = []
for child in scope['scope']:
scope['ip'] += add_state_and_ips(child, clients)
return scope['ip']
def get_scopes():
r = g.server.get('/scopes')
scopes = r.json()
r = g.server.get('/clients')
clients = r.json()
add_state_and_ips(scopes, clients['clients'])
return scopes, clients
@login_manager.user_loader
def load_user(user_id):
if user_id == 1:
@ -167,26 +191,7 @@ def logout():
@app.route('/scopes/')
@login_required
def scopes():
def add_state_and_ips(scope, clients):
if 'ip' in scope:
filtered_client = filter(lambda x: x['addr']==scope['ip'], clients)
client = next(filtered_client, False)
if client:
scope['state'] = client['state']
else:
scope['state'] = 'OFF'
scope['ip'] = [scope['ip']]
else:
scope['ip'] = []
for child in scope['scope']:
scope['ip'] += add_state_and_ips(child, clients)
return scope['ip']
r = g.server.get('/scopes')
scopes = r.json()
r = g.server.get('/clients')
clients = r.json()
add_state_and_ips(scopes, clients['clients'])
scopes, clients = get_scopes()
return render_template('scopes.html', scopes=scopes, clients=clients)
@app.route('/action/poweroff', methods=['POST'])
@ -585,7 +590,8 @@ def action_mode():
return redirect(url_for("scopes"))
form.ok.render_kw = { 'formaction': url_for('action_mode') }
return render_template('actions/mode.html', form=form)
scopes, clients = get_scopes()
return render_template('actions/mode.html', form=form, scopes=scopes, clients=clients)
@app.route('/action/image/create', methods=['GET', 'POST'])