summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel García Moreno <danigm@soleta.eu>2021-06-08 11:33:30 +0200
committerDaniel García Moreno <danigm@soleta.eu>2021-06-08 11:46:40 +0200
commit22dcea19ff74871b7200bfc7f976836f436c5342 (patch)
tree7c8808fafb9ac27db8e06491da5cb4d352d7e121
parentdb29b306aaeebcc385d9a0874f4386010a5c6a98 (diff)
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.
-rw-r--r--ogcp/static/css/soleta.css8
-rw-r--r--ogcp/templates/actions/mode.html3
-rw-r--r--ogcp/templates/base.html56
-rw-r--r--ogcp/templates/nav.html16
-rw-r--r--ogcp/templates/scopes.html88
-rw-r--r--ogcp/views.py48
6 files changed, 131 insertions, 88 deletions
diff --git a/ogcp/static/css/soleta.css b/ogcp/static/css/soleta.css
index d748f5b..ca49897 100644
--- a/ogcp/static/css/soleta.css
+++ b/ogcp/static/css/soleta.css
@@ -6,3 +6,11 @@ html, body {
width: 100%;
height: 100% !important;
}
+
+#sidebar .list-group-item {
+ background-color: transparent;
+}
+
+.toast {
+ margin: 10px;
+}
diff --git a/ogcp/templates/actions/mode.html b/ogcp/templates/actions/mode.html
index 0fd3266..3adce8f 100644
--- a/ogcp/templates/actions/mode.html
+++ b/ogcp/templates/actions/mode.html
@@ -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 %}
-
diff --git a/ogcp/templates/base.html b/ogcp/templates/base.html
index 5fda82a..dca6205 100644
--- a/ogcp/templates/base.html
+++ b/ogcp/templates/base.html
@@ -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>
diff --git a/ogcp/templates/nav.html b/ogcp/templates/nav.html
index 39b0ac2..f2ca3b5 100644
--- a/ogcp/templates/nav.html
+++ b/ogcp/templates/nav.html
@@ -6,13 +6,25 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
+ {% if current_user.is_authenticated %}
<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>
+ <a class="nav-link" href="{{ url_for('index') }}">{{ _('Dashboard') }}<span class="sr-only">(current)</span></a>
</li>
- {% if current_user.is_authenticated %}
<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>
diff --git a/ogcp/templates/scopes.html b/ogcp/templates/scopes.html
index 3cb84fd..00f6883 100644
--- a/ogcp/templates/scopes.html
+++ b/ogcp/templates/scopes.html
@@ -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 %}
+{% block container %}
+ <form id="scopesForm">
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
+ </form>
+ {{ super() }}
+</form>
+{% endblock %}
-<form>
- <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
+{% block sidebar %}
{{ print_scopes_tree(scopes["scope"]) }}
+{% endblock %}
- <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>
-</form>
-
+{% 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 %}
diff --git a/ogcp/views.py b/ogcp/views.py
index 9865e0f..5496daf 100644
--- a/ogcp/views.py
+++ b/ogcp/views.py
@@ -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'])