feat(admin): added customization dashboard
parent
6209d74514
commit
d52a8c1f05
|
@ -12,7 +12,7 @@ COPY admin/docker/requirements.pip3 /requirements.pip3
|
|||
RUN pip3 install --no-cache-dir -r requirements.pip3
|
||||
RUN apk del .build_deps
|
||||
|
||||
RUN apk add --no-cache curl py3-yaml yarn libpq openssl
|
||||
RUN apk add --no-cache curl py3-yaml yarn libpq openssl py3-pillow
|
||||
|
||||
RUN wget -O /usr/lib/python3.8/site-packages/diceware/wordlists/wordlist_cat_ascii.txt https://raw.githubusercontent.com/1ma/diceware-cat/master/cat-wordlist-ascii.txt
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ psycopg2==2.8.6
|
|||
python-keycloak==0.26.1
|
||||
minio==7.0.3
|
||||
urllib3==1.26.6
|
||||
schema==0.7.5
|
||||
|
||||
# Unused yet
|
||||
#flask-oidc==1.4.0
|
|
@ -0,0 +1,88 @@
|
|||
import logging as log
|
||||
import traceback
|
||||
from pprint import pprint
|
||||
|
||||
import os, shutil
|
||||
|
||||
from requests import get, post
|
||||
|
||||
from schema import And, Optional, Schema, SchemaError, Use
|
||||
import yaml
|
||||
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
from admin import app
|
||||
|
||||
|
||||
class Dashboard:
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.custom_menu = os.path.join(app.root_path, "../custom/menu/custom.yaml")
|
||||
|
||||
def _update_custom_menu(self, custom_menu_part):
|
||||
with open(self.custom_menu) as yml:
|
||||
menu = yaml.load(yml, Loader=yaml.FullLoader)
|
||||
menu = {**menu, **custom_menu_part}
|
||||
with open(self.custom_menu, "w") as yml:
|
||||
yml.write(yaml.dump(menu, default_flow_style=False))
|
||||
return True
|
||||
|
||||
def update_colours(self, colours):
|
||||
schema_template = Schema(
|
||||
{
|
||||
"background": And(Use(str)),
|
||||
"primary": And(Use(str)),
|
||||
"secondary": And(Use(str)),
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
schema_template.validate(colours)
|
||||
except SchemaError:
|
||||
return False
|
||||
|
||||
self._update_custom_menu({"colours": colours})
|
||||
return self.apply_updates()
|
||||
|
||||
def update_menu(self, menu):
|
||||
for menu_item in menu.keys():
|
||||
for mustexist_key in ["href", "icon", "name", "shortname"]:
|
||||
if mustexist_key not in menu[menu_item].keys():
|
||||
return False
|
||||
self._update_custom_menu({"apps_external": menu})
|
||||
return self.apply_updates()
|
||||
|
||||
def update_logo(self, logo):
|
||||
img = Image.open(logo.stream)
|
||||
img.save(os.path.join(app.root_path, "../custom/img/logo.png"))
|
||||
img.save(
|
||||
os.path.join(
|
||||
app.root_path,
|
||||
"../custom/system/keycloak/themes/liiibrelite/login/resources/img/logo.png",
|
||||
)
|
||||
)
|
||||
return self.apply_updates()
|
||||
|
||||
def update_background(self, background):
|
||||
img = Image.open(background.stream)
|
||||
img.save(os.path.join(app.root_path, "../custom/img/background.png"))
|
||||
img.save(
|
||||
os.path.join(
|
||||
app.root_path,
|
||||
"../custom/system/keycloak/themes/liiibrelite/login/resources/img/loginBG.png",
|
||||
)
|
||||
)
|
||||
img.save(
|
||||
os.path.join(
|
||||
app.root_path,
|
||||
"../custom/system/keycloak/themes/liiibrelite/login/resources/img/loginBG2.png",
|
||||
)
|
||||
)
|
||||
return self.apply_updates()
|
||||
|
||||
def apply_updates(self):
|
||||
# Keycloak
|
||||
# Api
|
||||
return True
|
|
@ -100,10 +100,6 @@ table.dataTable tr.shown td.details-control > button > i:before {
|
|||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar_logo {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
background: #3b3e47;
|
||||
|
@ -127,9 +123,17 @@ table.dataTable tr.shown td.details-control > button > i:before {
|
|||
|
||||
.nav_title {
|
||||
background: #3b3e47;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.nav_menu {
|
||||
padding: 10px;
|
||||
background: white;
|
||||
height: 75px;
|
||||
max-height: 75px;
|
||||
}
|
||||
|
||||
.nav_menu_logo {
|
||||
margin-top: 5px;
|
||||
width: 100;
|
||||
height: 45px;
|
||||
}
|
|
@ -0,0 +1,443 @@
|
|||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('.icon-dropdown').select2({
|
||||
width: "100%",
|
||||
templateSelection: formatText,
|
||||
templateResult: formatText
|
||||
});
|
||||
|
||||
function formatText (icon) {
|
||||
return $('<span><i class="fa ' + $(icon.element).data('icon') + '"></i> ' + icon.text + '</span>');
|
||||
};
|
||||
|
||||
$('#colorpicker-background').colorpicker().on('changeColor', function (e) {
|
||||
$('#colorpicker-background-input').val(e.color.toHex());
|
||||
$('.right_col').css('background-color', e.color.toHex());
|
||||
});
|
||||
|
||||
$('#colorpicker-background-input').on('keyDown', function (e) {
|
||||
$('#colorpicker-background').val(e.color.toHex());
|
||||
})
|
||||
|
||||
$('#colorpicker-primary').colorpicker().on('changeColor', function (e) {
|
||||
$('#colorpicker-primary-input').val(e.color.toHex());
|
||||
$('.left_col').css('background-color', e.color.toHex());
|
||||
$('body').css('background-color', e.color.toHex());
|
||||
});
|
||||
|
||||
$('#colorpicker-secondary').colorpicker().on('changeColor', function (e) {
|
||||
$('#colorpicker-secondary-input').val(e.color.toHex());
|
||||
$('.left_col').css('color', e.color.toHex());
|
||||
});
|
||||
|
||||
init_logo_cropper()
|
||||
init_background_cropper()
|
||||
|
||||
$('#save-colors').click(function () {
|
||||
console.log({
|
||||
'background': $('#colorpicker-background-input').val(),
|
||||
'primary': $('#colorpicker-primary-input').val(),
|
||||
'secondary': $('#colorpicker-secondary-input').val()
|
||||
})
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url:"/api/dashboard/colours",
|
||||
data: JSON.stringify({
|
||||
'background': $('#colorpicker-background-input').val(),
|
||||
'primary': $('#colorpicker-primary-input').val(),
|
||||
'secondary': $('#colorpicker-secondary-input').val()
|
||||
}),
|
||||
success: function(data)
|
||||
{
|
||||
$('#colorpicker-background').attr('data-container', data.colours.background);
|
||||
$('#colorpicker-background-input').val(data.colours.background);
|
||||
$('#colorpicker-primary').attr('data-container', data.colours.primary);
|
||||
$('#colorpicker-primary-input').val(data.colours.primary);
|
||||
$('#colorpicker-secondary').attr('data-container', data.colours.secondary);
|
||||
$('#colorpicker-secondary-input').val(data.colours.secondary);
|
||||
},
|
||||
error: function(data)
|
||||
{
|
||||
console.log('ERROR!')
|
||||
console.log(data)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
$('#save-menu').click(function () {
|
||||
ids = $('[id^="apps_external-"]')
|
||||
var menu_options = {};
|
||||
ids.each(function( index ) {
|
||||
// console.log(ids[index].id)
|
||||
if(!(ids[index].id.split('-')[1] in menu_options)){
|
||||
menu_options[ids[index].id.split('-')[1]]={}
|
||||
}
|
||||
menu_options[ids[index].id.split('-')[1]][ids[index].id.split('-')[2]]=$('#'+ids[index].id).val()
|
||||
})
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url:"/api/dashboard/menu",
|
||||
data: JSON.stringify(menu_options),
|
||||
success: function(data)
|
||||
{
|
||||
// $('#colorpicker-background').attr('data-container', data.colours.toHex());
|
||||
// $('#colorpicker-background-input').val(data.colours.toHex());
|
||||
// $('#colorpicker-primary').attr('data-container', data.colours.toHex());
|
||||
// $('#colorpicker-primary-input').val(data.colours.toHex());
|
||||
// $('#colorpicker-secondary').attr('data-container', data.colours.toHex());
|
||||
// $('#colorpicker-secondary-input').val(data.colours.toHex());
|
||||
},
|
||||
error: function(data)
|
||||
{
|
||||
console.log('ERROR!')
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
/* CROPPER */
|
||||
|
||||
|
||||
function init_logo_cropper() {
|
||||
|
||||
|
||||
if (typeof ($.fn.cropper) === 'undefined') { return; }
|
||||
console.log('init_logo_cropper');
|
||||
|
||||
var $image = $('#image_logo');
|
||||
var $dataX = $('#dataX');
|
||||
var $dataY = $('#dataY');
|
||||
var $dataHeight = $('#dataHeight');
|
||||
var $dataWidth = $('#dataWidth');
|
||||
var $dataRotate = $('#dataRotate');
|
||||
var $dataScaleX = $('#dataScaleX');
|
||||
var $dataScaleY = $('#dataScaleY');
|
||||
var cropWidth = 80;
|
||||
var cropHeight = 45;
|
||||
var aspectRatio = cropWidth / cropHeight;
|
||||
var options = {
|
||||
aspectRatio: aspectRatio,
|
||||
preview: '.img-preview-logo',
|
||||
crop: function (e) {
|
||||
$dataX.val(Math.round(e.x));
|
||||
$dataY.val(Math.round(e.y));
|
||||
$dataHeight.val(Math.round(e.height));
|
||||
$dataWidth.val(Math.round(e.width));
|
||||
$dataRotate.val(e.rotate);
|
||||
$dataScaleX.val(e.scaleX);
|
||||
$dataScaleY.val(e.scaleY);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Tooltip
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
|
||||
// Cropper
|
||||
$image.on({
|
||||
'build.cropper': function (e) {
|
||||
console.log(e.type);
|
||||
},
|
||||
'built.cropper': function (e) {
|
||||
console.log(e.type);
|
||||
},
|
||||
'cropstart.cropper': function (e) {
|
||||
console.log(e.type, e.action);
|
||||
},
|
||||
'cropmove.cropper': function (e) {
|
||||
console.log(e.type, e.action);
|
||||
},
|
||||
'cropend.cropper': function (e) {
|
||||
console.log(e.type, e.action);
|
||||
},
|
||||
'crop.cropper': function (e) {
|
||||
console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);
|
||||
},
|
||||
'zoom.cropper': function (e) {
|
||||
console.log(e.type, e.ratio);
|
||||
}
|
||||
}).cropper(options);
|
||||
|
||||
$('#save-logo-crop').click(function () {
|
||||
$image.data('cropper').getCroppedCanvas({ width: cropWidth, height: cropHeight }).toBlob(function (blob) {
|
||||
var uri = URL.createObjectURL(blob);
|
||||
var img = new Image();
|
||||
|
||||
img.src = uri;
|
||||
$('.nav_menu_logo').attr('src', img.src)
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('croppedImage', blob);
|
||||
$.ajax('/api/dashboard/logo', {
|
||||
method: "PUT",
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function () {
|
||||
// Update logo image
|
||||
$('.nav_menu_logo').attr('src', img.src)
|
||||
console.log('Upload success');
|
||||
},
|
||||
error: function () {
|
||||
console.log('Upload error');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
// Methods
|
||||
$('.docs-buttons-logo').on('click', '[data-method]', function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data();
|
||||
var $target;
|
||||
var result;
|
||||
|
||||
if ($this.prop('disabled') || $this.hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($image.data('cropper') && data.method) {
|
||||
data = $.extend({}, data); // Clone a new one
|
||||
|
||||
if (typeof data.target !== 'undefined') {
|
||||
$target = $(data.target);
|
||||
|
||||
if (typeof data.option === 'undefined') {
|
||||
try {
|
||||
data.option = JSON.parse($target.val());
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = $image.cropper(data.method, data.option, data.secondOption);
|
||||
|
||||
switch (data.method) {
|
||||
case 'scaleX':
|
||||
case 'scaleY':
|
||||
$(this).data('option', -data.option);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($.isPlainObject(result) && $target) {
|
||||
try {
|
||||
$target.val(JSON.stringify(result));
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Import image
|
||||
var $inputImage = $('#inputImage');
|
||||
var URL = window.URL || window.webkitURL;
|
||||
var blobURL;
|
||||
|
||||
if (URL) {
|
||||
$inputImage.change(function () {
|
||||
var files = this.files;
|
||||
var file;
|
||||
|
||||
if (!$image.data('cropper')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (files && files.length) {
|
||||
file = files[0];
|
||||
|
||||
if (/^image\/\w+$/.test(file.type)) {
|
||||
blobURL = URL.createObjectURL(file);
|
||||
$image.one('built.cropper', function () {
|
||||
|
||||
// Revoke when load complete
|
||||
URL.revokeObjectURL(blobURL);
|
||||
}).cropper('reset').cropper('replace', blobURL);
|
||||
$inputImage.val('');
|
||||
} else {
|
||||
window.alert('Please choose an image file.');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$inputImage.prop('disabled', true).parent().addClass('disabled');
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
/* CROPPER --- end */
|
||||
|
||||
function init_background_cropper() {
|
||||
|
||||
|
||||
if (typeof ($.fn.cropper) === 'undefined') { return; }
|
||||
console.log('init_background_cropper');
|
||||
|
||||
var $image = $('#image_background');
|
||||
var $dataX = $('#dataX');
|
||||
var $dataY = $('#dataY');
|
||||
var $dataHeight = $('#dataHeight');
|
||||
var $dataWidth = $('#dataWidth');
|
||||
var $dataRotate = $('#dataRotate');
|
||||
var $dataScaleX = $('#dataScaleX');
|
||||
var $dataScaleY = $('#dataScaleY');
|
||||
var cropWidth = 1920;
|
||||
var cropHeight = 1080;
|
||||
var aspectRatio = cropWidth / cropHeight;
|
||||
var options = {
|
||||
aspectRatio: aspectRatio,
|
||||
preview: '.img-preview-background',
|
||||
crop: function (e) {
|
||||
$dataX.val(Math.round(e.x));
|
||||
$dataY.val(Math.round(e.y));
|
||||
$dataHeight.val(Math.round(e.height));
|
||||
$dataWidth.val(Math.round(e.width));
|
||||
$dataRotate.val(e.rotate);
|
||||
$dataScaleX.val(e.scaleX);
|
||||
$dataScaleY.val(e.scaleY);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Tooltip
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
|
||||
// Cropper
|
||||
$image.on({
|
||||
'build.cropper': function (e) {
|
||||
console.log(e.type);
|
||||
},
|
||||
'built.cropper': function (e) {
|
||||
console.log(e.type);
|
||||
},
|
||||
'cropstart.cropper': function (e) {
|
||||
console.log(e.type, e.action);
|
||||
},
|
||||
'cropmove.cropper': function (e) {
|
||||
console.log(e.type, e.action);
|
||||
},
|
||||
'cropend.cropper': function (e) {
|
||||
console.log(e.type, e.action);
|
||||
},
|
||||
'crop.cropper': function (e) {
|
||||
console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);
|
||||
},
|
||||
'zoom.cropper': function (e) {
|
||||
console.log(e.type, e.ratio);
|
||||
}
|
||||
}).cropper(options);
|
||||
|
||||
$('#save-background-crop').click(function () {
|
||||
$image.data('cropper').getCroppedCanvas({ width: cropWidth, height: cropHeight }).toBlob(function (blob) {
|
||||
var uri = URL.createObjectURL(blob);
|
||||
var img = new Image();
|
||||
|
||||
img.src = uri;
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('croppedImage', blob);
|
||||
$.ajax('/api/dashboard/background', {
|
||||
method: "PUT",
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function () {
|
||||
// Update background image
|
||||
console.log('Upload success');
|
||||
},
|
||||
error: function () {
|
||||
console.log('Upload error');
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
// Methods
|
||||
$('.docs-buttons-background').on('click', '[data-method]', function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data();
|
||||
var $target;
|
||||
var result;
|
||||
|
||||
if ($this.prop('disabled') || $this.hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($image.data('cropper') && data.method) {
|
||||
data = $.extend({}, data); // Clone a new one
|
||||
|
||||
if (typeof data.target !== 'undefined') {
|
||||
$target = $(data.target);
|
||||
|
||||
if (typeof data.option === 'undefined') {
|
||||
try {
|
||||
data.option = JSON.parse($target.val());
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = $image.cropper(data.method, data.option, data.secondOption);
|
||||
|
||||
switch (data.method) {
|
||||
case 'scaleX':
|
||||
case 'scaleY':
|
||||
$(this).data('option', -data.option);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($.isPlainObject(result) && $target) {
|
||||
try {
|
||||
$target.val(JSON.stringify(result));
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Import image
|
||||
var $inputImage = $('#inputImage');
|
||||
var URL = window.URL || window.webkitURL;
|
||||
var blobURL;
|
||||
|
||||
if (URL) {
|
||||
$inputImage.change(function () {
|
||||
var files = this.files;
|
||||
var file;
|
||||
|
||||
if (!$image.data('cropper')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (files && files.length) {
|
||||
file = files[0];
|
||||
|
||||
if (/^image\/\w+$/.test(file.type)) {
|
||||
blobURL = URL.createObjectURL(file);
|
||||
$image.one('built.cropper', function () {
|
||||
|
||||
// Revoke when load complete
|
||||
URL.revokeObjectURL(blobURL);
|
||||
}).cropper('reset').cropper('replace', blobURL);
|
||||
$inputImage.val('');
|
||||
} else {
|
||||
window.alert('Please choose an image file.');
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$inputImage.prop('disabled', true).parent().addClass('disabled');
|
||||
}
|
||||
|
||||
|
||||
};
|
|
@ -94,7 +94,7 @@
|
|||
<script src="/vendors/select2/dist/js/select2.full.min.js"></script>
|
||||
<!-- SocketIO -->
|
||||
<script src="/node_modules/socket.io/client-dist/socket.io.min.js"></script>
|
||||
<script src="/static/js/status_socket.js"></script>
|
||||
<!-- <script src="/static/js/status_socket.js"></script> -->
|
||||
|
||||
<!-- isard initializers -->
|
||||
<script src="/static/dd.js"></script>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<div class="nav toggle">
|
||||
<a id="menu_toggle"><i class="fa fa-bars"></i></a>
|
||||
</div>
|
||||
<img src="/custom/img/logo.png" class="nav_menu_logo" alt="dd">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="">
|
||||
<a href="javascript:" class="user-profile dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
<!-- extend base layout -->
|
||||
{% extends "base.html" %}
|
||||
{% block css %}
|
||||
<!-- Ion.RangeSlider -->
|
||||
<link href="/vendors/normalize-css/normalize.css" rel="stylesheet">
|
||||
<!-- Switchery -->
|
||||
<link href="/vendors/switchery/dist/switchery.min.css" rel="stylesheet">
|
||||
<!-- Bootstrap Colorpicker -->
|
||||
<link href="/vendors/mjolnic-bootstrap-colorpicker/dist/css/bootstrap-colorpicker.min.css" rel="stylesheet">
|
||||
<link href="/vendors/cropper/dist/cropper.min.css" rel="stylesheet">
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% set icons = [ 'fa-500px', 'fa-address-book', 'fa-address-book-o', 'fa-address-card', 'fa-address-card-o', 'fa-adjust', 'fa-adn',
|
||||
'fa-align-center', 'fa-align-justify', 'fa-align-left', 'fa-align-right', 'fa-amazon', 'fa-ambulance', 'fa-american-sign-language-interpreting',
|
||||
'fa-anchor', 'fa-android', 'fa-angellist', 'fa-angle-double-down', 'fa-angle-double-left', 'fa-angle-double-right', 'fa-angle-double-up',
|
||||
'fa-angle-down', 'fa-angle-left', 'fa-angle-right', 'fa-angle-up', 'fa-apple', 'fa-archive', 'fa-area-chart', 'fa-arrow-circle-down',
|
||||
'fa-arrow-circle-left', 'fa-arrow-circle-o-down', 'fa-arrow-circle-o-left', 'fa-arrow-circle-o-right', 'fa-arrow-circle-o-up', 'fa-arrow-circle-right',
|
||||
'fa-arrow-circle-up', 'fa-arrow-down', 'fa-arrow-left', 'fa-arrow-right', 'fa-arrow-up', 'fa-arrows', 'fa-arrows-alt', 'fa-arrows-h',
|
||||
'fa-arrows-v', 'fa-asl-interpreting', 'fa-assistive-listening-systems', 'fa-asterisk', 'fa-at', 'fa-audio-description', 'fa-automobile',
|
||||
'fa-backward', 'fa-balance-scale', 'fa-ban', 'fa-bandcamp', 'fa-bank', 'fa-bar-chart', 'fa-bar-chart-o', 'fa-barcode', 'fa-bars', 'fa-bath',
|
||||
'fa-bathtub', 'fa-battery', 'fa-battery-0', 'fa-battery-1', 'fa-battery-2', 'fa-battery-3', 'fa-battery-4', 'fa-battery-empty',
|
||||
'fa-battery-full', 'fa-battery-half', 'fa-battery-quarter', 'fa-battery-three-quarters', 'fa-bed', 'fa-beer', 'fa-behance', 'fa-behance-square', 'fa-bell', 'fa-bell-o', 'fa-bell-slash', 'fa-bell-slash-o', 'fa-bicycle', 'fa-binoculars', 'fa-birthday-cake', 'fa-bitbucket', 'fa-bitbucket-square', 'fa-bitcoin', 'fa-black-tie', 'fa-blind', 'fa-bluetooth', 'fa-bold', 'fa-bolt', 'fa-bomb', 'fa-book', 'fa-bookmark', 'fa-bookmark-o', 'fa-braille', 'fa-briefcase', 'fa-btc', 'fa-bug', 'fa-building', 'fa-building-o', 'fa-bullhorn', 'fa-bullseye', 'fa-bus', 'fa-buysellads', 'fa-cab', 'fa-calculator', 'fa-calendar', 'fa-calendar-check-o', 'fa-calendar-minus-o', 'fa-calendar-o', 'fa-calendar-plus-o', 'fa-calendar-times-o', 'fa-camera', 'fa-camera-retro',
|
||||
'fa-car', 'fa-caret-down', 'fa-caret-left', 'fa-caret-right', 'fa-caret-square-o-down', 'fa-caret-square-o-left', 'fa-caret-square-o-right',
|
||||
'fa-caret-square-o-up', 'fa-caret-up', 'fa-cart-arrow-down', 'fa-cart-plus', 'fa-cc', 'fa-cc-amex', 'fa-cc-diners-club', 'fa-cc-discover',
|
||||
'fa-cc-jcb', 'fa-cc-mastercard', 'fa-cc-paypal', 'fa-cc-stripe', 'fa-cc-visa', 'fa-certificate', 'fa-chain', 'fa-chain-broken', 'fa-check',
|
||||
'fa-check-circle', 'fa-check-circle-o', 'fa-check-square', 'fa-check-square-o', 'fa-chevron-circle-down', 'fa-chevron-circle-left',
|
||||
'fa-chevron-circle-right', 'fa-chevron-circle-up', 'fa-chevron-down', 'fa-chevron-left', 'fa-chevron-right', 'fa-chevron-up', 'fa-child',
|
||||
'fa-chrome', 'fa-circle', 'fa-circle-o', 'fa-circle-o-notch', 'fa-circle-thin', 'fa-clipboard', 'fa-clock-o', 'fa-clone', 'fa-close',
|
||||
'fa-cloud', 'fa-cloud-download', 'fa-cloud-upload', 'fa-cny', 'fa-code', 'fa-code-fork', 'fa-codepen', 'fa-codiepie', 'fa-coffee', 'fa-cog',
|
||||
'fa-cogs', 'fa-columns', 'fa-comment', 'fa-comment-o', 'fa-commenting', 'fa-commenting-o', 'fa-comments', 'fa-comments-o', 'fa-compass',
|
||||
'fa-compress', 'fa-connectdevelop', 'fa-contao', 'fa-copy', 'fa-copyright', 'fa-creative-commons', 'fa-credit-card', 'fa-credit-card-alt',
|
||||
'fa-crop', 'fa-crosshairs', 'fa-css3', 'fa-cube', 'fa-cubes', 'fa-cut', 'fa-cutlery', 'fa-dashboard', 'fa-dashcube', 'fa-database', 'fa-deaf', 'fa-deafness', 'fa-dedent', 'fa-delicious', 'fa-desktop', 'fa-deviantart', 'fa-diamond', 'fa-digg', 'fa-dollar', 'fa-dot-circle-o', 'fa-download', 'fa-dribbble', 'fa-drivers-license', 'fa-drivers-license-o', 'fa-dropbox', 'fa-drupal', 'fa-edge', 'fa-edit', 'fa-eercast', 'fa-eject', 'fa-ellipsis-h', 'fa-ellipsis-v', 'fa-empire', 'fa-envelope', 'fa-envelope-o', 'fa-envelope-open',
|
||||
'fa-envelope-open-o', 'fa-envelope-square', 'fa-envira', 'fa-eraser', 'fa-etsy', 'fa-eur', 'fa-euro', 'fa-exchange', 'fa-exclamation',
|
||||
'fa-exclamation-circle', 'fa-exclamation-triangle', 'fa-expand', 'fa-expeditedssl', 'fa-external-link', 'fa-external-link-square', 'fa-eye',
|
||||
'fa-eye-slash', 'fa-eyedropper', 'fa-fa', 'fa-facebook', 'fa-facebook-f', 'fa-facebook-official', 'fa-facebook-square', 'fa-fast-backward',
|
||||
'fa-fast-forward', 'fa-fax', 'fa-feed', 'fa-female', 'fa-fighter-jet', 'fa-file', 'fa-file-archive-o', 'fa-file-audio-o', 'fa-file-code-o',
|
||||
'fa-file-excel-o', 'fa-file-image-o', 'fa-file-movie-o', 'fa-file-o', 'fa-file-pdf-o', 'fa-file-photo-o', 'fa-file-picture-o',
|
||||
'fa-file-powerpoint-o', 'fa-file-sound-o', 'fa-file-text', 'fa-file-text-o', 'fa-file-video-o', 'fa-file-word-o', 'fa-file-zip-o',
|
||||
'fa-files-o', 'fa-film', 'fa-filter', 'fa-fire', 'fa-fire-extinguisher', 'fa-firefox', 'fa-first-order', 'fa-flag', 'fa-flag-checkered',
|
||||
'fa-flag-o', 'fa-flash', 'fa-flask', 'fa-flickr', 'fa-floppy-o', 'fa-folder', 'fa-folder-o', 'fa-folder-open', 'fa-folder-open-o', 'fa-font',
|
||||
'fa-font-awesome', 'fa-fonticons', 'fa-fort-awesome', 'fa-forumbee', 'fa-forward', 'fa-foursquare', 'fa-free-code-camp', 'fa-frown-o',
|
||||
'fa-futbol-o', 'fa-gamepad', 'fa-gavel', 'fa-gbp', 'fa-ge', 'fa-gear', 'fa-gears', 'fa-genderless', 'fa-get-pocket', 'fa-gg', 'fa-gg-circle',
|
||||
'fa-gift', 'fa-git', 'fa-git-square', 'fa-github', 'fa-github-alt', 'fa-github-square', 'fa-gitlab', 'fa-gittip', 'fa-glass', 'fa-glide',
|
||||
'fa-glide-g', 'fa-globe', 'fa-google', 'fa-google-plus', 'fa-google-plus-circle', 'fa-google-plus-official', 'fa-google-plus-square',
|
||||
'fa-google-wallet', 'fa-graduation-cap', 'fa-gratipay', 'fa-grav', 'fa-group', 'fa-h-square', 'fa-hacker-news', 'fa-hand-grab-o',
|
||||
'fa-hand-lizard-o', 'fa-hand-o-down', 'fa-hand-o-left', 'fa-hand-o-right', 'fa-hand-o-up', 'fa-hand-paper-o', 'fa-hand-peace-o',
|
||||
'fa-hand-pointer-o', 'fa-hand-rock-o', 'fa-hand-scissors-o', 'fa-hand-spock-o', 'fa-hand-stop-o', 'fa-handshake-o', 'fa-hard-of-hearing',
|
||||
'fa-hashtag', 'fa-hdd-o', 'fa-header', 'fa-headphones', 'fa-heart', 'fa-heart-o', 'fa-heartbeat', 'fa-history', 'fa-home', 'fa-hospital-o',
|
||||
'fa-hotel', 'fa-hourglass', 'fa-hourglass-1', 'fa-hourglass-2', 'fa-hourglass-3', 'fa-hourglass-end', 'fa-hourglass-half', 'fa-hourglass-o',
|
||||
'fa-hourglass-start', 'fa-houzz', 'fa-html5', 'fa-i-cursor', 'fa-id-badge', 'fa-id-card', 'fa-id-card-o', 'fa-ils', 'fa-image', 'fa-imdb',
|
||||
'fa-inbox', 'fa-indent', 'fa-industry', 'fa-info', 'fa-info-circle', 'fa-inr', 'fa-instagram', 'fa-institution', 'fa-internet-explorer',
|
||||
'fa-intersex', 'fa-ioxhost', 'fa-italic', 'fa-joomla', 'fa-jpy', 'fa-jsfiddle', 'fa-key', 'fa-keyboard-o', 'fa-krw', 'fa-language',
|
||||
'fa-laptop', 'fa-lastfm', 'fa-lastfm-square', 'fa-leaf', 'fa-leanpub', 'fa-legal', 'fa-lemon-o', 'fa-level-down', 'fa-level-up',
|
||||
'fa-life-bouy', 'fa-life-buoy', 'fa-life-ring', 'fa-life-saver', 'fa-lightbulb-o', 'fa-line-chart', 'fa-link', 'fa-linkedin',
|
||||
'fa-linkedin-square', 'fa-linode', 'fa-linux', 'fa-list', 'fa-list-alt', 'fa-list-ol', 'fa-list-ul', 'fa-location-arrow', 'fa-lock',
|
||||
'fa-long-arrow-down', 'fa-long-arrow-left', 'fa-long-arrow-right', 'fa-long-arrow-up', 'fa-low-vision', 'fa-magic', 'fa-magnet',
|
||||
'fa-mail-forward', 'fa-mail-reply', 'fa-mail-reply-all', 'fa-male', 'fa-map', 'fa-map-marker', 'fa-map-o', 'fa-map-pin', 'fa-map-signs',
|
||||
'fa-mars', 'fa-mars-double', 'fa-mars-stroke', 'fa-mars-stroke-h', 'fa-mars-stroke-v', 'fa-maxcdn', 'fa-meanpath', 'fa-medium', 'fa-medkit',
|
||||
'fa-meetup', 'fa-meh-o', 'fa-mercury', 'fa-microchip', 'fa-microphone', 'fa-microphone-slash', 'fa-minus', 'fa-minus-circle', 'fa-minus-square',
|
||||
'fa-minus-square-o', 'fa-mixcloud', 'fa-mobile', 'fa-mobile-phone', 'fa-modx', 'fa-money', 'fa-moon-o', 'fa-mortar-board', 'fa-motorcycle', 'fa-mouse-pointer', 'fa-music', 'fa-navicon', 'fa-neuter', 'fa-newspaper-o', 'fa-object-group', 'fa-object-ungroup',
|
||||
'fa-odnoklassniki', 'fa-odnoklassniki-square', 'fa-opencart', 'fa-openid', 'fa-opera', 'fa-optin-monster', 'fa-outdent', 'fa-pagelines',
|
||||
'fa-paint-brush', 'fa-paper-plane', 'fa-paper-plane-o', 'fa-paperclip', 'fa-paragraph', 'fa-paste', 'fa-pause', 'fa-pause-circle',
|
||||
'fa-pause-circle-o', 'fa-paw', 'fa-paypal', 'fa-pencil', 'fa-pencil-square', 'fa-pencil-square-o', 'fa-percent', 'fa-phone', 'fa-phone-square',
|
||||
'fa-photo', 'fa-picture-o', 'fa-pie-chart', 'fa-pied-piper', 'fa-pied-piper-alt', 'fa-pied-piper-pp', 'fa-pinterest', 'fa-pinterest-p',
|
||||
'fa-pinterest-square', 'fa-plane', 'fa-play', 'fa-play-circle', 'fa-play-circle-o', 'fa-plug', 'fa-plus', 'fa-plus-circle', 'fa-plus-square',
|
||||
'fa-plus-square-o', 'fa-podcast', 'fa-power-off', 'fa-print', 'fa-product-hunt', 'fa-puzzle-piece', 'fa-qq', 'fa-qrcode', 'fa-question',
|
||||
'fa-question-circle', 'fa-question-circle-o', 'fa-quora', 'fa-quote-left', 'fa-quote-right', 'fa-ra', 'fa-random', 'fa-ravelry', 'fa-rebel',
|
||||
'fa-recycle', 'fa-reddit', 'fa-reddit-alien', 'fa-reddit-square', 'fa-refresh', 'fa-registered', 'fa-remove', 'fa-renren', 'fa-reorder',
|
||||
'fa-repeat', 'fa-reply', 'fa-reply-all', 'fa-resistance', 'fa-retweet', 'fa-rmb', 'fa-road', 'fa-rocket', 'fa-rotate-left', 'fa-rotate-right',
|
||||
'fa-rouble', 'fa-rss', 'fa-rss-square', 'fa-rub', 'fa-ruble', 'fa-rupee', 'fa-s15', 'fa-safari', 'fa-save', 'fa-scissors', 'fa-scribd',
|
||||
'fa-search', 'fa-search-minus', 'fa-search-plus', 'fa-sellsy', 'fa-send', 'fa-send-o', 'fa-server', 'fa-share', 'fa-share-alt',
|
||||
'fa-share-alt-square', 'fa-share-square', 'fa-share-square-o', 'fa-shekel', 'fa-sheqel', 'fa-shield', 'fa-ship', 'fa-shirtsinbulk',
|
||||
'fa-shopping-bag', 'fa-shopping-basket', 'fa-shopping-cart', 'fa-shower', 'fa-sign-in', 'fa-sign-language', 'fa-sign-out', 'fa-signal',
|
||||
'fa-signing', 'fa-simplybuilt', 'fa-sitemap', 'fa-skyatlas', 'fa-skype', 'fa-slack', 'fa-sliders', 'fa-slideshare', 'fa-smile-o', 'fa-snapchat',
|
||||
'fa-snapchat-ghost', 'fa-snapchat-square', 'fa-snowflake-o', 'fa-soccer-ball-o', 'fa-sort', 'fa-sort-alpha-asc', 'fa-sort-alpha-desc',
|
||||
'fa-sort-amount-asc', 'fa-sort-amount-desc', 'fa-sort-asc', 'fa-sort-desc', 'fa-sort-down', 'fa-sort-numeric-asc', 'fa-sort-numeric-desc',
|
||||
'fa-sort-up', 'fa-soundcloud', 'fa-space-shuttle', 'fa-spinner', 'fa-spoon', 'fa-spotify', 'fa-square', 'fa-square-o', 'fa-stack-exchange',
|
||||
'fa-stack-overflow', 'fa-star', 'fa-star-half', 'fa-star-half-empty', 'fa-star-half-full', 'fa-star-half-o', 'fa-star-o', 'fa-steam',
|
||||
'fa-steam-square', 'fa-step-backward', 'fa-step-forward', 'fa-stethoscope', 'fa-sticky-note', 'fa-sticky-note-o', 'fa-stop', 'fa-stop-circle',
|
||||
'fa-stop-circle-o', 'fa-street-view', 'fa-strikethrough', 'fa-stumbleupon', 'fa-stumbleupon-circle', 'fa-subscript', 'fa-subway', 'fa-suitcase',
|
||||
'fa-sun-o', 'fa-superpowers', 'fa-superscript', 'fa-support', 'fa-table', 'fa-tablet', 'fa-tachometer', 'fa-tag', 'fa-tags', 'fa-tasks',
|
||||
'fa-taxi', 'fa-telegram', 'fa-television', 'fa-tencent-weibo', 'fa-terminal', 'fa-text-height', 'fa-text-width', 'fa-th', 'fa-th-large',
|
||||
'fa-th-list', 'fa-themeisle', 'fa-thermometer', 'fa-thermometer-0', 'fa-thermometer-1', 'fa-thermometer-2', 'fa-thermometer-3', 'fa-thermometer-4',
|
||||
'fa-thermometer-empty', 'fa-thermometer-full', 'fa-thermometer-half', 'fa-thermometer-quarter', 'fa-thermometer-three-quarters', 'fa-thumb-tack',
|
||||
'fa-thumbs-down', 'fa-thumbs-o-down', 'fa-thumbs-o-up', 'fa-thumbs-up', 'fa-ticket', 'fa-times', 'fa-times-circle', 'fa-times-circle-o',
|
||||
'fa-times-rectangle', 'fa-times-rectangle-o', 'fa-tint', 'fa-toggle-down', 'fa-toggle-left', 'fa-toggle-off', 'fa-toggle-on', 'fa-toggle-right',
|
||||
'fa-toggle-up', 'fa-trademark', 'fa-train', 'fa-transgender', 'fa-transgender-alt', 'fa-trash', 'fa-trash-o', 'fa-tree', 'fa-trello', 'fa-tripadvisor',
|
||||
'fa-trophy', 'fa-truck', 'fa-try', 'fa-tty', 'fa-tumblr', 'fa-tumblr-square', 'fa-turkish-lira', 'fa-tv', 'fa-twitch', 'fa-twitter', 'fa-twitter-square',
|
||||
'fa-umbrella', 'fa-underline', 'fa-undo', 'fa-universal-access', 'fa-university', 'fa-unlink', 'fa-unlock', 'fa-unlock-alt', 'fa-unsorted',
|
||||
'fa-upload', 'fa-usb', 'fa-usd', 'fa-user', 'fa-user-circle', 'fa-user-circle-o', 'fa-user-md', 'fa-user-o', 'fa-user-plus', 'fa-user-secret',
|
||||
'fa-user-times', 'fa-users', 'fa-vcard', 'fa-vcard-o', 'fa-venus', 'fa-venus-double', 'fa-venus-mars', 'fa-viacoin', 'fa-viadeo',
|
||||
'fa-viadeo-square', 'fa-video-camera', 'fa-vimeo', 'fa-vimeo-square', 'fa-vine', 'fa-vk', 'fa-volume-control-phone', 'fa-volume-down',
|
||||
'fa-volume-off', 'fa-volume-up', 'fa-warning', 'fa-wechat', 'fa-weibo', 'fa-weixin', 'fa-whatsapp', 'fa-wheelchair', 'fa-wheelchair-alt',
|
||||
'fa-wifi', 'fa-wikipedia-w', 'fa-window-close', 'fa-window-close-o', 'fa-window-maximize', 'fa-window-minimize', 'fa-window-restore',
|
||||
'fa-windows', 'fa-won', 'fa-wordpress', 'fa-wpbeginner', 'fa-wpexplorer', 'fa-wpforms', 'fa-wrench', 'fa-xing', 'fa-xing-square',
|
||||
'fa-y-combinator', 'fa-y-combinator-square', 'fa-yahoo', 'fa-yc', 'fa-yc-square', 'fa-yelp', 'fa-yen', 'fa-yoast', 'fa-youtube',
|
||||
'fa-youtube-play', 'fa-youtube-square' ]
|
||||
%}
|
||||
|
||||
<style>
|
||||
#select_fa{
|
||||
font-family:"FontAwesome","Liberation Sans";
|
||||
font-size:14px;
|
||||
}
|
||||
#select_fa::before{
|
||||
vertical-align:middle;
|
||||
}
|
||||
</style>
|
||||
<div class="row">
|
||||
<!-- form color picker -->
|
||||
<div class="col-lg-2 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="x_panel">
|
||||
<div class="x_title">
|
||||
<h2>System colors</h2>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="x_content">
|
||||
<div class="form-group row text-center">
|
||||
<div class="col-md-12 col-sm-4 col-xs-6">
|
||||
<label class="control-label col-xs-12">Background</label>
|
||||
<div id="colorpicker-background" class="demo demo-auto inl-bl colorpicker-background" data-container="#colorpicker-background" data-color="{{data.colours.background}}" data-inline="true"></div>
|
||||
<input id="colorpicker-background-input" type="text" value="{{data.colours.background}}" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-12 col-sm-4 col-xs-6">
|
||||
<label class="control-label col-xs-12">Primary</label>
|
||||
<div id="colorpicker-primary" class="demo demo-auto inl-bl colorpicker-primary" data-container="#colorpicker-primary" data-color="{{data.colours.primary}}" data-inline="true"></div>
|
||||
<input id="colorpicker-primary-input" type="text" value="{{data.colours.primary}}" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-12 col-sm-4 col-xs-6">
|
||||
<label class="control-label col-xs-12">Secondary</label>
|
||||
<div id="colorpicker-secondary" class="demo demo-auto inl-bl colorpicker-secondary" data-container="#colorpicker-secondary" data-color="{{data.colours.secondary}}" data-inline="true"></div>
|
||||
<input id="colorpicker-secondary-input" type="text" value="{{data.colours.secondary}}" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<ul class="nav navbar-right panel_toolbox">
|
||||
<li><button id="save-colors" type="button" class="btn btn-primary">Save changes</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /form color picker -->
|
||||
<!-- custom menu -->
|
||||
<div class="col-lg-10 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="x_panel">
|
||||
<div class="x_title">
|
||||
<h2>Custom menu</h2>
|
||||
<!-- <button class="btn btn-info pull-right">
|
||||
<i class='fa fa-plus'></i>
|
||||
Add entry
|
||||
</button> -->
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="x_content">
|
||||
{% for menu_item in data.apps_external %}
|
||||
<div class="form-group row ml-4">
|
||||
<div class="col-md-12 col-xs-12">
|
||||
<!-- <button class="btn btn-danger pull-right">
|
||||
<i class='fa fa-trash'></i>
|
||||
</button> -->
|
||||
<h3>
|
||||
<i class='fa {{ menu_item.icon }}'></i>
|
||||
- {{ menu_item.name }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3 col-xs-12">
|
||||
<label class="control-label" for="apps_external-{{ menu_item.shortname }}-name">Name <span class="required">*</span>
|
||||
</label>
|
||||
<input id="apps_external-{{ menu_item.shortname }}-name" value="{{ menu_item.name }}" class="roundbox" name="apps_external-{{ menu_item.shortname }}-name" placeholder="" type="text" required style="width: 100%;">
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-12">
|
||||
<label class="control-label" for="apps_external-{{ menu_item.shortname }}-shortname">Shortname <span class="required">*</span>
|
||||
</label>
|
||||
<input id="apps_external-{{ menu_item.shortname }}-shortname" value="{{ menu_item.shortname }}" class="roundbox" name="apps_external-{{ menu_item.shortname }}-shortname" placeholder="" type="text" required style="width: 100%;">
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-12">
|
||||
<label class="control-label" for="apps_external-{{ menu_item.shortname }}-href">Url <span class="required">*</span>
|
||||
</label>
|
||||
<input id="apps_external-{{ menu_item.shortname }}-href" value="{{ menu_item.href }}" class="roundbox" name="apps_external-{{ menu_item.href }}-href" placeholder="" type="text" required style="width: 100%;">
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-12">
|
||||
<label class="control-label" for="apps_external-{{ menu_item.shortname }}-icon">Icon <span class="required">*</span>
|
||||
</label>
|
||||
<select class="icon-dropdown" id="apps_external-{{ menu_item.shortname }}-icon" name="apps_external-{{ menu_item.shortname }}-icon">
|
||||
{% for icon in icons %}
|
||||
<option value='fa {{ icon }}' data-icon="{{ icon }}" {% if 'fa ' + icon == menu_item.icon %} selected='selected' {% endif %}>
|
||||
{{ icon }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="form-group row">
|
||||
<ul class="nav navbar-right panel_toolbox">
|
||||
<li><button id="save-menu" type="button" class="btn btn-primary">Save changes</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /custom menu -->
|
||||
</div>
|
||||
<!-- Logo crop -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="x_panel">
|
||||
<div class="x_title">
|
||||
<h2>Logo Image</h2>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="x_content">
|
||||
<div class="form-group row">
|
||||
<!-- Image cropping -->
|
||||
<div class="container cropper">
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="img-container">
|
||||
<img id="image_logo" src="{{ data.logo }}" alt="Background login">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="docs-preview clearfix">
|
||||
<div class="img-preview img-preview-logo preview-lg"></div>
|
||||
</div>
|
||||
<div class="alert alert-info" role="alert">
|
||||
Your organization logo will appear in the top bar of all Digital Democratic Work Environments and in the login page.<br/>
|
||||
Recommended Format:
|
||||
<strong>
|
||||
<ul>
|
||||
<li>Size: 80 x 45 píxels</li>
|
||||
<li>Weight: 30 KB</li>
|
||||
<li>Format: .png or .jpg</li>
|
||||
</ul>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-9 docs-buttons-logo">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="setDragMode" data-option="move" title="Move">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Move">
|
||||
<span class="fa fa-arrows"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-method="setDragMode" data-option="crop" title="Crop">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Crop">
|
||||
<span class="fa fa-crop"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="zoom" data-option="0.1" title="Zoom In">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Zoom In">
|
||||
<span class="fa fa-search-plus"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-method="zoom" data-option="-0.1" title="Zoom Out">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Zoom Out">
|
||||
<span class="fa fa-search-minus"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="rotate" data-option="-45" title="Rotate Left">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("rotate", -45)">
|
||||
<span class="fa fa-rotate-left"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-method="rotate" data-option="45" title="Rotate Right">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("rotate", 45)">
|
||||
<span class="fa fa-rotate-right"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="reset" title="Reset">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Reset">
|
||||
<span class="fa fa-refresh"></span>
|
||||
</span>
|
||||
</button>
|
||||
<label class="btn btn-primary btn-upload" for="inputImage" title="Upload image file">
|
||||
<input type="file" class="sr-only" id="inputImage" name="file" accept="image/*">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Import image with Blob URLs">
|
||||
<span class="fa fa-upload"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /image cropping -->
|
||||
<div class="form-group row docs-buttons">
|
||||
<ul class="nav navbar-right panel_toolbox">
|
||||
<li><button id="save-logo-crop" type="button" class="btn btn-primary">Save changes</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Background crop -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="x_panel">
|
||||
<div class="x_title">
|
||||
<h2>Background Image</h2>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="x_content">
|
||||
<div class="form-group row">
|
||||
<!-- Image cropping -->
|
||||
<div class="container cropper">
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="img-container">
|
||||
<img id="image_background" src="{{ data.background_login }}" alt="Background login">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="docs-preview clearfix">
|
||||
<div class="img-preview img-preview-background preview-lg"></div>
|
||||
</div>
|
||||
<div class="alert alert-info" role="alert">
|
||||
The background image of your organization will appear in the Digital Democratic Login Page.<br/>
|
||||
Recommended Format:
|
||||
<strong>
|
||||
<ul>
|
||||
<li>Size: 1920 x 1080 píxels</li>
|
||||
<li>Weight: 1.75 MB</li>
|
||||
<li>Format: .png or .jpg</li>
|
||||
</ul>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-9 docs-buttons-background">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="setDragMode" data-option="move" title="Move">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Move">
|
||||
<span class="fa fa-arrows"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-method="setDragMode" data-option="crop" title="Crop">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Crop">
|
||||
<span class="fa fa-crop"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="zoom" data-option="0.1" title="Zoom In">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Zoom In">
|
||||
<span class="fa fa-search-plus"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-method="zoom" data-option="-0.1" title="Zoom Out">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Zoom Out">
|
||||
<span class="fa fa-search-minus"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="rotate" data-option="-45" title="Rotate Left">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("rotate", -45)">
|
||||
<span class="fa fa-rotate-left"></span>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-method="rotate" data-option="45" title="Rotate Right">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="$().cropper("rotate", 45)">
|
||||
<span class="fa fa-rotate-right"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-method="reset" title="Reset">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Reset">
|
||||
<span class="fa fa-refresh"></span>
|
||||
</span>
|
||||
</button>
|
||||
<label class="btn btn-primary btn-upload" for="inputImage" title="Upload image file">
|
||||
<input type="file" class="sr-only" id="inputImage" name="file" accept="image/*">
|
||||
<span class="docs-tooltip" data-toggle="tooltip" title="Import image with Blob URLs">
|
||||
<span class="fa fa-upload"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /image cropping -->
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<ul class="nav navbar-right panel_toolbox">
|
||||
<li><button id="save-background-crop" type="button" class="btn btn-primary">Save changes</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'pages/modals/groups_modals.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block pagescript %}
|
||||
<!-- Ion.RangeSlider -->
|
||||
<script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
|
||||
<!-- iCheck -->
|
||||
<script src="/vendors/iCheck/icheck.min.js"></script>
|
||||
<!-- Switchery -->
|
||||
<script src="/vendors/switchery/dist/switchery.min.js"></script>
|
||||
<!-- Desktops sse & modals -->
|
||||
<!-- Bootstrap Colorpicker -->
|
||||
<script src="/vendors/mjolnic-bootstrap-colorpicker/dist/js/bootstrap-colorpicker.min.js"></script>
|
||||
<!-- Cropper -->
|
||||
<script src="/vendors/cropper/dist/cropper.min.js"></script>
|
||||
<script src="/static/js/dashboard.js"></script>
|
||||
{% endblock %}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<div class="clearfix"></div>
|
||||
<!-- sidebar menu -->
|
||||
<div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
|
||||
<div id="sidebar-menu" class="main_menu_side hidden-print main_menu mt-4">
|
||||
<div class="menu_section">
|
||||
<h3>Administration</h3>
|
||||
<div class="clearfix"></div>
|
||||
|
@ -17,7 +17,7 @@
|
|||
<li><a href="/groups"><i class="fa fa-users"></i> Groups</a></li>
|
||||
<li><a href="/roles"><i class="fa fa-user-secret"></i> Roles</a></li>
|
||||
<li><a href="/sysadmin/external"><i class="fa fa-external-link"></i> Import</a></li>
|
||||
|
||||
<li><a href="/dashboard"><i class="fa fa-paint-brush"></i> Customization</a></li>
|
||||
{% if current_user.role == 'admin' %}
|
||||
<h3>System Admin</h3>
|
||||
<div class="clearfix"></div>
|
||||
|
|
|
@ -28,6 +28,10 @@ from keycloak.exceptions import KeycloakGetError
|
|||
|
||||
from ..lib.exceptions import UserExists, UserNotFound
|
||||
|
||||
from ..lib.dashboard import Dashboard
|
||||
|
||||
dashboard = Dashboard()
|
||||
|
||||
|
||||
@app.route("/api/resync")
|
||||
@login_required
|
||||
|
@ -434,21 +438,6 @@ def external_roles():
|
|||
)
|
||||
|
||||
|
||||
{
|
||||
"groups": "/alumnes/3er",
|
||||
"firstname": "Andreu",
|
||||
"lastname": "B",
|
||||
"email": "12andreub@escolamontseny.cat",
|
||||
"username": "12andreub",
|
||||
"password": "pepinillo",
|
||||
"password_temporal": "yes",
|
||||
"role": "student",
|
||||
"quota": "500 MB",
|
||||
"": "",
|
||||
"id": "12andreub",
|
||||
}
|
||||
|
||||
|
||||
def check_upload_errors(data):
|
||||
email_regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
|
||||
for u in data["data"]:
|
||||
|
@ -477,12 +466,37 @@ def check_upload_errors(data):
|
|||
"msg": "User " + u["username"] + " has invalid role: " + u["role"],
|
||||
}
|
||||
|
||||
if u["password_temporal"].lower() not in ["yes", "no"]:
|
||||
return {
|
||||
"pass": False,
|
||||
"msg": "User "
|
||||
+ u["username"]
|
||||
+ " has invalid password_temporal value (yes/no): "
|
||||
+ u["password_temporal"],
|
||||
@app.route("/api/dashboard/<item>", methods=["PUT"])
|
||||
# @login_required
|
||||
def dashboard_put(item):
|
||||
if item == "colours":
|
||||
print('colours')
|
||||
try:
|
||||
data = request.get_json(force=True)
|
||||
dashboard.update_colours(data)
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return json.dumps({"colours":data}), 200, {"Content-Type": "application/json"}
|
||||
if item == "menu":
|
||||
try:
|
||||
data = request.get_json(force=True)
|
||||
dashboard.update_menu(data)
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return json.dumps(data), 200, {"Content-Type": "application/json"}
|
||||
if item == "logo":
|
||||
dashboard.update_logo(request.files['croppedImage'])
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
if item == "background":
|
||||
dashboard.update_background(request.files['croppedImage'])
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
return (
|
||||
json.dumps(
|
||||
{
|
||||
"error": "update_error",
|
||||
"msg": "Error updating item " + item + "\n" + traceback.format_exc(),
|
||||
}
|
||||
return {"pass": True, "msg": ""}
|
||||
),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
|
|
@ -26,6 +26,8 @@ from admin import app
|
|||
from ..lib.avatars import Avatars
|
||||
from .decorators import is_admin
|
||||
|
||||
import requests
|
||||
|
||||
avatars = Avatars()
|
||||
|
||||
""" OIDC TESTS """
|
||||
|
@ -83,6 +85,15 @@ def avatar(userid):
|
|||
return send_file("static/img/missing.jpg", mimetype="image/jpeg")
|
||||
|
||||
|
||||
@app.route("/dashboard")
|
||||
# @login_required
|
||||
def dashboard(provider=False):
|
||||
data = json.loads(requests.get("http://isard-sso-api/json").text)
|
||||
return render_template(
|
||||
"pages/dashboard.html", title="Dashboard", nav="Dashboard", data=data
|
||||
)
|
||||
|
||||
|
||||
### SYS ADMIN
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# https://stackoverflow.com/questions/36743041/font-awesome-icon-in-select-option
|
||||
|
||||
## Gets the select font awesome data from option html tags and rebuils what we need
|
||||
import os
|
||||
|
||||
new_file = ""
|
||||
with open("select.html") as f:
|
||||
for line in f:
|
||||
icon = line.split("'")[1]
|
||||
icon_hex = line.split(">")[1].split(";")[0]
|
||||
new_file += (
|
||||
"<option value='fa "
|
||||
+ icon
|
||||
+ "' {% if menu_item.icon == 'fa'"
|
||||
+ icon
|
||||
+ "' %} selected='selected'{% endif %}>"
|
||||
+ icon_hex
|
||||
+ "; "
|
||||
+ icon
|
||||
+ "</option>\n"
|
||||
)
|
||||
|
||||
with open("select_output.html", "w") as f:
|
||||
f.writelines(new_file)
|
|
@ -18,7 +18,7 @@ services:
|
|||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ${BUILD_ROOT_PATH}/admin/src:/admin # Revome in production
|
||||
- ${BUILD_ROOT_PATH}/../custom:/admin/custom #:ro in production
|
||||
- ${BUILD_ROOT_PATH}/../custom:/admin/custom
|
||||
- ${DATA_FOLDER}/avatars:/admin/avatars:ro
|
||||
- ${DATA_FOLDER}/moodle/saml2:/admin/moodledata/saml2:rw
|
||||
- ${DATA_FOLDER}/saml_certs:/admin/saml_certs:rw
|
||||
|
|
Loading…
Reference in New Issue