From ed5671a548986e26ec2f11e705913c580a58ea9f Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2019 17:16:14 +0200 Subject: [PATCH 01/16] Don't write metadata updates to the log if it doesn't change the value --- lib/metadata.js | 19 ++++++++++++++++++- rpc.js | 9 ++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/metadata.js b/lib/metadata.js index 037024b57..bda0488dd 100644 --- a/lib/metadata.js +++ b/lib/metadata.js @@ -28,10 +28,14 @@ commands.ADD_OWNERS = function (meta, args) { throw new Error("METADATA_NONSENSE_OWNERS"); } + var changed = false; args.forEach(function (owner) { if (meta.owners.indexOf(owner) >= 0) { return; } meta.owners.push(owner); + changed = true; }); + + return changed; }; // ["RM_OWNERS", ["CrufexqXcY-z+eKJlEbNELVy5Sb7E-EAAEFI8GnEtZ0="], 1561623439989] @@ -45,13 +49,17 @@ commands.RM_OWNERS = function (meta, args) { throw new Error("METADATA_NONSENSE_OWNERS"); } + var changed = false; // remove owners one by one // we assume there are no duplicates args.forEach(function (owner) { var index = meta.owners.indexOf(owner); if (index < 0) { return; } meta.owners.splice(index, 1); + changed = true; }); + + return changed; }; // ["ADD_PENDING_OWNERS", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I="], 1561623438989] @@ -67,16 +75,20 @@ commands.ADD_PENDING_OWNERS = function (meta, args) { throw new Error("METADATA_NONSENSE_PENDING_OWNERS"); } + var changed = false; // Add pending_owners array if it doesn't exist if (!meta.pending_owners) { meta.pending_owners = deduplicate(args); - return; + return true; } // or fill it args.forEach(function (owner) { if (meta.pending_owners.indexOf(owner) >= 0) { return; } meta.pending_owners.push(owner); + changed = true; }); + + return changed; }; // ["RM_PENDING_OWNERS", ["CrufexqXcY-z+eKJlEbNELVy5Sb7E-EAAEFI8GnEtZ0="], 1561623439989] @@ -90,13 +102,17 @@ commands.RM_PENDING_OWNERS = function (meta, args) { throw new Error("METADATA_NONSENSE_PENDING_OWNERS"); } + var changed = false; // remove owners one by one // we assume there are no duplicates args.forEach(function (owner) { var index = meta.pending_owners.indexOf(owner); if (index < 0) { return; } meta.pending_owners.splice(index, 1); + changed = true; }); + + return changed; }; // ["RESET_OWNERS", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I="], 1561623439989] @@ -112,6 +128,7 @@ commands.RESET_OWNERS = function (meta, args) { // overwrite the existing owners with the new one meta.owners = deduplicate(args); + return true; }; commands.UPDATE_EXPIRATION = function () { diff --git a/rpc.js b/rpc.js index 058215200..08a4edecf 100644 --- a/rpc.js +++ b/rpc.js @@ -340,6 +340,7 @@ var getMetadata = function (Env, channel, cb) { value: value } */ +// XXX global saferphore may cause issues here, a queue "per channel" is probably better var metadataSem = Saferphore.create(1); var setMetadata = function (Env, data, unsafeKey, cb) { var channel = data.channel; @@ -382,13 +383,19 @@ var setMetadata = function (Env, data, unsafeKey, cb) { // Add the new metadata line var line = [command, data.value, +new Date()]; + var changed = false; try { - Meta.handleCommand(metadata, line); + changed = Meta.handleCommand(metadata, line); } catch (e) { g(); return void cb(e); } + // if your command is valid but it didn't result in any change to the metadata, + // call back now and don't write any "useless" line to the log + if (!changed) { + return void cb(void 0, metadata); + } Env.msgStore.writeMetadata(channel, JSON.stringify(line), function (e) { g(); if (e) { From d6b6b8f11f358dd9059516816d6f8d4f6bb4f693 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2019 17:42:30 +0200 Subject: [PATCH 02/16] Store the pad to the drive when accepting ownership --- www/common/common-ui-elements.js | 20 ++++++++++++++++++++ www/common/sframe-common-outer.js | 15 +++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index b37d71355..4f7373a7f 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -3068,6 +3068,7 @@ define([ }; var storePopupState = false; + var autoStoreModal = {}; UIElements.displayStorePadPopup = function (common, data) { if (storePopupState) { return; } storePopupState = true; @@ -3087,6 +3088,8 @@ define([ var initialHide = data && data.autoStore && data.autoStore === -1; var modal = UI.cornerPopup(text, actions, footer, {hidden: initialHide}); + autoStoreModal[priv.channel] = modal; + $(modal.popup).find('.cp-corner-footer a').click(function (e) { e.preventDefault(); common.openURL('/settings/'); @@ -3094,6 +3097,7 @@ define([ $(hide).click(function () { UIElements.displayCrowdfunding(common); + delete autoStoreModal[priv.channel]; modal.delete(); }); var waitingForStoringCb = false; @@ -3109,6 +3113,7 @@ define([ } return void UI.warn(Messages.autostore_error); } + delete autoStoreModal[priv.channel]; modal.delete(); UIElements.displayCrowdfunding(common); UI.log(Messages.autostore_saved); @@ -3350,6 +3355,21 @@ define([ // Send notification to the sender answer(true); + var data = JSON.parse(JSON.stringify(msg.content)); + data.metadata = res; + + // Add the pad to your drive + sframeChan.query('Q_ACCEPT_OWNERSHIP', data, function (err, res) { + if (err || (res && res.error)) { + return void console.error(err | res.error); + } + UI.log(Messages.saved); + if (autoStoreModal[data.channel]) { + autoStoreModal[data.channel].delete(); + delete autoStoreModal[data.channel]; + } + }); + // Remove yourself from the pending owners sframeChan.query('Q_SET_PAD_METADATA', { channel: msg.content.channel, diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index cc568553a..a8a00cd0b 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -476,6 +476,21 @@ define([ }); }); + sframeChan.on('Q_ACCEPT_OWNERSHIP', function (data, cb) { + var data = { + password: data.password, + href: data.href, + channel: data.channel, + title: data.title, + owners: data.metadata.owners, + expire: data.metadata.expire, + forceSave: true + }; + Cryptpad.setPadTitle(data, function (err) { + cb({error: err}); + }); + }); + sframeChan.on('Q_IMPORT_MEDIATAG', function (obj, cb) { var key = obj.key; var channel = obj.channel; From e76a62c2a587bdef797d6db9fbf00e84da47d7f2 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2019 17:45:55 +0200 Subject: [PATCH 03/16] Filter out pending_owners from the 'add owners' column --- www/common/common-ui-elements.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 4f7373a7f..bc358cfcf 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -226,7 +226,8 @@ define([ var drawAdd = function () { var _friends = JSON.parse(JSON.stringify(friends)); Object.keys(_friends).forEach(function (curve) { - if (owners.indexOf(_friends[curve].edPublic) !== -1) { + if (owners.indexOf(_friends[curve].edPublic) !== -1 || + pending_owners.indexOf(_friends[curve].edPublic) !== -1) { delete _friends[curve]; } }); From 772ecb7365f3b0c6519af7a1b3e1d54bca8f883f Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2019 17:58:36 +0200 Subject: [PATCH 04/16] Make sure you always know yourself in the ownerhsip modal --- www/common/common-ui-elements.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index bc358cfcf..a8ea163b7 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -154,6 +154,12 @@ define([ return true; } }); + if (ed === edPublic) { + f = f || user; + if (f.name) { + f.displayName = f.name; + } + } _owners[ed] = f || { displayName: 'Unknown user: '+ ed, // XXX notifications: true, From d190b8acd0c638fab659aeaa3b2d087135a423a0 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2019 18:19:56 +0200 Subject: [PATCH 05/16] Don't send user updates to friends via chat if you know their mailbox --- www/common/common-messaging.js | 3 +++ www/common/common-messenger.js | 18 ++++++++++++------ www/common/outer/mailbox-handlers.js | 3 +++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index 3e673b1e9..8e27b3fd9 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -83,6 +83,9 @@ define([ store.messenger.updateMyData(); } var myData = createData(store.proxy); + if (store.proxy.friends) { + store.proxy.friends.me = myData; + } var todo = function (friend) { if (!friend || !friend.notifications) { return; } myData.channel = friend.channel; diff --git a/www/common/common-messenger.js b/www/common/common-messenger.js index 365c8be40..50195e20c 100644 --- a/www/common/common-messenger.js +++ b/www/common/common-messenger.js @@ -229,6 +229,11 @@ define([ }); }; + messenger.onFriendUpdate = function (curve) { + var friend = getFriend(proxy, curve); + checkFriendData(curve, friend, friend.channel); + }; + // Id message allows us to map a netfluxId with a public curve key var onIdMessage = function (msg, sender) { var channel, parsed0; @@ -373,12 +378,14 @@ define([ || mySyncData.profile !== myData.profile || mySyncData.avatar !== myData.avatar) { delete myData.channel; - Object.keys(channels).forEach(function (chan) { - var channel = channels[chan]; + Object.keys(friends).forEach(function (curve) { + var friend = friends[curve]; + var chan = friend.channel; + if (friend.notifications) { return; } + if (!chan) { return; } - if (!channel) { - return void console.error('NO_SUCH_CHANNEL'); - } + var channel = channels[chan]; + if (!channel) { return; } if (channel.readOnly) { return; } var msg = [Types.update, myData.curvePublic, +new Date(), myData]; @@ -396,7 +403,6 @@ define([ info: myData, types: ['displayName', 'profile', 'avatar'], }); - friends.me = myData; } }; diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index ed1d954b1..bc2d0b9ff 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -153,6 +153,9 @@ define([ Object.keys(msg.content).forEach(function (key) { friend[key] = msg.content[key]; }); + if (ctx.store.messenger) { + ctx.store.messenger.onFriendUpdate(curve, friend); + } ctx.updateMetadata(); cb(true); }; From 4708d59a656c527c612a41982ffeeae5b8f59bba Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2019 18:46:52 +0200 Subject: [PATCH 06/16] Add notifications when removed from owners or pending_owners --- www/common/common-ui-elements.js | 52 +++++++++++++++++++++------- www/common/notifications.js | 18 ++++++++++ www/common/outer/mailbox-handlers.js | 24 +++++++++++-- www/common/sframe-common-outer.js | 4 +-- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index a8ea163b7..4e576d35a 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -203,25 +203,53 @@ define([ if (ed === edPublic) { me = true; } return ed; }).filter(function (x) { return x; }); - // Send the command - var send = function () { + NThen(function (waitFor) { + var msg = me ? + "Are you sure? You're going to give up on your rights, this can't be undone!" : + "Are you sure?"; // XXX + UI.confirm(msg, waitFor(function (yes) { + if (!yes) { + waitFor.abort(); + return; + } + })); + }).nThen(function (waitFor) { + // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, command: pending ? 'RM_PENDING_OWNERS' : 'RM_OWNERS', value: toRemove - }, function (err, res) { + }, waitFor(function (err, res) { err = err || (res && res.error); - if (err) { return void UI.warn('ERROR' + err); } // XXX - redrawAll(); + if (err) { + waitFor.abort(); + return void UI.warn('ERROR' + err); + } // XXX UI.log('DONE'); // XXX + })); + }).nThen(function (waitFor) { + sel.forEach(function (el) { + var friend = friends[$(el).attr('data-curve')]; + if (!friend) { return; } + common.mailbox.sendTo("RM_OWNER", { + channel: channel, + title: data.title, + pending: pending, + user: { + displayName: user.name, + avatar: user.avatar, + profile: user.profile, + notifications: user.notifications, + curvePublic: user.curvePublic, + edPublic: priv.edPublic + } + }, { + channel: friend.notifications, + curvePublic: friend.curvePublic + }, waitFor()); }); - }; - var msg = me ? - "Are you sure? You're going to give up on your rights, this can't be undone!" : - "Are you sure?"; // XXX - UI.confirm(msg, function (yes) { - if (!yes) { return; } - send(); + }).nThen(function () { + redrawAll(); }); }); $div.append(h('p', removeButton)); diff --git a/www/common/notifications.js b/www/common/notifications.js index b06e8e3fc..ef64c303a 100644 --- a/www/common/notifications.js +++ b/www/common/notifications.js @@ -252,6 +252,24 @@ define([ } }; + handlers['RM_OWNER'] = function (common, data) { + var content = data.content; + var msg = content.msg; + + // Display the notification + var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous; + var title = Util.fixHTML(msg.content.title); + Messages.owner_removed = '{0} has removed your ownership of {1}'; // XXX + Messages.owner_removedPending = '{0} has removed your pending ownership of {1}'; // XXX + var key = 'owner_removed' + (msg.content.pending ? 'Pending' : ''); + content.getFormatText = function () { + return Messages._getKey(key, [name, title]); + }; + if (!content.archived) { + content.dismissHandler = defaultDismiss(common, data); + } + }; + // NOTE: don't forget to fixHTML everything returned by "getFormatText" return { diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index bc2d0b9ff..aa65d6cfd 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -267,7 +267,6 @@ define([ handlers['ADD_OWNER'] = function (ctx, box, data, cb) { var msg = data.msg; var content = msg.content; -console.log(msg); if (msg.author !== content.user.curvePublic) { return void cb(true); } if (!content.href || !content.title || !content.channel) { @@ -278,7 +277,10 @@ console.log(msg); var channel = content.channel; if (addOwners[channel]) { return void cb(true); } - addOwners[channel] = true; + addOwners[channel] = { + type: box.type, + hash: data.hash + }; cb(false); }; @@ -289,6 +291,24 @@ console.log(msg); } }; + handlers['RM_OWNER'] = function (ctx, box, data, cb) { + var msg = data.msg; + var content = msg.content; + + if (msg.author !== content.user.curvePublic) { return void cb(true); } + if (!content.channel) { + console.log('Remove invalid notification'); + return void cb(true); + } + + var channel = content.channel; + + if (addOwners[channel] && content.pending) { + return void cb(false, addOwners[channel]); + } + cb(false); + }; + return { add: function (ctx, box, data, cb) { /** diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index a8a00cd0b..2a03392be 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -477,7 +477,7 @@ define([ }); sframeChan.on('Q_ACCEPT_OWNERSHIP', function (data, cb) { - var data = { + var _data = { password: data.password, href: data.href, channel: data.channel, @@ -486,7 +486,7 @@ define([ expire: data.metadata.expire, forceSave: true }; - Cryptpad.setPadTitle(data, function (err) { + Cryptpad.setPadTitle(_data, function (err) { cb({error: err}); }); }); From 6e5fdc0b8e3522c559a1e2a0382f9c14ee0a50e6 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Sep 2019 10:55:48 +0200 Subject: [PATCH 07/16] Get all pad attributes at once --- www/common/common-ui-elements.js | 50 +++++--------------------------- www/common/proxy-manager.js | 28 +++++------------- 2 files changed, 16 insertions(+), 62 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 4e576d35a..a02fb8851 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -71,56 +71,22 @@ define([ var getPropertiesData = function (common, cb) { var data = {}; NThen(function (waitFor) { - common.getPadAttribute('password', waitFor(function (err, val) { - data.password = val; - })); - }).nThen(function (waitFor) { var base = common.getMetadataMgr().getPrivateData().origin; - // XXX getFileData? - // XXX getPadMetadata - common.getPadAttribute('href', waitFor(function (err, val) { - if (!val) { return; } - data.href = base + val; - })); - common.getPadAttribute('roHref', waitFor(function (err, val) { - if (!val) { return; } - data.roHref = base + val; - })); - common.getPadAttribute('channel', waitFor(function (err, val) { - data.channel = val; - })); - common.getPadAttribute('rtChannel', waitFor(function (err, val) { - data.rtChannel = val; - })); - common.getPadAttribute('lastVersion', waitFor(function (err, val) { - data.lastVersion = val; - })); - common.getPadAttribute('atime', waitFor(function (err, val) { - data.atime = val; - })); - common.getPadAttribute('ctime', waitFor(function (err, val) { - data.ctime = val; - })); - common.getPadAttribute('title', waitFor(function (err, val) { - data.title = val; - })); - common.getPadAttribute('tags', waitFor(function (err, val) { - data.tags = val; + common.getPadAttribute('', waitFor(function (err, val) { + if (err || !val) { + waitFor.abort(); + return void cb(err || 'EEMPTY'); + } + Util.extend(data, val); + if (data.href) { data.href = base + data.href; } + if (data.roHref) { data.roHref = base + data.roHref; } })); common.getPadMetadata(null, waitFor(function (obj) { - console.log(obj); if (obj && obj.error) { return; } data.owners = obj.owners; data.expire = obj.expire; data.pending_owners = obj.pending_owners; })); - /* - common.getPadAttribute('owners', waitFor(function (err, val) { - data.owners = val; - })); - common.getPadAttribute('expire', waitFor(function (err, val) { - data.expire = val; - }));*/ }).nThen(function () { cb(void 0, data); }); diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 4bed6a1a2..5cfae1a38 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -738,29 +738,17 @@ define([ return void cb(null, Env.user.proxy[UserObject.SHARED_FOLDERS][sfId][data.attr]); } var datas = findHref(Env, data.href); - var nt = nThen; var res = {}; datas.forEach(function (d) { - nt = nt(function (waitFor) { - var atime, value; - var w = waitFor(); - nThen(function (waitFor2) { - d.userObject.getPadAttribute(data.href, 'atime', waitFor2(function (err, v) { - atime = v; - })); - d.userObject.getPadAttribute(data.href, data.attr, waitFor2(function (err, v) { - value = v; - })); - }).nThen(function () { - if (!res.value || res.atime < atime) { - res.atime = atime; - res.value = value; - } - w(); - }); - }).nThen; + var atime = d.data.atime; + + var value = attr ? d.data[attr] : JSON.parse(JSON.stringify(d.data)); + if (!res.value || res.atime < atime) { + res.atime = atime; + res.value = value; + } }); - nt(function () { cb(null, res.value); }); + cb(null, res.value); }; var getTagsList = function (Env) { From d52e449f87bd3181d49e5df982921185aed35407 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Sep 2019 13:29:57 +0200 Subject: [PATCH 08/16] Fix setMetadata RPC saferphore --- lib/metadata.js | 2 +- rpc.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/metadata.js b/lib/metadata.js index bda0488dd..fb8628410 100644 --- a/lib/metadata.js +++ b/lib/metadata.js @@ -144,7 +144,7 @@ var handleCommand = Meta.handleCommand = function (meta, line) { throw new Error("METADATA_UNSUPPORTED_COMMAND"); } - commands[command](meta, args); + return commands[command](meta, args); }; Meta.commands = Object.keys(commands); diff --git a/rpc.js b/rpc.js index 08a4edecf..352165b0b 100644 --- a/rpc.js +++ b/rpc.js @@ -394,6 +394,7 @@ var setMetadata = function (Env, data, unsafeKey, cb) { // if your command is valid but it didn't result in any change to the metadata, // call back now and don't write any "useless" line to the log if (!changed) { + g(); return void cb(void 0, metadata); } Env.msgStore.writeMetadata(channel, JSON.stringify(line), function (e) { From 32f1d1627fbdaa96122b7fd8b33903d5f12d3e43 Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 3 Sep 2019 13:30:35 +0200 Subject: [PATCH 09/16] Refresh properties modal when receiving metadata updates --- www/common/common-ui-elements.js | 274 +++++++++++--------- www/common/cryptpad-common.js | 2 + www/common/outer/async-store.js | 5 +- www/common/proxy-manager.js | 2 +- www/common/sframe-chainpad-netflux-outer.js | 4 + www/common/sframe-common-outer.js | 1 - 6 files changed, 160 insertions(+), 128 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index a02fb8851..f4f5108e6 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -271,14 +271,12 @@ define([ } })); }).nThen(function (waitFor) { - console.log('koko'); // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, command: 'ADD_PENDING_OWNERS', value: toAdd }, waitFor(function (err, res) { - console.error(arguments); err = err || (res && res.error); if (err) { waitFor.abort(); @@ -286,8 +284,6 @@ define([ } // XXX })); }).nThen(function (waitFor) { - console.log('okok'); - // TODO send notifications sel.forEach(function (el) { var friend = friends[$(el).attr('data-curve')]; if (!friend) { return; } @@ -318,13 +314,17 @@ define([ return $div2; }; + var pending = false; redrawAll = function () { - $div1.empty(); - $div2.empty(); + if (pending) { return; } + pending = true; common.getPadMetadata(null, function (obj) { + pending = false; if (obj && obj.error) { return; } owners = obj.owners; pending_owners = obj.pending_owners; + $div1.empty(); + $div2.empty(); $div1.append(drawRemove(false)).append(drawRemove(true)); $div2.append(drawAdd()); }); @@ -333,6 +333,15 @@ define([ $div1.append(drawRemove(false)).append(drawRemove(true)); $div2.append(drawAdd()); + var handler = sframeChan.on('EV_RT_METADATA', function (md) { + if (!$div1.length) { + return void handler.stop(); + } + owners = md.owners; + pending_owners = md.pending_owners; + redrawAll(); + }); + // Create modal var link = h('div.cp-share-columns', [ div1, @@ -349,139 +358,158 @@ define([ return UI.dialog.customModal(link, {buttons: linkButtons}); }; var getRightsProperties = function (common, data, cb) { - var $d = $('
'); - if (!data) { return void cb(void 0, $d); } + var $div = $('
'); + if (!data) { return void cb(void 0, $div); } - $('