diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js
index a79cb382f..c254c69ee 100644
--- a/customize.dist/translations/messages.fr.js
+++ b/customize.dist/translations/messages.fr.js
@@ -350,6 +350,7 @@ define(function () {
out.fm_templateName = "Modèles";
out.fm_searchName = "Recherche";
out.fm_recentPadsName = "Pads récents";
+ out.fm_ownedPadsName = "Possédés";
out.fm_searchPlaceholder = "Rechercher...";
out.fm_newButton = "Nouveau";
out.fm_newButtonTitle = "Créer un nouveau pad ou un dossier, importer un fichier dans le dossier courant";
diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js
index 1d7fd8b15..5cee6cb97 100644
--- a/customize.dist/translations/messages.js
+++ b/customize.dist/translations/messages.js
@@ -353,6 +353,7 @@ define(function () {
out.fm_templateName = "Templates";
out.fm_searchName = "Search";
out.fm_recentPadsName = "Recent pads";
+ out.fm_ownedPadsName = "Owned";
out.fm_searchPlaceholder = "Search...";
out.fm_newButton = "New";
out.fm_newButtonTitle = "Create a new pad or folder, import a file in the current folder";
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index c426a0272..992f98352 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -1241,8 +1241,8 @@ define([
}
// XXX TODO remove these lines
- ownedVal = undefined;
- expire = undefined;
+ //ownedVal = undefined;
+ //expire = undefined;
sframeChan.query("Q_CREATE_PAD", {
owned: ownedVal,
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index fca25b3b1..9e72194d9 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -361,6 +361,7 @@ define([
Store.addPad = function (data, cb) {
if (!data.href) { return void cb({error:'NO_HREF'}); }
var pad = makePad(data.href, data.title);
+ if (data.owners) { pad.owners = data.owners; }
store.userObject.pushData(pad, function (e, id) {
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root'];
@@ -522,6 +523,11 @@ define([
var p = Hash.parsePadUrl(href);
var h = p.hashData;
+ var owners;
+ if (Store.channel && Util.base64ToHex(h.channel) === Store.channel.wc.id) {
+ owners = Store.channel.data.owners || undefined;
+ }
+
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
var isStronger;
@@ -583,6 +589,7 @@ define([
Store.addPad({
href: href,
title: title,
+ owners: owners,
path: data.path || (store.data && store.data.initialPath)
}, cb);
return;
@@ -735,12 +742,14 @@ define([
// TODO with sharedworker
// channel will be an object storing the webchannel associated to each browser tab
- var channel = {
- queue: []
+ var channel = Store.channel = {
+ queue: [],
+ data: {}
};
Store.joinPad = function (data, cb) {
var conf = {
- onReady: function () {
+ onReady: function (padData) {
+ channel.data = padData || {};
postMessage("PAD_READY");
}, // post EV_PAD_READY
onMessage: function (m) {
diff --git a/www/common/outer/chainpad-netflux-worker.js b/www/common/outer/chainpad-netflux-worker.js
index a60d73b3e..ffd3f9078 100644
--- a/www/common/outer/chainpad-netflux-worker.js
+++ b/www/common/outer/chainpad-netflux-worker.js
@@ -36,6 +36,7 @@ define([], function () {
var owners = conf.owners;
var password = conf.password;
var expire = conf.expire;
+ var padData;
conf = undefined;
var initializing = true;
@@ -43,11 +44,11 @@ define([], function () {
var messageFromOuter = function () {};
- var onRdy = function () {
+ var onRdy = function (padData) {
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
// message through "network" when it is synced, and it triggers onReady for each channel joined.
if (!initializing) { return; }
- onReady();
+ onReady(padData);
//sframeChan.event('EV_RT_READY', null);
// we're fully synced
initializing = false;
@@ -92,13 +93,14 @@ define([], function () {
if (parsed.channel === wc.id && !validateKey) {
validateKey = parsed.validateKey;
}
+ padData = parsed;
// We have to return even if it is not the current channel:
// we don't want to continue with other channels messages here
return;
}
if (parsed.state && parsed.state === 1 && parsed.channel) {
if (parsed.channel === wc.id) {
- onRdy();
+ onRdy(padData);
}
// We have to return even if it is not the current channel:
// we don't want to continue with other channels messages here
@@ -180,7 +182,7 @@ define([], function () {
});
network.historyKeeper = hk;
- var cfg = {
+ var cfg = padData = {
validateKey: validateKey,
lastKnownHash: lastKnownHash,
owners: owners,
diff --git a/www/common/sframe-app-outer.js b/www/common/sframe-app-outer.js
index d0dcb631c..b65f3f98b 100644
--- a/www/common/sframe-app-outer.js
+++ b/www/common/sframe-app-outer.js
@@ -35,6 +35,8 @@ define([
};
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
- SFCommonO.start();
+ SFCommonO.start({
+ useCreationScreen: true
+ });
});
});
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index e31143e80..34d929522 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -567,7 +567,8 @@ define([
// Join the netflux channel
var rtStarted = false;
- var startRealtime = function () {
+ var startRealtime = function (rtConfig) {
+ rtConfig = rtConfig || {};
rtStarted = true;
var replaceHash = function (hash) {
if (window.history && window.history.replaceState) {
@@ -581,7 +582,7 @@ define([
window.location.hash = hash;
};
- CpNfOuter.start({
+ var cfg = {
sframeChan: sframeChan,
channel: secret.channel,
padRpc: Cryptpad.padRpc,
@@ -600,7 +601,11 @@ define([
if (readOnly || cfg.noHash) { return; }
replaceHash(Utils.Hash.getEditHashFromKeys(wc, secret.keys));
}
+ };
+ Object.keys(rtConfig).forEach(function (k) {
+ cfg[k] = rtConfig[k];
});
+ CpNfOuter.start(cfg);
};
sframeChan.on('Q_CREATE_PAD', function (data, cb) {
@@ -624,12 +629,11 @@ define([
var rtConfig = {};
if (data.owned) {
- //rtConfig.owners = [edPublic];
+ rtConfig.owners = [edPublic];
}
if (data.expire) {
- //rtConfig.expire = data.expire;
+ rtConfig.expire = data.expire;
}
-
if (data.template) {
// Pass rtConfig to useTemplate because Cryptput will create the file and
// we need to have the owners and expiration time in the first line on the
@@ -651,7 +655,7 @@ define([
if (!realtime) { return; }
if (isNewFile && Utils.LocalStore.isLoggedIn()
- && AppConfig.displayCreationScreen) { return; }
+ && AppConfig.displayCreationScreen && cfg.useCreationScreen) { return; }
startRealtime();
});
diff --git a/www/common/userObject.js b/www/common/userObject.js
index 2b9b47d38..1f2993542 100644
--- a/www/common/userObject.js
+++ b/www/common/userObject.js
@@ -453,6 +453,12 @@ define([
.map(function (str) { return Number(str); });
return sorted;
};
+ exp.getOwnedPads = function (edPub) {
+ var allFiles = files[FILES_DATA];
+ return Object.keys(allFiles).filter(function (id) {
+ return allFiles[id].owners && allFiles[id].owners.indexOf(edPub) !== -1;
+ }).map(function (k) { return Number(k); });;
+ };
/**
* OPERATIONS
diff --git a/www/drive/inner.html b/www/drive/inner.html
index 8f5a884ae..d2f7a2f55 100644
--- a/www/drive/inner.html
+++ b/www/drive/inner.html
@@ -44,6 +44,7 @@
Open
Open (read-only)
Delete
+ Delete permanently
Properties
Tags
diff --git a/www/drive/inner.js b/www/drive/inner.js
index aa8034f23..51e17a931 100644
--- a/www/drive/inner.js
+++ b/www/drive/inner.js
@@ -60,6 +60,8 @@ define([
var TRASH_NAME = Messages.fm_trashName;
var RECENT = "recent";
var RECENT_NAME = Messages.fm_recentPadsName;
+ var OWNED = "owned";
+ var OWNED_NAME = Messages.fm_ownedPadsName;
var LS_LAST = "app-drive-lastOpened";
var LS_OPENED = "app-drive-openedFolders";
@@ -180,6 +182,8 @@ define([
var $addIcon = $('', {"class": "fa fa-plus"});
var $renamedIcon = $('', {"class": "fa fa-flag"});
var $readonlyIcon = $('', {"class": "fa fa-eye"});
+ var $ownedIcon = $('', {"class": "fa fa-id-card-o"});
+ var $ownerIcon = $('', {"class": "fa fa-id-card"});
var history = {
isHistoryMode: false,
@@ -202,6 +206,7 @@ define([
var sframeChan = common.getSframeChannel();
var priv = metadataMgr.getPrivateData();
var user = metadataMgr.getUserData();
+ var edPublic = priv.edPublic;
APP.origin = priv.origin;
var isOwnDrive = function () {
@@ -255,9 +260,10 @@ define([
// Categories dislayed in the menu
// _WORKGROUP_ : do not display unsorted
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
+ if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
if (isWorkgroup()) { displayedCategories = [ROOT, TRASH, SEARCH]; }
- var virtualCategories = [SEARCH, RECENT];
+ var virtualCategories = [SEARCH, RECENT, OWNED];
if (!APP.loggedIn) {
displayedCategories = [FILES_DATA];
@@ -652,13 +658,18 @@ define([
if (!isOwnDrive()) {
hide.push($menu.find('a.cp-app-drive-context-own'));
}
+ if ($element.is('.cp-app-drive-element-owned')) {
+ hide.push($menu.find('a.cp-app-drive-context-delete'));
+ } else {
+ hide.push($menu.find('a.cp-app-drive-context-deleteowned'));
+ }
if ($element.is('.cp-app-drive-element-file')) {
// No folder in files
hide.push($menu.find('a.cp-app-drive-context-newfolder'));
- if ($element.is('.cp-app-drive-readonly')) {
+ if ($element.is('.cp-app-drive-element-readonly')) {
// Keep only open readonly
hide.push($menu.find('a.cp-app-drive-context-open'));
- } else if ($element.is('.cp-app-drive-noreadonly')) {
+ } else if ($element.is('.cp-app-drive-element-noreadonly')) {
// Keep only open readonly
hide.push($menu.find('a.cp-app-drive-context-openro'));
}
@@ -1169,6 +1180,13 @@ define([
var $renamed = $renamedIcon.clone().appendTo($state);
$renamed.attr('title', Messages._getKey('fm_renamedPad', [data.title]));
}
+ if (data.owners && data.owners.indexOf(edPublic) !== -1) {
+ var $owned = $ownedIcon.clone().appendTo($state);
+ $owned.attr('title', Messages.fm_padIsOwned || 'TODO: owned pad'); // XXX
+ } else if (data.owners && data.owners.length) {
+ var $owner = $ownerIcon.clone().appendTo($state);
+ $owner.attr('title', Messages.fm_padIsOwned || 'TODO: owned pad'); // XXX
+ }
var name = filesOp.getTitle(element);
@@ -1391,6 +1409,9 @@ define([
case RECENT:
msg = Messages.fm_info_recent;
break;
+ case OWNED:
+ msg = 'TODO: files deleted here are actually removed from the server...'; // XXX
+ break;
default:
msg = undefined;
}
@@ -1702,10 +1723,10 @@ define([
};
var sortElements = function (folder, path, oldkeys, prop, asc, useId) {
- var root = filesOp.find(path);
+ var root = path && filesOp.find(path);
var test = folder ? filesOp.isFolder : filesOp.isFile;
var keys = oldkeys.filter(function (e) {
- return useId ? test(e) : test(root[e]);
+ return useId ? test(e) : (path && test(root[e]));
});
if (keys.length < 2) { return keys; }
var mult = asc ? 1 : -1;
@@ -2065,6 +2086,41 @@ define([
});
};
+ // Owned pads category
+ var displayOwned = function ($container) {
+ var list = filesOp.getOwnedPads(edPublic);
+ if (list.length === 0) { return; }
+ var $fileHeader = getFileListHeader(false);
+ $container.append($fileHeader);
+ var sortedFiles = sortElements(false, false, list, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
+ sortedFiles.forEach(function (id) {
+ var paths = filesOp.findFile(id);
+ if (!paths.length) { return; }
+ var path = paths[0];
+ var $icon = getFileIcon(id);
+ var ro = filesOp.isReadOnlyFile(id);
+ // ro undefined maens it's an old hash which doesn't support read-only
+ var roClass = typeof(ro) === 'undefined' ? ' cp-app-drive-element-noreadonly' :
+ ro ? ' cp-app-drive-element-readonly' : '';
+ var $element = $('', {
+ 'class': 'cp-app-drive-element cp-app-drive-element-owned cp-app-drive-element-file cp-app-drive-element-row' + roClass
+ });
+ $element.prepend($icon).dblclick(function () {
+ openFile(id);
+ });
+ addFileData(id, $element);
+ $element.data('path', path);
+ $element.data('element', id);
+ $element.click(function(e) {
+ e.stopPropagation();
+ onElementClick(e, $element);
+ });
+ $element.contextmenu(openDefaultContextMenu);
+ $element.data('context', $defaultContextMenu);
+ $container.append($element);
+ });
+ };
+
// Display the selected directory into the content part (rightside)
// NOTE: Elements in the trash are not using the same storage structure as the others
// _WORKGROUP_ : do not change the lastOpenedFolder value in localStorage
@@ -2096,6 +2152,7 @@ define([
var isAllFiles = filesOp.comparePath(path, [FILES_DATA]);
var isSearch = path[0] === SEARCH;
var isRecent = path[0] === RECENT;
+ var isOwned = path[0] === OWNED;
var isVirtual = virtualCategories.indexOf(path[0]) !== -1;
var root = isVirtual ? undefined : filesOp.find(path);
@@ -2194,6 +2251,8 @@ define([
displaySearch($list, path[1]);
} else if (isRecent) {
displayRecent($list);
+ } else if (isOwned) {
+ displayOwned($list);
} else {
$dirContent.contextmenu(openContentContextMenu);
if (filesOp.hasSubfolder(root)) { $list.append($folderHeader); }
@@ -2374,6 +2433,15 @@ define([
$container.append($list);
};
+ var createOwned = function ($container, path) {
+ var $icon = $ownedIcon.clone(); // TODO
+ var isOpened = filesOp.comparePath(path, currentPath);
+ var $element = createTreeElement(OWNED_NAME, $icon, [OWNED], false, false, false, isOpened);
+ $element.addClass('root');
+ var $list = $('', { 'class': 'cp-app-drive-tree-category' }).append($element);
+ $container.append($list);
+ };
+
var search = APP.Search = {};
var createSearch = function ($container) {
var isInSearch = currentPath[0] === SEARCH;
@@ -2437,6 +2505,7 @@ define([
var $div = $('', {'class': 'cp-app-drive-tree-categories-container'})
.appendTo($tree);
if (displayedCategories.indexOf(RECENT) !== -1) { createRecent($div, [RECENT]); }
+ if (displayedCategories.indexOf(OWNED) !== -1) { createOwned($div, [OWNED]); }
if (displayedCategories.indexOf(ROOT) !== -1) { createTree($div, [ROOT]); }
if (displayedCategories.indexOf(TEMPLATE) !== -1) { createTemplate($div, [TEMPLATE]); }
if (displayedCategories.indexOf(FILES_DATA) !== -1) { createAllFiles($div, [FILES_DATA]); }
@@ -2683,6 +2752,21 @@ define([
}
moveElements(pathsList, [TRASH], false, refresh);
}
+ else if ($(this).hasClass('cp-app-drive-context-deleteowned')) {
+ // TODO
+ // Remove owned pad from drive and remove from server
+ var pathsList = [];
+ paths.forEach(function (p) { pathsList.push(p.path); });
+ var msg = Messages._getKey("fm_deleteOwnedPads"); // XXX
+ UI.confirm(msg, function(res) {
+ $(window).focus();
+ if (!res) { return; }
+ filesOp.delete(pathsList, refresh);
+ // TODO HERE
+ // RPC to delete from server?
+ });
+ return;
+ }
else if ($(this).hasClass("cp-app-drive-context-properties")) {
if (paths.length !== 1) { return; }
el = filesOp.find(paths[0].path);