View responses

This commit is contained in:
yflory 2021-05-21 13:39:33 +02:00
parent 3b30cfcc55
commit 07c90b6a94
6 changed files with 218 additions and 25 deletions

View File

@ -427,3 +427,7 @@
@cp_calendar-now: @cryptpad_color_brand_300;
@cp_calendar-now-fg: @cryptpad_color_grey_800;
// Forms
@cp_forms-bg1: @cryptpad_color_grey_800;
@cp_forms-bg2: @cryptpad_color_grey_900;
@cp_forms-border: @cryptpad_color_grey_800;

View File

@ -426,3 +426,8 @@
@cp_calendar-border: @cryptpad_color_grey_300;
@cp_calendar-now: @cryptpad_color_brand;
@cp_calendar-now-fg: @cryptpad_color_grey_200;
// Forms
@cp_forms-bg1: @cryptpad_color_grey_200;
@cp_forms-bg2: @cryptpad_color_grey_100;
@cp_forms-border: @cryptpad_color_grey_200;

View File

@ -69,7 +69,7 @@ define([
}, cb);
};
common.getAccessKeys = function (cb) {
common.getAccessKeys = function (cb, opts) {
var keys = [];
Nthen(function (waitFor) {
// Push account keys
@ -84,6 +84,7 @@ define([
});
} catch (e) { console.error(e); }
}));
// Push teams keys
postMessage("GET", {
key: ['teams'],
@ -92,6 +93,7 @@ define([
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);
});

View File

@ -20,6 +20,17 @@
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;
@ -34,8 +45,13 @@
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-content, div.cp-form-creator-results {
padding: 10px;
display: flex;
flex-flow: column;
@ -86,6 +102,54 @@
}
}
}
div.cp-form-creator-results {
display: flex;
flex-flow: column;
position: relative;
& > div {
background: @cp_forms-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_forms-bg2;
}
.cp-form-results-type-text {
max-height: 300px;
overflow: auto;
.cp-form-results-type-text-data {
padding: 5px 10px;
background: @cp_forms-bg2;
&:not(:last-child) { margin-bottom: 1px; }
}
}
.cp-form-results-type-radio {
display: table;
.cp-form-results-type-radio-data {
display: table-row;
border: 1px solid @cp_forms-border;
& > span {
border: 1px solid @cp_forms-border;
display: table-cell;
padding: 5px 10px;
background: @cp_forms-bg2;
&.cp-value {
min-width: 200px;
}
}
}
}
}
}
}

View File

@ -59,21 +59,17 @@ define([
Messages.form_duplicates = "Duplicate entries have been removed";
Messages.form_submit = "Submit";
Messages.form_update = "Update";
Messages.form_reset = "Reset";
Messages.form_sent = "Sent";
Messages.form_cantFindAnswers = "Unable to retrieve your existing answers for this form.";
// XXX to update our own answers, we need to store the server hash of the message
// and we'll be able to use getHistoryRange to fetch this message when we come back
Messages.form_viewResults = "Go to responses";
Messages.form_viewCreator = "Go to form creator";
var makeFormSettings = function (framework) {
// XXX
// Button to set results as public
// Checkbox to allow anonymous answers
// Button to clear all answers?
};
Messages.form_notAnswered = "And <b>{0}</b> empty answers";
var editOptions = function (v, cb) {
var add = h('button.btn.btn-secondary', [
@ -128,6 +124,12 @@ define([
];
};
var getEmpty = function (empty) {
if (empty) {
return UI.setHTML(h('div.cp-form-results-type-text-empty'), Messages._getKey('form_notAnswered', [empty]));
}
};
var TYPES = {
input: {
get: function () {
@ -140,6 +142,19 @@ define([
reset: function () { $tag.val(''); }
};
},
printResults: function (answers, uid) {
var results = [];
var empty = 0;
Object.keys(answers).forEach(function (author) {
var obj = answers[author];
var answer = obj.msg[uid];
if (!answer || !answer.trim()) { return empty++; }
results.push(h('div.cp-form-results-type-text-data', answer));
});
results.push(getEmpty(empty));
return h('div.cp-form-results-type-text', results);
},
icon: h('i.fa.fa-font')
},
radio: {
@ -185,12 +200,33 @@ define([
};
},
printResults: function (answers, uid) {
var results = [];
var empty = 0;
var count = {};
Object.keys(answers).forEach(function (author) {
var obj = answers[author];
var answer = obj.msg[uid];
if (!answer || !answer.trim()) { return empty++; }
count[answer] = count[answer] || 0;
count[answer]++;
});
Object.keys(count).forEach(function (value) {
results.push(h('div.cp-form-results-type-radio-data', [
h('span.cp-value', value),
h('span.cp-count', count[value])
]));
});
results.push(getEmpty(empty));
return h('div.cp-form-results-type-radio', results);
},
icon: h('i.fa.fa-list-ul')
}
};
var makeFormControls = function (framework, content) {
var send = h('button.btn.btn-primary', Messages.poll_commit);
var makeFormControls = function (framework, content, update) {
var send = h('button.btn.btn-primary', update ? Messages.form_update : Messages.form_submit);
var reset = h('button.btn.btn-danger-alt', Messages.form_reset);
$(reset).click(function () {
if (!Array.isArray(APP.formBlocks)) { return; }
@ -211,12 +247,13 @@ define([
mailbox: content.answers,
results: results
}, function (err, data) {
console.error(data);
$send.attr('disabled', 'disabled');
if (err || (data && data.error)) {
console.error(err || data.error);
return void UI.warn(Messages.error);
}
UI.alert(Messages.form_sent);
$send.text(Messages.form_update);
});
});
return h('div.cp-form-send-container', [send, reset]);
@ -321,7 +358,30 @@ define([
});
$container.empty().append(elements);
$container.append(makeFormControls(framework, content));
$container.append(makeFormControls(framework, content, Boolean(answers)));
};
var renderResults = function (content, answers) {
var $container = $('div.cp-form-creator-results').empty();
var form = content.form;
var elements = Object.keys(form).map(function (uid) {
var block = form[uid];
var type = block.type;
var model = TYPES[type];
if (!model || !model.printResults) { return; }
var print = model.printResults(answers, uid);
var q = h('div.cp-form-block-question', block.q || Messages.form_default);
return h('div.cp-form-block', [
h('div.cp-form-block-type', [
TYPES[type].icon.cloneNode(),
h('span', Messages['form_type_'+type])
]),
q,
h('div.cp-form-block-content', print),
]);
});
$container.append(elements);
};
var andThen = function (framework) {
@ -333,6 +393,39 @@ define([
var priv = metadataMgr.getPrivateData();
APP.isEditor = Boolean(priv.form_public);
var $body = $('body');
var makeFormSettings = function () {
var viewResults = h('button.btn.btn-primary', [
h('span.cp-app-form-button-results', Messages.form_viewResults),
h('span.cp-app-form-button-creator', Messages.form_viewCreator),
]);
var $v = $(viewResults).click(function () {
if ($body.hasClass('cp-app-form-results')) {
$body.removeClass('cp-app-form-results');
return;
}
$v.attr('disabled', 'disabled');
sframeChan.query("Q_FORM_FETCH_ANSWERS", {
channel: content.answers.channel,
validateKey: content.answers.validateKey,
publicKey: content.answers.publicKey
}, function (err, answers) {
$v.removeAttr('disabled');
$body.addClass('cp-app-form-results');
renderResults(content, answers);
});
});
return [
viewResults
];
// XXX
// Button to set results as public
// Checkbox to allow anonymous answers
// Button to clear all answers?
};
var makeFormCreator = function () {
@ -356,13 +449,21 @@ define([
});
return btn;
});
controlContainer = h('div.cp-form-creator-control', controls);
var settings = makeFormSettings();
controlContainer = h('div.cp-form-creator-control', [
h('div.cp-form-creator-settings', settings),
h('div.cp-form-creator-types', controls)
]);
}
var contentContainer = h('div.cp-form-creator-content');
var resultsContainer = h('div.cp-form-creator-results');
var div = h('div.cp-form-creator-container', [
controlContainer,
contentContainer,
resultsContainer
]);
return div;
};
@ -397,8 +498,10 @@ define([
if (APP.isEditor) {
sframeChan.query("Q_FORM_FETCH_ANSWERS", {
channel: content.answers.channel,
validateKey: content.answers.validateKey,
publicKey: content.answers.publicKey
}, function () {
}, function (err, obj) {
if (obj) { APP.answers = obj; }
updateForm(framework, content, true);
});

View File

@ -30,7 +30,7 @@ define([
};
var addRpc = function (sframeChan, Cryptpad, Utils) {
sframeChan.on('Q_FORM_FETCH_ANSWERS', function (data, cb) {
var keys;
var myKeys;
var CPNetflux;
var network;
nThen(function (w) {
@ -40,7 +40,15 @@ define([
CPNetflux = _CPNetflux;
}));
Cryptpad.getAccessKeys(w(function (_keys) {
keys = _keys;
if (!Array.isArray(_keys)) { return; }
_keys.some(function (_k) {
if ((!Cryptpad.initialTeam && !_k.id) || Cryptpad.initialTeam === _k.id) {
myKeys = _k;
return true;
}
});
console.error(myKeys);
}));
Cryptpad.makeNetwork(w(function (err, nw) {
network = nw;
@ -52,24 +60,31 @@ define([
var crypto = Utils.Crypto.Mailbox.createEncryptor({
curvePrivate: privateKey,
curvePublic: publicKey || data.publicKey
curvePublic: publicKey || data.publicKey,
validateKey: data.validateKey
});
var config = {
network: network,
channel: data.channel,
noChainPad: true,
validateKey: keys.secondaryValidateKey,
owners: [], // XXX add pad owner
owners: [myKeys.edPublic], // XXX add pad owner
crypto: crypto,
// XXX Cache
};
var results = {};
config.onReady = function () {
cb();
// XXX
cb(results);
network.disconnect();
};
config.onMessage = function () {
// XXX
config.onMessage = function (msg, peer, vKey, isCp, hash, senderCurve, cfg) {
var parsed = Utils.Util.tryParse(msg);
if (!parsed) { return; }
results[senderCurve] = {
msg: parsed,
hash: hash,
time: cfg.time
};
};
CPNetflux.start(config);
});