pad: Simplify reload after .etherpad import

The old "switch to pad" logic looked buggy, and it complicates pad
initialization. Forcing a refresh after importing an `.etherpad` file
isn't much of a UX downgrade.
This commit is contained in:
Richard Hansen 2021-10-28 15:55:47 -04:00
parent e974622561
commit 5cbbcbcee6
6 changed files with 10 additions and 83 deletions

View file

@ -34,6 +34,7 @@
### Notable enhancements ### Notable enhancements
* Simplified pad reload after importing an `.etherpad` file.
* For plugin authors: * For plugin authors:
* `clientVars` was added to the context for the `postAceInit` client-side * `clientVars` was added to the context for the `postAceInit` client-side
hook. Plugins should use this instead of the `clientVars` global variable. hook. Plugins should use this instead of the `clientVars` global variable.

View file

@ -579,9 +579,9 @@ Things in context:
This hook allows plugins to grant temporary write access to a pad. It is called This hook allows plugins to grant temporary write access to a pad. It is called
for each incoming message from a client. If write access is granted, it applies for each incoming message from a client. If write access is granted, it applies
to the current message and all future messages from the same socket.io to the current message and all future messages from the same socket.io
connection until the next `CLIENT_READY` or `SWITCH_TO_PAD` message. Read-only connection until the next `CLIENT_READY` message. Read-only access is reset
access is reset **after** each `CLIENT_READY` or `SWITCH_TO_PAD` message, so **after** each `CLIENT_READY` message, so granting write access has no effect
granting write access has no effect for those message types. for those message types.
The handleMessageSecurity function must return a Promise. If the Promise The handleMessageSecurity function must return a Promise. If the Promise
resolves to `true`, write access is granted as described above. Returning resolves to `true`, write access is granted as described above. Returning

View file

@ -235,8 +235,8 @@ const doImport = async (req, res, padId) => {
pad = await padManager.getPad(padId); pad = await padManager.getPad(padId);
padManager.unloadPad(padId); padManager.unloadPad(padId);
// direct Database Access means a pad user should perform a switchToPad // Direct database access means a pad user should reload the pad and not attempt to receive
// and not attempt to receive updated pad data // updated pad data.
if (directDatabaseAccess) return true; if (directDatabaseAccess) return true;
// tell clients to update // tell clients to update

View file

@ -283,8 +283,6 @@ exports.handleMessage = async (socket, message) => {
} else { } else {
messageLogger.warn(`Dropped message, unknown COLLABROOM Data Type ${message.data.type}`); messageLogger.warn(`Dropped message, unknown COLLABROOM Data Type ${message.data.type}`);
} }
} else if (message.type === 'SWITCH_TO_PAD') {
await handleSwitchToPad(socket, message, authorID);
} else { } else {
messageLogger.warn(`Dropped message, unknown Message Type ${message.type}`); messageLogger.warn(`Dropped message, unknown Message Type ${message.type}`);
} }
@ -806,44 +804,6 @@ const _correctMarkersInPad = (atext, apool) => {
return builder.toString(); return builder.toString();
}; };
const handleSwitchToPad = async (socket, message, _authorID) => {
const currentSessionInfo = sessioninfos[socket.id];
const padId = currentSessionInfo.padId;
// Check permissions for the new pad.
const newPadIds = await readOnlyManager.getIds(message.padId);
const {session: {user} = {}} = socket.client.request;
const {accessStatus, authorID} = await securityManager.checkAccess(
newPadIds.padId, message.sessionID, message.token, user);
if (accessStatus !== 'grant') {
// Access denied. Send the reason to the user.
socket.json.send({accessStatus});
return;
}
// The same token and session ID were passed to checkAccess in handleMessage, so this second call
// to checkAccess should return the same author ID.
assert(authorID === _authorID);
assert(authorID === currentSessionInfo.author);
// Check if the connection dropped during the access check.
if (sessioninfos[socket.id] !== currentSessionInfo) return;
// clear the session and leave the room
_getRoomSockets(padId).forEach((socket) => {
const sinfo = sessioninfos[socket.id];
if (sinfo && sinfo.author === currentSessionInfo.author) {
// fix user's counter, works on page refresh or if user closes browser window and then rejoins
sessioninfos[socket.id] = {};
socket.leave(padId);
}
});
// start up the new pad
const newSessionInfo = sessioninfos[socket.id];
createSessionInfoAuth(newSessionInfo, message);
await handleClientReady(socket, message, authorID);
};
// Creates/replaces the auth object in the given session info. // Creates/replaces the auth object in the given session info.
const createSessionInfoAuth = (sessionInfo, message) => { const createSessionInfoAuth = (sessionInfo, message) => {
// Remember this information since we won't // Remember this information since we won't

View file

@ -48,8 +48,6 @@ const socketio = require('./socketio');
const hooks = require('./pluginfw/hooks'); const hooks = require('./pluginfw/hooks');
let receivedClientVars = false;
// This array represents all GET-parameters which can be used to change a setting. // This array represents all GET-parameters which can be used to change a setting.
// name: the parameter-name, eg `?noColors=true` => `noColors` // name: the parameter-name, eg `?noColors=true` => `noColors`
// checkVal: the callback is only executed when // checkVal: the callback is only executed when
@ -181,8 +179,7 @@ const getUrlVars = () => {
return vars; return vars;
}; };
const sendClientReady = (isReconnect, messageType) => { const sendClientReady = (isReconnect) => {
messageType = typeof messageType !== 'undefined' ? messageType : 'CLIENT_READY';
let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1); let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1);
// unescape neccesary due to Safari and Opera interpretation of spaces // unescape neccesary due to Safari and Opera interpretation of spaces
padId = decodeURIComponent(padId); padId = decodeURIComponent(padId);
@ -201,7 +198,7 @@ const sendClientReady = (isReconnect, messageType) => {
const msg = { const msg = {
component: 'pad', component: 'pad',
type: messageType, type: 'CLIENT_READY',
padId, padId,
sessionID: Cookies.get('sessionID'), sessionID: Cookies.get('sessionID'),
token, token,
@ -218,6 +215,7 @@ const sendClientReady = (isReconnect, messageType) => {
}; };
const handshake = () => { const handshake = () => {
let receivedClientVars = false;
let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1); let padId = document.location.pathname.substring(document.location.pathname.lastIndexOf('/') + 1);
// unescape neccesary due to Safari and Opera interpretation of spaces // unescape neccesary due to Safari and Opera interpretation of spaces
padId = decodeURIComponent(padId); padId = decodeURIComponent(padId);
@ -394,38 +392,6 @@ const pad = {
getUserId: () => pad.myUserInfo.userId, getUserId: () => pad.myUserInfo.userId,
getUserName: () => pad.myUserInfo.name, getUserName: () => pad.myUserInfo.name,
userList: () => paduserlist.users(), userList: () => paduserlist.users(),
switchToPad: (padId) => {
let newHref = new RegExp(/.*\/p\/[^/]+/).exec(document.location.pathname) || clientVars.padId;
newHref = newHref[0];
const options = clientVars.padOptions;
if (typeof options !== 'undefined' && options != null) {
const optionArr = [];
$.each(options, (k, v) => {
const str = `${k}=${v}`;
optionArr.push(str);
});
const optionStr = optionArr.join('&');
newHref = `${newHref}?${optionStr}`;
}
// destroy old pad from DOM
// See https://github.com/ether/etherpad-lite/pull/3915
// TODO: Check if Destroying is enough and doesn't leave negative stuff
// See ace.js "editor.destroy" for a reference of how it was done before
$('#editorcontainer').find('iframe')[0].remove();
if (window.history && window.history.pushState) {
$('#chattext p').remove(); // clear the chat messages
window.history.pushState('', '', newHref);
receivedClientVars = false;
sendClientReady(false, 'SWITCH_TO_PAD');
} else {
// fallback
window.location.href = newHref;
}
},
sendClientMessage: (msg) => { sendClientMessage: (msg) => {
pad.collabClient.sendClientMessage(msg); pad.collabClient.sendClientMessage(msg);
}, },

View file

@ -67,7 +67,7 @@ const padimpexp = (() => {
importErrorMessage(message); importErrorMessage(message);
} else { } else {
$('#import_export').removeClass('popup-show'); $('#import_export').removeClass('popup-show');
if (directDatabaseAccess) pad.switchToPad(clientVars.padId); if (directDatabaseAccess) window.location.reload();
} }
$('#importsubmitinput').removeAttr('disabled').val(html10n.get('pad.impexp.importbutton')); $('#importsubmitinput').removeAttr('disabled').val(html10n.get('pad.impexp.importbutton'));
window.setTimeout(() => $('#importfileinput').removeAttr('disabled'), 0); window.setTimeout(() => $('#importfileinput').removeAttr('disabled'), 0);