players explorer ok
This commit is contained in:
parent
f357b60ceb
commit
da57c38ddd
File diff suppressed because one or more lines are too long
@ -80,6 +80,17 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.explr-item i.fa {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 2px;
|
||||||
|
font-size: 14px;
|
||||||
|
left: 16px;
|
||||||
|
color: rgb(187, 187, 187);
|
||||||
|
width: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.explr-toggler {
|
.explr-toggler {
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
@ -96,7 +107,8 @@
|
|||||||
|
|
||||||
/* Menu icons: */
|
/* Menu icons: */
|
||||||
|
|
||||||
.explr-tree .icon-text > li, .explr-tree li.icon-text { background-image: url("../../img/explr/attibutes.png"); }
|
.explr-tree .icon-text > li, .explr-tree li.icon-text { background-image: none; }
|
||||||
|
.explr-tree .icon-file > li, .explr-tree li.icon-file { background-image: url("../../img/explr/attibutes.png"); }
|
||||||
.explr-tree .icon-address > li, .explr-tree li.icon-address { background-image: url("../../img/explr/address.png"); }
|
.explr-tree .icon-address > li, .explr-tree li.icon-address { background-image: url("../../img/explr/address.png"); }
|
||||||
.explr-tree .icon-archives > li, .explr-tree li.icon-archives { background-image: url("../../img/explr/archives.png"); }
|
.explr-tree .icon-archives > li, .explr-tree li.icon-archives { background-image: url("../../img/explr/archives.png"); }
|
||||||
.explr-tree .icon-badge > li, .explr-tree li.icon-badge { background-image: url("../../img/explr/bestseller.png"); }
|
.explr-tree .icon-badge > li, .explr-tree li.icon-badge { background-image: url("../../img/explr/bestseller.png"); }
|
||||||
|
|||||||
110
data/www/js/fleet/node-players-old.js
Normal file
110
data/www/js/fleet/node-players-old.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
jQuery(document).ready(function ($) {
|
||||||
|
const $tableActive = $('table.active-node-players');
|
||||||
|
const $tableInactive = $('table.inactive-node-players');
|
||||||
|
|
||||||
|
const getId = function ($el) {
|
||||||
|
return $el.is('tr') ? $el.attr('data-level') : $el.parents('tr:eq(0)').attr('data-level');
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTable = function () {
|
||||||
|
$('table').each(function () {
|
||||||
|
if ($(this).find('tbody tr.node-player-item:visible').length === 0) {
|
||||||
|
$(this).find('tr.empty-tr').removeClass('hidden');
|
||||||
|
} else {
|
||||||
|
$(this).find('tr.empty-tr').addClass('hidden');
|
||||||
|
}
|
||||||
|
}).tableDnDUpdate();
|
||||||
|
updatePositions();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatePositions = function (table, row) {
|
||||||
|
const positions = {};
|
||||||
|
$('.node-player-item').each(function (index) {
|
||||||
|
positions[getId($(this))] = index;
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/fleet/node-player/position',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
data: JSON.stringify(positions),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const main = function () {
|
||||||
|
$("table").tableDnD({
|
||||||
|
dragHandle: 'td a.node-player-sort',
|
||||||
|
onDrop: updatePositions
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on('change', 'select.group-picker', function () {
|
||||||
|
document.location.href = $(this).val();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('change', 'input[type=checkbox]', function () {
|
||||||
|
$.ajax({
|
||||||
|
url: '/fleet/node-player/toggle',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
data: JSON.stringify({id: getId($(this)), enabled: $(this).is(':checked')}),
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
|
const $tr = $(this).parents('tr:eq(0)').remove().clone();
|
||||||
|
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
$tableActive.append($tr);
|
||||||
|
} else {
|
||||||
|
$tableInactive.append($tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('change', '#node-player-add-type', function () {
|
||||||
|
const value = $(this).val();
|
||||||
|
const inputType = $(this).find('option').filter(function (i, el) {
|
||||||
|
return $(el).val() === value;
|
||||||
|
}).data('input');
|
||||||
|
|
||||||
|
$('.node-player-add-object-input')
|
||||||
|
.addClass('hidden')
|
||||||
|
.prop('disabled', true)
|
||||||
|
.filter('#node-player-add-object-input-' + inputType)
|
||||||
|
.removeClass('hidden')
|
||||||
|
.prop('disabled', false)
|
||||||
|
;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$(document).on('click', '.node-player-add', function () {
|
||||||
|
showModal('modal-node-player-add');
|
||||||
|
$('.modal-node-player-add input:eq(0)').focus().select();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.node-player-edit', function () {
|
||||||
|
const nodePlayer = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
|
||||||
|
showModal('modal-node-player-edit');
|
||||||
|
$('.modal-node-player-edit input:visible:eq(0)').focus().select();
|
||||||
|
$('#node-player-edit-name').val(nodePlayer.name);
|
||||||
|
$('#node-player-edit-group-id').val(nodePlayer.group_id);
|
||||||
|
$('#node-player-edit-host').val(nodePlayer.host);
|
||||||
|
$('#node-player-edit-id').val(nodePlayer.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.node-player-delete', function () {
|
||||||
|
if (confirm(l.js_fleet_node_player_delete_confirmation)) {
|
||||||
|
const $tr = $(this).parents('tr:eq(0)');
|
||||||
|
$tr.remove();
|
||||||
|
updateTable();
|
||||||
|
$.ajax({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/fleet/node-player/delete',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
data: JSON.stringify({id: getId($(this))}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
main();
|
||||||
|
});
|
||||||
@ -1,108 +1,120 @@
|
|||||||
jQuery(document).ready(function ($) {
|
jQuery(document).ready(function ($) {
|
||||||
const $tableActive = $('table.active-node-players');
|
const initExplr = function () {
|
||||||
const $tableInactive = $('table.inactive-node-players');
|
$('.explr').each(function() {
|
||||||
|
$(this).explr({
|
||||||
const getId = function ($el) {
|
classesPlus: 'fa fa-plus',
|
||||||
return $el.is('tr') ? $el.attr('data-level') : $el.parents('tr:eq(0)').attr('data-level');
|
classesMinus: 'fa fa-minus',
|
||||||
};
|
onLoadFinish: function ($tree) {
|
||||||
|
$tree.removeClass('hidden');
|
||||||
const updateTable = function () {
|
|
||||||
$('table').each(function () {
|
|
||||||
if ($(this).find('tbody tr.node-player-item:visible').length === 0) {
|
|
||||||
$(this).find('tr.empty-tr').removeClass('hidden');
|
|
||||||
} else {
|
|
||||||
$(this).find('tr.empty-tr').addClass('hidden');
|
|
||||||
}
|
}
|
||||||
}).tableDnDUpdate();
|
|
||||||
updatePositions();
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatePositions = function (table, row) {
|
|
||||||
const positions = {};
|
|
||||||
$('.node-player-item').each(function (index) {
|
|
||||||
positions[getId($(this))] = index;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
// Open complete path in explorer sidebar
|
||||||
method: 'POST',
|
explrSidebarOpenFromFolder($(this).attr('data-working-folder-id'));
|
||||||
url: '/fleet/node-player/position',
|
});
|
||||||
headers: {'Content-Type': 'application/json'},
|
};
|
||||||
data: JSON.stringify(positions),
|
|
||||||
|
const initDrags = function () {
|
||||||
|
$('.draggable').each(function() {
|
||||||
|
$(this).draggable({
|
||||||
|
revert: "invalid",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.droppable').each(function() {
|
||||||
|
$(this).droppable({
|
||||||
|
accept: ".draggable",
|
||||||
|
over: function (event, ui) {
|
||||||
|
$(this).addClass("highlight-drop");
|
||||||
|
},
|
||||||
|
out: function (event, ui) {
|
||||||
|
$(this).removeClass("highlight-drop");
|
||||||
|
},
|
||||||
|
drop: function (event, ui) {
|
||||||
|
$(this).removeClass("highlight-drop");
|
||||||
|
const $form = $('#folder-move-form');
|
||||||
|
const $moved = ui.draggable;
|
||||||
|
const $target = $(this);
|
||||||
|
$form.find('[name=is_folder]').val($moved.attr('data-folder'))
|
||||||
|
$form.find('[name=entity_id]').val($moved.attr('data-id'))
|
||||||
|
$form.find('[name=new_folder_id]').val($target.attr('data-id'))
|
||||||
|
ui.draggable.position({
|
||||||
|
my: "center",
|
||||||
|
at: "center",
|
||||||
|
of: $(this),
|
||||||
|
using: function (pos) {
|
||||||
|
$(this).animate(pos, 50);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const main = function () {
|
const main = function () {
|
||||||
$("table").tableDnD({
|
initExplr();
|
||||||
dragHandle: 'td a.node-player-sort',
|
initDrags();
|
||||||
onDrop: updatePositions
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).on('change', 'select.group-picker', function () {
|
$(document).on('click', '.folder-add', function () {
|
||||||
document.location.href = $(this).val();
|
$('.dirview .new-folder').removeClass('hidden');
|
||||||
|
$('.page-content').animate({scrollTop: 0}, 0);
|
||||||
|
$('.dirview input').focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('change', 'input[type=checkbox]', function () {
|
|
||||||
$.ajax({
|
|
||||||
url: '/fleet/node-player/toggle',
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
data: JSON.stringify({id: getId($(this)), enabled: $(this).is(':checked')}),
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
|
|
||||||
const $tr = $(this).parents('tr:eq(0)').remove().clone();
|
|
||||||
|
|
||||||
if ($(this).is(':checked')) {
|
|
||||||
$tableActive.append($tr);
|
|
||||||
} else {
|
|
||||||
$tableInactive.append($tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTable();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('change', '#node-player-add-type', function () {
|
|
||||||
const value = $(this).val();
|
|
||||||
const inputType = $(this).find('option').filter(function (i, el) {
|
|
||||||
return $(el).val() === value;
|
|
||||||
}).data('input');
|
|
||||||
|
|
||||||
$('.node-player-add-object-input')
|
|
||||||
.addClass('hidden')
|
|
||||||
.prop('disabled', true)
|
|
||||||
.filter('#node-player-add-object-input-' + inputType)
|
|
||||||
.removeClass('hidden')
|
|
||||||
.prop('disabled', false)
|
|
||||||
;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$(document).on('click', '.node-player-add', function () {
|
$(document).on('click', '.node-player-add', function () {
|
||||||
showModal('modal-node-player-add');
|
showModal('modal-node-player-add');
|
||||||
$('.modal-node-player-add input:eq(0)').focus().select();
|
$('.modal-node-player-add input:eq(0)').focus().select();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.node-player-edit', function () {
|
$(document).on('click', '.explr-item-edit', function () {
|
||||||
const nodePlayer = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
|
const $item = $('.explr-dirview .highlight-clicked');
|
||||||
showModal('modal-node-player-edit');
|
const is_folder = $item.attr('data-folder') === '1';
|
||||||
$('.modal-node-player-edit input:visible:eq(0)').focus().select();
|
|
||||||
$('#node-player-edit-name').val(nodePlayer.name);
|
if (is_folder) {
|
||||||
$('#node-player-edit-group-id').val(nodePlayer.group_id);
|
$item.addClass('renaming');
|
||||||
$('#node-player-edit-host').val(nodePlayer.host);
|
$item.find('input').focus().select();
|
||||||
$('#node-player-edit-id').val(nodePlayer.id);
|
} else {
|
||||||
|
document.location.href = $(this).attr('data-node-player-route').replace('!c!', $item.attr('data-id'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.node-player-delete', function () {
|
$(document).on('click', '.explr-item-delete', function () {
|
||||||
|
const $item = $('.explr-dirview .highlight-clicked');
|
||||||
|
const is_folder = $item.attr('data-folder') === '1';
|
||||||
|
let route;
|
||||||
|
|
||||||
|
if (is_folder) {
|
||||||
|
route = $(this).attr('data-folder-route') + '?id=' + $item.attr('data-id');
|
||||||
|
} else {
|
||||||
|
route = $(this).attr('data-node-player-route') + '?id=' + $item.attr('data-id');
|
||||||
|
}
|
||||||
|
|
||||||
if (confirm(l.js_fleet_node_player_delete_confirmation)) {
|
if (confirm(l.js_fleet_node_player_delete_confirmation)) {
|
||||||
const $tr = $(this).parents('tr:eq(0)');
|
document.location.href = route;
|
||||||
$tr.remove();
|
}
|
||||||
updateTable();
|
|
||||||
$.ajax({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: '/fleet/node-player/delete',
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
data: JSON.stringify({id: getId($(this))}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.node-player-edit', function () {
|
||||||
|
const node_player = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
|
||||||
|
showModal('modal-node-player-edit');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$('.modal-node-player-edit input:visible:eq(0)').focus().select();
|
||||||
|
$('#node-player-edit-id').val(node_player.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('submit', '.modal-node-player-add form', function () {
|
||||||
|
const $modal = $(this).parents('.modal:eq(0)');
|
||||||
|
$modal.find('button[type=submit]').addClass('hidden');
|
||||||
|
$modal.find('.btn-loading').removeClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).keyup(function (e) {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
$('.dirview .new-folder').addClass('hidden');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -123,7 +123,7 @@ jQuery(document).ready(function ($) {
|
|||||||
$(document).on('click', '.explr-item-delete', function () {
|
$(document).on('click', '.explr-item-delete', function () {
|
||||||
const $item = $('.explr-dirview .highlight-clicked');
|
const $item = $('.explr-dirview .highlight-clicked');
|
||||||
const is_folder = $item.attr('data-folder') === '1';
|
const is_folder = $item.attr('data-folder') === '1';
|
||||||
let route = document.location.href;
|
let route;
|
||||||
|
|
||||||
if (is_folder) {
|
if (is_folder) {
|
||||||
route = $(this).attr('data-folder-route') + '?id=' + $item.attr('data-id');
|
route = $(this).attr('data-folder-route') + '?id=' + $item.attr('data-id');
|
||||||
@ -136,16 +136,6 @@ jQuery(document).ready(function ($) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.content-edit', function () {
|
|
||||||
const content = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
|
|
||||||
showModal('modal-content-edit');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$('.modal-content-edit input:visible:eq(0)').focus().select();
|
|
||||||
$('#content-edit-id').val(content.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('submit', '.modal-content-add form', function () {
|
$(document).on('submit', '.modal-content-add form', function () {
|
||||||
const $modal = $(this).parents('.modal:eq(0)');
|
const $modal = $(this).parents('.modal:eq(0)');
|
||||||
$modal.find('button[type=submit]').addClass('hidden');
|
$modal.find('button[type=submit]').addClass('hidden');
|
||||||
|
|||||||
@ -34,5 +34,6 @@
|
|||||||
|
|
||||||
// Import pages styles
|
// Import pages styles
|
||||||
@import 'pages/content';
|
@import 'pages/content';
|
||||||
|
@import 'pages/node_player';
|
||||||
//@import 'pages/settings';
|
//@import 'pages/settings';
|
||||||
//@import 'pages/sysinfo';
|
//@import 'pages/sysinfo';
|
||||||
|
|||||||
22
data/www/scss/pages/_node_player.scss
Normal file
22
data/www/scss/pages/_node_player.scss
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
.view-node-player-list main .main-container {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-node-player-edit main .main-container {
|
||||||
|
|
||||||
|
.bottom-content {
|
||||||
|
.page-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.form-holder {
|
||||||
|
margin: 20px 20px 20px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
14
lang/en.json
14
lang/en.json
@ -49,8 +49,6 @@
|
|||||||
|
|
||||||
"slideshow_content_page_title": "Content Library",
|
"slideshow_content_page_title": "Content Library",
|
||||||
"slideshow_content_button_add": "New Content",
|
"slideshow_content_button_add": "New Content",
|
||||||
"slideshow_content_button_add_folder": "New Folder",
|
|
||||||
"slideshow_content_folder_not_empty_error": "Folder isn't empty, you must delete its content first",
|
|
||||||
"slideshow_content_referenced_in_slide_error": "Content is referenced in a slide, remove slide first",
|
"slideshow_content_referenced_in_slide_error": "Content is referenced in a slide, remove slide first",
|
||||||
"slideshow_content_panel_active": "Content",
|
"slideshow_content_panel_active": "Content",
|
||||||
"slideshow_content_panel_empty": "Currently, there are no content. %link% now.",
|
"slideshow_content_panel_empty": "Currently, there are no content. %link% now.",
|
||||||
@ -106,6 +104,7 @@
|
|||||||
"fleet_node_player_form_label_name": "Name",
|
"fleet_node_player_form_label_name": "Name",
|
||||||
"fleet_node_player_form_label_group_id": "Group",
|
"fleet_node_player_form_label_group_id": "Group",
|
||||||
"fleet_node_player_form_label_host": "Host",
|
"fleet_node_player_form_label_host": "Host",
|
||||||
|
"fleet_node_player_form_label_operating_system": "OS",
|
||||||
"fleet_node_player_form_button_cancel": "Cancel",
|
"fleet_node_player_form_button_cancel": "Cancel",
|
||||||
"js_fleet_node_player_delete_confirmation": "Are you sure?",
|
"js_fleet_node_player_delete_confirmation": "Are you sure?",
|
||||||
|
|
||||||
@ -228,6 +227,8 @@
|
|||||||
"common_validate": "Validate",
|
"common_validate": "Validate",
|
||||||
"common_apply": "Apply",
|
"common_apply": "Apply",
|
||||||
"common_saved": "Changes have been saved",
|
"common_saved": "Changes have been saved",
|
||||||
|
"common_new_folder": "New Folder",
|
||||||
|
"common_folder_not_empty_error": "Folder isn't empty, you must delete its content first",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"login_error_not_found": "Bad credentials",
|
"login_error_not_found": "Bad credentials",
|
||||||
"login_error_bad_credentials": "Bad credentials",
|
"login_error_bad_credentials": "Bad credentials",
|
||||||
@ -266,6 +267,15 @@
|
|||||||
"enum_content_type_video_object_label": "Upload your video (MP4 only)",
|
"enum_content_type_video_object_label": "Upload your video (MP4 only)",
|
||||||
"enum_content_type_picture_object_label": "Upload your image",
|
"enum_content_type_picture_object_label": "Upload your image",
|
||||||
"enum_content_type_youtube_object_label": "Enter Youtube video URL",
|
"enum_content_type_youtube_object_label": "Enter Youtube video URL",
|
||||||
|
"enum_operating_system_raspbian": "Raspbian",
|
||||||
|
"enum_operating_system_windows": "Windows",
|
||||||
|
"enum_operating_system_macos": "MacOS",
|
||||||
|
"enum_operating_system_fedora": "Fedora",
|
||||||
|
"enum_operating_system_ubuntu": "Ubuntu",
|
||||||
|
"enum_operating_system_suse": "Suse",
|
||||||
|
"enum_operating_system_redhat": "Redhat",
|
||||||
|
"enum_operating_system_centos": "CentOS",
|
||||||
|
"enum_operating_system_other": "Other",
|
||||||
|
|
||||||
"sysinfo_rpi_model": "Raspberry Pi Model",
|
"sysinfo_rpi_model": "Raspberry Pi Model",
|
||||||
"sysinfo_rpi_model_unknown": "Not a Raspberry Pi or model information not available",
|
"sysinfo_rpi_model_unknown": "Not a Raspberry Pi or model information not available",
|
||||||
|
|||||||
14
lang/es.json
14
lang/es.json
@ -49,8 +49,6 @@
|
|||||||
|
|
||||||
"slideshow_content_page_title": "Biblioteca de contenidos",
|
"slideshow_content_page_title": "Biblioteca de contenidos",
|
||||||
"slideshow_content_button_add": "Nuevo Contenido",
|
"slideshow_content_button_add": "Nuevo Contenido",
|
||||||
"slideshow_content_button_add_folder": "Nuevo Carpeta",
|
|
||||||
"slideshow_content_folder_not_empty_error": "La carpeta no está vacía, primero debes eliminar su contenido",
|
|
||||||
"slideshow_content_referenced_in_slide_error": "Se hace referencia al contenido en una diapositiva; elimine la diapositiva primero",
|
"slideshow_content_referenced_in_slide_error": "Se hace referencia al contenido en una diapositiva; elimine la diapositiva primero",
|
||||||
"slideshow_content_panel_active": "Contenido",
|
"slideshow_content_panel_active": "Contenido",
|
||||||
"slideshow_content_panel_empty": "Actualmente, no hay contenido. %link% ahora.",
|
"slideshow_content_panel_empty": "Actualmente, no hay contenido. %link% ahora.",
|
||||||
@ -106,6 +104,7 @@
|
|||||||
"fleet_node_player_form_label_name": "Nombre",
|
"fleet_node_player_form_label_name": "Nombre",
|
||||||
"fleet_node_player_form_label_group_id": "Grupo",
|
"fleet_node_player_form_label_group_id": "Grupo",
|
||||||
"fleet_node_player_form_label_host": "Host",
|
"fleet_node_player_form_label_host": "Host",
|
||||||
|
"fleet_node_player_form_label_operating_system": "OS",
|
||||||
"fleet_node_player_form_button_cancel": "Cancelar",
|
"fleet_node_player_form_button_cancel": "Cancelar",
|
||||||
"js_fleet_node_player_delete_confirmation": "¿Estás seguro?",
|
"js_fleet_node_player_delete_confirmation": "¿Estás seguro?",
|
||||||
|
|
||||||
@ -228,6 +227,8 @@
|
|||||||
"common_validate": "Validar",
|
"common_validate": "Validar",
|
||||||
"common_apply": "Aplicar",
|
"common_apply": "Aplicar",
|
||||||
"common_saved": "Los cambios se han guardado",
|
"common_saved": "Los cambios se han guardado",
|
||||||
|
"common_new_folder": "Nuevo Carpeta",
|
||||||
|
"common_folder_not_empty_error": "La carpeta no está vacía, primero debes eliminar su contenido",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"login_error_not_found": "Credenciales incorrectas",
|
"login_error_not_found": "Credenciales incorrectas",
|
||||||
"login_error_bad_credentials": "Credenciales incorrectas",
|
"login_error_bad_credentials": "Credenciales incorrectas",
|
||||||
@ -266,6 +267,15 @@
|
|||||||
"enum_content_type_video_object_label": "Sube tu vídeo (solo MP4)",
|
"enum_content_type_video_object_label": "Sube tu vídeo (solo MP4)",
|
||||||
"enum_content_type_picture_object_label": "Sube tu imagen",
|
"enum_content_type_picture_object_label": "Sube tu imagen",
|
||||||
"enum_content_type_youtube_object_label": "Ingrese la URL del vídeo de Youtube",
|
"enum_content_type_youtube_object_label": "Ingrese la URL del vídeo de Youtube",
|
||||||
|
"enum_operating_system_raspbian": "Raspbian",
|
||||||
|
"enum_operating_system_windows": "Windows",
|
||||||
|
"enum_operating_system_macos": "MacOS",
|
||||||
|
"enum_operating_system_fedora": "Fedora",
|
||||||
|
"enum_operating_system_ubuntu": "Ubuntu",
|
||||||
|
"enum_operating_system_suse": "Suse",
|
||||||
|
"enum_operating_system_redhat": "Redhat",
|
||||||
|
"enum_operating_system_centos": "CentOS",
|
||||||
|
"enum_operating_system_other": "Otro",
|
||||||
|
|
||||||
"sysinfo_rpi_model": "Modelo de Raspberry Pi",
|
"sysinfo_rpi_model": "Modelo de Raspberry Pi",
|
||||||
"sysinfo_rpi_model_unknown": "No es una Raspberry Pi o la información del modelo no está disponible",
|
"sysinfo_rpi_model_unknown": "No es una Raspberry Pi o la información del modelo no está disponible",
|
||||||
|
|||||||
14
lang/fr.json
14
lang/fr.json
@ -49,8 +49,6 @@
|
|||||||
|
|
||||||
"slideshow_content_page_title": "Bibliothèque de contenus",
|
"slideshow_content_page_title": "Bibliothèque de contenus",
|
||||||
"slideshow_content_button_add": "Nouveau Contenu",
|
"slideshow_content_button_add": "Nouveau Contenu",
|
||||||
"slideshow_content_button_add_folder": "Nouveau Dossier",
|
|
||||||
"slideshow_content_folder_not_empty_error": "Le dossier n'est pas vide, vous devez d'abord supprimer son contenu",
|
|
||||||
"slideshow_content_referenced_in_slide_error": "Le contenu est référencé dans une slide, supprimez d'abord la slide",
|
"slideshow_content_referenced_in_slide_error": "Le contenu est référencé dans une slide, supprimez d'abord la slide",
|
||||||
"slideshow_content_panel_active": "Contenus",
|
"slideshow_content_panel_active": "Contenus",
|
||||||
"slideshow_content_panel_empty": "Actuellement, il n'y a aucun contenu. %link% maintenant.",
|
"slideshow_content_panel_empty": "Actuellement, il n'y a aucun contenu. %link% maintenant.",
|
||||||
@ -106,6 +104,7 @@
|
|||||||
"fleet_node_player_form_label_name": "Nom",
|
"fleet_node_player_form_label_name": "Nom",
|
||||||
"fleet_node_player_form_label_group_id": "Groupe",
|
"fleet_node_player_form_label_group_id": "Groupe",
|
||||||
"fleet_node_player_form_label_host": "Hôte",
|
"fleet_node_player_form_label_host": "Hôte",
|
||||||
|
"fleet_node_player_form_label_operating_system": "OS",
|
||||||
"fleet_node_player_form_button_cancel": "Annuler",
|
"fleet_node_player_form_button_cancel": "Annuler",
|
||||||
"js_fleet_node_player_delete_confirmation": "Êtes-vous sûr ?",
|
"js_fleet_node_player_delete_confirmation": "Êtes-vous sûr ?",
|
||||||
|
|
||||||
@ -228,6 +227,8 @@
|
|||||||
"common_validate": "Valider",
|
"common_validate": "Valider",
|
||||||
"common_apply": "Appliquer",
|
"common_apply": "Appliquer",
|
||||||
"common_saved": "Les modifications ont été enregistrées",
|
"common_saved": "Les modifications ont été enregistrées",
|
||||||
|
"common_new_folder": "Nouveau Dossier",
|
||||||
|
"common_folder_not_empty_error": "Le dossier n'est pas vide, vous devez d'abord supprimer son contenu",
|
||||||
"logout": "Déconnexion",
|
"logout": "Déconnexion",
|
||||||
"login_error_not_found": "Identifiants invalides",
|
"login_error_not_found": "Identifiants invalides",
|
||||||
"login_error_bad_credentials": "Identifiants invalides",
|
"login_error_bad_credentials": "Identifiants invalides",
|
||||||
@ -266,6 +267,15 @@
|
|||||||
"enum_content_type_video_object_label": "Uploadez votre vidéo (MP4 seulement)",
|
"enum_content_type_video_object_label": "Uploadez votre vidéo (MP4 seulement)",
|
||||||
"enum_content_type_picture_object_label": "Uploadez votre image",
|
"enum_content_type_picture_object_label": "Uploadez votre image",
|
||||||
"enum_content_type_youtube_object_label": "Enrez l'URL de la vidéo Youtube",
|
"enum_content_type_youtube_object_label": "Enrez l'URL de la vidéo Youtube",
|
||||||
|
"enum_operating_system_raspbian": "Raspbian",
|
||||||
|
"enum_operating_system_windows": "Windows",
|
||||||
|
"enum_operating_system_macos": "MacOS",
|
||||||
|
"enum_operating_system_fedora": "Fedora",
|
||||||
|
"enum_operating_system_ubuntu": "Ubuntu",
|
||||||
|
"enum_operating_system_suse": "Suse",
|
||||||
|
"enum_operating_system_redhat": "Redhat",
|
||||||
|
"enum_operating_system_centos": "CentOS",
|
||||||
|
"enum_operating_system_other": "Autre",
|
||||||
|
|
||||||
"sysinfo_rpi_model": "Modèle du Raspberry Pi",
|
"sysinfo_rpi_model": "Modèle du Raspberry Pi",
|
||||||
"sysinfo_rpi_model_unknown": "Le modèle n'est pas un Raspberry Pi",
|
"sysinfo_rpi_model_unknown": "Le modèle n'est pas un Raspberry Pi",
|
||||||
|
|||||||
14
lang/it.json
14
lang/it.json
@ -49,8 +49,6 @@
|
|||||||
|
|
||||||
"slideshow_content_page_title": "Libreria dei contenuti",
|
"slideshow_content_page_title": "Libreria dei contenuti",
|
||||||
"slideshow_content_button_add": "Nuovo Contenuto",
|
"slideshow_content_button_add": "Nuovo Contenuto",
|
||||||
"slideshow_content_button_add_folder": "Nuovo Cartella",
|
|
||||||
"slideshow_content_folder_not_empty_error": "La cartella non è vuota, devi prima eliminarne il contenuto",
|
|
||||||
"slideshow_content_referenced_in_slide_error": "Si fa riferimento al contenuto in una diapositiva, rimuovere prima la diapositiva",
|
"slideshow_content_referenced_in_slide_error": "Si fa riferimento al contenuto in una diapositiva, rimuovere prima la diapositiva",
|
||||||
"slideshow_content_panel_active": "Contenuti",
|
"slideshow_content_panel_active": "Contenuti",
|
||||||
"slideshow_content_panel_empty": "Attualmente non ci sono contenuti. %link% adesso.",
|
"slideshow_content_panel_empty": "Attualmente non ci sono contenuti. %link% adesso.",
|
||||||
@ -106,6 +104,7 @@
|
|||||||
"fleet_node_player_form_label_name": "Nome",
|
"fleet_node_player_form_label_name": "Nome",
|
||||||
"fleet_node_player_form_label_group_id": "Group",
|
"fleet_node_player_form_label_group_id": "Group",
|
||||||
"fleet_node_player_form_label_host": "Host",
|
"fleet_node_player_form_label_host": "Host",
|
||||||
|
"fleet_node_player_form_label_operating_system": "OS",
|
||||||
"fleet_node_player_form_button_cancel": "Cancella",
|
"fleet_node_player_form_button_cancel": "Cancella",
|
||||||
"js_fleet_node_player_delete_confirmation": "Sei sicuro?",
|
"js_fleet_node_player_delete_confirmation": "Sei sicuro?",
|
||||||
|
|
||||||
@ -228,6 +227,8 @@
|
|||||||
"common_validate": "Convalida",
|
"common_validate": "Convalida",
|
||||||
"common_apply": "Applica",
|
"common_apply": "Applica",
|
||||||
"common_saved": "Le modifiche sono state salvate",
|
"common_saved": "Le modifiche sono state salvate",
|
||||||
|
"common_new_folder": "Nuovo Cartella",
|
||||||
|
"common_folder_not_empty_error": "La cartella non è vuota, devi prima eliminarne il contenuto",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"login_error_not_found": "Credenziali errate",
|
"login_error_not_found": "Credenziali errate",
|
||||||
"login_error_bad_credentials": "Credenziali errate",
|
"login_error_bad_credentials": "Credenziali errate",
|
||||||
@ -266,6 +267,15 @@
|
|||||||
"enum_content_type_video_object_label": "Carica il tuo video (solo MP4)",
|
"enum_content_type_video_object_label": "Carica il tuo video (solo MP4)",
|
||||||
"enum_content_type_picture_object_label": "Carica la tua immagine",
|
"enum_content_type_picture_object_label": "Carica la tua immagine",
|
||||||
"enum_content_type_youtube_object_label": "Inserisci l'URL del video Youtube",
|
"enum_content_type_youtube_object_label": "Inserisci l'URL del video Youtube",
|
||||||
|
"enum_operating_system_raspbian": "Raspbian",
|
||||||
|
"enum_operating_system_windows": "Windows",
|
||||||
|
"enum_operating_system_macos": "MacOS",
|
||||||
|
"enum_operating_system_fedora": "Fedora",
|
||||||
|
"enum_operating_system_ubuntu": "Ubuntu",
|
||||||
|
"enum_operating_system_suse": "Suse",
|
||||||
|
"enum_operating_system_redhat": "Redhat",
|
||||||
|
"enum_operating_system_centos": "CentOS",
|
||||||
|
"enum_operating_system_other": "Altro",
|
||||||
|
|
||||||
"sysinfo_rpi_model": "Raspberry Pi Model",
|
"sysinfo_rpi_model": "Raspberry Pi Model",
|
||||||
"sysinfo_rpi_model_unknown": "Informazioni Raspberry Pi non disponibili",
|
"sysinfo_rpi_model_unknown": "Informazioni Raspberry Pi non disponibili",
|
||||||
|
|||||||
@ -6,7 +6,6 @@ from flask import Flask, render_template, redirect, request, url_for, send_from_
|
|||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from src.service.ModelStore import ModelStore
|
from src.service.ModelStore import ModelStore
|
||||||
from src.model.entity.Content import Content
|
from src.model.entity.Content import Content
|
||||||
from src.model.entity.Folder import Folder
|
|
||||||
from src.model.enum.ContentType import ContentType
|
from src.model.enum.ContentType import ContentType
|
||||||
from src.model.enum.FolderEntity import FolderEntity, FOLDER_ROOT_PATH
|
from src.model.enum.FolderEntity import FolderEntity, FOLDER_ROOT_PATH
|
||||||
from src.interface.ObController import ObController
|
from src.interface.ObController import ObController
|
||||||
@ -18,16 +17,16 @@ class ContentController(ObController):
|
|||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
self._app.add_url_rule('/slideshow/content', 'slideshow_content_list', self._auth(self.slideshow_content_list), methods=['GET'])
|
self._app.add_url_rule('/slideshow/content', 'slideshow_content_list', self._auth(self.slideshow_content_list), methods=['GET'])
|
||||||
self._app.add_url_rule('/slideshow/content/add-folder', 'slideshow_content_folder_add', self._auth(self.slideshow_content_folder_add), methods=['POST'])
|
|
||||||
self._app.add_url_rule('/slideshow/content/move-folder', 'slideshow_content_folder_move', self._auth(self.slideshow_content_folder_move), methods=['POST'])
|
|
||||||
self._app.add_url_rule('/slideshow/content/rename-folder', 'slideshow_content_folder_rename', self._auth(self.slideshow_content_folder_rename), methods=['POST'])
|
|
||||||
self._app.add_url_rule('/slideshow/content/delete-folder', 'slideshow_content_folder_delete', self._auth(self.slideshow_content_folder_delete), methods=['GET'])
|
|
||||||
self._app.add_url_rule('/slideshow/content/add', 'slideshow_content_add', self._auth(self.slideshow_content_add), methods=['GET', 'POST'])
|
self._app.add_url_rule('/slideshow/content/add', 'slideshow_content_add', self._auth(self.slideshow_content_add), methods=['GET', 'POST'])
|
||||||
self._app.add_url_rule('/slideshow/content/edit/<content_id>', 'slideshow_content_edit', self._auth(self.slideshow_content_edit), methods=['GET'])
|
self._app.add_url_rule('/slideshow/content/edit/<content_id>', 'slideshow_content_edit', self._auth(self.slideshow_content_edit), methods=['GET'])
|
||||||
self._app.add_url_rule('/slideshow/content/save/<content_id>', 'slideshow_content_save', self._auth(self.slideshow_content_save), methods=['POST'])
|
self._app.add_url_rule('/slideshow/content/save/<content_id>', 'slideshow_content_save', self._auth(self.slideshow_content_save), methods=['POST'])
|
||||||
self._app.add_url_rule('/slideshow/content/delete', 'slideshow_content_delete', self._auth(self.slideshow_content_delete), methods=['GET'])
|
self._app.add_url_rule('/slideshow/content/delete', 'slideshow_content_delete', self._auth(self.slideshow_content_delete), methods=['GET'])
|
||||||
self._app.add_url_rule('/slideshow/content/show/<content_id>', 'slideshow_content_show', self._auth(self.slideshow_content_show), methods=['GET'])
|
|
||||||
self._app.add_url_rule('/slideshow/content/cd', 'slideshow_content_cd', self._auth(self.slideshow_content_cd), methods=['GET'])
|
self._app.add_url_rule('/slideshow/content/cd', 'slideshow_content_cd', self._auth(self.slideshow_content_cd), methods=['GET'])
|
||||||
|
self._app.add_url_rule('/slideshow/content/add-folder', 'slideshow_content_folder_add', self._auth(self.slideshow_content_folder_add), methods=['POST'])
|
||||||
|
self._app.add_url_rule('/slideshow/content/move-folder', 'slideshow_content_folder_move', self._auth(self.slideshow_content_folder_move), methods=['POST'])
|
||||||
|
self._app.add_url_rule('/slideshow/content/rename-folder', 'slideshow_content_folder_rename', self._auth(self.slideshow_content_folder_rename), methods=['POST'])
|
||||||
|
self._app.add_url_rule('/slideshow/content/delete-folder', 'slideshow_content_folder_delete', self._auth(self.slideshow_content_folder_delete), methods=['GET'])
|
||||||
|
self._app.add_url_rule('/slideshow/content/show/<content_id>', 'slideshow_content_show', self._auth(self.slideshow_content_show), methods=['GET'])
|
||||||
|
|
||||||
def slideshow_content_list(self):
|
def slideshow_content_list(self):
|
||||||
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_content').as_string()
|
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_content').as_string()
|
||||||
@ -39,52 +38,11 @@ class ContentController(ObController):
|
|||||||
folders_tree=self._model_store.folder().get_folder_tree(FolderEntity.CONTENT),
|
folders_tree=self._model_store.folder().get_folder_tree(FolderEntity.CONTENT),
|
||||||
working_folder_path=working_folder_path,
|
working_folder_path=working_folder_path,
|
||||||
working_folder=working_folder,
|
working_folder=working_folder,
|
||||||
working_folder_children=self._model_store.folder().get_children(working_folder, sort='created_at', ascending=False),
|
working_folder_children=self._model_store.folder().get_children(folder=working_folder, entity=FolderEntity.CONTENT, sort='created_at', ascending=False),
|
||||||
enum_content_type=ContentType,
|
enum_content_type=ContentType,
|
||||||
enum_folder_entity=FolderEntity,
|
enum_folder_entity=FolderEntity,
|
||||||
)
|
)
|
||||||
|
|
||||||
def slideshow_content_folder_add(self):
|
|
||||||
self._model_store.folder().add_folder(
|
|
||||||
entity=FolderEntity.CONTENT,
|
|
||||||
name=request.form['name'],
|
|
||||||
)
|
|
||||||
|
|
||||||
return redirect(url_for('slideshow_content_list'))
|
|
||||||
|
|
||||||
def slideshow_content_folder_rename(self):
|
|
||||||
self._model_store.folder().rename_folder(
|
|
||||||
folder_id=request.form['id'],
|
|
||||||
name=request.form['name'],
|
|
||||||
)
|
|
||||||
|
|
||||||
return redirect(url_for('slideshow_content_list'))
|
|
||||||
|
|
||||||
def slideshow_content_folder_move(self):
|
|
||||||
self._model_store.folder().move_to_folder(
|
|
||||||
entity_id=request.form['entity_id'],
|
|
||||||
folder_id=request.form['new_folder_id'],
|
|
||||||
entity_is_folder=True if request.form['is_folder'] == '1' else False,
|
|
||||||
)
|
|
||||||
|
|
||||||
return redirect(url_for('slideshow_content_list'))
|
|
||||||
|
|
||||||
def slideshow_content_folder_delete(self):
|
|
||||||
folder = self._model_store.folder().get(request.args.get('id'))
|
|
||||||
|
|
||||||
if not folder:
|
|
||||||
return redirect(url_for('slideshow_content_list'))
|
|
||||||
|
|
||||||
content_counter = self._model_store.content().count_contents_for_folder(folder.id)
|
|
||||||
folder_counter = self._model_store.folder().count_subfolders_for_folder(folder.id)
|
|
||||||
|
|
||||||
if content_counter > 0 or folder_counter:
|
|
||||||
return redirect(url_for('slideshow_content_list', folder_not_empty_error=True))
|
|
||||||
|
|
||||||
self._model_store.folder().delete(id=folder.id)
|
|
||||||
|
|
||||||
return redirect(url_for('slideshow_content_list'))
|
|
||||||
|
|
||||||
def slideshow_content_add(self):
|
def slideshow_content_add(self):
|
||||||
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_content').as_string()
|
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_content').as_string()
|
||||||
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.CONTENT)
|
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.CONTENT)
|
||||||
@ -169,6 +127,47 @@ class ContentController(ObController):
|
|||||||
|
|
||||||
return redirect(url_for('slideshow_content_list', path=path))
|
return redirect(url_for('slideshow_content_list', path=path))
|
||||||
|
|
||||||
|
def slideshow_content_folder_add(self):
|
||||||
|
self._model_store.folder().add_folder(
|
||||||
|
entity=FolderEntity.CONTENT,
|
||||||
|
name=request.form['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('slideshow_content_list'))
|
||||||
|
|
||||||
|
def slideshow_content_folder_rename(self):
|
||||||
|
self._model_store.folder().rename_folder(
|
||||||
|
folder_id=request.form['id'],
|
||||||
|
name=request.form['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('slideshow_content_list'))
|
||||||
|
|
||||||
|
def slideshow_content_folder_move(self):
|
||||||
|
self._model_store.folder().move_to_folder(
|
||||||
|
entity_id=request.form['entity_id'],
|
||||||
|
folder_id=request.form['new_folder_id'],
|
||||||
|
entity_is_folder=True if request.form['is_folder'] == '1' else False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('slideshow_content_list'))
|
||||||
|
|
||||||
|
def slideshow_content_folder_delete(self):
|
||||||
|
folder = self._model_store.folder().get(request.args.get('id'))
|
||||||
|
|
||||||
|
if not folder:
|
||||||
|
return redirect(url_for('slideshow_content_list'))
|
||||||
|
|
||||||
|
content_counter = self._model_store.content().count_contents_for_folder(folder.id)
|
||||||
|
folder_counter = self._model_store.folder().count_subfolders_for_folder(folder.id)
|
||||||
|
|
||||||
|
if content_counter > 0 or folder_counter:
|
||||||
|
return redirect(url_for('slideshow_content_list', folder_not_empty_error=True))
|
||||||
|
|
||||||
|
self._model_store.folder().delete(id=folder.id)
|
||||||
|
|
||||||
|
return redirect(url_for('slideshow_content_list'))
|
||||||
|
|
||||||
def slideshow_content_show(self, content_id: int = 0):
|
def slideshow_content_show(self, content_id: int = 0):
|
||||||
content = self._model_store.content().get(content_id)
|
content = self._model_store.content().get(content_id)
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import Flask, render_template, redirect, request, url_for, jsonify
|
from flask import Flask, render_template, redirect, request, url_for, jsonify, abort
|
||||||
from src.service.ModelStore import ModelStore
|
from src.service.ModelStore import ModelStore
|
||||||
from src.model.entity.NodePlayer import NodePlayer
|
from src.model.entity.NodePlayer import NodePlayer
|
||||||
from src.interface.ObController import ObController
|
from src.interface.ObController import ObController
|
||||||
|
from src.model.enum.OperatingSystem import OperatingSystem
|
||||||
|
from src.model.enum.FolderEntity import FolderEntity, FOLDER_ROOT_PATH
|
||||||
|
from src.util.utils import str_to_enum
|
||||||
|
|
||||||
|
|
||||||
class FleetNodePlayerController(ObController):
|
class FleetNodePlayerController(ObController):
|
||||||
@ -17,62 +20,151 @@ class FleetNodePlayerController(ObController):
|
|||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
self._app.add_url_rule('/fleet/node-player/list', 'fleet_node_player_list', self.guard_fleet(self._auth(self.fleet_node_player_list)), methods=['GET'])
|
self._app.add_url_rule('/fleet/node-player', 'fleet_node_player_list', self.guard_fleet(self._auth(self.fleet_node_player_list)), methods=['GET'])
|
||||||
self._app.add_url_rule('/fleet/node-player/group/set/<group_id>', 'fleet_node_player_list_group_use', self._auth(self.fleet_node_player_list), methods=['GET'])
|
self._app.add_url_rule('/fleet/node-player/add', 'fleet_node_player_add', self.guard_fleet(self._auth(self.fleet_node_player_add)), methods=['GET', 'POST'])
|
||||||
self._app.add_url_rule('/fleet/node-player/add', 'fleet_node_player_add', self.guard_fleet(self._auth(self.fleet_node_player_add)), methods=['POST'])
|
self._app.add_url_rule('/fleet/node-player/edit/<node_player_id>', 'fleet_node_player_edit', self._auth(self.fleet_node_player_edit), methods=['GET'])
|
||||||
self._app.add_url_rule('/fleet/node-player/edit', 'fleet_node_player_edit', self.guard_fleet(self._auth(self.fleet_node_player_edit)), methods=['POST'])
|
self._app.add_url_rule('/fleet/node-player/save/<node_player_id>', 'fleet_node_player_save', self._auth(self.fleet_node_player_save), methods=['POST'])
|
||||||
self._app.add_url_rule('/fleet/node-player/toggle', 'fleet_node_player_toggle', self.guard_fleet(self._auth(self.fleet_node_player_toggle)), methods=['POST'])
|
self._app.add_url_rule('/fleet/node-player/delete', 'fleet_node_player_delete', self.guard_fleet(self._auth(self.fleet_node_player_delete)), methods=['GET'])
|
||||||
self._app.add_url_rule('/fleet/node-player/delete', 'fleet_node_player_delete', self.guard_fleet(self._auth(self.fleet_node_player_delete)), methods=['DELETE'])
|
self._app.add_url_rule('/fleet/node-player/cd', 'fleet_node_player_cd', self._auth(self.fleet_node_player_cd), methods=['GET'])
|
||||||
self._app.add_url_rule('/fleet/node-player/position', 'fleet_node_player_position', self.guard_fleet(self._auth(self.fleet_node_player_position)), methods=['POST'])
|
self._app.add_url_rule('/fleet/node-player/add-folder', 'fleet_node_player_folder_add', self._auth(self.fleet_node_player_folder_add), methods=['POST'])
|
||||||
|
self._app.add_url_rule('/fleet/node-player/move-folder', 'fleet_node_player_folder_move', self._auth(self.fleet_node_player_folder_move), methods=['POST'])
|
||||||
|
self._app.add_url_rule('/fleet/node-player/rename-folder', 'fleet_node_player_folder_rename', self._auth(self.fleet_node_player_folder_rename), methods=['POST'])
|
||||||
|
self._app.add_url_rule('/fleet/node-player/delete-folder', 'fleet_node_player_folder_delete', self._auth(self.fleet_node_player_folder_delete), methods=['GET'])
|
||||||
|
|
||||||
def fleet_node_player_list(self, group_id: int = 0):
|
def fleet_node_player_list(self):
|
||||||
current_group = self._model_store.node_player_group().get(group_id)
|
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_node_player').as_string()
|
||||||
group_id = current_group.id if current_group else None
|
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.NODE_PLAYER)
|
||||||
return render_template(
|
return render_template(
|
||||||
'fleet/player/list.jinja.html',
|
'fleet/node-players/list.jinja.html',
|
||||||
current_group=current_group,
|
node_players=self._model_store.node_player().get_all_indexed('folder_id', multiple=True),
|
||||||
groups=self._model_store.node_player_group().get_all_labels_indexed(),
|
folders_tree=self._model_store.folder().get_folder_tree(FolderEntity.NODE_PLAYER),
|
||||||
enabled_node_players=self._model_store.node_player().get_node_players(group_id=group_id, enabled=True),
|
working_folder_path=working_folder_path,
|
||||||
disabled_node_players=self._model_store.node_player().get_node_players(group_id=group_id, enabled=False)
|
working_folder=working_folder,
|
||||||
|
working_folder_children=self._model_store.folder().get_children(folder=working_folder, entity=FolderEntity.NODE_PLAYER, sort='created_at', ascending=False),
|
||||||
|
enum_operating_system=OperatingSystem,
|
||||||
|
enum_folder_entity=FolderEntity,
|
||||||
)
|
)
|
||||||
|
|
||||||
def fleet_node_player_add(self):
|
def fleet_node_player_add(self):
|
||||||
node_player = NodePlayer(
|
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_node_player').as_string()
|
||||||
|
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.NODE_PLAYER)
|
||||||
|
|
||||||
|
self._model_store.node_player().add_form(
|
||||||
|
NodePlayer(
|
||||||
name=request.form['name'],
|
name=request.form['name'],
|
||||||
host=request.form['host'],
|
host=request.form['host'],
|
||||||
group_id=request.form['group_id'] if 'group_id' in request.form and request.form['group_id'] else None,
|
operating_system=str_to_enum(request.form['operating_system'], OperatingSystem),
|
||||||
|
folder_id=working_folder.id if working_folder else None,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self._model_store.node_player().add_form(node_player)
|
|
||||||
|
|
||||||
if node_player.group_id:
|
|
||||||
return redirect(url_for('fleet_node_player_list_group_use', group_id=node_player.group_id))
|
|
||||||
|
|
||||||
return redirect(url_for('fleet_node_player_list'))
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
def fleet_node_player_edit(self):
|
def fleet_node_player_edit(self, node_player_id: int = 0):
|
||||||
node_player = self._model_store.node_player().update_form(
|
node_player = self._model_store.node_player().get(node_player_id)
|
||||||
request.form['id'],
|
|
||||||
request.form['name'],
|
if not node_player:
|
||||||
request.form['host'],
|
return abort(404)
|
||||||
request.form['group_id']
|
|
||||||
|
working_folder_path = self._model_store.variable().get_one_by_name('last_folder_node_player').as_string()
|
||||||
|
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.NODE_PLAYER)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'fleet/node-players/edit.jinja.html',
|
||||||
|
node_player=node_player,
|
||||||
|
working_folder_path=working_folder_path,
|
||||||
|
working_folder=working_folder,
|
||||||
|
enum_operating_system=OperatingSystem,
|
||||||
)
|
)
|
||||||
|
|
||||||
if node_player.group_id:
|
def fleet_node_player_save(self, node_player_id: int = 0):
|
||||||
return redirect(url_for('fleet_node_player_list_group_use', group_id=node_player.group_id))
|
node_player = self._model_store.node_player().get(node_player_id)
|
||||||
|
|
||||||
|
if not node_player:
|
||||||
return redirect(url_for('fleet_node_player_list'))
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
def fleet_node_player_toggle(self):
|
self._model_store.node_player().update_form(
|
||||||
data = request.get_json()
|
id=node_player.id,
|
||||||
self._model_store.node_player().update_enabled(data.get('id'), data.get('enabled'))
|
name=request.form['name'],
|
||||||
return jsonify({'status': 'ok'})
|
operating_system=str_to_enum(request.form['operating_system'], OperatingSystem),
|
||||||
|
host=request.form['host'],
|
||||||
|
)
|
||||||
|
self._post_update()
|
||||||
|
|
||||||
|
return redirect(url_for('fleet_node_player_edit', node_player_id=node_player_id, saved=1))
|
||||||
|
|
||||||
def fleet_node_player_delete(self):
|
def fleet_node_player_delete(self):
|
||||||
data = request.get_json()
|
node_player = self._model_store.node_player().get(request.args.get('id'))
|
||||||
self._model_store.node_player().delete(data.get('id'))
|
|
||||||
return jsonify({'status': 'ok'})
|
|
||||||
|
|
||||||
def fleet_node_player_position(self):
|
if not node_player:
|
||||||
data = request.get_json()
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
self._model_store.node_player().update_positions(data)
|
|
||||||
return jsonify({'status': 'ok'})
|
self._model_store.node_player().delete(node_player.id)
|
||||||
|
self._post_update()
|
||||||
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
|
def fleet_node_player_cd(self):
|
||||||
|
path = request.args.get('path')
|
||||||
|
|
||||||
|
if path == FOLDER_ROOT_PATH:
|
||||||
|
self._model_store.variable().update_by_name("last_folder_node_player", FOLDER_ROOT_PATH)
|
||||||
|
return redirect(url_for('fleet_node_player_list', path=FOLDER_ROOT_PATH))
|
||||||
|
|
||||||
|
if not path:
|
||||||
|
return abort(404)
|
||||||
|
|
||||||
|
cd_folder = self._model_store.folder().get_one_by_path(
|
||||||
|
path=path,
|
||||||
|
entity=FolderEntity.NODE_PLAYER
|
||||||
|
)
|
||||||
|
|
||||||
|
if not cd_folder:
|
||||||
|
return abort(404)
|
||||||
|
|
||||||
|
self._model_store.variable().update_by_name("last_folder_node_player", path)
|
||||||
|
|
||||||
|
return redirect(url_for('fleet_node_player_list', path=path))
|
||||||
|
|
||||||
|
def fleet_node_player_folder_add(self):
|
||||||
|
self._model_store.folder().add_folder(
|
||||||
|
entity=FolderEntity.NODE_PLAYER,
|
||||||
|
name=request.form['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
|
def fleet_node_player_folder_rename(self):
|
||||||
|
self._model_store.folder().rename_folder(
|
||||||
|
folder_id=request.form['id'],
|
||||||
|
name=request.form['name'],
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
|
def fleet_node_player_folder_move(self):
|
||||||
|
self._model_store.folder().move_to_folder(
|
||||||
|
entity_id=request.form['entity_id'],
|
||||||
|
folder_id=request.form['new_folder_id'],
|
||||||
|
entity_is_folder=True if request.form['is_folder'] == '1' else False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
|
def fleet_node_player_folder_delete(self):
|
||||||
|
folder = self._model_store.folder().get(request.args.get('id'))
|
||||||
|
|
||||||
|
if not folder:
|
||||||
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
|
node_player_counter = self._model_store.node_player().count_node_players_for_folder(folder.id)
|
||||||
|
folder_counter = self._model_store.folder().count_subfolders_for_folder(folder.id)
|
||||||
|
|
||||||
|
if node_player_counter > 0 or folder_counter:
|
||||||
|
return redirect(url_for('fleet_node_player_list', folder_not_empty_error=True))
|
||||||
|
|
||||||
|
self._model_store.folder().delete(id=folder.id)
|
||||||
|
|
||||||
|
return redirect(url_for('fleet_node_player_list'))
|
||||||
|
|
||||||
|
def _post_update(self):
|
||||||
|
pass
|
||||||
|
|||||||
@ -63,7 +63,7 @@ class ContentManager(ModelManager):
|
|||||||
return self.hydrate_object(object)
|
return self.hydrate_object(object)
|
||||||
|
|
||||||
def get_all(self, sort: Optional[str] = 'created_at', ascending=False) -> List[Content]:
|
def get_all(self, sort: Optional[str] = 'created_at', ascending=False) -> List[Content]:
|
||||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, sort=sort, ascending=ascending))
|
return self.hydrate_list(self._db.get_all(table_name=self.TABLE_NAME, sort=sort, ascending=ascending))
|
||||||
|
|
||||||
def get_all_indexed(self, attribute: str = 'id', multiple=False) -> Dict[str, Content]:
|
def get_all_indexed(self, attribute: str = 'id', multiple=False) -> Dict[str, Content]:
|
||||||
index = {}
|
index = {}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ from src.model.entity.Folder import Folder
|
|||||||
from src.model.enum.FolderEntity import FolderEntity, FOLDER_ROOT_PATH, FOLDER_ROOT_NAME
|
from src.model.enum.FolderEntity import FolderEntity, FOLDER_ROOT_PATH, FOLDER_ROOT_NAME
|
||||||
from src.manager.DatabaseManager import DatabaseManager
|
from src.manager.DatabaseManager import DatabaseManager
|
||||||
from src.manager.ContentManager import ContentManager
|
from src.manager.ContentManager import ContentManager
|
||||||
|
from src.manager.NodePlayerManager import NodePlayerManager
|
||||||
from src.manager.LangManager import LangManager
|
from src.manager.LangManager import LangManager
|
||||||
from src.manager.UserManager import UserManager
|
from src.manager.UserManager import UserManager
|
||||||
from src.manager.VariableManager import VariableManager
|
from src.manager.VariableManager import VariableManager
|
||||||
@ -47,11 +48,16 @@ class FolderManager(ModelManager):
|
|||||||
def get_by_entity(self, entity: FolderEntity) -> List[Folder]:
|
def get_by_entity(self, entity: FolderEntity) -> List[Folder]:
|
||||||
return self.get_by("entity = '{}'".format(entity.value))
|
return self.get_by("entity = '{}'".format(entity.value))
|
||||||
|
|
||||||
def get_children(self, folder: Optional[Folder], sort: Optional[str] = None, ascending=True) -> List[Folder]:
|
def get_children(self, folder: Optional[Folder], entity: Optional[FolderEntity] = None, sort: Optional[str] = None, ascending=True) -> List[Folder]:
|
||||||
if folder:
|
query = " 1=1 "
|
||||||
return self.get_by("parent_id = {}".format(folder.id), sort, ascending)
|
|
||||||
|
|
||||||
return self.get_by("parent_id is null", sort, ascending)
|
if entity:
|
||||||
|
query = "{} {}".format(query, "AND entity = '{}'".format(entity.value))
|
||||||
|
|
||||||
|
if folder:
|
||||||
|
return self.get_by("parent_id = {} AND {}".format(folder.id, query), sort, ascending)
|
||||||
|
|
||||||
|
return self.get_by("parent_id is null AND {}".format(query), sort, ascending)
|
||||||
|
|
||||||
def get_one_by_path(self, path: str, entity: FolderEntity) -> Folder:
|
def get_one_by_path(self, path: str, entity: FolderEntity) -> Folder:
|
||||||
parts = path[1:].split('/')
|
parts = path[1:].split('/')
|
||||||
@ -138,9 +144,16 @@ class FolderManager(ModelManager):
|
|||||||
params=(folder_id if folder else None, folder.depth + 1 if folder else 1, entity_id)
|
params=(folder_id if folder else None, folder.depth + 1 if folder else 1, entity_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
table = None
|
||||||
|
|
||||||
if folder.entity == FolderEntity.CONTENT:
|
if folder.entity == FolderEntity.CONTENT:
|
||||||
|
table = ContentManager.TABLE_NAME
|
||||||
|
elif folder.entity == FolderEntity.NODE_PLAYER:
|
||||||
|
table = NodePlayerManager.TABLE_NAME
|
||||||
|
|
||||||
|
if table:
|
||||||
return self._db.execute_write_query(
|
return self._db.execute_write_query(
|
||||||
query="UPDATE {} set folder_id = ? WHERE id = ?".format(ContentManager.TABLE_NAME),
|
query="UPDATE {} set folder_id = ? WHERE id = ?".format(table),
|
||||||
params=(folder_id, entity_id)
|
params=(folder_id, entity_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -148,6 +161,8 @@ class FolderManager(ModelManager):
|
|||||||
var_name = None
|
var_name = None
|
||||||
if entity == FolderEntity.CONTENT:
|
if entity == FolderEntity.CONTENT:
|
||||||
var_name = "last_folder_content"
|
var_name = "last_folder_content"
|
||||||
|
elif entity == FolderEntity.NODE_PLAYER:
|
||||||
|
var_name = "last_folder_node_player"
|
||||||
|
|
||||||
if not var_name:
|
if not var_name:
|
||||||
raise Error("No variable for entity {}".format(entity.value))
|
raise Error("No variable for entity {}".format(entity.value))
|
||||||
@ -164,7 +179,7 @@ class FolderManager(ModelManager):
|
|||||||
|
|
||||||
def add_folder(self, entity: FolderEntity, name: str) -> Folder:
|
def add_folder(self, entity: FolderEntity, name: str) -> Folder:
|
||||||
working_folder_path = self.get_working_folder(entity)
|
working_folder_path = self.get_working_folder(entity)
|
||||||
working_folder = self.get_one_by_path(path=working_folder_path, entity=FolderEntity.CONTENT)
|
working_folder = self.get_one_by_path(path=working_folder_path, entity=entity)
|
||||||
folder_path = "{}/{}".format(working_folder_path, name)
|
folder_path = "{}/{}".format(working_folder_path, name)
|
||||||
parts = folder_path[1:].split('/')
|
parts = folder_path[1:].split('/')
|
||||||
depth = len(parts) - 1
|
depth = len(parts) - 1
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from typing import Dict, Optional, List, Tuple, Union
|
from typing import Dict, Optional, List, Tuple, Union
|
||||||
|
|
||||||
from src.model.entity.NodePlayer import NodePlayer
|
from src.model.entity.NodePlayer import NodePlayer
|
||||||
|
from src.model.enum.OperatingSystem import OperatingSystem
|
||||||
from src.manager.DatabaseManager import DatabaseManager
|
from src.manager.DatabaseManager import DatabaseManager
|
||||||
from src.manager.LangManager import LangManager
|
from src.manager.LangManager import LangManager
|
||||||
from src.manager.UserManager import UserManager
|
from src.manager.UserManager import UserManager
|
||||||
@ -13,10 +14,10 @@ class NodePlayerManager(ModelManager):
|
|||||||
TABLE_NAME = "fleet_player"
|
TABLE_NAME = "fleet_player"
|
||||||
TABLE_MODEL = [
|
TABLE_MODEL = [
|
||||||
"name CHAR(255)",
|
"name CHAR(255)",
|
||||||
"enabled INTEGER DEFAULT 0",
|
|
||||||
"group_id INTEGER",
|
|
||||||
"position INTEGER",
|
|
||||||
"host CHAR(255)",
|
"host CHAR(255)",
|
||||||
|
"operating_system CHAR(100)",
|
||||||
|
"folder_id INTEGER",
|
||||||
|
"group_id INTEGER",
|
||||||
"created_by CHAR(255)",
|
"created_by CHAR(255)",
|
||||||
"updated_by CHAR(255)",
|
"updated_by CHAR(255)",
|
||||||
"created_at INTEGER",
|
"created_at INTEGER",
|
||||||
@ -31,6 +32,11 @@ class NodePlayerManager(ModelManager):
|
|||||||
if id:
|
if id:
|
||||||
raw_node_player['id'] = id
|
raw_node_player['id'] = id
|
||||||
|
|
||||||
|
[raw_node_player, user_tracker_edits] = self.user_manager.initialize_user_trackers(raw_node_player)
|
||||||
|
|
||||||
|
if len(user_tracker_edits) > 0:
|
||||||
|
self._db.update_by_id(self.TABLE_NAME, raw_node_player['id'], user_tracker_edits)
|
||||||
|
|
||||||
return NodePlayer(**raw_node_player)
|
return NodePlayer(**raw_node_player)
|
||||||
|
|
||||||
def hydrate_list(self, raw_node_players: list) -> List[NodePlayer]:
|
def hydrate_list(self, raw_node_players: list) -> List[NodePlayer]:
|
||||||
@ -51,17 +57,22 @@ class NodePlayerManager(ModelManager):
|
|||||||
|
|
||||||
return self.hydrate_object(object)
|
return self.hydrate_object(object)
|
||||||
|
|
||||||
def get_all(self, sort: bool = False) -> List[NodePlayer]:
|
def get_all(self, sort: Optional[str] = 'created_at', ascending=False) -> List[NodePlayer]:
|
||||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, "position" if sort else None))
|
return self.hydrate_list(self._db.get_all(table_name=self.TABLE_NAME, sort=sort, ascending=ascending))
|
||||||
|
|
||||||
def get_node_players(self, group_id: Optional[int] = None, enabled: bool = True) -> List[NodePlayer]:
|
def get_all_indexed(self, attribute: str = 'id', multiple=False) -> Dict[str, NodePlayer]:
|
||||||
query = "enabled = {}".format("1" if enabled else "0")
|
index = {}
|
||||||
if group_id:
|
|
||||||
query = "{} {}".format(query, "AND group_id = {}".format(group_id))
|
for item in self.get_node_players():
|
||||||
|
id = getattr(item, attribute)
|
||||||
|
if multiple:
|
||||||
|
if id not in index:
|
||||||
|
index[id] = []
|
||||||
|
index[id].append(item)
|
||||||
else:
|
else:
|
||||||
query = "{} {}".format(query, "AND group_id is NULL")
|
index[id] = item
|
||||||
|
|
||||||
return self.get_by(query=query, sort="position")
|
return index
|
||||||
|
|
||||||
def forget_for_user(self, user_id: int):
|
def forget_for_user(self, user_id: int):
|
||||||
node_players = self.get_by("created_by = '{}' or updated_by = '{}'".format(user_id, user_id))
|
node_players = self.get_by("created_by = '{}' or updated_by = '{}'".format(user_id, user_id))
|
||||||
@ -70,6 +81,17 @@ class NodePlayerManager(ModelManager):
|
|||||||
for node_player_id, edits in edits_node_players.items():
|
for node_player_id, edits in edits_node_players.items():
|
||||||
self._db.update_by_id(self.TABLE_NAME, node_player_id, edits)
|
self._db.update_by_id(self.TABLE_NAME, node_player_id, edits)
|
||||||
|
|
||||||
|
def get_node_players(self, group_id: Optional[int] = None, folder_id: Optional[id] = None) -> List[NodePlayer]:
|
||||||
|
query = " 1=1 "
|
||||||
|
|
||||||
|
if group_id:
|
||||||
|
query = "{} {}".format(query, "AND group_id = {}".format(group_id))
|
||||||
|
|
||||||
|
if folder_id:
|
||||||
|
query = "{} {}".format(query, "AND folder_id = {}".format(folder_id))
|
||||||
|
|
||||||
|
return self.get_by(query=query)
|
||||||
|
|
||||||
def pre_add(self, node_player: Dict) -> Dict:
|
def pre_add(self, node_player: Dict) -> Dict:
|
||||||
self.user_manager.track_user_on_create(node_player)
|
self.user_manager.track_user_on_create(node_player)
|
||||||
self.user_manager.track_user_on_update(node_player)
|
self.user_manager.track_user_on_update(node_player)
|
||||||
@ -91,21 +113,7 @@ class NodePlayerManager(ModelManager):
|
|||||||
def post_delete(self, node_player_id: str) -> str:
|
def post_delete(self, node_player_id: str) -> str:
|
||||||
return node_player_id
|
return node_player_id
|
||||||
|
|
||||||
def get_enabled_node_players(self) -> List[NodePlayer]:
|
def update_form(self, id: int, name: str, host: str, operating_system: Optional[OperatingSystem] = None, group_id: Optional[int] = None) -> NodePlayer:
|
||||||
return self.get_by(query="enabled = 1", sort="position")
|
|
||||||
|
|
||||||
def get_disabled_node_players(self) -> List[NodePlayer]:
|
|
||||||
return self.get_by(query="enabled = 0", sort="position")
|
|
||||||
|
|
||||||
def update_enabled(self, id: int, enabled: bool) -> None:
|
|
||||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update({"enabled": enabled, "position": 999}))
|
|
||||||
self.post_update(id)
|
|
||||||
|
|
||||||
def update_positions(self, positions: list) -> None:
|
|
||||||
for node_player_id, node_player_position in positions.items():
|
|
||||||
self._db.update_by_id(self.TABLE_NAME, node_player_id, {"position": node_player_position})
|
|
||||||
|
|
||||||
def update_form(self, id: int, name: str, host: str, group_id: Optional[int]) -> NodePlayer:
|
|
||||||
node_player = self.get(id)
|
node_player = self.get(id)
|
||||||
|
|
||||||
if not node_player:
|
if not node_player:
|
||||||
@ -114,6 +122,7 @@ class NodePlayerManager(ModelManager):
|
|||||||
form = {
|
form = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"host": host,
|
"host": host,
|
||||||
|
"operating_system": operating_system,
|
||||||
"group_id": group_id if group_id else None
|
"group_id": group_id if group_id else None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,3 +150,6 @@ class NodePlayerManager(ModelManager):
|
|||||||
|
|
||||||
def count_node_players_for_group(self, id: int) -> int:
|
def count_node_players_for_group(self, id: int) -> int:
|
||||||
return len(self.get_node_players(group_id=id))
|
return len(self.get_node_players(group_id=id))
|
||||||
|
|
||||||
|
def count_node_players_for_folder(self, folder_id: int) -> int:
|
||||||
|
return len(self.get_node_players(folder_id=folder_id))
|
||||||
|
|||||||
@ -133,6 +133,7 @@ class VariableManager:
|
|||||||
|
|
||||||
# Not editable (System information)
|
# Not editable (System information)
|
||||||
{"name": "last_folder_content", "value": FOLDER_ROOT_PATH, "type": VariableType.STRING, "editable": False, "description": self.t('settings_variable_desc_ro_last_folder_content')},
|
{"name": "last_folder_content", "value": FOLDER_ROOT_PATH, "type": VariableType.STRING, "editable": False, "description": self.t('settings_variable_desc_ro_last_folder_content')},
|
||||||
|
{"name": "last_folder_node_player", "value": FOLDER_ROOT_PATH, "type": VariableType.STRING, "editable": False, "description": self.t('settings_variable_desc_ro_last_folder_node_player')},
|
||||||
{"name": "last_restart", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_editable')},
|
{"name": "last_restart", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_editable')},
|
||||||
{"name": "last_slide_update", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_last_slide_update')},
|
{"name": "last_slide_update", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_last_slide_update')},
|
||||||
{"name": "refresh_player_request", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_refresh_player_request')},
|
{"name": "refresh_player_request", "value": time.time(), "type": VariableType.TIMESTAMP, "editable": False, "description": self.t('settings_variable_desc_ro_refresh_player_request')},
|
||||||
|
|||||||
@ -2,17 +2,19 @@ import json
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
from src.model.enum.OperatingSystem import OperatingSystem
|
||||||
|
from src.util.utils import str_to_enum
|
||||||
|
|
||||||
|
|
||||||
class NodePlayer:
|
class NodePlayer:
|
||||||
|
|
||||||
def __init__(self, host: str = '', enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[int] = None, group_id: Optional[int] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
|
def __init__(self, host: str = '', name: str = 'Untitled', operating_system: Optional[OperatingSystem] = None, id: Optional[int] = None, group_id: Optional[int] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None, folder_id: Optional[int] = None):
|
||||||
self._id = id if id else None
|
self._id = id if id else None
|
||||||
self._group_id = group_id
|
self._group_id = group_id
|
||||||
self._host = host
|
self._host = host
|
||||||
self._enabled = enabled
|
self._operating_system = str_to_enum(operating_system, OperatingSystem) if isinstance(operating_system, str) else operating_system
|
||||||
self._name = name
|
self._name = name
|
||||||
self._position = position
|
self._folder_id = folder_id
|
||||||
self._created_by = created_by if created_by else None
|
self._created_by = created_by if created_by else None
|
||||||
self._updated_by = updated_by if updated_by else None
|
self._updated_by = updated_by if updated_by else None
|
||||||
self._created_at = int(created_at if created_at else time.time())
|
self._created_at = int(created_at if created_at else time.time())
|
||||||
@ -38,14 +40,6 @@ class NodePlayer:
|
|||||||
def host(self, value: str):
|
def host(self, value: str):
|
||||||
self._host = value
|
self._host = value
|
||||||
|
|
||||||
@property
|
|
||||||
def enabled(self) -> bool:
|
|
||||||
return bool(self._enabled)
|
|
||||||
|
|
||||||
@enabled.setter
|
|
||||||
def enabled(self, value: bool):
|
|
||||||
self._enabled = bool(value)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
@ -54,14 +48,6 @@ class NodePlayer:
|
|||||||
def name(self, value: str):
|
def name(self, value: str):
|
||||||
self._name = value
|
self._name = value
|
||||||
|
|
||||||
@property
|
|
||||||
def position(self) -> int:
|
|
||||||
return self._position
|
|
||||||
|
|
||||||
@position.setter
|
|
||||||
def position(self, value: int):
|
|
||||||
self._position = value
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_by(self) -> str:
|
def created_by(self) -> str:
|
||||||
return self._created_by
|
return self._created_by
|
||||||
@ -78,6 +64,14 @@ class NodePlayer:
|
|||||||
def updated_by(self, value: str):
|
def updated_by(self, value: str):
|
||||||
self._updated_by = value
|
self._updated_by = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def folder_id(self) -> Optional[int]:
|
||||||
|
return self._folder_id
|
||||||
|
|
||||||
|
@folder_id.setter
|
||||||
|
def folder_id(self, value: Optional[int]):
|
||||||
|
self._folder_id = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at(self) -> int:
|
def created_at(self) -> int:
|
||||||
return self._created_at
|
return self._created_at
|
||||||
@ -94,18 +88,26 @@ class NodePlayer:
|
|||||||
def updated_at(self, value: int):
|
def updated_at(self, value: int):
|
||||||
self._updated_at = value
|
self._updated_at = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def operating_system(self) -> Optional[OperatingSystem]:
|
||||||
|
return self._operating_system
|
||||||
|
|
||||||
|
@operating_system.setter
|
||||||
|
def operating_system(self, value: Optional[OperatingSystem]):
|
||||||
|
self._operating_system = value
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"NodePlayer(" \
|
return f"NodePlayer(" \
|
||||||
f"id='{self.id}',\n" \
|
f"id='{self.id}',\n" \
|
||||||
f"group_id='{self.group_id}',\n" \
|
f"group_id='{self.group_id}',\n" \
|
||||||
f"name='{self.name}',\n" \
|
f"name='{self.name}',\n" \
|
||||||
f"enabled='{self.enabled}',\n" \
|
f"operating_system='{self.operating_system}',\n" \
|
||||||
f"position='{self.position}',\n" \
|
|
||||||
f"host='{self.host}',\n" \
|
f"host='{self.host}',\n" \
|
||||||
f"created_by='{self.created_by}',\n" \
|
f"created_by='{self.created_by}',\n" \
|
||||||
f"updated_by='{self.updated_by}',\n" \
|
f"updated_by='{self.updated_by}',\n" \
|
||||||
f"created_at='{self.created_at}',\n" \
|
f"created_at='{self.created_at}',\n" \
|
||||||
f"updated_at='{self.updated_at}',\n" \
|
f"updated_at='{self.updated_at}',\n" \
|
||||||
|
f"folder_id='{self.folder_id}',\n" \
|
||||||
f")"
|
f")"
|
||||||
|
|
||||||
def to_json(self, edits: dict = {}) -> str:
|
def to_json(self, edits: dict = {}) -> str:
|
||||||
@ -121,11 +123,11 @@ class NodePlayer:
|
|||||||
"id": self.id,
|
"id": self.id,
|
||||||
"group_id": self.group_id,
|
"group_id": self.group_id,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"enabled": self.enabled,
|
"operating_system": self.operating_system.value,
|
||||||
"position": self.position,
|
|
||||||
"host": self.host,
|
"host": self.host,
|
||||||
"created_by": self.created_by,
|
"created_by": self.created_by,
|
||||||
"updated_by": self.updated_by,
|
"updated_by": self.updated_by,
|
||||||
"created_at": self.created_at,
|
"created_at": self.created_at,
|
||||||
"updated_at": self.updated_at,
|
"updated_at": self.updated_at,
|
||||||
|
"folder_id": self.folder_id,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class ContentType(Enum):
|
|||||||
return ContentInputType.TEXT
|
return ContentInputType.TEXT
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_fa_icon(value: Enum) -> ContentInputType:
|
def get_fa_icon(value: Enum) -> str:
|
||||||
if value == ContentType.PICTURE:
|
if value == ContentType.PICTURE:
|
||||||
return 'fa-regular fa-image'
|
return 'fa-regular fa-image'
|
||||||
elif value == ContentType.VIDEO:
|
elif value == ContentType.VIDEO:
|
||||||
@ -46,7 +46,7 @@ class ContentType(Enum):
|
|||||||
return 'fa-file'
|
return 'fa-file'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_color_icon(value: Enum) -> ContentInputType:
|
def get_color_icon(value: Enum) -> str:
|
||||||
if value == ContentType.PICTURE:
|
if value == ContentType.PICTURE:
|
||||||
return 'info'
|
return 'info'
|
||||||
elif value == ContentType.VIDEO:
|
elif value == ContentType.VIDEO:
|
||||||
|
|||||||
37
src/model/enum/OperatingSystem.py
Normal file
37
src/model/enum/OperatingSystem.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class OperatingSystem(Enum):
|
||||||
|
|
||||||
|
RASPBIAN = 'raspbian'
|
||||||
|
WINDOWS = 'windows'
|
||||||
|
MACOS = 'macos'
|
||||||
|
FEDORA = 'fedora'
|
||||||
|
UBUNTU = 'ubuntu'
|
||||||
|
SUSE = 'suse'
|
||||||
|
REDHAT = 'redhat'
|
||||||
|
CENTOS = 'centos'
|
||||||
|
OTHER = 'other'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_fa_icon(value: Enum) -> str:
|
||||||
|
if value == OperatingSystem.RASPBIAN:
|
||||||
|
return 'fa-brands fa-raspberry-pi'
|
||||||
|
elif value == OperatingSystem.WINDOWS:
|
||||||
|
return 'fa-brands fa-windows'
|
||||||
|
elif value == OperatingSystem.MACOS:
|
||||||
|
return 'fa-brands fa-apple'
|
||||||
|
elif value == OperatingSystem.FEDORA:
|
||||||
|
return 'fa-brands fa-fedora'
|
||||||
|
elif value == OperatingSystem.UBUNTU:
|
||||||
|
return 'fa-brands fa-ubuntu'
|
||||||
|
elif value == OperatingSystem.SUSE:
|
||||||
|
return 'fa-brands fa-suse'
|
||||||
|
elif value == OperatingSystem.REDHAT:
|
||||||
|
return 'fa-brands fa-redhat'
|
||||||
|
elif value == OperatingSystem.CENTOS:
|
||||||
|
return 'fa-brands fa-centos'
|
||||||
|
elif value == OperatingSystem.OTHER:
|
||||||
|
return 'fa-server'
|
||||||
|
|
||||||
|
return 'fa-server'
|
||||||
105
views/fleet/node-players/edit.jinja.html
Normal file
105
views/fleet/node-players/edit.jinja.html
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
{% set active_pill_route='fleet_node_player_list' %}
|
||||||
|
{% extends 'base.jinja.html' %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
{{ l.fleet_node_player_page_title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block add_css %}
|
||||||
|
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/lib/flatpickr.min.css"/>
|
||||||
|
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/lib/jquery-explr-1.4.css"/>
|
||||||
|
{{ HOOK(H_SLIDESHOW_CONTENT_CSS) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block add_js %}
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/lib/jquery-explr-1.4.js"></script>
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/fleet/node-players.js"></script>
|
||||||
|
|
||||||
|
{{ HOOK(H_SLIDESHOW_CONTENT_JAVASCRIPT) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body_class %}view-node-player-edit edit-page{% endblock %}
|
||||||
|
|
||||||
|
{% block page %}
|
||||||
|
<div class="top-content">
|
||||||
|
<h1>
|
||||||
|
{{ l.fleet_node_player_form_edit_title }}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if request.args.get('saved') %}
|
||||||
|
<div class="alert alert-success alert-timeout">
|
||||||
|
<i class="fa fa-check icon-left"></i>
|
||||||
|
{{ l.common_saved }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="bottom-content">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="inner dirview">
|
||||||
|
<div class="breadcrumb-container">
|
||||||
|
<ul class="breadcrumb">
|
||||||
|
{% set ns = namespace(breadpath='') %}
|
||||||
|
{% for dir in working_folder_path[1:].split('/') %}
|
||||||
|
{% set ns.breadpath = ns.breadpath ~ '/' ~ dir %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ url_for('fleet_node_player_cd', path=ns.breadpath) }}">
|
||||||
|
<i class="explr-icon explr-icon-folder"></i>
|
||||||
|
{{ truncate(dir, 25, '...') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if not loop.last %}
|
||||||
|
<li class="divider">
|
||||||
|
<i class="fa fa-chevron-right"></i>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="horizontal">
|
||||||
|
<div class="form-holder">
|
||||||
|
<form class="form" action="{{ url_for('fleet_node_player_save', node_player_id=node_player.id) }}" method="POST">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="node-player-edit-name">{{ l.fleet_node_player_form_label_name }}</label>
|
||||||
|
<div class="widget">
|
||||||
|
<input type="text" name="name" id="node-player-edit-name" required="required" value="{{ node_player.name }}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="node-player-edit-host">{{ l.fleet_node_player_form_label_host }}</label>
|
||||||
|
<div class="widget">
|
||||||
|
<input type="text" name="host" id="node-player-edit-host" required="required" value="{{ node_player.host }}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="node-player-add-operating-system">{{ l.fleet_node_player_form_label_operating_system }}</label>
|
||||||
|
<div class="widget">
|
||||||
|
<select name="operating_system" id="node-player-add-operating-system">
|
||||||
|
{% for os in enum_operating_system %}
|
||||||
|
<option value="{{ os.value }}">
|
||||||
|
{{ t(os) }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions actions-left">
|
||||||
|
<button type="submit" class="folder-edit btn-info">
|
||||||
|
<i class="fa fa-save icon-left"></i>
|
||||||
|
{{ l.common_save }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
195
views/fleet/node-players/list.jinja.html
Normal file
195
views/fleet/node-players/list.jinja.html
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
{% extends 'base.jinja.html' %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
{{ l.fleet_node_player_page_title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block add_css %}
|
||||||
|
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/lib/jquery-explr-1.4.css"/>
|
||||||
|
{{ HOOK(H_SLIDESHOW_CONTENT_CSS) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block add_js %}
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/lib/jquery-explr-1.4.js"></script>
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/fleet/node-players.js"></script>
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/lib/jquery-ui.min.js"></script>
|
||||||
|
|
||||||
|
{{ HOOK(H_SLIDESHOW_CONTENT_JAVASCRIPT) }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body_class %}view-node-player-list{% endblock %}
|
||||||
|
|
||||||
|
{% block page %}
|
||||||
|
<div class="top-content">
|
||||||
|
<h1>
|
||||||
|
{{ l.fleet_node_player_page_title }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="top-actions">
|
||||||
|
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_START) }}
|
||||||
|
|
||||||
|
<div class="explr-selection-actions">
|
||||||
|
<button class="explr-item-edit btn-info" data-node-player-route="{{ url_for('fleet_node_player_edit', node_player_id='!c!') }}">
|
||||||
|
<i class="fa fa-pencil"></i>
|
||||||
|
</button>
|
||||||
|
<button class="explr-item-delete btn-danger-alt" data-folder-route="{{ url_for('fleet_node_player_folder_delete') }}" data-node-player-route="{{ url_for('fleet_node_player_delete') }}">
|
||||||
|
<i class="fa fa-trash-alt"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-info node-player-add item-add">
|
||||||
|
<i class="fa fa-file-circle-plus icon-left"></i>
|
||||||
|
{{ l.fleet_node_player_button_add }}
|
||||||
|
</button>
|
||||||
|
<button class="folder-add btn-neutral">
|
||||||
|
<i class="fa fa-folder-plus icon-left"></i>
|
||||||
|
{{ l.common_new_folder }}
|
||||||
|
</button>
|
||||||
|
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_END) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if request.args.get('folder_not_empty_error') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="fa fa-warning icon-left"></i>
|
||||||
|
{{ l.common_folder_not_empty_error }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if request.args.get('referenced_in_node_player_group_error') %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<i class="fa fa-warning icon-left"></i>
|
||||||
|
{{ l.fleet_node_player_referenced_in_node_player_group_error }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="bottom-content">
|
||||||
|
<div class="page-panel left-panel explr-explorer">
|
||||||
|
|
||||||
|
{% macro render_folder(folder) %}
|
||||||
|
{% set node_player_children = node_players[folder.id]|default([]) %}
|
||||||
|
{% set has_children = folder.children or node_player_children %}
|
||||||
|
|
||||||
|
<li class="icon-folder li-explr-folder li-explr-folder-{{ folder.id }}">
|
||||||
|
<a href="{{ url_for('fleet_node_player_cd') }}?path={{ folder.path }}" class="{% if folder.path == working_folder_path %}active{% endif %}">
|
||||||
|
{{ folder.name }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{% if has_children %}
|
||||||
|
<ul>
|
||||||
|
{% for child in folder.children %}
|
||||||
|
{{ render_folder(child) }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for node_player in node_player_children %}
|
||||||
|
{% set icon = enum_operating_system.get_fa_icon(node_player.operating_system) %}
|
||||||
|
<li class="explr-item">
|
||||||
|
<i class="fa {{ icon }}"></i>
|
||||||
|
<a href="{{ url_for('fleet_node_player_edit', node_player_id=node_player.id) }}">
|
||||||
|
{{ node_player.name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
<ul class="explr hidden" id="tree" data-working-folder-id="{{ working_folder.id }}">
|
||||||
|
{{ render_folder(folders_tree) }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="folder-move-form" action="{{ url_for('fleet_node_player_folder_move') }}" class="hidden" method="POST">
|
||||||
|
<input type="hidden" name="entity_id" />
|
||||||
|
<input type="hidden" name="new_folder_id" />
|
||||||
|
<input type="hidden" name="is_folder" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="inner dirview">
|
||||||
|
<div class="breadcrumb-container">
|
||||||
|
<ul class="breadcrumb">
|
||||||
|
{% set ns = namespace(breadpath='') %}
|
||||||
|
{% for dir in working_folder_path[1:].split('/') %}
|
||||||
|
{% set ns.breadpath = ns.breadpath ~ '/' ~ dir %}
|
||||||
|
<li>
|
||||||
|
{% if loop.last %}
|
||||||
|
<span>
|
||||||
|
<i class="explr-icon explr-icon-folder"></i>
|
||||||
|
{{ truncate(dir, 25, '...') }}
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('fleet_node_player_cd', path=ns.breadpath) }}">
|
||||||
|
<i class="explr-icon explr-icon-folder"></i>
|
||||||
|
{{ truncate(dir, 25, '...') }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% if not loop.last %}
|
||||||
|
<li class="divider">
|
||||||
|
<i class="fa fa-chevron-right"></i>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<ul class="explr-dirview">
|
||||||
|
<li class="new-folder hidden">
|
||||||
|
<a href="javascript:void(0);">
|
||||||
|
<i class="fa fa-folder"></i>
|
||||||
|
<form action="{{ url_for('fleet_node_player_folder_add') }}" method="POST">
|
||||||
|
<input type="text" name="name" autocomplete="off" />
|
||||||
|
</form>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% set parent_path = '/'.join(working_folder_path.rstrip('/').split('/')[:-1]) %}
|
||||||
|
{% if parent_path %}
|
||||||
|
<li class="previous-folder droppable" data-path="{{ parent_path }}" data-id="{{ working_folder.parent_id }}" data-folder="1">
|
||||||
|
<a href="{{ url_for('fleet_node_player_cd', path=parent_path) }}" class="explr-link">
|
||||||
|
<i class="fa fa-folder"></i>
|
||||||
|
..
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for folder in working_folder_children %}
|
||||||
|
{% set folder_path = working_folder_path ~ '/' ~ folder.name %}
|
||||||
|
<li class="draggable droppable" data-path="{{ folder_path }}" data-id="{{ folder.id }}" data-folder="1">
|
||||||
|
<a href="{{ url_for('fleet_node_player_cd', path=folder_path) }}" class="explr-link explr-item-selectable">
|
||||||
|
<i class="fa fa-folder"></i>
|
||||||
|
<span>{{ truncate(folder.name, 25, '...') }}</span>
|
||||||
|
<form action="{{ url_for('fleet_node_player_folder_rename') }}" method="POST">
|
||||||
|
<input type="text" name="name" value="{{ folder.name }}" autocomplete="off" />
|
||||||
|
<input type="hidden" name="id" value="{{ folder.id }}" />
|
||||||
|
</form>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
{% for node_player in node_players[working_folder.id|default(None)]|default([]) %}
|
||||||
|
{% set icon = enum_operating_system.get_fa_icon(node_player.operating_system) %}
|
||||||
|
<li class="draggable" data-path="{{ working_folder_path }}" data-id="{{ node_player.id }}" data-folder="0">
|
||||||
|
<a href="{{ url_for('fleet_node_player_edit', node_player_id=node_player.id) }}" target="_blank" class="explr-link explr-item-selectable">
|
||||||
|
<i class="fa {{ icon }}"></i>
|
||||||
|
{{ truncate(node_player.name, 25, '...') }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modals hidden">
|
||||||
|
<div class="modals-outer">
|
||||||
|
<div class="modals-inner">
|
||||||
|
{% include 'fleet/node-players/modal/add.jinja.html' %}
|
||||||
|
{% include 'core/utrack.jinja.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
47
views/fleet/node-players/modal/add.jinja.html
Normal file
47
views/fleet/node-players/modal/add.jinja.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<div class="modal modal-node-player-add modal-node-player">
|
||||||
|
<h2>
|
||||||
|
{{ l.fleet_node_player_form_add_title }}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<form class="form" action="{{ url_for('fleet_node_player_add') }}" method="POST" enctype="multipart/form-data">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="node-player-add-name">{{ l.fleet_node_player_form_label_name }}</label>
|
||||||
|
<div class="widget">
|
||||||
|
<input name="name" type="text" id="node-player-add-name" required="required"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="node-player-add-host">{{ l.fleet_node_player_form_label_host }}</label>
|
||||||
|
<div class="widget">
|
||||||
|
<input name="host" type="text" id="node-player-add-host" required="required"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="node-player-add-operating-system">{{ l.fleet_node_player_form_label_operating_system }}</label>
|
||||||
|
<div class="widget">
|
||||||
|
<select name="operating_system" id="node-player-add-operating-system">
|
||||||
|
{% for os in enum_operating_system %}
|
||||||
|
<option value="{{ os.value }}">
|
||||||
|
{{ t(os) }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button type="button" class="btn btn-naked modal-close">
|
||||||
|
{{ l.common_close }}
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-info">
|
||||||
|
<i class="fa fa-save icon-left"></i>{{ l.common_save }}
|
||||||
|
</button>
|
||||||
|
<button type="button" disabled="disabled" class="btn btn-naked hidden btn-loading">
|
||||||
|
{{ l.common_loading }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@ -1,67 +0,0 @@
|
|||||||
<table class="{{ tclass }}-node-players">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{ l.fleet_node_player_panel_th_name }}</th>
|
|
||||||
{% if AUTH_ENABLED %}
|
|
||||||
<th class="tac">
|
|
||||||
<i class="fa fa-user"></i>
|
|
||||||
</th>
|
|
||||||
{% endif %}
|
|
||||||
<th class="tac">{{ l.fleet_node_player_panel_th_host }}</th>
|
|
||||||
<th class="tac">{{ l.fleet_node_player_panel_th_enabled }}</th>
|
|
||||||
<th class="tac">{{ l.fleet_node_player_panel_th_activity }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr class="empty-tr {% if node_players|length != 0 %}hidden{% endif %}">
|
|
||||||
<td colspan="4">
|
|
||||||
{{ l.fleet_node_player_panel_empty|replace(
|
|
||||||
'%link%',
|
|
||||||
('<a href="javascript:void(0);" class="item-add node-player-add">'~l.fleet_node_player_button_add~'</a>')|safe
|
|
||||||
) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% for node_player in node_players %}
|
|
||||||
<tr class="node-player-item" data-level="{{ node_player.id }}" data-entity="{{ node_player.to_json({"created_by": track_created(node_player).username, "updated_by": track_updated(node_player).username}) }}">
|
|
||||||
<td class="infos">
|
|
||||||
<div class="inner">
|
|
||||||
<a href="javascript:void(0);" class="item-sort node-player-sort">
|
|
||||||
<i class="fa fa-sort icon-left"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="badge"><i class="fa fa-key icon-left"></i> {{ node_player.id }}</div>
|
|
||||||
|
|
||||||
<i class="fa fa-tv icon-left"></i>
|
|
||||||
{{ node_player.name }}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
{% if AUTH_ENABLED %}
|
|
||||||
<td class="tac">
|
|
||||||
{% set creator = track_created(node_player) %}
|
|
||||||
{% if creator.username %}
|
|
||||||
<a href="javascript:void(0);" class="badge item-utrack node-player-utrack {% if not creator.enabled %}anonymous{% endif %}">
|
|
||||||
{{ creator.username }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
<td class="tac">
|
|
||||||
{{ node_player.host }}
|
|
||||||
</td>
|
|
||||||
<td class="tac">
|
|
||||||
<label class="pure-material-switch">
|
|
||||||
<input type="checkbox" {% if node_player.enabled %}checked="checked"{% endif %}><span></span>
|
|
||||||
</label>
|
|
||||||
</td>
|
|
||||||
<td class="actions tac">
|
|
||||||
<a href="javascript:void(0);" class="item-edit node-player-edit">
|
|
||||||
<i class="fa fa-pencil"></i>
|
|
||||||
</a>
|
|
||||||
<a href="javascript:void(0);" class="item-delete node-player-delete">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
{% extends 'base.jinja.html' %}
|
|
||||||
|
|
||||||
{% block page_title %}
|
|
||||||
{{ l.fleet_node_player_page_title }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block add_css %}
|
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_CSS) }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block add_js %}
|
|
||||||
<script src="{{ STATIC_PREFIX }}js/lib/tablednd-fixed.js"></script>
|
|
||||||
<script src="{{ STATIC_PREFIX }}js/fleet/node-players.js"></script>
|
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_JAVASCRIPT) }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block page %}
|
|
||||||
<div class="toolbar">
|
|
||||||
<h2><i class="fa fa-tv icon-left"></i>{{ l.fleet_node_player_page_title }}</h2>
|
|
||||||
|
|
||||||
<div class="toolbar-actions">
|
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_TOOLBAR_ACTIONS_START) }}
|
|
||||||
<button class="purple node-player-add item-add"><i class="fa fa-plus icon-left"></i>{{ l.fleet_node_player_button_add }}</button>
|
|
||||||
|
|
||||||
<select class="select-item-picker group-picker">
|
|
||||||
<option value="{{ url_for('fleet_node_player_list') }}" {% if not current_group %}selected="selected"{% endif %}>
|
|
||||||
{{ l.common_default_node_player_group }}
|
|
||||||
</option>
|
|
||||||
{% for group_id, group_name in groups.items() %}
|
|
||||||
{% set is_active_group = str(current_group.id) == str(group_id) %}
|
|
||||||
<option value="{{ url_for('fleet_node_player_list_group_use', group_id=group_id) }}" {% if is_active_group %}selected="selected"{% endif %}>
|
|
||||||
{{ group_name }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_TOOLBAR_ACTIONS_END) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel">
|
|
||||||
<div class="panel-body">
|
|
||||||
<h3>{{ l.fleet_node_player_panel_active }}</h3>
|
|
||||||
|
|
||||||
{% with tclass='active', node_players=enabled_node_players %}
|
|
||||||
{% include 'fleet/player/component/table.jinja.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="panel panel-inactive">
|
|
||||||
<div class="panel-body">
|
|
||||||
<h3>{{ l.fleet_node_player_panel_inactive }}</h3>
|
|
||||||
|
|
||||||
{% with tclass='inactive', node_players=disabled_node_players %}
|
|
||||||
{% include 'fleet/player/component/table.jinja.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modals hidden">
|
|
||||||
<div class="modals-outer">
|
|
||||||
<a href="javascript:void(0);" class="modal-close">
|
|
||||||
<i class="fa fa-close"></i>
|
|
||||||
</a>
|
|
||||||
<div class="modals-inner">
|
|
||||||
{% include 'fleet/player/modal/add.jinja.html' %}
|
|
||||||
{% include 'fleet/player/modal/edit.jinja.html' %}
|
|
||||||
{% include 'core/utrack.jinja.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
<div class="modal modal-node-player-add">
|
|
||||||
<h2>
|
|
||||||
{{ l.fleet_node_player_form_add_title }}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<form class="form" action="/fleet/node-player/add" method="POST" enctype="multipart/form-data">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="node-player-add-name">{{ l.fleet_node_player_form_label_name }}</label>
|
|
||||||
<div class="widget">
|
|
||||||
<input name="name" type="text" id="node-player-add-name" required="required" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="node-player-add-group-id">{{ l.fleet_node_player_group_form_label_group_id }}</label>
|
|
||||||
<div class="widget">
|
|
||||||
<select name="group_id" id="node-player-add-group-id">
|
|
||||||
<option value="" {% if not current_group.id %}selected="selected"{% endif %}>{{ l.common_default_node_player_group }}</option>
|
|
||||||
{% for group_id, group_name in groups.items() %}
|
|
||||||
<option value="{{ group_id }}" {% if current_group.id == str(group_id) %}selected="selected"{% endif %}>{{ group_name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="node-player-add-host">{{ l.fleet_node_player_form_label_host }}</label>
|
|
||||||
<div class="widget">
|
|
||||||
<input type="text" name="host" id="node-player-add-host" required="required" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<button type="button" class="btn-normal modal-close">
|
|
||||||
{{ l.fleet_node_player_form_button_cancel }}
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="green">
|
|
||||||
<i class="fa fa-plus icon-left"></i> {{ l.fleet_node_player_form_add_submit }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
<div class="modal modal-node-player-edit hidden">
|
|
||||||
<h2>
|
|
||||||
{{ l.fleet_node_player_form_edit_title }}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<form class="form" action="/fleet/node-player/edit" method="POST">
|
|
||||||
<input type="hidden" name="id" id="node-player-edit-id" />
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="node-player-edit-name">{{ l.fleet_node_player_form_label_name }}</label>
|
|
||||||
<div class="widget">
|
|
||||||
<input type="text" name="name" id="node-player-edit-name" required="required" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="node-player-edit-group-id">{{ l.fleet_node_player_group_form_label_group_id }}</label>
|
|
||||||
<div class="widget">
|
|
||||||
<select name="group_id" id="node-player-edit-group-id">
|
|
||||||
<option value="">{{ l.common_default_node_player_group }}</option>
|
|
||||||
{% for group_id, group_name in groups.items() %}
|
|
||||||
<option value="{{ group_id }}">{{ group_name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="node-player-edit-host">{{ l.fleet_node_player_form_label_host }}</label>
|
|
||||||
<div class="widget">
|
|
||||||
<input type="text" name="host" id="node-player-edit-host" required="required" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<button type="button" class="btn-normal modal-close">
|
|
||||||
{{ l.fleet_node_player_form_button_cancel }}
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="green">
|
|
||||||
<i class="fa fa-save icon-left"></i>{{ l.fleet_node_player_form_edit_submit }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -41,9 +41,9 @@
|
|||||||
<i class="fa fa-file-circle-plus icon-left"></i>
|
<i class="fa fa-file-circle-plus icon-left"></i>
|
||||||
{{ l.slideshow_content_button_add }}
|
{{ l.slideshow_content_button_add }}
|
||||||
</button>
|
</button>
|
||||||
<button class="folder-add btn-success-alt">
|
<button class="folder-add btn-neutral">
|
||||||
<i class="fa fa-folder-plus icon-left"></i>
|
<i class="fa fa-folder-plus icon-left"></i>
|
||||||
{{ l.slideshow_content_button_add_folder }}
|
{{ l.common_new_folder }}
|
||||||
</button>
|
</button>
|
||||||
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_END) }}
|
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_END) }}
|
||||||
</div>
|
</div>
|
||||||
@ -52,7 +52,7 @@
|
|||||||
{% if request.args.get('folder_not_empty_error') %}
|
{% if request.args.get('folder_not_empty_error') %}
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
<i class="fa fa-warning icon-left"></i>
|
<i class="fa fa-warning icon-left"></i>
|
||||||
{{ l.slideshow_content_folder_not_empty_error }}
|
{{ l.common_folder_not_empty_error }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@ -81,18 +81,10 @@
|
|||||||
{{ render_folder(child) }}
|
{{ render_folder(child) }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for content in content_children %}
|
{% for content in content_children %}
|
||||||
{% set icon = 'icon-folder' %}
|
{% set icon = enum_content_type.get_fa_icon(content.type) %}
|
||||||
{% if content.type.value == 'picture' %}
|
{% set color = enum_content_type.get_color_icon(content.type) %}
|
||||||
{% set icon = 'icon-landscape' %}
|
<li class="explr-item">
|
||||||
{% elif content.type.value == 'video' %}
|
<i class="fa {{ icon }} {{ color }}"></i>
|
||||||
{% set icon = 'icon-video' %}
|
|
||||||
{% elif content.type.value == 'url' %}
|
|
||||||
{% set icon = 'icon-chain' %}
|
|
||||||
{% elif content.type.value == 'youtube' %}
|
|
||||||
{% set icon = 'icon-youtube' %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<li class="{{ icon }}">
|
|
||||||
<a href="{{ url_for('slideshow_content_show', content_id=content.id) }}" target="_blank">
|
<a href="{{ url_for('slideshow_content_show', content_id=content.id) }}" target="_blank">
|
||||||
{{ content.name }}
|
{{ content.name }}
|
||||||
</a>
|
</a>
|
||||||
@ -178,11 +170,12 @@
|
|||||||
|
|
||||||
|
|
||||||
{% for content in contents[working_folder.id|default(None)]|default([]) %}
|
{% for content in contents[working_folder.id|default(None)]|default([]) %}
|
||||||
{% set icon = enum_content_type.get_fa_icon(content.type) ~ ' ' ~ enum_content_type.get_color_icon(content.type) %}
|
{% set icon = enum_content_type.get_fa_icon(content.type) %}
|
||||||
|
{% set color = enum_content_type.get_color_icon(content.type) %}
|
||||||
|
|
||||||
<li class="draggable" data-path="{{ working_folder_path }}" data-id="{{ content.id }}" data-folder="0">
|
<li class="draggable" data-path="{{ working_folder_path }}" data-id="{{ content.id }}" data-folder="0">
|
||||||
<a href="{{ url_for('slideshow_content_show', content_id=content.id) }}" target="_blank" class="explr-link explr-item-selectable">
|
<a href="{{ url_for('slideshow_content_show', content_id=content.id) }}" target="_blank" class="explr-link explr-item-selectable">
|
||||||
<i class="fa {{ icon }}"></i>
|
<i class="fa {{ icon }} {{ color }}"></i>
|
||||||
{{ truncate(content.name, 25, '...') }}
|
{{ truncate(content.name, 25, '...') }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user