Commit 84db18c6 authored by Stavros Sachtouris's avatar Stavros Sachtouris Committed by Giorgos Korfiatis
Browse files

Implement settings GUI window and functionalities

parent d5f7bfe8
......@@ -8,7 +8,7 @@
<style>
.box {
margin: 0 auto;
width: 95%;
width: 90%;
height: 256px;
overflow: auto;
background: #e0e0e0;
......@@ -22,9 +22,9 @@
<h3>Agkyra... it syncs</h3>
<p><b>Agkyra</b> is a minimal syncing client for Pithos+.<br/>It syncs a Pithos+ container with a local folder.</p>
<p class="disclaimer">Developed and supported by the Okeanos/Synnefo development team of GRNET<br/><b>okeanos-dev@grnet.gr</b></p>
<p class="disclaimer">Developed and supported by the Okeanos/Synnefo development team of GRNET<br/><b>contact: okeanos-dev@grnet.gr</b></p>
<h3 class="disclaimer">Copyright 2015: Greek Research and Technology Network<br/>Licensed under:</h3>
<h3 class="disclaimer">(C) 2015: Greek Research and Technology Network<br/>Licensed under:</h3>
<embed class="box" src="COPYRIGHT" />
</div>
<footer class="footer js-footer">
......
<!DOCTYPE html>
<html>
<head><title>GUI for Agkyra Pithos+ Syncing Client</title></head>
<body>
<script src="protocol.js"></script>
<script src="settings.js"></script>
<script type="text/javascript">
// Setup GUI
var windows = {
"settings": null,
"about": null,
"index": gui.Window.get()
}
function closeWindows() {
for (win in windows) if (windows[win]) windows[win].close();
}
// GUI components
var tray = new gui.Tray({
// tooltip: 'Paused (0% synced)',
title: 'Agkyra syncs with Pithos+',
icon: 'images/tray.png'
});
var menu = new gui.Menu();
// Progress and Pause
var start_syncing = 'Start Syncing';
var start_icon = 'images/play.png';
var pause_syncing = 'Pause Syncing';
var paused = true;
progress_item = new gui.MenuItem({
// progress menu item
label: 'Initializing',
type: 'normal',
enabled: false
});
menu.append(progress_item);
menu.append(new gui.MenuItem({type: 'separator'}));
pause_item = new gui.MenuItem({
// pause menu item
icon: 'images/play_pause.png',
label: '',
type: 'normal',
click: function() {
if (paused) {post_start(socket);} else {post_pause(socket);}
}
});
pause_item.enabled = false;
menu.append(pause_item);
// Update progress
window.setInterval(function() {
var status = globals['status'];
var new_progress = progress_item.label;
var new_pause = pause_item.label;
var menu_modified = false;
if (status['paused'] !== null) {
switch(pause_item.label) {
case pause_syncing: if (status['paused']) {
// Update to "Paused - start syncing"
paused = true;
new_pause = start_syncing;
menu_modified = true;
} // else continue syncing
new_progress = status['progress'] + '%' + ' synced';
break;
case start_syncing: if (status['paused']) return;
// else update to "Syncing - pause syncing"
paused = false;
new_pause = pause_syncing;
new_progress = status['progress'] + '%' + ' synced';
menu_modified = true;
break;
default:
if (status['paused']) {new_pause = start_syncing; paused=true;}
else {new_pause = pause_syncing; paused=false;}
new_progress = status['progress'] + '%' + ' synced';
pause_item.enabled = true;
menu_modified = true;
}
}
if (new_pause != pause_item.label) {
pause_item.label = new_pause;
menu_modified = true;
}
if (new_progress != progress_item.label) {
progress_item.label = new_progress;
menu_modified = true;
}
if (menu_modified) {
if (paused) progress_item.label += ' - paused';
tray.menu = menu;
}
get_status(socket);
}, 1500);
// Menu actions contents
menu.append(new gui.MenuItem({
label: 'Open local folder',
icon: 'images/folder.png',
click: function () {
var dir = globals['settings']['directory'];
console.log('Open ' + dir);
gui.Shell.showItemInFolder(dir);
}
}));
menu.append(new gui.MenuItem({
label: 'Launch Pithos+ page',
icon: 'images/pithos.png',
click: function () {
var pithos_url = globals['settings']['pithos_url'];
console.log('Visit ' + pithos_url);
gui.Shell.openExternal(pithos_url);
}
}));
// Settings and About
menu.append(new gui.MenuItem({type: 'separator'}));
menu.append(new gui.MenuItem({
label: 'Settings',
icon: 'images/settings.png',
click: function () {
export_settings(globals.settings);
if (windows['settings']) windows['settings'].close();
windows['settings'] = gui.Window.open("settings.html", {
toolbar: false, focus: true,
width: 841, height: 520,
});
windows['settings'].on('closed', function() {
new_settings = import_settings();
console.log('Settings windows is closed, PUT');
put_settings(socket, new_settings);
get_settings(socket);
});
},
}));
menu.append(new gui.MenuItem({
label: 'About',
icon: 'images/about.png',
click: function () {
if (windows['about']) windows['about'].close();
windows['about'] = gui.Window.open("about.html", {
toolbar: false, resizable: false, focus: true,
width: 679, height: 420
});
}
}));
// Quit
menu.append(new gui.MenuItem({type: 'separator'}));
menu.append(new gui.MenuItem({
label: 'Quit Agkyra',
icon: 'images/exit.png',
click: function() {post_shutdown(socket);}
}));
tray.menu = menu;
</script>
</body>
</html>
\ No newline at end of file
{
"name": "Agkyra",
"main": "index.html",
"main": "menu.html",
"description": "A Pithos+ Syncing Client for Desktop",
"version": 0.0,
"window": {
......
......@@ -10,7 +10,6 @@ function send_json(socket, msg) {
socket.send(JSON.stringify(msg))
}
var requests = []
var globals = {
'settings': {
'token': null,
......@@ -25,7 +24,6 @@ var globals = {
// Protocol: requests ::: responses
function post_gui_id(socket) {
requests.push('post gui_id');
send_json(socket, {"method": "post", "gui_id": cnf['gui_id']})
} // expected response: {"ACCEPTED": 202}
......@@ -36,30 +34,25 @@ function post_shutdown(socket) {
function post_pause(socket) {
console.log('SEND post pause');
requests.push('post pause');
send_json(socket, {'method': 'post', 'path': 'pause'});
} // expected response: {"OK": 200}
function post_start(socket) {
console.log('SEND post start');
requests.push('post start');
send_json(socket, {'method': 'post', 'path': 'start'});
} // expected response: {"OK": 200}
function get_settings(socket) {
requests.push('get settings');
send_json(socket, {'method': 'get', 'path': 'settings'});
} // expected response: {settings JSON}
function put_settings(socket, new_settings) {
requests.push('put settings');
new_settings['method'] = 'put';
new_settings['path'] = 'settings';
send_json(socket, new_settings);
} // expected response: {"CREATED": 201}
function get_status(socket) {
requests.push('get status');
send_json(socket, {'method': 'get', 'path': 'status'});
} // expected response {"progress": ..., "paused": ...}
......@@ -72,7 +65,8 @@ socket.onopen = function() {
}
socket.onmessage = function(e) {
var r = JSON.parse(e.data)
switch(requests.shift()) {
//console.log('RECV: ' + r['action'])
switch(r['action']) {
case 'post gui_id':
if (r['ACCEPTED'] === 202) {
get_settings(this);
......@@ -101,7 +95,8 @@ socket.onmessage = function(e) {
console.log('Helper: ' + JSON.stringify(r));
}
break;
case 'get status': globals['status'] = r;
case 'get status':
globals['status'] = r;
break;
default:
console.log('Incomprehensible response ' + r);
......@@ -110,153 +105,9 @@ socket.onmessage = function(e) {
};
socket.onerror = function (e) {
console.log('GUI - helper error' + e.data);
gui.Window.get().close();
closeWindows();
}
socket.onclose = function() {
console.log('Connection to helper closed');
closeWindows();
}
// Setup GUI
var windows = {
"settings": null,
"about": null,
"index": gui.Window.get()
}
function closeWindows() {
for (win in windows) if (windows[win]) windows[win].close();
}
// GUI components
var tray = new gui.Tray({
// tooltip: 'Paused (0% synced)',
title: 'Agkyra syncs with Pithos+',
icon: 'images/tray.png'
});
var menu = new gui.Menu();
// Progress and Pause
var start_syncing = 'Start Syncing';
var start_icon = 'images/play.png';
var pause_syncing = 'Pause Syncing';
var paused = true;
progress_item = new gui.MenuItem({
// progress menu item
label: 'Initializing',
type: 'normal',
enabled: false
});
menu.append(progress_item);
menu.append(new gui.MenuItem({type: 'separator'}));
pause_item = new gui.MenuItem({
// pause menu item
icon: 'images/play_pause.png',
label: '',
type: 'normal',
click: function() {
if (paused) {post_start(socket);} else {post_pause(socket);}
}
});
pause_item.enabled = false;
menu.append(pause_item);
// Update progress
window.setInterval(function() {
var status = globals['status'];
var new_progress = progress_item.label;
var new_pause = pause_item.label;
var menu_modified = false;
if (status['paused'] !== null) {
switch(pause_item.label) {
case pause_syncing: if (status['paused']) {
// Update to "Paused - start syncing"
paused = true;
new_pause = start_syncing;
menu_modified = true;
} // else continue syncing
new_progress = status['progress'] + '%' + ' synced';
break;
case start_syncing: if (status['paused']) return;
// else update to "Syncing - pause syncing"
paused = false;
new_pause = pause_syncing;
new_progress = status['progress'] + '%' + ' synced';
menu_modified = true;
break;
default:
if (status['paused']) {new_pause = start_syncing; paused=true;}
else {new_pause = pause_syncing; paused=false;}
new_progress = status['progress'] + '%' + ' synced';
pause_item.enabled = true;
menu_modified = true;
}
}
if (new_pause != pause_item.label) {
pause_item.label = new_pause;
menu_modified = true;
}
if (new_progress != progress_item.label) {
progress_item.label = new_progress;
menu_modified = true;
}
if (menu_modified) {
if (paused) progress_item.label += ' - paused';
tray.menu = menu;
}
get_status(socket);
}, 1500);
// Menu actions contents
menu.append(new gui.MenuItem({
label: 'Open local folder',
icon: 'images/folder.png',
click: function () {
var dir = globals['settings']['directory'];
console.log('Open ' + dir);
gui.Shell.showItemInFolder(dir);
}
}));
menu.append(new gui.MenuItem({
label: 'Launch Pithos+ page',
icon: 'images/pithos.png',
click: function () {
var pithos_url = globals['settings']['pithos_url'];
console.log('Visit ' + pithos_url);
gui.Shell.openExternal(pithos_url);
}
}));
// Settings and About
menu.append(new gui.MenuItem({type: 'separator'}));
menu.append(new gui.MenuItem({
label: 'Settings',
icon: 'images/settings.png',
click: function () {
if (windows['settings']) windows['settings'].close();
windows['settings'] = gui.Window.open("settings.html", {
"toolbar": false, "focus": true});
}
}));
menu.append(new gui.MenuItem({
label: 'About',
icon: 'images/about.png',
click: function () {
if (windows['about']) windows['about'].close();
windows['about'] = gui.Window.open("about.html", {
"toolbar": false, "resizable": false, "focus": true});
}
}));
// Quit
menu.append(new gui.MenuItem({type: 'separator'}));
menu.append(new gui.MenuItem({
label: 'Quit Agkyra',
icon: 'images/exit.png',
click: function() {post_shutdown(socket);}
}));
tray.menu = menu;
<!DOCTYPE html>
<html>
<head>
<title>User Settings</title>
<link rel="stylesheet" href="static/stylesheets/normalize.css" />
<link rel="stylesheet" href="static/stylesheets/main.css" />
<script src="static/js/jquery.js"></script>
<script src="static/js/common.js"></script>
</head>
<script src="settings.js"></script>
<script type="text/javascript">
var fs = require('fs');
var exclude = null;
$(document).ready(function() {
var url = get_setting('url');
if (url) $('#cloud-url').val(url);
var token = get_setting('token');
if (token) $('#token').val(token);
var container = get_setting('container');
if (container) $('#container').val(container);
var directory = get_setting('directory');
if (directory) $('#directory').html(directory);
exclude = get_setting('exclude');
if (exclude) $('#exclude').val(
fs.readFileSync(exclude, encoding='utf-8'));
});
function update_exclude(new_content) {
if (exclude) fs.writeFile(exclude, new_content);
}
</script>
</head>
<body>
<div class="row js-main">
<header>
......@@ -23,18 +44,20 @@
</div>
<div class="small-9 columns">
<input type="text" id="cloud-url" placeholder="Authentication URL">
<small class="error">Invalid entry</small>
<input type="text" id="cloud-url" placeholder="Authentication URL"
onchange="set_setting('url', $(this).val())">
<small class="error" style="visibility: hidden">Invalid entry</small>
</div>
</div>
<div class="row error">
<div class="small-3 columns">
<label for="token" class="right inline">Token</label>
<label for="token" class="right inline">User token</label>
</div>
<div class="small-9 columns">
<input type="text" id="token" placeholder="User token">
<small class="error">Invalid entry</small>
<input type="text" id="token" placeholder="User token"
onchange="set_setting('token', $(this).val())">
<small class="error" style="visibility: hidden">Invalid entry</small>
</div>
</div>
</fieldset>
......@@ -45,31 +68,43 @@
<label for="container" class="right inline">Remote container</label>
</div>
<div class="small-9 columns">
<input type="text" id="container" value="pithos">
<input type="text" id="container" placeholder="Pithos+ container"
onchange="set_setting('container', $(this).val())">
</div>
</div>
<div class="row">
<div class="small-3 columns">
<label for="" class="right inline">Local directory</label>
<label for="directory" class="right inline">Local directory</label>
</div>
<div class="small-9 columns" id="directory">
No dir chosen</div>
</div>
<div class="row">
<div class="small-3 columns">Change it:</div>
<div class="small-9 columns">
<input type="file" nwdirectory />
<!-- TODO: change the label of this field -->
<input type="file" nwdirectory
onchange="
$('#directory').html($(this).val());
set_setting('directory', $(this).val());
" />
</div>
<script>var gui=require('nw.gui');</script>
</div>
<div class="row">
<div class="small-3 columns">
<label for="" class="right inline">Exclude these items from syncing</label>
</div>
<div class="small-9 columns">
<textarea>tmp/
skata*</textarea>
<textarea id="exclude"
onchange="update_exclude($(this).val())">
</textarea>
</div>
</div>
</fieldset>
<div class="clearfix">
<a class="button right">Sync</a>
</div>
<!--<div class="clearfix">
<a id="sync_button" class="button right"
onclick="window.close();">OK</a>
</div> -->
</div>
<div class="small-4 columns">
......
/**
* Methods for accessing settings between documents
*/
function export_settings(settings) {
global.settings = settings;
}
function import_settings() {
return global.settings;
}
function get_setting(key) {
return global.settings[key];
}
function set_setting(key, val) {
global.settings[key] = val;
}
from ws4py.websocket import WebSocket
import json
import logging
from os.path import abspath
LOG = logging.getLogger(__name__)
......@@ -11,23 +12,25 @@ class WebSocketProtocol(WebSocket):
-- INTERRNAL HANDSAKE --
GUI: {"method": "post", "gui_id": <GUI ID>}
HELPER: {"ACCEPTED": 202}" or "{"REJECTED": 401}
HELPER: {"ACCEPTED": 202, "method": "post"}" or
"{"REJECTED": 401, "action": "post gui_id"}
-- SHUT DOWN --
GUI: {"method": "post", "path": "shutdown"}
-- PAUSE --
GUI: {"method": "post", "path": "pause"}
HELPER: {"OK": 200} or error
HELPER: {"OK": 200, "action": "post pause"} or error
-- start --
GUI: {"method": "post", "path": "start"}
HELPER: {"OK": 200} or error
HELPER: {"OK": 200, "action": "post start"} or error
-- GET SETTINGS --
GUI: {"method": "get", "path": "settings"}
HELPER:
{
"action": "get settings",
"token": <user token>,
"url": <auth url>,
"container": <container>,
......@@ -45,11 +48,13 @@ class WebSocketProtocol(WebSocket):
"pithos_url": <pithos URL>,
"exclude": <file path>
}
HELPER: {"CREATED": 201} or {<ERROR>: <ERROR CODE>}
HELPER: {"CREATED": 201, "action": "put settings",} or
{<ERROR>: <ERROR CODE>, "action": "get settings",}
-- GET STATUS --
GUI: {"method": "get", "path": "status"}
HELPER: ""progress": <int>, "paused": <boolean>} or {<ERROR>: <ERROR CODE>}
HELPER: {"progress": <int>, "paused": <boolean>, "action": "get status"} or
{<ERROR>: <ERROR CODE>, "action": "get status"}
"""
gui_id = None
......@@ -59,7 +64,7 @@ class WebSocketProtocol(WebSocket):
url=' https://accounts.okeanos.grnet.gr/identity/v2.0',
container='pithos',
directory='/tmp/.',
exclude='agkyra.log',
exclude=abspath('exclude.cnf'),
pithos_url='https://pithos.okeanos.grnet.gr/ui/')
status = dict(progress=0, paused=False)
......@@ -73,6 +78,9 @@ class WebSocketProtocol(WebSocket):
def get_settings(self):
return self.settings