Index: lams_tool_doku/conf/etherpad-lite/settings.json =================================================================== diff -u --- lams_tool_doku/conf/etherpad-lite/settings.json (revision 0) +++ lams_tool_doku/conf/etherpad-lite/settings.json (revision 74b4aef858c62df885b2b55c25127e683b875dfd) @@ -0,0 +1,535 @@ +/* + * This file must be valid JSON. But comments are allowed + * + * Please edit settings.json, not settings.json.template + * + * Please note that starting from Etherpad 1.6.0 you can store DB credentials in + * a separate file (credentials.json). + * + * + * ENVIRONMENT VARIABLE SUBSTITUTION + * ================================= + * + * All the configuration values can be read from environment variables using the + * syntax "${ENV_VAR}" or "${ENV_VAR:default_value}". + * + * This is useful, for example, when running in a Docker container. + * + * EXAMPLE: + * "port": "${PORT:9001}" + * "minify": "${MINIFY}" + * "skinName": "${SKIN_NAME:colibris}" + * + * Would read the configuration values for those items from the environment + * variables PORT, MINIFY and SKIN_NAME. + * + * If PORT and SKIN_NAME variables were not defined, the default values 9001 and + * "colibris" would be used. + * The configuration value "minify", on the other hand, does not have a + * designated default value. Thus, if the environment variable MINIFY were + * undefined, "minify" would be null. + * + * REMARKS: + * 1) please note that variable substitution always needs to be quoted. + * + * "port": 9001, <-- Literal values. When not using + * "minify": false substitution, only strings must be + * "skinName": "colibris" quoted. Booleans and numbers must not. + * + * "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable + * "minify": "${MINIFY:true}" substitution, put quotes around its name, + * "skinName": "${SKIN_NAME}" even if the required value is a number or + * a boolean. + * Etherpad will take care of rewriting it + * to the proper type if necessary. + * + * "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes + * "minify": ${MINIFY} around variable names are missing. + * "skinName": ${SKIN_NAME} + * + * 2) Beware of undefined variables and default values: nulls and empty strings + * are different! + * + * This is particularly important for user's passwords (see the relevant + * section): + * + * "password": "${PASSW}" // if PASSW is not defined would result in password === null + * "password": "${PASSW:}" // if PASSW is not defined would result in password === '' + * + * 3) if you want to use newlines in the default value of a string parameter, + * use "\n" as usual. + * + * "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2" + */ +{ + /* + * Name your instance! + */ + "title": "Etherpad", + + /* + * favicon default name + * alternatively, set up a fully specified Url to your own favicon + */ + "favicon": "favicon.ico", + + /* + * Skin name. + * + * Its value has to be an existing directory under src/static/skins. + * You can write your own, or use one of the included ones: + * + * - "no-skin": an empty skin (default). This yields the unmodified, + * traditional Etherpad theme. + * - "colibris": the new experimental skin (since Etherpad 1.8), candidate to + * become the default in Etherpad 2.0 + */ + "skinName": "no-skin", + + /* + * IP and port which Etherpad should bind at. + * + * Binding to a Unix socket is also supported: just use an empty string for + * the ip, and put the full path to the socket in the port parameter. + * + * EXAMPLE USING UNIX SOCKET: + * "ip": "", // <-- has to be an empty string + * "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket + */ + "ip": "0.0.0.0", + "port": 9001, + + /* + * Option to hide/show the settings.json in admin page. + * + * Default option is set to true + */ + "showSettingsInAdminPage": true, + + /* + * Node native SSL support + * + * This is disabled by default. + * Make sure to have the minimum and correct file access permissions set so + * that the Etherpad server can access them + */ + + /* + "ssl" : { + "key" : "/path-to-your/epl-server.key", + "cert" : "/path-to-your/epl-server.crt", + "ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"] + }, + */ + + /* + * The type of the database. + * + * You can choose between many DB drivers, for example: dirty, postgres, + * sqlite, mysql. + * + * You shouldn't use "dirty" for for anything else than testing or + * development. + * + * + * Database specific settings are dependent on dbType, and go in dbSettings. + * Remember that since Etherpad 1.6.0 you can also store these informations in + * credentials.json. + * + * For a complete list of the supported drivers, please refer to: + * https://www.npmjs.com/package/ueberdb2 + */ + + "dbType": "dirty", + "dbSettings": { + "filename": "var/dirty.db" + }, + + /* + * An Example of MySQL Configuration (commented out). + * + * See: https://github.com/ether/etherpad-lite/wiki/How-to-use-Etherpad-Lite-with-MySQL + */ + + /* + "dbType" : "mysql", + "dbSettings" : { + "user": "etherpaduser", + "host": "localhost", + "port": 3306, + "password": "PASSWORD", + "database": "etherpad_lite_db", + "charset": "utf8mb4" + }, + */ + + /* + * The default text of a pad + */ + "defaultPadText" : "", + + /* + * Default Pad behavior. + * + * Change them if you want to override. + */ + "padOptions": { + "noColors": false, + "showControls": true, + "showChat": false, + "showLineNumbers": false, + "useMonospaceFont": false, + "userName": false, + "userColor": false, + "rtl": false, + "alwaysShowChat": false, + "chatAndUsers": false, + "lang": "en-gb" + }, + + /* + * Pad Shortcut Keys + */ + "padShortcutEnabled" : { + "altF9": true, /* focus on the File Menu and/or editbar */ + "altC": true, /* focus on the Chat window */ + "cmdShift2": true, /* shows a gritter popup showing a line author */ + "delete": true, + "return": true, + "esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */ + "cmdS": true, /* save a revision */ + "tab": true, /* indent */ + "cmdZ": true, /* undo/redo */ + "cmdY": true, /* redo */ + "cmdI": true, /* italic */ + "cmdB": true, /* bold */ + "cmdU": true, /* underline */ + "cmd5": true, /* strike through */ + "cmdShiftL": true, /* unordered list */ + "cmdShiftN": true, /* ordered list */ + "cmdShift1": true, /* ordered list */ + "cmdShiftC": true, /* clear authorship */ + "cmdH": true, /* backspace */ + "ctrlHome": true, /* scroll to top of pad */ + "pageUp": true, + "pageDown": true + }, + + /* + * Should we suppress errors from being visible in the default Pad Text? + */ + "suppressErrorsInPadText": false, + + /* + * If this option is enabled, a user must have a session to access pads. + * This effectively allows only group pads to be accessed. + */ + "requireSession": false, + + /* + * Users may edit pads but not create new ones. + * + * Pad creation is only via the API. + * This applies both to group pads and regular pads. + */ + "editOnly": false, + + /* + * If set to true, those users who have a valid session will automatically be + * granted access to password protected pads. + */ + "sessionNoPassword": false, + + /* + * If true, all css & js will be minified before sending to the client. + * + * This will improve the loading performance massively, but makes it difficult + * to debug the javascript/css + */ + "minify": true, + + /* + * How long may clients use served javascript code (in seconds)? + * + * Not setting this may cause problems during deployment. + * Set to 0 to disable caching. + */ + "maxAge": 21600, // 60 * 60 * 6 = 6 hours + + /* + * Absolute path to the Abiword executable. + * + * Abiword is needed to get advanced import/export features of pads. Setting + * it to null disables Abiword and will only allow plain text and HTML + * import/exports. + */ + "abiword": null, + + /* + * This is the absolute path to the soffice executable. + * + * LibreOffice can be used in lieu of Abiword to export pads. + * Setting it to null disables LibreOffice exporting. + */ + "soffice": null, + + /* + * Path to the Tidy executable. + * + * Tidy is used to improve the quality of exported pads. + * Setting it to null disables Tidy. + */ + "tidyHtml": null, + + /* + * Allow import of file types other than the supported ones: + * txt, doc, docx, rtf, odt, html & htm + */ + "allowUnknownFileEnds": true, + + /* + * This setting is used if you require authentication of all users. + * + * Note: "/admin" always requires authentication. + */ + "requireAuthentication": false, + + /* + * Require authorization by a module, or a user with is_admin set, see below. + */ + "requireAuthorization": false, + + /* + * When you use NGINX or another proxy/load-balancer set this to true. + * + * This is especially necessary when the reverse proxy performs SSL + * termination, otherwise the cookies will not have the "secure" flag. + * + * The other effect will be that the logs will contain the real client's IP, + * instead of the reverse proxy's IP. + */ + "trustProxy": false, + + /* + * Privacy: disable IP logging + */ + "disableIPlogging": false, + + /* + * Time (in seconds) to automatically reconnect pad when a "Force reconnect" + * message is shown to user. + * + * Set to 0 to disable automatic reconnection. + */ + "automaticReconnectionTimeout": 0, + + /* + * By default, when caret is moved out of viewport, it scrolls the minimum + * height needed to make this line visible. + */ + "scrollWhenFocusLineIsOutOfViewport": { + + /* + * Percentage of viewport height to be additionally scrolled. + * + * E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in + * the middle of viewport, when user edits a line above of the + * viewport + * + * Set to 0 to disable extra scrolling + */ + "percentage": { + "editionAboveViewport": 0, + "editionBelowViewport": 0 + }, + + /* + * Time (in milliseconds) used to animate the scroll transition. + * Set to 0 to disable animation + */ + "duration": 0, + + /* + * Flag to control if it should scroll when user places the caret in the + * last line of the viewport + */ + "scrollWhenCaretIsInTheLastLineOfViewport": false, + + /* + * Percentage of viewport height to be additionally scrolled when user + * presses arrow up in the line of the top of the viewport. + * + * Set to 0 to let the scroll to be handled as default by Etherpad + */ + "percentageToScrollWhenUserPressesArrowUp": 0 + }, + + /* + * Users for basic authentication. + * + * is_admin = true gives access to /admin. + * If you do not uncomment this, /admin will not be available! + * + * WARNING: passwords should not be stored in plaintext in this file. + * If you want to mitigate this, please install ep_hash_auth and + * follow the section "secure your installation" in README.md + */ + + /* + "users": { + "admin": { + // 1) "password" can be replaced with "hash" if you install ep_hash_auth + // 2) please note that if password is null, the user will not be created + "password": "changeme1", + "is_admin": true + }, + "user": { + // 1) "password" can be replaced with "hash" if you install ep_hash_auth + // 2) please note that if password is null, the user will not be created + "password": "changeme1", + "is_admin": false + } + }, + */ + + /* + * Restrict socket.io transport methods + */ + "socketTransportProtocols" : ["xhr-polling", "jsonp-polling", "htmlfile"], + + /* + * Allow Load Testing tools to hit the Etherpad Instance. + * + * WARNING: this will disable security on the instance. + */ + "loadTest": false, + + /* + * Disable indentation on new line when previous line ends with some special + * chars (':', '[', '(', '{') + */ + + /* + "indentationOnNewLine": false, + */ + + /* + * From Etherpad 1.8.3 onwards, import and export of pads is always rate + * limited. + * + * The default is to allow at most 10 requests per IP in a 90 seconds window. + * After that the import/export request is rejected. + * + * See https://github.com/nfriedly/express-rate-limit for more options + */ + "importExportRateLimiting": { + // duration of the rate limit window (milliseconds) + "windowMs": 90000, + + // maximum number of requests per IP to allow during the rate limit window + "max": 10 + }, + + /* + * From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported + * file is always bounded. + * + * File size is specified in bytes. Default is 50 MB. + */ + "importMaxFileSize": 52428800, // 50 * 1024 * 1024 + + /* + * Toolbar buttons configuration. + * + * Uncomment to customize. + */ + + + "toolbar": { + "left": [ + ["bold", "italic", "underline", "strikethrough"], + ["orderedlist", "unorderedlist", "indent", "outdent"], + ["undo", "redo"] + //*LAMS* commented out the following two lines + //, + //["clearauthorship"] + ], + "right": [ + //*LAMS* commented out the following two lines + //["importexport", "timeslider", "savedrevision"], + //["settings", "embed"], + ["timeslider"], + ["showusers"] + ], + "timeslider": [ + ["timeslider_export", "timeslider_returnToPad"] + ] + }, + + + /* + * Expose Etherpad version in the web interface and in the Server http header. + * + * Do not enable on production machines. + */ + "exposeVersion": false, + + /* + * The log level we are using. + * + * Valid values: DEBUG, INFO, WARN, ERROR + */ + "loglevel": "INFO", + + /* + * Logging configuration. See log4js documentation for further information: + * https://github.com/nomiddlename/log4js-node + * + * You can add as many appenders as you want here. + */ + "logconfig" : + { "appenders": [ + { "type": "console" + //, "category": "access"// only logs pad access + } + + /* + , { "type": "file" + , "filename": "your-log-file-here.log" + , "maxLogSize": 1024 + , "backups": 3 // how many log files there're gonna be at max + //, "category": "test" // only log a specific category + } + */ + + /* + , { "type": "logLevelFilter" + , "level": "warn" // filters out all log messages that have a lower level than "error" + , "appender": + { Use whatever appender you want here } + } + */ + + /* + , { "type": "logLevelFilter" + , "level": "error" // filters out all log messages that have a lower level than "error" + , "appender": + { "type": "smtp" + , "subject": "An error occurred in your EPL instance!" + , "recipients": "bar@blurdybloop.com, baz@blurdybloop.com" + , "sendInterval": 300 // 60 * 5 = 5 minutes -- will buffer log messages; set to 0 to send a mail for every message + , "transport": "SMTP", "SMTP": { // see https://github.com/andris9/Nodemailer#possible-transport-methods + "host": "smtp.example.com", "port": 465, + "secureConnection": true, + "auth": { + "user": "foo@example.com", + "pass": "bar_foo" + } + } + } + } + */ + + ] + } // logconfig +} Index: lams_tool_doku/conf/etherpad-lite/src/node/db/AuthorManager.js =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r74b4aef858c62df885b2b55c25127e683b875dfd --- lams_tool_doku/conf/etherpad-lite/src/node/db/AuthorManager.js (.../AuthorManager.js) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ lams_tool_doku/conf/etherpad-lite/src/node/db/AuthorManager.js (.../AuthorManager.js) (revision 74b4aef858c62df885b2b55c25127e683b875dfd) @@ -18,259 +18,225 @@ * limitations under the License. */ - -var ERR = require("async-stacktrace"); -var db = require("./DB").db; +var db = require("./DB"); var customError = require("../utils/customError"); var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; exports.getColorPalette = function(){ - //*LAMS* modified the following array: replaced dark colors with the more lighter ones - return ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#ecbcbc", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#bcc8ec", "#dcc9ef", "#efc9e6", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd", "#c9e1d9", "#12d1ad", "#d5e8e5", "#d5daed", "#a091c7", "#c1dae5", "#e0d0f0", "#e6e76d", "#e3bfd0", "#f386e5", "#4ecc0c", "#c0c236", "#d2c1bd", "#b5de6a", "#9b88fd", "#c2dde1", "#c8d3c0", "#e267fe", "#f1c0cc", "#babad0", "#cde3c2", "#f7d2f1", "#86dc6c", "#b5a714", "#dfcdd2", "#ebd4e6", "#4b81c8", "#c4d2cd", "#c6c9b9", "#d16084", "#efe1ce", "#8c8bd8"]; + //*LAMS* modified the following array: replaced dark colors with the more lighter ones + return ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#ecbcbc", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#bcc8ec", "#dcc9ef", "#efc9e6", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd", "#c9e1d9", "#12d1ad", "#d5e8e5", "#d5daed", "#a091c7", "#c1dae5", "#e0d0f0", "#e6e76d", "#e3bfd0", "#f386e5", "#4ecc0c", "#c0c236", "#d2c1bd", "#b5de6a", "#9b88fd", "#c2dde1", "#c8d3c0", "#e267fe", "#f1c0cc", "#babad0", "#cde3c2", "#f7d2f1", "#86dc6c", "#b5a714", "#dfcdd2", "#ebd4e6", "#4b81c8", "#c4d2cd", "#c6c9b9", "#d16084", "#efe1ce", "#8c8bd8"]; }; /** * Checks if the author exists */ -exports.doesAuthorExists = function (authorID, callback) +exports.doesAuthorExist = async function(authorID) { - //check if the database entry of this author exists - db.get("globalAuthor:" + authorID, function (err, author) - { - if(ERR(err, callback)) return; - callback(null, author != null); - }); + let author = await db.get("globalAuthor:" + authorID); + + return author !== null; } +/* exported for backwards compatibility */ +exports.doesAuthorExists = exports.doesAuthorExist; + /** - * Returns the AuthorID for a token. - * @param {String} token The token - * @param {Function} callback callback (err, author) + * Returns the AuthorID for a token. + * @param {String} token The token */ -exports.getAuthor4Token = function (token, callback) +exports.getAuthor4Token = async function(token) { - mapAuthorWithDBKey("token2author", token, function(err, author) - { - if(ERR(err, callback)) return; - //return only the sub value authorID - callback(null, author ? author.authorID : author); - }); + let author = await mapAuthorWithDBKey("token2author", token); + + // return only the sub value authorID + return author ? author.authorID : author; } /** - * Returns the AuthorID for a mapper. + * Returns the AuthorID for a mapper. * @param {String} token The mapper * @param {String} name The name of the author (optional) - * @param {Function} callback callback (err, author) */ -exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback) +exports.createAuthorIfNotExistsFor = async function(authorMapper, name) { - mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author) - { - if(ERR(err, callback)) return; - - //set the name of this author - if(name) - exports.setAuthorName(author.authorID, name); - - //return the authorID - callback(null, author); - }); -} + let author = await mapAuthorWithDBKey("mapper2author", authorMapper); + if (name) { + // set the name of this author + await exports.setAuthorName(author.authorID, name); + } + + return author; +}; + /** * Returns the AuthorID for a mapper. We can map using a mapperkey, * so far this is token2author and mapper2author - * @param {String} mapperkey The database key name for this mapper + * @param {String} mapperkey The database key name for this mapper * @param {String} mapper The mapper - * @param {Function} callback callback (err, author) */ -function mapAuthorWithDBKey (mapperkey, mapper, callback) -{ - //try to map to an author - db.get(mapperkey + ":" + mapper, function (err, author) - { - if(ERR(err, callback)) return; - - //there is no author with this mapper, so create one - if(author == null) - { - exports.createAuthor(null, function(err, author) - { - if(ERR(err, callback)) return; - - //create the token2author relation - db.set(mapperkey + ":" + mapper, author.authorID); - - //return the author - callback(null, author); - }); - } - //there is a author with this mapper - else - { - //update the timestamp of this author - db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime()); - - //return the author - callback(null, {authorID: author}); - } - }); +async function mapAuthorWithDBKey (mapperkey, mapper) +{ + // try to map to an author + let author = await db.get(mapperkey + ":" + mapper); + + if (author === null) { + // there is no author with this mapper, so create one + let author = await exports.createAuthor(null); + + // create the token2author relation + await db.set(mapperkey + ":" + mapper, author.authorID); + + // return the author + return author; + } + + // there is an author with this mapper + // update the timestamp of this author + await db.setSub("globalAuthor:" + author, ["timestamp"], Date.now()); + + // return the author + return { authorID: author}; } /** - * Internal function that creates the database entry for an author - * @param {String} name The name of the author + * Internal function that creates the database entry for an author + * @param {String} name The name of the author */ -exports.createAuthor = function(name, callback) +exports.createAuthor = function(name) { - //create the new author name - var author = "a." + randomString(16); - - //create the globalAuthors db entry - var authorObj = {"colorId" : Math.floor(Math.random()*(exports.getColorPalette().length)), "name": name, "timestamp": new Date().getTime()}; - - //set the global author db entry + // create the new author name + let author = "a." + randomString(16); + + // create the globalAuthors db entry + let authorObj = { + "colorId": Math.floor(Math.random() * (exports.getColorPalette().length)), + "name": name, + "timestamp": Date.now() + }; + + // set the global author db entry + // NB: no await, since we're not waiting for the DB set to finish db.set("globalAuthor:" + author, authorObj); - - callback(null, {authorID: author}); + + return { authorID: author }; } /** * Returns the Author Obj of the author * @param {String} author The id of the author - * @param {Function} callback callback(err, authorObj) */ -exports.getAuthor = function (author, callback) +exports.getAuthor = function(author) { - db.get("globalAuthor:" + author, callback); + // NB: result is already a Promise + return db.get("globalAuthor:" + author); } - - /** * Returns the color Id of the author * @param {String} author The id of the author - * @param {Function} callback callback(err, colorId) */ -exports.getAuthorColorId = function (author, callback) +exports.getAuthorColorId = function(author) { - db.getSub("globalAuthor:" + author, ["colorId"], callback); + return 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 - * @param {Function} callback (optional) */ -exports.setAuthorColorId = function (author, colorId, callback) +exports.setAuthorColorId = function(author, colorId) { - db.setSub("globalAuthor:" + author, ["colorId"], colorId, callback); + return db.setSub("globalAuthor:" + author, ["colorId"], colorId); } /** * Returns the name of the author * @param {String} author The id of the author - * @param {Function} callback callback(err, name) */ -exports.getAuthorName = function (author, callback) +exports.getAuthorName = function(author) { - db.getSub("globalAuthor:" + author, ["name"], callback); + return 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 - * @param {Function} callback (optional) */ -exports.setAuthorName = function (author, name, callback) +exports.setAuthorName = function(author, name) { - db.setSub("globalAuthor:" + author, ["name"], name, callback); + return db.setSub("globalAuthor:" + author, ["name"], name); } /** * Returns an array of all pads this author contributed to * @param {String} author The id of the author - * @param {Function} callback (optional) */ -exports.listPadsOfAuthor = function (authorID, callback) +exports.listPadsOfAuthor = async function(authorID) { /* 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 */ - //get the globalAuthor - db.get("globalAuthor:" + authorID, function(err, author) - { - if(ERR(err, callback)) return; - //author does not exists - if(author == null) - { - callback(new customError("authorID does not exist","apierror")) - } - //everything is fine, return the pad IDs - else - { - var pads = []; - if(author.padIDs != null) - { - for (var padId in author.padIDs) - { - pads.push(padId); - } - } - callback(null, {padIDs: pads}); - } - }); + // get the globalAuthor + let author = await db.get("globalAuthor:" + authorID); + + if (author === null) { + // author does not exist + throw new customError("authorID does not exist", "apierror"); + } + + // everything is fine, return the pad IDs + let padIDs = Object.keys(author.padIDs || {}); + + return { padIDs }; } /** * Adds a new pad to the list of contributions * @param {String} author The id of the author * @param {String} padID The id of the pad the author contributes to */ -exports.addPad = function (authorID, padID) +exports.addPad = async function(authorID, padID) { - //get the entry - db.get("globalAuthor:" + authorID, function(err, author) - { - if(ERR(err)) return; - if(author == null) return; - - //the entry doesn't exist so far, let's create it - if(author.padIDs == null) - { - author.padIDs = {}; - } - - //add the entry for this pad - author.padIDs[padID] = 1;// anything, because value is not used - - //save the new element back - db.set("globalAuthor:" + authorID, author); - }); + // get the entry + let author = await db.get("globalAuthor:" + authorID); + + if (author === null) return; + + /* + * ACHTUNG: padIDs can also be undefined, not just null, so it is not possible + * to perform a strict check here + */ + if (!author.padIDs) { + // the entry doesn't exist so far, let's create it + author.padIDs = {}; + } + + // add the entry for this pad + author.padIDs[padID] = 1; // anything, because value is not used + + // save the new element back + db.set("globalAuthor:" + authorID, author); } /** * Removes a pad from the list of contributions * @param {String} author The id of the author * @param {String} padID The id of the pad the author contributes to */ -exports.removePad = function (authorID, padID) +exports.removePad = async function(authorID, padID) { - db.get("globalAuthor:" + authorID, function (err, author) - { - if(ERR(err)) return; - if(author == null) return; - - if(author.padIDs != null) - { - //remove pad from author - delete author.padIDs[padID]; - db.set("globalAuthor:" + authorID, author); - } - }); + let author = await db.get("globalAuthor:" + authorID); + + if (author === null) return; + + if (author.padIDs !== null) { + // remove pad from author + delete author.padIDs[padID]; + db.set("globalAuthor:" + authorID, author); + } } Fisheye: Tag 74b4aef858c62df885b2b55c25127e683b875dfd refers to a dead (removed) revision in file `lams_tool_doku/conf/etherpad-lite/src/node/db/SecurityManager.js'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 74b4aef858c62df885b2b55c25127e683b875dfd refers to a dead (removed) revision in file `lams_tool_doku/conf/etherpad-lite/src/node/utils/Settings.js'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_tool_doku/conf/etherpad-lite/src/static/js/ace2_inner.js =================================================================== diff -u -r7a970d9f598f46bd1e556157d63c6525e2d2d111 -r74b4aef858c62df885b2b55c25127e683b875dfd --- lams_tool_doku/conf/etherpad-lite/src/static/js/ace2_inner.js (.../ace2_inner.js) (revision 7a970d9f598f46bd1e556157d63c6525e2d2d111) +++ lams_tool_doku/conf/etherpad-lite/src/static/js/ace2_inner.js (.../ace2_inner.js) (revision 74b4aef858c62df885b2b55c25127e683b875dfd) @@ -292,6 +292,7 @@ { if ((typeof author) != "string") { + top.console.error("Going to throw new error, potentially caused by: https://github.com/ether/etherpad-lite/issues/2802"); throw new Error("setAuthorInfo: author (" + author + ") is not a string"); } if (!info) @@ -343,7 +344,7 @@ { if (dynamicCSS) { - /** *LAMS* Prevent highlighted line overlapping. This code sets padding too high. + /* *LAMS* Prevent highlighted line overlapping. This code sets padding too high. var backgroundHeight = lineMetricsDiv.offsetHeight; var lineHeight = textLineHeight(); var extraBodding = 0; @@ -357,7 +358,7 @@ spanStyle.paddingTop = extraTodding + "px"; spanStyle.paddingBottom = extraBodding + "px"; } - **/ + */ } function fadeColor(colorCSS, fadeFrac) @@ -627,15 +628,6 @@ }); }, 0); - // Chrome can't handle the truth.. If CSS rule white-space:pre-wrap - // is true then any paste event will insert two lines.. - // Sadly this will mean you get a walking Caret in Chrome when clicking on a URL - // So this has to be set to pre-wrap ;( - // We need to file a bug w/ the Chromium team. - if(browser.chrome){ - $("#innerdocbody").addClass("noprewrap"); - } - } function setStyled(newVal) @@ -3376,8 +3368,7 @@ { try { - var newWindow = window.open(n.href, '_blank'); - newWindow.focus(); + window.open(n.href, '_blank', 'noopener,noreferrer'); } catch (e) { Index: lams_tool_doku/conf/etherpad-lite/src/static/js/pad.js =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r74b4aef858c62df885b2b55c25127e683b875dfd --- lams_tool_doku/conf/etherpad-lite/src/static/js/pad.js (.../pad.js) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ lams_tool_doku/conf/etherpad-lite/src/static/js/pad.js (.../pad.js) (revision 74b4aef858c62df885b2b55c25127e683b875dfd) @@ -1,5 +1,5 @@ /** - * This code is mostly from the old Etherpad. Please help us to comment this code. + * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED */ @@ -74,7 +74,7 @@ var getParameters = [ { name: "noColors", checkVal: "true", callback: function(val) { settings.noColors = true; $('#clearAuthorship').hide(); } }, { name: "showControls", checkVal: "false", callback: function(val) { $('#editbar').addClass('hideControlsEditbar'); $('#editorcontainer').addClass('hideControlsEditor'); } }, - { name: "showChat", checkVal: "false", callback: function(val) { $('#chaticon').hide(); } }, + { name: "showChat", checkVal: "true", callback: function(val) { $('#chaticon').show(); } }, { name: "showLineNumbers", checkVal: "false", callback: function(val) { settings.LineNumbersDisabled = true; } }, { name: "useMonospaceFont", checkVal: "true", callback: function(val) { settings.useMonospaceFontGlobal = true; } }, // If the username is set as a parameter we should set a global value that we can call once we have initiated the pad. @@ -99,15 +99,15 @@ setting.callback(value); } } - + // Then URL applied stuff var params = getUrlVars() - + for(var i = 0; i < getParameters.length; i++) { var setting = getParameters[i]; var value = params[setting.name]; - + if(value && (value == setting.checkVal || setting.checkVal == null)) { setting.callback(value); @@ -156,7 +156,7 @@ token = "t." + randomString(); createCookie("token", token, 60); } - + var sessionID = decodeURIComponent(readCookie("sessionID")); var password = readCookie("password"); @@ -169,14 +169,14 @@ "token": token, "protocolVersion": 2 }; - + //this is a reconnect, lets tell the server our revisionnumber if(isReconnect == true) { msg.client_rev=pad.collabClient.getCurrentRevisionNumber(); msg.reconnect=true; } - + socket.json.send(msg); } @@ -203,20 +203,27 @@ socket.once('connect', function () { sendClientReady(false); }); - + socket.on('reconnect', function () { pad.collabClient.setChannelState("CONNECTED"); - pad.sendClientReady(true); + pad.sendClientReady(receivedClientVars); }); - + socket.on('reconnecting', function() { + pad.collabClient.setStateIdle(); + pad.collabClient.setIsPendingRevision(true); pad.collabClient.setChannelState("RECONNECTING"); }); socket.on('reconnect_failed', function(error) { pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout"); }); + socket.on('error', function(error) { + pad.collabClient.setStateIdle(); + pad.collabClient.setIsPendingRevision(true); + }); + var initalized = false; socket.on('message', function(obj) @@ -254,7 +261,7 @@ $("#passwordinput").focus(); } } - + //if we haven't recieved the clientVars yet, then this message should it be else if (!receivedClientVars && obj.type == "CLIENT_VARS") { @@ -267,7 +274,7 @@ clientVars = obj.data; clientVars.userAgent = "Anonymous"; clientVars.collab_client_vars.clientAgent = "Anonymous"; - + //initalize the pad pad._afterHandshake(); initalized = true; @@ -298,7 +305,7 @@ { pad.changeViewOption('noColors', true); } - + if (settings.rtlIsTrue == true) { pad.changeViewOption('rtlIsTrue', true); @@ -307,7 +314,7 @@ // If the Monospacefont value is set to true then change it to monospace. if (settings.useMonospaceFontGlobal == true) { - pad.changeViewOption('useMonospaceFont', true); + pad.changeViewOption('padFontFamily', 'monospace'); } // if the globalUserName value is set we need to tell the server and the client about the new authorname if (settings.globalUserName !== false) @@ -335,6 +342,12 @@ console.warn(obj); padconnectionstatus.disconnected(obj.disconnect); socket.disconnect(); + + // block user from making any change to the pad + padeditor.disable(); + padeditbar.disable(); + padimpexp.disable(); + return; } else @@ -345,13 +358,13 @@ }); // Bind the colorpicker var fb = $('#colorpicker').farbtastic({ callback: '#mycolorpickerpreview', width: 220}); - // Bind the read only button + // Bind the read only button $('#readonlyinput').on('click',function(){ padeditbar.setEmbedLinks(); }); } -$.extend($.gritter.options, { +$.extend($.gritter.options, { position: 'bottom-right', // defaults to 'top-right' but can be 'bottom-left', 'bottom-right', 'top-left', 'top-right' (added in 1.7.1) fade: false, // dont fade, too jerky on mobile time: 6000 // hang on the screen for... @@ -424,7 +437,7 @@ if(window.history && window.history.pushState) { $('#chattext p').remove(); //clear the chat messages - window.history.pushState("", "", newHref); + window.history.pushState("", "", newHref); receivedClientVars = false; sendClientReady(false, 'SWITCH_TO_PAD'); } @@ -453,7 +466,7 @@ // This will check if the prefs-cookie is set. // Otherwise it shows up a message to the user. padcookie.init(); - if (!readCookie("prefs")) + if (!padcookie.isCookiesEnabled()) { $('#loading').hide(); $('#noCookie').show(); @@ -462,7 +475,7 @@ }, _afterHandshake: function() { - pad.clientTimeOffset = new Date().getTime() - clientVars.serverTimestamp; + pad.clientTimeOffset = Date.now() - clientVars.serverTimestamp; //initialize the chat chat.init(this); getParams(); @@ -548,18 +561,8 @@ if(padcookie.getPref("rtlIsTrue") == true){ pad.changeViewOption('rtlIsTrue', true); } + pad.changeViewOption('padFontFamily', padcookie.getPref("padFontFamily")); - var fonts = ['useMonospaceFont', 'useOpenDyslexicFont', 'useComicSansFont', 'useCourierNewFont', 'useGeorgiaFont', 'useImpactFont', - 'useLucidaFont', 'useLucidaSansFont', 'usePalatinoFont', 'useTahomaFont', 'useTimesNewRomanFont', - 'useTrebuchetFont', 'useVerdanaFont', 'useSymbolFont', 'useWebdingsFont', 'useWingDingsFont', 'useSansSerifFont', - 'useSerifFont']; - - $.each(fonts, function(i, font){ - if(padcookie.getPref(font) == true){ - pad.changeViewOption(font, true); - } - }) - hooks.aCallAll("postAceInit", {ace: padeditor.ace, pad: pad}); } }, @@ -732,20 +735,20 @@ pad.diagnosticInfo.disconnectedMessage = message; pad.diagnosticInfo.padId = pad.getPadId(); pad.diagnosticInfo.socket = {}; - - //we filter non objects from the socket object and put them in the diagnosticInfo + + //we filter non objects from the socket object and put them in the diagnosticInfo //this ensures we have no cyclic data - this allows us to stringify the data for(var i in socket.socket) { var value = socket.socket[i]; var type = typeof value; - + if(type == "string" || type == "number") { pad.diagnosticInfo.socket[i] = value; } } - + pad.asyncSendDiagnosticInfo(); if (typeof window.ajlog == "string") { @@ -824,7 +827,7 @@ $.ajax( { type: 'post', - url: '/ep/pad/connection-diagnostic-info', + url: 'ep/pad/connection-diagnostic-info', data: { diagnosticInfo: JSON.stringify(pad.diagnosticInfo) }, Index: lams_tool_doku/conf/etherpad-lite/src/static/js/pad_userlist.js =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r74b4aef858c62df885b2b55c25127e683b875dfd --- lams_tool_doku/conf/etherpad-lite/src/static/js/pad_userlist.js (.../pad_userlist.js) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ lams_tool_doku/conf/etherpad-lite/src/static/js/pad_userlist.js (.../pad_userlist.js) (revision 74b4aef858c62df885b2b55c25127e683b875dfd) @@ -472,7 +472,7 @@ $("#otheruserstable tr").remove(); - //*LAMS* commented out the following paragraph + //*LAMS* commented out the following paragraph /* if (pad.getUserIsGuest()) { @@ -492,7 +492,7 @@ }, 0); }); } - */ + */ // color picker $("#myswatchbox").click(showColorPicker); @@ -812,6 +812,7 @@ function showColorPicker() { previousColorId = myUserInfo.colorId; + $.farbtastic('#colorpicker').setColor(myUserInfo.colorId) if (!colorPickerOpen) { Index: lams_tool_doku/readme.txt =================================================================== diff -u -rae0aa5d86b30cdd76b500e73fbf8b92c4f0f17eb -r74b4aef858c62df885b2b55c25127e683b875dfd --- lams_tool_doku/readme.txt (.../readme.txt) (revision ae0aa5d86b30cdd76b500e73fbf8b92c4f0f17eb) +++ lams_tool_doku/readme.txt (.../readme.txt) (revision 74b4aef858c62df885b2b55c25127e683b875dfd) @@ -1,8 +1,8 @@ -Modifications required to be done for Etherpad server (version #1.6.1): +Modifications required to be done for Etherpad server (version #1.8.0 - develop branch, snapshot made on 2020-04-16): * Install ep_resize plugin https://github.com/tiblu/ep_resize -* Copy the folder /lams_tool_doku/conf/etherpad-lite/ over to /${etherpad-lite-server-folder}/ +* Copy contents of folder /lams_tool_doku/conf/etherpad-lite/ over to /${etherpad-lite-server-folder}/ It customises Etherpad and the plugin * In order to hide Etherpad index page make the following file empty: /${etherpad-lite-server-folder}/src/templates/index.html.