mirror of https://github.com/xwiki-labs/cryptpad
Merge branch 'form' into staging
This commit is contained in:
commit
ff5ba8eb1c
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
|
@ -1,9 +1,9 @@
|
|||
@font-face {
|
||||
font-family: 'cptools';
|
||||
src:
|
||||
url('fonts/cptools.ttf?n9y2kz') format('truetype'),
|
||||
url('fonts/cptools.woff?n9y2kz') format('woff'),
|
||||
url('fonts/cptools.svg?n9y2kz#cptools') format('svg');
|
||||
url('fonts/cptools.ttf?am461j') format('truetype'),
|
||||
url('fonts/cptools.woff?am461j') format('woff'),
|
||||
url('fonts/cptools.svg?am461j#cptools') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
|
@ -25,11 +25,35 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.cptools-sheet:before {
|
||||
content: "\e908";
|
||||
.cptools-form-list-check:before {
|
||||
content: "\e916";
|
||||
}
|
||||
.cptools-slide:before {
|
||||
content: "\e907";
|
||||
.cptools-form-grid-check:before {
|
||||
content: "\e917";
|
||||
}
|
||||
.cptools-form-poll:before {
|
||||
content: "\e910";
|
||||
}
|
||||
.cptools-form-grid-radio:before {
|
||||
content: "\e918";
|
||||
}
|
||||
.cptools-form-list-radio:before {
|
||||
content: "\e919";
|
||||
}
|
||||
.cptools-form-page-break:before {
|
||||
content: "\e91a";
|
||||
}
|
||||
.cptools-form-paragraph:before {
|
||||
content: "\e91b";
|
||||
}
|
||||
.cptools-form-text:before {
|
||||
content: "\e91c";
|
||||
}
|
||||
.cptools-form-list-ordered:before {
|
||||
content: "\e91d";
|
||||
}
|
||||
.cptools-folder-no-color:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.cptools-whiteboard:before {
|
||||
content: "\e901";
|
||||
|
@ -37,6 +61,9 @@
|
|||
.cptools-new-template:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.cptools-shared-folder:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.cptools-file-upload:before {
|
||||
content: "\e904";
|
||||
}
|
||||
|
@ -46,9 +73,24 @@
|
|||
.cptools-poll:before {
|
||||
content: "\e906";
|
||||
}
|
||||
.cptools-slide:before {
|
||||
content: "\e907";
|
||||
}
|
||||
.cptools-sheet:before {
|
||||
content: "\e908";
|
||||
}
|
||||
.cptools-folder-open:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.cptools-kanban:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.cptools-folder:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
.cptools-shared-folder-open:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
.cptools-code:before {
|
||||
content: "\e90d";
|
||||
}
|
||||
|
@ -58,8 +100,11 @@
|
|||
.cptools-file:before {
|
||||
content: "\e90f";
|
||||
}
|
||||
.cptools-destroy:before {
|
||||
content: "\e915";
|
||||
.cptools-palette:before {
|
||||
content: "\e911";
|
||||
}
|
||||
.cptools-folder-upload:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.cptools-add-bottom:before {
|
||||
content: "\e913";
|
||||
|
@ -67,24 +112,6 @@
|
|||
.cptools-add-top:before {
|
||||
content: "\e914";
|
||||
}
|
||||
.cptools-folder-upload:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.cptools-folder-no-color:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.cptools-shared-folder:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.cptools-folder-open:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.cptools-folder:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
.cptools-shared-folder-open:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
.cptools-palette:before {
|
||||
content: "\e911";
|
||||
.cptools-destroy:before {
|
||||
content: "\e915";
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
code: #EAA000;
|
||||
slide: #e57614;
|
||||
poll: #2c9e98;
|
||||
form: #2c9e98;
|
||||
whiteboard: #a72ba7;
|
||||
kanban: #8C4;
|
||||
sheet: #40865c;
|
||||
|
@ -426,3 +427,13 @@
|
|||
@cp_calendar-now: @cryptpad_color_brand_300;
|
||||
@cp_calendar-now-fg: @cryptpad_color_grey_800;
|
||||
|
||||
// Forms
|
||||
@cp_form-bg1: @cryptpad_color_grey_800;
|
||||
@cp_form-bg2: @cryptpad_color_grey_900;
|
||||
@cp_form-border: @cryptpad_color_grey_800;
|
||||
@cp_form-poll-color: @cryptpad_color_grey_800;
|
||||
@cp_form-poll-no: @cryptpad_color_light_red;
|
||||
@cp_form-poll-yes: @cryptpad_color_light_green;
|
||||
@cp_form-poll-maybe: @cryptpad_color_light_yellow;
|
||||
@cp_form-poll-yes-color: @cryptpad_color_green;
|
||||
@cp_form-invalid: @cryptpad_color_red;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
code: #EAA000;
|
||||
slide: #e57614;
|
||||
poll: #2c9e98;
|
||||
form: #2c9e98;
|
||||
whiteboard: #a72ba7;
|
||||
kanban: #8C4;
|
||||
sheet: #40865c;
|
||||
|
@ -425,3 +426,14 @@
|
|||
@cp_calendar-border: @cryptpad_color_grey_300;
|
||||
@cp_calendar-now: @cryptpad_color_brand;
|
||||
@cp_calendar-now-fg: @cryptpad_color_grey_200;
|
||||
|
||||
// Forms
|
||||
@cp_form-bg1: @cryptpad_color_grey_200;
|
||||
@cp_form-bg2: @cryptpad_color_grey_100;
|
||||
@cp_form-border: @cryptpad_color_grey_200;
|
||||
@cp_form-poll-color: @cryptpad_color_grey_800;
|
||||
@cp_form-poll-no: @cryptpad_color_light_red;
|
||||
@cp_form-poll-yes: @cryptpad_color_light_green;
|
||||
@cp_form-poll-maybe: @cryptpad_color_light_yellow;
|
||||
@cp_form-poll-yes-color: @cryptpad_color_green;
|
||||
@cp_form-invalid: @cryptpad_color_red;
|
||||
|
|
|
@ -71,6 +71,12 @@
|
|||
|
||||
div.cp-button-confirm {
|
||||
display: inline-block;
|
||||
&.new {
|
||||
vertical-align: top;
|
||||
button {
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
button {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
@ -85,7 +91,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
button.cp-button-confirm-placeholder {
|
||||
button.cp-button-confirm-placeholder:not(.new) {
|
||||
margin-bottom: 3px !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ define(function() {
|
|||
* You should never remove the drive from this list.
|
||||
*/
|
||||
AppConfig.availablePadTypes = ['drive', 'teams', 'pad', 'sheet', 'code', 'slide', 'poll', 'kanban', 'whiteboard',
|
||||
/*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts' /*, 'calendar' */];
|
||||
/*'doc', 'presentation',*/ 'file', /*'todo',*/ 'contacts', 'form'];
|
||||
/* The registered only types are apps restricted to registered users.
|
||||
* You should never remove apps from this list unless you know what you're doing. The apps
|
||||
* listed here by default can't work without a user account.
|
||||
|
@ -117,6 +117,7 @@ define(function() {
|
|||
code: 'cptools-code',
|
||||
slide: 'cptools-slide',
|
||||
poll: 'cptools-poll',
|
||||
form: 'cptools-poll',
|
||||
whiteboard: 'cptools-whiteboard',
|
||||
todo: 'cptools-todo',
|
||||
contacts: 'fa-address-book',
|
||||
|
|
|
@ -34,6 +34,12 @@ var factory = function (Util, Crypto, Keys, Nacl) {
|
|||
var keyPair = Nacl.sign.keyPair.fromSecretKey(privateKey);
|
||||
return Nacl.util.encodeBase64(keyPair.publicKey);
|
||||
};
|
||||
Hash.getCurvePublicFromPrivate = function (curvePrivateSafeStr) {
|
||||
var curvePrivateStr = Crypto.b64AddSlashes(curvePrivateSafeStr);
|
||||
var privateKey = Nacl.util.decodeBase64(curvePrivateStr);
|
||||
var keyPair = Nacl.box.keyPair.fromSecretKey(privateKey);
|
||||
return Nacl.util.encodeBase64(keyPair.publicKey);
|
||||
};
|
||||
|
||||
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
|
||||
var version = secret.version;
|
||||
|
@ -209,6 +215,17 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
|
|||
});
|
||||
return k ? Crypto.b64AddSlashes(k) : '';
|
||||
};
|
||||
var getAuditorKey = function (hashArr) {
|
||||
var k;
|
||||
// Check if we have a ownerKey for this pad
|
||||
hashArr.some(function (data) {
|
||||
if (/^auditor=/.test(data)) {
|
||||
k = data.slice(8);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return k ? Crypto.b64AddSlashes(k) : '';
|
||||
};
|
||||
var getOwnerKey = function (hashArr) {
|
||||
var k;
|
||||
// Check if we have a ownerKey for this pad
|
||||
|
@ -231,6 +248,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
|
|||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
parsed.versionHash = getVersionHash(options);
|
||||
parsed.auditorKey = getAuditorKey(options);
|
||||
parsed.newPadOpts = getNewPadOpts(options);
|
||||
parsed.loginOpts = getLoginOpts(options);
|
||||
parsed.ownerKey = getOwnerKey(options);
|
||||
|
@ -272,6 +290,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
|
|||
present: parsed.present,
|
||||
ownerKey: parsed.ownerKey,
|
||||
versionHash: parsed.versionHash,
|
||||
auditorKey: parsed.auditorKey,
|
||||
newPadOpts: parsed.newPadOpts,
|
||||
loginOpts: parsed.loginOpts,
|
||||
password: parsed.password
|
||||
|
@ -298,6 +317,10 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app)
|
|||
if (versionHash) {
|
||||
hash += 'hash=' + Crypto.b64RemoveSlashes(versionHash) + '/';
|
||||
}
|
||||
var auditorKey = typeof(opts.auditorKey) !== "undefined" ? opts.auditorKey : parsed.auditorKey;
|
||||
if (auditorKey) {
|
||||
hash += 'auditor=' + Crypto.b64RemoveSlashes(auditorKey) + '/';
|
||||
}
|
||||
if (opts.newPadOpts) { hash += 'newpad=' + opts.newPadOpts + '/'; }
|
||||
if (opts.loginOpts) { hash += 'login=' + opts.loginOpts + '/'; }
|
||||
return hash;
|
||||
|
|
|
@ -747,6 +747,7 @@ define([
|
|||
cb = Util.once(cb);
|
||||
}
|
||||
var classes = 'btn ' + (config.classes || 'btn-primary');
|
||||
var newCls = config.new ? '.new' : '';
|
||||
|
||||
var button = h('button', {
|
||||
"class": classes,
|
||||
|
@ -759,7 +760,7 @@ define([
|
|||
});
|
||||
var timer = h('div.cp-button-timer', div);
|
||||
|
||||
var content = h('div.cp-button-confirm', [
|
||||
var content = h('div.cp-button-confirm'+newCls, [
|
||||
button,
|
||||
timer
|
||||
]);
|
||||
|
@ -795,7 +796,8 @@ define([
|
|||
to = setTimeout(todo, INTERVAL);
|
||||
};
|
||||
|
||||
$(originalBtn).addClass('cp-button-confirm-placeholder').click(function (e) {
|
||||
var newCls2 = config.new ? 'new' : '';
|
||||
$(originalBtn).addClass('cp-button-confirm-placeholder').addClass(newCls2).click(function (e) {
|
||||
e.stopPropagation();
|
||||
// If we have a validation function, continue only if it's true
|
||||
if (config.validate && !config.validate()) { return; }
|
||||
|
@ -1175,6 +1177,7 @@ define([
|
|||
var label = h('span.cp-checkmark-label', labelTxt);
|
||||
|
||||
$mark.keydown(function (e) {
|
||||
if ($input.is(':disabled')) { return; }
|
||||
if (e.which === 32) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
@ -1220,20 +1223,22 @@ define([
|
|||
$.extend(markOpts, opts.mark || {});
|
||||
|
||||
var input = h('input', inputOpts);
|
||||
var $input = $(input);
|
||||
var mark = h('span.cp-radio-mark', markOpts);
|
||||
var label = h('span.cp-checkmark-label', labelTxt);
|
||||
|
||||
$(mark).keydown(function (e) {
|
||||
if ($input.is(':disabled')) { return; }
|
||||
if (e.which === 32) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if ($(input).is(':checked')) { return; }
|
||||
$(input).prop('checked', !$(input).is(':checked'));
|
||||
$(input).change();
|
||||
if ($input.is(':checked')) { return; }
|
||||
$input.prop('checked', !$input.is(':checked'));
|
||||
$input.change();
|
||||
}
|
||||
});
|
||||
|
||||
$(input).change(function () { $(mark).focus(); });
|
||||
$input.change(function () { $(mark).focus(); });
|
||||
|
||||
var radio = h('label', labelOpts, [
|
||||
input,
|
||||
|
|
|
@ -936,7 +936,8 @@ define([
|
|||
return button;
|
||||
};
|
||||
|
||||
var createMdToolbar = function (common, editor) {
|
||||
var createMdToolbar = function (common, editor, cfg) {
|
||||
cfg = cfg || {};
|
||||
var $toolbar = $('<div>', {
|
||||
'class': 'cp-markdown-toolbar'
|
||||
});
|
||||
|
@ -1025,9 +1026,39 @@ define([
|
|||
icon: 'fa-newspaper-o'
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof(cfg.embed) === "function") {
|
||||
actions.embed = {
|
||||
icon: 'fa-picture-o',
|
||||
action: function () {
|
||||
var _cfg = {
|
||||
types: ['file'],
|
||||
where: ['root']
|
||||
};
|
||||
common.openFilePicker(_cfg, function (data) {
|
||||
if (data.type !== 'file') {
|
||||
console.log("Unexpected data type picked " + data.type);
|
||||
return;
|
||||
}
|
||||
if (data.type !== 'file') { console.log('unhandled embed type ' + data.type); return; }
|
||||
common.setPadAttribute('atime', +new Date(), null, data.href);
|
||||
var privateDat = common.getMetadataMgr().getPrivateData();
|
||||
var origin = privateDat.fileHost || privateDat.origin;
|
||||
var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src;
|
||||
cfg.embed($('<media-tag src="' + src +
|
||||
'" data-crypto-key="cryptpad:' + data.key + '"></media-tag>'), data);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var onClick = function () {
|
||||
var type = $(this).attr('data-type');
|
||||
var texts = editor.getSelections();
|
||||
if (actions[type].action) {
|
||||
return actions[type].action();
|
||||
}
|
||||
var newTexts = texts.map(function (str) {
|
||||
str = str || Messages.mdToolbar_defaultText;
|
||||
if (actions[type].apply) {
|
||||
|
@ -1054,7 +1085,7 @@ define([
|
|||
}).appendTo($toolbar);
|
||||
return $toolbar;
|
||||
};
|
||||
UIElements.createMarkdownToolbar = function (common, editor) {
|
||||
UIElements.createMarkdownToolbar = function (common, editor, opts) {
|
||||
var readOnly = common.getMetadataMgr().getPrivateData().readOnly;
|
||||
if (readOnly) {
|
||||
return {
|
||||
|
@ -1064,7 +1095,7 @@ define([
|
|||
};
|
||||
}
|
||||
|
||||
var $toolbar = createMdToolbar(common, editor);
|
||||
var $toolbar = createMdToolbar(common, editor, opts);
|
||||
var cfg = {
|
||||
title: Messages.mdToolbar_button,
|
||||
element: $toolbar
|
||||
|
@ -1133,6 +1164,7 @@ define([
|
|||
sheet: 'sheets',
|
||||
poll: 'poll',
|
||||
kanban: 'kanban',
|
||||
form: 'form',
|
||||
whiteboard: 'whiteboard',
|
||||
};
|
||||
|
||||
|
@ -1472,11 +1504,13 @@ define([
|
|||
if (config.isSelect) {
|
||||
var pressed = '';
|
||||
var to;
|
||||
$container.onChange = Util.mkEvent();
|
||||
$container.on('click', 'a', function () {
|
||||
value = $(this).data('value');
|
||||
var $val = $(this);
|
||||
var textValue = $val.html() || value;
|
||||
$button.find('.cp-dropdown-button-title').html(textValue);
|
||||
$container.onChange.fire(textValue, value);
|
||||
});
|
||||
$container.keydown(function (e) {
|
||||
var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible');
|
||||
|
@ -2050,6 +2084,7 @@ define([
|
|||
AppConfig.registeredOnlyTypes.indexOf(p) !== -1) { return; }
|
||||
return true;
|
||||
});
|
||||
Messages.type.form = "Form"; // XXX
|
||||
types.forEach(function (p) {
|
||||
var $element = $('<li>', {
|
||||
'class': 'cp-icons-element',
|
||||
|
@ -3012,6 +3047,7 @@ define([
|
|||
// ACCEPT
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: msg.content.channel,
|
||||
channels: msg.content.channels,
|
||||
command: 'ADD_OWNERS',
|
||||
value: [priv.edPublic]
|
||||
}, function (err, res) {
|
||||
|
@ -3061,6 +3097,7 @@ define([
|
|||
// Remove yourself from the pending owners
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: msg.content.channel,
|
||||
channels: msg.content.channels,
|
||||
command: 'RM_PENDING_OWNERS',
|
||||
value: [priv.edPublic]
|
||||
}, function (err, res) {
|
||||
|
@ -3077,6 +3114,7 @@ define([
|
|||
// Remove yourself from the pending owners
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: msg.content.channel,
|
||||
channels: msg.content.channels,
|
||||
command: 'RM_PENDING_OWNERS',
|
||||
value: [priv.edPublic]
|
||||
}, function (err, res) {
|
||||
|
|
|
@ -76,7 +76,7 @@ define([
|
|||
postMessage("GET", {
|
||||
key: ['edPrivate'],
|
||||
}, waitFor(function (obj) {
|
||||
if (obj.error) { return; }
|
||||
if (!obj || obj.error) { return; }
|
||||
try {
|
||||
keys.push({
|
||||
edPrivate: obj,
|
||||
|
@ -84,14 +84,16 @@ define([
|
|||
});
|
||||
} catch (e) { console.error(e); }
|
||||
}));
|
||||
|
||||
// Push teams keys
|
||||
postMessage("GET", {
|
||||
key: ['teams'],
|
||||
}, waitFor(function (obj) {
|
||||
if (obj.error) { return; }
|
||||
if (!obj || obj.error) { return; }
|
||||
Object.keys(obj || {}).forEach(function (id) {
|
||||
var t = obj[id];
|
||||
var _keys = t.keys.drive || {};
|
||||
_keys.id = id;
|
||||
if (!_keys.edPrivate) { return; }
|
||||
keys.push(t.keys.drive);
|
||||
});
|
||||
|
@ -101,6 +103,57 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
common.getFormKeys = function (cb) {
|
||||
var curvePrivate;
|
||||
var formSeed;
|
||||
Nthen(function (waitFor) {
|
||||
postMessage("GET", {
|
||||
key: ['curvePrivate'],
|
||||
}, waitFor(function (obj) {
|
||||
if (!obj || obj.error) { return; }
|
||||
curvePrivate = obj;
|
||||
}));
|
||||
postMessage("GET", {
|
||||
key: ['form_seed'],
|
||||
}, waitFor(function (obj) {
|
||||
if (!obj || obj.error) { return; }
|
||||
formSeed = obj;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
cb({
|
||||
curvePrivate: curvePrivate,
|
||||
curvePublic: curvePrivate && Hash.getCurvePublicFromPrivate(curvePrivate),
|
||||
formSeed: formSeed
|
||||
});
|
||||
});
|
||||
};
|
||||
common.getFormAnswer = function (data, cb) {
|
||||
postMessage("GET", {
|
||||
key: ['forms', data.channel],
|
||||
}, cb);
|
||||
};
|
||||
common.storeFormAnswer = function (data) {
|
||||
postMessage("SET", {
|
||||
key: ['forms', data.channel],
|
||||
value: {
|
||||
hash: data.hash,
|
||||
curvePrivate: data.curvePrivate,
|
||||
anonymous: data.anonymous
|
||||
}
|
||||
}, function (obj) {
|
||||
if (obj && obj.error) {
|
||||
if (obj.error === "ENODRIVE") {
|
||||
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
|
||||
if (answered.indexOf(data.channel) === -1) { answered.push(data.channel); }
|
||||
localStorage.CP_formAnswered = JSON.stringify(answered);
|
||||
return;
|
||||
}
|
||||
console.error(obj.error);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
common.makeNetwork = function (cb) {
|
||||
require([
|
||||
'/bower_components/netflux-websocket/netflux-client.js',
|
||||
|
@ -712,6 +765,10 @@ define([
|
|||
delete meta.chat2;
|
||||
delete meta.chat;
|
||||
delete meta.cursor;
|
||||
|
||||
if (meta.type === "form") {
|
||||
delete parsed.answers;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -32,6 +32,12 @@ define([
|
|||
var teamOwner = data.teamId;
|
||||
var title = opts.title;
|
||||
|
||||
var p = priv.propChannels;
|
||||
var otherChan;
|
||||
if (p && p.answersChannel) {
|
||||
otherChan = [p.answersChannel];
|
||||
}
|
||||
|
||||
opts = opts || {};
|
||||
var redrawAll = function () {};
|
||||
|
||||
|
@ -255,6 +261,7 @@ define([
|
|||
// Send the command
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
command: 'ADD_OWNERS',
|
||||
value: toAddTeams.map(function (obj) { return obj.edPublic; }),
|
||||
teamId: teamOwner
|
||||
|
@ -290,6 +297,7 @@ define([
|
|||
// Send the command
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
command: 'ADD_PENDING_OWNERS',
|
||||
value: toAdd,
|
||||
teamId: teamOwner
|
||||
|
@ -310,6 +318,7 @@ define([
|
|||
// Send the command
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
command: 'ADD_OWNERS',
|
||||
value: [priv.edPublic],
|
||||
teamId: teamOwner
|
||||
|
@ -338,6 +347,7 @@ define([
|
|||
if (!friend) { return; }
|
||||
common.mailbox.sendTo("ADD_OWNER", {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
href: href,
|
||||
calendar: opts.calendar,
|
||||
password: data.password || priv.password,
|
||||
|
@ -417,6 +427,12 @@ define([
|
|||
var allowed = data.allowed || [];
|
||||
var teamOwner = data.teamId;
|
||||
|
||||
var p = priv.propChannels;
|
||||
var otherChan;
|
||||
if (p && p.answersChannel) {
|
||||
otherChan = [p.answersChannel];
|
||||
}
|
||||
|
||||
var redrawAll = function () {};
|
||||
|
||||
var addBtn = h('button.btn.btn-primary.cp-access-add', [h('i.fa.fa-arrow-left'), h('i.fa.fa-arrow-up')]);
|
||||
|
@ -495,6 +511,7 @@ define([
|
|||
// Send the command
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
command: 'RM_ALLOWED',
|
||||
value: [ed],
|
||||
teamId: teamOwner
|
||||
|
@ -524,6 +541,7 @@ define([
|
|||
var val = $checkbox.is(':checked');
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
command: 'RESTRICT_ACCESS',
|
||||
value: [Boolean(val)],
|
||||
teamId: teamOwner
|
||||
|
@ -659,6 +677,7 @@ define([
|
|||
// Send the command
|
||||
sframeChan.query('Q_SET_PAD_METADATA', {
|
||||
channel: channel,
|
||||
channels: otherChan,
|
||||
command: 'ADD_ALLOWED',
|
||||
value: toAdd,
|
||||
teamId: teamOwner
|
||||
|
@ -987,6 +1006,15 @@ define([
|
|||
UI.findCancelButton().click();
|
||||
if (err || (obj && obj.error)) { UI.warn(Messages.error); }
|
||||
});
|
||||
|
||||
// If this is a form wiht a answer channel, delete it too
|
||||
var p = priv.propChannels;
|
||||
if (p.answersChannel) {
|
||||
sframeChan.query('Q_DELETE_OWNED', {
|
||||
teamId: typeof(owned) !== "boolean" ? owned : undefined,
|
||||
channel: p.answersChannel
|
||||
}, function () {});
|
||||
}
|
||||
});
|
||||
if (!opts.noEditPassword) { $d.append(h('br')); }
|
||||
$d.append(h('div', [
|
||||
|
@ -1020,7 +1048,7 @@ define([
|
|||
var owned = Modal.isOwned(Env, data);
|
||||
|
||||
// Request edit access
|
||||
if (common.isLoggedIn() && ((data.roHref && !data.href) || data.fakeHref) && !owned && !opts.calendar) {
|
||||
if (common.isLoggedIn() && ((data.roHref && !data.href) || data.fakeHref) && !owned && !opts.calendar && priv.app !== 'form') {
|
||||
var requestButton = h('button.btn.btn-secondary.no-margin.cp-access-margin-right',
|
||||
Messages.requestEdit_button);
|
||||
var requestBlock = h('p', requestButton);
|
||||
|
@ -1058,7 +1086,7 @@ define([
|
|||
var canMute = data.mailbox && owned === true && (
|
||||
(typeof (data.mailbox) === "string" && data.owners[0] === edPublic) ||
|
||||
data.mailbox[edPublic]);
|
||||
if (owned === true && !opts.calendar) {
|
||||
if (owned === true && !opts.calendar && priv.app !== 'form') {
|
||||
var cbox = UI.createCheckbox('cp-access-mute', Messages.access_muteRequests, !canMute);
|
||||
var $cbox = $(cbox);
|
||||
var spinner = UI.makeSpinner($cbox);
|
||||
|
|
|
@ -24,6 +24,7 @@ define([
|
|||
if (privateData.propChannels) {
|
||||
var p = privateData.propChannels;
|
||||
data.channel = data.channel || p.channel;
|
||||
data.answersChannel = data.answersChannel || p.answersChannel;
|
||||
data.rtChannel = data.rtChannel || p.rtChannel;
|
||||
data.lastVersion = data.lastVersion || p.lastVersion;
|
||||
data.lastCpHash = data.lastCpHash || p.lastCpHash;
|
||||
|
@ -75,6 +76,7 @@ define([
|
|||
var bytes = 0;
|
||||
var historyBytes;
|
||||
var chan = [data.channel];
|
||||
if (data.answersChannel) { chan.push(data.answersChannel); }
|
||||
if (data.rtChannel) { chan.push(data.rtChannel); }
|
||||
if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); }
|
||||
|
||||
|
|
|
@ -494,7 +494,23 @@ define([
|
|||
var parsed = Hash.parsePadUrl(pathname);
|
||||
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
|
||||
var versionHash = hashes.viewHash && opts.versionHash;
|
||||
var canBAR = parsed.type !== 'drive' && !versionHash;
|
||||
var isForm = parsed.type === "form"; // && opts.auditorHash;
|
||||
var canBAR = parsed.type !== 'drive' && !versionHash && !isForm;
|
||||
|
||||
var labelEdit = Messages.share_linkEdit;
|
||||
var labelView = Messages.share_linkView;
|
||||
|
||||
var auditor;
|
||||
if (isForm) {
|
||||
Messages.share_formEdit = "Author"; // XXX
|
||||
Messages.share_formView = "Participant"; // XXX
|
||||
Messages.share_formAuditor = "Auditor"; // XXX
|
||||
labelEdit = Messages.share_formEdit;
|
||||
labelView = Messages.share_formView;
|
||||
auditor = UI.createRadio('accessRights', 'cp-share-form', Messages.share_formAuditor, false, {
|
||||
mark: {tabindex:1},
|
||||
});
|
||||
}
|
||||
|
||||
var burnAfterReading = (hashes.viewHash && canBAR) ?
|
||||
UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
|
||||
|
@ -505,12 +521,13 @@ define([
|
|||
h('label', Messages.share_linkAccess),
|
||||
h('div.radio-group',[
|
||||
UI.createRadio('accessRights', 'cp-share-editable-false',
|
||||
Messages.share_linkView, true, { mark: {tabindex:1} }),
|
||||
labelView, 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
|
||||
labelEdit, false, { mark: {tabindex:1} }),
|
||||
auditor]),
|
||||
burnAfterReading,
|
||||
]);
|
||||
|
||||
// Burn after reading
|
||||
|
@ -553,6 +570,7 @@ define([
|
|||
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'));
|
||||
var formAuditor = Util.isChecked($rights.find('#cp-share-form'));
|
||||
if (versionHash) {
|
||||
edit = false;
|
||||
present = false;
|
||||
|
@ -569,6 +587,9 @@ define([
|
|||
}
|
||||
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash
|
||||
: hashes.viewHash;
|
||||
if (formAuditor && opts.auditorHash) {
|
||||
hash = opts.auditorHash;
|
||||
}
|
||||
var href = burnAfterReading ? opts.burnAfterReadingUrl
|
||||
: (origin + pathname + '#' + hash);
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
|
@ -594,6 +615,9 @@ define([
|
|||
$rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
|
||||
$rights.find('#cp-share-editable-true').attr('checked', true);
|
||||
}
|
||||
if (isForm && !opts.auditorHash) {
|
||||
$rights.find('#cp-share-form').removeAttr('checked').attr('disabled', true);
|
||||
}
|
||||
|
||||
var getLink = function () {
|
||||
return $rights.parent().find('#cp-share-link-preview');
|
||||
|
|
|
@ -112,6 +112,7 @@ define([
|
|||
Store.set = function (clientId, data, cb) {
|
||||
var s = getStore(data.teamId);
|
||||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
if (!s.proxy) { return void cb({ error: 'ENODRIVE' }); }
|
||||
var path = data.key.slice();
|
||||
var key = path.pop();
|
||||
var obj = Util.find(s.proxy, path);
|
||||
|
@ -629,6 +630,7 @@ define([
|
|||
if (!proxy.uid) {
|
||||
store.noDriveUid = store.noDriveUid || Hash.createChannelId();
|
||||
}
|
||||
|
||||
var metadata = {
|
||||
// "user" is shared with everybody via the userlist
|
||||
user: {
|
||||
|
@ -655,7 +657,7 @@ define([
|
|||
accountName: proxy.login_name || '',
|
||||
offline: store.proxy && store.offline,
|
||||
teams: teams,
|
||||
plan: account.plan
|
||||
plan: account.plan,
|
||||
}
|
||||
};
|
||||
cb(JSON.parse(JSON.stringify(metadata)));
|
||||
|
@ -2139,11 +2141,23 @@ define([
|
|||
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
|
||||
if (!data.command) { return void cb({ error: 'EINVAL' }); }
|
||||
var s = getStore(data.teamId);
|
||||
var otherChannels = data.channels;
|
||||
delete data.channels;
|
||||
s.rpc.setMetadata(data, function (err, res) {
|
||||
if (err) { return void cb({ error: err }); }
|
||||
if (!Array.isArray(res) || !res.length) { return void cb({}); }
|
||||
cb(res[0]);
|
||||
});
|
||||
// If we have other related channels, send the command for them too
|
||||
if (Array.isArray(otherChannels)) {
|
||||
otherChannels.forEach(function (chan) {
|
||||
var _d = Util.clone(data);
|
||||
_d.channel = chan;
|
||||
Store.setPadMetadata(clientId, _d, function () {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// GET_FULL_HISTORY from sframe-common-outer
|
||||
|
@ -2696,7 +2710,12 @@ define([
|
|||
|
||||
nThen(function (waitFor) {
|
||||
if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; }
|
||||
if (!proxy.forms) { proxy.forms = {}; }
|
||||
if (!proxy.friends_pending) { proxy.friends_pending = {}; }
|
||||
// Form seed is used to generate a box encryption keypair when
|
||||
// answering a form anonymously
|
||||
if (!proxy.form_seed) { proxy.form_seed = Hash.createChannelId(); }
|
||||
|
||||
|
||||
// Call onCacheReady if the manager is not yet defined
|
||||
if (!manager) {
|
||||
|
|
|
@ -818,6 +818,7 @@ define([
|
|||
_findChannels(Env, toUnpin).forEach(function (id) {
|
||||
var data = _getFileData(Env, id);
|
||||
var arr = [data.channel];
|
||||
if (data.answersChannel) { arr.push(data.answersChannel); }
|
||||
if (data.rtChannel) { arr.push(data.rtChannel); }
|
||||
if (data.lastVersion) { arr.push(Hash.hrefToHexChannelId(data.lastVersion)); }
|
||||
Array.prototype.push.apply(toKeep, arr);
|
||||
|
@ -1184,6 +1185,10 @@ define([
|
|||
result.push(otherChan);
|
||||
}
|
||||
}
|
||||
// Pin form answers channels
|
||||
if (data.answersChannel && result.indexOf(data.answersChannel) === -1) {
|
||||
result.push(data.answersChannel);
|
||||
}
|
||||
// Pin onlyoffice realtime patches
|
||||
if (data.rtChannel && result.indexOf(data.rtChannel) === -1) {
|
||||
result.push(data.rtChannel);
|
||||
|
|
|
@ -553,11 +553,13 @@ MessengerUI, Messages, Pages) {
|
|||
if (toolbar.isDeleted) {
|
||||
return void UI.warn(Messages.deletedFromServer);
|
||||
}
|
||||
var privateData = config.metadataMgr.getPrivateData();
|
||||
var title = (config.title && config.title.getTitle && config.title.getTitle())
|
||||
|| (config.title && config.title.defaultName)
|
||||
|| "";
|
||||
Common.getSframeChannel().event('EV_SHARE_OPEN', {
|
||||
title: title
|
||||
title: title,
|
||||
auditorHash: privateData.form_auditorHash
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||
@import (reference) '../../customize/src/less2/include/tools.less';
|
||||
@import (reference) '../../customize/src/less2/include/avatar.less';
|
||||
|
||||
&.cp-app-form {
|
||||
@form_input-width: 400px;
|
||||
|
||||
.framework_main(
|
||||
@bg-color: @colortheme_apps[form]
|
||||
);
|
||||
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
font: @colortheme_app-font;
|
||||
color: @cryptpad_text_col;
|
||||
background-color: @cp_app-bg;
|
||||
|
||||
#cp-app-form-editor {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.cp-app-form-results {
|
||||
div.cp-form-creator-content, .cp-app-form-button-results {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
&:not(.cp-app-form-results) {
|
||||
div.cp-form-creator-results, .cp-app-form-button-creator {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
#cp-app-form-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
min-width: 300px;
|
||||
|
||||
.cp-form-input-block {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
div.cp-form-creator-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
min-width: 300px;
|
||||
//flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
|
||||
@media screen and (max-width: 1000px) {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
.cp-form-creator-control {
|
||||
width: 100% !important;
|
||||
.cp-form-creator-settings {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp-form-creator-settings {
|
||||
padding: 30px;
|
||||
& > div:not(:last-child) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
div.cp-form-filler-container {
|
||||
width: 300px;
|
||||
min-width: 0;
|
||||
flex: 0 300 300px;
|
||||
}
|
||||
div.cp-form-creator-control {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
width: 300px;
|
||||
.cp-form-creator-types {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
}
|
||||
div.cp-form-creator-content, div.cp-form-creator-results {
|
||||
max-width: 1000px;
|
||||
min-width: 300px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1 1 1000px;
|
||||
overflow: auto;
|
||||
|
||||
.cp-form-creator-add-inline {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
button {
|
||||
width: 50px;
|
||||
i {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.cp-form-creator-inline-add {
|
||||
font-size: 25px;
|
||||
margin-right: 30px;
|
||||
.add-close { display: none; }
|
||||
&.displayed {
|
||||
.add-close { display: inline; }
|
||||
.add-open { display: none; }
|
||||
}
|
||||
}
|
||||
.cp-form-creator-control-inline {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
button:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.cp-form-creator-types {
|
||||
.btn-default {
|
||||
background: transparent;
|
||||
&:hover, &:not(:disabled):active, &:focus {
|
||||
background-color: @cp_buttons-default;
|
||||
}
|
||||
}
|
||||
button {
|
||||
border: 0px;
|
||||
//padding-bottom: 3px;
|
||||
i {
|
||||
font-size: 35px;
|
||||
line-height: 35px;
|
||||
}
|
||||
}
|
||||
&:first-child {
|
||||
margin-right: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-form-creator-add-full {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 50px 0px 100px 0px;
|
||||
&> div:first-child {
|
||||
border-right: 1px solid fade(@cryptpad_text_col, 25%);
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
padding-right: 10px;
|
||||
margin-right: 10px;
|
||||
i {
|
||||
color: fade(@cryptpad_text_col, 25%);
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
.cp-form-creator-control-inline {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: space-around;
|
||||
button:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.cp-form-creator-types {
|
||||
.btn-default {
|
||||
background: transparent;
|
||||
&:hover, &:not(:disabled):active, &:focus {
|
||||
background-color: @cp_buttons-default;
|
||||
}
|
||||
}
|
||||
button {
|
||||
border: 0px;
|
||||
padding:5px;
|
||||
margin-right: 10px;
|
||||
i {
|
||||
font-size: 35px;
|
||||
line-height: 35px;
|
||||
}
|
||||
}
|
||||
&:first-child {
|
||||
margin-bottom: 20px;
|
||||
margin-right: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp-form-page + .cp-form-send-container {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.cp-form-page-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 10px 0;
|
||||
& > span {
|
||||
margin: 0 20px;
|
||||
width: 100px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
button {
|
||||
&.cp-next {
|
||||
.fa {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-form-block {
|
||||
.tools_unselectable();
|
||||
background: @cp_form-bg1;
|
||||
padding: 10px;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cp-form-block-drag-handle {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
color: @cp_sidebar-hint;
|
||||
i {
|
||||
cursor: grab;
|
||||
&:first-child {
|
||||
height: 3px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.sortable-ghost { visibility: hidden; }
|
||||
&.sortable-drag { opacity: 0.9 !important; }
|
||||
|
||||
.cp-form-block-question {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.cp-form-block-content {
|
||||
overflow-x: auto;
|
||||
.cp-form-page-break-edit {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.cp-form-edit-buttons-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
input:invalid {
|
||||
border: 1px solid @cp_form-invalid;
|
||||
}
|
||||
}
|
||||
.cp-form-input-block {
|
||||
//width: @form_input-width;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid @cp_sidebar-hint;
|
||||
margin-bottom: 10px;
|
||||
&:not(.editing) {
|
||||
input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0 !important;
|
||||
& ~ button:not(:disabled) {
|
||||
.cp-form-edit { display: inline; }
|
||||
.cp-form-save { display: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
input {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
padding: 0 10px !important;
|
||||
height: auto;
|
||||
font-size: 20px;
|
||||
}
|
||||
button {
|
||||
.cp-form-edit {
|
||||
display: none;
|
||||
}
|
||||
.cp-form-save { display: inline; }
|
||||
}
|
||||
.cp-form-block-drag {
|
||||
font-size: 22px;
|
||||
width: 20px;
|
||||
margin-left: 5px;
|
||||
text-align: center;
|
||||
line-height: 31px;
|
||||
}
|
||||
}
|
||||
&.editable {
|
||||
cursor: grab;
|
||||
.cp-form-edit-save {
|
||||
margin-top: 20px;
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.cp-form-edit-type {
|
||||
margin-bottom: 10px;
|
||||
.cp-dropdown-container {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-form-edit-max-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
input {
|
||||
width: 100px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.cp-form-edit-options-block {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
.CodeMirror {
|
||||
cursor: default;
|
||||
flex: 1;
|
||||
margin: auto;
|
||||
min-width: 80%;
|
||||
width: 80%;
|
||||
min-height: 200px;
|
||||
height: 200px;
|
||||
border: 1px solid @cp_forms-border;
|
||||
.CodeMirror-placeholder {
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-form-edit-block {
|
||||
|
||||
button.btn-secondary {
|
||||
margin-left: 30px;
|
||||
}
|
||||
.cp-form-handle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
color: @cp_sidebar-hint;
|
||||
i:first-child {
|
||||
margin-right: 1px;
|
||||
}
|
||||
}
|
||||
.cp-form-edit-block-input {
|
||||
margin-bottom: 5px; // XXX DB margin bug
|
||||
&.sortable-ghost { visibility: hidden; }
|
||||
&.sortable-drag { opacity: 0.9 !important; }
|
||||
display: flex;
|
||||
width: 400px;
|
||||
input {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
border-color: @cryptpad_text_col;
|
||||
border-right: 0px;
|
||||
}
|
||||
button {
|
||||
i { margin: 0 !important; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div.cp-form-creator-results {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
position: relative;
|
||||
& > div {
|
||||
background: @cp_form-bg1;
|
||||
padding: 10px;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.cp-form-block-question {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.cp-form-block-type {
|
||||
float: right;
|
||||
padding: 5px;
|
||||
margin-top: -10px;
|
||||
margin-right: -10px;
|
||||
i { margin-right: 5px; }
|
||||
background: @cp_form-bg2;
|
||||
}
|
||||
.cp-form-results-type-text {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
.cp-form-results-type-text-data {
|
||||
padding: 5px 10px;
|
||||
background: @cp_form-bg2;
|
||||
&:not(:last-child) { margin-bottom: 1px; }
|
||||
}
|
||||
}
|
||||
.cp-form-results-type-textarea-data {
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
border: 1px solid @cp_profile-hint;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.cp-form-results-type-radio {
|
||||
display: table;
|
||||
.cp-form-results-type-multiradio-data {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
.cp-form-results-type-radio-data {
|
||||
display: table-row;
|
||||
border: 1px solid @cp_form-border;
|
||||
& > span {
|
||||
border: 1px solid @cp_form-border;
|
||||
display: table-cell;
|
||||
padding: 5px 10px;
|
||||
background: @cp_form-bg2;
|
||||
&.cp-value {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-form-individual {
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.cp-form-warning {
|
||||
color: @cp-limit-bar-warning;
|
||||
}
|
||||
.cp-form-friend {
|
||||
color: @cp_profile-hint;
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp-form-type-radio, .cp-form-type-checkbox {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: baseline;
|
||||
.cp-radio {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
.cp-form-type-multiradio {
|
||||
display: table;
|
||||
& > * {
|
||||
display: table-row;
|
||||
& > * {
|
||||
display: table-cell;
|
||||
padding: 5px 20px;
|
||||
vertical-align: middle;
|
||||
&:first-child {
|
||||
min-width: 200px;
|
||||
}
|
||||
.cp-radio-mark {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-form-type-sort {
|
||||
cursor: grab;
|
||||
padding: 2px;
|
||||
.cp-form-handle {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.cp-form-sort-order {
|
||||
border: 1px solid @cp_profile-hint;
|
||||
padding: 0 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.cp-form-type-poll {
|
||||
display: inline-flex;
|
||||
flex-flow: column;
|
||||
& > div {
|
||||
display: flex;
|
||||
}
|
||||
.cp-poll-cell {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&:first-child {
|
||||
width: 200px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.cp-poll-time-day {
|
||||
flex-basis: 100px;
|
||||
border-right: 1px solid @cryptpad_text_col;
|
||||
border-left: 1px solid @cryptpad_text_col;
|
||||
border-top: 1px solid @cryptpad_text_col;
|
||||
}
|
||||
&.cp-form-poll-switch {
|
||||
flex-flow: row;
|
||||
& > div {
|
||||
flex-flow: column;
|
||||
}
|
||||
.cp-poll-cell:not(.cp-poll-switch) {
|
||||
&:first-child {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
.cp-form-poll-option, .cp-poll-switch {
|
||||
width: 200px;
|
||||
}
|
||||
.cp-poll-time-day {
|
||||
flex-basis: 40px;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid @cryptpad_text_col;
|
||||
border-left: 1px solid @cryptpad_text_col;
|
||||
border-top: 1px solid @cryptpad_text_col;
|
||||
}
|
||||
}
|
||||
.cp-form-poll-choice, .cp-form-poll-answer {
|
||||
.fa {
|
||||
display: none;
|
||||
}
|
||||
color: @cp_form-poll-color;
|
||||
&[data-value="0"] {
|
||||
background: @cp_form-poll-no;
|
||||
.cp-no { display: inline; }
|
||||
}
|
||||
&[data-value="1"] {
|
||||
background: @cp_form-poll-yes;
|
||||
.cp-yes { display: inline; }
|
||||
}
|
||||
&[data-value="2"] {
|
||||
background: @cp_form-poll-maybe;
|
||||
.cp-maybe { display: inline; }
|
||||
}
|
||||
}
|
||||
div.cp-form-poll-choice {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
border: 5px double @cp_form-bg1;
|
||||
}
|
||||
div.cp-form-poll-answer {
|
||||
color: @cp_form-poll-yes-color;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CryptPad</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<link href="/customize/src/outer.css?ver=1.3.2" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<iframe-placeholder>
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="cp-app-noscroll">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<script async data-bootload="/form/inner.js" data-main="/common/sframe-boot.js?ver=1.7" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<style>
|
||||
.loading-hidden { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="cp-app-form">
|
||||
<div id="cp-toolbar" class="cp-toolbar-container"></div>
|
||||
<div id="cp-app-form-editor">
|
||||
<div id="cp-app-form-container"></div>
|
||||
</div>
|
||||
<noscript>
|
||||
<p><strong>OOPS</strong> In order to do encryption in your browser, Javascript is really <strong>really</strong> required.</p>
|
||||
<p><strong>OUPS</strong> Afin de pouvoir réaliser le chiffrement dans votre navigateur, Javascript est <strong>vraiment</strong> nécessaire.</p>
|
||||
</noscript>
|
||||
</body>
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,358 @@
|
|||
// Load #1, load as little as possible because we are in a race to get the loading screen up.
|
||||
define([
|
||||
'/bower_components/nthen/index.js',
|
||||
'/api/config',
|
||||
'/common/dom-ready.js',
|
||||
'/common/sframe-common-outer.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (nThen, ApiConfig, DomReady, SFCommonO) {
|
||||
var Nacl = window.nacl;
|
||||
|
||||
var href, hash;
|
||||
// Loaded in load #2
|
||||
nThen(function (waitFor) {
|
||||
DomReady.onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
var obj = SFCommonO.initIframe(waitFor, true);
|
||||
href = obj.href;
|
||||
hash = obj.hash;
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
var privateKey, publicKey;
|
||||
var channels = {};
|
||||
var getPropChannels = function () {
|
||||
return channels;
|
||||
};
|
||||
var addData = function (meta, CryptPad, user, Utils) {
|
||||
var keys = Utils.secret && Utils.secret.keys;
|
||||
|
||||
var parsed = Utils.Hash.parseTypeHash('pad', hash.slice(1));
|
||||
if (parsed && parsed.auditorKey) {
|
||||
meta.form_auditorKey = parsed.auditorKey;
|
||||
meta.form_auditorHash = hash;
|
||||
}
|
||||
|
||||
var secondary = keys && keys.secondaryKey;
|
||||
if (!secondary) { return; }
|
||||
var curvePair = Nacl.box.keyPair.fromSecretKey(Nacl.util.decodeUTF8(secondary).slice(0,32));
|
||||
var validateKey = keys.secondaryValidateKey;
|
||||
meta.form_answerValidateKey = validateKey;
|
||||
|
||||
publicKey = meta.form_public = Nacl.util.encodeBase64(curvePair.publicKey);
|
||||
privateKey = meta.form_private = Nacl.util.encodeBase64(curvePair.secretKey);
|
||||
|
||||
var auditorHash = Utils.Hash.getViewHashFromKeys({
|
||||
version: 1,
|
||||
channel: Utils.secret.channel,
|
||||
keys: { viewKeyStr: Nacl.util.encodeBase64(keys.cryptKey) }
|
||||
});
|
||||
var _parsed = Utils.Hash.parseTypeHash('pad', auditorHash);
|
||||
meta.form_auditorHash = _parsed.getHash({auditorKey: privateKey});
|
||||
};
|
||||
var addRpc = function (sframeChan, Cryptpad, Utils) {
|
||||
sframeChan.on('EV_FORM_PIN', function (data) {
|
||||
channels.answersChannel = data.channel;
|
||||
Cryptpad.getPadAttribute('answersChannel', function (err, res) {
|
||||
// If already stored, don't pin it again
|
||||
if (res && res === data.channel) { return; }
|
||||
Cryptpad.pinPads([data.channel], function () {
|
||||
Cryptpad.setPadAttribute('answersChannel', data.channel, function () {});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
var getAnonymousKeys = function (formSeed, channel) {
|
||||
var array = Nacl.util.decodeBase64(formSeed + channel);
|
||||
var hash = Nacl.hash(array);
|
||||
var secretKey = Nacl.util.encodeBase64(hash.subarray(32));
|
||||
var publicKey = Utils.Hash.getCurvePublicFromPrivate(secretKey);
|
||||
return {
|
||||
curvePrivate: secretKey,
|
||||
curvePublic: publicKey,
|
||||
};
|
||||
};
|
||||
var u8_slice = function (A, start, end) {
|
||||
return new Uint8Array(Array.prototype.slice.call(A, start, end));
|
||||
};
|
||||
var u8_concat = function (A) {
|
||||
var length = 0;
|
||||
A.forEach(function (a) { length += a.length; });
|
||||
var total = new Uint8Array(length);
|
||||
|
||||
var offset = 0;
|
||||
A.forEach(function (a) {
|
||||
total.set(a, offset);
|
||||
offset += a.length;
|
||||
});
|
||||
return total;
|
||||
};
|
||||
var anonProof = function (channel, theirPub, anonKeys) {
|
||||
var u8_plain = Nacl.util.decodeUTF8(channel);
|
||||
var u8_nonce = Nacl.randomBytes(Nacl.box.nonceLength);
|
||||
var u8_cipher = Nacl.box(
|
||||
u8_plain,
|
||||
u8_nonce,
|
||||
Nacl.util.decodeBase64(theirPub),
|
||||
Nacl.util.decodeBase64(anonKeys.curvePrivate)
|
||||
);
|
||||
var u8_bundle = u8_concat([
|
||||
u8_nonce, // 24 uint8s
|
||||
u8_cipher, // arbitrary length
|
||||
]);
|
||||
return {
|
||||
key: anonKeys.curvePublic,
|
||||
proof: Nacl.util.encodeBase64(u8_bundle)
|
||||
};
|
||||
};
|
||||
var checkAnonProof = function (proofObj, channel, curvePrivate) {
|
||||
var pub = proofObj.key;
|
||||
var proofTxt = proofObj.proof;
|
||||
try {
|
||||
var u8_bundle = Nacl.util.decodeBase64(proofTxt);
|
||||
var u8_nonce = u8_slice(u8_bundle, 0, Nacl.box.nonceLength);
|
||||
var u8_cipher = u8_slice(u8_bundle, Nacl.box.nonceLength);
|
||||
var u8_plain = Nacl.box.open(
|
||||
u8_cipher,
|
||||
u8_nonce,
|
||||
Nacl.util.decodeBase64(pub),
|
||||
Nacl.util.decodeBase64(curvePrivate)
|
||||
);
|
||||
return channel === Nacl.util.encodeUTF8(u8_plain);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
sframeChan.on('Q_FORM_FETCH_ANSWERS', function (data, _cb) {
|
||||
var cb = Utils.Util.once(_cb);
|
||||
var myKeys = {};
|
||||
var myFormKeys;
|
||||
var accessKeys;
|
||||
var CPNetflux, Pinpad;
|
||||
var network;
|
||||
var noDriveAnswered = false;
|
||||
nThen(function (w) {
|
||||
require([
|
||||
'/bower_components/chainpad-netflux/chainpad-netflux.js',
|
||||
'/common/pinpad.js',
|
||||
], w(function (_CPNetflux, _Pinpad) {
|
||||
CPNetflux = _CPNetflux;
|
||||
Pinpad = _Pinpad;
|
||||
}));
|
||||
Cryptpad.getAccessKeys(w(function (_keys) {
|
||||
if (!Array.isArray(_keys)) { return; }
|
||||
accessKeys = _keys;
|
||||
|
||||
_keys.some(function (_k) {
|
||||
if ((!Cryptpad.initialTeam && !_k.id) || Cryptpad.initialTeam === _k.id) {
|
||||
myKeys = _k;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}));
|
||||
Cryptpad.getFormKeys(w(function (keys) {
|
||||
if (!keys.curvePublic && !keys.formSeed) {
|
||||
// No drive mode
|
||||
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
|
||||
noDriveAnswered = answered.indexOf(data.channel) !== -1;
|
||||
}
|
||||
myFormKeys = keys;
|
||||
}));
|
||||
Cryptpad.makeNetwork(w(function (err, nw) {
|
||||
network = nw;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
if (!network) { return void cb({error: "E_CONNECT"}); }
|
||||
|
||||
if (myFormKeys.formSeed) {
|
||||
myFormKeys = getAnonymousKeys(myFormKeys.formSeed, data.channel);
|
||||
}
|
||||
|
||||
var keys = Utils.secret && Utils.secret.keys;
|
||||
|
||||
var curvePrivate = privateKey || data.privateKey;
|
||||
var crypto = Utils.Crypto.Mailbox.createEncryptor({
|
||||
curvePrivate: curvePrivate,
|
||||
curvePublic: publicKey || data.publicKey,
|
||||
validateKey: data.validateKey
|
||||
});
|
||||
var config = {
|
||||
network: network,
|
||||
channel: data.channel,
|
||||
noChainPad: true,
|
||||
validateKey: keys.secondaryValidateKey,
|
||||
owners: [myKeys.edPublic],
|
||||
crypto: crypto,
|
||||
// XXX Cache
|
||||
};
|
||||
var results = {};
|
||||
config.onError = function (info) {
|
||||
cb({ error: info.type });
|
||||
};
|
||||
config.onRejected = function (data, cb) {
|
||||
if (!Array.isArray(data) || !data.length || data[0].length !== 16) {
|
||||
return void cb(true);
|
||||
}
|
||||
if (!Array.isArray(accessKeys)) { return void cb(true); }
|
||||
network.historyKeeper = data[0];
|
||||
nThen(function (waitFor) {
|
||||
accessKeys.forEach(function (obj) {
|
||||
Pinpad.create(network, obj, waitFor(function (e) {
|
||||
console.log('done', obj);
|
||||
if (e) { console.error(e); }
|
||||
}));
|
||||
});
|
||||
}).nThen(function () {
|
||||
cb();
|
||||
});
|
||||
};
|
||||
config.onReady = function () {
|
||||
var myKey;
|
||||
// If we have submitted an anonymous answer, retrieve it
|
||||
if (myFormKeys.curvePublic && results[myFormKeys.curvePublic]) {
|
||||
myKey = myFormKeys.curvePublic;
|
||||
}
|
||||
cb({
|
||||
noDriveAnswered: noDriveAnswered,
|
||||
myKey: myKey,
|
||||
results: results
|
||||
});
|
||||
network.disconnect();
|
||||
};
|
||||
config.onMessage = function (msg, peer, vKey, isCp, hash, senderCurve, cfg) {
|
||||
var parsed = Utils.Util.tryParse(msg);
|
||||
if (!parsed) { return; }
|
||||
if (parsed._proof) {
|
||||
var check = checkAnonProof(parsed._proof, data.channel, curvePrivate);
|
||||
if (check) {
|
||||
delete results[parsed._proof.key];
|
||||
}
|
||||
}
|
||||
results[senderCurve] = {
|
||||
msg: parsed,
|
||||
hash: hash,
|
||||
time: cfg.time
|
||||
};
|
||||
};
|
||||
CPNetflux.start(config);
|
||||
});
|
||||
});
|
||||
sframeChan.on("Q_FETCH_MY_ANSWERS", function (data, cb) {
|
||||
var answer;
|
||||
var myKeys;
|
||||
nThen(function (w) {
|
||||
Cryptpad.getFormKeys(w(function (keys) {
|
||||
myKeys = keys;
|
||||
}));
|
||||
Cryptpad.getFormAnswer({channel: data.channel}, w(function (obj) {
|
||||
if (!obj || obj.error) {
|
||||
if (obj && obj.error === "ENODRIVE") {
|
||||
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
|
||||
if (answered.indexOf(data.channel) !== -1) {
|
||||
cb({error:'EANSWERED'});
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
return void w.abort();
|
||||
}
|
||||
w.abort();
|
||||
return void cb(obj);
|
||||
}
|
||||
answer = obj;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
if (answer.anonymous) {
|
||||
if (!myKeys.formSeed) { return void cb({ error: "ANONYMOUS_ERROR" }); }
|
||||
myKeys = getAnonymousKeys(myKeys.formSeed, data.channel);
|
||||
}
|
||||
Cryptpad.getHistoryRange({
|
||||
channel: data.channel,
|
||||
lastKnownHash: answer.hash,
|
||||
toHash: answer.hash,
|
||||
}, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj); }
|
||||
var messages = obj.messages;
|
||||
var res = Utils.Crypto.Mailbox.openOwnSecretLetter(messages[0].msg, {
|
||||
validateKey: data.validateKey,
|
||||
ephemeral_private: Nacl.util.decodeBase64(answer.curvePrivate),
|
||||
my_private: Nacl.util.decodeBase64(myKeys.curvePrivate),
|
||||
their_public: Nacl.util.decodeBase64(data.publicKey)
|
||||
});
|
||||
res.content._isAnon = answer.anonymous;
|
||||
cb(JSON.parse(res.content));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
var noDriveSeed = Utils.Hash.createChannelId();
|
||||
sframeChan.on("Q_FORM_SUBMIT", function (data, cb) {
|
||||
var box = data.mailbox;
|
||||
var myKeys;
|
||||
nThen(function (w) {
|
||||
Cryptpad.getFormKeys(w(function (keys) {
|
||||
// If formSeed doesn't exists, it means we're probably in noDrive mode.
|
||||
// We can create a seed in localStorage.
|
||||
if (!keys.formSeed) {
|
||||
// No drive mode
|
||||
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
|
||||
if(answered.indexOf(data.channel) !== -1) {
|
||||
// Already answered: abort
|
||||
return void cb({ error: "EANSWERED" });
|
||||
}
|
||||
keys = { formSeed: noDriveSeed };
|
||||
}
|
||||
myKeys = keys;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
var myAnonymousKeys;
|
||||
if (data.anonymous) {
|
||||
if (!myKeys.formSeed) { return void cb({ error: "ANONYMOUS_ERROR" }); }
|
||||
myKeys = getAnonymousKeys(myKeys.formSeed, box.channel);
|
||||
} else {
|
||||
myAnonymousKeys = getAnonymousKeys(myKeys.formSeed, box.channel);
|
||||
}
|
||||
var keys = Utils.secret && Utils.secret.keys;
|
||||
myKeys.signingKey = keys.secondarySignKey;
|
||||
|
||||
var ephemeral_keypair = Nacl.box.keyPair();
|
||||
var ephemeral_private = Nacl.util.encodeBase64(ephemeral_keypair.secretKey);
|
||||
myKeys.ephemeral_keypair = ephemeral_keypair;
|
||||
|
||||
if (myAnonymousKeys) {
|
||||
var proof = anonProof(box.channel, box.publicKey, myAnonymousKeys);
|
||||
data.results._proof = proof;
|
||||
}
|
||||
|
||||
var crypto = Utils.Crypto.Mailbox.createEncryptor(myKeys);
|
||||
var text = JSON.stringify(data.results);
|
||||
var ciphertext = crypto.encrypt(text, box.publicKey);
|
||||
|
||||
var hash = ciphertext.slice(0,64);
|
||||
Cryptpad.anonRpcMsg("WRITE_PRIVATE_MESSAGE", [
|
||||
box.channel,
|
||||
ciphertext
|
||||
], function (err, response) {
|
||||
Cryptpad.storeFormAnswer({
|
||||
channel: box.channel,
|
||||
hash: hash,
|
||||
curvePrivate: ephemeral_private,
|
||||
anonymous: Boolean(data.anonymous)
|
||||
});
|
||||
cb({error: err, response: response, hash: hash});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
SFCommonO.start({
|
||||
addData: addData,
|
||||
addRpc: addRpc,
|
||||
cache: true,
|
||||
noDrive: true,
|
||||
hash: hash,
|
||||
href: href,
|
||||
useCreationScreen: true,
|
||||
messaging: true,
|
||||
getPropChannels: getPropChannels
|
||||
});
|
||||
});
|
||||
});
|
|
@ -241,7 +241,12 @@ define([
|
|||
e.stopPropagation();
|
||||
});
|
||||
var common = framework._.sfCommon;
|
||||
var markdownTb = common.createMarkdownToolbar(editor);
|
||||
var markdownTb = common.createMarkdownToolbar(editor, {
|
||||
embed: function (mt) {
|
||||
editor.focus();
|
||||
editor.replaceSelection($(mt)[0].outerHTML);
|
||||
}
|
||||
});
|
||||
$(text).before(markdownTb.toolbar);
|
||||
$(markdownTb.toolbar).show();
|
||||
editor.refresh();
|
||||
|
|
|
@ -58,6 +58,7 @@ define([
|
|||
hashes: data.hashes || priv.hashes,
|
||||
common: common,
|
||||
title: data.title,
|
||||
auditorHash: data.auditorHash,
|
||||
versionHash: data.versionHash,
|
||||
friends: friends,
|
||||
onClose: function () {
|
||||
|
|
Loading…
Reference in New Issue