Allow edit/delete/multiple answers without a drive and fix race condition

This commit is contained in:
yflory 2022-12-08 16:53:29 +01:00
parent f0bc1ef07a
commit 600771682a
7 changed files with 73 additions and 33 deletions

View File

@ -210,7 +210,7 @@ Channel.trimHistory = function (Env, safeKey, data, cb) {
// Delete a signed mailbox message. This is used when users want
// to delete their form reponses.
Channel.deleteMailboxMessage = function (Env, safeKey, data, cb) {
Channel.deleteMailboxMessage = function (Env, data, cb) {
const channelId = data.channel;
const hash = data.hash;
const proof = data.proof;
@ -355,10 +355,11 @@ Channel.writePrivateMessage = function (Env, args, _cb, Server, netfluxId) {
Server.getChannelUserList(channelId).forEach(function (userId) {
Server.send(userId, fullMessage);
});
cb();
});
cb();
});
};

View File

@ -20,6 +20,7 @@ const UNAUTHENTICATED_CALLS = {
IS_CHANNEL_PINNED: Pinning.isChannelPinned, // FIXME drop this RPC
IS_NEW_CHANNEL: Channel.isNewChannel,
WRITE_PRIVATE_MESSAGE: Channel.writePrivateMessage,
DELETE_MAILBOX_MESSAGE: Channel.deleteMailboxMessage,
GET_METADATA: Metadata.getMetadata,
};
@ -47,7 +48,6 @@ const AUTHENTICATED_USER_TARGETED = {
CLEAR_OWNED_CHANNEL: Channel.clearOwnedChannel,
REMOVE_OWNED_CHANNEL: Channel.removeOwnedChannel,
TRIM_HISTORY: Channel.trimHistory,
DELETE_MAILBOX_MESSAGE: Channel.deleteMailboxMessage,
UPLOAD_STATUS: Upload.status,
UPLOAD: Upload.upload,
UPLOAD_COMPLETE: Upload.complete,

View File

@ -125,6 +125,12 @@ define([
formSeed = obj;
}));
}).nThen(function () {
if (!formSeed) { // no drive mode
formSeed = localStorage.CP_formSeed || Hash.createChannelId();
localStorage.CP_formSeed = formSeed;
} else {
delete localStorage.CP_formSeed;
}
cb({
curvePrivate: curvePrivate,
curvePublic: curvePrivate && Hash.getCurvePublicFromPrivate(curvePrivate),
@ -132,16 +138,39 @@ define([
});
});
};
common.getFormAnswer = function (data, onlyLast, cb) {
common.getFormAnswer = function (data, cb) {
postMessage("GET", {
key: ['forms', data.channel],
}, function (obj) {
if (!obj || obj.error) { return void cb(obj); }
if (!Array.isArray(obj)) { obj = [obj]; }
if (obj && obj.error === "ENODRIVE") {
var all = Util.tryParse(localStorage.CP_formAnswers || "{}");
return void cb(all[data.channel]);
}
if (obj && obj.error) { return void cb(obj); }
var last = obj[obj.length - 1];
if (onlyLast) { return void cb(last); }
return void cb(obj);
if (obj) {
if (!Array.isArray(obj)) { obj = [obj]; }
return void cb(obj);
}
// We have a drive and no answer but maybe we had
// previous "nodrive" answers: migrate
var old = Util.tryParse(localStorage.CP_formAnswers || "{}");
if (Array.isArray(old[data.channel])) {
var d = old[data.channel];
return void postMessage("SET", {
key: ['forms', data.channel],
value: d
}, function (obj) {
// Delete old data if it was correctly stored in the drive
if (obj && obj.error) { return void cb(d); }
delete old[data.channel];
localStorage.CP_formAnswers = JSON.stringify(old);
cb(d);
});
}
cb();
});
};
common.storeFormAnswer = function (data, cb) {
@ -153,7 +182,7 @@ define([
};
var answers = [];
Nthen(function (waitFor) {
common.getFormAnswer(data, false, waitFor(function (obj) {
common.getFormAnswer(data, waitFor(function (obj) {
if (!obj || obj.error) { return; }
answers = obj;
}));
@ -165,9 +194,15 @@ define([
}, function (obj) {
if (obj && obj.error) {
if (obj.error === "ENODRIVE") {
var all = Util.tryParse(localStorage.CP_formAnswers || "{}");
all[data.channel] = answers;
localStorage.CP_formAnswers = JSON.stringify(all);
/*
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
if (answered.indexOf(data.channel) === -1) { answered.push(data.channel); }
localStorage.CP_formAnswered = JSON.stringify(answered);
*/
return void cb();
}
console.error(obj.error);
@ -178,7 +213,7 @@ define([
};
common.deleteFormAnswers = function (data, _cb) {
var cb = Util.once(_cb);
common.getFormAnswer(data, false, function (obj) {
common.getFormAnswer(data, function (obj) {
if (!obj || obj.error) { return void cb(); }
if (!obj.length) { return void cb(); }
var n = Nthen;
@ -224,7 +259,16 @@ define([
postMessage("SET", {
key: ['forms', data.channel],
value: obj
}, cb);
}, function (_obj) {
if (_obj && _obj.error === "ENODRIVE") {
var all = Util.tryParse(localStorage.CP_formAnswers || "{}");
if (obj) { all[data.channel] = obj; }
else { delete all[data.channel]; }
localStorage.CP_formAnswers = JSON.stringify(all);
return void cb();
}
return void cb(_obj);
});
});
});
};
@ -2480,6 +2524,7 @@ define([
anonHash: LocalStore.getFSHash(),
localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), // TODO move this to LocalStore ?
language: common.getLanguage(),
form_seed: localStorage.CP_formSeed,
cache: rdyCfg.cache,
noDrive: rdyCfg.noDrive,
disableCache: localStorage['CRYPTPAD_STORE|disableCache'],

View File

@ -2162,8 +2162,8 @@ define([
};
Store.deleteMailboxMessage = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.deleteMailboxMessage(data, function (e) {
if (!store.anon_rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.anon_rpc.send('DELETE_MAILBOX_MESSAGE', data, function (e) {
cb({error:e});
});
};
@ -2962,6 +2962,9 @@ define([
if (!rt.proxy.uid && store.noDriveUid) {
rt.proxy.uid = store.noDriveUid;
}
if (!rt.proxy.form_seed && data.form_seed) {
rt.proxy.form_seed = data.form_seed;
}
/*
// deprecating localStorage migration as of 4.2.0
var drive = rt.proxy.drive;

View File

@ -249,16 +249,6 @@ var factory = function (Util, Rpc) {
}, cb);
};
exp.deleteMailboxMessage = function (obj, cb) {
rpc.send('DELETE_MAILBOX_MESSAGE', {
channel: obj.channel,
hash: obj.hash,
proof: obj.proof
}, cb);
};
cb(e, exp);
});
};

View File

@ -3218,7 +3218,8 @@ define([
$(anonOffContent).append(h('span.cp-form-anon-answer-input', [
Messages.form_answerAs,
h('input', {
value: user.name || '',
value: (typeof(APP.cantAnon) === "string" && APP.cantAnon)
|| user.name || '',
placeholder: Messages.form_anonName
})
]));
@ -3227,7 +3228,8 @@ define([
$anonName.on('click input', function () {
if (!Util.isChecked($off)) { $off.prop('checked', true); }
});
} else if (APP.cantAnon) {
}
if (APP.cantAnon) {
// You've already answered with your credentials
$(anonRadioOn).find('input').attr('disabled', 'disabled');
$(anonRadioOff).find('input[type="radio"]').prop('checked', true);
@ -4143,13 +4145,17 @@ define([
});
}
var loggedIn = framework._.sfCommon.isLoggedIn();
// In view mode, add "Submit" and "reset" buttons
APP.cantAnon = Object.keys(answers || {}).length && !answers._isAnon;
if (!loggedIn && answers && answers._userdata && answers._userdata.name) {
APP.cantAnon = answers._userdata.name;
}
$container.append(makeFormControls(framework, content, evOnChange));
// In view mode, tell the user if answers are forced to be anonymous or authenticated
var infoTxt;
var loggedIn = framework._.sfCommon.isLoggedIn();
if (content.answers.makeAnonymous) {
infoTxt = Messages.form_anonAnswer;
} else if (!content.answers.anonymous && loggedIn && !content.answers.cantEdit) {

View File

@ -265,7 +265,7 @@ define([
Cryptpad.getFormKeys(w(function (keys) {
myKeys = keys;
}));
Cryptpad.getFormAnswer({channel: data.channel}, false, w(function (obj) {
Cryptpad.getFormAnswer({channel: data.channel}, w(function (obj) {
if (!obj || obj.error) {
if (obj && obj.error === "ENODRIVE") {
var answered = JSON.parse(localStorage.CP_formAnswered || "[]");
@ -354,11 +354,6 @@ define([
// 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;