Sync queue management UI, sync/reboot/poweroff all btns
This commit is contained in:
parent
a029b2e902
commit
e44ff71a58
10
app.py
10
app.py
|
@ -38,6 +38,9 @@ gui_l10n = {"locale": LOCALE[:2],
|
||||||
"str_sync": _("Sync"),
|
"str_sync": _("Sync"),
|
||||||
"str_poweroff": _("Poweroff"),
|
"str_poweroff": _("Poweroff"),
|
||||||
"str_reboot": _("Reboot"),
|
"str_reboot": _("Reboot"),
|
||||||
|
# TODO : add to l10n
|
||||||
|
"str_poweroff_all": _("Poweroff all"),
|
||||||
|
"str_reboot_all": _("Reboot all"),
|
||||||
"str_blink": _("Blink"),
|
"str_blink": _("Blink"),
|
||||||
"str_link": _("Link"),
|
"str_link": _("Link"),
|
||||||
"str_refresh": _("Refresh"),
|
"str_refresh": _("Refresh"),
|
||||||
|
@ -631,11 +634,8 @@ def sync(host, arg0):
|
||||||
if arg0 == "stop":
|
if arg0 == "stop":
|
||||||
return stop_upload()
|
return stop_upload()
|
||||||
elif host == "all":
|
elif host == "all":
|
||||||
reset_current_upload()
|
# This is done in js for now.
|
||||||
return sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
pass
|
||||||
# TODO : figure it out for multiple hosts
|
|
||||||
# ~ for hostl in hosts_available:
|
|
||||||
# ~ sync_media_folder(media_folder_local, media_folder_remote_expanded, hostl, cmd_port)
|
|
||||||
else:
|
else:
|
||||||
reset_current_upload()
|
reset_current_upload()
|
||||||
return sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
return sync_media_folder(media_folder_local, media_folder_remote_expanded, host, cmd_port)
|
||||||
|
|
|
@ -28,7 +28,7 @@ sha256 : 0fe3fe76d0e56e445124fa20646fa8b3d8c59568786b3ebc8a96d83d92f203e3
|
||||||
* Use nginx reverse proxy + SSL between server and clients ( https://medium.com/@antelle/how-to-generate-a-self-signed-ssl-certificate-for-an-ip-address-f0dd8dddf754 )
|
* Use nginx reverse proxy + SSL between server and clients ( https://medium.com/@antelle/how-to-generate-a-self-signed-ssl-certificate-for-an-ip-address-f0dd8dddf754 )
|
||||||
* Webgui beautifying
|
* Webgui beautifying
|
||||||
|
|
||||||
## 0.4 : 2022-10-21-videopi.img.xz
|
## 0.4 : 2022-10-21-pilpil.img.xz
|
||||||
md5 : dee7af70135994169cab4f073ee51905
|
md5 : dee7af70135994169cab4f073ee51905
|
||||||
sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ sha256 : ec3e17fc9b41f8c5181484e9866be2d1d92cab8403210e3d22f4f689edd4cfde
|
||||||
* Add media folder sync (scp, rsync, http upload)
|
* Add media folder sync (scp, rsync, http upload)
|
||||||
* Install script ; Wifi setup, generate/install SSH keys/ nginx SSL cert/key fore each host, change hostname, static IPs
|
* Install script ; Wifi setup, generate/install SSH keys/ nginx SSL cert/key fore each host, change hostname, static IPs
|
||||||
|
|
||||||
## 0.5 : 2022-11-17-videopi.img.xz
|
## 0.5 : 2022-11-17-pilpil.img.xz
|
||||||
md5 : d1dd7404ec05d16c8a4179370a214821
|
md5 : d1dd7404ec05d16c8a4179370a214821
|
||||||
sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
||||||
|
|
||||||
|
@ -55,6 +55,31 @@ sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
||||||
* FR localisation for server, client and install script
|
* FR localisation for server, client and install script
|
||||||
* pep8ification
|
* pep8ification
|
||||||
|
|
||||||
|
## 0.6 : 2023-01-06-pilpil.img.xz
|
||||||
|
md5 :
|
||||||
|
sha256 :
|
||||||
|
|
||||||
|
* os update, cleanup
|
||||||
|
* server: upload in chunks
|
||||||
|
* server: file upload : only send new files, get upload status (/sync/status)
|
||||||
|
* server: pep8fied (except for line length)
|
||||||
|
* server: better overclock values for RPIs
|
||||||
|
* server: VLC file caching set to 5000ms for seamless playback
|
||||||
|
* server: BCM4345 workaround to solve wifi connectivity (https://wiki.arthus.net/?Rpi_wifi_-_BCM4345_and_CTRL-EVENT-ASSOC-REJECT)
|
||||||
|
* webgui: remove file from timeline (drag&drop to bin)
|
||||||
|
* webgui: upload progress dialog
|
||||||
|
* webgui: l10n html + js
|
||||||
|
* webgui: Add remote file enqueuing
|
||||||
|
* webgui: Fix timeline UI
|
||||||
|
* webgui: Add video thumbnails to timeline UI
|
||||||
|
* webgui: fix timeline/file sync with multiple clients
|
||||||
|
* webgui: file sync queuing UI
|
||||||
|
* webgui: sync all live hosts button
|
||||||
|
* webgui: Add remove from sync queue button
|
||||||
|
* webgui: Poweroff/Reboot all btn
|
||||||
|
* client: Only blink when running on rpi
|
||||||
|
* client: Generate video thumbnails on startup/upload with ffmpeg, serve SVG when file not found, create folder if needed
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# FS checklist
|
# FS checklist
|
||||||
|
@ -70,32 +95,27 @@ sha256 : 401359a84c6d60902c05602bd52fae70f0b2ecac36d550b52d14e1e3230854a6
|
||||||
|
|
||||||
|
|
||||||
# DOING NEXT :
|
# DOING NEXT :
|
||||||
* server : remove incomplete files before upload
|
* FIXME : playhead stuck after removing item from playlist
|
||||||
* webgui/server : interrupt current download ?
|
|
||||||
* webgui : upload status CSS transition ?
|
|
||||||
|
|
||||||
# DONE :
|
# DONE :
|
||||||
|
|
||||||
* server : upload in chunks
|
|
||||||
* webgui: remove file from timeline (drag&drop to bin?) (check bugs)
|
|
||||||
* webgui: upload progress dialog
|
|
||||||
* server : file upload : only send new files, get upload status (/sync/status)
|
|
||||||
* webgui: l10n html + js
|
|
||||||
* webgui: Add remote file enqueuing
|
|
||||||
* webgui: Fix timeline UI
|
|
||||||
* webgui: Add video thumbnails to timeline UI
|
|
||||||
* client: Only blink when running on rpi
|
|
||||||
* client: Generate video thumbnail on start with ffmpegthumbnailer, serve SVG when file not found
|
|
||||||
* server: pep8fied (except for line length)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO :
|
# TODO :
|
||||||
* ~ Test with several rpis
|
* check control from wifi client ( smartphone )
|
||||||
* ? Scripts hotspot linux : nmcli
|
* playlist save/load ?
|
||||||
win : https://github.com/JamesCullum/Windows-Hotspot
|
* webgui/server : interrupt current download ?
|
||||||
mac : https://apple.stackexchange.com/questions/2488/start-stop-internet-sharing-from-a-script
|
* server : remove incomplete uploaded files before new upload ?
|
||||||
|
* webgui : upload status CSS transition ?
|
||||||
* ! Remove git personal details/resolv.conf, remove authorized_keys, ssh config, clean home, re-enable ssh pw login
|
* ! Remove git personal details/resolv.conf, remove authorized_keys, ssh config, clean home, re-enable ssh pw login
|
||||||
* ~ Doc
|
* ~ Doc
|
||||||
|
|
||||||
# OTHER:
|
# OTHER:
|
||||||
|
* ? Scripts hotspot
|
||||||
|
linux : nmcli
|
||||||
|
win : https://github.com/JamesCullum/Windows-Hotspot
|
||||||
|
mac : https://apple.stackexchange.com/questions/2488/start-stop-internet-sharing-from-a-script
|
||||||
* get_client_rssi.sh on server
|
* get_client_rssi.sh on server
|
||||||
|
|
||||||
|
# À FINANCER
|
||||||
|
* Shutters/motor control
|
||||||
|
* XLR support
|
||||||
|
* UDEV rules : keyboard/mouse = standalone GUI
|
100
static/script.js
100
static/script.js
|
@ -435,6 +435,8 @@ function update_host_list(infos_array){
|
||||||
host_down.forEach(function(element){
|
host_down.forEach(function(element){
|
||||||
document.getElementById(element).style.display = 'none';
|
document.getElementById(element).style.display = 'none';
|
||||||
});
|
});
|
||||||
|
currentUser.hosts_up = host_up;
|
||||||
|
currentUser.hosts_down = host_down;
|
||||||
currentUser.status_all = host_up.length + " client(s).";
|
currentUser.status_all = host_up.length + " client(s).";
|
||||||
return host_up.length;
|
return host_up.length;
|
||||||
}
|
}
|
||||||
|
@ -449,10 +451,10 @@ function freeze_ui_host(host, html_attributes, inner_html=0, inner_text=0){
|
||||||
let upload_dialog_cont_element = add_HTML_element("div", html_attributes, 0, host);
|
let upload_dialog_cont_element = add_HTML_element("div", html_attributes, 0, host);
|
||||||
container_element.insertBefore(upload_dialog_cont_element, siblings[0]);
|
container_element.insertBefore(upload_dialog_cont_element, siblings[0]);
|
||||||
if (inner_html){
|
if (inner_html){
|
||||||
let child_node = add_HTML_element("div", inner_html, 0, host)
|
let child_node = add_HTML_element("div", inner_html, 0, host);
|
||||||
let new_node = container_element.children.item(html_attributes.id + host).appendChild(child_node);
|
let new_node = container_element.children.item(html_attributes.id + host).appendChild(child_node);
|
||||||
if (inner_text){
|
if (inner_text){
|
||||||
new_node.innerText = inner_text;
|
new_node.innerHTML = inner_text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,17 +463,43 @@ function freeze_queued_container(command){
|
||||||
freeze_attributes = {"id":"ul_queued_freeze_", "class": "ul_queued_freeze"};
|
freeze_attributes = {"id":"ul_queued_freeze_", "class": "ul_queued_freeze"};
|
||||||
inner_html = {"id":"ul_queued_msg_", "class": "ul_queued_msg"};
|
inner_html = {"id":"ul_queued_msg_", "class": "ul_queued_msg"};
|
||||||
inner_text = t9n[LOCALE].item_queued;
|
inner_text = t9n[LOCALE].item_queued;
|
||||||
freeze_ui_host(command.split("/")[2], freeze_attributes, inner_html, inner_text);
|
let host = command.split("/")[2];
|
||||||
|
inner_text += '<buttton id="sync_unqueue_' + host + '" onclick="cancel_sync(this)" class="command btn btn-block btn-lg btn-default btn-unqueue" role="button">X</button';
|
||||||
|
freeze_ui_host(host, freeze_attributes, inner_html, inner_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unfreeze_queued_container(host){
|
function unfreeze_queued_container(host){
|
||||||
let container = document.getElementById(host);
|
let container = document.getElementById(host);
|
||||||
let exists = container.querySelector("div.ul_queued_freeze");
|
let exists = container.querySelector("div.ul_queued_freeze");
|
||||||
exists? exists.remove(): false;
|
if (exists){
|
||||||
|
exists.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unfreeze_all_containers(){
|
||||||
|
if (currentUser.hosts_up){
|
||||||
|
currentUser.hosts_up.forEach(function(host){
|
||||||
|
unfreeze_queued_container(host);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec_all_hosts(command){
|
||||||
|
let authorized_commands = ['reboot', 'poweroff'];
|
||||||
|
// Only send authorized commands
|
||||||
|
if (authorized_commands.indexOf(command) == -1 ){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ( confirm(t9n[LOCALE].confirmMessage) ) {
|
||||||
|
if (currentUser.hosts_up){
|
||||||
|
currentUser.hosts_up.forEach(function(host){
|
||||||
|
send_ajax_cmd("/" + host + "/" + command);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function display_upload_status(command) {
|
function display_upload_status(command) {
|
||||||
// TODO : First this should set the HTMl with empty values, then call update_upload...
|
|
||||||
let host = command.split("/")[2];
|
let host = command.split("/")[2];
|
||||||
let container_element = document.getElementById(host);
|
let container_element = document.getElementById(host);
|
||||||
let siblings = container_element.children;
|
let siblings = container_element.children;
|
||||||
|
@ -534,6 +562,7 @@ async function update_upload_status(current_upload) {
|
||||||
// Upload finished
|
// Upload finished
|
||||||
console.log("Upload finished - Destroying dialog for " + currentUser.last_ul_host);
|
console.log("Upload finished - Destroying dialog for " + currentUser.last_ul_host);
|
||||||
destroy_upload_status(currentUser.last_ul_host);
|
destroy_upload_status(currentUser.last_ul_host);
|
||||||
|
|
||||||
// Remove first item from command queue
|
// Remove first item from command queue
|
||||||
currentUser.ul_queue.shift();
|
currentUser.ul_queue.shift();
|
||||||
// Upload done, un-freeze
|
// Upload done, un-freeze
|
||||||
|
@ -553,6 +582,7 @@ async function update_upload_status(current_upload) {
|
||||||
document.getElementById("ul_status_filename_" + currentUser.last_ul_host).innerHTML = t9n[LOCALE].filename + ": " + current_upload.filename;
|
document.getElementById("ul_status_filename_" + currentUser.last_ul_host).innerHTML = t9n[LOCALE].filename + ": " + current_upload.filename;
|
||||||
document.getElementById("ul_status_filesize_" + currentUser.last_ul_host).innerHTML = t9n[LOCALE].size + ": " + current_upload.size + t9n[LOCALE].size_unit;
|
document.getElementById("ul_status_filesize_" + currentUser.last_ul_host).innerHTML = t9n[LOCALE].size + ": " + current_upload.size + t9n[LOCALE].size_unit;
|
||||||
// Progress bar CSS
|
// Progress bar CSS
|
||||||
|
// FIXME : cursor stops updating after upload ?
|
||||||
if (current_upload.transferred_percent) {
|
if (current_upload.transferred_percent) {
|
||||||
document.getElementById("ul_progress_" + currentUser.last_ul_host).innerText = current_upload.transferred_percent + "%";
|
document.getElementById("ul_progress_" + currentUser.last_ul_host).innerText = current_upload.transferred_percent + "%";
|
||||||
document.getElementById("ul_progress_" + currentUser.last_ul_host).style.background = "linear-gradient(90deg, " + TIMELINE_COLOR_CURSOR + " " + current_upload.transferred_percent + "%, #fff " + current_upload.transferred_percent + "%)";
|
document.getElementById("ul_progress_" + currentUser.last_ul_host).style.background = "linear-gradient(90deg, " + TIMELINE_COLOR_CURSOR + " " + current_upload.transferred_percent + "%, #fff " + current_upload.transferred_percent + "%)";
|
||||||
|
@ -613,12 +643,7 @@ function parse_result(command, infos_array) {
|
||||||
//~ console.log("stopping upload...");
|
//~ console.log("stopping upload...");
|
||||||
//~ console.log(infos_array);
|
//~ console.log(infos_array);
|
||||||
//~ destroy_upload_status();
|
//~ destroy_upload_status();
|
||||||
} //else {
|
}
|
||||||
//~ console.log("displaying status");
|
|
||||||
//~ console.log(infos_array);
|
|
||||||
//~ if (infos_array.total_count) {
|
|
||||||
//~ display_upload_status(infos_array)
|
|
||||||
//}
|
|
||||||
} else {
|
} else {
|
||||||
setTimeout(send_ajax_cmd, 180, "/all/list");
|
setTimeout(send_ajax_cmd, 180, "/all/list");
|
||||||
setTimeout(send_ajax_cmd, 180, "/all/status");
|
setTimeout(send_ajax_cmd, 180, "/all/status");
|
||||||
|
@ -685,7 +710,6 @@ function sync_host() {
|
||||||
// Update status
|
// Update status
|
||||||
currentUser.status_all = t9n[LOCALE].sync;
|
currentUser.status_all = t9n[LOCALE].sync;
|
||||||
// Create dialog
|
// Create dialog
|
||||||
// TODO : display grayed out dialog when host is queued for upload
|
|
||||||
display_upload_status(command_q);
|
display_upload_status(command_q);
|
||||||
// Set current host
|
// Set current host
|
||||||
currentUser.last_ul_host = host;
|
currentUser.last_ul_host = host;
|
||||||
|
@ -695,6 +719,7 @@ function sync_host() {
|
||||||
} else {
|
} else {
|
||||||
console.log("Nothing else in the queue !");
|
console.log("Nothing else in the queue !");
|
||||||
// Make sure sane values are set back
|
// Make sure sane values are set back
|
||||||
|
unfreeze_all_containers();
|
||||||
currentUser.last_ul_host = 0;
|
currentUser.last_ul_host = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -704,6 +729,20 @@ function sync_host() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function cancel_sync(host_btn_id){
|
||||||
|
let host = host_btn_id.id.split("_")[2];
|
||||||
|
let command = "/sync/" + host;
|
||||||
|
if (currentUser.ul_queue){
|
||||||
|
let item_index = currentUser.ul_queue.indexOf(command);
|
||||||
|
// Skip [0]
|
||||||
|
if (item_index > 0){
|
||||||
|
console.log(host + " unqueuing");
|
||||||
|
currentUser.ul_queue.splice(item_index, 1);
|
||||||
|
unfreeze_queued_container(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
addEventListener("DOMContentLoaded", function() {
|
addEventListener("DOMContentLoaded", function() {
|
||||||
//~ adjust_timeline();
|
//~ adjust_timeline();
|
||||||
|
@ -716,11 +755,11 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
let command = clickedButton.value;
|
let command = clickedButton.value;
|
||||||
|
|
||||||
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
if ( command.indexOf("/reboot" ) > -1 || command.indexOf("/poweroff") > -1 ) {
|
||||||
// TODO : l10n
|
if (command.indexOf("/all" ) == -1){
|
||||||
if ( !confirm(t9n[LOCALE].confirmMessage) ) {
|
if ( !confirm(t9n[LOCALE].confirmMessage) ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if ( command == "/scan" ) {
|
} else if ( command == "/scan" ) {
|
||||||
document.getElementById("status_all").innerHTML = currentUser.status_all;
|
document.getElementById("status_all").innerHTML = currentUser.status_all;
|
||||||
|
|
||||||
|
@ -741,27 +780,36 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
request.onload = send_ajax_cmd(command);
|
request.onload = send_ajax_cmd(command);
|
||||||
|
|
||||||
} else if ( command.indexOf("/sync/") > -1 ) {
|
} else if ( command.indexOf("/sync/") > -1 ) {
|
||||||
console.log("Sync command detected");
|
if ( command.indexOf("/all") > -1 ) {
|
||||||
|
console.log("Sync all request");
|
||||||
|
// If command is /sync/all, add live hosts to sync queue
|
||||||
|
if (currentUser.hosts_up){
|
||||||
|
currentUser.hosts_up.forEach(function(host){
|
||||||
|
let command = "/sync/" + host;
|
||||||
|
if (currentUser.ul_queue.indexOf(command) == -1){
|
||||||
|
console.log("Adding " + host + " to sync queue...");
|
||||||
|
currentUser.ul_queue.push(command);
|
||||||
|
freeze_queued_container(command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (currentUser.ul_queue.length > 1){
|
||||||
|
sync_host();
|
||||||
|
}
|
||||||
|
// Prevent request
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// If command not already in queue, add it
|
// If command not already in queue, add it
|
||||||
if (!currentUser.ul_queue.includes(command)){
|
if (!currentUser.ul_queue.includes(command)){
|
||||||
console.log("Queuing command...");
|
|
||||||
currentUser.ul_queue.push(command);
|
currentUser.ul_queue.push(command);
|
||||||
freeze_queued_container(command);
|
freeze_queued_container(command);
|
||||||
} else {
|
} else {
|
||||||
console.log("Command already in queue...");
|
console.log("Command already in queue...");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
sync_host();
|
sync_host();
|
||||||
//~ if (currentUser.ul_queue.length > 1){
|
// Prevent request
|
||||||
//~ if (currentUser.freeze_ul){
|
|
||||||
//~ console.log("UL freezed, wait for queue to complete.")
|
|
||||||
// Offload command sending to sync_host()
|
|
||||||
return 1;
|
return 1;
|
||||||
//~ }
|
|
||||||
//~ currentUser.status_all = t9n[LOCALE].sync;
|
|
||||||
//~ // Display dialog
|
|
||||||
//~ display_upload_status(command);
|
|
||||||
//~ // Request values to fill dialog
|
|
||||||
//~ setTimeout(send_ajax_cmd, 1000, command + "/status");
|
|
||||||
}
|
}
|
||||||
else if ( command.indexOf("/browse") > -1 ) {
|
else if ( command.indexOf("/browse") > -1 ) {
|
||||||
request.onload = send_ajax_cmd("/all/browse");
|
request.onload = send_ajax_cmd("/all/browse");
|
||||||
|
|
|
@ -63,7 +63,12 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
/*
|
/*
|
||||||
.client_container .left_col{border-right: #a8a8a8 2px solid;}
|
.client_container .left_col{border-right: #a8a8a8 2px solid;}
|
||||||
*/
|
*/
|
||||||
.client_container .button{}
|
.client_container .btn {
|
||||||
|
min-width:1em;
|
||||||
|
}
|
||||||
|
.client_container .right_col .btn {
|
||||||
|
min-width: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
.client_container .upload_dialog_cont {
|
.client_container .upload_dialog_cont {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -122,6 +127,7 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
margin-top:1em;
|
margin-top:1em;
|
||||||
background-color: #33333361;
|
background-color: #33333361;
|
||||||
}
|
}
|
||||||
|
|
||||||
[id^="tl_cont"] {
|
[id^="tl_cont"] {
|
||||||
float: left;
|
float: left;
|
||||||
width: 10%;
|
width: 10%;
|
||||||
|
@ -152,7 +158,7 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
.buttons {width:75%;margin:auto;text-align: center;padding: 2em;}
|
.buttons {width:75%;margin:auto;text-align: center;padding: 2em;}
|
||||||
.btn {
|
.btn {
|
||||||
margin:auto;
|
margin:auto;
|
||||||
width:3em;
|
min-width:2em;
|
||||||
height: 4em;
|
height: 4em;
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -182,6 +188,11 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
}
|
}
|
||||||
.btn_txt {display: block;font-size: small;}
|
.btn_txt {display: block;font-size: small;}
|
||||||
|
|
||||||
|
.btn-unqueue {background: #bbb;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
#delete_btn {
|
#delete_btn {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
|
@ -199,6 +210,7 @@ tr:nth-child(2n+1) {background-color: #888;}
|
||||||
|
|
||||||
/*Right column*/
|
/*Right column*/
|
||||||
.right_col {width: 71%;display: inline-block;}
|
.right_col {width: 71%;display: inline-block;}
|
||||||
|
|
||||||
/*Left column*/
|
/*Left column*/
|
||||||
.left_col {width: 28%;display: inline-block;float: left;clear: left;}
|
.left_col {width: 28%;display: inline-block;float: left;clear: left;}
|
||||||
.left_col button {
|
.left_col button {
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
<button value="/all/clear" class="command btn btn-block btn-lg btn-default" role="button">X<span class="btn_txt">{{gui_l10n['str_clear']}}</span></button>
|
<button value="/all/clear" class="command btn btn-block btn-lg btn-default" role="button">X<span class="btn_txt">{{gui_l10n['str_clear']}}</span></button>
|
||||||
<button value="/all/move/0/1" class="command btn btn-block btn-lg btn-default" role="button">β<span class="btn_txt">{{gui_l10n['str_sort']}}</span></button>
|
<button value="/all/move/0/1" class="command btn btn-block btn-lg btn-default" role="button">β<span class="btn_txt">{{gui_l10n['str_sort']}}</span></button>
|
||||||
<button value="/sync/all" class="command btn btn-block btn-lg btn-default" role="button">↭<span class="btn_txt">{{gui_l10n['str_sync']}}</span></button>
|
<button value="/sync/all" class="command btn btn-block btn-lg btn-default" role="button">↭<span class="btn_txt">{{gui_l10n['str_sync']}}</span></button>
|
||||||
|
<button value="/poweroff/all" onclick="exec_all_hosts('poweroff')" class="command btn btn-block btn-lg btn-default" role="button">⏻<span class="btn_txt">{{gui_l10n['str_poweroff_all']}}</span></button>
|
||||||
|
<button value="/reboot/all" onclick="exec_all_hosts('reboot')" class="command btn btn-block btn-lg btn-default" role="button">↺<span class="btn_txt">{{gui_l10n['str_reboot_all']}}</span></button>
|
||||||
<button id="delete_btn" ondrop="drop(event, this)", ondragover="drag_over_bin(event)" ondragleave="drag_leave_bin(event)" class="delete_btn" role="button">🗑<span class="btn_txt">{{gui_l10n['str_delete']}}</span></button>
|
<button id="delete_btn" ondrop="drop(event, this)", ondragover="drag_over_bin(event)" ondragleave="drag_leave_bin(event)" class="delete_btn" role="button">🗑<span class="btn_txt">{{gui_l10n['str_delete']}}</span></button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue