Moved more classes to ts. (#6179)

This commit is contained in:
SamTV12345 2024-02-22 11:36:43 +01:00 committed by GitHub
parent 3ea6f1072d
commit 4bd27a1c79
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 790 additions and 653 deletions

View file

@ -104,7 +104,7 @@ Example returns:
}
*/
exports.getAttributePool = async (padID) => {
exports.getAttributePool = async (padID: string) => {
const pad = await getPadSafe(padID, true);
return {pool: pad.pool};
};
@ -122,7 +122,7 @@ Example returns:
}
*/
exports.getRevisionChangeset = async (padID, rev) => {
exports.getRevisionChangeset = async (padID: string, rev: string) => {
// try to parse the revision number
if (rev !== undefined) {
rev = checkValidRev(rev);
@ -155,7 +155,7 @@ Example returns:
{code: 0, message:"ok", data: {text:"Welcome Text"}}
{code: 1, message:"padID does not exist", data: null}
*/
exports.getText = async (padID, rev) => {
exports.getText = async (padID: string, rev: string) => {
// try to parse the revision number
if (rev !== undefined) {
rev = checkValidRev(rev);
@ -173,7 +173,7 @@ exports.getText = async (padID, rev) => {
}
// get the text of this revision
// getInternalRevisionAText() returns an atext object but we only want the .text inside it.
// getInternalRevisionAText() returns an atext object, but we only want the .text inside it.
// Details at https://github.com/ether/etherpad-lite/issues/5073
const {text} = await pad.getInternalRevisionAText(rev);
return {text};
@ -200,7 +200,7 @@ Example returns:
* @param {String} authorId the id of the author, defaulting to empty string
* @returns {Promise<void>}
*/
exports.setText = async (padID, text, authorId = '') => {
exports.setText = async (padID: string, text?: string, authorId: string = ''): Promise<void> => {
// text is required
if (typeof text !== 'string') {
throw new CustomError('text is not a string', 'apierror');
@ -225,7 +225,7 @@ Example returns:
@param {String} text the text of the pad
@param {String} authorId the id of the author, defaulting to empty string
*/
exports.appendText = async (padID, text, authorId = '') => {
exports.appendText = async (padID:string, text?: string, authorId:string = '') => {
// text is required
if (typeof text !== 'string') {
throw new CustomError('text is not a string', 'apierror');
@ -247,7 +247,7 @@ Example returns:
@param {String} rev the revision number, defaulting to the latest revision
@return {Promise<{html: string}>} the html of the pad
*/
exports.getHTML = async (padID, rev) => {
exports.getHTML = async (padID: string, rev: string): Promise<{ html: string; }> => {
if (rev !== undefined) {
rev = checkValidRev(rev);
}
@ -283,7 +283,7 @@ Example returns:
@param {String} html the html of the pad
@param {String} authorId the id of the author, defaulting to empty string
*/
exports.setHTML = async (padID, html, authorId = '') => {
exports.setHTML = async (padID: string, html:string|object, authorId = '') => {
// html string is required
if (typeof html !== 'string') {
throw new CustomError('html is not a string', 'apierror');
@ -324,7 +324,7 @@ Example returns:
@param {Number} start the start point of the chat-history
@param {Number} end the end point of the chat-history
*/
exports.getChatHistory = async (padID, start, end) => {
exports.getChatHistory = async (padID: string, start:number, end:number) => {
if (start && end) {
if (start < 0) {
throw new CustomError('start is below zero', 'apierror');
@ -374,7 +374,7 @@ Example returns:
@param {String} authorID the id of the author
@param {Number} time the timestamp of the chat-message
*/
exports.appendChatMessage = async (padID, text, authorID, time) => {
exports.appendChatMessage = async (padID: string, text: string|object, authorID: string, time: number) => {
// text is required
if (typeof text !== 'string') {
throw new CustomError('text is not a string', 'apierror');
@ -404,7 +404,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.getRevisionsCount = async (padID) => {
exports.getRevisionsCount = async (padID: string) => {
// get the pad
const pad = await getPadSafe(padID, true);
return {revisions: pad.getHeadRevisionNumber()};
@ -419,7 +419,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.getSavedRevisionsCount = async (padID) => {
exports.getSavedRevisionsCount = async (padID: string) => {
// get the pad
const pad = await getPadSafe(padID, true);
return {savedRevisions: pad.getSavedRevisionsNumber()};
@ -434,7 +434,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.listSavedRevisions = async (padID) => {
exports.listSavedRevisions = async (padID: string) => {
// get the pad
const pad = await getPadSafe(padID, true);
return {savedRevisions: pad.getSavedRevisionsList()};
@ -450,7 +450,7 @@ Example returns:
@param {String} padID the id of the pad
@param {Number} rev the revision number, defaulting to the latest revision
*/
exports.saveRevision = async (padID, rev) => {
exports.saveRevision = async (padID: string, rev: number) => {
// check if rev is a number
if (rev !== undefined) {
rev = checkValidRev(rev);
@ -483,7 +483,7 @@ Example returns:
@param {String} padID the id of the pad
@return {Promise<{lastEdited: number}>} the timestamp of the last revision of the pad
*/
exports.getLastEdited = async (padID) => {
exports.getLastEdited = async (padID: string): Promise<{ lastEdited: number; }> => {
// get the pad
const pad = await getPadSafe(padID, true);
const lastEdited = await pad.getLastEdit();
@ -497,11 +497,11 @@ Example returns:
{code: 0, message:"ok", data: null}
{code: 1, message:"pad does already exist", data: null}
@param {String} padName the name of the new pad
@param {String} padID the name of the new pad
@param {String} text the initial text of the pad
@param {String} authorId the id of the author, defaulting to empty string
*/
exports.createPad = async (padID, text, authorId = '') => {
exports.createPad = async (padID: string, text: string, authorId = '') => {
if (padID) {
// ensure there is no $ in the padID
if (padID.indexOf('$') !== -1) {
@ -527,7 +527,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.deletePad = async (padID) => {
exports.deletePad = async (padID: string) => {
const pad = await getPadSafe(padID, true);
await pad.remove();
};
@ -543,7 +543,7 @@ exports.deletePad = async (padID) => {
@param {Number} rev the revision number, defaulting to the latest revision
@param {String} authorId the id of the author, defaulting to empty string
*/
exports.restoreRevision = async (padID, rev, authorId = '') => {
exports.restoreRevision = async (padID: string, rev: number, authorId = '') => {
// check if rev is a number
if (rev === undefined) {
throw new CustomError('rev is not defined', 'apierror');
@ -563,7 +563,7 @@ exports.restoreRevision = async (padID, rev, authorId = '') => {
const oldText = pad.text();
atext.text += '\n';
const eachAttribRun = (attribs, func) => {
const eachAttribRun = (attribs: string[], func:Function) => {
let textIndex = 0;
const newTextStart = 0;
const newTextEnd = atext.text.length;
@ -580,7 +580,7 @@ exports.restoreRevision = async (padID, rev, authorId = '') => {
const builder = Changeset.builder(oldText.length);
// assemble each line into the builder
eachAttribRun(atext.attribs, (start, end, attribs) => {
eachAttribRun(atext.attribs, (start: number, end: number, attribs:string[]) => {
builder.insert(atext.text.substring(start, end), attribs);
});
@ -610,7 +610,7 @@ Example returns:
@param {String} destinationID the id of the destination pad
@param {Boolean} force whether to overwrite the destination pad if it exists
*/
exports.copyPad = async (sourceID, destinationID, force) => {
exports.copyPad = async (sourceID: string, destinationID: string, force: boolean) => {
const pad = await getPadSafe(sourceID, true);
await pad.copy(destinationID, force);
};
@ -628,7 +628,7 @@ Example returns:
@param {Boolean} force whether to overwrite the destination pad if it exists
@param {String} authorId the id of the author, defaulting to empty string
*/
exports.copyPadWithoutHistory = async (sourceID, destinationID, force, authorId = '') => {
exports.copyPadWithoutHistory = async (sourceID: string, destinationID: string, force:boolean, authorId = '') => {
const pad = await getPadSafe(sourceID, true);
await pad.copyPadWithoutHistory(destinationID, force, authorId);
};
@ -645,7 +645,7 @@ Example returns:
@param {String} destinationID the id of the destination pad
@param {Boolean} force whether to overwrite the destination pad if it exists
*/
exports.movePad = async (sourceID, destinationID, force) => {
exports.movePad = async (sourceID: string, destinationID: string, force:boolean) => {
const pad = await getPadSafe(sourceID, true);
await pad.copy(destinationID, force);
await pad.remove();
@ -660,7 +660,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.getReadOnlyID = async (padID) => {
exports.getReadOnlyID = async (padID: string) => {
// we don't need the pad object, but this function does all the security stuff for us
await getPadSafe(padID, true);
@ -679,7 +679,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} roID the readonly id of the pad
*/
exports.getPadID = async (roID) => {
exports.getPadID = async (roID: string) => {
// get the PadId
const padID = await readOnlyManager.getPadId(roID);
if (padID == null) {
@ -699,7 +699,7 @@ Example returns:
@param {String} padID the id of the pad
@param {Boolean} publicStatus the public status of the pad
*/
exports.setPublicStatus = async (padID, publicStatus) => {
exports.setPublicStatus = async (padID: string, publicStatus: boolean|string) => {
// ensure this is a group pad
checkGroupPad(padID, 'publicStatus');
@ -723,7 +723,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.getPublicStatus = async (padID) => {
exports.getPublicStatus = async (padID: string) => {
// ensure this is a group pad
checkGroupPad(padID, 'publicStatus');
@ -741,7 +741,7 @@ Example returns:
{code: 1, message:"padID does not exist", data: null}
@param {String} padID the id of the pad
*/
exports.listAuthorsOfPad = async (padID) => {
exports.listAuthorsOfPad = async (padID: string) => {
// get the pad
const pad = await getPadSafe(padID, true);
const authorIDs = pad.getAllAuthors();
@ -773,7 +773,7 @@ Example returns:
@param {String} msg the message to send
*/
exports.sendClientsMessage = async (padID, msg) => {
exports.sendClientsMessage = async (padID: string, msg: string) => {
await getPadSafe(padID, true); // Throw if the padID is invalid or if the pad does not exist.
padMessageHandler.handleCustomMessage(padID, msg);
};
@ -799,7 +799,7 @@ Example returns:
@param {String} padID the id of the pad
@return {Promise<{chatHead: number}>} the chatHead of the pad
*/
exports.getChatHead = async (padID) => {
exports.getChatHead = async (padID:string): Promise<{ chatHead: number; }> => {
// get the pad
const pad = await getPadSafe(padID, true);
return {chatHead: pad.chatHead};
@ -825,7 +825,7 @@ Example returns:
@param {Number} startRev the start revision number
@param {Number} endRev the end revision number
*/
exports.createDiffHTML = async (padID, startRev, endRev) => {
exports.createDiffHTML = async (padID: string, startRev: number, endRev: number) => {
// check if startRev is a number
if (startRev !== undefined) {
startRev = checkValidRev(startRev);
@ -846,7 +846,7 @@ exports.createDiffHTML = async (padID, startRev, endRev) => {
let padDiff;
try {
padDiff = new PadDiff(pad, startRev, endRev);
} catch (e) {
} catch (e:any) {
throw {stop: e.message};
}
@ -872,6 +872,7 @@ exports.getStats = async () => {
const sessionInfos = padMessageHandler.sessioninfos;
const sessionKeys = Object.keys(sessionInfos);
// @ts-ignore
const activePads = new Set(Object.entries(sessionInfos).map((k) => k[1].padId));
const {padIDs} = await padManager.listAllPads();
@ -888,7 +889,7 @@ exports.getStats = async () => {
**************************** */
// gets a pad safe
const getPadSafe = async (padID, shouldExist, text, authorId = '') => {
const getPadSafe = async (padID: string|object, shouldExist: boolean, text?:string, authorId:string = '') => {
// check if padID is a string
if (typeof padID !== 'string') {
throw new CustomError('padID is not a string', 'apierror');
@ -917,7 +918,7 @@ const getPadSafe = async (padID, shouldExist, text, authorId = '') => {
};
// checks if a padID is part of a group
const checkGroupPad = (padID, field) => {
const checkGroupPad = (padID: string, field: string) => {
// ensure this is a group pad
if (padID && padID.indexOf('$') === -1) {
throw new CustomError(

View file

@ -95,7 +95,7 @@ exports.getColorPalette = () => [
* Checks if the author exists
* @param {String} authorID The id of the author
*/
exports.doesAuthorExist = async (authorID) => {
exports.doesAuthorExist = async (authorID: string) => {
const author = await db.get(`globalAuthor:${authorID}`);
return author != null;
@ -114,7 +114,7 @@ exports.doesAuthorExists = exports.doesAuthorExist;
* @param {String} mapperkey The database key name for this mapper
* @param {String} mapper The mapper
*/
const mapAuthorWithDBKey = async (mapperkey, mapper) => {
const mapAuthorWithDBKey = async (mapperkey: string, mapper:string) => {
// try to map to an author
const author = await db.get(`${mapperkey}:${mapper}`);
@ -142,7 +142,7 @@ const mapAuthorWithDBKey = async (mapperkey, mapper) => {
* @param {String} token The token of the author
* @return {Promise<string|*|{authorID: string}|{authorID: *}>}
*/
const getAuthor4Token = async (token) => {
const getAuthor4Token = async (token: string) => {
const author = await mapAuthorWithDBKey('token2author', token);
// return only the sub value authorID
@ -155,7 +155,7 @@ const getAuthor4Token = async (token) => {
* @param {Object} user
* @return {Promise<*>}
*/
exports.getAuthorId = async (token, user) => {
exports.getAuthorId = async (token: string, user: object) => {
const context = {dbKey: token, token, user};
let [authorId] = await hooks.aCallFirst('getAuthorId', context);
if (!authorId) authorId = await getAuthor4Token(context.dbKey);
@ -168,7 +168,7 @@ exports.getAuthorId = async (token, user) => {
* @deprecated Use `getAuthorId` instead.
* @param {String} token The token
*/
exports.getAuthor4Token = async (token) => {
exports.getAuthor4Token = async (token: string) => {
warnDeprecated(
'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead');
return await getAuthor4Token(token);
@ -179,7 +179,7 @@ exports.getAuthor4Token = async (token) => {
* @param {String} authorMapper The mapper
* @param {String} name The name of the author (optional)
*/
exports.createAuthorIfNotExistsFor = async (authorMapper, name) => {
exports.createAuthorIfNotExistsFor = async (authorMapper: string, name: string) => {
const author = await mapAuthorWithDBKey('mapper2author', authorMapper);
if (name) {
@ -195,7 +195,7 @@ exports.createAuthorIfNotExistsFor = async (authorMapper, name) => {
* Internal function that creates the database entry for an author
* @param {String} name The name of the author
*/
exports.createAuthor = async (name) => {
exports.createAuthor = async (name: string) => {
// create the new author name
const author = `a.${randomString(16)}`;
@ -216,41 +216,41 @@ exports.createAuthor = async (name) => {
* Returns the Author Obj of the author
* @param {String} author The id of the author
*/
exports.getAuthor = async (author) => await db.get(`globalAuthor:${author}`);
exports.getAuthor = async (author: string) => await db.get(`globalAuthor:${author}`);
/**
* Returns the color Id of the author
* @param {String} author The id of the author
*/
exports.getAuthorColorId = async (author) => await db.getSub(`globalAuthor:${author}`, ['colorId']);
exports.getAuthorColorId = async (author: string) => await db.getSub(`globalAuthor:${author}`, ['colorId']);
/**
* Sets the color Id of the author
* @param {String} author The id of the author
* @param {String} colorId The color id of the author
*/
exports.setAuthorColorId = async (author, colorId) => await db.setSub(
exports.setAuthorColorId = async (author: string, colorId: string) => await db.setSub(
`globalAuthor:${author}`, ['colorId'], colorId);
/**
* Returns the name of the author
* @param {String} author The id of the author
*/
exports.getAuthorName = async (author) => await db.getSub(`globalAuthor:${author}`, ['name']);
exports.getAuthorName = async (author: string) => await db.getSub(`globalAuthor:${author}`, ['name']);
/**
* Sets the name of the author
* @param {String} author The id of the author
* @param {String} name The name of the author
*/
exports.setAuthorName = async (author, name) => await db.setSub(
exports.setAuthorName = async (author: string, name: string) => await db.setSub(
`globalAuthor:${author}`, ['name'], name);
/**
* Returns an array of all pads this author contributed to
* @param {String} authorID The id of the author
*/
exports.listPadsOfAuthor = async (authorID) => {
exports.listPadsOfAuthor = async (authorID: string) => {
/* There are two other places where this array is manipulated:
* (1) When the author is added to a pad, the author object is also updated
* (2) When a pad is deleted, each author of that pad is also updated
@ -275,7 +275,7 @@ exports.listPadsOfAuthor = async (authorID) => {
* @param {String} authorID The id of the author
* @param {String} padID The id of the pad the author contributes to
*/
exports.addPad = async (authorID, padID) => {
exports.addPad = async (authorID: string, padID: string) => {
// get the entry
const author = await db.get(`globalAuthor:${authorID}`);
@ -302,7 +302,7 @@ exports.addPad = async (authorID, padID) => {
* @param {String} authorID The id of the author
* @param {String} padID The id of the pad the author contributes to
*/
exports.removePad = async (authorID, padID) => {
exports.removePad = async (authorID: string, padID: string) => {
const author = await db.get(`globalAuthor:${authorID}`);
if (author == null) return;

View file

@ -42,7 +42,7 @@ exports.listAllGroups = async () => {
* @param {String} groupID The id of the group
* @return {Promise<void>} Resolves when the group is deleted
*/
exports.deleteGroup = async (groupID) => {
exports.deleteGroup = async (groupID: string): Promise<void> => {
const group = await db.get(`group:${groupID}`);
// ensure group exists
@ -82,7 +82,7 @@ exports.deleteGroup = async (groupID) => {
* @param {String} groupID the id of the group to delete
* @return {Promise<boolean>} Resolves to true if the group exists
*/
exports.doesGroupExist = async (groupID) => {
exports.doesGroupExist = async (groupID: string) => {
// try to get the group entry
const group = await db.get(`group:${groupID}`);
@ -108,7 +108,7 @@ exports.createGroup = async () => {
* @param groupMapper the mapper of the group
* @return {Promise<{groupID: string}|{groupID: *}>} a promise that resolves to the group ID
*/
exports.createGroupIfNotExistsFor = async (groupMapper) => {
exports.createGroupIfNotExistsFor = async (groupMapper: string|object) => {
if (typeof groupMapper !== 'string') {
throw new CustomError('groupMapper is not a string', 'apierror');
}
@ -134,7 +134,7 @@ exports.createGroupIfNotExistsFor = async (groupMapper) => {
* @param {String} authorId The id of the author
* @return {Promise<{padID: string}>} a promise that resolves to the id of the new pad
*/
exports.createGroupPad = async (groupID, padName, text, authorId = '') => {
exports.createGroupPad = async (groupID: string, padName: string, text: string, authorId: string = ''): Promise<{ padID: string; }> => {
// create the padID
const padID = `${groupID}$${padName}`;
@ -167,7 +167,7 @@ exports.createGroupPad = async (groupID, padName, text, authorId = '') => {
* @param {String} groupID The id of the group
* @return {Promise<{padIDs: string[]}>} a promise that resolves to the ids of all pads of the group
*/
exports.listPads = async (groupID) => {
exports.listPads = async (groupID: string): Promise<{ padIDs: string[]; }> => {
const exists = await exports.doesGroupExist(groupID);
// ensure the group exists

View file

@ -1,4 +1,8 @@
'use strict';
import {Database} from "ueberdb2";
import {AChangeSet, APool, AText} from "../types/PadType";
import {MapArrayType} from "../types/MapType";
/**
* The pad object, defined with joose
*/
@ -28,20 +32,29 @@ const promises = require('../utils/promises');
* @param {String} txt The text to clean
* @returns {String} The cleaned text
*/
exports.cleanText = (txt) => txt.replace(/\r\n/g, '\n')
exports.cleanText = (txt:string): string => txt.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.replace(/\t/g, ' ')
.replace(/\xa0/g, ' ');
class Pad {
private db: Database;
private atext: AText;
private pool: APool;
private head: number;
private chatHead: number;
private publicStatus: boolean;
private id: string;
private savedRevisions: any[];
/**
* @param id
* @param [database] - Database object to access this pad's records (and only this pad's records;
* the shared global Etherpad database object is still used for all other pad accesses, such
* as copying the pad). Defaults to the shared global Etherpad database object. This parameter
* can be used to shard pad storage across multiple database backends, to put each pad in its
* own database table, or to validate imported pad data before it is written to the database.
*/
constructor(id, database = db) {
constructor(id:string, database = db) {
this.db = database;
this.atext = Changeset.makeAText('\n');
this.pool = new AttributePool();
@ -80,7 +93,7 @@ class Pad {
* @param {String} authorId The id of the author
* @return {Promise<number|string>}
*/
async appendRevision(aChangeset, authorId = '') {
async appendRevision(aChangeset:AChangeSet, authorId = '') {
const newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
if (newAText.text === this.atext.text && newAText.attribs === this.atext.attribs &&
this.head !== -1) {
@ -95,6 +108,7 @@ class Pad {
const hook = this.head === 0 ? 'padCreate' : 'padUpdate';
await Promise.all([
// @ts-ignore
this.db.set(`pad:${this.id}:revs:${newRev}`, {
changeset: aChangeset,
meta: {
@ -129,32 +143,39 @@ class Pad {
}
toJSON() {
const o = {...this, pool: this.pool.toJsonable()};
const o:Pad = {...this, pool: this.pool.toJsonable()};
// @ts-ignore
delete o.db;
// @ts-ignore
delete o.id;
return o;
}
// save all attributes to the database
async saveToDatabase() {
// @ts-ignore
await this.db.set(`pad:${this.id}`, this);
}
// get time of last edit (changeset application)
async getLastEdit() {
const revNum = this.getHeadRevisionNumber();
// @ts-ignore
return await this.db.getSub(`pad:${this.id}:revs:${revNum}`, ['meta', 'timestamp']);
}
async getRevisionChangeset(revNum) {
async getRevisionChangeset(revNum: number) {
// @ts-ignore
return await this.db.getSub(`pad:${this.id}:revs:${revNum}`, ['changeset']);
}
async getRevisionAuthor(revNum) {
async getRevisionAuthor(revNum: number) {
// @ts-ignore
return await this.db.getSub(`pad:${this.id}:revs:${revNum}`, ['meta', 'author']);
}
async getRevisionDate(revNum) {
async getRevisionDate(revNum: number) {
// @ts-ignore
return await this.db.getSub(`pad:${this.id}:revs:${revNum}`, ['meta', 'timestamp']);
}
@ -162,7 +183,8 @@ class Pad {
* @param {number} revNum - Must be a key revision number (see `getKeyRevisionNumber`).
* @returns The attribute text stored at `revNum`.
*/
async _getKeyRevisionAText(revNum) {
async _getKeyRevisionAText(revNum: number) {
// @ts-ignore
return await this.db.getSub(`pad:${this.id}:revs:${revNum}`, ['meta', 'atext']);
}
@ -182,7 +204,7 @@ class Pad {
return authorIds;
}
async getInternalRevisionAText(targetRev) {
async getInternalRevisionAText(targetRev: number) {
const keyRev = this.getKeyRevisionNumber(targetRev);
const headRev = this.getHeadRevisionNumber();
if (targetRev > headRev) targetRev = headRev;
@ -197,17 +219,17 @@ class Pad {
return atext;
}
async getRevision(revNum) {
async getRevision(revNum: number) {
return await this.db.get(`pad:${this.id}:revs:${revNum}`);
}
async getAllAuthorColors() {
const authorIds = this.getAllAuthors();
const returnTable = {};
const returnTable:MapArrayType<string> = {};
const colorPalette = authorManager.getColorPalette();
await Promise.all(
authorIds.map((authorId) => authorManager.getAuthorColorId(authorId).then((colorId) => {
authorIds.map((authorId) => authorManager.getAuthorColorId(authorId).then((colorId:string) => {
// colorId might be a hex color or an number out of the palette
returnTable[authorId] = colorPalette[colorId] || colorId;
})));
@ -215,7 +237,7 @@ class Pad {
return returnTable;
}
getValidRevisionRange(startRev, endRev) {
getValidRevisionRange(startRev: any, endRev:any) {
startRev = parseInt(startRev, 10);
const head = this.getHeadRevisionNumber();
endRev = endRev ? parseInt(endRev, 10) : head;
@ -236,14 +258,14 @@ class Pad {
return null;
}
getKeyRevisionNumber(revNum) {
getKeyRevisionNumber(revNum: number) {
return Math.floor(revNum / 100) * 100;
}
/**
* @returns {string} The pad's text.
*/
text() {
text(): string {
return this.atext.text;
}
@ -258,7 +280,7 @@ class Pad {
* @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).
*/
async spliceText(start, ndel, ins, authorId = '') {
async spliceText(start:number, ndel:number, ins: string, authorId: string = '') {
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();
@ -283,7 +305,7 @@ class Pad {
* @param {string} [authorId] - The author ID of the user that initiated the change, if
* applicable.
*/
async setText(newText, authorId = '') {
async setText(newText: string, authorId = '') {
await this.spliceText(0, this.text().length, newText, authorId);
}
@ -294,7 +316,7 @@ class Pad {
* @param {string} [authorId] - The author ID of the user that initiated the change, if
* applicable.
*/
async appendText(newText, authorId = '') {
async appendText(newText:string, authorId = '') {
await this.spliceText(this.text().length - 1, 0, newText, authorId);
}
@ -308,7 +330,7 @@ class Pad {
* @param {?number} [time] - Message timestamp (milliseconds since epoch). Deprecated; use
* `msgOrText.time` instead.
*/
async appendChatMessage(msgOrText, authorId = null, time = null) {
async appendChatMessage(msgOrText: string|typeof ChatMessage, authorId = null, time = null) {
const msg =
msgOrText instanceof ChatMessage ? msgOrText : new ChatMessage(msgOrText, authorId, time);
this.chatHead++;
@ -325,7 +347,7 @@ class Pad {
* @param {number} entryNum - ID of the desired chat message.
* @returns {?ChatMessage}
*/
async getChatMessage(entryNum) {
async getChatMessage(entryNum: number) {
const entry = await this.db.get(`pad:${this.id}:chat:${entryNum}`);
if (entry == null) return null;
const message = ChatMessage.fromObject(entry);
@ -340,7 +362,7 @@ class Pad {
* (inclusive), in order. Note: `start` and `end` form a closed interval, not a half-open
* interval as is typical in code.
*/
async getChatMessages(start, end) {
async getChatMessages(start: string, end: number) {
const entries =
await Promise.all(Stream.range(start, end + 1).map(this.getChatMessage.bind(this)));
@ -356,7 +378,7 @@ class Pad {
});
}
async init(text, authorId = '') {
async init(text:string, authorId = '') {
// try to load the pad
const value = await this.db.get(`pad:${this.id}`);
@ -377,7 +399,7 @@ class Pad {
await hooks.aCallAll('padLoad', {pad: this});
}
async copy(destinationID, force) {
async copy(destinationID: string, force: boolean) {
// Kick everyone from this pad.
// This was commented due to https://github.com/ether/etherpad-lite/issues/3183.
// Do we really need to kick everyone out?
@ -392,15 +414,18 @@ class Pad {
// if force is true and already exists a Pad with the same id, remove that Pad
await this.removePadIfForceIsTrueAndAlreadyExist(destinationID, force);
const copyRecord = async (keySuffix) => {
const copyRecord = async (keySuffix: string) => {
const val = await this.db.get(`pad:${this.id}${keySuffix}`);
await db.set(`pad:${destinationID}${keySuffix}`, val);
};
const promises = (function* () {
yield copyRecord('');
// @ts-ignore
yield* Stream.range(0, this.head + 1).map((i) => copyRecord(`:revs:${i}`));
// @ts-ignore
yield* Stream.range(0, this.chatHead + 1).map((i) => copyRecord(`:chat:${i}`));
// @ts-ignore
yield this.copyAuthorInfoToDestinationPad(destinationID);
if (destGroupID) yield db.setSub(`group:${destGroupID}`, ['pads', destinationID], 1);
}).call(this);
@ -427,8 +452,8 @@ class Pad {
return {padID: destinationID};
}
async checkIfGroupExistAndReturnIt(destinationID) {
let destGroupID = false;
async checkIfGroupExistAndReturnIt(destinationID: string) {
let destGroupID:false|string = false;
if (destinationID.indexOf('$') >= 0) {
destGroupID = destinationID.split('$')[0];
@ -442,7 +467,7 @@ class Pad {
return destGroupID;
}
async removePadIfForceIsTrueAndAlreadyExist(destinationID, force) {
async removePadIfForceIsTrueAndAlreadyExist(destinationID: string, force: boolean|string) {
// if the pad exists, we should abort, unless forced.
const exists = await padManager.doesPadExist(destinationID);
@ -465,13 +490,13 @@ class Pad {
}
}
async copyAuthorInfoToDestinationPad(destinationID) {
async copyAuthorInfoToDestinationPad(destinationID: string) {
// add the new sourcePad to all authors who contributed to the old one
await Promise.all(this.getAllAuthors().map(
(authorID) => authorManager.addPad(authorID, destinationID)));
}
async copyPadWithoutHistory(destinationID, force, authorId = '') {
async copyPadWithoutHistory(destinationID: string, force: string|boolean, authorId = '') {
// flush the source pad
this.saveToDatabase();
@ -554,18 +579,18 @@ class Pad {
}
// remove the readonly entries
p.push(readOnlyManager.getReadOnlyId(padID).then(async (readonlyID) => {
p.push(readOnlyManager.getReadOnlyId(padID).then(async (readonlyID: string) => {
await db.remove(`readonly2pad:${readonlyID}`);
}));
p.push(db.remove(`pad2readonly:${padID}`));
// delete all chat messages
p.push(promises.timesLimit(this.chatHead + 1, 500, async (i) => {
p.push(promises.timesLimit(this.chatHead + 1, 500, async (i: string) => {
await this.db.remove(`pad:${this.id}:chat:${i}`, null);
}));
// delete all revisions
p.push(promises.timesLimit(this.head + 1, 500, async (i) => {
p.push(promises.timesLimit(this.head + 1, 500, async (i: string) => {
await this.db.remove(`pad:${this.id}:revs:${i}`, null);
}));
@ -587,12 +612,12 @@ class Pad {
}
// set in db
async setPublicStatus(publicStatus) {
async setPublicStatus(publicStatus: boolean) {
this.publicStatus = publicStatus;
await this.saveToDatabase();
}
async addSavedRevision(revNum, savedById, label) {
async addSavedRevision(revNum: string, savedById: string, label: string) {
// if this revision is already saved, return silently
for (const i in this.savedRevisions) {
if (this.savedRevisions[i] && this.savedRevisions[i].revNum === revNum) {
@ -601,7 +626,7 @@ class Pad {
}
// build the saved revision object
const savedRevision = {};
const savedRevision:MapArrayType<any> = {};
savedRevision.revNum = revNum;
savedRevision.savedById = savedById;
savedRevision.label = label || `Revision ${revNum}`;
@ -664,7 +689,7 @@ class Pad {
if (k === 'author' && v) authorIds.add(v);
});
const revs = Stream.range(0, head + 1)
.map(async (r) => {
.map(async (r: number) => {
const isKeyRev = r === this.getKeyRevisionNumber(r);
try {
return await Promise.all([
@ -675,7 +700,7 @@ class Pad {
isKeyRev,
isKeyRev ? this._getKeyRevisionAText(r) : null,
]);
} catch (err) {
} catch (err:any) {
err.message = `(pad ${this.id} revision ${r}) ${err.message}`;
throw err;
}
@ -708,7 +733,7 @@ class Pad {
}
atext = Changeset.applyToAText(changeset, atext, pool);
if (isKeyRev) assert.deepEqual(keyAText, atext);
} catch (err) {
} catch (err:any) {
err.message = `(pad ${this.id} revision ${r}) ${err.message}`;
throw err;
}
@ -721,12 +746,12 @@ class Pad {
assert(Number.isInteger(this.chatHead));
assert(this.chatHead >= -1);
const chats = Stream.range(0, this.chatHead + 1)
.map(async (c) => {
.map(async (c: number) => {
try {
const msg = await this.getChatMessage(c);
assert(msg != null);
assert(msg instanceof ChatMessage);
} catch (err) {
} catch (err:any) {
err.message = `(pad ${this.id} chat message ${c}) ${err.message}`;
throw err;
}

View file

@ -19,6 +19,8 @@
* limitations under the License.
*/
import {MapArrayType} from "../types/MapType";
const CustomError = require('../utils/customError');
const Pad = require('../db/Pad');
const db = require('./DB');
@ -35,12 +37,16 @@ const settings = require('../utils/Settings');
* If this is needed in other places, it would be wise to make this a prototype
* that's defined somewhere more sensible.
*/
const globalPads = {
get(name) { return this[`:${name}`]; },
set(name, value) {
const globalPads:MapArrayType<any> = {
get(name: string)
{
return this[`:${name}`];
},
set(name: string, value: any)
{
this[`:${name}`] = value;
},
remove(name) {
remove(name: string) {
delete this[`:${name}`];
},
};
@ -51,6 +57,9 @@ const globalPads = {
* Updated without db access as new pads are created/old ones removed.
*/
const padList = new class {
private _cachedList: string[] | null;
private _list: Set<string>;
private _loaded: Promise<void> | null;
constructor() {
this._cachedList = null;
this._list = new Set();
@ -74,13 +83,13 @@ const padList = new class {
return this._cachedList;
}
addPad(name) {
addPad(name: string) {
if (this._list.has(name)) return;
this._list.add(name);
this._cachedList = null;
}
removePad(name) {
removePad(name: string) {
if (!this._list.has(name)) return;
this._list.delete(name);
this._cachedList = null;
@ -96,7 +105,7 @@ const padList = new class {
* @param {string} [authorId] - Optional author ID of the user that initiated the pad creation (if
* applicable).
*/
exports.getPad = async (id, text, authorId = '') => {
exports.getPad = async (id: string, text: string, authorId:string = '') => {
// check if this is a valid padId
if (!exports.isValidPadId(id)) {
throw new CustomError(`${id} is not a valid padId`, 'apierror');
@ -140,7 +149,7 @@ exports.listAllPads = async () => {
};
// checks if a pad exists
exports.doesPadExist = async (padId) => {
exports.doesPadExist = async (padId: string) => {
const value = await db.get(`pad:${padId}`);
return (value != null && value.atext);
@ -159,7 +168,7 @@ const padIdTransforms = [
];
// returns a sanitized padId, respecting legacy pad id formats
exports.sanitizePadId = async (padId) => {
exports.sanitizePadId = async (padId: string) => {
for (let i = 0, n = padIdTransforms.length; i < n; ++i) {
const exists = await exports.doesPadExist(padId);
@ -169,6 +178,7 @@ exports.sanitizePadId = async (padId) => {
const [from, to] = padIdTransforms[i];
// @ts-ignore
padId = padId.replace(from, to);
}
@ -178,12 +188,12 @@ exports.sanitizePadId = async (padId) => {
return padId;
};
exports.isValidPadId = (padId) => /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
exports.isValidPadId = (padId: string) => /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
/**
* Removes the pad from database and unloads it.
*/
exports.removePad = async (padId) => {
exports.removePad = async (padId: string) => {
const p = db.remove(`pad:${padId}`);
exports.unloadPad(padId);
padList.removePad(padId);
@ -191,6 +201,6 @@ exports.removePad = async (padId) => {
};
// removes a pad from the cache
exports.unloadPad = (padId) => {
exports.unloadPad = (padId: string) => {
globalPads.remove(padId);
};

View file

@ -29,14 +29,14 @@ const randomString = require('../utils/randomstring');
* @param {String} id the pad's id
* @return {Boolean} true if the id is readonly
*/
exports.isReadOnlyId = (id) => id.startsWith('r.');
exports.isReadOnlyId = (id:string) => id.startsWith('r.');
/**
* returns a read only id for a pad
* @param {String} padId the id of the pad
* @return {String} the read only id
*/
exports.getReadOnlyId = async (padId) => {
exports.getReadOnlyId = async (padId:string) => {
// check if there is a pad2readonly entry
let readOnlyId = await db.get(`pad2readonly:${padId}`);
@ -57,14 +57,14 @@ exports.getReadOnlyId = async (padId) => {
* @param {String} readOnlyId read only id
* @return {String} the padId
*/
exports.getPadId = async (readOnlyId) => await db.get(`readonly2pad:${readOnlyId}`);
exports.getPadId = async (readOnlyId:string) => await db.get(`readonly2pad:${readOnlyId}`);
/**
* returns the padId and readonlyPadId in an object for any id
* @param {String} id read only id or real pad id
* @return {Object} an object with the padId and readonlyPadId
*/
exports.getIds = async (id) => {
exports.getIds = async (id:string) => {
const readonly = exports.isReadOnlyId(id);
// Might be null, if this is an unknown read-only id

View file

@ -19,6 +19,8 @@
* limitations under the License.
*/
import {UserSettingsObject} from "../types/UserSettingsObject";
const authorManager = require('./AuthorManager');
const hooks = require('../../static/js/pluginfw/hooks.js');
const padManager = require('./PadManager');
@ -55,7 +57,7 @@ const DENY = Object.freeze({accessStatus: 'deny'});
* @param {Object} userSettings
* @return {DENY|{accessStatus: String, authorID: String}}
*/
exports.checkAccess = async (padID, sessionCookie, token, userSettings) => {
exports.checkAccess = async (padID:string, sessionCookie:string, token:string, userSettings:UserSettingsObject) => {
if (!padID) {
authLogger.debug('access denied: missing padID');
return DENY;
@ -95,7 +97,7 @@ exports.checkAccess = async (padID, sessionCookie, token, userSettings) => {
}
// allow plugins to deny access
const isFalse = (x) => x === false;
const isFalse = (x:boolean) => x === false;
if (hooks.callAll('onAccessCheck', {padID, token, sessionCookie}).some(isFalse)) {
authLogger.debug('access denied: an onAccessCheck hook function returned false');
return DENY;

View file

@ -36,7 +36,7 @@ const authorManager = require('./AuthorManager');
* sessionCookie, and is bound to a group with the given ID, then this returns the author ID
* bound to the session. Otherwise, returns undefined.
*/
exports.findAuthorID = async (groupID, sessionCookie) => {
exports.findAuthorID = async (groupID:string, sessionCookie: string) => {
if (!sessionCookie) return undefined;
/*
* Sometimes, RFC 6265-compliant web servers may send back a cookie whose
@ -65,7 +65,7 @@ exports.findAuthorID = async (groupID, sessionCookie) => {
const sessionInfoPromises = sessionIDs.map(async (id) => {
try {
return await exports.getSessionInfo(id);
} catch (err) {
} catch (err:any) {
if (err.message === 'sessionID does not exist') {
console.debug(`SessionManager getAuthorID: no session exists with ID ${id}`);
} else {
@ -75,7 +75,10 @@ exports.findAuthorID = async (groupID, sessionCookie) => {
return undefined;
});
const now = Math.floor(Date.now() / 1000);
const isMatch = (si) => (si != null && si.groupID === groupID && now < si.validUntil);
const isMatch = (si: {
groupID: string;
validUntil: number;
}|null) => (si != null && si.groupID === groupID && now < si.validUntil);
const sessionInfo = await promises.firstSatisfies(sessionInfoPromises, isMatch);
if (sessionInfo == null) return undefined;
return sessionInfo.authorID;
@ -86,7 +89,7 @@ exports.findAuthorID = async (groupID, sessionCookie) => {
* @param {String} sessionID The id of the session
* @return {Promise<boolean>} Resolves to true if the session exists
*/
exports.doesSessionExist = async (sessionID) => {
exports.doesSessionExist = async (sessionID: string) => {
// check if the database entry of this session exists
const session = await db.get(`session:${sessionID}`);
return (session != null);
@ -99,7 +102,7 @@ exports.doesSessionExist = async (sessionID) => {
* @param {Number} validUntil The unix timestamp when the session should expire
* @return {Promise<{sessionID: string}>} the id of the new session
*/
exports.createSession = async (groupID, authorID, validUntil) => {
exports.createSession = async (groupID: string, authorID: string, validUntil: number) => {
// check if the group exists
const groupExists = await groupManager.doesGroupExist(groupID);
if (!groupExists) {
@ -160,7 +163,7 @@ exports.createSession = async (groupID, authorID, validUntil) => {
* @param {String} sessionID The id of the session
* @return {Promise<Object>} the sessioninfos
*/
exports.getSessionInfo = async (sessionID) => {
exports.getSessionInfo = async (sessionID:string) => {
// check if the database entry of this session exists
const session = await db.get(`session:${sessionID}`);
@ -178,7 +181,7 @@ exports.getSessionInfo = async (sessionID) => {
* @param {String} sessionID The id of the session
* @return {Promise<void>} Resolves when the session is deleted
*/
exports.deleteSession = async (sessionID) => {
exports.deleteSession = async (sessionID:string) => {
// ensure that the session exists
const session = await db.get(`session:${sessionID}`);
if (session == null) {
@ -207,7 +210,7 @@ exports.deleteSession = async (sessionID) => {
* @param {String} groupID The id of the group
* @return {Promise<Object>} The sessioninfos of all sessions of this group
*/
exports.listSessionsOfGroup = async (groupID) => {
exports.listSessionsOfGroup = async (groupID: string) => {
// check that the group exists
const exists = await groupManager.doesGroupExist(groupID);
if (!exists) {
@ -223,7 +226,7 @@ exports.listSessionsOfGroup = async (groupID) => {
* @param {String} authorID The id of the author
* @return {Promise<Object>} The sessioninfos of all sessions of this author
*/
exports.listSessionsOfAuthor = async (authorID) => {
exports.listSessionsOfAuthor = async (authorID: string) => {
// check that the author exists
const exists = await authorManager.doesAuthorExist(authorID);
if (!exists) {
@ -240,7 +243,7 @@ exports.listSessionsOfAuthor = async (authorID) => {
* @param {String} dbkey The db key to use to get the sessions
* @return {Promise<*>}
*/
const listSessionsWithDBKey = async (dbkey) => {
const listSessionsWithDBKey = async (dbkey: string) => {
// get the group2sessions entry
const sessionObject = await db.get(dbkey);
const sessions = sessionObject ? sessionObject.sessionIDs : null;
@ -249,7 +252,7 @@ const listSessionsWithDBKey = async (dbkey) => {
for (const sessionID of Object.keys(sessions || {})) {
try {
sessions[sessionID] = await exports.getSessionInfo(sessionID);
} catch (err) {
} catch (err:any) {
if (err.name === 'apierror') {
console.warn(`Found bad session ${sessionID} in ${dbkey}`);
sessions[sessionID] = null;
@ -262,9 +265,11 @@ const listSessionsWithDBKey = async (dbkey) => {
return sessions;
};
/**
* checks if a number is an int
* @param {number|string} value
* @return {boolean} If the value is an integer
*/
const isInt = (value) => (parseFloat(value) === parseInt(value)) && !isNaN(value);
// @ts-ignore
const isInt = (value:number|string): boolean => (parseFloat(value) === parseInt(value)) && !isNaN(value);