mirror of https://git.48k.eu/ogcp
Compare commits
58 Commits
Author | SHA1 | Date |
---|---|---|
|
4e0bb82f9f | |
|
c6adc0f29b | |
|
35269b31a7 | |
|
1cf6fbc49e | |
|
d9597e4e01 | |
|
b3b47ac0cb | |
|
4c21e370a3 | |
|
4616e3dcf5 | |
|
d840e4af37 | |
|
91465fc269 | |
|
c33fe3ca77 | |
|
9bb40d267f | |
|
00be07352d | |
|
b2aa0e3dbb | |
|
89cc9d3f9f | |
|
159f4c56a5 | |
|
ea9310f97a | |
|
dd4b7ad229 | |
|
655ffbc0bb | |
|
f75a72b1cf | |
|
05cba727e0 | |
|
fd8da5de26 | |
|
c7c28d6e92 | |
|
a58587dc80 | |
|
17644e584e | |
|
edd44da64c | |
|
f02c899e3e | |
|
a241ce1bcd | |
|
a353cbcaa1 | |
|
76fe1b775a | |
|
92ab31650c | |
|
9c7a687d56 | |
|
270089983a | |
|
66b663e051 | |
|
bcffdff135 | |
|
75cd6d9883 | |
|
6af4330016 | |
|
d6a896628f | |
|
6feffeab5d | |
|
6fecb9d34b | |
|
39371747db | |
|
c1d9018e21 | |
|
77a60b717a | |
|
340b7fde54 | |
|
ec209480ea | |
|
9bf161fc7a | |
|
19295f8158 | |
|
f03077edb7 | |
|
7296372e9c | |
|
053519beae | |
|
2ca2215ed6 | |
|
db2869088f | |
|
233156b19a | |
|
f85c61df99 | |
|
65d2d75ddb | |
|
696a81fd11 | |
|
6b33268b5c | |
|
a1b164b106 |
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
|
||||
# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
|
||||
# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the
|
||||
|
@ -73,7 +73,8 @@ class SetupForm(FlaskForm):
|
|||
ips = HiddenField()
|
||||
disk = SelectField(label='Disk', validate_choice=False)
|
||||
disk_type = SelectField(label=_l('Type'),
|
||||
choices=[('MSDOS', 'MBR'),
|
||||
choices=[('EMPTY', 'EMPTY'),
|
||||
('MSDOS', 'MBR'),
|
||||
('GPT', 'GPT')])
|
||||
partitions = FieldList(FormField(PartitionForm),
|
||||
min_entries=1,
|
||||
|
@ -172,6 +173,11 @@ class RunScriptForm(FlaskForm):
|
|||
arguments = StringField(label=_l('Arguments'))
|
||||
submit = SubmitField(label=_l('Submit'))
|
||||
|
||||
class RunCmdForm(FlaskForm):
|
||||
ips = HiddenField()
|
||||
command = StringField(label=_l('Command'))
|
||||
submit = SubmitField(label=_l('Submit'))
|
||||
|
||||
class ImportClientsForm(FlaskForm):
|
||||
server = HiddenField()
|
||||
room = SelectField(label=_l('Room'))
|
||||
|
@ -186,9 +192,15 @@ class BootModeForm(FlaskForm):
|
|||
|
||||
class OgliveForm(FlaskForm):
|
||||
ips = HiddenField()
|
||||
server = HiddenField()
|
||||
oglive = SelectField(label=_l('ogLive'))
|
||||
ok = SubmitField(label=_l('Submit'))
|
||||
|
||||
class SetRepoForm(FlaskForm):
|
||||
ips = HiddenField()
|
||||
repo = SelectField(label=_l('Repository'))
|
||||
ok = SubmitField(label=_l('Submit'))
|
||||
|
||||
class ImageCreateForm(FlaskForm):
|
||||
ip = HiddenField()
|
||||
os = SelectField(label=_l('Partition'), choices=[])
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
|
||||
# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
|
||||
# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (C) 2020-2021 Soleta Networks <info@soleta.eu>
|
||||
# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the
|
||||
|
@ -6,10 +6,22 @@
|
|||
# (at your option) any later version.
|
||||
|
||||
from ogcp import app
|
||||
from flask import session, flash
|
||||
from flask_babel import _
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
class ServerError(Exception):
|
||||
pass
|
||||
|
||||
def flash_once(message, category='message'):
|
||||
if '_flashes' not in session:
|
||||
session['_flashes'] = []
|
||||
|
||||
if (category, message) not in session['_flashes']:
|
||||
flash(message, category)
|
||||
|
||||
class OGServer:
|
||||
def __init__(self, name, ip, port, api_token):
|
||||
self.name = name
|
||||
|
@ -22,26 +34,57 @@ class OGServer:
|
|||
self.URL = f'http://{self.ip}:{self.port}'
|
||||
self.HEADERS = {'Authorization' : self.api_token}
|
||||
|
||||
def get(self, path, payload=None):
|
||||
def _log_http_status_code(self, res):
|
||||
if res.status_code == 400:
|
||||
err_msg = _('Invalid payload')
|
||||
elif res.status_code == 404:
|
||||
err_msg = _('Object not found')
|
||||
elif res.status_code == 405:
|
||||
err_msg = _('Method not allowed')
|
||||
elif res.status_code == 409:
|
||||
err_msg = _('Object already exists')
|
||||
elif res.status_code == 423:
|
||||
err_msg = _('Object in use')
|
||||
elif res.status_code == 501:
|
||||
err_msg = _('Cannot connect to database')
|
||||
elif res.status_code == 507:
|
||||
err_msg = _('Disk full')
|
||||
else:
|
||||
err_msg = _(f'Received status code {res.status_code}')
|
||||
|
||||
flash_once(err_msg, category='error')
|
||||
|
||||
def _request(self, method, path, payload, expected_status):
|
||||
try:
|
||||
r = requests.get(f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload)
|
||||
res = requests.request(
|
||||
method,
|
||||
f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload,
|
||||
)
|
||||
if res.status_code not in expected_status:
|
||||
self._log_http_status_code(res)
|
||||
raise ServerError
|
||||
return res
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
return None
|
||||
return r
|
||||
flash_once(_('Cannot connect to ogserver'), category='error')
|
||||
except requests.exceptions.Timeout:
|
||||
flash_once(_('Request to ogserver timed out'), category='error')
|
||||
except requests.exceptions.TooManyRedirects:
|
||||
flash_once(_('Too many redirects occurred while contacting ogserver'), category='error')
|
||||
except requests.exceptions.RequestException as e:
|
||||
flash_once(_('An error occurred while contacting ogserver: %(error)s', error=str(e)), category='error')
|
||||
raise ServerError
|
||||
|
||||
def get(self, path, payload=None):
|
||||
return self._request('GET', path, payload, expected_status={200})
|
||||
|
||||
def post(self, path, payload):
|
||||
r = requests.post(f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload)
|
||||
return r
|
||||
return self._request('POST', path, payload, expected_status={200, 202})
|
||||
|
||||
def delete(self, path, payload):
|
||||
r = requests.delete(f'{self.URL}{path}',
|
||||
headers=self.HEADERS,
|
||||
json=payload)
|
||||
return r
|
||||
return self._request('DELETE', path, payload, expected_status={200})
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
|
|
|
@ -3,6 +3,61 @@ const macs = new Map();
|
|||
const Interval = 1000;
|
||||
let updateTimeoutId = null;
|
||||
|
||||
const StorageGroup = Object.freeze({
|
||||
SHOW: 'show',
|
||||
CHECK: 'check',
|
||||
});
|
||||
|
||||
class ogStorage {
|
||||
static STORAGE_VERSION = Object.freeze(1);
|
||||
|
||||
static store(group, context, elemId, value) {
|
||||
const key = `${group}-${context}-${elemId}`;
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
static remove(group, context, elemId) {
|
||||
const key = `${group}-${context}-${elemId}`;
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
static hasKey(group, context, elemId) {
|
||||
const key = `${group}-${context}-${elemId}`;
|
||||
return localStorage.getItem(key) !== null;
|
||||
}
|
||||
|
||||
static deleteInvalidStorage(items, group, context) {
|
||||
if (localStorage.getItem('storageVersion') < ogStorage.STORAGE_VERSION) {
|
||||
localStorage.clear();
|
||||
localStorage.setItem('storageVersion', ogStorage.STORAGE_VERSION);
|
||||
return
|
||||
}
|
||||
|
||||
const prefix = `${group}-${context}`;
|
||||
const existingKeys = items.map(function() {
|
||||
return `${group}-${context}-${this.id}`;
|
||||
}).get();
|
||||
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
|
||||
if (!key.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
if (!existingKeys.includes(key)) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static storeCheckboxStatus(checkbox, context) {
|
||||
if (checkbox.checked)
|
||||
ogStorage.store(StorageGroup.CHECK, context, checkbox.id, "");
|
||||
else
|
||||
ogStorage.remove(StorageGroup.CHECK, context, checkbox.id);
|
||||
}
|
||||
}
|
||||
|
||||
async function show_client_mac(pill_id) {
|
||||
const pill = $('#' +pill_id);
|
||||
|
||||
|
@ -47,20 +102,13 @@ function showSelectedClient(client_checkbox) {
|
|||
}
|
||||
|
||||
function showSelectedClientsOnEvents() {
|
||||
const checkboxes = $('input:checkbox[form|="scopesForm"]');
|
||||
const checkboxes = $('#sidebar input:checkbox');
|
||||
|
||||
checkboxes.on('change show-client', function () {
|
||||
showSelectedClient(this);
|
||||
});
|
||||
}
|
||||
|
||||
function storeCheckboxStatus(checkbox, context) {
|
||||
if (checkbox.checked)
|
||||
localStorage.setItem(context + checkbox.id, "check");
|
||||
else
|
||||
localStorage.removeItem(context + checkbox.id);
|
||||
}
|
||||
|
||||
function findParentCheckboxes(element) {
|
||||
const $element = $(element);
|
||||
const parents = $element.parentsUntil('#scopes').not('ul');
|
||||
|
@ -92,6 +140,8 @@ function setParentStatus(checkboxes) {
|
|||
function configureCommandCheckboxes(context) {
|
||||
const checkboxes = $('input:checkbox[form="scopesForm"]');
|
||||
|
||||
ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context);
|
||||
|
||||
// Ensure the form fields are sent
|
||||
$('#scopesForm').on('submit', function() {
|
||||
checkboxes.each(function() {
|
||||
|
@ -111,7 +161,7 @@ function configureCommandCheckboxes(context) {
|
|||
|
||||
checkboxes.on('change', function () {
|
||||
const checked = this.checked;
|
||||
const childrenCheckboxes = $('input:checkbox[form|="scopesForm"]', this.parentNode);
|
||||
const childrenCheckboxes = $('input[type="checkbox"][form="scopesForm"]', this.parentNode);
|
||||
|
||||
// Uncheck all other checkboxes outside of the actual center branch
|
||||
if (checked) {
|
||||
|
@ -132,79 +182,44 @@ function configureCommandCheckboxes(context) {
|
|||
|
||||
checkboxes.each(function() {
|
||||
showSelectedClient(this);
|
||||
storeCheckboxStatus(this, context);
|
||||
ogStorage.storeCheckboxStatus(this, context);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function keepSelectedClients(context) {
|
||||
const checkboxes = $('input:checkbox[form|="scopesForm"]')
|
||||
const checkboxes = $('#sidebar input:checkbox')
|
||||
|
||||
checkboxes.on('change', function (event) {
|
||||
storeCheckboxStatus(this, context);
|
||||
ogStorage.storeCheckboxStatus(this, context);
|
||||
});
|
||||
|
||||
ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context);
|
||||
|
||||
checkboxes.each(function () {
|
||||
if (localStorage.getItem(context + this.id) == 'check') {
|
||||
if (ogStorage.hasKey(StorageGroup.CHECK, context, this.id)) {
|
||||
this.checked = true;
|
||||
$(this).trigger('show-client');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function keepImagesTreeState() {
|
||||
const images_tree = $('#servers .collapse')
|
||||
images_tree.on('hidden.bs.collapse', function (event) {
|
||||
function keepTreeState(selector, context) {
|
||||
const tree_items = $(selector + ' .collapse');
|
||||
|
||||
ogStorage.deleteInvalidStorage(tree_items, StorageGroup.SHOW, context);
|
||||
|
||||
tree_items.on('hidden.bs.collapse', function (event) {
|
||||
event.stopPropagation();
|
||||
localStorage.removeItem(this.id);
|
||||
ogStorage.remove(StorageGroup.SHOW, context, this.id)
|
||||
});
|
||||
images_tree.on('shown.bs.collapse', function (event) {
|
||||
tree_items.on('shown.bs.collapse', function (event) {
|
||||
event.stopPropagation();
|
||||
localStorage.setItem(this.id, 'show');
|
||||
ogStorage.store(StorageGroup.SHOW, context, this.id, "")
|
||||
});
|
||||
|
||||
images_tree.each(function () {
|
||||
if (localStorage.getItem(this.id) == 'show') {
|
||||
$(this).collapse('show');
|
||||
} else {
|
||||
$(this).siblings('a').addClass('collapsed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function keepReposTreeState() {
|
||||
const repos_tree = $('#repos-list .collapse')
|
||||
|
||||
repos_tree.on('hidden.bs.collapse', function (event) {
|
||||
event.stopPropagation();
|
||||
localStorage.removeItem(this.id);
|
||||
});
|
||||
repos_tree.on('shown.bs.collapse', function (event) {
|
||||
event.stopPropagation();
|
||||
localStorage.setItem(this.id, 'show');
|
||||
});
|
||||
|
||||
repos_tree.each(function () {
|
||||
if (localStorage.getItem(this.id) == 'show') {
|
||||
$(this).collapse('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function keepScopesTreeState() {
|
||||
const scopes_tree = $('#scopes .collapse')
|
||||
|
||||
scopes_tree.on('hidden.bs.collapse', function (event) {
|
||||
event.stopPropagation();
|
||||
localStorage.removeItem(this.id);
|
||||
});
|
||||
scopes_tree.on('shown.bs.collapse', function (event) {
|
||||
event.stopPropagation();
|
||||
localStorage.setItem(this.id, 'show');
|
||||
});
|
||||
|
||||
scopes_tree.each(function () {
|
||||
if (localStorage.getItem(this.id) == 'show') {
|
||||
tree_items.each(function () {
|
||||
if (ogStorage.hasKey(StorageGroup.SHOW, context, this.id)) {
|
||||
$(this).collapse('show');
|
||||
} else {
|
||||
$(this).siblings('a').addClass('collapsed');
|
||||
|
@ -337,56 +352,19 @@ function updateScopes(scopes) {
|
|||
return hasLiveChildren;
|
||||
}
|
||||
|
||||
function unfoldAll() {
|
||||
$('#scopes .collapse').collapse('show');
|
||||
}
|
||||
|
||||
function checkImageServer() {
|
||||
const images = $('input:checkbox[form|="imagesForm"][name!="image-server"]')
|
||||
|
||||
images.on('change', function() {
|
||||
const selectedServer = $('#' + $.escapeSelector(this.dataset.server));
|
||||
const serversSelector = 'input:checkbox[name|="image-server"]';
|
||||
const nonSelectedServers = $(serversSelector).not(selectedServer);
|
||||
|
||||
selectedServer.prop('checked', true);
|
||||
|
||||
nonSelectedServers.each(function() {
|
||||
$(this).prop('checked', false);
|
||||
const checkboxes = $('input:checkbox[data-server|="' + this.id + '"]');
|
||||
checkboxes.prop('checked', false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkRepoServer() {
|
||||
const repos = $('input:checkbox[form|="reposForm"][name!="repos-server"]')
|
||||
repos.on('change', function() {
|
||||
const selectedServer = $('#' + $.escapeSelector(this.dataset.server));
|
||||
const serversSelector = 'input:checkbox[name|="repos-server"]';
|
||||
const nonSelectedServers = $(serversSelector).not(selectedServer);
|
||||
|
||||
selectedServer.prop('checked', true);
|
||||
|
||||
nonSelectedServers.each(function() {
|
||||
$(this).prop('checked', false);
|
||||
const checkboxes = $('input:checkbox[data-server|="' + this.id + '"]');
|
||||
checkboxes.prop('checked', false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkFolderParent(context) {
|
||||
const folder = $('input:checkbox[form|="scopesForm"][name="folder"]')
|
||||
const folder = $('#sidebar input:checkbox[name="folder"]')
|
||||
folder.on('change', function() {
|
||||
const folder_parent = $('#' + $.escapeSelector(this.dataset.parentInput));
|
||||
folder_parent.prop('checked', this.checked);
|
||||
storeCheckboxStatus(folder_parent.get(0), context);
|
||||
ogStorage.storeCheckboxStatus(folder_parent.get(0), context);
|
||||
});
|
||||
}
|
||||
|
||||
function limitCheckboxes(context) {
|
||||
const checkboxes = $('input:checkbox[form|="scopesForm"]');
|
||||
const checkboxes = $('#sidebar input:checkbox');
|
||||
|
||||
ogStorage.deleteInvalidStorage(checkboxes, StorageGroup.CHECK, context);
|
||||
|
||||
checkboxes.on('change', function () {
|
||||
const currentCheckbox = $(this);
|
||||
|
@ -405,18 +383,18 @@ function limitCheckboxes(context) {
|
|||
}
|
||||
});
|
||||
|
||||
checkScopeServer();
|
||||
checkCheckbox('scope-server');
|
||||
|
||||
checkboxes.each(function() {
|
||||
storeCheckboxStatus(this, context);
|
||||
ogStorage.storeCheckboxStatus(this, context);
|
||||
showSelectedClient(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkScopeServer() {
|
||||
const servers = $('input:checkbox[form|="scopesForm"][name="scope-server"]');
|
||||
servers.each(function() {
|
||||
function checkCheckbox(inputName) {
|
||||
const checkboxes = $('#sidebar input:checkbox[name="' + inputName + '"]');
|
||||
checkboxes.each(function() {
|
||||
const checkbox = this;
|
||||
const checkboxChildren = $('input:checkbox', this.parentNode).not(this);
|
||||
if (checkboxChildren.length == 0) return;
|
||||
|
@ -425,3 +403,15 @@ function checkScopeServer() {
|
|||
checkbox.checked = checkedChildren.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function checkOnChange(inputName) {
|
||||
const checkboxes = $('#sidebar input:checkbox')
|
||||
|
||||
checkboxes.on('change', function (event) {
|
||||
checkCheckbox(inputName);
|
||||
});
|
||||
|
||||
checkboxes.each(function () {
|
||||
checkCheckbox(inputName)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
action=url_for('action_center_add'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
action=url_for('action_room_add'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
{{ wtf.quick_form(form,
|
||||
action=url_for('server_add_post'),
|
||||
method='post',
|
||||
button_map={'submit_btn':'primary'}) }}
|
||||
button_map={'submit_btn':'primary'},
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -38,10 +38,16 @@
|
|||
</div>
|
||||
|
||||
{% set show_part_images = True %}
|
||||
{% set show_free_size = True %}
|
||||
{% set readonly_disk_inspector = True %}
|
||||
{% include 'disk_inspector.html' %}
|
||||
|
||||
<br>
|
||||
|
||||
{% include 'cache_inspector.html' %}
|
||||
|
||||
<br>
|
||||
|
||||
{% include 'efi_inspector.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
{% extends 'scopes.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
{% import "macros.html" as macros %}
|
||||
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block nav_client %} active {% endblock %}
|
||||
{% block nav_client_search %} active {% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h2 class="mx-5 subhead-heading">
|
||||
{{ _('Search clients') }}
|
||||
</h2>
|
||||
|
||||
<div class="mx-5 my-3">
|
||||
<label for="name-filter">{{ _('Name') }}</label>
|
||||
<input type="text" id="name-filter" class="form-control mb-2">
|
||||
|
||||
<label for="ip-filter">{{ _('IP Address') }}</label>
|
||||
<input type="text" id="ip-filter" class="form-control mb-2">
|
||||
|
||||
<button id="search-button" class="btn btn-primary">{{ _('Search') }}</button>
|
||||
</div>
|
||||
|
||||
<div id="clients-container" class="mx-5 mt-3"></div>
|
||||
|
||||
<script>
|
||||
let clients = {{ clients|tojson|safe }};
|
||||
|
||||
function renderClients(data) {
|
||||
const container = document.getElementById('clients-container');
|
||||
container.innerHTML = '';
|
||||
|
||||
let currentPath = null;
|
||||
let ul = null;
|
||||
|
||||
data.forEach(client => {
|
||||
if (client.tree_path !== currentPath) {
|
||||
currentPath = client.tree_path;
|
||||
|
||||
const pathElement = document.createElement('p');
|
||||
pathElement.innerHTML = `<strong>${currentPath}</strong>:`;
|
||||
container.appendChild(pathElement);
|
||||
|
||||
ul = document.createElement('ul');
|
||||
container.appendChild(ul);
|
||||
}
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `${client.name} (IP: ${client.ip.join(', ')})`;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function filterClients() {
|
||||
const nameFilter = document.getElementById('name-filter').value.toLowerCase();
|
||||
const ipFilter = document.getElementById('ip-filter').value;
|
||||
|
||||
// If both filters are empty, don't display any clients
|
||||
if (!nameFilter && !ipFilter) {
|
||||
document.getElementById('clients-container').innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = clients.filter(client => {
|
||||
const matchesName = nameFilter ? client.name.toLowerCase().includes(nameFilter) : true;
|
||||
const matchesIP = ipFilter ? client.ip.some(ip => ip.includes(ipFilter)) : true;
|
||||
return matchesName && matchesIP;
|
||||
});
|
||||
|
||||
renderClients(filtered);
|
||||
}
|
||||
|
||||
filterClients();
|
||||
document.getElementById('search-button').addEventListener('click', filterClients);
|
||||
|
||||
// Search on Enter key press
|
||||
document.querySelectorAll('#name-filter, #ip-filter').forEach(input => {
|
||||
input.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
filterClients();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
|
@ -45,7 +45,7 @@
|
|||
action=url_for('action_center_delete'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
action=url_for('action_client_delete'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
action=url_for('action_image_delete'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% extends 'repos.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block nav_repos %} active{% endblock %}
|
||||
|
@ -9,7 +11,7 @@
|
|||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Delete repo')}}</h2>
|
||||
|
||||
<form class="form mx-5" method="POST">
|
||||
<form class="form m-5" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ form.server() }}
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
action=url_for('action_room_delete'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
action=url_for('server_delete_post'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -45,6 +45,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'danger'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
action=url_for('action_hardware'),
|
||||
method='post',
|
||||
button_map={'refresh': 'primary'},
|
||||
extra_classes='m-2')}}
|
||||
extra_classes='m-5')}}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead class="thead-dark">
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
{% extends 'images.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Update image')}} {{ form.name.data }}</h2>
|
||||
|
||||
<form class="form mx-5" method="POST" action="{{ url_for('action_image_config') }}">
|
||||
<form class="form m-5" method="POST" action="{{ url_for('action_image_config') }}">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ form.image_id() }}
|
||||
|
|
|
@ -10,14 +10,15 @@
|
|||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Create a partition image')}}</h2>
|
||||
|
||||
<h2 class="mx-5">
|
||||
{{ _('Selected client') }}: {{ form.ip.data }}
|
||||
</h1>
|
||||
{{ macros.cmd_selected_clients(selected_clients) }}
|
||||
|
||||
{% set partition_field_id = 'os' %}
|
||||
{% include 'partition_warning.html' %}
|
||||
|
||||
{{ wtf.quick_form(form,
|
||||
action=url_for('action_image_create'),
|
||||
method='post',
|
||||
button_map={'create': 'primary'},
|
||||
extra_classes='mx-5') }}
|
||||
extra_classes='m-5') }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
{% extends 'images.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Image details')}}</h2>
|
||||
|
||||
<div class="container mx-5">
|
||||
<div class="container m-5">
|
||||
<form class="form" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
{{ macros.cmd_selected_clients(selected_clients) }}
|
||||
|
||||
{% set partition_field_id = 'partition' %}
|
||||
{% include 'partition_warning.html' %}
|
||||
|
||||
{{ wtf.quick_form(form,
|
||||
action=url_for('action_image_restore'),
|
||||
method='post',
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
{{ macros.cmd_selected_clients(selected_clients) }}
|
||||
|
||||
{% set partition_field_id = 'os' %}
|
||||
{% include 'partition_warning.html' %}
|
||||
|
||||
<form class="form mx-5" method="POST" action="{{ url_for('action_image_update') }}">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
action=url_for('action_clients_import_post'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,6 +10,30 @@
|
|||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Client log')}}</h2>
|
||||
|
||||
<pre>{{ log }}</pre>
|
||||
<style>
|
||||
/* Prevent overflow */
|
||||
pre {
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid d-flex flex-column" style="height: 90vh;">
|
||||
<div class="border p-3 overflow-auto flex-grow-1" id="logContainer">
|
||||
<pre>{{ log }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function scrollToBottom() {
|
||||
const logContainer = document.getElementById('logContainer');
|
||||
logContainer.scrollTo({ top: logContainer.scrollHeight, behavior: 'instant' });
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", scrollToBottom);
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% extends 'images.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
{% extends 'lives.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2 class="mx-5 subhead-heading">
|
||||
{{ _('Set default ogLive') }}
|
||||
</h2>
|
||||
|
||||
<p class="mx-5">{{ _('Default live: %(default_live)s', default_live=default_live) }}</p>
|
||||
|
||||
{{ wtf.quick_form(form,
|
||||
action=url_for('action_live_default'),
|
||||
method='post',
|
||||
button_map={'ok': 'primary'},
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,46 @@
|
|||
{% extends 'commands.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
{% import "macros.html" as macros %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2 class="mx-5 subhead-heading">
|
||||
{{ _('Partition scheme mismatch') }}
|
||||
</h2>
|
||||
|
||||
{{ macros.cmd_selected_clients(selected_clients) }}
|
||||
</br>
|
||||
|
||||
<div class="container mx-5">
|
||||
<b>{{ _('Cannot proceed with this command, selected clients have non-uniform or valid partition scheme') }}</b>
|
||||
</div>
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead class="text-center">
|
||||
<tr>
|
||||
<th style="min-width: 15em;">{{ _('Partitions') }}</th>
|
||||
<th>{{ _('Clients') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for idx in range(part_data | length) %}
|
||||
<tr>
|
||||
<td>
|
||||
{% for disk_id, part_id, part_type, fs_type, part_size in part_data.get_partition_setup(idx) %}
|
||||
<div>Part {{ part_id }} | {{ fs_type }} | {{ (part_size / 1024) | int}} MiB</div>
|
||||
{% else %}
|
||||
{{ _('Empty') }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
{% for ip in part_data.get_clients(idx) %}<div class="card d-inline-block" style="padding: 5px; margin: 3px;">{{ ip }}</div>{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
|
@ -21,7 +21,7 @@
|
|||
action=url_for('action_poweroff'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
action=url_for('action_reboot'),
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% extends 'repos.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block nav_repos %} active{% endblock %}
|
||||
|
@ -9,7 +11,7 @@
|
|||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Repo details')}}</h2>
|
||||
|
||||
<form class="form mx-5" method="POST">
|
||||
<form class="form m-5" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ form.server() }}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
{% extends 'commands.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
{% import "macros.html" as macros %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block nav_setup %} active{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% set ip_list = form.ips.data.split(' ') %}
|
||||
{% set ip_count = ip_list | length %}
|
||||
<h2 class="mx-5 subhead-heading">
|
||||
{{ _('Changing repository of %(ip_count)d computer(s)', ip_count=ip_count) }}
|
||||
</h2>
|
||||
|
||||
{{ macros.cmd_selected_clients(selected_clients) }}
|
||||
|
||||
{% if repos_set|length > 1 %}
|
||||
<div class="mx-5 form-group">
|
||||
<p>Selected clients have different ogLive</p>
|
||||
|
||||
<table class="table table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Repository</th>
|
||||
<th>Clients</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-left">
|
||||
{% for repo, clients in repos_set.items() %}
|
||||
<tr>
|
||||
<th>{{repo}}</th>
|
||||
<td>
|
||||
{% for ip in clients %}<div class="card d-inline-block" style="padding: 5px; margin: 3px;">{{ ip }}</div>{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ wtf.quick_form(form,
|
||||
action=url_for('action_repo_set'),
|
||||
method='post',
|
||||
button_map={'ok': 'primary'},
|
||||
extra_classes="m-5") }}
|
||||
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="{{ url_for('static', filename='AdminLTE/plugins/jquery/jquery.min.js') }}"></script>
|
||||
<script>
|
||||
var reposSet = {{ repos_set|tojson|safe }};
|
||||
// Update pill data
|
||||
$('.badge-pill').each(function(index) {
|
||||
for (const repo in reposSet) {
|
||||
for (const clientName of reposSet[repo]) {
|
||||
if ($(this).html().includes(clientName)) {
|
||||
$(this).html($(this).html() + '<br>' + repo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
|
@ -1,5 +1,7 @@
|
|||
{% extends 'repos.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block nav_repos %} active{% endblock %}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% extends 'repos.html' %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% set sidebar_state = 'disabled' %}
|
||||
{% set btn_back = true %}
|
||||
|
||||
{% block nav_repos %} active{% endblock %}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
{{ wtf.quick_form(form,
|
||||
method='post',
|
||||
button_map={'submit': 'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Update server')}}</h2>
|
||||
|
||||
<form class="form mx-5" method="POST">
|
||||
<form class="form m-5" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ form.server_addr() }}
|
||||
|
|
|
@ -16,13 +16,15 @@
|
|||
|
||||
{{ macros.cmd_selected_clients(selected_clients) }}
|
||||
|
||||
<p>
|
||||
{% if os_groups|length > 0 %}
|
||||
|
||||
<p class="mx-5">
|
||||
{% if os_groups|length > 1 %}
|
||||
The selected clients have different installed OS:
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<form class="form-inline" method="POST" id="sessionForm">
|
||||
<form class="form-inline m-5" method="POST" id="sessionForm">
|
||||
<table class="table table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
|
@ -51,4 +53,10 @@ The selected clients have different installed OS:
|
|||
</button>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<div class="card text-center p-3">
|
||||
<b>{{ _('No bootable OS') }}</b>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
action=url_for('action_software'),
|
||||
method='post',
|
||||
button_map={'view': 'primary', 'update': 'primary'},
|
||||
extra_classes="mx-5")}}
|
||||
extra_classes="m-5")}}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
action=url_for('user_delete_post'),
|
||||
method='post',
|
||||
button_map={'submit_btn':'primary'},
|
||||
extra_classes="mx-5") }}
|
||||
extra_classes="m-5") }}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
<li class="nav-item {% block nav_servers %}{% endblock %}">
|
||||
<a class="nav-link" href="{{ url_for('manage_servers') }}">{{ _('Servers') }}</a>
|
||||
</li>
|
||||
<li class="nav-item {% block nav_lives %}{% endblock %}">
|
||||
<a class="nav-link" href="{{ url_for('manage_lives') }}">{{ _('Lives') }}</a>
|
||||
</li>
|
||||
<li class="nav-item {% block nav_users %}{% endblock %}">
|
||||
<a class="nav-link" href="{{ url_for('users') }}">{{ _('Users') }}</a>
|
||||
</li>
|
||||
|
@ -111,7 +114,7 @@
|
|||
<!-- ChartJS -->
|
||||
<script src="{{ url_for('static', filename='AdminLTE/plugins/chart.js/Chart.min.js') }}"></script>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/ogcp.js') }}?v=16"></script>
|
||||
<script src="{{ url_for('static', filename='js/ogcp.js') }}?v=25"></script>
|
||||
|
||||
<script>
|
||||
// error messages
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</ul>
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('Disk size') }}
|
||||
{{ _('Cache size') }}
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('used') }} (%)
|
||||
|
@ -100,9 +100,9 @@
|
|||
}
|
||||
|
||||
function updateChart(ip) {
|
||||
var totalCache = toGiB(storageData[ip].total, 3);
|
||||
var totalCache = toGiB(storageData[ip].used + storageData[ip].free, 3);
|
||||
var usedCache = toGiB(storageData[ip].used, 3);
|
||||
var freeCache = toGiB(storageData[ip].total - storageData[ip].used, 3)
|
||||
var freeCache = toGiB(storageData[ip].free, 3);
|
||||
|
||||
cacheChart.data.datasets[0].data = [
|
||||
usedCache,
|
||||
|
@ -134,9 +134,7 @@
|
|||
$('.badge-pill').each(function(index) {
|
||||
for (var ip in storageData) {
|
||||
if ($(this).html().includes(ip)) {
|
||||
var totalCache = storageData[ip].total;
|
||||
var usedCache = storageData[ip].used;
|
||||
var freeCache = toGiB(totalCache - usedCache, 1)
|
||||
var freeCache = toGiB(storageData[ip].free, 1)
|
||||
$(this).html($(this).html() + '<br>free: ' + freeCache + ' GiB');
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
<div class="container mx-5">
|
||||
|
||||
{% include 'client_status_leyend.html' %}
|
||||
|
||||
{% for server_id, server_data in servers_data.items() %}
|
||||
<div class="accordion card" id="shellAccordion">
|
||||
<div class="card-header" id="heading_1">
|
||||
|
@ -22,8 +24,10 @@
|
|||
<table class="table table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('IP') }}</th>
|
||||
<th>{{ _('Link speed') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th>{{ _('Details') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -31,9 +35,10 @@
|
|||
<tbody data-target="cache-fieldset" id="cacheTable" class="text-left">
|
||||
{% for client_data in server_data.clients %}
|
||||
<tr data-toggle="fieldset-entry">
|
||||
<td>{{ client_data.name }}</td>
|
||||
<td>{{ client_data.addr }}</td>
|
||||
<td>
|
||||
{% if client_data.speed is not none %}
|
||||
{% if client_data.speed is not none and client_data.speed > 0 %}
|
||||
{% if client_data.speed >= 1000 %}
|
||||
{{ (client_data.speed / 1000) | int }} Gb/s
|
||||
{% else %}
|
||||
|
@ -43,6 +48,31 @@
|
|||
{{ _('Not available') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<i class="nav-icon fa-circle
|
||||
{% if client_data.state == 'OPG' and client_data.last_cmd.result == 'failure' %}
|
||||
fas text-warning fa-times-circle
|
||||
{% elif client_data.state == 'OPG' %}
|
||||
fas text-warning
|
||||
{% elif client_data.state == 'LNX' %}
|
||||
fas text-linux
|
||||
{% elif client_data.state == 'LNX' %}
|
||||
fas fa-user-circle text-linux
|
||||
{% elif client_data.state == 'WIN' %}
|
||||
fas text-windows
|
||||
{% elif client_data.state == 'WIN' %}
|
||||
fas fa-user-circle text-windows
|
||||
{% elif client_data.state == 'BSY' %}
|
||||
fas text-danger
|
||||
{% elif client_data.state == 'VDI' %}
|
||||
fas text-success
|
||||
{% elif client_data.state == 'WOL_SENT' %}
|
||||
fas text-wol
|
||||
{% else %}
|
||||
far
|
||||
{% endif %}
|
||||
"></i>
|
||||
</td>
|
||||
<td><a href="{{ url_for('action_client_info', client_ip = client_data.addr) }}">{{ _('View details') }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul id="clients-color-legend" class="d-flex flex-wrap justify-content-center nav ogcp-nav nav-pills">
|
||||
<li class="nav-item"><i class="nav-icon far fa-circle"></i> {{_('Shutdown')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-wol"></i> {{_('WoL sent')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-warning"></i> ogLive </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-danger"></i> {{_('Busy')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-linux"></i> Linux </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-user-circle text-linux"></i> {{_('Linux session')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-windows"></i> Windows </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-user-circle text-windows"></i> {{_('Windows session')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-success"></i> VDI </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -40,6 +40,8 @@
|
|||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<input class="btn btn-light dropdown-item{% block nav_setup_set_bootmode %}{% endblock %}" type="submit" value="{{ _('Set boot mode') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_mode') }}" formmethod="get">
|
||||
<input class="btn btn-light dropdown-item{% block nav_setup_set_repo %}{% endblock %}" type="submit" value="{{ _('Set repository') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_repo_set') }}" formmethod="get">
|
||||
<input class="btn btn-light dropdown-item{% block nav_setup_set_oglive %}{% endblock %}" type="submit" value="{{ _('Set ogLive') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_oglive') }}" formmethod="get">
|
||||
<input class="btn btn-light dropdown-item{% block nav_setup_setup %}{% endblock %}" type="submit" value="{{ _('Partition & Format') }}"
|
||||
|
@ -95,11 +97,13 @@
|
|||
|
||||
<div class="dropdown btn">
|
||||
<button class="btn btn-secondary btn-light dropdown-toggle{% block nav_script %}{% endblock %}" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-expanded="false">
|
||||
{{ _('Script') }}
|
||||
{{ _('Run') }}
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<input class="btn btn-light dropdown-item{% block nav_run_script %}{% endblock %}" type="submit" value="{{ _('Run') }}"
|
||||
<input class="btn btn-light dropdown-item{% block nav_run_script %}{% endblock %}" type="submit" value="{{ _('Script') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_run_script') }}" formmethod="get">
|
||||
<input class="btn btn-light dropdown-item{% block nav_run_cmd %}{% endblock %}" type="submit" value="{{ _('Command') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_run_cmd') }}" formmethod="get">
|
||||
<input class="btn btn-light dropdown-item{% block nav_display_output %}{% endblock %}" type="submit" value="{{ _('Display output') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_script_display_output') }}" formmethod="get">
|
||||
</div>
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
<div class="row">
|
||||
|
||||
<!-- disk stats -->
|
||||
<div class="col-{{ colsize }}">
|
||||
<div class="col-6">
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('Disk stats') }}
|
||||
|
@ -129,7 +129,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Memory stats -->
|
||||
<div class="col-{{ colsize }}">
|
||||
<div class="col-6">
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('Memory') }}
|
||||
|
@ -165,7 +165,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Swap stats -->
|
||||
<div class="col-{{ colsize }}">
|
||||
<div class="col-6">
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('Swap') }}
|
||||
|
@ -205,7 +205,7 @@
|
|||
</div>
|
||||
|
||||
<!-- latest images -->
|
||||
<div class="col-{{ colsize }}">
|
||||
<div class="col-6">
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('Latest images') }}
|
||||
|
@ -224,7 +224,7 @@
|
|||
</div>
|
||||
|
||||
<!-- ogLives -->
|
||||
<div class="col-{{ colsize }}">
|
||||
<div class="col-6">
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('ogLive images') }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% if selected_disk is defined and setup_data is defined %}
|
||||
|
||||
<form class="form-inline mx-5" method="POST" id="setupForm">
|
||||
<form class="form-inline m-5" method="POST" id="setupForm">
|
||||
{{ disk_form.hidden_tag() }}
|
||||
{{ disk_form.ips() }}
|
||||
|
||||
|
@ -37,6 +37,9 @@
|
|||
<th>{{ _('Type') }}</th>
|
||||
<th>{{ _('Filesystem') }}</th>
|
||||
<th>{{ _('Size') }} (MiB)</th>
|
||||
{% if show_free_size is defined %}
|
||||
<th>{{ _('Free') }} (MiB)</th>
|
||||
{% endif %}
|
||||
{% if show_part_images is defined %}
|
||||
<th>{{ _('Image') }}</th>
|
||||
{% endif %}
|
||||
|
@ -64,12 +67,13 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if readonly_disk_inspector is defined %}
|
||||
{{ partition.size(class_="form-control", oninput="handleEdit(this)", readonly="readonly") }}
|
||||
{% else %}
|
||||
{% if not readonly_disk_inspector is defined %}
|
||||
{{ partition.size(class_="form-control", oninput="handleEdit(this)") }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if show_free_size is defined %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if show_part_images is defined %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
|
@ -176,7 +180,12 @@
|
|||
let freeSpace = diskSize;
|
||||
let partNum = 1;
|
||||
$('#partitionsTable tr').each(function() {
|
||||
{% if readonly_disk_inspector is defined %}
|
||||
let partitionSize = parseInt($(this).find('td').eq(3).text().trim());
|
||||
{% else %}
|
||||
let partitionSize = parseInt($(this).find('td').eq(3).find('input').val().trim());
|
||||
{% endif %}
|
||||
|
||||
if (isNaN(partitionSize)) {
|
||||
partitionSize = 0;
|
||||
}
|
||||
|
@ -291,12 +300,23 @@
|
|||
|
||||
let row = partitionsTable.find('tr').eq(i - 1);
|
||||
|
||||
row.find('td').eq(0).text(p.partition);
|
||||
row.find('td').eq(1).find('select').val(p.code);
|
||||
row.find('td').eq(2).find('select').val(p.filesystem);
|
||||
row.find('td').eq(3).find('input').val(Math.floor(p.size / 1024));
|
||||
var idx = 0;
|
||||
row.find('td').eq(idx++).text(p.partition);
|
||||
row.find('td').eq(idx++).find('select').val(p.code);
|
||||
row.find('td').eq(idx++).find('select').val(p.filesystem);
|
||||
|
||||
{% if readonly_disk_inspector is defined %}
|
||||
row.find('td').eq(idx++).text(Math.floor(p.size / 1024));
|
||||
{% else %}
|
||||
row.find('td').eq(idx++).find('input').val(Math.floor(p.size / 1024));
|
||||
{% endif %}
|
||||
|
||||
{% if show_free_size is defined %}
|
||||
row.find('td').eq(idx++).text(Math.floor(p.free_size / (1024 * 1024)));
|
||||
{% endif %}
|
||||
|
||||
{% if show_part_images is defined %}
|
||||
row.find('td').eq(4).text(p.image);
|
||||
row.find('td').eq(idx++).text(p.image);
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
{% if efi_data is defined %}
|
||||
|
||||
{% if efi_data['entries']|length > 0 %}
|
||||
|
||||
<div class="form-group mx-5">
|
||||
<label class="control-label">{{ _('Boot entries') }}</label>
|
||||
<table class="table table-bordered">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>{{ _('Order') }}</th>
|
||||
<th>{{ _('Active') }}</th>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Description') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry_data in efi_data['entries'] %}
|
||||
<tr>
|
||||
<td>
|
||||
<p>
|
||||
{% if entry_data['order'] is defined %}
|
||||
{{ entry_data['order'] }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
{% if entry_data['active'] == 1 %}
|
||||
{{ _('yes') }}
|
||||
{% else %}
|
||||
{{ _('no') }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ entry_data['name'] }}</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>{{ entry_data['description'] }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="card text-center p-3">
|
||||
<b>{{ _('No EFI contents') }}</b>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
|
@ -14,8 +14,9 @@
|
|||
// in the scope
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (document.readyState === 'complete') {
|
||||
keepImagesTreeState();
|
||||
checkImageServer();
|
||||
keepTreeState('#servers', 'images');
|
||||
keepSelectedClients('images');
|
||||
checkOnChange('image-server');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -28,6 +29,7 @@
|
|||
{% set parent_id = "repos-" ~ loop.index0 %}
|
||||
<li class="nav-item">
|
||||
<input class="form-check-input" type="checkbox" form="imagesForm"
|
||||
{% if sidebar_state %}style="filter: grayscale(100%);" onclick="return false;"{% endif %}
|
||||
id="{{ server_str }}" value="{{ server_str }}"
|
||||
onclick="return false;" name="image-server" hidden/>
|
||||
<a class="nav-link" data-toggle="collapse" data-target="#repos-{{ loop.index0 }}">
|
||||
|
@ -43,9 +45,11 @@
|
|||
{% for image in repo_data["images"] %}
|
||||
<li id="{{ image["name"] }}_{{ image["id"] }}" class="nav-item">
|
||||
<input class="form-check-input" type="checkbox" form="imagesForm"
|
||||
{% if sidebar_state %}style="filter: grayscale(100%);" onclick="return false;"{% endif %}
|
||||
data-server="{{ server_str }}" value="{{ image["id"] }}"
|
||||
{% if image.get("selected", False) %}checked{% endif %}
|
||||
name="{{ image["name"] }}_{{ image["id"] }}" />
|
||||
name="{{ image["name"] }}_{{ image["id"] }}"
|
||||
id="image{{ image["id"] }}"/>
|
||||
{{ image["name"] }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
@ -68,7 +72,7 @@
|
|||
form="imagesForm" formaction="{{ url_for('action_image_delete') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('IMAGE', 'UPDATE') %}
|
||||
<input class="btn btn-light" type="submit" value="{{ _('Update image') }}"
|
||||
<input class="btn btn-light" type="submit" value="{{ _('Edit image') }}"
|
||||
form="imagesForm" formaction="{{ url_for('action_image_config') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block nav_lives %}active{% endblock %}
|
||||
|
||||
{% block container %}
|
||||
<form id="livesForm">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
</form>
|
||||
{{ super() }}
|
||||
</form>
|
||||
|
||||
<script>
|
||||
// Launch the javascript on document ready, so all the global functions exists
|
||||
// in the scope
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (document.readyState === 'complete') {
|
||||
keepTreeState('#servers', 'lives');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<ul id="servers" class="nav ogcp-nav flex-column nav-pills">
|
||||
{% for lives_data in oglive_list %}
|
||||
<li class="nav-item">
|
||||
{% set server_ip_port = lives_data["server"].ip ~ ":" ~ lives_data["server"].port %}
|
||||
<input id="{{ server_ip_port }}" class="form-check-input" type="checkbox" form="livesForm"
|
||||
{% if sidebar_state %}style="filter: grayscale(100%);" onclick="return false;"{% endif %}
|
||||
value="{{ server_ip_port }}" name="server"
|
||||
{% if loop.index == 1 %}checked{% endif %}></input>
|
||||
<a class="nav-link" data-toggle="collapse" href="#server{{loop.index}}">
|
||||
{{ lives_data["server"]["name"] }}
|
||||
</a>
|
||||
<ul class="nav flex-column collapse" id="server{{loop.index}}">
|
||||
{% for oglive in lives_data["json"]["oglive"] %}
|
||||
<li class="nav-item">
|
||||
<a>{{ oglive["directory"] }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block commands %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<input class="btn btn-light {% block nav_live_default %}{% endblock %}" type="submit" value="{{ _('Set default') }}"
|
||||
form="livesForm" formaction="{{ url_for('action_live_default') }}" formmethod="get">
|
||||
{% if btn_back %}
|
||||
<button class="btn btn-danger ml-3" type="button" id="backButton" onclick="history.back()">
|
||||
{{ _("Back") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -10,7 +10,7 @@
|
|||
if (document.readyState === 'complete') {
|
||||
showSelectedClientsOnEvents();
|
||||
updateScopeState();
|
||||
keepScopesTreeState();
|
||||
keepTreeState('#scopes', 'scopes');
|
||||
let context = {{ selection_mode | tojson | safe }};
|
||||
{% if selection_mode == 'commands' %}
|
||||
configureCommandCheckboxes(context);
|
||||
|
@ -92,22 +92,10 @@
|
|||
{% endmacro %}
|
||||
|
||||
{% macro selected_clients() -%}
|
||||
<h2 class="mx-5 subhead-heading">{{_('Selected clients')}}</h2>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul id="clients-color-legend" class="d-flex flex-wrap justify-content-center nav ogcp-nav nav-pills">
|
||||
<li class="nav-item"><i class="nav-icon far fa-circle"></i> {{_('Shutdown')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-wol"></i> {{_('WoL sent')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-warning"></i> ogLive </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-danger"></i> {{_('Busy')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-linux"></i> Linux </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-user-circle text-linux"></i> {{_('Linux session')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-windows"></i> Windows </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-user-circle text-windows"></i> {{_('Windows session')}} </li>
|
||||
<li class="nav-item"><i class="nav-icon fas fa-circle text-success"></i> VDI </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="mx-5 subhead-heading">{{_('Selected clients')}}</h3>
|
||||
|
||||
{% include 'client_status_leyend.html' %}
|
||||
|
||||
<div id="selected-clients" class="d-flex flex-wrap justify-content-center"></div>
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<div class="mx-5" id="partition-warning" style="display:none; color:red; font-weight: bold;">
|
||||
{{ _('Warning: You have selected a partition from a disk other than 1. This will be considered for storage only.') }}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function checkPartition() {
|
||||
var partitionSelect = document.getElementById('{{ partition_field_id }}');
|
||||
var selectedValue = partitionSelect.value;
|
||||
var warningDiv = document.getElementById('partition-warning');
|
||||
|
||||
// Extract the disk_id
|
||||
var diskId = parseInt(selectedValue.split(' ')[0]);
|
||||
|
||||
if (diskId !== 1) {
|
||||
warningDiv.style.display = 'block';
|
||||
} else {
|
||||
warningDiv.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var partitionSelect = document.getElementById('{{ partition_field_id }}');
|
||||
if (partitionSelect) {
|
||||
partitionSelect.addEventListener('change', checkPartition);
|
||||
checkPartition();
|
||||
} else {
|
||||
console.error("Element with ID '{{ partition_field_id }}' not found.");
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -18,6 +18,7 @@
|
|||
{% set repos_list = repos["json"]["repositories"] %}
|
||||
<li class="nav-item">
|
||||
<input id="{{ server_ip_port }}"class="form-check-input" type="checkbox" form="reposForm"
|
||||
{% if sidebar_state %}style="filter: grayscale(100%);" onclick="return false;"{% endif %}
|
||||
value="{{ server_ip_port }}" name="repos-server" />
|
||||
<a class="nav-link {% if not repos_list %}disabled{% endif %}" href="#server{{loop.index}}"
|
||||
{% if repos_list %}data-toggle="collapse"{% endif %}>
|
||||
|
@ -27,6 +28,8 @@
|
|||
{% for r in repos_list %}
|
||||
<li class="nav-item">
|
||||
<input class="form-check-input" type="checkbox" form="reposForm"
|
||||
{% if sidebar_state %}style="filter: grayscale(100%);" onclick="return false;"{% endif %}
|
||||
id="repo{{ r["id"] }}"
|
||||
data-server="{{server_ip_port}}"
|
||||
value="{{ r["id"] }}"
|
||||
name="{{ r["name"]~_~r["id"] }}" />
|
||||
|
@ -42,8 +45,9 @@
|
|||
// in the scope
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (document.readyState === 'complete') {
|
||||
keepReposTreeState()
|
||||
checkRepoServer()
|
||||
keepTreeState('#repos-list', 'repos');
|
||||
keepSelectedClients('repos');
|
||||
checkOnChange('repos-server');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -62,7 +66,7 @@
|
|||
form="reposForm" formaction="{{ url_for('action_repo_delete') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('REPOSITORY', 'UPDATE') %}
|
||||
<input class="btn btn-light {% block nav_repo_update %}{% endblock %}" type="submit" value="{{ _('Update repo') }}"
|
||||
<input class="btn btn-light {% block nav_repo_update %}{% endblock %}" type="submit" value="{{ _('Edit repo') }}"
|
||||
form="reposForm" formaction="{{ url_for('action_repo_update') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<input class="btn btn-light dropdown-item {% block nav_client_add %}{% endblock %}" type="submit" value="{{ _('Add client') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_client_add') }}" formmethod="get">
|
||||
{% endif %}
|
||||
<input class="btn btn-light dropdown-item {% block nav_client_update %}{% endblock %}" type="submit" value="{{ _('Update client') }}"
|
||||
<input class="btn btn-light dropdown-item {% block nav_client_update %}{% endblock %}" type="submit" value="{{ _('Edit client') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_client_update') }}" formmethod="get">
|
||||
{% if current_user.get_permission('CLIENT', 'UPDATE') %}
|
||||
<input class="btn btn-light dropdown-item {% block nav_client_move %}{% endblock %}" type="submit" value="{{ _('Move client') }}"
|
||||
|
@ -42,6 +42,8 @@
|
|||
<input class="btn btn-light dropdown-item {% block nav_client_delete %}{% endblock %}" type="submit" value="{{ _('Delete client') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_client_delete') }}" formmethod="get">
|
||||
{% endif %}
|
||||
<input class="btn btn-light dropdown-item {% block nav_client_search %}{% endblock %}" type="submit" value="{{ _('Search client') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_client_search') }}" formmethod="get">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -56,7 +58,7 @@
|
|||
form="scopesForm" formaction="{{ url_for('action_room_add') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('ROOM', 'UPDATE') %}
|
||||
<input class="btn btn-light dropdown-item {% block nav_room_update %}{% endblock %}" type="submit" value="{{ _('Update room') }}"
|
||||
<input class="btn btn-light dropdown-item {% block nav_room_update %}{% endblock %}" type="submit" value="{{ _('Edit room') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_room_update') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('ROOM', 'DELETE') %}
|
||||
|
@ -78,7 +80,7 @@
|
|||
form="scopesForm" formaction="{{ url_for('action_center_add') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('CENTER', 'UPDATE') %}
|
||||
<input class="btn btn-light dropdown-item {% block nav_center_update %}{% endblock %}" type="submit" value="{{ _('Update center') }}"
|
||||
<input class="btn btn-light dropdown-item {% block nav_center_update %}{% endblock %}" type="submit" value="{{ _('Edit center') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_center_update') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('CENTER', 'DELETE') %}
|
||||
|
@ -101,7 +103,7 @@
|
|||
form="scopesForm" formaction="{{ url_for('action_folder_add') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('FOLDER', 'UPDATE') %}
|
||||
<input class="btn btn-light dropdown-item {% block nav_folder_update %}{% endblock %}" type="submit" value="{{ _('Update folder') }}"
|
||||
<input class="btn btn-light dropdown-item {% block nav_folder_update %}{% endblock %}" type="submit" value="{{ _('Edit folder') }}"
|
||||
form="scopesForm" formaction="{{ url_for('action_folder_update') }}" formmethod="get">
|
||||
{% endif %}
|
||||
{% if current_user.get_permission('FOLDER', 'DELETE') %}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
{% block commands %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<input class="btn btn-light {% block nav_server_update %}{% endblock %}" type="submit" value="{{ _('Update server') }}"
|
||||
<input class="btn btn-light {% block nav_server_update %}{% endblock %}" type="submit" value="{{ _('Edit server') }}"
|
||||
form="serversForm" formaction="{{ url_for('server_update_get') }}" formmethod="get">
|
||||
{% if btn_back %}
|
||||
<button class="btn btn-danger ml-3" type="button" id="backButton" onclick="history.back()">
|
||||
|
|
1472
ogcp/views.py
1472
ogcp/views.py
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue