diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index fff1bb3d8..d54b707d9 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -645,7 +645,7 @@ define([ UIElements.displayAvatar(common, $(avatar), data.avatar, name); return h('div.cp-share-friend', { 'data-ed': data.edPublic, - 'data-curve': data.curvePublic, + 'data-curve': data.curvePublic || '', 'data-name': name.toLowerCase(), 'data-order': i, title: name, @@ -731,6 +731,7 @@ define([ var createShareWithFriends = function (config, onShare) { var common = config.common; + var sframeChan = common.getSframeChannel(); var title = config.title; var friends = config.friends; var myName = common.getMetadataMgr().getUserData().name; @@ -741,14 +742,14 @@ define([ return friends[c].curvePublic.slice(0,8); }); - var $div; + var div = h('div.cp-share-column.contains-nav'); + var $div = $(div); // Replace "copy link" by "share with friends" if at least one friend is selected // Also create the "share with friends" button if it doesn't exist var refreshButtons = function () { var $nav = $div.parents('.alertify').find('nav'); var friendMode = $div.find('.cp-share-friend.cp-selected').length; - console.log(friendMode, Boolean(friendMode)); if (friendMode) { $nav.find('button.cp-share-with-friends').prop('disabled', ''); } else { @@ -757,11 +758,31 @@ define([ }; var friendsList = UIElements.getFriendsList(Messages.share_linkFriends, config, refreshButtons); - var div = friendsList.div; - $div = $(div); - $div.addClass('contains-nav'); + var friendDiv = friendsList.div; + $div.append(friendDiv); var others = friendsList.others; + var privateData = common.getMetadataMgr().getPrivateData(); + var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {}; + var teams = {}; + Object.keys(teamsData).forEach(function (id) { + var t = teamsData[id]; + teams[t.edPublic] = { + notifications: true, + displayName: t.name, + edPublic: t.edPublic, + avatar: t.avatar, + id: id + } + }); + var teamsList = UIElements.getFriendsList('Share with a team', { + common: common, + noFilter: true, + friends: teams + }, refreshButtons); + $div.append(teamsList.div); + $(teamsList.div).append(h('div.cp-share-grid', teamsList.others)); + var shareButtons = [{ className: 'primary cp-share-with-friends', name: Messages.share_withFriends, @@ -770,18 +791,35 @@ define([ var $friends = $div.find('.cp-share-friend.cp-selected'); $friends.each(function (i, el) { var curve = $(el).attr('data-curve'); - if (!curve || !friends[curve]) { return; } - var friend = friends[curve]; - if (!friend.notifications || !friend.curvePublic) { return; } - common.mailbox.sendTo("SHARE_PAD", { + // Check if the selected element is a friend or a team + if (curve) { // Friend + if (!curve || !friends[curve]) { return; } + var friend = friends[curve]; + if (!friend.notifications || !friend.curvePublic) { return; } + common.mailbox.sendTo("SHARE_PAD", { + href: href, + password: config.password, + isTemplate: config.isTemplate, + name: myName, + title: title + }, { + channel: friend.notifications, + curvePublic: friend.curvePublic + }); + return; + } + // Team + var ed = $(el).attr('data-ed'); + var team = teams[ed]; + if (!team) { return; } + sframeChan.query('Q_STORE_IN_TEAM', { href: href, password: config.password, - isTemplate: config.isTemplate, - name: myName, - title: title - }, { - channel: friend.notifications, - curvePublic: friend.curvePublic + path: config.isTemplate ? ['template'] : undefined, + title: title, + teamId: team.id + }, function (err) { + if (err) { return void console.error(err); } }); }); @@ -829,7 +867,7 @@ define([ $(el).attr('data-order', i).css('order', i); }); // Display them - $div.append(h('div.cp-share-grid', others)); + $(friendDiv).append(h('div.cp-share-grid', others)); $div.append(UI.dialog.getButtons(shareButtons, config.onClose)); $div.find('.cp-share-friend').click(function () { var sel = $(this).hasClass('cp-selected'); @@ -904,10 +942,6 @@ define([ var parsed = Hash.parsePadUrl(href); return origin + parsed.getUrl({embed: embed, present: present}); }; - $(link).find('#cp-share-link-preview').val(getLinkValue()); - $(link).find('input[type="radio"], input[type="checkbox"]').on('change', function () { - $(link).find('#cp-share-link-preview').val(getLinkValue()); - }); var linkButtons = [{ className: 'cancel', name: Messages.cancel, @@ -939,6 +973,11 @@ define([ $(mainShareColumn).append(UI.dialog.getButtons(shareButtons, config.onClose)).appendTo($link); $(friendsList).appendTo($link); + $(link).find('#cp-share-link-preview').val(getLinkValue()); + $(link).find('input[type="radio"], input[type="checkbox"]').on('change', function () { + $(link).find('#cp-share-link-preview').val(getLinkValue()); + }); + var frameLink = UI.dialog.customModal(link, { buttons: linkButtons, onClose: config.onClose, diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 09b18f707..eac836a5f 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -687,6 +687,38 @@ define([ }); }; + common.storeInTeam = function (data, cb) { + if (!data.href) { return void cb({error: 'EINVAL'}); } + var parsed = Hash.parsePadUrl(data.href); + var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password); + if (!secret || !secret.channel) { return void cb ({error: 'EINVAL'}); } + Nthen(function (waitFor) { + // Set the correct owner and expiration time if we can find them + postMessage('GET_PAD_METADATA', { + channel: secret.channel + }, waitFor(function (obj) { + if (!obj || obj.error) { return; } + data.owners = obj.owners; + data.expire = +obj.expire; + })); + }).nThen(function () { + postMessage("SET_PAD_TITLE", { + teamId: data.teamId, + href: data.href, + title: data.title, + password: data.password, + channel: secret.channel, + path: data.path, + owners: data.owners, + expire: data.expire, + forceSave: 1 + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(); + }); + }); + }; + // Needed for the secure filepicker app common.getSecureFilesList = function (query, cb) { postMessage("GET_SECURE_FILES_LIST", query, function (list) { diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 03b263319..d2db7413c 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -957,15 +957,33 @@ define([ expire = data.expire; } - // Check if the pad is stored in any managers. - // If it is stored, update its data, otherwise ask the user where to store it + // If a teamId is provided, it means we want to store the pad in a specific + // team drive. In this case, we just need to check if the pad is already + // stored in th eteam drive. + // If no team ID is provided, this may be a pad shared iwth its URL. + // We need to check if the pad is stored in any managers (user or teams). + // If it is stored, update its data, otherwise ask the user if they want to store it var allData = []; var sendTo = []; + var inMyDrive; getAllStores().forEach(function (s) { + if (data.teamId && s.id !== data.teamId) { return; } + var res = s.manager.findChannel(channel); if (res.length) { sendTo.push(s.id); } + + // If we've just accepted ownership for a pad stored in a shared folder, + // we need to make a copy of this pad in our drive. We're going to check + // if the pad is stored in our MAIN drive. + // We only need to check this if the current manager is the target (data.teamId) + if (data.teamId === s.id) { + inMyDrive = res.some(function (obj) { + return !obj.fId; + }); + } + Array.prototype.push.apply(allData, res); }); var contains = allData.length !== 0; @@ -990,14 +1008,13 @@ define([ pad.href = href; }); - // If we've just accepted ownership for a pad stored in a shared folder, - // we need to make a copy of this pad in our drive. We're going to check - // the pad is owned by us BUT is not stored in our main drive - var inMyDrive = allData.some(function (obj) { - return !obj.fId; - }); - // XXX owned by one of our teams? - var ownedByMe = Array.isArray(owners) && owners.indexOf(store.proxy.edPublic) !== -1; + // Pads owned by us ("us" can be a user or a team) that are not in our "main" drive + // (meaning they are stored in a shared folder) must be added to the "main" drive. + // This is to make sure owners always have control over owned data. + var edPublic = data.teamId ? + Util.find(store.proxy, ['teams', data.teamId, 'keys', 'edPublic']) : + store.proxy.edPublic; + var ownedByMe = Array.isArray(owners) && owners.indexOf(edPublic) !== -1; // Add the pad if it does not exist in our drive if (!contains || (ownedByMe && !inMyDrive)) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 7faa736d3..0a233dcf8 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -458,6 +458,10 @@ define([ setDocumentTitle(); }); + sframeChan.on('Q_STORE_IN_TEAM', function (data, cb) { + Cryptpad.storeInTeam(data, cb); + }); + sframeChan.on('EV_SET_HASH', function (hash) { window.location.hash = hash; });