From e83e589cf0e142cf5489dec75f8557b059dd5211 Mon Sep 17 00:00:00 2001
From: yflory <yann.flory@xwiki.com>
Date: Wed, 14 Feb 2018 19:41:07 +0100
Subject: [PATCH] Kick from pad when an owned channel is deleted + whiteboard
 and poll

---
 customize.dist/translations/messages.fr.js |  1 +
 customize.dist/translations/messages.js    |  1 +
 rpc.js                                     |  2 +-
 www/common/common-ui-elements.js           |  2 -
 www/common/outer/async-store.js            |  2 +-
 www/common/outer/userObject.js             | 14 ++++---
 www/common/pinpad.js                       |  4 +-
 www/common/sframe-app-framework.js         |  9 ++++-
 www/common/toolbar3.js                     |  1 +
 www/common/userObject.js                   |  7 ++--
 www/drive/inner.js                         |  6 ++-
 www/poll/app-poll.less                     |  2 +
 www/poll/inner.js                          | 41 +++++++++++++++++++--
 www/poll/main.js                           |  4 +-
 www/whiteboard/app-whiteboard.less         |  2 +
 www/whiteboard/inner.js                    | 43 +++++++++++++++++++++-
 www/whiteboard/main.js                     |  4 +-
 17 files changed, 122 insertions(+), 23 deletions(-)

diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index 87caa929f..2d21cf9a6 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -33,6 +33,7 @@ define(function () {
     out.anonymousStoreDisabled = "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive.";
     out.expiredError = "Ce pad a atteint sa date d'expiration est n'est donc plus disponible.";
     out.expiredErrorCopy = ' Vous pouvez toujours copier son contenu ailleurs en appuyant sur <em>Échap</em>.<br> Dés que vous aurez quitté la page, il sera impossible de le récupérer.';
+    out.deletedError = 'Ce pad a été supprimé par son propriétaire et n\'est donc plus disponible.';
 
     out.loading = "Chargement...";
     out.error = "Erreur";
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 367e704c9..0113dbd84 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -34,6 +34,7 @@ define(function () {
     out.anonymousStoreDisabled = "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive.";
     out.expiredError = 'This pad has reached its expiration time and is no longer available.';
     out.expiredErrorCopy = ' You can still copy the content to another location by pressing <em>Esc</em>.<br>Once you leave this page, it will disappear forever!';
+    out.deletedError = 'This pad has been deleted by its owner and is no longer available.';
 
     out.loading = "Loading...";
     out.error = "Error";
diff --git a/rpc.js b/rpc.js
index 94d4d4e7e..8148c189c 100644
--- a/rpc.js
+++ b/rpc.js
@@ -1348,7 +1348,7 @@ RPC.create = function (
             case 'REMOVE_OWNED_CHANNEL':
                 return void removeOwnedChannel(Env, msg[1], publicKey, function (e, response) {
                     if (e) { return void Respond(e); }
-                    Respond(void 0, response);
+                    Respond(void 0, "OK");
                 });
             // restricted to privileged users...
             case 'UPLOAD':
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 432061d2e..e0e5f1ff7 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -1655,8 +1655,6 @@ define([
         var metadataMgr = common.getMetadataMgr();
         var type = metadataMgr.getMetadataLazy().type;
 
-        // XXX check text for pad creation screen + translate it in French
-
         var $body = $('body');
         var $creationContainer = $('<div>', { id: 'cp-creation-container' }).appendTo($body);
         var $creation = $('<div>', { id: 'cp-creation' }).appendTo($creationContainer);
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index 1ca46e7f1..ae7d752b6 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -897,7 +897,7 @@ define([
             case 'addFolder':
                 store.userObject.addFolder(data.path, data.name, cb); break;
             case 'delete':
-                store.userObject.delete(data.paths, cb, data.nocheck); break;
+                store.userObject.delete(data.paths, cb, data.nocheck, data.isOwnPadRemoved); break;
             case 'emptyTrash':
                 store.userObject.emptyTrash(cb); break;
             case 'rename':
diff --git a/www/common/outer/userObject.js b/www/common/outer/userObject.js
index 3293dcb36..70a8445f7 100644
--- a/www/common/outer/userObject.js
+++ b/www/common/outer/userObject.js
@@ -85,8 +85,11 @@ define([
             delete files[FILES_DATA][id];
         };
 
-        exp.checkDeletedFiles = function () {
-            // Nothing in OLD_FILES_DATA for workgroups
+        // Find files in FILES_DATA that are not anymore in the drive, and remove them from
+        // FILES_DATA. If there are owned pads, remove them from server too, unless the flag tells
+        // us they're already removed
+        exp.checkDeletedFiles = function (isOwnPadRemoved) {
+            // Nothing in FILES_DATA for workgroups
             if (workgroup || (!loggedIn && !config.testMode)) { return; }
 
             var filesList = exp.getFiles([ROOT, 'hrefArray', TRASH]);
@@ -96,7 +99,8 @@ define([
                     var fd = exp.getFileData(id);
                     var channelId = fd && fd.href && Hash.hrefToHexChannelId(fd.href);
                     // If trying to remove an owned pad, remove it from server also
-                    if (fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
+                    if (!isOwnPadRemoved &&
+                            fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
                         removeOwnedChannel(channelId, function (obj) {
                             if (obj && obj.error) { console.error(obj.error); }
                         });
@@ -123,7 +127,7 @@ define([
                 files[TRASH][obj.name].splice(idx, 1);
             });
         };
-        exp.deleteMultiplePermanently = function (paths, nocheck) {
+        exp.deleteMultiplePermanently = function (paths, nocheck, isOwnPadRemoved) {
             var hrefPaths = paths.filter(function(x) { return exp.isPathIn(x, ['hrefArray']); });
             var rootPaths = paths.filter(function(x) { return exp.isPathIn(x, [ROOT]); });
             var trashPaths = paths.filter(function(x) { return exp.isPathIn(x, [TRASH]); });
@@ -179,7 +183,7 @@ define([
 
             // In some cases, we want to remove pads from a location without removing them from
             // OLD_FILES_DATA (replaceHref)
-            if (!nocheck) { exp.checkDeletedFiles(); }
+            if (!nocheck) { exp.checkDeletedFiles(isOwnPadRemoved); }
         };
 
         // Move
diff --git a/www/common/pinpad.js b/www/common/pinpad.js
index 0bbaddd37..f38d7fc57 100644
--- a/www/common/pinpad.js
+++ b/www/common/pinpad.js
@@ -157,8 +157,8 @@ define([
                 }
                 rpc.send('REMOVE_OWNED_CHANNEL', channel, function (e, response) {
                     if (e) { return void cb(e); }
-                    if (response && response.length) {
-                        cb(void 0, response[0]); // I haven't tested this...
+                    if (response && response.length && response[0] === "OK") {
+                        cb();
                     } else {
                         cb('INVALID_RESPONSE');
                     }
diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js
index 2e51776d6..ef516e86d 100644
--- a/www/common/sframe-app-framework.js
+++ b/www/common/sframe-app-framework.js
@@ -340,6 +340,11 @@ define([
                 if (err.loaded) {
                     msg += Messages.expiredErrorCopy;
                 }
+            } else if (err.type === 'EDELETED') {
+                msg = Messages.deletedError;
+                if (err.loaded) {
+                    msg += Messages.expiredErrorCopy;
+                }
             }
             UI.errorLoadingScreen(msg, true, true);
         };
@@ -436,7 +441,9 @@ define([
             var priv = common.getMetadataMgr().getPrivateData();
             if (priv.isNewFile) {
                 var c = (priv.settings.general && priv.settings.general.creation) || {};
-                if (c.skip && !priv.forceCreationScreen) { return void common.createPad(c, waitFor()); }
+                if (c.skip && !priv.forceCreationScreen) {
+                    return void common.createPad(c, waitFor());
+                }
                 common.getPadCreationScreen(c, waitFor());
             }
         }).nThen(function (waitFor) {
diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js
index cb1bb764c..e014503c6 100644
--- a/www/common/toolbar3.js
+++ b/www/common/toolbar3.js
@@ -1100,6 +1100,7 @@ define([
         toolbar.deleted = function (/*userId*/) {
             toolbar.isErrorState = true;
             toolbar.connected = false;
+            updateUserList(toolbar, config);
             if (toolbar.spinner) {
                 toolbar.spinner.text(Messages.deletedFromServer);
             }
diff --git a/www/common/userObject.js b/www/common/userObject.js
index 223376963..4f7f03b23 100644
--- a/www/common/userObject.js
+++ b/www/common/userObject.js
@@ -556,17 +556,18 @@ define([
         // DELETE
         // Permanently delete multiple files at once using a list of paths
         // NOTE: We have to be careful when removing elements from arrays (trash root, unsorted or template)
-        exp.delete = function (paths, cb, nocheck) {
+        exp.delete = function (paths, cb, nocheck, isOwnPadRemoved) {
             if (sframeChan) {
                 return void sframeChan.query("Q_DRIVE_USEROBJECT", {
                     cmd: "delete",
                     data: {
                         paths: paths,
-                        nocheck: nocheck
+                        nocheck: nocheck,
+                        isOwnPadRemoved: isOwnPadRemoved
                     }
                 }, cb);
             }
-            exp.deleteMultiplePermanently(paths, nocheck);
+            exp.deleteMultiplePermanently(paths, nocheck, isOwnPadRemoved);
             if (typeof cb === "function") { cb(); }
         };
         exp.emptyTrash = function (cb) {
diff --git a/www/drive/inner.js b/www/drive/inner.js
index be4909415..2f5ff9fa0 100644
--- a/www/drive/inner.js
+++ b/www/drive/inner.js
@@ -2715,6 +2715,8 @@ define([
             UI.confirm(msgD, function(res) {
                 $(window).focus();
                 if (!res) { return; }
+                filesOp.delete(pathsList, refresh);
+                /*
                 // Try to delete each selected pad from server, and delete from drive if no error
                 var n = nThen(function () {});
                 pathsList.forEach(function (p) {
@@ -2726,10 +2728,12 @@ define([
                         sframeChan.query('Q_REMOVE_OWNED_CHANNEL', channel,
                                          waitFor(function (e) {
                             if (e) { return void console.error(e); }
-                            filesOp.delete([p], refresh);
+                            filesOp.delete([p], function () {}, false, true);
                         }));
                     });
                 });
+                n.nThen(function () { refresh(); });
+                */
             });
         };
         $contextMenu.on("click", "a", function(e) {
diff --git a/www/poll/app-poll.less b/www/poll/app-poll.less
index 88839f96e..0a5d5ca59 100644
--- a/www/poll/app-poll.less
+++ b/www/poll/app-poll.less
@@ -6,6 +6,7 @@
 @import (once) '../../customize/src/less2/include/tokenfield.less';
 @import (once) '../../customize/src/less2/include/tools.less';
 @import (once) '../../customize/src/less2/include/avatar.less';
+@import (once) '../../customize/src/less2/include/creation.less';
 
 .toolbar_main(
     @bg-color: @colortheme_poll-bg,
@@ -15,6 +16,7 @@
 .fileupload_main();
 .alertify_main();
 .tokenfield_main();
+.creation_main();
 
 @poll-fore: #555;
 
diff --git a/www/poll/inner.js b/www/poll/inner.js
index dd585ec31..f88ac5763 100644
--- a/www/poll/inner.js
+++ b/www/poll/inner.js
@@ -1119,13 +1119,34 @@ define([
         }
 
         UI.removeLoadingScreen();
-        if (isNew) {
+        var privateDat = metadataMgr.getPrivateData();
+        var skipTemp = Util.find(privateDat,
+            ['settings', 'general', 'creation', 'noTemplate']);
+        var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']);
+        if (isNew && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) {
             common.openTemplatePicker();
         }
     };
 
-    var onDisconnect = function () {
+    // Manage disconnections because of network or error
+    var onDisconnect = function (info) {
         setEditable(false);
+        if (info && ['EEXPIRED', 'EDELETED'].indexOf(info.type) !== -1) {
+            APP.toolbar.deleted();
+            var msg = info.type;
+            if (info.type === 'EEXPIRED') {
+                msg = Messages.expiredError;
+                if (info.loaded) {
+                    msg += Messages.expiredErrorCopy;
+                }
+            } else if (info.type === 'EDELETED') {
+                msg = Messages.deletedError;
+                if (info.loaded) {
+                    msg += Messages.expiredErrorCopy;
+                }
+            }
+            return void UI.errorLoadingScreen(msg, true, true);
+        }
         UI.alert(Messages.common_connectionLost, undefined, true);
     };
 
@@ -1175,6 +1196,7 @@ define([
         Title.setToolbar(APP.toolbar);
 
         var $rightside = APP.toolbar.$rightside;
+        var $drawer = APP.toolbar.$drawer;
 
         metadataMgr.onChange(function () {
             var md = copyObject(metadataMgr.getMetadata());
@@ -1189,6 +1211,9 @@ define([
         var $forgetPad = common.createButton('forget', true, {}, forgetCb);
         $rightside.append($forgetPad);
 
+        var $properties = common.createButton('properties', true);
+        $drawer.append($properties);
+
         /* save as template */
         if (!metadataMgr.getPrivateData().isTemplate) {
             var templateObj = {
@@ -1201,7 +1226,7 @@ define([
 
         /* add an export button */
         var $export = common.createButton('export', true, {}, exportFile);
-        $rightside.append($export);
+        $drawer.append($export);
 
         var $help = common.createButton('', true).click(function () { showHelp(); })
             .appendTo($rightside);
@@ -1255,6 +1280,16 @@ define([
             SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
         }).nThen(function (waitFor) {
             common.getSframeChannel().onReady(waitFor());
+        }).nThen(function (waitFor) {
+            if (!AppConfig.displayCreationScreen) { return; }
+            var priv = common.getMetadataMgr().getPrivateData();
+            if (priv.isNewFile) {
+                var c = (priv.settings.general && priv.settings.general.creation) || {};
+                if (c.skip && !priv.forceCreationScreen) {
+                    return void common.createPad(c, waitFor());
+                }
+                common.getPadCreationScreen(c, waitFor());
+            }
         }).nThen(function (/* waitFor */) {
             Test.registerInner(common.getSframeChannel());
             var metadataMgr = common.getMetadataMgr();
diff --git a/www/poll/main.js b/www/poll/main.js
index 737038ead..85bbb6f62 100644
--- a/www/poll/main.js
+++ b/www/poll/main.js
@@ -36,6 +36,8 @@ define([
         };
         window.addEventListener('message', onMsg);
     }).nThen(function (/*waitFor*/) {
-        SFCommonO.start();
+        SFCommonO.start({
+            useCreationScreen: true
+        });
     });
 });
diff --git a/www/whiteboard/app-whiteboard.less b/www/whiteboard/app-whiteboard.less
index 28e6a6e0c..29c739b6e 100644
--- a/www/whiteboard/app-whiteboard.less
+++ b/www/whiteboard/app-whiteboard.less
@@ -5,6 +5,7 @@
 @import (once) '../../customize/src/less2/include/alertify.less';
 @import (once) '../../customize/src/less2/include/tools.less';
 @import (once) '../../customize/src/less2/include/tokenfield.less';
+@import (once) '../../customize/src/less2/include/creation.less';
 
 .toolbar_main(
     @bg-color: @colortheme_whiteboard-bg,
@@ -14,6 +15,7 @@
 .fileupload_main();
 .alertify_main();
 .tokenfield_main();
+.creation_main();
 
 // body
 &.cp-app-whiteboard {
diff --git a/www/whiteboard/inner.js b/www/whiteboard/inner.js
index 4e5729fa8..febf63c5b 100644
--- a/www/whiteboard/inner.js
+++ b/www/whiteboard/inner.js
@@ -415,6 +415,7 @@ define([
             Title.setToolbar(toolbar);
 
             var $rightside = toolbar.$rightside;
+            var $drawer = toolbar.$drawer;
 
             /* save as template */
             if (!metadataMgr.getPrivateData().isTemplate) {
@@ -428,7 +429,7 @@ define([
 
             /* add an export button */
             var $export = common.createButton('export', true, {}, saveImage);
-            $rightside.append($export);
+            $drawer.append($export);
 
             if (common.isLoggedIn()) {
                 common.createButton('savetodrive', true, {}, function () {})
@@ -449,6 +450,9 @@ define([
             });
             $rightside.append($forget);
 
+            var $properties = common.createButton('properties', true);
+            toolbar.$drawer.append($properties);
+
             if (!readOnly) {
                 makeColorButton($rightside);
 
@@ -562,7 +566,12 @@ define([
 
 
                 if (readOnly) { return; }
-                if (isNew) {
+
+                var privateDat = metadataMgr.getPrivateData();
+                var skipTemp = Util.find(privateDat,
+                    ['settings', 'general', 'creation', 'noTemplate']);
+                var skipCreation = Util.find(privateDat, ['settings', 'general', 'creation', 'skip']);
+                if (isNew && (!AppConfig.displayCreationScreen || (!skipTemp && skipCreation))) {
                     common.openTemplatePicker();
                 }
             });
@@ -605,6 +614,24 @@ define([
             }
         };
 
+        config.onError = function (err) {
+            setEditable(false);
+            toolbar.deleted();
+            var msg = err.type;
+            if (err.type === 'EEXPIRED') {
+                msg = Messages.expiredError;
+                if (err.loaded) {
+                    msg += Messages.expiredErrorCopy;
+                }
+            } else if (err.type === 'EDELETED') {
+                msg = Messages.deletedError;
+                if (err.loaded) {
+                    msg += Messages.expiredErrorCopy;
+                }
+            }
+            UI.errorLoadingScreen(msg, true, true);
+        };
+
         cpNfInner = common.startRealtime(config);
         metadataMgr = cpNfInner.metadataMgr;
 
@@ -640,6 +667,18 @@ define([
                 $('body').append($div.html());
             }));
             SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
+        }).nThen(function (waitFor) {
+            common.getSframeChannel().onReady(waitFor());
+        }).nThen(function (waitFor) {
+            if (!AppConfig.displayCreationScreen) { return; }
+            var priv = common.getMetadataMgr().getPrivateData();
+            if (priv.isNewFile) {
+                var c = (priv.settings.general && priv.settings.general.creation) || {};
+                if (c.skip && !priv.forceCreationScreen) {
+                    return void common.createPad(c, waitFor());
+                }
+                common.getPadCreationScreen(c, waitFor());
+            }
         }).nThen(function (/*waitFor*/) {
             andThen(common);
         });
diff --git a/www/whiteboard/main.js b/www/whiteboard/main.js
index ce1f14d9c..1c63ad811 100644
--- a/www/whiteboard/main.js
+++ b/www/whiteboard/main.js
@@ -36,6 +36,8 @@ define([
         };
         window.addEventListener('message', onMsg);
     }).nThen(function (/*waitFor*/) {
-        SFCommonO.start();
+        SFCommonO.start({
+            useCreationScreen: true
+        });
     });
 });