diff --git a/customize.dist/src/less2/include/alertify.less b/customize.dist/src/less2/include/alertify.less index 3274f1c63..c73c27c78 100644 --- a/customize.dist/src/less2/include/alertify.less +++ b/customize.dist/src/less2/include/alertify.less @@ -427,7 +427,7 @@ display: flex; flex-flow: row; - .cp-share-column { + & > .cp-share-column { width: 50%; padding: 0 10px; } diff --git a/historyKeeper.js b/historyKeeper.js index 6027fc3fe..10137dd06 100644 --- a/historyKeeper.js +++ b/historyKeeper.js @@ -717,6 +717,9 @@ module.exports.create = function (cfg) { if (channel && metadata_cache[channel] && typeof (metadata) === "object") { Log.silly('SET_METADATA_CACHE', 'Channel '+ channel +', metadata: '+ JSON.stringify(metadata)); metadata_cache[channel] = metadata; + if (ctx.channels[channel] && ctx.channels[channel].index) { + ctx.channels[channel].index.metadata = metadata; + } historyKeeperBroadcast(ctx, channel, metadata); } }; diff --git a/lib/metadata.js b/lib/metadata.js index 11214931b..037024b57 100644 --- a/lib/metadata.js +++ b/lib/metadata.js @@ -54,6 +54,51 @@ commands.RM_OWNERS = function (meta, args) { }); }; +// ["ADD_PENDING_OWNERS", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I="], 1561623438989] +commands.ADD_PENDING_OWNERS = function (meta, args) { + // bail out if args isn't an array + if (!Array.isArray(args)) { + throw new Error('METADATA_INVALID_PENDING_OWNERS'); + } + + // you shouldn't be able to get here if there are no owners + // because only an owner should be able to change the owners + if (meta.pending_owners && !Array.isArray(meta.pending_owners)) { + throw new Error("METADATA_NONSENSE_PENDING_OWNERS"); + } + + // Add pending_owners array if it doesn't exist + if (!meta.pending_owners) { + meta.pending_owners = deduplicate(args); + return; + } + // or fill it + args.forEach(function (owner) { + if (meta.pending_owners.indexOf(owner) >= 0) { return; } + meta.pending_owners.push(owner); + }); +}; + +// ["RM_PENDING_OWNERS", ["CrufexqXcY-z+eKJlEbNELVy5Sb7E-EAAEFI8GnEtZ0="], 1561623439989] +commands.RM_PENDING_OWNERS = function (meta, args) { + // what are you doing if you don't have owners to remove? + if (!Array.isArray(args)) { + throw new Error('METADATA_INVALID_PENDING_OWNERS'); + } + // if there aren't any owners to start, this is also pointless + if (!Array.isArray(meta.pending_owners)) { + throw new Error("METADATA_NONSENSE_PENDING_OWNERS"); + } + + // 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); + }); +}; + // ["RESET_OWNERS", ["7eEqelGso3EBr5jHlei6av4r9w2B9XZiGGwA1EgZ-5I="], 1561623439989] commands.RESET_OWNERS = function (meta, args) { // expect a new array, even if it's empty @@ -73,7 +118,7 @@ commands.UPDATE_EXPIRATION = function () { throw new Error("E_NOT_IMPLEMENTED"); }; -var handleCommand = function (meta, line) { +var handleCommand = Meta.handleCommand = function (meta, line) { var command = line[0]; var args = line[1]; //var time = line[2]; @@ -84,6 +129,7 @@ var handleCommand = function (meta, line) { commands[command](meta, args); }; +Meta.commands = Object.keys(commands); Meta.createLineHandler = function (ref, errorHandler) { ref.meta = {}; @@ -125,4 +171,3 @@ Meta.createLineHandler = function (ref, errorHandler) { }; }; -Meta.commands = Object.keys(commands); diff --git a/rpc.js b/rpc.js index 514d5e7d7..6ab004865 100644 --- a/rpc.js +++ b/rpc.js @@ -340,6 +340,7 @@ var getMetadata = function (Env, channel, cb) { value: value } */ +var metadataSem = Saferphore.create(1); var setMetadata = function (Env, data, unsafeKey, cb) { var channel = data.channel; var command = data.command; @@ -347,25 +348,52 @@ var setMetadata = function (Env, data, unsafeKey, cb) { if (!command || typeof (command) !== 'string') { return void cb ('INVALID_COMMAND'); } if (Meta.commands.indexOf(command) === -1) { return void('UNSUPPORTED_COMMAND'); } - // XXX should we add checks to "metadata.js" to make sure data.value is - // valid for the selected command? + metadataSem.take(function (give) { + var g = give(); + getMetadata(Env, channel, function (err, metadata) { + if (err) { + g(); + return void cb(err); + } + if (!(metadata && Array.isArray(metadata.owners))) { + g(); + return void cb('E_NO_OWNERS'); + } - getMetadata(Env, channel, function (err, metadata) { - if (err) { return void cb(err); } - if (!(metadata && Array.isArray(metadata.owners))) { return void cb('E_NO_OWNERS'); } - // Confirm that the channel is owned by the user in question - if (metadata.owners.indexOf(unsafeKey) === -1) { - return void cb('INSUFFICIENT_PERMISSIONS'); - } + // Confirm that the channel is owned by the user in question + // or the user is accepting a pending ownerhsip offer + if (metadata.pending_owners && Array.isArray(metadata.pending_owners) && + metadata.pending_owners.indexOf(unsafeKey) !== -1 && + metadata.owners.indexOf(unsafeKey) === -1) { - // Add the new metadata line - var line = JSON.stringify([command, data.value]); - return void Env.msgStore.writeMetadata(channel, line, function (e) { - if (e) { + // If you are a pending owner, make sure you can only add yourelf as an owner + if ((command !== 'ADD_OWNERS' && command !== 'RM_PENDING_OWNERS') + || !Array.isArray(data.value) + || data.value.length !== 1 + || data.value[0] !== unsafeKey) { + g(); + return void cb('INSUFFICIENT_PERMISSIONS'); + } + + } else if (metadata.owners.indexOf(unsafeKey) === -1) { + g(); + return void cb('INSUFFICIENT_PERMISSIONS'); + } + + // Add the new metadata line + var line = [command, data.value, +new Date()]; + try { + Meta.handleCommand(metadata, line); + } catch (e) { + g(); return void cb(e); } - getMetadata(Env, channel, function (err, metadata) { - // XXX handle error here? + + Env.msgStore.writeMetadata(channel, JSON.stringify(line), function (e) { + g(); + if (e) { + return void cb(e); + } cb(void 0, metadata); }); }); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 650d0bf1f..b37d71355 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -76,6 +76,8 @@ define([ })); }).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; @@ -99,29 +101,52 @@ define([ 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.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); }); }; - var createOwnerModal = function (common, channel, owners) { + var createOwnerModal = function (common, data) { var friends = common.getFriends(true); var sframeChan = common.getSframeChannel(); var priv = common.getMetadataMgr().getPrivateData(); + var user = common.getMetadataMgr().getUserData(); var edPublic = priv.edPublic; + var channel = data.channel; + var owners = data.owners; + var pending_owners = data.pending_owners; + + var redrawAll = function () {}; + + var div1 = h('div.cp-share-friends.cp-share-column'); + var div2 = h('div.cp-share-friends.cp-share-column'); + var $div1 = $(div1); + var $div2 = $(div2); // Remove owner column - var drawRemove = function () { + var drawRemove = function (pending) { var _owners = {}; - owners.forEach(function (ed) { + var o = pending ? pending_owners : owners; + o.forEach(function (ed) { var f; Object.keys(friends).some(function (c) { if (friends[c].edPublic === ed) { @@ -135,17 +160,19 @@ define([ edPublic: ed, }; }); - var removeCol = UIElements.getFriendsList('Remove an existing owner instantly', { + var msg = pending ? 'Remove a pending owner:' + : 'Remove an existing owner:'; // XXX + var removeCol = UIElements.getFriendsList(msg, { common: common, friends: _owners, noFilter: true }, function () { console.log(arguments); }); - var $div1 = $(removeCol.div); + var $div = $(removeCol.div); var others1 = removeCol.others; - $div1.append(h('div.cp-share-grid', others1)); - $div1.find('.cp-share-friend').click(function () { + $div.append(h('div.cp-share-grid', others1)); + $div.find('.cp-share-friend').click(function () { var sel = $(this).hasClass('cp-selected'); if (!sel) { $(this).addClass('cp-selected'); @@ -157,10 +184,11 @@ define([ }); // When clicking on the remove button, we check the selected users. // If you try to remove yourself, we'll display an additional warning message - var removeButton = h('button.no-margin', 'Remove owners'); // XXX + var btnMsg = pending ? 'Remove pending owners' : 'Remove owners'; // XXX + var removeButton = h('button.no-margin', btnMsg); $(removeButton).click(function () { // Check selection - var $sel = $div1.find('.cp-share-friend.cp-selected'); + var $sel = $div.find('.cp-share-friend.cp-selected'); var sel = $sel.toArray(); var me = false; var toRemove = sel.map(function (el) { @@ -173,14 +201,12 @@ define([ var send = function () { sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, - command: 'RM_OWNERS', + command: pending ? 'RM_PENDING_OWNERS' : 'RM_OWNERS', value: toRemove }, function (err, res) { err = err || (res && res.error); if (err) { return void UI.warn('ERROR' + err); } // XXX - owners = res.owners; - drawRemove().insertBefore($div1); - $div1.remove(); + redrawAll(); UI.log('DONE'); // XXX }); }; @@ -192,20 +218,26 @@ define([ send(); }); }); - $div1.append(h('p', removeButton)); - return $div1; + $div.append(h('p', removeButton)); + return $div; }; // Add owners column var drawAdd = function () { + var _friends = JSON.parse(JSON.stringify(friends)); + Object.keys(_friends).forEach(function (curve) { + if (owners.indexOf(_friends[curve].edPublic) !== -1) { + delete _friends[curve]; + } + }); var addCol = UIElements.getFriendsList('Ask a friend to be an owner.', { common: common, - friends: friends + friends: _friends }, function () { // XXX onSelect... console.log(arguments); }); - var $div2 = $(addCol.div); + $div2 = $(addCol.div); var others2 = addCol.others; $div2.append(h('div.cp-share-grid', others2)); $div2.find('.cp-share-friend').click(function () { @@ -226,38 +258,86 @@ define([ var $sel = $div2.find('.cp-share-friend.cp-selected'); var sel = $sel.toArray(); var toAdd = sel.map(function (el) { - return $(el).attr('data-curve'); + return friends[$(el).attr('data-curve')].edPublic; }).filter(function (x) { return x; }); - // Send the command - var send = function () { - // XXX Pinning problem.... + + NThen(function (waitFor) { + var msg = "Are you sure?"; // XXX + UI.confirm(msg, waitFor(function (yes) { + if (!yes) { + waitFor.abort(); + return; + } + })); + }).nThen(function (waitFor) { + console.log('koko'); + // Send the command sframeChan.query('Q_SET_PAD_METADATA', { channel: channel, - command: 'ADD_OWNERS', + command: 'ADD_PENDING_OWNERS', value: toAdd - }, function (err, res) { + }, waitFor(function (err, res) { + console.error(arguments); err = err || (res && res.error); - if (err) { return void UI.warn('ERROR' + err); } // XXX - owners = res.owners; - drawRemove().insertBefore($div2); - $div2.remove(); - UI.log('DONE'); // XXX + if (err) { + waitFor.abort(); + return void UI.warn('ERROR' + err); + } // XXX + })); + }).nThen(function (waitFor) { + console.log('okok'); + // TODO send notifications + sel.forEach(function (el) { + var friend = friends[$(el).attr('data-curve')]; + if (!friend) { return; } + common.mailbox.sendTo("ADD_OWNER", { + channel: channel, + href: data.href, + password: data.password, + title: data.title, + 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 = "Are you sure?"; // XXX - UI.confirm(msg, function (yes) { - if (!yes) { return; } - send(); + }).nThen(function () { + redrawAll(); + UI.log('DONE'); // XXX }); }); - //$div2.append(h('p', addButton)); + $div2.append(h('p', addButton)); return $div2; }; + redrawAll = function () { + $div1.empty(); + $div2.empty(); + common.getPadMetadata(null, function (obj) { + if (obj && obj.error) { return; } + owners = obj.owners; + pending_owners = obj.pending_owners; + $div1.append(drawRemove(false)).append(drawRemove(true)); + $div2.append(drawAdd()); + }); + }; + + $div1.append(drawRemove(false)).append(drawRemove(true)); + $div2.append(drawAdd()); + // Create modal var link = h('div.cp-share-columns', [ - drawRemove()[0], - drawAdd()[0] + div1, + div2 + /*drawRemove()[0], + drawAdd()[0]*/ ]); var linkButtons = [{ className: 'cancel', @@ -310,7 +390,7 @@ define([ if (owned) { var manageOwners = h('button.no-margin', 'Manage owners'); // XXX $(manageOwners).click(function () { - var modal = createOwnerModal(common, data.channel, data.owners); + var modal = createOwnerModal(common, data); UI.openCustomModal(modal, { wide: true, }); @@ -3152,7 +3232,7 @@ define([ UIElements.displayFriendRequestModal = function (common, data) { var msg = data.content.msg; - var text = Messages._getKey('contacts_request', [msg.content.displayName]); + var text = Messages._getKey('contacts_request', [Util.fixHTML(msg.content.displayName)]); var todo = function (yes) { common.getSframeChannel().query("Q_ANSWER_FRIEND_REQUEST", { @@ -3197,5 +3277,132 @@ define([ UI.openCustomModal(modal); }; + UIElements.displayAddOwnerModal = function (common, data) { + var priv = common.getMetadataMgr().getPrivateData(); + var user = common.getMetadataMgr().getUserData(); + var sframeChan = common.getSframeChannel(); + var msg = data.content.msg; + + var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous; + var title = Util.fixHTML(msg.content.title); + + Messages.owner_add = '{0} wants you to be an owner of the pad {1}. Do you accept?'; //XXX + var text = Messages._getKey('owner_add', [name, title]); + + var link = h('a', { + href: '#' + }, Messages.requestEdit_viewPad); + $(link).click(function (e) { + e.preventDefault(); + e.stopPropagation(); + if (msg.content.password) { + common.sessionStorage.put('newPadPassword', msg.content.password, function () { + common.openURL(msg.content.href); + }); + return; + } + common.openURL(msg.content.href); + }); + + var div = h('div', [ + UI.setHTML(h('p'), text), + link + ]); + + var answer = function (yes) { + common.mailbox.sendTo("ADD_OWNER_ANSWER", { + channel: msg.content.channel, + href: msg.content.href, + password: msg.content.password, + title: msg.content.title, + answer: yes, + user: { + displayName: user.name, + avatar: user.avatar, + profile: user.profile, + notifications: user.notifications, + curvePublic: user.curvePublic, + edPublic: priv.edPublic + } + }, { + channel: msg.content.user.notifications, + curvePublic: msg.content.user.curvePublic + }); + common.mailbox.dismiss(data, function (err) { + console.log(err); + }); + }; + + var todo = function (yes) { + if (yes) { + // ACCEPT + sframeChan.query('Q_SET_PAD_METADATA', { + channel: msg.content.channel, + command: 'ADD_OWNERS', + value: [priv.edPublic] + }, function (err, res) { + err = err || (res && res.error); + if (err) { + return void UI.warn('ERROR ' + err); + } // XXX + UI.log('DONE'); // XXX + + // Send notification to the sender + answer(true); + + // Remove yourself from the pending owners + sframeChan.query('Q_SET_PAD_METADATA', { + channel: msg.content.channel, + command: 'RM_PENDING_OWNERS', + value: [priv.edPublic] + }, function (err, res) { + err = err || (res && res.error); + if (err) { + console.error(err); + } + }); + }); + return; + } + + // DECLINE + // Remove yourself from the pending owners + sframeChan.query('Q_SET_PAD_METADATA', { + channel: msg.content.channel, + command: 'RM_PENDING_OWNERS', + value: [priv.edPublic] + }, function (err, res) { + err = err || (res && res.error); + if (err) { + console.error(err); + } + // Send notification to the sender + answer(false); + }); + }; + + var buttons = [{ + name: Messages.friendRequest_later, + onClick: function () {}, + keys: [27] + }, { + className: 'primary', + name: Messages.friendRequest_accept, + onClick: function () { + todo(true); + }, + keys: [13] + }, { + className: 'primary', + name: Messages.friendRequest_decline, + onClick: function () { + todo(false); + }, + keys: [[13, 'ctrl']] + }]; + var modal = UI.dialog.customModal(div, {buttons: buttons}); + UI.openCustomModal(modal); + }; + return UIElements; }); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 4ed0976ff..56e5edcae 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -758,6 +758,10 @@ define([ pad.onConnectEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent(); + pad.getPadMetadata = function (data, cb) { + postMessage('GET_PAD_METADATA', data, cb); + }; + pad.requestAccess = function (data, cb) { postMessage("REQUEST_PAD_ACCESS", data, cb); }; @@ -769,7 +773,10 @@ define([ postMessage('SET_PAD_METADATA', data, cb); }; common.getPadMetadata = function (data, cb) { - postMessage('GET_PAD_METADATA', data, cb); + common.anonRpcMsg('GET_METADATA', data.channel, function (err, obj) { + if (err) { return void cb({error: err}); } + cb(obj && obj[0]); + }); }; common.changePadPassword = function (Crypt, href, newPassword, edPublic, cb) { diff --git a/www/common/notifications.js b/www/common/notifications.js index 72b62b501..b06e8e3fc 100644 --- a/www/common/notifications.js +++ b/www/common/notifications.js @@ -210,6 +210,48 @@ define([ }; }; + + handlers['ADD_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_request = '{0} wants you to be an owner of {1}'; // XXX + content.getFormatText = function () { + return Messages._getKey('owner_request', [name, title]); + }; + + // Check authenticity + if (msg.author !== msg.content.user.curvePublic) { return; } + + // if not archived, add handlers + if (!content.archived) { + content.handler = function () { + UIElements.displayAddOwnerModal(common, data); + }; + } + }; + + handlers['ADD_OWNER_ANSWER'] = 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_request_accepted = '{0} has accepted your offer to be an owner of {1}'; // XXX + Messages.owner_request_declined = '{0} has declined your offer to be an owner of {1}'; // XXX + var key = 'owner_request_' + (msg.content.answer ? 'accepted' : 'declined'); + 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/async-store.js b/www/common/outer/async-store.js index 619c4018f..83dcf5806 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1374,6 +1374,7 @@ define([ }; Store.getPadMetadata = function (clientId, data, cb) { + console.log(data); if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } var channel = channels[data.channel]; if (!channel) { return void cb({ error: 'ENOTFOUND' }); } @@ -1396,6 +1397,7 @@ define([ cb(channel.data || {}); }; Store.setPadMetadata = function (clientId, data, cb) { + console.log(data); if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } if (!data.command) { return void cb({ error: 'EINVAL' }); } store.rpc.setMetadata(data, function (err, res) { diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index 9d383caa1..ed1d954b1 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -259,6 +259,33 @@ define([ cb(false); }; + // Hide duplicates when receiving an ADD_OWNER notification: + var addOwners = {}; + 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) { + console.log('Remove invalid notification'); + return void cb(true); + } + + var channel = content.channel; + + if (addOwners[channel]) { return void cb(true); } + addOwners[channel] = true; + + cb(false); + }; + removeHandlers['ADD_OWNER'] = function (ctx, box, data) { + var channel = data.content.channel; + if (addOwners[channel]) { + delete addOwners[channel]; + } + }; + return { add: function (ctx, box, data, cb) { /** diff --git a/www/common/sframe-common-mailbox.js b/www/common/sframe-common-mailbox.js index 5aaaa5340..e468b29f1 100644 --- a/www/common/sframe-common-mailbox.js +++ b/www/common/sframe-common-mailbox.js @@ -32,13 +32,17 @@ define([ }); }; - mailbox.sendTo = function (type, content, user) { + mailbox.sendTo = function (type, content, user, cb) { + cb = cb || function () {}; execCommand('SENDTO', { type: type, msg: content, user: user }, function (err, obj) { - if (err || (obj && obj.error)) { return void console.error(err || obj.error); } + cb(err || (obj && obj.error), obj); + if (err || (obj && obj.error)) { + return void console.error(err || obj.error); + } }); }; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 38214752f..cc568553a 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -981,7 +981,7 @@ define([ // Try to get the owner's mailbox from the pad metadata first. // If it's is an older owned pad, check if the owner is a friend // or an acquaintance (from async-store directly in requestAccess) - Cryptpad.getPadMetadata({ + Cryptpad.pad.getPadMetadata({ channel: secret.channel }, waitFor(function (obj) { obj = obj || {}; @@ -1004,6 +1004,15 @@ define([ }); }); + sframeChan.on('Q_GET_PAD_METADATA', function (data, cb) { + if (!data || !data.channel) { + data = { + channel: secret.channel + }; + } + console.log(data); + Cryptpad.getPadMetadata(data, cb); + }); sframeChan.on('Q_SET_PAD_METADATA', function (data, cb) { Cryptpad.setPadMetadata(data, cb); }); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 58e1ffcb0..6b4fc96aa 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -467,6 +467,13 @@ define([ }, { timeout: 60000 }); }; + funcs.getPadMetadata = function (data, cb) { + ctx.sframeChan.query('Q_GET_PAD_METADATA', data, function (err, val) { + if (err || (val && val.error)) { return void cb({error: err || val.error}); } + cb(val); + }); + }; + funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; funcs.openURL = function (url) { ctx.sframeChan.event('EV_OPEN_URL', url); }; funcs.openUnsafeURL = function (url) { diff --git a/www/drive/inner.js b/www/drive/inner.js index 08babf7e0..59fbaba2f 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -3716,6 +3716,20 @@ define([ data.sharedFolder = true; } + if (manager.isFile(el) && data.roHref) { // Only for pads! + sframeChan.query('Q_GET_PAD_METADATA', { + channel: data.channel + }, function (err, val) { + console.log(arguments); + if (!err && !(val && val.error)) { + data.owners = val.owners; + data.expire = val.expire; + data.pending_owners = val.pending_owners; + } + UIElements.getProperties(common, data, cb); + }); + return; + } UIElements.getProperties(common, data, cb); };