diff --git a/customize.dist/messages.js b/customize.dist/messages.js
index 1351044a6..cf6f7d527 100755
--- a/customize.dist/messages.js
+++ b/customize.dist/messages.js
@@ -167,6 +167,17 @@ define(req, function(AppConfig, Default, Language) {
Messages.oo_blockURLImageDrop = 'Dragging images from a browser is not supported.'; // XXX
Messages.form_footerInfo = "This form and your answers are protected with end-to-end encryption
Only the form author can read the data
To learn more and create your own forms visit the CryptPad website"; // XXX
+ // XXX
+ Messages.form_guestAuthorTitle = "Save your author link before sharing";
+ Messages.form_guestEditLinkDefinition = "This form's author link is the private link to modify questions and read responses.";
+ Messages.form_guestAuthorBody = "This author link is currently not stored anywhere since you are not logged in. To avoid loosing access in future please save this link now:";
+ Messages.form_guestAuthorBodyStored = "This author link is currently only stored on this device, please save it to avoid loosing access to your form.";
+ Messages.form_guestAuthorCopied = "Author link copied";
+ Messages.form_guestAuthorCopy = "Copy author link";
+ Messages.form_guestAuthorContinuePublic = "Continue to public link";
+ Messages.form_guestPublicTitle = "Copy your public link";
+ Messages.form_guestPublicBody = "Share this link with participants so they can fill out your form.";
+
return Messages;
});
diff --git a/customize.dist/src/less2/include/creation.less b/customize.dist/src/less2/include/creation.less
index 6fcf8f276..03e0392ac 100644
--- a/customize.dist/src/less2/include/creation.less
+++ b/customize.dist/src/less2/include/creation.less
@@ -119,15 +119,16 @@
.cp-creation-checkboxes {
min-width: 300px;
flex-flow: column;
- align-items: baseline !important;
- max-height: 150px;
- min-height: 120px !important;
+ align-items: stretch !important;
+ max-height: 180px;
+ min-height: 140px !important;
justify-content: space-evenly;
flex: 1;
& > div {
display: flex;
width: 100%;
max-width: 100%;
+ min-width: 0;
margin: auto;
text-align: left;
}
@@ -258,40 +259,76 @@
}
.cp-creation-teams {
- display: none !important;
- .cp-creation-teams-grid {
- display: flex;
- flex-wrap: wrap;
- padding: 0 2px;
- flex: 1;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ min-width: 0;
+ width: 100%;
+ .cp-creation-store-label,
+ .cp-creation-help {
+ flex: none;
}
- .cp-creation-team {
- .avatar_main(25px);
- width: 140px;
- height: 35px;
- display: flex;
- justify-content: center;
+ .cp-creation-team-avatar {
+ .avatar_main(20px);
+ flex: none;
+ display: inline-flex;
align-items: center;
- padding: 5px;
- cursor: default;
- font: @colortheme_app-font;
- margin: 0 1px;
-
- .tools_unselectable();
-
- &.cp-selected {
- background-color: @cp_creation-button-bg;
- color: @cp_creation-button-fg;
+ justify-content: center;
+ svg, .lucide {
+ margin: 0;
}
- .cp-creation-team-name {
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
+ }
+ .cp-creation-team-name {
+ flex: 1 1 0;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ text-align: left;
+ }
+ .cp-dropdown-container {
+ flex: 1 1 0;
+ width: 0;
+ min-width: 0;
+ button.btn {
width: 100%;
- text-align: center;
- line-height: 18px;
+ justify-content: flex-start;
+ overflow: hidden;
+ color: @cp_creation-fg;
+ background: @cp_forms-bg;
+ border: 1px solid @cp_forms-border;
+ text-transform: none;
+ .cp-dropdown-button-title {
+ flex: 1 1 0;
+ min-width: 0;
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ gap: 0.5rem;
+ overflow: hidden;
+ text-align: left;
+ }
+ }
+ .cp-dropdown-content {
+ left: 0;
+ right: 0;
+ width: 100%;
+ min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
+ overflow-x: hidden;
+ li[role="menuitem"] {
+ overflow: hidden;
+ > a {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ min-width: 0;
+ max-width: 100%;
+ overflow: hidden;
+ }
+ }
}
- border: 1px solid @cp_creation-button-bg;
}
}
diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 2e2348535..3b91e3d8a 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -2814,55 +2814,79 @@ define([
}
// Team pad
- var team;
- // FIXME: broken wen cache is enabled
- var teamExists = privateData.teams && Object.keys(privateData.teams).length;
- var teamValue;
// storeInTeam can be
// * a team ID ==> store in the team drive, and the team will be the owner
// * -1 ==> store in the user drive, and the user will be the owner
- // * undefined ==> ask
- if (teamExists) {
- var teams = Object.keys(privateData.teams).map(function (id) {
- var data = privateData.teams[id];
- var avatar = h('span.cp-creation-team-avatar.cp-avatar');
- // We assume that teams always have a non-empty name, so we don't need a UID
- common.displayAvatar($(avatar), data.avatar, data.name);
- return h('div.cp-creation-team', {
- 'data-id': id,
- title: data.name,
- },[
- avatar,
- h('span.cp-creation-team-name', data.name)
- ]);
- });
- teams.unshift(h('div.cp-creation-team', {
- 'data-id': '-1',
- title: Messages.settings_cat_drive
- }, [
- h('span.cp-creation-team-avatar', Icons.get('drive')),
- h('span.cp-creation-team-name', Messages.settings_cat_drive)
- ]));
- team = h('div.cp-creation-teams', [
- Messages.team_pcsSelectLabel,
- h('div.cp-creation-teams-grid', teams),
- createHelper('#', Messages.team_pcsSelectHelp)
- ]);
- var $team = $(team);
- $team.find('.cp-creation-team').click(function () {
- if ($(this).hasClass('cp-selected')) {
- teamValue = undefined;
- return void $(this).removeClass('cp-selected');
- }
- $team.find('.cp-creation-team').removeClass('cp-selected');
- $(this).addClass('cp-selected');
- teamValue = $(this).attr('data-id');
- });
- if (privateData.storeInTeam) {
- $team.find('[data-id="'+privateData.storeInTeam+'"]').addClass('cp-selected');
- teamValue = privateData.storeInTeam;
+ // * undefined ==> ask (CryptDrive / team / don't store); default CryptDrive
+ var team;
+ var teamValue = typeof(privateData.storeInTeam) === "undefined" ? '-1' : String(privateData.storeInTeam);
+ var getTeamLabel = function (val) {
+ if (val === '-1') { return Messages.settings_cat_drive; }
+ if (!val || val === 'none') { return Messages.autostore_hide; }
+ return privateData.teams?.[val]?.name || val;
+ };
+ var getTeamAvatar = function (val) {
+ if (val === '-1') {
+ return h('span.cp-creation-team-avatar', Icons.get('drive'));
}
- }
+ if (!val || val === 'none') {
+ return h('span.cp-creation-team-avatar', Icons.get('close'));
+ }
+ var data = privateData.teams?.[val];
+ var avatar = h('span.cp-creation-team-avatar.cp-avatar');
+ if (data) {
+ common.displayAvatar($(avatar), data.avatar, data.name);
+ }
+ return avatar;
+ };
+ var teamOptions = [{
+ tag: 'a',
+ attributes: { 'data-value': '-1' },
+ content: [getTeamAvatar('-1'), h('span.cp-creation-team-name', getTeamLabel('-1'))]
+ }];
+ Object.keys(privateData.teams || {}).forEach(function (id) {
+ var data = privateData.teams[id];
+ if (!data) { return; }
+ teamOptions.push({
+ tag: 'a',
+ attributes: { 'data-value': id },
+ content: [getTeamAvatar(id), h('span.cp-creation-team-name', data.name)]
+ });
+ });
+ teamOptions.push({
+ tag: 'a',
+ attributes: { 'data-value': 'none' },
+ content: [getTeamAvatar('none'), h('span.cp-creation-team-name', getTeamLabel('none'))]
+ });
+ var $teamSelect = UIElements.createDropdown({
+ text: getTeamLabel(teamValue),
+ options: teamOptions,
+ isSelect: true,
+ caretDown: true,
+ initialValue: teamValue,
+ buttonTitle: getTeamLabel(teamValue),
+ common: common
+ });
+ var $teamBtn = $teamSelect.find('button').addClass('btn');
+ var setTeamButton = function (val) {
+ var label = getTeamLabel(val);
+ $teamBtn[0]?._tippy?.destroy();
+ $teamBtn.attr('title', label);
+ $teamBtn.find('.cp-dropdown-button-title').empty().append([
+ getTeamAvatar(val),
+ h('span.cp-creation-team-name', label)
+ ]);
+ };
+ setTeamButton(teamValue);
+ $teamSelect.onChange.reg(function (text, value) {
+ teamValue = typeof(value) === "undefined" ? 'none' : String(value);
+ setTeamButton(teamValue);
+ });
+ team = h('div.cp-creation-teams', [
+ h('span.cp-creation-store-label', Messages.team_pcsSelectLabel),
+ $teamSelect[0],
+ // createHelper('#', Messages.team_pcsSelectHelp) - commented until the documentation is updated
+ ]);
// Owned pads
@@ -3145,9 +3169,16 @@ define([
var templateContent = $template.data('content') || undefined;
// Team
var team;
- if (teamValue) {
- team = privateData.teams[teamValue] || {};
- team.id = Number(teamValue);
+ if (teamValue === 'none') {
+ team = false;
+ } else if (teamValue === '-1') {
+ team = { id: -1 };
+ } else if (teamValue) {
+ var selectedTeam = privateData.teams[teamValue] || {};
+ team = {
+ id: Number(teamValue),
+ edPublic: selectedTeam.edPublic
+ };
}
return {
diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index a6b8fe541..eac86517b 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -1151,6 +1151,13 @@ define([
data.href = parsed.getUrl({present: parsed.present});
if (typeof (data.title) !== "string") { return cb('Missing title'); }
+ if (common.initialTeam === false) {
+ if (!data.forceSave) {
+ common.autoStore.onStoreRequest.fire({});
+ return void cb(null, { notStored: true });
+ }
+ delete common.initialTeam;
+ }
if (common.initialTeam) {
// If the value is -1, it means the user drive was selected from the pad creation screen
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index d6e5a05d7..9b265c13b 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -1179,6 +1179,11 @@ define([
openURL(url);
});
sframeChan.on('EV_OPEN_URL', openURL);
+ sframeChan.on('EV_OPEN_VIEW_URL', function () {
+ if (!hashes || !hashes.viewHash) { return; }
+ var a = window.open(Utils.Hash.hashToHref(hashes.viewHash, 'form'));
+ if (!a) { _sframeChan.event('EV_POPUP_BLOCKED'); }
+ });
sframeChan.on('Q_GET_PAD_METADATA', function (data, cb) {
if (!data || !data.channel) {
@@ -1769,7 +1774,9 @@ define([
});
sframeChan.on('Q_SAVE_AS_TEMPLATE', function (data, cb) {
- data.teamId = Cryptpad.initialTeam;
+ if (Cryptpad.initialTeam) {
+ data.teamId = Cryptpad.initialTeam;
+ }
Cryptpad.saveAsTemplate(Cryptget.put, data, cb);
});
@@ -2292,12 +2299,8 @@ define([
});
});
});
- sframeChan.on('EV_OPEN_VIEW_URL', function () {
- var url = Utils.Hash.hashToHref(hashes.viewHash, 'form');
- var a = window.open(url);
- if (!a) {
- sframeChan.event('EV_POPUP_BLOCKED');
- }
+ sframeChan.on('EV_FORM_GUEST_SHARE_OPEN', function (data) {
+ initSecureModal('formGuestShare', data || {});
});
Handler.formCommandHandlers(sframeChan, Utils, nThen, Cryptpad);
@@ -2550,8 +2553,12 @@ define([
if (cfg.integration) { rtConfig.metadata.selfdestruct = true; }
- if (data.team) {
+ if (data.team === false) {
+ Cryptpad.initialTeam = false;
+ } else if (data.team) {
Cryptpad.initialTeam = data.team.id;
+ } else {
+ delete Cryptpad.initialTeam;
}
if (data.owned && data.team && data.team.edPublic) {
rtConfig.metadata.owners = [data.team.edPublic];
diff --git a/www/form/inner.js b/www/form/inner.js
index 0d419feea..c8f77ea4c 100644
--- a/www/form/inner.js
+++ b/www/form/inner.js
@@ -16,7 +16,6 @@ define([
'/common/common-hash.js',
'/common/common-interface.js',
'/common/common-ui-elements.js',
- '/common/clipboard.js',
'/common/inner/common-mediatag.js',
'/common/hyperscript.js',
'/customize/messages.js',
@@ -64,7 +63,6 @@ define([
Hash,
UI,
UIElements,
- Clipboard,
MT,
h,
Messages,
@@ -4920,14 +4918,35 @@ define([
Messages.form_geturl
]);
var preview = h('div.cp-forms-results-participant', [previewBtn, participantBtn]);
+ var shouldWarnGuestAuthor = function () {
+ if (framework._.sfCommon.isLoggedIn()) { return false; }
+ if (!APP.isEditor) { return false; }
+ return true;
+ };
+ var warnGuestAuthorThen = function (next) {
+ framework._.sfCommon.isPadStored(function (err, stored) {
+ sframeChan.event('EV_FORM_GUEST_SHARE_OPEN', {
+ next: next,
+ stored: Boolean(stored)
+ });
+ });
+ };
+
$(previewBtn).click(function () {
- sframeChan.event('EV_OPEN_VIEW_URL');
+ if (!shouldWarnGuestAuthor()) {
+ return void sframeChan.event('EV_OPEN_VIEW_URL');
+ }
+ warnGuestAuthorThen('preview');
});
$(participantBtn).click(function () {
- sframeChan.query('Q_COPY_VIEW_URL', null, function (err, success) {
- if (success) { return void UI.log(Messages.shareSuccess); }
- UI.warn(Messages.error);
- });
+ if (!shouldWarnGuestAuthor()) {
+ sframeChan.query('Q_COPY_VIEW_URL', null, function (err, success) {
+ if (success) { return void UI.log(Messages.shareSuccess); }
+ UI.warn(Messages.error);
+ });
+ return;
+ }
+ warnGuestAuthorThen('public');
});
// Private / public status
diff --git a/www/secureiframe/app-secure.less b/www/secureiframe/app-secure.less
index 19470b6c8..bcc4b38ec 100644
--- a/www/secureiframe/app-secure.less
+++ b/www/secureiframe/app-secure.less
@@ -106,4 +106,9 @@
}
}
}
+ .alertify .cp-form-guest-share-modal {
+ h4, h5, h6, p, textarea {
+ color: @cryptpad_text_col;
+ }
+ }
}
diff --git a/www/secureiframe/inner.js b/www/secureiframe/inner.js
index bc1bac989..c3bb5b98f 100644
--- a/www/secureiframe/inner.js
+++ b/www/secureiframe/inner.js
@@ -44,6 +44,123 @@ define([
};
var create = {};
+ create['formGuestShare'] = function (data) {
+ data = data || {};
+ require(['/common/clipboard.js'], function (Clipboard) {
+ var priv = metadataMgr.getPrivateData();
+ var hashes = priv.hashes || {};
+ var origin = priv.origin || '';
+ var editUrl = hashes.editHash ?
+ (origin + Hash.hashToHref(hashes.editHash, 'form')) : '';
+ var viewUrl = hashes.viewHash ?
+ (origin + Hash.hashToHref(hashes.viewHash, 'form')) : '';
+
+ var showPublic = function () {
+ if (!viewUrl) { return void hideIframe(); }
+ var frame;
+ var modal = UI.dialog.customModal(h('div.cp-form-guest-share-modal', [
+ h('h4', Messages.form_guestPublicTitle),
+ h('p', Messages.form_guestPublicBody),
+ UI.dialog.selectableArea(viewUrl, {
+ id: 'cp-form-guest-public-link',
+ rows: 2
+ })
+ ]), {
+ onClose: function () {
+ if (displayed === frame) { hideIframe(); }
+ },
+ buttons: [{
+ className: 'cancel',
+ name: Messages.cancel,
+ onClick: function () {},
+ keys: [27]
+ }, {
+ className: 'primary',
+ name: Messages.form_geturl,
+ iconClass: 'copy',
+ onClick: function () {
+ Clipboard.copy(viewUrl, function (err) {
+ if (err) { return void UI.warn(Messages.error); }
+ UI.log(Messages.shareSuccess);
+ hideIframe();
+ });
+ return true;
+ },
+ keys: [13]
+ }]
+ });
+ frame = UI.openCustomModal(modal);
+ displayed = frame;
+ };
+
+ var showAuthor = function () {
+ if (!editUrl) {
+ if (data.next === 'public') { return void showPublic(); }
+ if (data.next === 'preview') {
+ sframeChan.event('EV_OPEN_VIEW_URL');
+ }
+ return void hideIframe();
+ }
+ var frame;
+ var stay;
+ var modal = UI.dialog.customModal(h('div.cp-form-guest-share-modal', [
+ h('h4', Messages.form_guestAuthorTitle),
+ h('p', Messages.form_guestEditLinkDefinition),
+ h('p', data.stored ?
+ Messages.form_guestAuthorBodyStored : Messages.form_guestAuthorBody),
+ UI.dialog.selectableArea(editUrl, {
+ id: 'cp-form-guest-author-link',
+ rows: 2
+ })
+ ]), {
+ onClose: function () {
+ if (stay || displayed !== frame) { return; }
+ hideIframe();
+ },
+ buttons: [{
+ className: 'cancel',
+ name: Messages.cancel,
+ onClick: function () {},
+ keys: [27]
+ }, {
+ className: 'secondary',
+ name: Messages.form_guestAuthorCopy,
+ iconClass: 'copy',
+ onClick: function () {
+ Clipboard.copy(editUrl, function (err) {
+ if (err) { return void UI.warn(Messages.error); }
+ UI.log(Messages.form_guestAuthorCopied);
+ });
+ return true;
+ },
+ keys: []
+ }, {
+ className: 'primary',
+ name: data.next === 'public' ?
+ Messages.form_guestAuthorContinuePublic : Messages.continue,
+ onClick: function () {
+ if (data.next === 'public') {
+ stay = true;
+ if (frame && frame.closeModal) {
+ frame.closeModal(showPublic);
+ } else {
+ showPublic();
+ }
+ return true;
+ }
+ if (data.next === 'preview') {
+ sframeChan.event('EV_OPEN_VIEW_URL');
+ }
+ },
+ keys: [13]
+ }]
+ });
+ frame = UI.openCustomModal(modal);
+ displayed = frame;
+ };
+ showAuthor();
+ });
+ };
// Share modal
create['share'] = function (data) {