diff --git a/CHANGELOG.md b/CHANGELOG.md
index 890f8328d..b80b5d0d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,29 @@
+# WoollyMammoth (3.22.0)
+
+## Goals
+
+We've been working on some long-term projects that we hope to deliver over the course of the next few releases. In the meantime, this release includes a number of minor improvements.
+
+## Update notes
+
+To upgrade from 3.21.0 to 3.22.0:
+
+1. Stop your server
+2. Get the latest platform code with git
+3. Install client-side dependencies with `bower update`
+4. Restart the CryptPad API server
+
+## Features
+
+* Contributors have helped by translating more of CryptPad into Finnish and traditional Chinese via [our weblate instance](https://weblate.cryptpad.fr/projects/cryptpad/app/)
+
+## Bug fixes
+
+* Some of the special behaviour implemented for Org-mode in our code editor sometimes failed when the document was first changed into Org-mode.
+* We now clear some minor personal preferences like whether certain tooltips had been dismissed when you log out.
+* We identified and addressed a number of issues with teams that caused valid teams to not be displayed and team member rights to fail to upgrade until a full session reload.
+* We now display the number of days before an unregistered user's documents are considered inactive in their drive instead of hardcoding "3 months".
+
# VietnameseRhinoceros (3.21.0)
## Goals
diff --git a/customize.dist/pages.js b/customize.dist/pages.js
index bd5627139..ab86aa7a8 100644
--- a/customize.dist/pages.js
+++ b/customize.dist/pages.js
@@ -62,7 +62,7 @@ define([
var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ?
'/imprint.html' : AppConfig.imprint);
- Pages.versionString = "CryptPad v3.21.0 (VietnameseRhinoceros)";
+ Pages.versionString = "CryptPad v3.22.0 (WoollyMammoth)";
// used for the about menu
Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined;
diff --git a/package-lock.json b/package-lock.json
index cb59a4dac..fb230790b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "cryptpad",
- "version": "3.21.0",
+ "version": "3.22.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 866058611..7f00bc651 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
- "version": "3.21.0",
+ "version": "3.22.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",
diff --git a/www/code/orgmode.js b/www/code/orgmode.js
index c2af1bd5e..942d141f2 100644
--- a/www/code/orgmode.js
+++ b/www/code/orgmode.js
@@ -123,7 +123,10 @@ define([
};
});
+ var init = false;
CodeMirror.registerHelper("orgmode", "init", function (editor) {
+ if (init) { return; }
+
editor.setOption("extraKeys", {
"Tab": function(cm) { org_cycle(cm); },
"Shift-Tab": function(cm){ org_shifttab(cm); },
@@ -139,6 +142,7 @@ define([
"Shift-Right": function(cm){ org_shiftright(cm); }
});
+ init = true;
editor.on('mousedown', toggleHandler);
editor.on('touchstart', toggleHandler);
editor.on('gutterClick', foldLine);
@@ -155,6 +159,9 @@ define([
});
CodeMirror.registerHelper("orgmode", "destroy", function (editor) {
+ if (!init) { return; }
+
+ init = false;
editor.off('mousedown', toggleHandler);
editor.off('touchstart', toggleHandler);
editor.off('gutterClick', foldLine);
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index a431bf5e7..7f9e94423 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -229,181 +229,7 @@ define([
};
};
-
- var createShareWithFriends = function (config, onShare, linkGetter) {
- var common = config.common;
- var sframeChan = common.getSframeChannel();
- var title = config.title;
- var friends = config.friends || {};
- var teams = config.teams || {};
- var myName = common.getMetadataMgr().getUserData().name;
- var order = [];
-
- var smallCurves = Object.keys(friends).map(function (c) {
- return friends[c].curvePublic.slice(0,8);
- });
-
- var div = h('div.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.closest('.alertify').find('nav');
-
- var friendMode = $div.find('.cp-usergrid-user.cp-selected').length;
- if (friendMode) {
- $nav.find('button.cp-share-with-friends').prop('disabled', '');
- } else {
- $nav.find('button.cp-share-with-friends').prop('disabled', 'disabled');
- }
- };
-
- config.noInclude = true;
- Object.keys(friends).forEach(function (curve) {
- var data = friends[curve];
- if (curve.length > 40 && data.notifications) { return; }
- delete friends[curve];
- });
-
- var others = [];
- if (Object.keys(friends).length) {
- var friendsList = UIElements.getUserGrid(Messages.share_linkFriends, {
- common: common,
- data: friends,
- noFilter: false,
- large: true
- }, refreshButtons);
- var friendDiv = friendsList.div;
- $div.append(friendDiv);
- others = friendsList.icons;
- }
-
- if (Object.keys(teams).length) {
- var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
- common: common,
- noFilter: true,
- large: true,
- data: teams
- }, refreshButtons);
- $div.append(teamsList.div);
- }
-
- var shareButton = {
- className: 'primary cp-share-with-friends',
- name: Messages.share_withFriends,
- onClick: function () {
- var href;
- NThen(function (waitFor) {
- var w = waitFor();
- // linkGetter can be async if this is a burn after reading URL
- var res = linkGetter({}, function (url) {
- if (!url) {
- waitFor.abort();
- return;
- }
- href = url;
- setTimeout(w);
- });
- if (res && /^http/.test(res)) {
- href = Hash.getRelativeHref(res);
- setTimeout(w);
- return;
- }
- }).nThen(function () {
- var $friends = $div.find('.cp-usergrid-user.cp-selected');
- $friends.each(function (i, el) {
- var curve = $(el).attr('data-curve');
- var ed = $(el).attr('data-ed');
- var friend = curve && friends[curve];
- var team = teams[ed];
- // If the selected element is a friend or a team without edit right,
- // send a notification
- var mailbox = friend || ((team && team.viewer) ? team : undefined);
- if (mailbox) { // Friend
- if (friends[curve] && !mailbox.notifications) { return; }
- if (mailbox.notifications && mailbox.curvePublic) {
- common.mailbox.sendTo("SHARE_PAD", {
- href: href,
- password: config.password,
- isTemplate: config.isTemplate,
- name: myName,
- title: title
- }, {
- viewed: team && team.id,
- channel: mailbox.notifications,
- curvePublic: mailbox.curvePublic
- });
- return;
- }
- }
- // If it's a team with edit right, add the pad directly
- if (!team) { return; }
- sframeChan.query('Q_STORE_IN_TEAM', {
- href: href,
- password: config.password,
- path: config.isTemplate ? ['template'] : undefined,
- title: title,
- teamId: team.id
- }, function (err) {
- if (err) { return void console.error(err); }
- });
- });
-
- UI.findCancelButton().click();
-
- // Update the "recently shared with" array:
- // Get the selected curves
- var curves = $friends.toArray().map(function (el) {
- return ($(el).attr('data-curve') || '').slice(0,8);
- }).filter(function (x) { return x; });
- // Prepend them to the "order" array
- Array.prototype.unshift.apply(order, curves);
- order = Util.deduplicateString(order);
- // Make sure we don't have "old" friends and save
- order = order.filter(function (curve) {
- return smallCurves.indexOf(curve) !== -1;
- });
- common.setAttribute(['general', 'share-friends'], order);
- if (onShare) {
- onShare.fire();
- }
- });
- },
- keys: [13]
- };
-
- common.getAttribute(['general', 'share-friends'], function (err, val) {
- order = val || [];
- // Sort friends by "recently shared with"
- others.sort(function (a, b) {
- var ca = ($(a).attr('data-curve') || '').slice(0,8);
- var cb = ($(b).attr('data-curve') || '').slice(0,8);
- if (!ca && !cb) { return 0; }
- if (!ca) { return 1; }
- if (!cb) { return -1; }
- var ia = order.indexOf(ca);
- var ib = order.indexOf(cb);
- if (ia === -1 && ib === -1) { return 0; }
- if (ia === -1) { return 1; }
- if (ib === -1) { return -1; }
- return ia - ib;
- });
- // Reorder the friend icons
- others.forEach(function (el, i) {
- $(el).attr('data-order', i).css('order', i);
- });
- // Display them
- $(friendDiv).find('.cp-usergrid-grid').detach();
- $(friendDiv).append(h('div.cp-usergrid-grid', others));
- refreshButtons();
- });
- return {
- content: div,
- buttons: [shareButton]
- };
- };
-
- var noContactsMessage = function(common){
+ UIElements.noContactsMessage = function (common) {
var metadataMgr = common.getMetadataMgr();
var data = metadataMgr.getUserData();
var origin = metadataMgr.getPrivateData().origin;
@@ -446,655 +272,6 @@ define([
}
};
- var getEditableTeams = function (common, config) {
- var privateData = common.getMetadataMgr().getPrivateData();
- var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {};
- var teams = {};
- Object.keys(teamsData).forEach(function (id) {
- // config.teamId only exists when we're trying to share a pad from a team drive
- // In this case, we don't want to share the pad with the current team
- if (config.teamId && config.teamId === id) { return; }
- var t = teamsData[id];
- teams[t.edPublic] = {
- viewer: !teamsData[id].hasSecondaryKey,
- notifications: t.notifications,
- curvePublic: t.curvePublic,
- displayName: t.name,
- edPublic: t.edPublic,
- avatar: t.avatar,
- id: id
- };
- });
- return teams;
- };
- var makeBurnAfterReadingUrl = function (common, href, channel, cb) {
- var keyPair = Hash.generateSignPair();
- var parsed = Hash.parsePadUrl(href);
- var newHref = parsed.getUrl({
- ownerKey: keyPair.safeSignKey
- });
- var sframeChan = common.getSframeChannel();
- var rtChannel;
- NThen(function (waitFor) {
- if (parsed.type !== "sheet") { return; }
- common.getPadAttribute('rtChannel', waitFor(function (err, chan) {
- rtChannel = chan;
- }));
- }).nThen(function (waitFor) {
- sframeChan.query('Q_SET_PAD_METADATA', {
- channel: channel,
- command: 'ADD_OWNERS',
- value: [keyPair.validateKey]
- }, waitFor(function (err) {
- if (err) {
- waitFor.abort();
- UI.warn(Messages.error);
- }
- }));
- if (rtChannel) {
- sframeChan.query('Q_SET_PAD_METADATA', {
- channel: rtChannel,
- command: 'ADD_OWNERS',
- value: [keyPair.validateKey]
- }, waitFor(function (err) {
- if (err) {
- console.error(err);
- }
- }));
- }
- }).nThen(function () {
- cb(newHref);
- });
- };
- UIElements.createShareModal = function (config) {
- var origin = config.origin;
- var pathname = config.pathname;
- var hashes = config.hashes;
- var common = config.common;
-
- if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; }
-
- // check if the pad is password protected
- var hash = hashes.editHash || hashes.viewHash;
- var href = origin + pathname + '#' + hash;
- var parsedHref = Hash.parsePadUrl(href);
- var hasPassword = parsedHref.hashData.password;
-
- var makeFaqLink = function () {
- var link = h('span', [
- h('i.fa.fa-question-circle'),
- h('a', {href: '#'}, Messages.passwordFaqLink)
- ]);
- $(link).click(function () {
- common.openURL(config.origin + "/faq.html#security-pad_password");
- });
- return link;
- };
-
-
- var parsed = Hash.parsePadUrl(pathname);
- var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
- var canBAR = parsed.type !== 'drive';
-
- var burnAfterReading = (hashes.viewHash && canBAR) ?
- UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
- mark: {tabindex:1},
- label: {style: "display: none;"}
- }) : undefined;
- var rights = h('div.msg.cp-inline-radio-group', [
- h('label', Messages.share_linkAccess),
- h('div.radio-group',[
- UI.createRadio('accessRights', 'cp-share-editable-false',
- Messages.share_linkView, true, { mark: {tabindex:1} }),
- canPresent ? UI.createRadio('accessRights', 'cp-share-present',
- Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined,
- UI.createRadio('accessRights', 'cp-share-editable-true',
- Messages.share_linkEdit, false, { mark: {tabindex:1} })]),
- burnAfterReading
- ]);
-
- // Burn after reading
- // Check if we are an owner of this pad. If we are, we can show the burn after reading option.
- // When BAR is selected, display a red message indicating the consequence and add
- // the options to generate the BAR url
- var barAlert = h('div.alert.alert-danger.cp-alertify-bar-selected', {
- style: 'display: none;'
- }, Messages.burnAfterReading_warningLink);
- var channel = Hash.getSecrets('pad', hash, config.password).channel;
- common.getPadMetadata({
- channel: channel
- }, function (obj) {
- if (!obj || obj.error) { return; }
- var priv = common.getMetadataMgr().getPrivateData();
- // Not an owner: don't display the burn after reading option
- if (!Array.isArray(obj.owners) || obj.owners.indexOf(priv.edPublic) === -1) {
- $(burnAfterReading).remove();
- return;
- }
- // When the burn after reading option is selected, transform the modal buttons
- $(burnAfterReading).css({
- display: 'flex'
- });
- });
-
- var $rights = $(rights);
-
- var saveValue = function () {
- var edit = Util.isChecked($rights.find('#cp-share-editable-true'));
- var present = Util.isChecked($rights.find('#cp-share-present'));
- common.setAttribute(['general', 'share'], {
- edit: edit,
- present: present
- });
- };
-
- var burnAfterReadingUrl;
-
- var getLinkValue = function (initValue, cb) {
- var val = initValue || {};
- var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true'));
- var embed = val.embed;
- var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present'));
- var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar'));
- if (burnAfterReading && !burnAfterReadingUrl) {
- if (cb) { // Called from the contacts tab, "share" button
- var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
- return makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
- cb(url);
- });
- }
- return Messages.burnAfterReading_generateLink;
- }
- var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash;
- var href = burnAfterReading ? burnAfterReadingUrl : (origin + pathname + '#' + hash);
- var parsed = Hash.parsePadUrl(href);
- return origin + parsed.getUrl({embed: embed, present: present});
- };
-
- var makeCancelButton = function() {
- return {
- className: 'cancel',
- name: Messages.cancel,
- onClick: function () {},
- keys: [27]
- };
- };
-
- // Share link tab
- var linkContent = config.sharedFolder ? [
- h('label', Messages.sharedFolders_share),
- h('br'),
- ] : [
- UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
- ];
- linkContent.push(h('div.cp-spacer'));
- linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
-
- // Show alert if the pad is password protected
- if (hasPassword) {
- linkContent.push(h('div.alert.alert-primary', [
- h('i.fa.fa-lock'),
- Messages.share_linkPasswordAlert, h('br'),
- makeFaqLink()
- ]));
- }
-
- // warning about sharing links
- var localStore = window.cryptpadStore;
- var dismissButton = h('span.fa.fa-times');
- var shareLinkWarning = h('div.alert.alert-warning.dismissable',
- { style: 'display: none;' },
- [
- h('span.cp-inline-alert-text', Messages.share_linkWarning),
- dismissButton
- ]);
- linkContent.push(shareLinkWarning);
-
- localStore.get('hide-alert-shareLinkWarning', function (val) {
- if (val === '1') { return; }
- $(shareLinkWarning).show();
-
- $(dismissButton).on('click', function () {
- localStore.put('hide-alert-shareLinkWarning', '1');
- $(shareLinkWarning).remove();
- });
-
- });
-
- linkContent.push($(barAlert).clone()[0]); // Burn after reading
-
- var link = h('div.cp-share-modal', linkContent);
- var $link = $(link);
-
- var linkButtons = [
- makeCancelButton(),
- !config.sharedFolder && {
- className: 'secondary cp-nobar',
- name: Messages.share_linkOpen,
- onClick: function () {
- saveValue();
- var v = getLinkValue({
- embed: Util.isChecked($link.find('#cp-share-embed'))
- });
- window.open(v);
- return true;
- },
- keys: [[13, 'ctrl']]
- }, {
- className: 'primary cp-nobar',
- name: Messages.share_linkCopy,
- onClick: function () {
- saveValue();
- var v = getLinkValue({
- embed: Util.isChecked($link.find('#cp-share-embed'))
- });
- var success = Clipboard.copy(v);
- if (success) { UI.log(Messages.shareSuccess); }
- },
- keys: [13]
- }, {
- className: 'primary cp-bar',
- name: 'GENERATE LINK',
- onClick: function () {
- var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
- makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
- burnAfterReadingUrl = url;
- $rights.find('input[type="radio"]').trigger('change');
- });
- return true;
- },
- keys: []
- }
- ];
-
- var frameLink = UI.dialog.customModal(link, {
- buttons: linkButtons,
- onClose: config.onClose,
- });
- $(frameLink).find('.cp-bar').hide();
-
- // Share with contacts tab
-
- var teams = getEditableTeams(common, config);
- config.teams = teams;
- var hasFriends = Object.keys(config.friends || {}).length ||
- Object.keys(teams).length;
- var onFriendShare = Util.mkEvent();
-
-
- var friendsObject = hasFriends ? createShareWithFriends(config, onFriendShare, getLinkValue) : noContactsMessage(common);
- var friendsList = friendsObject.content;
-
- onFriendShare.reg(saveValue);
-
- var contactsContent = h('div.cp-share-modal');
- var $contactsContent = $(contactsContent);
-
- $contactsContent.append(friendsList);
-
- // Show alert if the pad is password protected
- if (hasPassword) {
- $contactsContent.append(h('div.alert.alert-primary', [
- h('i.fa.fa-unlock'),
- Messages.share_contactPasswordAlert, h('br'),
- makeFaqLink()
- ]));
- }
-
- $(contactsContent).append($(barAlert).clone()); // Burn after reading
-
- var contactButtons = friendsObject.buttons;
- contactButtons.unshift(makeCancelButton());
-
- var onShowContacts = function () {
- if (!hasFriends) {
- $rights.hide();
- }
- };
-
- var frameContacts = UI.dialog.customModal(contactsContent, {
- buttons: contactButtons,
- onClose: config.onClose,
- });
-
- // Embed tab
- var getEmbedValue = function () {
- var url = getLinkValue({
- embed: true
- });
- return '';
- };
- var embedContent = [
- h('p', Messages.viewEmbedTag),
- UI.dialog.selectableArea(getEmbedValue(), { id: 'cp-embed-link-preview', tabindex: 1, rows: 3})
- ];
-
- // Show alert if the pad is password protected
- if (hasPassword) {
- embedContent.push(h('div.alert.alert-primary', [
- h('i.fa.fa-lock'), ' ',
- Messages.share_embedPasswordAlert, h('br'),
- makeFaqLink()
- ]));
- }
-
- var embedButtons = [
- makeCancelButton(), {
- className: 'primary',
- name: Messages.share_linkCopy,
- onClick: function () {
- var v = getEmbedValue();
- var success = Clipboard.copy(v);
- if (success) { UI.log(Messages.shareSuccess); }
- },
- keys: [13]
- }];
-
- var onShowEmbed = function () {
- $rights.find('#cp-share-bar').closest('label').hide();
- $rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked');
- $rights.find('input[type="radio"]').trigger('change');
- };
-
- var embed = h('div.cp-share-modal', embedContent);
- var $embed = $(embed);
-
- var frameEmbed = UI.dialog.customModal(embed, {
- buttons: embedButtons,
- onClose: config.onClose,
- });
-
- // update values for link and embed preview when radio btns change
- $embed.find('#cp-embed-link-preview').val(getEmbedValue());
- $link.find('#cp-share-link-preview').val(getLinkValue());
- $rights.find('input[type="radio"]').on('change', function () {
- $link.find('#cp-share-link-preview').val(getLinkValue({
- embed: Util.isChecked($link.find('#cp-share-embed'))
- }));
- // Hide or show the burn after reading alert
- if (Util.isChecked($rights.find('#cp-share-bar')) && !burnAfterReadingUrl) {
- $('.cp-alertify-bar-selected').show();
- // Show burn after reading button
- $('.alertify').find('.cp-bar').show();
- $('.alertify').find('.cp-nobar').hide();
- return;
- }
- $embed.find('#cp-embed-link-preview').val(getEmbedValue());
- // Hide burn after reading button
- $('.alertify').find('.cp-nobar').show();
- $('.alertify').find('.cp-bar').hide();
- $('.cp-alertify-bar-selected').hide();
- });
- $link.find('input[type="checkbox"]').on('change', function () {
- $link.find('#cp-share-link-preview').val(getLinkValue({
- embed: Util.isChecked($link.find('#cp-share-embed'))
- }));
- });
-
-
- // Create modal
- var resetTab = function () {
- $rights.show();
- $rights.find('label.cp-radio').show();
- };
- var tabs = [{
- title: Messages.share_contactCategory,
- icon: "fa fa-address-book",
- content: frameContacts,
- active: hasFriends,
- onShow: onShowContacts,
- onHide: resetTab
- }, {
- title: Messages.share_linkCategory,
- icon: "fa fa-link",
- content: frameLink,
- active: !hasFriends
- }, {
- title: Messages.share_embedCategory,
- icon: "fa fa-code",
- content: frameEmbed,
- onShow: onShowEmbed,
- onHide: resetTab
- }];
- if (typeof(AppConfig.customizeShareOptions) === 'function') {
- AppConfig.customizeShareOptions(hashes, tabs, {
- type: 'DEFAULT',
- origin: origin,
- pathname: pathname
- });
- }
-
- var modal = UI.dialog.tabs(tabs);
- $(modal).find('.alertify-tabs-titles').after(rights);
-
- // disable edit share options if you don't have edit rights
- if (!hashes.editHash) {
- $rights.find('#cp-share-editable-false').attr('checked', true);
- $rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true);
- } else if (!hashes.viewHash) {
- $rights.find('#cp-share-editable-false').removeAttr('checked').attr('disabled', true);
- $rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
- $rights.find('#cp-share-editable-true').attr('checked', true);
- }
-
- common.getAttribute(['general', 'share'], function (err, val) {
- val = val || {};
- if (val.present && canPresent) {
- $rights.find('#cp-share-editable-false').prop('checked', false);
- $rights.find('#cp-share-editable-true').prop('checked', false);
- $rights.find('#cp-share-present').prop('checked', true);
- } else if ((val.edit === false && hashes.viewHash) || !hashes.editHash) {
- $rights.find('#cp-share-editable-false').prop('checked', true);
- $rights.find('#cp-share-editable-true').prop('checked', false);
- $rights.find('#cp-share-present').prop('checked', false);
- } else {
- $rights.find('#cp-share-editable-true').prop('checked', true);
- $rights.find('#cp-share-editable-false').prop('checked', false);
- $rights.find('#cp-share-present').prop('checked', false);
- }
- delete val.embed;
- if (!canPresent) {
- delete val.present;
- }
- $link.find('#cp-share-link-preview').val(getLinkValue(val));
- });
- common.getMetadataMgr().onChange(function () {
- // "hashes" is only available is the secure "share" app
- var _hashes = common.getMetadataMgr().getPrivateData().hashes;
- if (!_hashes) { return; }
- hashes = _hashes;
- $link.find('#cp-share-link-preview').val(getLinkValue());
- });
- return modal;
- };
-
- UIElements.createFileShareModal = function (config) {
- var origin = config.origin;
- var pathname = config.pathname;
- var hashes = config.hashes;
- var common = config.common;
- var fileData = config.fileData;
-
- if (!hashes.fileHash) { throw new Error("You must provide a file hash"); }
- var url = origin + pathname + '#' + hashes.fileHash;
-
- // check if the file is password protected
- var parsedHref = Hash.parsePadUrl(url);
- var hasPassword = parsedHref.hashData.password;
-
- var makeFaqLink = function () {
- var link = h('span', [
- h('i.fa.fa-question-circle'),
- h('a', {href: '#'}, Messages.passwordFaqLink)
- ]);
- $(link).click(function () {
- common.openURL(config.origin + "/faq.html#security-pad_password");
- });
- return link;
- };
-
- var getLinkValue = function () { return url; };
-
- var makeCancelButton = function() {
- return {className: 'cancel',
- name: Messages.cancel,
- onClick: function () {},
- keys: [27]};
- };
-
- // Share link tab
- var linkContent = [
- UI.dialog.selectableArea(getLinkValue(), { id: 'cp-share-link-preview', tabindex: 1, rows:2 })
- ];
-
- // Show alert if the pad is password protected
- if (hasPassword) {
- linkContent.push(h('div.alert.alert-primary', [
- h('i.fa.fa-lock'),
- Messages.share_linkPasswordAlert, h('br'),
- makeFaqLink()
- ]));
- }
-
- // warning about sharing links
- var localStore = window.cryptpadStore;
- var dismissButton = h('span.fa.fa-times');
- var shareLinkWarning = h('div.alert.alert-warning.dismissable',
- { style: 'display: none;' },
- [
- h('span.cp-inline-alert-text', Messages.share_linkWarning),
- dismissButton
- ]);
- linkContent.push(shareLinkWarning);
-
- localStore.get('hide-alert-shareLinkWarning', function (val) {
- if (val === '1') { return; }
- $(shareLinkWarning).show();
-
- $(dismissButton).on('click', function () {
- localStore.put('hide-alert-shareLinkWarning', '1');
- $(shareLinkWarning).remove();
- });
-
- });
-
- var link = h('div.cp-share-modal', linkContent);
-
- var linkButtons = [
- makeCancelButton(),
- {
- className: 'primary',
- name: Messages.share_linkCopy,
- onClick: function () {
- var v = getLinkValue();
- var success = Clipboard.copy(v);
- if (success) { UI.log(Messages.shareSuccess);
- }
- },
- keys: [13]
- }
- ];
-
- var frameLink = UI.dialog.customModal(link, {
- buttons: linkButtons,
- onClose: config.onClose,
- });
-
- // share with contacts tab
- var teams = getEditableTeams(common, config);
- config.teams = teams;
- var hasFriends = Object.keys(config.friends || {}).length ||
- Object.keys(teams).length;
-
- var friendsObject = hasFriends ? createShareWithFriends(config, null, getLinkValue) : noContactsMessage(common);
- var friendsList = friendsObject.content;
-
- var contactsContent = h('div.cp-share-modal');
- var $contactsContent = $(contactsContent);
- $contactsContent.append(friendsList);
-
- // Show alert if the pad is password protected
- if (hasPassword) {
- $contactsContent.append(h('div.alert.alert-primary', [
- h('i.fa.fa-unlock'),
- Messages.share_contactPasswordAlert, h('br'),
- makeFaqLink()
- ]));
- }
-
- var contactButtons = friendsObject.buttons;
- contactButtons.unshift(makeCancelButton());
-
- var frameContacts = UI.dialog.customModal(contactsContent, {
- buttons: contactButtons,
- onClose: config.onClose,
- });
-
-
- // Embed tab
- var embed = h('div.cp-share-modal', [
- h('p', Messages.fileEmbedScript),
- UI.dialog.selectable(common.getMediatagScript()),
- h('p', Messages.fileEmbedTag),
- UI.dialog.selectable(common.getMediatagFromHref(fileData)),
- ]);
-
- // Show alert if the pad is password protected
- if (hasPassword) {
- embed.append(h('div.alert.alert-primary', [
- h('i.fa.fa-lock'), ' ',
- Messages.share_embedPasswordAlert, h('br'),
- makeFaqLink()
- ]));
- }
-
- var embedButtons = [{
- className: 'cancel',
- name: Messages.cancel,
- onClick: function () {},
- keys: [27]
- }, {
- className: 'primary',
- name: Messages.share_mediatagCopy,
- onClick: function () {
- var v = common.getMediatagFromHref(fileData);
- var success = Clipboard.copy(v);
- if (success) { UI.log(Messages.shareSuccess); }
- },
- keys: [13]
- }];
- var frameEmbed = UI.dialog.customModal(embed, {
- buttons: embedButtons,
- onClose: config.onClose,
- });
-
- // Create modal
- var tabs = [{
- title: Messages.share_contactCategory,
- icon: "fa fa-address-book",
- content: frameContacts,
- active: hasFriends,
- }, {
- title: Messages.share_linkCategory,
- icon: "fa fa-link",
- content: frameLink,
- active: !hasFriends
- }, {
- title: Messages.share_embedCategory,
- icon: "fa fa-code",
- content: frameEmbed
- }];
- if (typeof(AppConfig.customizeShareOptions) === 'function') {
- AppConfig.customizeShareOptions(hashes, tabs, {
- type: 'FILE',
- origin: origin,
- pathname: pathname
- });
- }
- var modal = UI.dialog.tabs(tabs);
- return modal;
- };
-
UIElements.createInviteTeamModal = function (config) {
var common = config.common;
var hasFriends = Object.keys(config.friends || {}).length !== 0;
@@ -1159,7 +336,7 @@ define([
buttons: contactsButtons
};
};
- var friendsObject = hasFriends ? getContacts() : noContactsMessage(common);
+ var friendsObject = hasFriends ? getContacts() : UIElements.noContactsMessage(common);
var friendsList = friendsObject.content;
var contactsButtons = friendsObject.buttons;
contactsButtons.unshift({
@@ -2254,7 +1431,12 @@ define([
});
$container.keydown(function (e) {
var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible');
+ if (!$value.length) {
+ $value = $innerblock.find('[data-value]').first();
+ }
if (e.which === 38) { // Up
+ e.preventDefault();
+ e.stopPropagation();
if ($value.length) {
$value.mouseleave();
var $prev = $value.prev();
@@ -2263,6 +1445,8 @@ define([
}
}
if (e.which === 40) { // Down
+ e.preventDefault();
+ e.stopPropagation();
if ($value.length) {
$value.mouseleave();
var $next = $value.next();
@@ -2271,12 +1455,16 @@ define([
}
}
if (e.which === 13) { //Enter
+ e.preventDefault();
+ e.stopPropagation();
if ($value.length) {
$value.click();
hide();
}
}
if (e.which === 27) { // Esc
+ e.preventDefault();
+ e.stopPropagation();
$value.mouseleave();
hide();
}
diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js
index 3447f5056..8f10fe3bc 100644
--- a/www/common/drive-ui.js
+++ b/www/common/drive-ui.js
@@ -1,5 +1,6 @@
define([
'jquery',
+ '/api/config/',
'/common/toolbar.js',
'json.sortify',
'/common/common-util.js',
@@ -9,6 +10,7 @@ define([
'/common/common-constants.js',
'/common/common-feedback.js',
+ '/common/inner/share.js',
'/common/inner/access.js',
'/common/inner/properties.js',
@@ -19,6 +21,7 @@ define([
'/customize/messages.js',
], function (
$,
+ ApiConfig,
Toolbar,
JSONSortify,
Util,
@@ -27,6 +30,7 @@ define([
UI,
Constants,
Feedback,
+ Share,
Access,
Properties,
nThen,
@@ -2007,26 +2011,25 @@ define([
if (!parsed.hash && !roParsed.hash) { return void console.error("Invalid href: "+(data.href || data.roHref)); }
var friends = common.getFriends();
var ro = folders[id] && folders[id].version >= 2;
- var modal = UIElements.createShareModal({
- teamId: APP.team,
- origin: APP.origin,
- pathname: "/drive/",
- friends: friends,
- title: data.title,
- password: data.password,
- sharedFolder: true,
- common: common,
- hashes: {
- editHash: parsed.hash,
- viewHash: ro && roParsed.hash,
- }
- });
// If we're a viewer and this is an old shared folder (no read-only mode), we
// can't share the read-only URL and we don't have access to the edit one.
// We should hide the share button.
- if (!modal) { return; }
+ if (!data.href && !ro) { return; }
$shareBlock.click(function () {
- UI.openCustomModal(modal);
+ Share.getShareModal(common, {
+ teamId: APP.team,
+ origin: APP.origin,
+ pathname: "/drive/",
+ friends: friends,
+ title: data.title,
+ password: data.password,
+ sharedFolder: true,
+ common: common,
+ hashes: {
+ editHash: parsed.hash,
+ viewHash: ro && roParsed.hash,
+ }
+ });
});
$container.append($shareBlock);
return $shareBlock;
@@ -2340,7 +2343,7 @@ define([
return $(common.fixLinks($box.html(msg)));
}
if (!APP.loggedIn) {
- msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages.fm_info_anonymous;
+ msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages._getKey('fm_info_anonymous', [ApiConfig.inactiveTime || 90]);
return $(common.fixLinks($box.html(msg)));
}
if (!msg || APP.store['hide-info-' + path[0]] === '1') {
@@ -3660,14 +3663,6 @@ define([
if (!readOnlyFolder) {
createNewButton(isInRoot, APP.toolbar.$bottomL);
}
- /*
- // The share button is not displayed anymore in the toolbar: users can't know
- // if they're going to share the current shared folder or the selected pad
- if (sfId) {
- createShareButton(sfId, APP.toolbar.$bottomL);
- }
- */
-
if (APP.mobile()) {
var $context = $('