diff --git a/.gitignore b/.gitignore
index 50796e9bb..5e4e0e31b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
datastore
tasks
www/bower_components/*
+www/accounts
node_modules
/config.js
customization
diff --git a/.jshintignore b/.jshintignore
index 70cec6a0e..b9ed0f0ce 100644
--- a/.jshintignore
+++ b/.jshintignore
@@ -15,6 +15,7 @@ www/common/onlyoffice/v2*
server.js
www/common/old-media-tag.js
www/scratch
+www/accounts
www/lib
www/accounts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 890f8328d..1e30778d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,31 @@
+# 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/)
+* We've updated the syntax highlighting code that we use throughout the platform to include Rustlang (and possibly other languages that have been updated in the meantime).
+* You can now use _ctrl-f_ in user or team drives to jump immediately to the search interface instead of possibly scrolling up to click on its entry in the sidebar.
+
+## 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/ckeditor-contents.css b/customize.dist/ckeditor-contents.css
index 4d2abae08..000162c00 100644
--- a/customize.dist/ckeditor-contents.css
+++ b/customize.dist/ckeditor-contents.css
@@ -205,3 +205,11 @@ a > img {
.cp-link-clicked a {
cursor: pointer;
}
+
+media-tag {
+ display: inline-block;
+}
+media-tag * {
+ width: 100%;
+ height: 100%;
+}
diff --git a/customize.dist/messages.js b/customize.dist/messages.js
index d42d8fa46..11d814540 100755
--- a/customize.dist/messages.js
+++ b/customize.dist/messages.js
@@ -5,7 +5,7 @@ var map = {
'de': 'Deutsch',
'el': 'Ελληνικά',
'es': 'Español',
- 'fi': 'Suomalainen',
+ 'fi': 'Suomi',
'fr': 'Français',
//'hi': 'हिन्दी',
'it': 'Italiano',
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/customize.dist/src/less2/include/alertify.less b/customize.dist/src/less2/include/alertify.less
index db951613c..c3767f073 100644
--- a/customize.dist/src/less2/include/alertify.less
+++ b/customize.dist/src/less2/include/alertify.less
@@ -153,12 +153,10 @@
max-width: 500px;
margin: 0 auto;
text-align: left;
- padding: @alertify_padding-base;
background: #fff;
box-shadow: @alertify_box-shadow;
&.wide {
- width: 1000px;
- max-width: 70%;
+ max-width: 850px;
}
}
@@ -178,13 +176,14 @@
max-height: 100%;
display: flex;
flex-flow: column;
+ padding: @alertify_padding-base;
.alertify-tabs-titles {
height: 40px;
display: flex;
border-bottom: 1px solid @alertify-fore;
margin-bottom: 10px;
box-sizing: content-box;
- span {
+ .alertify-tabs-title {
.tools_unselectable();
font-size: 20px;
height: 40px;
@@ -201,6 +200,13 @@
color: #949494;
cursor: not-allowed;
}
+ &:not(.alertify-tabs-active) {
+ @media (max-width: @browser_media-medium-screen) {
+ .tab-title-text {
+ display: none;
+ }
+ }
+ }
}
span.alertify-tabs-active {
background-color: @alertify-fore !important;
@@ -393,9 +399,15 @@
.cp-share-columns {
display: flex;
flex-flow: row;
+ @media screen and (max-width: (@browser_media-medium-screen)) {
+ flex-flow: column;
+ }
& > .cp-share-column {
width: 50%;
+ @media screen and (max-width: (@browser_media-medium-screen)) {
+ width: unset;
+ }
//padding: 0 10px;
position: relative;
&.contains-nav {
@@ -414,19 +426,32 @@
}
&:first-child {
margin-right: @alertify_padding-base;
+ @media screen and (max-width: (@browser_media-medium-screen)) {
+ margin: 0 0 @alertify_padding-base 0;
+ }
}
&:last-child {
margin-left: @alertify_padding-base;
+ @media screen and (max-width: (@browser_media-medium-screen)) {
+ margin: 0px;
+ }
}
}
& > .cp-share-column-mid {
display: flex;
align-items: center;
+ justify-content: center;
+ @media screen and (max-width: (@browser_media-medium-screen)) {
+ margin-bottom: @alertify_padding-base;
+ }
button {
width: 50px;
margin: 0;
min-width: 0;
font-size: 18px !important;
+ @media screen and (max-width: (@browser_media-medium-screen)) {
+ width: 100%;
+ }
}
}
}
diff --git a/customize.dist/src/less2/include/corner.less b/customize.dist/src/less2/include/corner.less
index feec62165..04d503cc9 100644
--- a/customize.dist/src/less2/include/corner.less
+++ b/customize.dist/src/less2/include/corner.less
@@ -89,6 +89,7 @@
}
&.cp-corner-big {
width: 500px;
+ max-width: 95%;
}
.cp-corner-dontshow {
diff --git a/customize.dist/src/less2/include/modal.less b/customize.dist/src/less2/include/modal.less
index 8ed10a1b2..08c0121fd 100644
--- a/customize.dist/src/less2/include/modal.less
+++ b/customize.dist/src/less2/include/modal.less
@@ -41,6 +41,7 @@
box-shadow: @variables_shadow;
padding: @variables_padding;
+ padding-top: @variables_padding * 2;
position: relative;
//top: 15vh; bottom: 15vh;
@@ -57,6 +58,11 @@
margin-bottom: 1em;
}
+ & > p:not(.cp-modal-form) {
+ text-align: left; // XXX needs testing
+ margin-right: 30px;
+ }
+
.cp-modal-form {
display: flex;
flex-wrap: wrap;
diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less
index e8282f6e8..3855d15a8 100644
--- a/customize.dist/src/less2/include/modals-ui-elements.less
+++ b/customize.dist/src/less2/include/modals-ui-elements.less
@@ -1,5 +1,7 @@
@import (reference) "./colortheme-all.less";
@import (reference) "./variables.less";
+@import (reference) "./browser.less";
+
.modals-ui-elements_main() {
--LessLoader_require: LessLoader_currentFile();
}
@@ -20,6 +22,15 @@
.cp-radio {
margin-right: 30px;
}
+ @media (max-width: @browser_media-medium-screen) {
+ flex-direction: column;
+ &:not(:last-child) {
+ margin-bottom: 0px;
+ }
+ .cp-radio {
+ margin: 0 0 5px 0;
+ }
+ }
}
}
@@ -74,6 +85,24 @@
}
// Access modal
+ button.cp-access-add {
+ i {
+ margin-right: 0px !important;
+ }
+ i.fa-arrow-left {
+ display: inline;
+ @media (max-width: @browser_media-medium-screen) {
+ display: none;
+ }
+ }
+ i.fa-arrow-up {
+ display: none;
+ @media (max-width: @browser_media-medium-screen) {
+ display: inline;
+ }
+ }
+ }
+
.cp-overlay-container {
position: relative;
.cp-overlay {
diff --git a/lib/commands/admin-rpc.js b/lib/commands/admin-rpc.js
index c60cdcfbc..427d9c03c 100644
--- a/lib/commands/admin-rpc.js
+++ b/lib/commands/admin-rpc.js
@@ -146,6 +146,33 @@ var flushCache = function (Env, Server, cb) {
cb(void 0, true);
};
+// CryptPad_AsyncStore.rpc.send('ADMIN', ['ARCHIVE_DOCUMENT', documentID], console.log)
+var archiveDocument = function (Env, Server, cb, data) {
+ var id = Array.isArray(data) && data[1];
+ if (typeof(id) !== 'string' || id.length < 32) { return void cb("EINVAL"); }
+
+ switch (id.length) {
+ case 32:
+ // TODO disconnect users from active sessions
+ return void Env.msgStore.archiveChannel(id, cb);
+ case 48:
+ return void Env.blobStore.archive.blob(id, cb);
+ default:
+ return void cb("INVALID_ID_LENGTH");
+ }
+
+ // archival for blob proofs isn't automated, but evict-inactive.js will
+ // clean up orpaned blob proofs
+ // Env.blobStore.archive.proof(userSafeKey, blobId, cb)
+};
+
+var restoreArchivedDocument = function (Env, Server, cb) {
+ // Env.msgStore.restoreArchivedChannel(channelName, cb)
+ // Env.blobStore.restore.blob(blobId, cb)
+ // Env.blobStore.restore.proof(userSafekey, blobId, cb)
+
+ cb("NOT_IMPLEMENTED");
+};
// CryptPad_AsyncStore.rpc.send('ADMIN', ['SET_DEFAULT_STORAGE_LIMIT', 1024 * 1024 * 1024 /* 1GB */], console.log)
var setDefaultStorageLimit = function (Env, Server, cb, data) {
@@ -174,6 +201,9 @@ var commands = {
GET_FILE_DESCRIPTOR_LIMIT: getFileDescriptorLimit,
SET_DEFAULT_STORAGE_LIMIT: setDefaultStorageLimit,
GET_CACHE_STATS: getCacheStats,
+
+ ARCHIVE_DOCUMENT: archiveDocument,
+ RESTORE_ARCHIVED_DOCUMENT: restoreArchivedDocument,
};
Admin.command = function (Env, safeKey, data, _cb, Server) {
diff --git a/package-lock.json b/package-lock.json
index ce127b9f9..b76dad661 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 0f79bc8af..aef342f36 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/inner.js b/www/code/inner.js
index 05356a972..692bba8bb 100644
--- a/www/code/inner.js
+++ b/www/code/inner.js
@@ -134,6 +134,16 @@ define([
framework._.toolbar.$drawer.append(helpMenu.button);
};
+
+ var previews = {};
+ previews['gfm'] = function (val, $div, common) {
+ DiffMd.apply(DiffMd.render(val), $div, common);
+ };
+ previews['markdown'] = previews['gfm'];
+ previews['htmlmixed'] = function (val, $div, common) {
+ DiffMd.apply(val, $div, common);
+ };
+
var mkPreviewPane = function (editor, CodeMirror, framework, isPresentMode) {
var $previewContainer = $('#cp-app-code-preview');
var $preview = $('#cp-app-code-preview-content');
@@ -149,17 +159,19 @@ define([
var $previewButton = framework._.sfCommon.createButton('preview', true);
var forceDrawPreview = function () {
+ var f = previews[CodeMirror.highlightMode];
+ if (!f) { return; }
try {
if (editor.getValue() === '') {
$previewContainer.addClass('cp-app-code-preview-isempty');
return;
}
$previewContainer.removeClass('cp-app-code-preview-isempty');
- DiffMd.apply(DiffMd.render(editor.getValue()), $preview, framework._.sfCommon);
+ f(editor.getValue(), $preview, framework._.sfCommon);
} catch (e) { console.error(e); }
};
var drawPreview = Util.throttle(function () {
- if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) { return; }
+ if (!previews[CodeMirror.highlightMode]) { return; }
if (!$previewButton.is('.cp-toolbar-button-active')) { return; }
forceDrawPreview();
}, 400);
@@ -171,7 +183,7 @@ define([
previewTo = setTimeout(function () {
$codeMirror.removeClass('transition');
}, 500);
- if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) {
+ if (!previews[CodeMirror.highlightMode]) {
$previewContainer.show();
}
$previewContainer.toggle();
@@ -213,7 +225,7 @@ define([
});
var modeChange = function (mode) {
- if (['markdown', 'gfm'].indexOf(mode) !== -1) {
+ if (previews[mode]) {
$previewButton.show();
framework._.sfCommon.getPadAttribute('previewMode', function (e, data) {
if (e) { return void console.error(e); }
@@ -273,7 +285,7 @@ define([
// keep trying to draw until you're confident it has been drawn
previewInt = setInterval(function () {
// give up if it's not a valid preview mode
- if (['markdown', 'gfm'].indexOf(CodeMirror.highlightMode) === -1) { return void clear(); }
+ if (!previews[CodeMirror.highlightMode]) { return void clear(); }
// give up if content has been drawn
if ($preview.text()) { return void clear(); }
// only draw if there is actually content to display
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-interface.js b/www/common/common-interface.js
index 37ef044ff..647036356 100644
--- a/www/common/common-interface.js
+++ b/www/common/common-interface.js
@@ -221,9 +221,9 @@ define([
tabs.forEach(function (tab, i) {
if (!(tab.content || tab.disabled) || !tab.title) { return; }
var content = h('div.alertify-tabs-content', tab.content);
- var title = h('span.alertify-tabs-title'+ (tab.disabled ? '.disabled' : ''), tab.title);
+ var title = h('span.alertify-tabs-title'+ (tab.disabled ? '.disabled' : ''), h('span.tab-title-text',{id: 'cp-tab-' + tab.title.toLowerCase(), 'aria-hidden':"true"}, tab.title));
if (tab.icon) {
- var icon = h('i', {class: tab.icon});
+ var icon = h('i', {class: tab.icon, 'aria-labelledby': 'cp-tab-' + tab.title.toLowerCase()});
$(title).prepend(' ').prepend(icon);
}
$(title).click(function () {
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 39558e760..e0a12b4e8 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({
@@ -1241,7 +418,7 @@ define([
var localStore = window.cryptpadStore;
localStore.get('hide-alert-teamInvite', function (val) {
if (val === '1') { return; }
- $(linkWarning).show();
+ $(linkWarning).css('display', 'flex');
$(dismissButton).on('click', function () {
localStore.put('hide-alert-teamInvite', '1');
@@ -2104,6 +1281,9 @@ define([
cb(null, $container);
return {
$container: $container,
+ update: function () {
+ common.getPinUsage(teamId, todo);
+ },
stop: function () {
clearInterval(interval);
}
diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js
index 6062b1d63..734551983 100644
--- a/www/common/diffMarked.js
+++ b/www/common/diffMarked.js
@@ -7,14 +7,15 @@ define([
'/common/hyperscript.js',
'/common/inner/common-mediatag.js',
'/common/media-tag.js',
- '/common/highlight/highlight.pack.js',
'/customize/messages.js',
+ '/common/highlight/highlight.pack.js',
'/bower_components/diff-dom/diffDOM.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'css!/common/highlight/styles/github.css'
-],function ($, ApiConfig, Marked, Hash, Util, h, MT, MediaTag, Highlight, Messages) {
+],function ($, ApiConfig, Marked, Hash, Util, h, MT, MediaTag, Messages) {
var DiffMd = {};
+ var Highlight = window.hljs;
var DiffDOM = window.diffDOM;
var renderer = new Marked.Renderer();
var restrictedRenderer = new Marked.Renderer();
diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js
index 3447f5056..4165ef009 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,
@@ -149,6 +153,17 @@ define([
var localStore = window.cryptpadStore;
APP.store = {};
+ $(window).keydown(function (e) {
+ if (e.which === 70 && e.ctrlKey) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (APP.displayDirectory) {
+ APP.displayDirectory([SEARCH]);
+ }
+ return;
+ }
+ });
+
var makeLS = function (teamId) {
var suffix = teamId ? ('-' + teamId) : '';
var LS_LAST = "app-drive-lastOpened" + suffix;
@@ -1374,7 +1389,7 @@ define([
}
$button.show();
$button.css({
- background: '#000'
+ background: '#63b1f7'
});
window.setTimeout(function () {
$button.css({
@@ -2007,26 +2022,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 +2354,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 +3674,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 = $('