mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 14:13:34 +01:00
Pad: Plumb author ID through mutation operations
This commit is contained in:
parent
5f60b3aab2
commit
3b8549342a
7 changed files with 35 additions and 30 deletions
|
@ -274,8 +274,9 @@ Pad.prototype.text = function () {
|
|||
* @param {number} ndel - Number of characters to remove starting at `start`. Must be a non-negative
|
||||
* integer less than or equal to `this.text().length - start`.
|
||||
* @param {string} ins - New text to insert at `start` (after the `ndel` characters are deleted).
|
||||
* @param {string} [authorId] - Author ID of the user making the change (if applicable).
|
||||
*/
|
||||
Pad.prototype.spliceText = async function (start, ndel, ins) {
|
||||
Pad.prototype.spliceText = async function (start, ndel, ins, authorId = '') {
|
||||
if (start < 0) throw new RangeError(`start index must be non-negative (is ${start})`);
|
||||
if (ndel < 0) throw new RangeError(`characters to delete must be non-negative (is ${ndel})`);
|
||||
const orig = this.text();
|
||||
|
@ -289,7 +290,7 @@ Pad.prototype.spliceText = async function (start, ndel, ins) {
|
|||
if (!willEndWithNewline) ins += '\n';
|
||||
if (ndel === 0 && ins.length === 0) return;
|
||||
const changeset = Changeset.makeSplice(orig, start, ndel, ins);
|
||||
await this.appendRevision(changeset);
|
||||
await this.appendRevision(changeset, authorId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -297,18 +298,20 @@ Pad.prototype.spliceText = async function (start, ndel, ins) {
|
|||
*
|
||||
* @param {string} newText - The pad's new text. If this string does not end with a newline, one
|
||||
* will be automatically appended.
|
||||
* @param {string} [authorId] - The author ID of the user that initiated the change, if applicable.
|
||||
*/
|
||||
Pad.prototype.setText = async function (newText) {
|
||||
await this.spliceText(0, this.text().length, newText);
|
||||
Pad.prototype.setText = async function (newText, authorId = '') {
|
||||
await this.spliceText(0, this.text().length, newText, authorId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Appends text to the pad.
|
||||
*
|
||||
* @param {string} newText - Text to insert just BEFORE the pad's existing terminating newline.
|
||||
* @param {string} [authorId] - The author ID of the user that initiated the change, if applicable.
|
||||
*/
|
||||
Pad.prototype.appendText = async function (newText) {
|
||||
await this.spliceText(this.text().length - 1, 0, newText);
|
||||
Pad.prototype.appendText = async function (newText, authorId = '') {
|
||||
await this.spliceText(this.text().length - 1, 0, newText, authorId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -368,7 +371,7 @@ Pad.prototype.getChatMessages = async function (start, end) {
|
|||
});
|
||||
};
|
||||
|
||||
Pad.prototype.init = async function (text) {
|
||||
Pad.prototype.init = async function (text, authorId = '') {
|
||||
// replace text with default text if text isn't set
|
||||
if (text == null) {
|
||||
text = settings.defaultPadText;
|
||||
|
@ -391,7 +394,7 @@ Pad.prototype.init = async function (text) {
|
|||
// this pad doesn't exist, so create it
|
||||
const firstChangeset = Changeset.makeSplice('\n', 0, 0, exports.cleanText(text));
|
||||
|
||||
await this.appendRevision(firstChangeset, '');
|
||||
await this.appendRevision(firstChangeset, authorId);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -476,7 +479,7 @@ Pad.prototype.copyAuthorInfoToDestinationPad = async function (destinationID) {
|
|||
(authorID) => authorManager.addPad(authorID, destinationID)));
|
||||
};
|
||||
|
||||
Pad.prototype.copyPadWithoutHistory = async function (destinationID, force) {
|
||||
Pad.prototype.copyPadWithoutHistory = async function (destinationID, force, authorId = '') {
|
||||
// flush the source pad
|
||||
this.saveToDatabase();
|
||||
|
||||
|
@ -494,7 +497,7 @@ Pad.prototype.copyPadWithoutHistory = async function (destinationID, force) {
|
|||
}
|
||||
|
||||
// initialize the pad with a new line to avoid getting the defaultText
|
||||
const newPad = await padManager.getPad(destinationID, '\n');
|
||||
const newPad = await padManager.getPad(destinationID, '\n', authorId);
|
||||
newPad.pool = this.pool.clone();
|
||||
|
||||
const oldAText = this.atext;
|
||||
|
@ -514,7 +517,7 @@ Pad.prototype.copyPadWithoutHistory = async function (destinationID, force) {
|
|||
// create a changeset that removes the previous text and add the newText with
|
||||
// all atributes present on the source pad
|
||||
const changeset = Changeset.pack(oldLength, newLength, assem.toString(), newText);
|
||||
newPad.appendRevision(changeset);
|
||||
newPad.appendRevision(changeset, authorId);
|
||||
|
||||
await hooks.aCallAll('padCopy', {originalPad: this, destinationID});
|
||||
|
||||
|
|
|
@ -91,9 +91,11 @@ const padList = new class {
|
|||
/**
|
||||
* Returns a Pad Object with the callback
|
||||
* @param id A String with the id of the pad
|
||||
* @param {Function} callback
|
||||
* @param {string} [text] - Optional initial pad text if creating a new pad.
|
||||
* @param {string} [authorId] - Optional author ID of the user that initiated the pad creation (if
|
||||
* applicable).
|
||||
*/
|
||||
exports.getPad = async (id, text) => {
|
||||
exports.getPad = async (id, text, authorId = '') => {
|
||||
// check if this is a valid padId
|
||||
if (!exports.isValidPadId(id)) {
|
||||
throw new CustomError(`${id} is not a valid padId`, 'apierror');
|
||||
|
@ -123,7 +125,7 @@ exports.getPad = async (id, text) => {
|
|||
pad = new Pad.Pad(id);
|
||||
|
||||
// initialize the pad
|
||||
await pad.init(text);
|
||||
await pad.init(text, authorId);
|
||||
hooks.callAll('padLoad', {pad});
|
||||
globalPads.set(id, pad);
|
||||
padList.addPad(id);
|
||||
|
|
|
@ -74,7 +74,7 @@ const tmpDirectory = os.tmpdir();
|
|||
/**
|
||||
* do a requested import
|
||||
*/
|
||||
const doImport = async (req, res, padId) => {
|
||||
const doImport = async (req, res, padId, authorId) => {
|
||||
// pipe to a file
|
||||
// convert file to html via abiword or soffice
|
||||
// set html in the pad
|
||||
|
@ -140,7 +140,7 @@ const doImport = async (req, res, padId) => {
|
|||
let directDatabaseAccess = false;
|
||||
if (fileIsEtherpad) {
|
||||
// Use '\n' to avoid the default pad text if the pad doesn't yet exist.
|
||||
const pad = await padManager.getPad(padId, '\n');
|
||||
const pad = await padManager.getPad(padId, '\n', authorId);
|
||||
const headCount = pad.head;
|
||||
if (headCount >= 10) {
|
||||
logger.warn('Aborting direct database import attempt of a pad that already has content');
|
||||
|
@ -148,7 +148,7 @@ const doImport = async (req, res, padId) => {
|
|||
}
|
||||
const text = await fs.readFile(srcFile, 'utf8');
|
||||
directDatabaseAccess = true;
|
||||
await importEtherpad.setPadRaw(padId, text);
|
||||
await importEtherpad.setPadRaw(padId, text, authorId);
|
||||
}
|
||||
|
||||
// convert file to html if necessary
|
||||
|
@ -205,12 +205,12 @@ const doImport = async (req, res, padId) => {
|
|||
if (!directDatabaseAccess) {
|
||||
if (importHandledByPlugin || useConverter || fileIsHTML) {
|
||||
try {
|
||||
await importHtml.setPadHTML(pad, text);
|
||||
await importHtml.setPadHTML(pad, text, authorId);
|
||||
} catch (err) {
|
||||
logger.warn(`Error importing, possibly caused by malformed HTML: ${err.stack || err}`);
|
||||
}
|
||||
} else {
|
||||
await pad.setText(text);
|
||||
await pad.setText(text, authorId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,13 +233,13 @@ const doImport = async (req, res, padId) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
exports.doImport = async (req, res, padId) => {
|
||||
exports.doImport = async (req, res, padId, authorId = '') => {
|
||||
let httpStatus = 200;
|
||||
let code = 0;
|
||||
let message = 'ok';
|
||||
let directDatabaseAccess;
|
||||
try {
|
||||
directDatabaseAccess = await doImport(req, res, padId);
|
||||
directDatabaseAccess = await doImport(req, res, padId, authorId);
|
||||
} catch (err) {
|
||||
const known = err instanceof ImportError && err.status;
|
||||
if (!known) logger.error(`Internal error during import: ${err.stack || err}`);
|
||||
|
|
|
@ -676,13 +676,13 @@ const handleUserChanges = async (socket, message) => {
|
|||
|
||||
const correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool);
|
||||
if (correctionChangeset) {
|
||||
await pad.appendRevision(correctionChangeset);
|
||||
await pad.appendRevision(correctionChangeset, thisSession.author);
|
||||
}
|
||||
|
||||
// Make sure the pad always ends with an empty line.
|
||||
if (pad.text().lastIndexOf('\n') !== pad.text().length - 1) {
|
||||
const nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length - 1, 0, '\n');
|
||||
await pad.appendRevision(nlChangeset);
|
||||
await pad.appendRevision(nlChangeset, thisSession.author);
|
||||
}
|
||||
|
||||
// The client assumes that ACCEPT_COMMIT and NEW_CHANGES messages arrive in order. Make sure we
|
||||
|
|
|
@ -70,12 +70,12 @@ exports.expressCreateServer = (hookName, args, cb) => {
|
|||
args.app.post('/p/:pad/import', (req, res, next) => {
|
||||
(async () => {
|
||||
const {session: {user} = {}} = req;
|
||||
const {accessStatus} = await securityManager.checkAccess(
|
||||
const {accessStatus, authorID: authorId} = await securityManager.checkAccess(
|
||||
req.params.pad, req.cookies.sessionID, req.cookies.token, user);
|
||||
if (accessStatus !== 'grant' || !webaccess.userCanModify(req.params.pad, req)) {
|
||||
return res.status(403).send('Forbidden');
|
||||
}
|
||||
await importHandler.doImport(req, res, req.params.pad);
|
||||
await importHandler.doImport(req, res, req.params.pad, authorId);
|
||||
})().catch((err) => next(err || new Error(err)));
|
||||
});
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ const supportedElems = require('../../static/js/contentcollector').supportedElem
|
|||
|
||||
const logger = log4js.getLogger('ImportEtherpad');
|
||||
|
||||
exports.setPadRaw = async (padId, r) => {
|
||||
exports.setPadRaw = async (padId, r, authorId = '') => {
|
||||
const records = JSON.parse(r);
|
||||
|
||||
// get supported block Elements from plugins, we will use this later.
|
||||
|
@ -110,7 +110,7 @@ exports.setPadRaw = async (padId, r) => {
|
|||
return v;
|
||||
},
|
||||
});
|
||||
await pad.init();
|
||||
await pad.init(null, authorId);
|
||||
await pad.check();
|
||||
|
||||
await Promise.all([
|
||||
|
|
|
@ -23,7 +23,7 @@ const jsdom = require('jsdom');
|
|||
const apiLogger = log4js.getLogger('ImportHtml');
|
||||
let processor;
|
||||
|
||||
exports.setPadHTML = async (pad, html) => {
|
||||
exports.setPadHTML = async (pad, html, authorId = '') => {
|
||||
if (processor == null) {
|
||||
const [{rehype}, {default: minifyWhitespace}] =
|
||||
await Promise.all([import('rehype'), import('rehype-minify-whitespace')]);
|
||||
|
@ -88,6 +88,6 @@ exports.setPadHTML = async (pad, html) => {
|
|||
const theChangeset = builder.toString();
|
||||
|
||||
apiLogger.debug(`The changeset: ${theChangeset}`);
|
||||
await pad.setText('\n');
|
||||
await pad.appendRevision(theChangeset);
|
||||
await pad.setText('\n', authorId);
|
||||
await pad.appendRevision(theChangeset, authorId);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue