Fix forms issues and allow answers in no drive mode

This commit is contained in:
yflory 2021-06-02 16:49:18 +02:00
parent 88f834fbb7
commit 4cc84b8c80
5 changed files with 105 additions and 44 deletions

View file

@ -141,7 +141,15 @@ define([
anonymous: data.anonymous
}
}, function (obj) {
if (obj && obj.error) { console.error(obj.error); }
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);
}
});
};

View file

@ -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);

View file

@ -64,6 +64,10 @@
flex: 1;
overflow: auto;
.cp-form-page + .cp-form-send-container {
margin-top: 10px;
}
.cp-form-page-container {
display: flex;
justify-content: center;

View file

@ -108,6 +108,7 @@ define([
Messages.form_delete = "Delete block";
Messages.form_cantFindAnswers = "Unable to retrieve your existing answers for this form.";
Messages.form_answered = "You already answered this form";
Messages.form_viewResults = "Go to responses";
Messages.form_viewCreator = "Go to form creator";
@ -1274,7 +1275,7 @@ define([
var form = content.form;
var switchMode = h('button.btn.btn-primary', Messages.form_showIndividual);
$controls.append(switchMode);
$controls.hide().append(switchMode);
var show = function (answers, header) {
var elements = content.order.map(function (uid) {
@ -1299,6 +1300,8 @@ define([
};
show(answers);
if (APP.isEditor || APP.isAuditor) { $controls.show(); }
var $s = $(switchMode).click(function () {
$results.empty();
if (!summary) {
@ -1436,6 +1439,9 @@ define([
}, function (err, data) {
$send.attr('disabled', 'disabled');
if (err || (data && data.error)) {
if (data.error === "EANSWERED") {
return void UI.warn(Messages.form_answered);
}
console.error(err || data.error);
return void UI.warn(Messages.error);
}
@ -1663,34 +1669,36 @@ define([
});
_content.push(div);
var pageContainer = h('div.cp-form-page-container');
var $page = $(pageContainer);
_content.push(pageContainer);
var refreshPage = function (current) {
$page.empty();
if (!current || current < 1) { current = 1; }
if (current > pages) { current = pages; }
var left = h('button.btn.btn-secondary.small.cp-prev', [
h('i.fa.fa-chevron-left'),
h('span', Messages.form_page_prev)
]);
var state = h('span', Messages._getKey('form_page', [current, pages]));
var right = h('button.btn.btn-secondary.small.cp-next', [
h('span', Messages.form_page_next),
h('i.fa.fa-chevron-right'),
]);
$(left).click(function () { refreshPage(current - 1); });
$(right).click(function () { refreshPage(current + 1); });
$page.append([left, state, right]);
$container.find('.cp-form-page').hide();
$($container.find('.cp-form-page').get(current-1)).show();
if (current !== pages) {
$container.find('.cp-form-send-container').hide();
} else {
$container.find('.cp-form-send-container').show();
}
};
setTimeout(refreshPage);
if (pages > 1) {
var pageContainer = h('div.cp-form-page-container');
var $page = $(pageContainer);
_content.push(pageContainer);
var refreshPage = function (current) {
$page.empty();
if (!current || current < 1) { current = 1; }
if (current > pages) { current = pages; }
var left = h('button.btn.btn-secondary.small.cp-prev', [
h('i.fa.fa-chevron-left'),
h('span', Messages.form_page_prev)
]);
var state = h('span', Messages._getKey('form_page', [current, pages]));
var right = h('button.btn.btn-secondary.small.cp-next', [
h('span', Messages.form_page_next),
h('i.fa.fa-chevron-right'),
]);
$(left).click(function () { refreshPage(current - 1); });
$(right).click(function () { refreshPage(current + 1); });
$page.append([left, state, right]);
$container.find('.cp-form-page').hide();
$($container.find('.cp-form-page').get(current-1)).show();
if (current !== pages) {
$container.find('.cp-form-send-container').hide();
} else {
$container.find('.cp-form-send-container').show();
}
};
setTimeout(refreshPage);
}
}
$container.empty().append(_content);
@ -1745,7 +1753,6 @@ define([
$toolbarContainer.after(helpMenu.menu);
// XXX refresh form settings on remote change
var makeFormSettings = function () {
// Private / public status
var resultsType = h('div.cp-form-results-type-container');
@ -1761,6 +1768,7 @@ define([
UI.confirm(Messages.form_makePublicWarning, function (yes) {
if (!yes) { return; }
$makePublic.attr('disabled', 'disabled');
var priv = metadataMgr.getPrivateData();
content.answers.privateKey = priv.form_private;
framework.localChange();
framework._.cpNfInner.chainpad.onSettle(function () {
@ -1889,11 +1897,6 @@ define([
resultsType,
viewResults,
];
// XXX
// Button to set results as public
// Checkbox to allow anonymous answers
// Button to clear all answers?
};
var checkIntegrity = function (getter) {
@ -2025,18 +2028,22 @@ define([
// * viewers ==> check if you've already answered and show form (new or edit)
// * editors ==> show schema and warn users if existing questions already have answers
if (priv.form_auditorKey) {
var getResults = function (key) {
sframeChan.query("Q_FORM_FETCH_ANSWERS", {
channel: content.answers.channel,
validateKey: content.answers.validateKey,
publicKey: content.answers.publicKey,
privateKey: priv.form_auditorKey
privateKey: key
}, function (err, obj) {
var answers = obj && obj.results;
if (answers) { APP.answers = answers; }
$body.addClass('cp-app-form-results');
renderResults(content, answers);
});
};
if (priv.form_auditorKey) {
APP.isAuditor = true;
getResults(priv.form_auditorKey);
return;
}
@ -2073,6 +2080,17 @@ define([
}, function (err, obj) {
var answers = obj && obj.results;
if (answers) { APP.answers = answers; }
if (obj && obj.noDriveAnswered) {
// No drive mode already answered: can't answer again
if (answers) {
$body.addClass('cp-app-form-results');
renderResults(content, answers);
} else {
return void UI.errorLoadingScreen(Messages.form_answered);
}
return;
}
checkIntegrity(false);
var myAnswers;
var curve1 = user.curvePublic;
@ -2096,6 +2114,14 @@ define([
publicKey: content.answers.publicKey
}, function (err, obj) {
if (obj && obj.error) {
if (obj.error === "EANSWERED") {
// No drive mode already answered: can't answer again
if (content.answers.privateKey) {
return void getResults(content.answers.privateKey);
}
// Here, we know results are private so we can use an error screen
return void UI.errorLoadingScreen(Messages.form_answered);
}
UI.warn(Messages.form_cantFindAnswers);
}
var answers;

View file

@ -47,7 +47,6 @@ define([
});
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) {
@ -127,6 +126,7 @@ define([
var accessKeys;
var CPNetflux, Pinpad;
var network;
var noDriveAnswered = false;
nThen(function (w) {
require([
'/bower_components/chainpad-netflux/chainpad-netflux.js',
@ -147,6 +147,11 @@ define([
});
}));
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) {
@ -204,6 +209,7 @@ define([
myKey = myFormKeys.curvePublic;
}
cb({
noDriveAnswered: noDriveAnswered,
myKey: myKey,
results: results
});
@ -236,6 +242,15 @@ define([
}));
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);
}
@ -266,19 +281,26 @@ define([
});
});
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 () {
// XXX if we are a registered user (myKeys.curvePrivate exists), we may
// have already answered anonymously. We should send a "proof" to show
// that the existing anonymous answer are ours (using myKeys.formSeed).
// Even if we never answered anonymously, the keyPair would be unique to
// the current channel so it wouldn't leak anything.
var myAnonymousKeys;
if (data.anonymous) {
if (!myKeys.formSeed) { return void cb({ error: "ANONYMOUS_ERROR" }); }