dragndrop + bulk upload + bulk delete + shit/cmd/ctrl multi file selection + player default view if no fallback playlist
This commit is contained in:
parent
fab5f8f49e
commit
5065c9fe54
File diff suppressed because one or more lines are too long
90
data/www/js/dragdrop.js
vendored
Normal file
90
data/www/js/dragdrop.js
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
jQuery(function ($) {
|
||||
|
||||
const main = function () {
|
||||
fileUpload();
|
||||
};
|
||||
|
||||
const fileUpload = function () {
|
||||
$('.btn-super-upload').each(function () {
|
||||
const $button = $(this);
|
||||
const $input = $(this).find('input[type=file]');
|
||||
$input.fileupload({
|
||||
url: $(this).attr('data-route'),
|
||||
dropZone: $('body'),
|
||||
formData: {},
|
||||
dataType: 'json',
|
||||
add: function (e, data) {
|
||||
const $alert = $('.alert-danger');
|
||||
const $bar = $button.find('.progress-bar');
|
||||
$bar.css('width', '0%');
|
||||
$button.addClass('uploading').removeClass('btn-info btn-super-upload').addClass('btn-naked btn-super-upload-busy');
|
||||
$alert.addClass('hidden').text('');
|
||||
data.submit();
|
||||
},
|
||||
progressall: function (e, data) {
|
||||
const progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
const $bar = $button.find('.progress-bar');
|
||||
const $percent = $button.find('.percent');
|
||||
$bar.css('width', progress + '%');
|
||||
$percent.text(progress + '%');
|
||||
},
|
||||
always: function (e, data) {
|
||||
const response = data._response.jqXHR;
|
||||
$button.removeClass('uploading').removeClass('btn-naked btn-super-upload-busy').addClass('btn-info btn-super-upload');
|
||||
if (response.status != 200) {
|
||||
const $alert = $('.alert-danger').removeClass('hidden');
|
||||
if (response.status == 413) {
|
||||
$alert.text(l.js_common_http_error_413);
|
||||
} else {
|
||||
$alert.text(l.js_common_http_error_occured.replace('%code%', response.status));
|
||||
}
|
||||
} else {
|
||||
document.location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
main();
|
||||
|
||||
$(document).on('click', '.btn-super-upload', function (e) {
|
||||
$(this).find('input[type=file]')[0].click();
|
||||
});
|
||||
|
||||
$(document).on('dragenter', 'body', function () {
|
||||
$(this).addClass('dragenter');
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('dragover', 'body', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$(this).addClass('dragover');
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('dragleave', 'body', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$(this).removeClass('dragenter dragover');
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('drop', 'body', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$(this).removeClass('dragenter dragover');
|
||||
|
||||
const $dz = $('.dropzone:visible');
|
||||
|
||||
if (isset($dz.attr('data-handle-drop') && $dz.attr('data-handle-drop') === '1')) {
|
||||
const $inputTarget = $("#" + $dz.attr('data-related-input'));
|
||||
const droppedFiles = e.originalEvent.dataTransfer.files;
|
||||
$inputTarget.prop("files", droppedFiles).trigger('change');
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,6 +2,7 @@ let onPickedElement = function (element) {
|
||||
};
|
||||
|
||||
jQuery(function ($) {
|
||||
let lastClicked = null;
|
||||
|
||||
const explrSidebarOpenFromFolder = function (folderId) {
|
||||
const $leaf = $('.li-explr-folder-' + folderId);
|
||||
@ -70,29 +71,59 @@ jQuery(function ($) {
|
||||
initExplr();
|
||||
};
|
||||
|
||||
const selectEpxlrLink = function ($link) {
|
||||
$('a.explr-link').removeClass('highlight-clicked');
|
||||
$('a.explr-link').parent().removeClass('highlight-clicked');
|
||||
$('body').removeClass('explr-selection explr-selection-actionable explr-selection-entity explr-selection-folder');
|
||||
const updateBodyClasses = function () {
|
||||
const $selectedLinks = $('a.explr-link.highlight-clicked');
|
||||
const isMultiSelect = $selectedLinks.length > 1;
|
||||
const isSingleSelect = $selectedLinks.length === 1;
|
||||
const $link = $selectedLinks.last();
|
||||
|
||||
if ($link.hasClass('explr-item-selectable')) {
|
||||
$link.addClass('highlight-clicked');
|
||||
$link.parent().addClass('highlight-clicked');
|
||||
$('body').addClass('explr-selection');
|
||||
if ($link.hasClass('explr-item-actionable')) {
|
||||
$('body').addClass('explr-selection-actionable');
|
||||
}
|
||||
if ($link.hasClass('explr-item-entity')) {
|
||||
$('body').addClass('explr-selection-entity');
|
||||
}
|
||||
if ($link.hasClass('explr-item-folder')) {
|
||||
$('body').addClass('explr-selection-folder');
|
||||
}
|
||||
}
|
||||
$('body')
|
||||
.toggleClass('explr-selection', isSingleSelect)
|
||||
.toggleClass('explr-selection-actionable', isSingleSelect && $link.hasClass('explr-item-actionable'))
|
||||
.toggleClass('explr-selection-entity', isSingleSelect && $link.hasClass('explr-item-entity'))
|
||||
.toggleClass('explr-selection-folder', isSingleSelect && $link.hasClass('explr-item-folder'))
|
||||
.toggleClass('explr-multiselection', isMultiSelect)
|
||||
.toggleClass('explr-multiselection-actionable', isMultiSelect && $selectedLinks.hasClass('explr-item-actionable'))
|
||||
.toggleClass('explr-multiselection-entity', isMultiSelect && $selectedLinks.hasClass('explr-item-entity'))
|
||||
.toggleClass('explr-multiselection-folder', isMultiSelect && $selectedLinks.hasClass('explr-item-folder'));
|
||||
};
|
||||
|
||||
const selectEpxlrLink = function ($link) {
|
||||
$link.addClass('highlight-clicked');
|
||||
$link.parent().addClass('highlight-clicked');
|
||||
updateBodyClasses();
|
||||
};
|
||||
|
||||
const clearSelection = function () {
|
||||
$('a.explr-link').removeClass('highlight-clicked');
|
||||
$('a.explr-link').parent().removeClass('highlight-clicked');
|
||||
$('body').removeClass('explr-selection explr-selection-actionable explr-selection-entity explr-selection-folder explr-multiselection explr-multiselection-actionable explr-multiselection-entity explr-multiselection-folder');
|
||||
};
|
||||
|
||||
const handleShiftClick = function ($link) {
|
||||
const $links = $('li > a.explr-link');
|
||||
const start = $links.index(lastClicked);
|
||||
const end = $links.index($link);
|
||||
const [from, to] = start < end ? [start, end] : [end, start];
|
||||
$links.slice(from, to + 1).each(function () {
|
||||
selectEpxlrLink($(this));
|
||||
});
|
||||
updateBodyClasses();
|
||||
};
|
||||
|
||||
const handleCmdCtrlClick = function ($link) {
|
||||
if ($link.hasClass('highlight-clicked')) {
|
||||
$link.removeClass('highlight-clicked');
|
||||
$link.parent().removeClass('highlight-clicked');
|
||||
} else {
|
||||
selectEpxlrLink($link);
|
||||
}
|
||||
updateBodyClasses();
|
||||
};
|
||||
|
||||
|
||||
const getExplrSelection = function () {
|
||||
return $('.explr-dirview .highlight-clicked');
|
||||
return $('.explr-dirview li.highlight-clicked');
|
||||
};
|
||||
|
||||
const renameExplrItem = function ($item) {
|
||||
@ -116,9 +147,9 @@ jQuery(function ($) {
|
||||
let route;
|
||||
|
||||
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');
|
||||
} else {
|
||||
route = $(this).attr('data-entity-route') + '?id=' + $item.attr('data-id');
|
||||
route = $(this).attr('data-entity-route') + '&id=' + $item.attr('data-id');
|
||||
}
|
||||
|
||||
if (confirm(l.js_common_are_you_sure)) {
|
||||
@ -126,18 +157,44 @@ jQuery(function ($) {
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.explr-items-delete', function () {
|
||||
const $items = getExplrSelection();
|
||||
const folder_ids = [], entity_ids = [];
|
||||
|
||||
$items.each(function() {
|
||||
const is_folder = $(this).attr('data-folder') === '1';
|
||||
const id = $(this).attr('data-id');
|
||||
|
||||
if (is_folder) {
|
||||
folder_ids.push(id);
|
||||
} else {
|
||||
entity_ids.push(id);
|
||||
}
|
||||
});
|
||||
|
||||
if (confirm(l.js_common_are_you_sure)) {
|
||||
document.location.href = $(this).attr('data-route')
|
||||
+ '&folder_ids=' + folder_ids.join(',')
|
||||
+ '&entity_ids=' + entity_ids.join(',')
|
||||
;
|
||||
}
|
||||
});
|
||||
|
||||
$(document).keyup(function (e) {
|
||||
const $selectedLink = $('.explr-item-selectable.highlight-clicked');
|
||||
const $selectedLi = $selectedLink.parents('li:eq(0)');
|
||||
|
||||
if (e.key === "Escape") {
|
||||
$('.dirview .new-folder').addClass('hidden');
|
||||
$('.dirview .renaming').removeClass('renaming');
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
if ($('.renaming input:focus').length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === "Escape") {
|
||||
$('.dirview .new-folder').addClass('hidden');
|
||||
$('.dirview .renaming').removeClass('renaming');
|
||||
} else if (e.code === "Space") {
|
||||
if (e.code === "Space") {
|
||||
renameExplrItem($selectedLi);
|
||||
} else if ($selectedLink.length) {
|
||||
const $prevLi = $selectedLi.prev('li:visible');
|
||||
@ -158,6 +215,9 @@ jQuery(function ($) {
|
||||
if ($('.explr-item-delete:visible').length) {
|
||||
$('.explr-item-delete:visible').click();
|
||||
}
|
||||
if ($('.explr-items-delete:visible').length) {
|
||||
$('.explr-items-delete:visible').click();
|
||||
}
|
||||
}
|
||||
} else if (e.key.indexOf('Arrow') === 0) {
|
||||
selectEpxlrLink($('.explr-dirview li:visible:eq(0)').find('.explr-link'));
|
||||
@ -167,7 +227,17 @@ jQuery(function ($) {
|
||||
// Explorer item selection
|
||||
$(document).on('click', 'a.explr-link', function (event) {
|
||||
event.preventDefault();
|
||||
selectEpxlrLink($(this));
|
||||
const $link = $(this);
|
||||
|
||||
if (event.shiftKey && lastClicked) {
|
||||
handleShiftClick($link);
|
||||
} else if (event.metaKey || event.ctrlKey) {
|
||||
handleCmdCtrlClick($link);
|
||||
} else {
|
||||
clearSelection();
|
||||
selectEpxlrLink($link);
|
||||
}
|
||||
lastClicked = $link;
|
||||
});
|
||||
$(document).on('click', 'a.explr-pick-element', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
const $modalsRoot = $('.modals');
|
||||
const $pickersRoot = $('.pickers');
|
||||
|
||||
const isset = function (obj){
|
||||
return obj !== undefined && obj !== null;
|
||||
};
|
||||
|
||||
const showModal = function (modalClass) {
|
||||
$modalsRoot.removeClass('hidden').find('form').trigger('reset');
|
||||
$modalsRoot.find('.modal').addClass('hidden');
|
||||
|
||||
1477
data/www/js/lib/jquery-fileupload.js
vendored
Normal file
1477
data/www/js/lib/jquery-fileupload.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@ jQuery(document).ready(function ($) {
|
||||
$('.modal-variable-edit input:visible:eq(0)').focus().select();
|
||||
$('#variable-edit-name').val(variable.name);
|
||||
$('#variable-edit-description').html(variable.description);
|
||||
$('#variable-edit-description-edition').html(variable.description_edition).toggleClass('hidden', variable.description_edition == '');
|
||||
$('#variable-edit-description-edition').html(variable.description_edition).toggleClass('hidden', variable.description_edition === '');
|
||||
$('#variable-edit-value').val(variable.value);
|
||||
$('#variable-edit-id').val(variable.id);
|
||||
});
|
||||
|
||||
@ -77,7 +77,6 @@ main {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.explr-selection-actions + .btn,
|
||||
.btn:first-child {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ button,
|
||||
letter-spacing: -0.5px;
|
||||
margin-top: -$shadowOffset;
|
||||
min-width: 38px;
|
||||
min-height: 34px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
64
data/www/scss/components/_dragdrop.scss
Normal file
64
data/www/scss/components/_dragdrop.scss
Normal file
@ -0,0 +1,64 @@
|
||||
body.dragover {
|
||||
.shakeondrag {
|
||||
animation: shakednd .1s linear alternate infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-super-upload-busy,
|
||||
.btn-super-upload {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
|
||||
&.btn-super-upload-busy {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.unprogress {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.progress {
|
||||
display: none;
|
||||
width: 200px;
|
||||
height: 10px;
|
||||
background: #666;
|
||||
border-radius: $baseRadius;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.progress-bar {
|
||||
border-radius: $baseRadius;
|
||||
background-color: $seaBlue;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.percent {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 2px;
|
||||
font-size: 15px;
|
||||
color: white;
|
||||
text-shadow: 0 0 2px black;
|
||||
}
|
||||
}
|
||||
|
||||
&.uploading {
|
||||
.progress {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.unprogress {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,6 +73,7 @@ ul.explr-tree {
|
||||
}
|
||||
}
|
||||
|
||||
.explr-multiselection-actions,
|
||||
.explr-selection-actions {
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
@ -107,6 +108,28 @@ body.explr-selection-actionable {
|
||||
}
|
||||
}
|
||||
|
||||
body.explr-multiselection-actionable {
|
||||
.explr-multiselection-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&.explr-multiselection-folder {
|
||||
.explr-multiselection-actions {
|
||||
button.explr-multiselection-folder {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.explr-multiselection-entity {
|
||||
.explr-multiselection-actions {
|
||||
button.explr-multiselection-entity {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ul.explr-dirview {
|
||||
display: flex;
|
||||
@ -119,9 +142,9 @@ ul.explr-dirview {
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin: 18px;
|
||||
min-width: 90px;
|
||||
min-height: 104px;
|
||||
margin: 10px 10px;
|
||||
min-width: 100px;
|
||||
min-height: 130px;
|
||||
padding-top: 5px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: $baseRadius;
|
||||
@ -162,6 +185,35 @@ ul.explr-dirview {
|
||||
min-width: 84px;
|
||||
position: relative;
|
||||
|
||||
&.with-thumbnail {
|
||||
|
||||
.img-holder {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: #070707;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
margin-bottom: 12px;
|
||||
|
||||
img {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: 0;
|
||||
text-shadow: 0 .5px .5px #777;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 64px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
@ -10,3 +10,12 @@
|
||||
left: 27px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shakednd {
|
||||
0% {
|
||||
transform: rotate(-2deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(2deg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
@import 'components/breadcrumb';
|
||||
@import 'components/modals';
|
||||
@import 'components/toast';
|
||||
@import 'components/dragdrop';
|
||||
|
||||
// Legacy
|
||||
@import 'components/panes';
|
||||
|
||||
@ -1,10 +1,30 @@
|
||||
|
||||
.view-content-list main .main-container {
|
||||
.view-content-list {
|
||||
main .main-container {
|
||||
.page-content {
|
||||
.inner {
|
||||
padding-bottom: 10px;
|
||||
|
||||
.dropzone {
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-object-input {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.dragover {
|
||||
main .main-container .inner .dropzone {
|
||||
border-radius: $baseRadius;
|
||||
background: rgba($white, .1);
|
||||
border: 1px dashed rgba($white, .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view-content-edit main .main-container {
|
||||
|
||||
@ -244,6 +244,8 @@
|
||||
"common_copied": "Element copied in clipboard!",
|
||||
"common_host_placeholder": "raspberrypi.local or 192.168.1.85",
|
||||
"common_reachable_at": "Host",
|
||||
"common_http_error_occured": "Error %code% occured",
|
||||
"common_http_error_413": "Files are too large",
|
||||
"logout": "Logout",
|
||||
"login_error_not_found": "Bad credentials",
|
||||
"login_error_bad_credentials": "Bad credentials",
|
||||
@ -300,5 +302,6 @@
|
||||
"sysinfo_network_interface": "Network Interface",
|
||||
"sysinfo_mac_address": "MAC Address",
|
||||
"sysinfo_ip_address": "IP Address",
|
||||
"player_default_welcome_message": "To manage this player, go to a browser at"
|
||||
"player_default_welcome_message": "To manage this player, go to a browser at",
|
||||
"player_noplaylist_welcome_message": "No playlist is configured by default, go to a browser at"
|
||||
}
|
||||
|
||||
@ -245,6 +245,8 @@
|
||||
"common_copied": "¡Elemento copiado!",
|
||||
"common_host_placeholder": "raspberrypi.local o 192.168.1.85",
|
||||
"common_reachable_at": "Host",
|
||||
"common_http_error_occured": "Se ha producido un error %code%",
|
||||
"common_http_error_413": "Los archivos son demasiado grandes",
|
||||
"logout": "Cerrar sesión",
|
||||
"login_error_not_found": "Credenciales incorrectas",
|
||||
"login_error_bad_credentials": "Credenciales incorrectas",
|
||||
@ -301,5 +303,6 @@
|
||||
"sysinfo_network_interface": "Interfaz de red",
|
||||
"sysinfo_mac_address": "Dirección MAC",
|
||||
"sysinfo_ip_address": "Dirección IP",
|
||||
"player_default_welcome_message": "Para gestionar este reproductor, ve a un navegador en"
|
||||
"player_default_welcome_message": "Para gestionar este reproductor, ve a un navegador en",
|
||||
"player_noplaylist_welcome_message": "No hay ninguna playlist configurada de forma predeterminada, vaya a un navegador en"
|
||||
}
|
||||
|
||||
@ -246,6 +246,8 @@
|
||||
"common_copied": "Element copié !",
|
||||
"common_host_placeholder": "raspberrypi.local ou 192.168.1.85",
|
||||
"common_reachable_at": "Hôte",
|
||||
"common_http_error_occured": "Une erreur %code% est apparue",
|
||||
"common_http_error_413": "Les fichiers sont trop volumineux",
|
||||
"logout": "Déconnexion",
|
||||
"login_error_not_found": "Identifiants invalides",
|
||||
"login_error_bad_credentials": "Identifiants invalides",
|
||||
@ -302,5 +304,6 @@
|
||||
"sysinfo_network_interface": "Interface Réseau",
|
||||
"sysinfo_mac_address": "Addresse MAC",
|
||||
"sysinfo_ip_address": "Addresse IP",
|
||||
"player_default_welcome_message": "Pour gérer ce lecteur, allez sur un navigateur à l'adresse"
|
||||
"player_default_welcome_message": "Pour gérer ce lecteur, allez sur un navigateur à l'adresse",
|
||||
"player_noplaylist_welcome_message": "Aucune playlist n'est configurée par défaut, allez sur un navigateur à l'adresse"
|
||||
}
|
||||
|
||||
@ -245,6 +245,8 @@
|
||||
"common_copied": "Elemento copiato!",
|
||||
"common_host_placeholder": "raspberrypi.local o 192.168.1.85",
|
||||
"common_reachable_at": "Host",
|
||||
"common_http_error_occured": "Si è verificato un errore %code%",
|
||||
"common_http_error_413": "I file sono troppo grandi",
|
||||
"logout": "Logout",
|
||||
"login_error_not_found": "Credenziali errate",
|
||||
"login_error_bad_credentials": "Credenziali errate",
|
||||
@ -301,5 +303,6 @@
|
||||
"sysinfo_network_interface": "interfaccia di rete",
|
||||
"sysinfo_mac_address": "Indirizzo MAC",
|
||||
"sysinfo_ip_address": "indirizzo IP",
|
||||
"player_default_welcome_message": "Per gestire questo lettore, vai al browser all'indirizzo"
|
||||
"player_default_welcome_message": "Per gestire questo lettore, vai al browser all'indirizzo",
|
||||
"player_noplaylist_welcome_message": "Nessuna playlist è configurata per impostazione predefinita, vai su un browser all'indirizzo"
|
||||
}
|
||||
@ -28,11 +28,25 @@ class ContentController(ObController):
|
||||
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'])
|
||||
self._app.add_url_rule('/slideshow/content/upload-bulk', 'slideshow_content_upload_bulk', self._auth(self.slideshow_content_upload_bulk), methods=['POST'])
|
||||
self._app.add_url_rule('/slideshow/content/delete-bulk-explr', 'slideshow_content_delete_bulk_explr', self._auth(self.slideshow_content_delete_bulk_explr), methods=['GET'])
|
||||
|
||||
def get_working_folder(self):
|
||||
working_folder_path = request.args.get('path', None)
|
||||
working_folder = None
|
||||
|
||||
if working_folder_path:
|
||||
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.CONTENT)
|
||||
|
||||
if not working_folder:
|
||||
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)
|
||||
|
||||
return working_folder_path, working_folder
|
||||
|
||||
def slideshow_content_list(self):
|
||||
self._model_store.variable().update_by_name('last_pillmenu_slideshow', 'slideshow_content_list')
|
||||
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_path, working_folder = self.get_working_folder()
|
||||
slides_with_content = self._model_store.slide().get_all_indexed(attribute='content_id', multiple=True)
|
||||
|
||||
return render_template(
|
||||
@ -48,8 +62,7 @@ class ContentController(ObController):
|
||||
)
|
||||
|
||||
def slideshow_content_add(self):
|
||||
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_path, working_folder = self.get_working_folder()
|
||||
|
||||
self._model_store.content().add_form_raw(
|
||||
name=request.form['name'],
|
||||
@ -60,7 +73,24 @@ class ContentController(ObController):
|
||||
folder_id=working_folder.id if working_folder else None
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
def slideshow_content_upload_bulk(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
file = request.files['object']
|
||||
type = ContentType.guess_content_type_file(file)
|
||||
name = file.filename.rsplit('.', 1)[0]
|
||||
|
||||
self._model_store.content().add_form_raw(
|
||||
name=name,
|
||||
type=type,
|
||||
request_files=request.files,
|
||||
upload_dir=self._app.config['UPLOAD_FOLDER'],
|
||||
folder_id=working_folder.id if working_folder else None
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
def slideshow_content_edit(self, content_id: int = 0):
|
||||
content = self._model_store.content().get(content_id)
|
||||
@ -68,8 +98,7 @@ class ContentController(ObController):
|
||||
if not content:
|
||||
return abort(404)
|
||||
|
||||
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_path, working_folder = self.get_working_folder()
|
||||
|
||||
return render_template(
|
||||
'slideshow/contents/edit.jinja.html',
|
||||
@ -80,10 +109,11 @@ class ContentController(ObController):
|
||||
)
|
||||
|
||||
def slideshow_content_save(self, content_id: int = 0):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
content = self._model_store.content().get(content_id)
|
||||
|
||||
if not content:
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
self._model_store.content().update_form(
|
||||
id=content.id,
|
||||
@ -95,27 +125,25 @@ class ContentController(ObController):
|
||||
return redirect(url_for('slideshow_content_edit', content_id=content_id, saved=1))
|
||||
|
||||
def slideshow_content_delete(self):
|
||||
content = self._model_store.content().get(request.args.get('id'))
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
error_tuple = self.delete_content_by_id(request.args.get('id'))
|
||||
route_args = {
|
||||
"path": working_folder_path,
|
||||
}
|
||||
|
||||
if not content:
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
if error_tuple:
|
||||
route_args[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
slide_counter = self._model_store.slide().count_slides_for_content(content.id)
|
||||
|
||||
if slide_counter > 0:
|
||||
return redirect(url_for('slideshow_content_list', referenced_in_slide_error=True))
|
||||
|
||||
self._model_store.content().delete(content.id)
|
||||
self._post_update()
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', **route_args))
|
||||
|
||||
def slideshow_content_rename(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
self._model_store.content().update_form(
|
||||
id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
def slideshow_content_cd(self):
|
||||
path = request.args.get('path')
|
||||
@ -140,46 +168,46 @@ class ContentController(ObController):
|
||||
return redirect(url_for('slideshow_content_list', path=path))
|
||||
|
||||
def slideshow_content_folder_add(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
self._model_store.folder().add_folder(
|
||||
entity=FolderEntity.CONTENT,
|
||||
name=request.form['name'],
|
||||
working_folder_path=request.form['working_folder_path'],
|
||||
working_folder_path=working_folder_path
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
def slideshow_content_folder_rename(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
self._model_store.folder().rename_folder(
|
||||
folder_id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
def slideshow_content_folder_move(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
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 'is_folder' in request.form and request.form['is_folder'] == '1' else False,
|
||||
)
|
||||
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
return redirect(url_for('slideshow_content_list', path=working_folder_path))
|
||||
|
||||
def slideshow_content_folder_delete(self):
|
||||
folder = self._model_store.folder().get(request.args.get('id'))
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
error_tuple = self.delete_folder_by_id(request.args.get('id'))
|
||||
route_args = {
|
||||
"path": working_folder_path,
|
||||
}
|
||||
|
||||
if not folder:
|
||||
return redirect(url_for('slideshow_content_list'))
|
||||
if error_tuple:
|
||||
route_args[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
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'))
|
||||
return redirect(url_for('slideshow_content_list', **route_args))
|
||||
|
||||
def slideshow_content_show(self, content_id: int = 0):
|
||||
content = self._model_store.content().get(content_id)
|
||||
@ -201,6 +229,57 @@ class ContentController(ObController):
|
||||
|
||||
return redirect(location)
|
||||
|
||||
def slideshow_content_delete_bulk_explr(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
entity_ids = request.args.get('entity_ids', '').split(',')
|
||||
folder_ids = request.args.get('folder_ids', '').split(',')
|
||||
route_args_dict = {"path": working_folder_path}
|
||||
|
||||
for id in entity_ids:
|
||||
if id:
|
||||
error_tuple = self.delete_content_by_id(id)
|
||||
|
||||
if error_tuple:
|
||||
route_args_dict[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
for id in folder_ids:
|
||||
if id:
|
||||
error_tuple = self.delete_folder_by_id(id)
|
||||
|
||||
if error_tuple:
|
||||
route_args_dict[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
return redirect(url_for('slideshow_content_list', **route_args_dict))
|
||||
|
||||
def delete_content_by_id(self, id):
|
||||
content = self._model_store.content().get(id)
|
||||
|
||||
if not content:
|
||||
return None
|
||||
|
||||
if self._model_store.slide().count_slides_for_content(content.id) > 0:
|
||||
return 'referenced_in_slide_error', content.name
|
||||
|
||||
self._model_store.content().delete(content.id)
|
||||
self._post_update()
|
||||
return None
|
||||
|
||||
def delete_folder_by_id(self, id):
|
||||
folder = self._model_store.folder().get(id)
|
||||
|
||||
if not folder:
|
||||
return None
|
||||
|
||||
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 'folder_not_empty_error', folder.name
|
||||
|
||||
self._model_store.folder().delete(id=folder.id)
|
||||
self._post_update()
|
||||
return None
|
||||
|
||||
def _post_update(self):
|
||||
pass
|
||||
|
||||
|
||||
@ -31,11 +31,24 @@ class FleetNodePlayerController(ObController):
|
||||
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'])
|
||||
self._app.add_url_rule('/fleet/node-player/delete-bulk-explr', 'fleet_node_player_delete_bulk_explr', self._auth(self.fleet_node_player_delete_bulk_explr), methods=['GET'])
|
||||
|
||||
def get_working_folder(self):
|
||||
working_folder_path = request.args.get('path', None)
|
||||
working_folder = None
|
||||
|
||||
if working_folder_path:
|
||||
working_folder = self._model_store.folder().get_one_by_path(path=working_folder_path, entity=FolderEntity.NODE_PLAYER)
|
||||
|
||||
if not working_folder:
|
||||
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 working_folder_path, working_folder
|
||||
|
||||
def fleet_node_player_list(self):
|
||||
self._model_store.variable().update_by_name('last_pillmenu_fleet', 'fleet_node_player_list')
|
||||
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)
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
return render_template(
|
||||
'fleet/node-players/list.jinja.html',
|
||||
@ -50,8 +63,7 @@ class FleetNodePlayerController(ObController):
|
||||
)
|
||||
|
||||
def fleet_node_player_add(self):
|
||||
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)
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
self._model_store.node_player().add_form(
|
||||
NodePlayer(
|
||||
@ -62,7 +74,7 @@ class FleetNodePlayerController(ObController):
|
||||
)
|
||||
)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
def fleet_node_player_edit(self, node_player_id: int = 0):
|
||||
node_player = self._model_store.node_player().get(node_player_id)
|
||||
@ -70,8 +82,7 @@ class FleetNodePlayerController(ObController):
|
||||
if not node_player:
|
||||
return abort(404)
|
||||
|
||||
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)
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
return render_template(
|
||||
'fleet/node-players/edit.jinja.html',
|
||||
@ -84,9 +95,10 @@ class FleetNodePlayerController(ObController):
|
||||
def fleet_node_player_save(self, node_player_id: int = 0):
|
||||
node_player_id = request.form['id'] if 'id' in request.form else node_player_id
|
||||
node_player = self._model_store.node_player().get(node_player_id)
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
if not node_player:
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
self._model_store.node_player().update_form(
|
||||
id=node_player.id,
|
||||
@ -97,25 +109,28 @@ class FleetNodePlayerController(ObController):
|
||||
self._post_update()
|
||||
|
||||
# return redirect(url_for('fleet_node_player_edit', node_player_id=node_player_id, saved=1))
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
def fleet_node_player_delete(self):
|
||||
node_player = self._model_store.node_player().get(request.args.get('id'))
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
error_tuple = self.delete_node_player_by_id(request.args.get('id'))
|
||||
route_args = {
|
||||
"path": working_folder_path,
|
||||
}
|
||||
|
||||
if not node_player:
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
if error_tuple:
|
||||
route_args[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
self._model_store.node_player().delete(node_player.id)
|
||||
self._post_update()
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', **route_args))
|
||||
|
||||
def fleet_node_player_rename(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
self._model_store.node_player().update_form(
|
||||
id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
def fleet_node_player_cd(self):
|
||||
path = request.args.get('path')
|
||||
@ -140,46 +155,96 @@ class FleetNodePlayerController(ObController):
|
||||
return redirect(url_for('fleet_node_player_list', path=path))
|
||||
|
||||
def fleet_node_player_folder_add(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
self._model_store.folder().add_folder(
|
||||
entity=FolderEntity.NODE_PLAYER,
|
||||
name=request.form['name'],
|
||||
working_folder_path=request.form['working_folder_path'],
|
||||
working_folder_path=working_folder_path
|
||||
)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
def fleet_node_player_folder_rename(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
self._model_store.folder().rename_folder(
|
||||
folder_id=request.form['id'],
|
||||
name=request.form['name'],
|
||||
)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
def fleet_node_player_folder_move(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
|
||||
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 'is_folder' in request.form and request.form['is_folder'] == '1' else False,
|
||||
)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return redirect(url_for('fleet_node_player_list', path=working_folder_path))
|
||||
|
||||
def fleet_node_player_folder_delete(self):
|
||||
folder = self._model_store.folder().get(request.args.get('id'))
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
error_tuple = self.delete_folder_by_id(request.args.get('id'))
|
||||
route_args = {
|
||||
"path": working_folder_path,
|
||||
}
|
||||
|
||||
if error_tuple:
|
||||
route_args[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
return redirect(url_for('fleet_node_player_list', **route_args))
|
||||
|
||||
def fleet_node_player_delete_bulk_explr(self):
|
||||
working_folder_path, working_folder = self.get_working_folder()
|
||||
entity_ids = request.args.get('entity_ids', '').split(',')
|
||||
folder_ids = request.args.get('folder_ids', '').split(',')
|
||||
route_args_dict = {"path": working_folder_path}
|
||||
|
||||
for id in entity_ids:
|
||||
if id:
|
||||
error_tuple = self.delete_node_player_by_id(id)
|
||||
|
||||
if error_tuple:
|
||||
route_args_dict[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
for id in folder_ids:
|
||||
if id:
|
||||
error_tuple = self.delete_folder_by_id(id)
|
||||
|
||||
if error_tuple:
|
||||
route_args_dict[error_tuple[0]] = error_tuple[1]
|
||||
|
||||
return redirect(url_for('fleet_node_player_list', **route_args_dict))
|
||||
|
||||
def delete_node_player_by_id(self, id):
|
||||
node_player = self._model_store.node_player().get(id)
|
||||
|
||||
if not node_player:
|
||||
return None
|
||||
|
||||
self._model_store.node_player().delete(node_player.id)
|
||||
self._post_update()
|
||||
return None
|
||||
|
||||
def delete_folder_by_id(self, id):
|
||||
folder = self._model_store.folder().get(id)
|
||||
|
||||
if not folder:
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return None
|
||||
|
||||
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))
|
||||
return 'folder_not_empty_error', folder.name
|
||||
|
||||
self._model_store.folder().delete(id=folder.id)
|
||||
|
||||
return redirect(url_for('fleet_node_player_list'))
|
||||
return None
|
||||
|
||||
def _post_update(self):
|
||||
pass
|
||||
|
||||
@ -40,7 +40,7 @@ class PlayerController(ObController):
|
||||
try:
|
||||
items = self._get_playlist(playlist_id=playlist_id, preview_content_id=preview_content_id)
|
||||
except NoFallbackPlaylistException:
|
||||
abort(404)
|
||||
return redirect(url_for('player_default', noplaylist=1))
|
||||
|
||||
intro_slide_duration = 0 if items['preview_mode'] else int(request.args.get('intro', self._model_store.variable().get_one_by_name('intro_slide_duration').eval()))
|
||||
animation_enabled = bool(request.args.get('animation', int(self._model_store.variable().get_one_by_name('slide_animation_enabled').eval())))
|
||||
@ -64,7 +64,8 @@ class PlayerController(ObController):
|
||||
return render_template(
|
||||
'player/default.jinja.html',
|
||||
interfaces=[iface['ip_address'] for iface in get_network_interfaces()],
|
||||
time_with_seconds=self._model_store.variable().get_one_by_name('default_slide_time_with_seconds')
|
||||
time_with_seconds=self._model_store.variable().get_one_by_name('default_slide_time_with_seconds'),
|
||||
noplaylist=request.args.get('noplaylist', '0') == '1'
|
||||
)
|
||||
|
||||
def player_playlist(self, playlist_slug_or_id: str = ''):
|
||||
|
||||
@ -73,9 +73,10 @@ class DatabaseManager:
|
||||
sanitized_params.append(param)
|
||||
|
||||
try:
|
||||
with self._conn:
|
||||
self._conn.execute('BEGIN')
|
||||
cur = self._conn.cursor()
|
||||
cur.execute(query, tuple(sanitized_params))
|
||||
self._conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
if not silent_errors:
|
||||
logging.error("SQL query execution error while writing '{}': {}".format(query, e))
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import mimetypes
|
||||
|
||||
from enum import Enum
|
||||
from typing import Union
|
||||
from typing import Union, List, Optional
|
||||
|
||||
from src.util.utils import str_to_enum
|
||||
|
||||
|
||||
class ContentInputType(Enum):
|
||||
|
||||
UPLOAD = 'upload'
|
||||
@ -23,6 +26,25 @@ class ContentType(Enum):
|
||||
YOUTUBE = 'youtube'
|
||||
VIDEO = 'video'
|
||||
|
||||
@staticmethod
|
||||
def guess_content_type_file(file):
|
||||
mime_type, _ = mimetypes.guess_type(file.filename)
|
||||
|
||||
if mime_type in [
|
||||
'image/gif',
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/webp',
|
||||
'image/jpg'
|
||||
]:
|
||||
return ContentType.PICTURE
|
||||
elif mime_type in [
|
||||
'video/mp4'
|
||||
]:
|
||||
return ContentType.VIDEO
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_input(value: Enum) -> ContentInputType:
|
||||
if value == ContentType.PICTURE:
|
||||
|
||||
@ -44,8 +44,16 @@
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn explr-item-delete explr-selection-entity explr-selection-folder btn-danger-alt"
|
||||
data-folder-route="{{ url_for('fleet_node_player_folder_delete') }}"
|
||||
data-entity-route="{{ url_for('fleet_node_player_delete') }}">
|
||||
data-folder-route="{{ url_for('fleet_node_player_folder_delete') }}?path={{ working_folder_path }}"
|
||||
data-entity-route="{{ url_for('fleet_node_player_delete') }}?path={{ working_folder_path }}">
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="explr-multiselection-actions">
|
||||
<button type="button"
|
||||
class="btn explr-items-delete explr-multiselection-entity explr-multiselection-folder btn-danger-alt"
|
||||
data-route="{{ url_for('fleet_node_player_delete_bulk_explr') }}?path={{ working_folder_path }}">
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -114,8 +122,7 @@
|
||||
<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="hidden" name="working_folder_path" value="{{ working_folder_path }}" />
|
||||
<form action="{{ url_for('fleet_node_player_folder_add') }}?path={{ working_folder_path }}" method="POST">
|
||||
<input type="text" name="name" autocomplete="off"/>
|
||||
</form>
|
||||
</a>
|
||||
@ -141,7 +148,7 @@
|
||||
class="explr-link explr-item-selectable explr-item-actionable explr-item-folder">
|
||||
<i class="fa fa-folder"></i>
|
||||
<span>{{ truncate(folder.name, explr_limit_chars, '...') }}</span>
|
||||
<form action="{{ url_for('fleet_node_player_folder_rename') }}" method="POST">
|
||||
<form action="{{ url_for('fleet_node_player_folder_rename') }}?path={{ working_folder_path }}" method="POST">
|
||||
<input type="text" name="name" value="{{ folder.name }}" autocomplete="off"/>
|
||||
<input type="hidden" name="id" value="{{ folder.id }}"/>
|
||||
</form>
|
||||
@ -166,7 +173,7 @@
|
||||
</sub>
|
||||
{% endif %}
|
||||
<span>{{ truncate(node_player.name, explr_limit_chars, '...') }}</span>
|
||||
<form action="{{ url_for('fleet_node_player_rename') }}" method="POST">
|
||||
<form action="{{ url_for('fleet_node_player_rename') }}?path={{ working_folder_path }}" method="POST">
|
||||
<input type="text" name="name" value="{{ node_player.name }}" autocomplete="off"/>
|
||||
<input type="hidden" name="id" value="{{ node_player.id }}"/>
|
||||
</form>
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
<script>
|
||||
const interfaces = {{ json_dumps(interfaces) | safe }};
|
||||
const translation_common_unknown_ipaddr = '{{ l.common_unknown_ipaddr }}';
|
||||
const translation_player_default_welcome_message = '{{ l.player_default_welcome_message }}';
|
||||
const translation_player_default_welcome_message = '{% if noplaylist %}{{ l.player_noplaylist_welcome_message }}{% else %}{{ l.player_default_welcome_message }}{% endif %}';
|
||||
const manage_url_template = '{{ 'http://%ipaddr%:' ~ PORT ~ url_for('manage') }}';
|
||||
|
||||
const setIps = function(ips) {
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
<div class="horizontal">
|
||||
<div class="form-holder">
|
||||
<form class="form" action="{{ url_for('slideshow_content_save', content_id=content.id) }}" method="POST">
|
||||
<form class="form" action="{{ url_for('slideshow_content_save', content_id=content.id) }}?path={{ working_folder_path }}" method="POST">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="content-edit-name">{{ l.slideshow_content_form_label_name }}</label>
|
||||
|
||||
@ -10,10 +10,18 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block add_js %}
|
||||
<script>
|
||||
var l = $.extend(l, {
|
||||
'js_common_http_error_occured': '{{ l.common_http_error_occured }}',
|
||||
'js_common_http_error_413': '{{ l.common_http_error_413 }}'
|
||||
});
|
||||
</script>
|
||||
<script src="{{ STATIC_PREFIX }}js/lib/jquery-explr-1.4.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/explorer.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/slideshow/contents.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/lib/jquery-ui.min.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/lib/jquery-fileupload.js"></script>
|
||||
<script src="{{ STATIC_PREFIX }}js/dragdrop.js"></script>
|
||||
|
||||
{{ HOOK(H_SLIDESHOW_CONTENT_JAVASCRIPT) }}
|
||||
{% endblock %}
|
||||
@ -27,27 +35,47 @@
|
||||
<div class="top-actions">
|
||||
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_START) }}
|
||||
|
||||
<button type="button" class="btn btn-info content-add item-add">
|
||||
<i class="fa fa-file-circle-plus icon-left"></i>
|
||||
{{ l.slideshow_content_button_add }}
|
||||
</button>
|
||||
<button type="button" class="folder-add btn-neutral">
|
||||
<i class="fa fa-folder-plus icon-left"></i>
|
||||
{{ l.common_new_folder }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-info content-add item-add">
|
||||
<i class="fa fa-file-circle-plus icon-left"></i>
|
||||
{{ l.slideshow_content_button_add }}
|
||||
</button>
|
||||
|
||||
<a href="javascript:void(0);" class="btn btn-info btn-super-upload" data-route="{{ url_for('slideshow_content_upload_bulk') }}?path={{ working_folder_path }}">
|
||||
<input type="file" id="content_files" name="object" class="hidden" multiple />
|
||||
<div class="unprogress">
|
||||
<i class="fa fa-bolt"></i>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:50%"></div>
|
||||
<div class="percent">0%</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="explr-selection-actions">
|
||||
<button type="button" class="btn explr-item-edit explr-selection-entity btn-info"
|
||||
data-entity-route="{{ url_for('slideshow_content_edit', content_id='!c!') }}">
|
||||
<i class="fa fa-eye"></i>
|
||||
</button>
|
||||
<button type="button" class="btn explr-item-rename explr-selection-entity explr-selection-folder btn-info">
|
||||
<button type="button"
|
||||
class="btn explr-item-rename explr-selection-entity explr-selection-folder btn-info">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn explr-item-delete explr-selection-entity explr-selection-folder btn-danger-alt"
|
||||
data-folder-route="{{ url_for('slideshow_content_folder_delete') }}"
|
||||
data-entity-route="{{ url_for('slideshow_content_delete') }}">
|
||||
data-folder-route="{{ url_for('slideshow_content_folder_delete') }}?path={{ working_folder_path }}"
|
||||
data-entity-route="{{ url_for('slideshow_content_delete') }}?path={{ working_folder_path }}">
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="explr-multiselection-actions">
|
||||
<button type="button"
|
||||
class="btn explr-items-delete explr-multiselection-entity explr-multiselection-folder btn-danger-alt"
|
||||
data-route="{{ url_for('slideshow_content_delete_bulk_explr') }}?path={{ working_folder_path }}">
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -61,13 +89,13 @@
|
||||
<i class="fa fa-warning icon-left"></i>
|
||||
{{ l.common_folder_not_empty_error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if request.args.get('referenced_in_slide_error') %}
|
||||
{% elif request.args.get('referenced_in_slide_error') %}
|
||||
<div class="alert alert-danger">
|
||||
<i class="fa fa-warning icon-left"></i>
|
||||
{{ l.slideshow_content_referenced_in_slide_error }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-danger hidden"></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="bottom-content">
|
||||
@ -112,12 +140,12 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropzone" data-related-input="content_files">
|
||||
<ul class="explr-dirview">
|
||||
<li class="new-folder hidden">
|
||||
<a href="javascript:void(0);">
|
||||
<i class="fa fa-folder"></i>
|
||||
<form action="{{ url_for('slideshow_content_folder_add') }}" method="POST">
|
||||
<input type="hidden" name="working_folder_path" value="{{ working_folder_path }}" />
|
||||
<form action="{{ url_for('slideshow_content_folder_add') }}?path={{ working_folder_path }}" method="POST">
|
||||
<input type="text" name="name" autocomplete="off"/>
|
||||
</form>
|
||||
</a>
|
||||
@ -143,7 +171,7 @@
|
||||
class="explr-link explr-item-selectable explr-item-actionable explr-item-folder">
|
||||
<i class="fa fa-folder"></i>
|
||||
<span>{{ truncate(folder.name, explr_limit_chars, '...') }}</span>
|
||||
<form action="{{ url_for('slideshow_content_folder_rename') }}" method="POST">
|
||||
<form action="{{ url_for('slideshow_content_folder_rename') }}?path={{ working_folder_path }}" method="POST">
|
||||
<input type="text" name="name" value="{{ folder.name }}" autocomplete="off"/>
|
||||
<input type="hidden" name="id" value="{{ folder.id }}"/>
|
||||
</form>
|
||||
@ -155,14 +183,22 @@
|
||||
{% for content in foldered_contents[working_folder.id|default(None)]|default([]) %}
|
||||
{% set icon = enum_content_type.get_fa_icon(content.type) %}
|
||||
{% set color = enum_content_type.get_color_icon(content.type) %}
|
||||
{% set thumbnail = content.type == enum_content_type.PICTURE %}
|
||||
|
||||
<li class="draggable" data-path="{{ working_folder_path }}" data-id="{{ content.id }}"
|
||||
data-folder="0">
|
||||
<a href="{{ url_for('slideshow_content_edit', content_id=content.id) }}"
|
||||
class="explr-link explr-item-selectable explr-item-actionable explr-item-entity">
|
||||
class="explr-link explr-item-selectable explr-item-actionable explr-item-entity {{ 'with-thumbnail' if thumbnail }}">
|
||||
|
||||
{% if content.type == enum_content_type.PICTURE %}
|
||||
<div class="img-holder">
|
||||
<img src="/{{ content.location }}" alt=""/>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<i class="fa {{ icon }} {{ color }}"></i>
|
||||
<span>{{ truncate(content.name, explr_limit_chars, '...') }}</span>
|
||||
<form action="{{ url_for('slideshow_content_rename') }}" method="POST">
|
||||
<form action="{{ url_for('slideshow_content_rename') }}?path={{ working_folder_path }}" method="POST">
|
||||
<input type="text" name="name" value="{{ content.name }}" autocomplete="off"/>
|
||||
<input type="hidden" name="id" value="{{ content.id }}"/>
|
||||
</form>
|
||||
@ -171,6 +207,7 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modals hidden">
|
||||
<div class="modals-outer">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user