node player groups wip

This commit is contained in:
jr-k 2024-07-16 13:50:08 +02:00
parent 348682b400
commit 84da36d953
26 changed files with 560 additions and 230 deletions

File diff suppressed because one or more lines are too long

View File

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

View File

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

View 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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +0,0 @@
class PlaylistSlugAlreadyExist(Exception):
pass

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1 @@
1.22.0 2.0.0

View File

@ -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"},
] ]
} }
}) %} }) %}

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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