feat(admin): added customization dashboard

darta 2021-12-28 10:32:14 +01:00
parent 6209d74514
commit d52a8c1f05
13 changed files with 1052 additions and 33 deletions

View File

@ -12,7 +12,7 @@ COPY admin/docker/requirements.pip3 /requirements.pip3
RUN pip3 install --no-cache-dir -r requirements.pip3 RUN pip3 install --no-cache-dir -r requirements.pip3
RUN apk del .build_deps 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 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

View File

@ -10,6 +10,7 @@ psycopg2==2.8.6
python-keycloak==0.26.1 python-keycloak==0.26.1
minio==7.0.3 minio==7.0.3
urllib3==1.26.6 urllib3==1.26.6
schema==0.7.5
# Unused yet # Unused yet
#flask-oidc==1.4.0 #flask-oidc==1.4.0

View File

@ -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

View File

@ -100,10 +100,6 @@ table.dataTable tr.shown td.details-control > button > i:before {
} }
/* Sidebar */ /* Sidebar */
.sidebar_logo {
width: 50px;
height: 50px;
}
.sidebar-footer { .sidebar-footer {
background: #3b3e47; background: #3b3e47;
@ -127,9 +123,17 @@ table.dataTable tr.shown td.details-control > button > i:before {
.nav_title { .nav_title {
background: #3b3e47; background: #3b3e47;
margin-bottom: 15px;
} }
.nav_menu { .nav_menu {
padding: 10px;
background: white; background: white;
height: 75px;
max-height: 75px;
} }
.nav_menu_logo {
margin-top: 5px;
width: 100;
height: 45px;
}

View File

@ -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');
}
};

View File

@ -94,7 +94,7 @@
<script src="/vendors/select2/dist/js/select2.full.min.js"></script> <script src="/vendors/select2/dist/js/select2.full.min.js"></script>
<!-- SocketIO --> <!-- SocketIO -->
<script src="/node_modules/socket.io/client-dist/socket.io.min.js"></script> <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 --> <!-- isard initializers -->
<script src="/static/dd.js"></script> <script src="/static/dd.js"></script>

View File

@ -4,6 +4,7 @@
<div class="nav toggle"> <div class="nav toggle">
<a id="menu_toggle"><i class="fa fa-bars"></i></a> <a id="menu_toggle"><i class="fa fa-bars"></i></a>
</div> </div>
<img src="/custom/img/logo.png" class="nav_menu_logo" alt="dd">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class=""> <li class="">
<a href="javascript:" class="user-profile dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <a href="javascript:" class="user-profile dropdown-toggle" data-toggle="dropdown" aria-expanded="false">

View File

@ -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(&quot;rotate&quot;, -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(&quot;rotate&quot;, 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(&quot;rotate&quot;, -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(&quot;rotate&quot;, 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 %}

View File

@ -7,7 +7,7 @@
<div class="clearfix"></div> <div class="clearfix"></div>
<!-- sidebar menu --> <!-- 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"> <div class="menu_section">
<h3>Administration</h3> <h3>Administration</h3>
<div class="clearfix"></div> <div class="clearfix"></div>
@ -17,7 +17,7 @@
<li><a href="/groups"><i class="fa fa-users"></i> Groups</a></li> <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="/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="/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' %} {% if current_user.role == 'admin' %}
<h3>System Admin</h3> <h3>System Admin</h3>
<div class="clearfix"></div> <div class="clearfix"></div>

View File

@ -28,6 +28,10 @@ from keycloak.exceptions import KeycloakGetError
from ..lib.exceptions import UserExists, UserNotFound from ..lib.exceptions import UserExists, UserNotFound
from ..lib.dashboard import Dashboard
dashboard = Dashboard()
@app.route("/api/resync") @app.route("/api/resync")
@login_required @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): def check_upload_errors(data):
email_regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" email_regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
for u in data["data"]: for u in data["data"]:
@ -477,12 +466,37 @@ def check_upload_errors(data):
"msg": "User " + u["username"] + " has invalid role: " + u["role"], "msg": "User " + u["username"] + " has invalid role: " + u["role"],
} }
if u["password_temporal"].lower() not in ["yes", "no"]: @app.route("/api/dashboard/<item>", methods=["PUT"])
return { # @login_required
"pass": False, def dashboard_put(item):
"msg": "User " if item == "colours":
+ u["username"] print('colours')
+ " has invalid password_temporal value (yes/no): " try:
+ u["password_temporal"], 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"},
)

View File

@ -26,6 +26,8 @@ from admin import app
from ..lib.avatars import Avatars from ..lib.avatars import Avatars
from .decorators import is_admin from .decorators import is_admin
import requests
avatars = Avatars() avatars = Avatars()
""" OIDC TESTS """ """ OIDC TESTS """
@ -83,6 +85,15 @@ def avatar(userid):
return send_file("static/img/missing.jpg", mimetype="image/jpeg") 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 ### SYS ADMIN

View File

@ -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)

View File

@ -18,7 +18,7 @@ services:
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- ${BUILD_ROOT_PATH}/admin/src:/admin # Revome in production - ${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}/avatars:/admin/avatars:ro
- ${DATA_FOLDER}/moodle/saml2:/admin/moodledata/saml2:rw - ${DATA_FOLDER}/moodle/saml2:/admin/moodledata/saml2:rw
- ${DATA_FOLDER}/saml_certs:/admin/saml_certs:rw - ${DATA_FOLDER}/saml_certs:/admin/saml_certs:rw