node player groups wip
This commit is contained in:
parent
348682b400
commit
84da36d953
File diff suppressed because one or more lines are too long
@ -1,70 +1,23 @@
|
|||||||
jQuery(document).ready(function ($) {
|
jQuery(document).ready(function ($) {
|
||||||
|
|
||||||
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-group-item:visible').length === 0) {
|
|
||||||
$(this).find('tr.empty-tr').removeClass('hidden');
|
|
||||||
} else {
|
|
||||||
$(this).find('tr.empty-tr').addClass('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const main = function () {
|
const main = function () {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).on('change', '#node-player-group-add-type', function () {
|
|
||||||
const value = $(this).val();
|
|
||||||
const inputType = $(this).find('option').filter(function (i, el) {
|
|
||||||
return $(el).val() === value;
|
|
||||||
}).data('input');
|
|
||||||
|
|
||||||
$('.node-player-group-add-object-input')
|
|
||||||
.addClass('hidden')
|
|
||||||
.prop('disabled', true)
|
|
||||||
.filter('#node-player-group-add-object-input-' + inputType)
|
|
||||||
.removeClass('hidden')
|
|
||||||
.prop('disabled', false)
|
|
||||||
;
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.node-player-group-add', function () {
|
$(document).on('click', '.node-player-group-add', function () {
|
||||||
showModal('modal-node-player-group-add');
|
showModal('modal-node-player-group-add');
|
||||||
$('.modal-node-player-group-add input:eq(0)').focus().select();
|
$('.modal-node-player-group-add input:eq(0)').focus().select();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.node-player-group-edit', function () {
|
$(document).on('click', '.node-player-group-preview', function () {
|
||||||
const nodePlayerGroup = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
|
const $iframe = $('<iframe>', {
|
||||||
showModal('modal-node-player-group-edit');
|
src: $(this).attr('data-url'),
|
||||||
$('.modal-node-player-group-edit input:visible:eq(0)').focus().select();
|
frameborder: 0
|
||||||
$('#node-player-group-edit-name').val(nodePlayerGroup.name);
|
});
|
||||||
$('#node-player-group-edit-playlist-id').val(nodePlayerGroup.playlist_id);
|
|
||||||
$('#node-player-group-edit-id').val(nodePlayerGroup.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.node-player-group-delete', function () {
|
$(this).parents('.preview:eq(0)').append($iframe);
|
||||||
if (confirm(l.js_fleet_node_player_delete_confirmation)) {
|
$(this).remove();
|
||||||
const $tr = $(this).parents('tr:eq(0)');
|
|
||||||
$.ajax({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: '/fleet/node-player-group/delete',
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
data: JSON.stringify({id: getId($(this))}),
|
|
||||||
success: function(data) {
|
|
||||||
$tr.remove();
|
|
||||||
updateTable();
|
|
||||||
},
|
|
||||||
error: function(data) {
|
|
||||||
$('.alert-error').html(data.responseJSON.message).removeClass('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
main();
|
main();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
@import 'pages/logs';
|
@import 'pages/logs';
|
||||||
@import 'pages/node_player';
|
@import 'pages/node_player';
|
||||||
@import 'pages/playlist';
|
@import 'pages/playlist';
|
||||||
|
@import 'pages/player-group';
|
||||||
@import 'pages/slideshow';
|
@import 'pages/slideshow';
|
||||||
@import 'pages/plugins';
|
@import 'pages/plugins';
|
||||||
@import 'pages/settings';
|
@import 'pages/settings';
|
||||||
|
|||||||
152
data/www/scss/pages/_player-group.scss
Normal file
152
data/www/scss/pages/_player-group.scss
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
.view-player-group-list main .main-container {
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.modal-slide {
|
||||||
|
// h2 {
|
||||||
|
// font-size: 20px;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// input {
|
||||||
|
// &[disabled] {
|
||||||
|
// color: #AAA;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// .slide-schedule-group,
|
||||||
|
// .slide-schedule-end-group {
|
||||||
|
// select {
|
||||||
|
// margin-right: 5px;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// select,
|
||||||
|
// input {
|
||||||
|
// font-size: 12px;
|
||||||
|
// max-width: 50%;
|
||||||
|
//
|
||||||
|
// &.datetimepicker {
|
||||||
|
// margin-left: 5px;
|
||||||
|
// padding-left: 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
.bottom-content {
|
||||||
|
.page-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #DDD;
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.playlist-holder {
|
||||||
|
// margin: 20px 20px 20px 10px;
|
||||||
|
// flex: 1;
|
||||||
|
//
|
||||||
|
// .form-holder {
|
||||||
|
// margin: 20px 0 0 0;
|
||||||
|
//
|
||||||
|
// form {
|
||||||
|
// max-width: initial;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// .form-group {
|
||||||
|
// flex-grow: 0;
|
||||||
|
// margin-bottom: 5px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// .preview-holder {
|
||||||
|
// .form-group {
|
||||||
|
// flex-grow: 0;
|
||||||
|
// margin-bottom: 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// h4 {
|
||||||
|
// font-size: 14px;
|
||||||
|
// display: flex;
|
||||||
|
// flex-direction: row;
|
||||||
|
// justify-content: flex-start;
|
||||||
|
// align-items: center;
|
||||||
|
// align-self: stretch;
|
||||||
|
// color: white;
|
||||||
|
// padding-bottom: 10px;
|
||||||
|
// text-decoration: none;
|
||||||
|
//
|
||||||
|
// &.divide {
|
||||||
|
// border-top: 1px solid #222;
|
||||||
|
// margin-top: 20px;
|
||||||
|
// padding-top: 20px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// .qrcode-pic {
|
||||||
|
// margin-top: 10px;
|
||||||
|
//
|
||||||
|
// img {
|
||||||
|
// border: 1px dashed #555;
|
||||||
|
// padding: 5px;
|
||||||
|
// border-radius: $baseRadius;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// .preview {
|
||||||
|
// background: black;
|
||||||
|
// border: 1px solid rgba($white, .3);
|
||||||
|
// border-radius: $baseRadius;
|
||||||
|
// justify-content: center;
|
||||||
|
// align-items: center;
|
||||||
|
// align-self: stretch;
|
||||||
|
// display: flex;
|
||||||
|
// margin: 10px 0 20px 0;
|
||||||
|
// height: 300px;
|
||||||
|
//
|
||||||
|
// iframe {
|
||||||
|
// flex: 1;
|
||||||
|
// align-self: stretch;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//.slides-holder {
|
||||||
|
// align-self: stretch;
|
||||||
|
// border-right: 1px solid #222;
|
||||||
|
// margin: 20px 10px 20px 20px;
|
||||||
|
// padding-right: 20px;
|
||||||
|
// flex: 1.3;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-panel.left-panel {
|
||||||
|
flex: 0.3;
|
||||||
|
max-width: initial;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8,7 +8,10 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
margin: 30px 0 30px 0;
|
margin: 10px 0 0 0;
|
||||||
|
border: 1px dashed #222;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
li.slide-item {
|
li.slide-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -122,8 +122,8 @@
|
|||||||
"fleet_node_player_group_form_add_submit": "Add",
|
"fleet_node_player_group_form_add_submit": "Add",
|
||||||
"fleet_node_player_group_form_edit_title": "Edit Playgroup",
|
"fleet_node_player_group_form_edit_title": "Edit Playgroup",
|
||||||
"fleet_node_player_group_form_edit_submit": "Save",
|
"fleet_node_player_group_form_edit_submit": "Save",
|
||||||
"fleet_node_player_group_form_label_name": "Name",
|
"fleet_node_player_group_form_label_name": "Enter playgroup name",
|
||||||
"fleet_node_player_group_form_label_playlist_id": "Playlist",
|
"fleet_node_player_group_form_label_playlist_id": "Select a playlist to play for this playgroup",
|
||||||
"fleet_node_player_group_form_button_cancel": "Cancel",
|
"fleet_node_player_group_form_button_cancel": "Cancel",
|
||||||
"js_fleet_node_player_group_delete_confirmation": "Are you sure?",
|
"js_fleet_node_player_group_delete_confirmation": "Are you sure?",
|
||||||
"node_player_group_delete_has_node_player": "Playgroup has players, please remove or unassign them before and retry",
|
"node_player_group_delete_has_node_player": "Playgroup has players, please remove or unassign them before and retry",
|
||||||
@ -232,6 +232,7 @@
|
|||||||
"common_new_folder": "New Folder",
|
"common_new_folder": "New Folder",
|
||||||
"common_folder_not_empty_error": "Folder isn't empty, you must delete its content first",
|
"common_folder_not_empty_error": "Folder isn't empty, you must delete its content first",
|
||||||
"common_copied": "Element copied in clipboard!",
|
"common_copied": "Element copied in clipboard!",
|
||||||
|
"common_host_placeholder": "raspberrypi.local or 192.168.1.85",
|
||||||
"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",
|
||||||
|
|||||||
@ -122,8 +122,8 @@
|
|||||||
"fleet_node_player_group_form_add_submit": "Agregar",
|
"fleet_node_player_group_form_add_submit": "Agregar",
|
||||||
"fleet_node_player_group_form_edit_title": "Editar Playgroup",
|
"fleet_node_player_group_form_edit_title": "Editar Playgroup",
|
||||||
"fleet_node_player_group_form_edit_submit": "Guardar",
|
"fleet_node_player_group_form_edit_submit": "Guardar",
|
||||||
"fleet_node_player_group_form_label_name": "Nombre",
|
"fleet_node_player_group_form_label_name": "Ingrese el nombre del playgroup",
|
||||||
"fleet_node_player_group_form_label_playlist_id": "Lista de reproducción",
|
"fleet_node_player_group_form_label_playlist_id": "Selecciona una playlist para reproducir para este playgroup",
|
||||||
"fleet_node_player_group_form_button_cancel": "Cancelar",
|
"fleet_node_player_group_form_button_cancel": "Cancelar",
|
||||||
"js_fleet_node_player_group_delete_confirmation": "¿Estás seguro?",
|
"js_fleet_node_player_group_delete_confirmation": "¿Estás seguro?",
|
||||||
"node_player_group_delete_has_node_player": "El playgroup tiene reproductores, por favor elimínelos o desasígnelos antes y reintente",
|
"node_player_group_delete_has_node_player": "El playgroup tiene reproductores, por favor elimínelos o desasígnelos antes y reintente",
|
||||||
@ -232,6 +232,7 @@
|
|||||||
"common_new_folder": "Nuevo Carpeta",
|
"common_new_folder": "Nuevo Carpeta",
|
||||||
"common_folder_not_empty_error": "La carpeta no está vacía, primero debes eliminar su contenido",
|
"common_folder_not_empty_error": "La carpeta no está vacía, primero debes eliminar su contenido",
|
||||||
"common_copied": "¡Elemento copiado!",
|
"common_copied": "¡Elemento copiado!",
|
||||||
|
"common_host_placeholder": "raspberrypi.local o 192.168.1.85",
|
||||||
"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",
|
||||||
|
|||||||
@ -123,8 +123,8 @@
|
|||||||
"fleet_node_player_group_form_add_submit": "Ajouter",
|
"fleet_node_player_group_form_add_submit": "Ajouter",
|
||||||
"fleet_node_player_group_form_edit_title": "Modification d'un playgroup",
|
"fleet_node_player_group_form_edit_title": "Modification d'un playgroup",
|
||||||
"fleet_node_player_group_form_edit_submit": "Enregistrer",
|
"fleet_node_player_group_form_edit_submit": "Enregistrer",
|
||||||
"fleet_node_player_group_form_label_name": "Nom",
|
"fleet_node_player_group_form_label_name": "Entrez le nom du playgroup",
|
||||||
"fleet_node_player_group_form_label_playlist_id": "Liste de lecture",
|
"fleet_node_player_group_form_label_playlist_id": "Sélectionnez une playlist à jouer pour ce playgroup",
|
||||||
"fleet_node_player_group_form_button_cancel": "Annuler",
|
"fleet_node_player_group_form_button_cancel": "Annuler",
|
||||||
"js_fleet_node_player_group_delete_confirmation": "Êtes-vous sûr ?",
|
"js_fleet_node_player_group_delete_confirmation": "Êtes-vous sûr ?",
|
||||||
"node_player_group_delete_has_node_player": "Le playgroup a des lecteurs, supprimez-les ou réassignez-les avant de le supprimer",
|
"node_player_group_delete_has_node_player": "Le playgroup a des lecteurs, supprimez-les ou réassignez-les avant de le supprimer",
|
||||||
@ -233,6 +233,7 @@
|
|||||||
"common_new_folder": "Nouveau Dossier",
|
"common_new_folder": "Nouveau Dossier",
|
||||||
"common_folder_not_empty_error": "Le dossier n'est pas vide, vous devez d'abord supprimer son contenu",
|
"common_folder_not_empty_error": "Le dossier n'est pas vide, vous devez d'abord supprimer son contenu",
|
||||||
"common_copied": "Element copié !",
|
"common_copied": "Element copié !",
|
||||||
|
"common_host_placeholder": "raspberrypi.local ou 192.168.1.85",
|
||||||
"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",
|
||||||
|
|||||||
@ -122,8 +122,8 @@
|
|||||||
"fleet_node_player_group_form_add_submit": "Aggiungi",
|
"fleet_node_player_group_form_add_submit": "Aggiungi",
|
||||||
"fleet_node_player_group_form_edit_title": "Modifica Playgroup",
|
"fleet_node_player_group_form_edit_title": "Modifica Playgroup",
|
||||||
"fleet_node_player_group_form_edit_submit": "Salva",
|
"fleet_node_player_group_form_edit_submit": "Salva",
|
||||||
"fleet_node_player_group_form_label_name": "Nome",
|
"fleet_node_player_group_form_label_name": "Inserisci il nome del playgroup",
|
||||||
"fleet_node_player_group_form_label_playlist_id": "Playlist",
|
"fleet_node_player_group_form_label_playlist_id": "Seleziona una playlist da riprodurre per questo playgroup",
|
||||||
"fleet_node_player_group_form_button_cancel": "Cancella",
|
"fleet_node_player_group_form_button_cancel": "Cancella",
|
||||||
"js_fleet_node_player_group_delete_confirmation": "Sei sicuro?",
|
"js_fleet_node_player_group_delete_confirmation": "Sei sicuro?",
|
||||||
"node_player_group_delete_has_node_player": "Lo playgroup ha una playlist, rumuovila o riassegnala e riprova",
|
"node_player_group_delete_has_node_player": "Lo playgroup ha una playlist, rumuovila o riassegnala e riprova",
|
||||||
@ -232,6 +232,7 @@
|
|||||||
"common_new_folder": "Nuovo Cartella",
|
"common_new_folder": "Nuovo Cartella",
|
||||||
"common_folder_not_empty_error": "La cartella non è vuota, devi prima eliminarne il contenuto",
|
"common_folder_not_empty_error": "La cartella non è vuota, devi prima eliminarne il contenuto",
|
||||||
"common_copied": "Elemento copiato!",
|
"common_copied": "Elemento copiato!",
|
||||||
|
"common_host_placeholder": "raspberrypi.local o 192.168.1.85",
|
||||||
"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",
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import json
|
|||||||
from flask import Flask, render_template, redirect, request, url_for, jsonify
|
from flask import Flask, render_template, redirect, request, url_for, jsonify
|
||||||
from src.service.ModelStore import ModelStore
|
from src.service.ModelStore import ModelStore
|
||||||
from src.model.entity.NodePlayerGroup import NodePlayerGroup
|
from src.model.entity.NodePlayerGroup import NodePlayerGroup
|
||||||
|
from src.model.enum.FolderEntity import FolderEntity
|
||||||
|
from src.model.enum.OperatingSystem import OperatingSystem
|
||||||
from src.interface.ObController import ObController
|
from src.interface.ObController import ObController
|
||||||
|
|
||||||
|
|
||||||
@ -17,37 +19,68 @@ class FleetNodePlayerGroupController(ObController):
|
|||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
def register(self):
|
def register(self):
|
||||||
self._app.add_url_rule('/fleet/node-player-group/list', 'fleet_node_player_group_list', self.guard_fleet(self._auth(self.fleet_node_player_group_list)), methods=['GET'])
|
self._app.add_url_rule('/fleet/node-player-group', 'fleet_node_player_group', self.guard_fleet(self._auth(self.fleet_node_player_group)), methods=['GET'])
|
||||||
|
self._app.add_url_rule('/fleet/node-player-group/list/<player_group_id>', 'fleet_node_player_group_list', self.guard_fleet(self._auth(self.fleet_node_player_group_list)), methods=['GET'])
|
||||||
self._app.add_url_rule('/fleet/node-player-group/add', 'fleet_node_player_group_add', self.guard_fleet(self._auth(self.fleet_node_player_group_add)), methods=['POST'])
|
self._app.add_url_rule('/fleet/node-player-group/add', 'fleet_node_player_group_add', self.guard_fleet(self._auth(self.fleet_node_player_group_add)), methods=['POST'])
|
||||||
self._app.add_url_rule('/fleet/node-player-group/edit', 'fleet_node_player_group_edit', self.guard_fleet(self._auth(self.fleet_node_player_group_edit)), methods=['POST'])
|
self._app.add_url_rule('/fleet/node-player-group/save', 'fleet_node_player_group_save', self.guard_fleet(self._auth(self.fleet_node_player_group_save)), methods=['POST'])
|
||||||
self._app.add_url_rule('/fleet/node-player-group/delete', 'fleet_node_player_group_delete', self.guard_fleet(self._auth(self.fleet_node_player_group_delete)), methods=['DELETE'])
|
self._app.add_url_rule('/fleet/node-player-group/delete/<player_group_id>', 'fleet_node_player_group_delete', self.guard_fleet(self._auth(self.fleet_node_player_group_delete)), methods=['GET'])
|
||||||
|
|
||||||
|
def fleet_node_player_group(self):
|
||||||
|
return redirect(url_for('fleet_node_player_group_list', player_group_id=0))
|
||||||
|
|
||||||
|
def fleet_node_player_group_list(self, player_group_id: int = 0):
|
||||||
|
current_player_group = self._model_store.node_player_group().get(player_group_id)
|
||||||
|
node_player_groups = self._model_store.node_player_group().get_all(sort="created_at", ascending=False)
|
||||||
|
pcounters = self._model_store.node_player_group().get_player_counters_by_player_groups()
|
||||||
|
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)
|
||||||
|
|
||||||
|
if not current_player_group and len(node_player_groups) > 0:
|
||||||
|
current_player_group = None
|
||||||
|
|
||||||
def fleet_node_player_group_list(self):
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'fleet/player-group/list.jinja.html',
|
'fleet/player-group/list.jinja.html',
|
||||||
node_player_groups=self._model_store.node_player_group().get_groups(with_default=True),
|
error=request.args.get('error', None),
|
||||||
playlists=self._model_store.playlist().get_all_labels_indexed()
|
current_player_group=current_player_group,
|
||||||
|
node_player_groups=node_player_groups,
|
||||||
|
pcounters=pcounters,
|
||||||
|
playlists=self._model_store.playlist().get_all_labels_indexed(),
|
||||||
|
players=self._model_store.node_player().get_node_players(group_id=current_player_group.id) if current_player_group else [],
|
||||||
|
foldered_node_players=self._model_store.node_player().get_all_indexed('folder_id', multiple=True),
|
||||||
|
working_folder_path=working_folder_path,
|
||||||
|
working_folder=working_folder,
|
||||||
|
folders_tree=self._model_store.folder().get_folder_tree(FolderEntity.NODE_PLAYER),
|
||||||
|
enum_operating_system=OperatingSystem,
|
||||||
|
enum_folder_entity=FolderEntity,
|
||||||
)
|
)
|
||||||
|
|
||||||
def fleet_node_player_group_add(self):
|
def fleet_node_player_group_add(self):
|
||||||
playlist_id = request.form['playlist_id'] if 'playlist_id' in request.form and request.form['playlist_id'] else None
|
playlist_id = request.form['playlist_id'] if 'playlist_id' in request.form and request.form['playlist_id'] else None
|
||||||
self._model_store.node_player_group().add_form(NodePlayerGroup(
|
node_player_group = NodePlayerGroup(
|
||||||
name=request.form['name'],
|
name=request.form['name'],
|
||||||
playlist_id=playlist_id,
|
playlist_id=playlist_id,
|
||||||
))
|
)
|
||||||
return redirect(url_for('fleet_node_player_group_list'))
|
|
||||||
|
|
||||||
def fleet_node_player_group_edit(self):
|
try:
|
||||||
|
node_player_group = self._model_store.node_player_group().add_form(node_player_group)
|
||||||
|
except:
|
||||||
|
abort(409)
|
||||||
|
|
||||||
|
self._model_store.node_player_group().add_form(node_player_group)
|
||||||
|
return redirect(url_for('fleet_node_player_group_list', player_group_id=node_player_group.id))
|
||||||
|
|
||||||
|
def fleet_node_player_group_save(self):
|
||||||
playlist_id = request.form['playlist_id'] if 'playlist_id' in request.form and request.form['playlist_id'] else None
|
playlist_id = request.form['playlist_id'] if 'playlist_id' in request.form and request.form['playlist_id'] else None
|
||||||
self._model_store.node_player_group().update_form(request.form['id'], request.form['name'], playlist_id)
|
self._model_store.node_player_group().update_form(
|
||||||
return redirect(url_for('fleet_node_player_group_list'))
|
id=request.form['id'],
|
||||||
|
name=request.form['name'],
|
||||||
|
playlist_id=playlist_id
|
||||||
|
)
|
||||||
|
return redirect(url_for('fleet_node_player_group_list', player_group_id=request.form['id']))
|
||||||
|
|
||||||
def fleet_node_player_group_delete(self):
|
def fleet_node_player_group_delete(self, player_group_id: int):
|
||||||
data = request.get_json()
|
if self._model_store.node_player().count_node_players_for_group(player_group_id) > 0:
|
||||||
id = data.get('id')
|
return redirect(url_for('fleet_node_player_group_list', player_group_id=player_group_id, error='node_player_group_delete_has_node_player'))
|
||||||
|
|
||||||
if self._model_store.node_player().count_node_players_for_group(id) > 0:
|
self._model_store.node_player_group().delete(player_group_id)
|
||||||
return jsonify({'status': 'error', 'message': self.t('node_player_group_delete_has_node_player')}), 400
|
return redirect(url_for('fleet_node_player_group'))
|
||||||
|
|
||||||
self._model_store.node_player_group().delete(id)
|
|
||||||
return jsonify({'status': 'ok'})
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import Flask, render_template, redirect, request, url_for, jsonify, abort
|
from flask import Flask, render_template, redirect, request, url_for, jsonify, abort
|
||||||
from src.exceptions.PlaylistSlugAlreadyExist import PlaylistSlugAlreadyExist
|
|
||||||
from src.service.ModelStore import ModelStore
|
from src.service.ModelStore import ModelStore
|
||||||
from src.model.entity.Playlist import Playlist
|
from src.model.entity.Playlist import Playlist
|
||||||
from src.model.enum.FolderEntity import FolderEntity
|
from src.model.enum.FolderEntity import FolderEntity
|
||||||
@ -64,7 +63,7 @@ class PlaylistController(ObController):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
playlist = self._model_store.playlist().add_form(playlist)
|
playlist = self._model_store.playlist().add_form(playlist)
|
||||||
except PlaylistSlugAlreadyExist as e:
|
except:
|
||||||
abort(409)
|
abort(409)
|
||||||
|
|
||||||
return redirect(url_for('playlist_list', playlist_id=playlist.id))
|
return redirect(url_for('playlist_list', playlist_id=playlist.id))
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
class PlaylistSlugAlreadyExist(Exception):
|
|
||||||
pass
|
|
||||||
@ -224,8 +224,9 @@ class DatabaseManager:
|
|||||||
"DELETE FROM settings WHERE name = 'default_slide_duration'",
|
"DELETE FROM settings WHERE name = 'default_slide_duration'",
|
||||||
"DELETE FROM settings WHERE name = 'playlist_default_time_sync'",
|
"DELETE FROM settings WHERE name = 'playlist_default_time_sync'",
|
||||||
"DELETE FROM settings WHERE name = 'slide_animation_exit_effect'",
|
"DELETE FROM settings WHERE name = 'slide_animation_exit_effect'",
|
||||||
|
"UPDATE fleet_player_group SET slug = id WHERE slug = '' or slug is null",
|
||||||
"UPDATE content SET uuid = id WHERE uuid = '' or uuid is null",
|
"UPDATE content SET uuid = id WHERE uuid = '' or uuid is null",
|
||||||
]
|
]
|
||||||
|
|
||||||
for query in queries:
|
for query in queries:
|
||||||
self.execute_write_query(query=query, silent_errors=True)
|
self.execute_write_query(query=query, silent_errors=True)
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
from typing import Dict, Optional, List, Tuple, Union
|
from typing import Dict, Optional, List, Tuple, Union
|
||||||
|
|
||||||
from src.model.entity.NodePlayerGroup import NodePlayerGroup
|
from src.model.entity.NodePlayerGroup import NodePlayerGroup
|
||||||
|
from src.util.utils import slugify, slugify_next
|
||||||
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
|
||||||
from src.manager.VariableManager import VariableManager
|
from src.manager.VariableManager import VariableManager
|
||||||
|
from src.manager.NodePlayerManager import NodePlayerManager
|
||||||
from src.service.ModelManager import ModelManager
|
from src.service.ModelManager import ModelManager
|
||||||
|
|
||||||
|
|
||||||
@ -13,6 +15,7 @@ class NodePlayerGroupManager(ModelManager):
|
|||||||
TABLE_NAME = "fleet_player_group"
|
TABLE_NAME = "fleet_player_group"
|
||||||
TABLE_MODEL = [
|
TABLE_MODEL = [
|
||||||
"name CHAR(255)",
|
"name CHAR(255)",
|
||||||
|
"slug CHAR(255)",
|
||||||
"playlist_id INTEGER",
|
"playlist_id INTEGER",
|
||||||
"created_by CHAR(255)",
|
"created_by CHAR(255)",
|
||||||
"updated_by CHAR(255)",
|
"updated_by CHAR(255)",
|
||||||
@ -28,48 +31,61 @@ class NodePlayerGroupManager(ModelManager):
|
|||||||
if id:
|
if id:
|
||||||
raw_node_player_group['id'] = id
|
raw_node_player_group['id'] = id
|
||||||
|
|
||||||
|
[raw_node_player_group, user_tracker_edits] = self.user_manager.initialize_user_trackers(raw_node_player_group)
|
||||||
|
|
||||||
|
if len(user_tracker_edits) > 0:
|
||||||
|
self._db.update_by_id(self.TABLE_NAME, raw_node_player_group['id'], user_tracker_edits)
|
||||||
|
|
||||||
return NodePlayerGroup(**raw_node_player_group)
|
return NodePlayerGroup(**raw_node_player_group)
|
||||||
|
|
||||||
def hydrate_list(self, raw_node_player_groups: list) -> List[NodePlayerGroup]:
|
def hydrate_list(self, raw_node_player_groups: list) -> List[NodePlayerGroup]:
|
||||||
return [self.hydrate_object(raw_node_player_group) for raw_node_player_group in raw_node_player_groups]
|
return [self.hydrate_object(raw_node_player_group) for raw_node_player_group in raw_node_player_groups]
|
||||||
|
|
||||||
def get(self, id: int) -> Optional[NodePlayerGroup]:
|
def get(self, id: int) -> Optional[NodePlayerGroup]:
|
||||||
|
if not id:
|
||||||
|
return None
|
||||||
|
|
||||||
object = self._db.get_by_id(self.TABLE_NAME, id)
|
object = self._db.get_by_id(self.TABLE_NAME, id)
|
||||||
return self.hydrate_object(object, id) if object else None
|
return self.hydrate_object(object, id) if object else None
|
||||||
|
|
||||||
def get_by(self, query, sort: Optional[str] = None) -> List[NodePlayerGroup]:
|
def get_by(self, query, sort: Optional[str] = None, values: dict = {}) -> List[NodePlayerGroup]:
|
||||||
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
|
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort, values=values))
|
||||||
|
|
||||||
def get_one_by(self, query) -> Optional[NodePlayerGroup]:
|
def get_one_by(self, query, values: dict = {}, sort: Optional[str] = None, ascending=True, limit: Optional[int] = None) -> Optional[NodePlayerGroup]:
|
||||||
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
|
object = self._db.get_one_by_query(self.TABLE_NAME, query=query, values=values, sort=sort, ascending=ascending, limit=limit)
|
||||||
|
|
||||||
if not object:
|
if not object:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.hydrate_object(object)
|
return self.hydrate_object(object)
|
||||||
|
|
||||||
def get_all(self, sort: bool = False) -> List[NodePlayerGroup]:
|
def get_player_counters_by_player_groups(self, group_id: Optional[int] = None):
|
||||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, "name" if sort else None))
|
pcounters = self._db.execute_read_query("select group_id, count(distinct id) as total_counter FROM {} GROUP BY group_id".format(
|
||||||
|
NodePlayerManager.TABLE_NAME,
|
||||||
|
"WHERE 1=1 {}".format(
|
||||||
|
" AND group_id = {}".format(group_id) if group_id else ""
|
||||||
|
)
|
||||||
|
))
|
||||||
|
map = {}
|
||||||
|
for pcounter in pcounters:
|
||||||
|
map[pcounter['group_id']] = pcounter['total_counter']
|
||||||
|
|
||||||
def get_all_labels_indexed(self, with_default: bool = False) -> Dict:
|
if group_id:
|
||||||
|
return map[group_id] if group_id in map else 0
|
||||||
|
|
||||||
|
return map
|
||||||
|
|
||||||
|
def get_all(self, sort: Optional[str] = 'created_at', ascending=False) -> List[NodePlayerGroup]:
|
||||||
|
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, sort=sort, ascending=ascending))
|
||||||
|
|
||||||
|
def get_all_labels_indexed(self) -> Dict:
|
||||||
index = {}
|
index = {}
|
||||||
|
|
||||||
for item in self.get_groups(with_default=with_default):
|
for item in self.get_all():
|
||||||
index[item.id] = item.name
|
index[item.id] = item.name
|
||||||
|
|
||||||
return index
|
return index
|
||||||
|
|
||||||
def get_groups(self, with_default: bool = False):
|
|
||||||
node_player_groups = self.get_all(sort=True)
|
|
||||||
|
|
||||||
if not with_default:
|
|
||||||
return node_player_groups
|
|
||||||
|
|
||||||
return [NodePlayerGroup(
|
|
||||||
id=None,
|
|
||||||
name=self.t('common_default_node_player_group'))
|
|
||||||
] + node_player_groups
|
|
||||||
|
|
||||||
def get_node_players_groups(self, playlist_id: Optional[int] = None) -> List[NodePlayerGroup]:
|
def get_node_players_groups(self, playlist_id: Optional[int] = None) -> List[NodePlayerGroup]:
|
||||||
query = " 1=1 "
|
query = " 1=1 "
|
||||||
|
|
||||||
@ -85,12 +101,35 @@ class NodePlayerGroupManager(ModelManager):
|
|||||||
for node_player_group_id, edits in edits_node_player_groups.items():
|
for node_player_group_id, edits in edits_node_player_groups.items():
|
||||||
self._db.update_by_id(self.TABLE_NAME, node_player_group_id, edits)
|
self._db.update_by_id(self.TABLE_NAME, node_player_group_id, edits)
|
||||||
|
|
||||||
|
def get_available_slug(self, slug) -> str:
|
||||||
|
known_group = {"slug": slug}
|
||||||
|
next_slug = slug
|
||||||
|
while known_group is not None:
|
||||||
|
next_slug = slugify_next(next_slug)
|
||||||
|
known_group = self.get_one_by(query="slug = ?", values={"slug": next_slug}, sort="created_at", ascending=False, limit=1)
|
||||||
|
|
||||||
|
return next_slug
|
||||||
|
|
||||||
|
def slugify(self, node_player_group: Dict) -> Dict:
|
||||||
|
node_player_group["slug"] = slugify(node_player_group["name"])
|
||||||
|
|
||||||
|
known_group = self.get_one_by(query="slug = ?", values={
|
||||||
|
"slug": node_player_group["slug"]
|
||||||
|
}, sort="created_at", ascending=False, limit=1)
|
||||||
|
|
||||||
|
if known_group:
|
||||||
|
node_player_group["slug"] = self.get_available_slug(node_player_group["slug"])
|
||||||
|
|
||||||
|
return node_player_group
|
||||||
|
|
||||||
def pre_add(self, node_player_group: Dict) -> Dict:
|
def pre_add(self, node_player_group: Dict) -> Dict:
|
||||||
|
node_player_group = self.slugify(node_player_group)
|
||||||
self.user_manager.track_user_on_create(node_player_group)
|
self.user_manager.track_user_on_create(node_player_group)
|
||||||
self.user_manager.track_user_on_update(node_player_group)
|
self.user_manager.track_user_on_update(node_player_group)
|
||||||
return node_player_group
|
return node_player_group
|
||||||
|
|
||||||
def pre_update(self, node_player_group: Dict) -> Dict:
|
def pre_update(self, node_player_group: Dict) -> Dict:
|
||||||
|
node_player_group = self.slugify(node_player_group)
|
||||||
self.user_manager.track_user_on_update(node_player_group)
|
self.user_manager.track_user_on_update(node_player_group)
|
||||||
return node_player_group
|
return node_player_group
|
||||||
|
|
||||||
@ -107,10 +146,20 @@ class NodePlayerGroupManager(ModelManager):
|
|||||||
return node_player_group_id
|
return node_player_group_id
|
||||||
|
|
||||||
def update_form(self, id: int, name: str, playlist_id: Optional[int]) -> None:
|
def update_form(self, id: int, name: str, playlist_id: Optional[int]) -> None:
|
||||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update({"name": name, "playlist_id": playlist_id if playlist_id else None}))
|
node_player_group = self.get(id)
|
||||||
|
|
||||||
|
if not node_player_group:
|
||||||
|
return
|
||||||
|
|
||||||
|
form = {
|
||||||
|
"name": name,
|
||||||
|
"playlist_id": playlist_id if playlist_id else None
|
||||||
|
}
|
||||||
|
|
||||||
|
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update(form))
|
||||||
self.post_update(id)
|
self.post_update(id)
|
||||||
|
|
||||||
def add_form(self, node_player_group: Union[NodePlayerGroup, Dict]) -> None:
|
def add_form(self, node_player_group: Union[NodePlayerGroup, Dict]) -> NodePlayerGroup:
|
||||||
form = node_player_group
|
form = node_player_group
|
||||||
|
|
||||||
if not isinstance(node_player_group, dict):
|
if not isinstance(node_player_group, dict):
|
||||||
@ -118,12 +167,19 @@ class NodePlayerGroupManager(ModelManager):
|
|||||||
del form['id']
|
del form['id']
|
||||||
|
|
||||||
self._db.add(self.TABLE_NAME, self.pre_add(form))
|
self._db.add(self.TABLE_NAME, self.pre_add(form))
|
||||||
|
node_player_group = self.get_one_by(query="slug = ?", values={
|
||||||
|
"slug": form["slug"]
|
||||||
|
})
|
||||||
self.post_add(node_player_group.id)
|
self.post_add(node_player_group.id)
|
||||||
|
return node_player_group
|
||||||
|
|
||||||
def delete(self, id: int) -> None:
|
def delete(self, id: int) -> None:
|
||||||
self.pre_delete(id)
|
node_player_group = self.get(id)
|
||||||
self._db.delete_by_id(self.TABLE_NAME, id)
|
|
||||||
self.post_delete(id)
|
if node_player_group:
|
||||||
|
self.pre_delete(id)
|
||||||
|
self._db.delete_by_id(self.TABLE_NAME, id)
|
||||||
|
self.post_delete(id)
|
||||||
|
|
||||||
def to_dict(self, node_player_groups: List[NodePlayerGroup]) -> List[Dict]:
|
def to_dict(self, node_player_groups: List[NodePlayerGroup]) -> List[Dict]:
|
||||||
return [node_player_group.to_dict() for node_player_group in node_player_groups]
|
return [node_player_group.to_dict() for node_player_group in node_player_groups]
|
||||||
|
|||||||
@ -4,7 +4,6 @@ from typing import Dict, Optional, List, Tuple, Union
|
|||||||
|
|
||||||
from src.model.entity.Playlist import Playlist
|
from src.model.entity.Playlist import Playlist
|
||||||
from src.util.utils import get_optional_string, get_yt_video_id, slugify, slugify_next
|
from src.util.utils import get_optional_string, get_yt_video_id, slugify, slugify_next
|
||||||
from src.exceptions.PlaylistSlugAlreadyExist import PlaylistSlugAlreadyExist
|
|
||||||
from src.manager.DatabaseManager import DatabaseManager
|
from src.manager.DatabaseManager import DatabaseManager
|
||||||
from src.manager.SlideManager import SlideManager
|
from src.manager.SlideManager import SlideManager
|
||||||
from src.manager.LangManager import LangManager
|
from src.manager.LangManager import LangManager
|
||||||
@ -90,23 +89,6 @@ class PlaylistManager(ModelManager):
|
|||||||
|
|
||||||
return index
|
return index
|
||||||
|
|
||||||
def get_enabled_playlists(self, with_default: bool = False) -> List[Playlist]:
|
|
||||||
playlists = self.get_by(query="enabled = 1")
|
|
||||||
|
|
||||||
if not with_default:
|
|
||||||
return playlists
|
|
||||||
|
|
||||||
return [Playlist(
|
|
||||||
id=None,
|
|
||||||
name=self.t('common_default_playlist'))
|
|
||||||
] + playlists
|
|
||||||
|
|
||||||
def get_disabled_playlists(self) -> List[Playlist]:
|
|
||||||
return self.get_by(query="enabled = 0")
|
|
||||||
|
|
||||||
def update_enabled(self, id: int, enabled: bool) -> None:
|
|
||||||
self._db.update_by_id(self.TABLE_NAME, id, {"enabled": enabled})
|
|
||||||
|
|
||||||
def forget_for_user(self, user_id: int):
|
def forget_for_user(self, user_id: int):
|
||||||
playlists = self.get_by("created_by = '{}' or updated_by = '{}'".format(user_id, user_id))
|
playlists = self.get_by("created_by = '{}' or updated_by = '{}'".format(user_id, user_id))
|
||||||
edits_playlists = self.user_manager.forget_user_for_entity(playlists, user_id)
|
edits_playlists = self.user_manager.forget_user_for_entity(playlists, user_id)
|
||||||
@ -123,7 +105,7 @@ class PlaylistManager(ModelManager):
|
|||||||
|
|
||||||
return next_slug
|
return next_slug
|
||||||
|
|
||||||
def pre_add(self, playlist: Dict) -> Dict:
|
def slugify(self, playlist: Dict) -> Dict:
|
||||||
playlist["slug"] = slugify(playlist["name"])
|
playlist["slug"] = slugify(playlist["name"])
|
||||||
|
|
||||||
known_playlist = self.get_one_by(query="slug = ?", values={
|
known_playlist = self.get_one_by(query="slug = ?", values={
|
||||||
@ -133,12 +115,16 @@ class PlaylistManager(ModelManager):
|
|||||||
if known_playlist:
|
if known_playlist:
|
||||||
playlist["slug"] = self.get_available_slug(playlist["slug"])
|
playlist["slug"] = self.get_available_slug(playlist["slug"])
|
||||||
|
|
||||||
|
return playlist
|
||||||
|
|
||||||
|
def pre_add(self, playlist: Dict) -> Dict:
|
||||||
|
playlist = self.slugify(playlist)
|
||||||
self.user_manager.track_user_on_create(playlist)
|
self.user_manager.track_user_on_create(playlist)
|
||||||
self.user_manager.track_user_on_update(playlist)
|
self.user_manager.track_user_on_update(playlist)
|
||||||
return playlist
|
return playlist
|
||||||
|
|
||||||
def pre_update(self, playlist: Dict) -> Dict:
|
def pre_update(self, playlist: Dict) -> Dict:
|
||||||
playlist["slug"] = slugify(playlist["name"])
|
playlist = self.slugify(playlist)
|
||||||
self.user_manager.track_user_on_update(playlist)
|
self.user_manager.track_user_on_update(playlist)
|
||||||
return playlist
|
return playlist
|
||||||
|
|
||||||
|
|||||||
@ -6,10 +6,11 @@ from typing import Optional, Union
|
|||||||
|
|
||||||
class NodePlayerGroup:
|
class NodePlayerGroup:
|
||||||
|
|
||||||
def __init__(self, name: str = 'Untitled', playlist_id: Optional[int] = None, 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, name: str = 'Untitled', slug: str = 'untitled', playlist_id: Optional[int] = None, id: Optional[int] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
|
||||||
self._id = id if id else None
|
self._id = id if id else None
|
||||||
self._playlist_id = playlist_id
|
self._playlist_id = playlist_id
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self._slug = slug
|
||||||
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())
|
||||||
@ -35,6 +36,14 @@ class NodePlayerGroup:
|
|||||||
def name(self, value: str):
|
def name(self, value: str):
|
||||||
self._name = value
|
self._name = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def slug(self) -> str:
|
||||||
|
return self._slug
|
||||||
|
|
||||||
|
@slug.setter
|
||||||
|
def slug(self, value: str):
|
||||||
|
self._slug = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_by(self) -> str:
|
def created_by(self) -> str:
|
||||||
return self._created_by
|
return self._created_by
|
||||||
@ -71,6 +80,7 @@ class NodePlayerGroup:
|
|||||||
return f"NodePlayer(" \
|
return f"NodePlayer(" \
|
||||||
f"id='{self.id}',\n" \
|
f"id='{self.id}',\n" \
|
||||||
f"name='{self.name}',\n" \
|
f"name='{self.name}',\n" \
|
||||||
|
f"slug='{self.slug}',\n" \
|
||||||
f"playlist_id='{self.playlist_id}',\n" \
|
f"playlist_id='{self.playlist_id}',\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" \
|
||||||
@ -90,6 +100,7 @@ class NodePlayerGroup:
|
|||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
|
"slug": self.slug,
|
||||||
"playlist_id": self.playlist_id,
|
"playlist_id": self.playlist_id,
|
||||||
"created_by": self.created_by,
|
"created_by": self.created_by,
|
||||||
"updated_by": self.updated_by,
|
"updated_by": self.updated_by,
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
1.22.0
|
2.0.0
|
||||||
@ -84,7 +84,7 @@
|
|||||||
"position": 1,
|
"position": 1,
|
||||||
"pills": [
|
"pills": [
|
||||||
{"name": l.fleet_node_player_page_title, "route": "fleet_node_player_list", "icon": "fa-tv"},
|
{"name": l.fleet_node_player_page_title, "route": "fleet_node_player_list", "icon": "fa-tv"},
|
||||||
{"name": l.fleet_node_player_group_page_title, "route": "fleet_node_player_group_list", "icon": "fa-layer-group"},
|
{"name": l.fleet_node_player_group_page_title, "route": "fleet_node_player_group", "route_alt": "fleet_node_player_group_list", "icon": "fa-layer-group"},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}) %}
|
}) %}
|
||||||
|
|||||||
32
views/fleet/node-players/component/explr-sidebar.jinja.html
Normal file
32
views/fleet/node-players/component/explr-sidebar.jinja.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% macro render_folder(folder) %}
|
||||||
|
{% set node_player_children = foldered_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="{% if use_href %}{{ url_for('fleet_node_player_cd') }}?path={{ folder.path }}{% else %}javascript:void(0);{% endif %}"
|
||||||
|
class="{% if folder.path == working_folder_path %}active{% endif %} {{ 'explr-pick-folder' if not use_href }}">
|
||||||
|
{{ 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 }} {{ color }}"></i>
|
||||||
|
<a href="{% if use_href %}{{ url_for('fleet_node_player_edit', node_player_id=node_player.id) }}{% else %}javascript:void(0);{% endif %}" class="{{ 'explr-pick-element' if not use_href }}" data-json="{{ json_dumps(node_player.to_dict()) }}">
|
||||||
|
{{ 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>
|
||||||
@ -87,7 +87,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="node-player-edit-host">{{ l.fleet_node_player_form_label_host }}</label>
|
<label for="node-player-edit-host">{{ l.fleet_node_player_form_label_host }}</label>
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<input type="text" name="host" id="node-player-edit-host" required="required" value="{{ node_player.host }}" />
|
<input type="text" name="host" id="node-player-edit-host" required="required" value="{{ node_player.host }}" placeholder="{{ l.common_host_placeholder }}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="node-player-add-host">{{ l.fleet_node_player_form_label_host }}</label>
|
<label for="node-player-add-host">{{ l.fleet_node_player_form_label_host }}</label>
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<input name="host" type="text" id="node-player-add-host" required="required"/>
|
<input name="host" type="text" id="node-player-add-host" required="required" placeholder="{{ l.common_host_placeholder }}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
15
views/fleet/node-players/modal/explr-picker.jinja.html
Normal file
15
views/fleet/node-players/modal/explr-picker.jinja.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<div class="modal modal-explr-picker modal-node-player-explr-picker modal-node-player">
|
||||||
|
<h2>
|
||||||
|
{{ l.common_pick_element }}
|
||||||
|
</h2>
|
||||||
|
{% with use_href=False %}
|
||||||
|
{% include 'fleet/node-players/component/explr-sidebar.jinja.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button type="button" class="btn btn-naked picker-close">
|
||||||
|
<i class="fa fa-close icon-left"></i>{{ l.common_close }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -1,72 +1,105 @@
|
|||||||
<table class="{{ tclass }}-node-players">
|
{#<table class="{{ tclass }}-node-players">#}
|
||||||
<thead>
|
{# <thead>#}
|
||||||
<tr>
|
{# <tr>#}
|
||||||
<th>{{ l.fleet_node_player_group_panel_th_name }}</th>
|
{# <th>{{ l.fleet_node_player_group_panel_th_name }}</th>#}
|
||||||
{% if AUTH_ENABLED %}
|
{# {% if AUTH_ENABLED %}#}
|
||||||
<th class="tac">
|
{# <th class="tac">#}
|
||||||
<i class="fa fa-user"></i>
|
{# <i class="fa fa-user"></i>#}
|
||||||
</th>
|
{# </th>#}
|
||||||
{% endif %}
|
{# {% endif %}#}
|
||||||
{% if PLAYLIST_ENABLED %}
|
{# {% if PLAYLIST_ENABLED %}#}
|
||||||
<th class="tac">{{ l.fleet_node_player_group_panel_th_playlist }}</th>
|
{# <th class="tac">{{ l.fleet_node_player_group_panel_th_playlist }}</th>#}
|
||||||
{% endif %}
|
{# {% endif %}#}
|
||||||
<th class="tac">{{ l.fleet_node_player_group_panel_th_activity }}</th>
|
{# <th class="tac">{{ l.fleet_node_player_group_panel_th_activity }}</th>#}
|
||||||
</tr>
|
{# </tr>#}
|
||||||
</thead>
|
{# </thead>#}
|
||||||
<tbody>
|
{# <tbody>#}
|
||||||
<tr class="empty-tr {% if node_player_groups|length != 0 %}hidden{% endif %}">
|
{# <tr class="empty-tr {% if node_player_groups|length != 0 %}hidden{% endif %}">#}
|
||||||
<td colspan="4">
|
{# <td colspan="4">#}
|
||||||
{{ l.fleet_node_player_group_panel_empty|replace(
|
{# {{ l.fleet_node_player_group_panel_empty|replace(#}
|
||||||
'%link%',
|
{# '%link%',#}
|
||||||
('<a href="javascript:void(0);" class="item-add node-player-group-add">'~l.fleet_node_player_group_button_add~'</a>')|safe
|
{# ('<a href="javascript:void(0);" class="item-add node-player-group-add">'~l.fleet_node_player_group_button_add~'</a>')|safe#}
|
||||||
) }}
|
{# ) }}#}
|
||||||
</td>
|
{# </td>#}
|
||||||
</tr>
|
{# </tr>#}
|
||||||
|
{##}
|
||||||
|
{# {% for node_player_group in node_player_groups %}#}
|
||||||
|
{# <tr class="node-player-group-item" data-level="{{ node_player_group.id }}" data-entity="{{ node_player_group.to_json({"created_by": track_created(node_player_group).username, "updated_by": track_updated(node_player_group).username}) }}">#}
|
||||||
|
{# <td class="infos">#}
|
||||||
|
{# <div class="inner">#}
|
||||||
|
{# {% if node_player_group.id %}#}
|
||||||
|
{# <div class="badge"><i class="fa fa-key icon-left"></i> {{ node_player_group.id }}</div>#}
|
||||||
|
{# {% else %}#}
|
||||||
|
{# <div class="badge"><i class="fa fa-lock"></i></div>#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{##}
|
||||||
|
{# <i class="fa fa-layer-group icon-left"></i>#}
|
||||||
|
{# {{ node_player_group.name }}#}
|
||||||
|
{# </div>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# {% if AUTH_ENABLED %}#}
|
||||||
|
{# <td class="tac">#}
|
||||||
|
{# {% if node_player_group.id %}#}
|
||||||
|
{# {% set creator = track_created(node_player_group) %}#}
|
||||||
|
{# {% if creator.username %}#}
|
||||||
|
{# <a href="javascript:void(0);" class="badge item-utrack node-player-group-utrack {% if not creator.enabled %}anonymous{% endif %}">#}
|
||||||
|
{# {{ creator.username }}#}
|
||||||
|
{# </a>#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{# </td>#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{# <td class="tac">#}
|
||||||
|
{# {% if node_player_group.playlist_id and playlists[node_player_group.playlist_id] %}#}
|
||||||
|
{# {{ playlists[node_player_group.playlist_id] }}#}
|
||||||
|
{# {% else %}#}
|
||||||
|
{# {{ l.common_default_playlist }}#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{# </td>#}
|
||||||
|
{# <td class="actions tac">#}
|
||||||
|
{# {% if node_player_group.id %}#}
|
||||||
|
{# <a href="javascript:void(0);" class="item-edit node-player-group-edit">#}
|
||||||
|
{# <i class="fa fa-pencil"></i>#}
|
||||||
|
{# </a>#}
|
||||||
|
{# <a href="javascript:void(0);" class="item-delete node-player-group-delete">#}
|
||||||
|
{# <i class="fa fa-trash"></i>#}
|
||||||
|
{# </a>#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{# </td>#}
|
||||||
|
{# </tr>#}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
{# </tbody>#}
|
||||||
|
{#</table>#}
|
||||||
|
|
||||||
{% for node_player_group in node_player_groups %}
|
|
||||||
<tr class="node-player-group-item" data-level="{{ node_player_group.id }}" data-entity="{{ node_player_group.to_json({"created_by": track_created(node_player_group).username, "updated_by": track_updated(node_player_group).username}) }}">
|
|
||||||
<td class="infos">
|
|
||||||
<div class="inner">
|
|
||||||
{% if node_player_group.id %}
|
|
||||||
<div class="badge"><i class="fa fa-key icon-left"></i> {{ node_player_group.id }}</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="badge"><i class="fa fa-lock"></i></div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<i class="fa fa-layer-group icon-left"></i>
|
<div class="tiles node-player-groups">
|
||||||
{{ node_player_group.name }}
|
<div class="tiles-inner">
|
||||||
|
{% for node_player_group in node_player_groups %}
|
||||||
|
{% set active = current_player_group and ''~node_player_group.id == ''~current_player_group.id %}
|
||||||
|
<a href="{{ url_for('fleet_node_player_group_list', player_group_id=node_player_group.id) }}"
|
||||||
|
class="{% if active %}active{% endif %} tile-item node-player-group-item-{{ node_player_group.id }} node-player-group-item"
|
||||||
|
data-level="{{ node_player_group.id }}"
|
||||||
|
data-entity="{{ node_player_group.to_json() }}">
|
||||||
|
<div class="tile-body">
|
||||||
|
{% set title = node_player_group.name|trim %}
|
||||||
|
{% set title = title if title|length > 0 %}
|
||||||
|
{{ truncate((title)|default(l.common_untitled), 35, '...') }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div class="tile-metrics">
|
||||||
{% if AUTH_ENABLED %}
|
<div class="foot-span players-counter">
|
||||||
<td class="tac">
|
{% set pcounter = pcounters[node_player_group.id]|default(0) %}
|
||||||
{% if node_player_group.id %}
|
{% if pcounter > 0 %}
|
||||||
{% set creator = track_created(node_player_group) %}
|
{{ pcounter }} <sub><i class="fa fa-display"></i></sub>
|
||||||
{% if creator.username %}
|
{% else %}
|
||||||
<a href="javascript:void(0);" class="badge item-utrack node-player-group-utrack {% if not creator.enabled %}anonymous{% endif %}">
|
{{ l.common_empty }}
|
||||||
{{ creator.username }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
</div>
|
||||||
</td>
|
</div>
|
||||||
{% endif %}
|
</a>
|
||||||
<td class="tac">
|
{% endfor %}
|
||||||
{% if node_player_group.playlist_id and playlists[node_player_group.playlist_id] %}
|
|
||||||
{{ playlists[node_player_group.playlist_id] }}
|
<div class="inner-empty empty-flag {% if node_player_groups|length != 0 %}hidden{% endif %}">
|
||||||
{% else %}
|
<i class="fa fa-list"></i>
|
||||||
{{ l.common_default_playlist }}
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td class="actions tac">
|
|
||||||
{% if node_player_group.id %}
|
|
||||||
<a href="javascript:void(0);" class="item-edit node-player-group-edit">
|
|
||||||
<i class="fa fa-pencil"></i>
|
|
||||||
</a>
|
|
||||||
<a href="javascript:void(0);" class="item-delete node-player-group-delete">
|
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
@ -6,46 +6,93 @@
|
|||||||
|
|
||||||
{% block add_css %}
|
{% block add_css %}
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_CSS) }}
|
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_CSS) }}
|
||||||
|
<link rel="stylesheet" href="{{ STATIC_PREFIX }}css/lib/jquery-explr-1.4.css"/>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block add_js %}
|
{% block add_js %}
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/lib/jquery-explr-1.4.js"></script>
|
||||||
<script src="{{ STATIC_PREFIX }}js/fleet/node-player-groups.js"></script>
|
<script src="{{ STATIC_PREFIX }}js/fleet/node-player-groups.js"></script>
|
||||||
|
<script src="{{ STATIC_PREFIX }}js/explorer.js"></script>
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_JAVASCRIPT) }}
|
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_JAVASCRIPT) }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block page %}
|
{% block body_class %}view-player-group-list{% endblock %}
|
||||||
<div class="toolbar">
|
|
||||||
<h2><i class="fa fa-layer-group icon-left"></i>{{ l.fleet_node_player_group_page_title }}</h2>
|
|
||||||
|
|
||||||
<div class="toolbar-actions">
|
{% block page %}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="top-content">
|
||||||
|
{# <h1>#}
|
||||||
|
{# {{ l.playlist_page_title }}#}
|
||||||
|
{# </h1>#}
|
||||||
|
|
||||||
|
<div class="top-actions">
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_TOOLBAR_ACTIONS_START) }}
|
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_TOOLBAR_ACTIONS_START) }}
|
||||||
<button type="button" class="purple node-player-group-add item-add"><i class="fa fa-plus icon-left"></i>{{ l.fleet_node_player_group_button_add }}</button>
|
|
||||||
|
<button type="button" class="btn btn-info node-player-group-add item-add">
|
||||||
|
<i class="fa fa-layer-group icon-left"></i>
|
||||||
|
{{ l.fleet_node_player_group_button_add }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{% if current_player_group %}
|
||||||
|
<a href="{{ url_for('fleet_node_player_group_delete', player_group_id=current_player_group.id) }}"
|
||||||
|
class="btn btn-danger-alt protected">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_TOOLBAR_ACTIONS_END) }}
|
{{ HOOK(H_FLEET_NODE_PLAYER_GROUP_TOOLBAR_ACTIONS_END) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-error hidden">
|
<div class="alert alert-info tiles-empty empty-flag {% if node_player_groups|length != 0 %}hidden{% endif %}">
|
||||||
|
{{ l.node_player_group_panel_empty|replace(
|
||||||
|
'%link%',
|
||||||
|
('<a href="javascript:void(0);" class="item-add node-player-group-add">'~l.fleet_node_player_group_button_add~'</a>')|safe
|
||||||
|
) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel">
|
{% if error %}
|
||||||
<div class="panel-body">
|
<div class="alert alert-danger">
|
||||||
<h3>{{ l.fleet_node_player_group_panel_active }}</h3>
|
{{ l[error] }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% with tclass='active', node_player_groups=node_player_groups %}
|
<div class="bottom-content">
|
||||||
|
<div class="page-panel left-panel">
|
||||||
|
{% with node_player_groups=node_player_groups %}
|
||||||
{% include 'fleet/player-group/component/table.jinja.html' %}
|
{% include 'fleet/player-group/component/table.jinja.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="inner">
|
||||||
|
{% if current_player_group %}
|
||||||
|
{# {% with node_player_groups=node_player_groups %}#}
|
||||||
|
{# {% include 'fleet/player-group/component/edit.jinja.html' %}#}
|
||||||
|
{# {% endwith %}#}
|
||||||
|
{% else %}
|
||||||
|
<div class="inner-empty empty-flag ">
|
||||||
|
<i class="fa fa-layer-group"></i>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modals hidden">
|
<div class="modals hidden">
|
||||||
<div class="modals-outer">
|
<div class="modals-outer">
|
||||||
<a href="javascript:void(0);" class="modal-close">
|
|
||||||
<i class="fa fa-close"></i>
|
|
||||||
</a>
|
|
||||||
<div class="modals-inner">
|
<div class="modals-inner">
|
||||||
{% include 'fleet/player-group/modal/add.jinja.html' %}
|
{% include 'fleet/player-group/modal/add.jinja.html' %}
|
||||||
{% include 'fleet/player-group/modal/edit.jinja.html' %}
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pickers hidden">
|
||||||
|
<div class="modals-outer">
|
||||||
|
<div class="modals-inner">
|
||||||
|
{% include 'fleet/node-players/modal/explr-picker.jinja.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,11 +3,11 @@
|
|||||||
{{ l.fleet_node_player_group_form_add_title }}
|
{{ l.fleet_node_player_group_form_add_title }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<form class="form" action="/fleet/node-player-group/add" method="POST" enctype="multipart/form-data">
|
<form class="form" action="{{ url_for('fleet_node_player_group_add') }}" method="POST">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="node-player-group-add-name">{{ l.fleet_node_player_group_form_label_name }}</label>
|
<label for="node-player-group-add-name">{{ l.fleet_node_player_group_form_label_name }}</label>
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<input name="name" type="text" id="node-player-group-add-name" required="required" />
|
<input name="name" type="text" id="node-player-group-add-name" required="required"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -15,7 +15,6 @@
|
|||||||
<label for="node-player-group-add-playlist-id">{{ l.fleet_node_player_group_form_label_playlist_id }}</label>
|
<label for="node-player-group-add-playlist-id">{{ l.fleet_node_player_group_form_label_playlist_id }}</label>
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<select name="playlist_id" id="node-player-group-add-playlist-id">
|
<select name="playlist_id" id="node-player-group-add-playlist-id">
|
||||||
<option value="">{{ l.common_default_playlist }}</option>
|
|
||||||
{% for playlist_id, playlist_name in playlists.items() %}
|
{% for playlist_id, playlist_name in playlists.items() %}
|
||||||
<option value="{{ playlist_id }}">{{ playlist_name }}</option>
|
<option value="{{ playlist_id }}">{{ playlist_name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -24,11 +23,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button type="button" class="btn-normal modal-close">
|
<button type="button" class="btn btn-naked modal-close">
|
||||||
{{ l.fleet_node_player_group_form_button_cancel }}
|
{{ l.common_close }}
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="green">
|
<button type="submit" class="btn btn-info">
|
||||||
<i class="fa fa-plus icon-left"></i> {{ l.fleet_node_player_group_form_add_submit }}
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -73,5 +73,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<div class="inner-empty empty-flag ">
|
||||||
|
<i class="fa fa-image"></i>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
Loading…
Reference in New Issue
Block a user