From 73a369a728bfe4baf12056536235798aca6ec073 Mon Sep 17 00:00:00 2001
From: Stavros Sachtouris <saxtouri@admin.grnet.gr>
Date: Mon, 13 Jul 2015 16:25:40 +0300
Subject: [PATCH] Help GUI user to resolve folder/container problems

Implement a dialogue window which is pop up when the directory or
container have been removed. The proposed solution is to create
the missing piece and reset the syncing process. If the user
accepts, the proposed solution is enforced. Otherwise the system
stays in a non working mode, where the user can fix the problem by
handling the settings.
---
 agkyra/nwgui/dialogue.html    | 42 +++++++++++++++++++++++++++++++++++
 agkyra/nwgui/menu.html        | 27 ++++++++++++++++++----
 agkyra/nwgui/notify.js        |  8 +------
 agkyra/nwgui/protocol.js      |  5 +++++
 agkyra/nwgui/settings.html    |  3 ++-
 agkyra/nwgui/settings.js      |  8 +++++++
 agkyra/protocol.py            | 32 ++++++++++++++++++--------
 agkyra/ui_data/common_el.json |  6 +++++
 agkyra/ui_data/common_en.json |  6 +++++
 9 files changed, 116 insertions(+), 21 deletions(-)
 create mode 100644 agkyra/nwgui/dialogue.html

diff --git a/agkyra/nwgui/dialogue.html b/agkyra/nwgui/dialogue.html
new file mode 100644
index 0000000..db7339d
--- /dev/null
+++ b/agkyra/nwgui/dialogue.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2015 GRNET S.A.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+<head>
+    <meta charset="UTF-8" />
+    <title>Agkyra</title>
+    <link rel="stylesheet" href="static/stylesheets/main.css" />
+    <script src="static/js/jquery.js"></script>
+    <script src="settings.js"></script>
+    <script type="text/javascript">var d = get_dialogue();</script>
+</head>
+<body>
+    <div class="wrapper"><form>
+            <h4 id="text">Do you agree to a fuzzy question which will be used against you in the future?</h4>
+        <div class="clearfix small-12 columns">
+            <a id="OK" class="button right" onclick="set_dialogue(d.msg, d.terms, true); window.close();">OK</a>
+            <a id="CANCEL" class="button right" onclick="window.close()">Cancel</a>
+        </div>
+    </form></div>
+
+    <script type="text/javascript">
+        document.getElementById('text').innerHTML = d.msg;
+        document.getElementById('OK').innerHTML = d.terms.OK;
+        document.getElementById('CANCEL').innerHTML = d.terms.CANCEL;
+    </script>
+</body>
+</html>
diff --git a/agkyra/nwgui/menu.html b/agkyra/nwgui/menu.html
index 6c758a5..14c5699 100644
--- a/agkyra/nwgui/menu.html
+++ b/agkyra/nwgui/menu.html
@@ -31,7 +31,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 var windows = {
   "settings": null,
   "about": null,
-  "index": gui.Window.get()
+  "index": gui.Window.get(),
+  "dialogue": null
 }
 function closeWindows() {
   for (win in windows) if (windows[win]) windows[win].close();
@@ -221,6 +222,7 @@ window.setInterval(function() {
   var new_progress = COMMON.NOTIFICATION[globals.status.code];
   var new_pause = '';
   var tray_icon_off = false;
+  var dialogue_msg = null;
   switch(globals.status.code) {
     case STATUS['UNINITIALIZED']:
     case STATUS['INITIALIZING']:
@@ -252,11 +254,27 @@ window.setInterval(function() {
         new_progress += ', '+ COMMON.MENU.REMAINING.replace(
                     '%s', remaining(globals.status));
     break;
-    case STATUS['SETTINGS MISSING']:
-    case STATUS['AUTH URL ERROR']:
-    case STATUS['TOKEN ERROR']:
     case STATUS['DIRECTORY ERROR']:
+      dialogue_msg = COMMON.DIALOGUE[globals.status.code].replace(
+        '%s', globals.settings.directory);
     case STATUS['CONTAINER ERROR']:
+      if (!dialogue_msg)
+        dialogue_msg = COMMON.DIALOGUE[globals.status.code].replace(
+          '%s', globals.settings.container);
+      set_dialogue(dialogue_msg, COMMON.DIALOGUE, false);
+      if (windows.dialogue === null) {
+        windows['dialogue'] = gui.Window.open(
+          'dialogue.html', {
+            toolbar: false, focus: true,
+            width: 550, height: 220});
+        windows['dialogue'].on('closed', function() {
+          var d = get_dialogue();
+          if (d.response) post_force(socket);
+        });
+      }
+    case STATUS['AUTH URL ERROR']:
+    case STATUS['TOKEN ERROR']:
+    case STATUS['SETTINGS MISSING']:
       deactivate_menu();
       new_pause = COMMON.MENU.INACTIVE;
       settings_menu.enabled = true;
@@ -282,6 +300,7 @@ window.setInterval(function() {
     tray.icon = tray_icon.off;
   else if (!(tray_icon_off || tray.icon === tray_icon.on))
     tray.icon = tray_icon.on;
+
   get_status(socket);
 }, 1500);
 
diff --git a/agkyra/nwgui/notify.js b/agkyra/nwgui/notify.js
index faff8b2..4dd3629 100644
--- a/agkyra/nwgui/notify.js
+++ b/agkyra/nwgui/notify.js
@@ -33,15 +33,9 @@ var ntf_timeout = {
     'error': 4000
 }
 
-var notify_menu = new gui.MenuItem({
-    label: 'Notifications',
-    icon: 'static/images/play_pause.png',
-    iconIsTemplate: false,
-});
-
 function notify_user(msg, level, ntf_title) {
     var n = new Notification(ntf_title[level], {
         lang: 'utf-8', body: msg, icon: ntf_icon[level]
     });
     setTimeout(n.close.bind(n), ntf_timeout[level]);
-}
\ No newline at end of file
+}
diff --git a/agkyra/nwgui/protocol.js b/agkyra/nwgui/protocol.js
index de45442..e673da1 100644
--- a/agkyra/nwgui/protocol.js
+++ b/agkyra/nwgui/protocol.js
@@ -79,6 +79,11 @@ function post_start(socket) {
   send_json(socket, {'method': 'post', 'path': 'start'});
 } // expected response: {"OK": 200}
 
+function post_force(socket) {
+  log_debug('SEND post force');
+  send_json(socket, {'method': 'post', 'path': 'force'});
+}
+
 function get_settings(socket) {
   send_json(socket, {'method': 'get', 'path': 'settings'});
 } // expected response: {settings JSON}
diff --git a/agkyra/nwgui/settings.html b/agkyra/nwgui/settings.html
index b736e0b..7cf4a3d 100644
--- a/agkyra/nwgui/settings.html
+++ b/agkyra/nwgui/settings.html
@@ -282,7 +282,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
                             <input type="text" id="cloud-url" placeholder="Authentication URL"
                             onchange="
                             var identity_url = $(this).val().replace(/\/+$/, '');
-                            settings['url'] = identity_url;                            refresh_endpoints(identity_url);
+                            settings['url'] = identity_url;
+                            refresh_endpoints(identity_url);
                             check_cloud_url();">
                             <small>Invalid entry</small>
                         </div>
diff --git a/agkyra/nwgui/settings.js b/agkyra/nwgui/settings.js
index ef21d2a..669a43b 100644
--- a/agkyra/nwgui/settings.js
+++ b/agkyra/nwgui/settings.js
@@ -29,10 +29,18 @@ function get_setting(key) {
     return global.settings[key];
 }
 
+function get_dialogue() {
+    return global.dialogue;
+}
+
 function set_setting(key, val) {
     global.settings[key] = val;
 }
 
+function set_dialogue(msg, terms, response) {
+    global.dialogue = {msg: msg, terms: terms, response: response};
+}
+
 function refresh_endpoints(identity_url) {
     $.post(identity_url + '/tokens', function(data) {
         var endpoints = data.access.serviceCatalog
diff --git a/agkyra/protocol.py b/agkyra/protocol.py
index 3a3c137..1d208da 100644
--- a/agkyra/protocol.py
+++ b/agkyra/protocol.py
@@ -225,6 +225,10 @@ class WebSocketProtocol(WebSocket):
     GUI: {"method": "post", "path": "start"}
     HELPER: {"OK": 200, "action": "post start"} or error
 
+    -- FORCE START --
+    GUI: {"method": "post", "path": "force"}
+    HELPER: {"OK": 200, "action": "post force"} or error
+
     -- GET SETTINGS --
     GUI: {"method": "get", "path": "settings"}
     HELPER:
@@ -459,13 +463,13 @@ class WebSocketProtocol(WebSocket):
             #             d.update(unsynced=0, synced=0, failed=0)
             while msg:
                 if isinstance(msg, messaging.SyncMessage):
-                    LOG.error('UNSYNCED +1 %s' % getattr(msg, 'objname', ''))
+                    LOG.debug('UNSYNCED +1 %s' % getattr(msg, 'objname', ''))
                     self.set_status(unsynced=self.get_status('unsynced') + 1)
                 elif isinstance(msg, messaging.AckSyncMessage):
-                    LOG.error('SYNCED +1 %s' % getattr(msg, 'objname', ''))
+                    LOG.debug('SYNCED +1 %s' % getattr(msg, 'objname', ''))
                     self.set_status(synced=self.get_status('synced') + 1)
                 elif isinstance(msg, messaging.SyncErrorMessage):
-                    LOG.error('FAILED +1 %s' % getattr(msg, 'objname', ''))
+                    LOG.debug('FAILED +1 %s' % getattr(msg, 'objname', ''))
                     self.set_status(failed=self.get_status('failed') + 1)
                 elif isinstance(msg, messaging.LocalfsSyncDisabled):
                     self.set_status(code=STATUS['DIRECTORY ERROR'])
@@ -527,7 +531,7 @@ class WebSocketProtocol(WebSocket):
                     local_ok = False
                     break
                 elif isinstance(msg, messaging.PithosSyncDisabled):
-                    self.set_status(code=STATUS['CONTAINER ERRIR'])
+                    self.set_status(code=STATUS['CONTAINER ERROR'])
                     remote_ok = False
                     break
                 elif isinstance(msg, messaging.LocalfsSyncEnabled):
@@ -580,17 +584,26 @@ class WebSocketProtocol(WebSocket):
         syncer_.wait_sync_threads()
 
     def pause_sync(self):
+        """Pause syncing (assuming it is up and running)"""
         if self.syncer:
             self.syncer.stop_decide()
             self.set_status(code=STATUS['PAUSING'])
-        # syncer_ = self.syncer
-        # if syncer_ and not syncer_.paused:
-        #     Thread(target=self._pause_syncer).start()
-        #     self.set_status(code=STATUS['PAUSING'])
 
     def start_sync(self):
+        """Start syncing"""
         self.syncer.start_decide()
 
+    def force_sync(self):
+        """Force syncing, assuming there is a directory or container problem"""
+        self.set_status(code=STATUS['INITIALIZING'])
+        self.syncer_settings.purge_db_archives_and_enable()
+        self.init_sync()
+        if self.syncer:
+            self.syncer.start_decide()
+            self.set_status(code=STATUS['SYNCING'])
+        else:
+            self.set_status(code=STATUS['CRITICAL ERROR'])
+
     def send_json(self, msg):
         LOG.debug('send: %s' % msg)
         self.send(json.dumps(msg))
@@ -609,7 +622,8 @@ class WebSocketProtocol(WebSocket):
                 return
             {
                 'start': self.start_sync,
-                'pause': self.pause_sync
+                'pause': self.pause_sync,
+                'force': self.force_sync
             }[action]()
             self.send_json({'OK': 200, 'action': 'post %s' % action})
         elif r['ui_id'] == self.ui_id:
diff --git a/agkyra/ui_data/common_el.json b/agkyra/ui_data/common_el.json
index a519981..d4b3abc 100644
--- a/agkyra/ui_data/common_el.json
+++ b/agkyra/ui_data/common_el.json
@@ -13,6 +13,12 @@
         "CONTAINER ERROR": 204,
         "CRITICAL ERROR": 1000
     },
+    "DIALOGUE": {
+        "203": "Ο Ο„ΞΏΟ€ΞΉΞΊΟŒΟ‚ φάκΡλος \"%s\" δΡν υπάρχΡι. ΕπιθυμΡίτΡ τη δημιουργία Ξ½Ξ­ΞΏΟ… ΞΊΞ±ΞΉ την ΡπανΡκίνηση του συγχρονισμού;",
+        "204": "Ο απομακρυσμένος πΡριέκτης (container) \"%s\" δΡν υπάρχΡι. ΕπιθυμΡίτΡ τη δημιουργία Ξ½Ξ­ΞΏΟ… ΞΊΞ±ΞΉ την ΡπανΡκίνηση του συγχρονισμού;",
+        "OK": "ΕντάξΡι",
+        "CANCEL": "Άκυρο"
+    },
     "NOTIFICATION": {
         "0": "Ξ‘Ξ½Ξ΅Ξ½Ξ΅ΟΞ³ΟŒ",
         "1": "Εκκίνηση ...",
diff --git a/agkyra/ui_data/common_en.json b/agkyra/ui_data/common_en.json
index 84d5f04..6523440 100644
--- a/agkyra/ui_data/common_en.json
+++ b/agkyra/ui_data/common_en.json
@@ -27,6 +27,12 @@
         "204": "Remote container error",
         "1000": "Critical error"
     },
+    "DIALOGUE": {
+        "203": "Local directory \"%s\" does not exist. Would you like Agkyra to create a new one and restart the syncing process?",
+        "204": "Remote container \"%s\" does not exist. Would you like Agkyra to create a new one and restart the syncing process?",
+        "OK": "OK",
+        "CANCEL": "Cancel"
+    },
     "MENU": {
         "TITLE": "Agkyra Syncing Client",
         "START": "Start syncing",
-- 
GitLab