'use strict'; /** * The AuthorManager controlls all information about the Pad authors */ /* * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const db = require('./DB'); const CustomError = require('../utils/customError'); const randomString = require('../../static/js/pad_utils').randomString; exports.getColorPalette = () => [ '#ffc7c7', '#fff1c7', '#e3ffc7', '#c7ffd5', '#c7ffff', '#c7d5ff', '#e3c7ff', '#ffc7f1', '#ffa8a8', '#ffe699', '#cfff9e', '#99ffb3', '#a3ffff', '#99b3ff', '#cc99ff', '#ff99e5', '#e7b1b1', '#e9dcAf', '#cde9af', '#bfedcc', '#b1e7e7', '#c3cdee', '#d2b8ea', '#eec3e6', '#e9cece', '#e7e0ca', '#d3e5c7', '#bce1c5', '#c1e2e2', '#c1c9e2', '#cfc1e2', '#e0bdd9', '#baded3', '#a0f8eb', '#b1e7e0', '#c3c8e4', '#cec5e2', '#b1d5e7', '#cda8f0', '#f0f0a8', '#f2f2a6', '#f5a8eb', '#c5f9a9', '#ececbb', '#e7c4bc', '#daf0b2', '#b0a0fd', '#bce2e7', '#cce2bb', '#ec9afe', '#edabbd', '#aeaeea', '#c4e7b1', '#d722bb', '#f3a5e7', '#ffa8a8', '#d8c0c5', '#eaaedd', '#adc6eb', '#bedad1', '#dee9af', '#e9afc2', '#f8d2a0', '#b3b3e6', ]; /** * Checks if the author exists */ exports.doesAuthorExist = async (authorID) => { const 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 */ exports.getAuthor4Token = async (token) => { const author = await mapAuthorWithDBKey('token2author', token); // return only the sub value authorID return author ? author.authorID : author; }; /** * Returns the AuthorID for a mapper. * @param {String} token The mapper * @param {String} name The name of the author (optional) */ exports.createAuthorIfNotExistsFor = async (authorMapper, name) => { const 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} mapper The mapper */ async function mapAuthorWithDBKey(mapperkey, mapper) { // try to map to an author const author = await db.get(`${mapperkey}:${mapper}`); if (author == null) { // there is no author with this mapper, so create one const 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 */ exports.createAuthor = async (name) => { // create the new author name const author = `a.${randomString(16)}`; // create the globalAuthors db entry const authorObj = { colorId: Math.floor(Math.random() * (exports.getColorPalette().length)), name, timestamp: Date.now(), }; // set the global author db entry await db.set(`globalAuthor:${author}`, authorObj); return {authorID: author}; }; /** * Returns the Author Obj of the author * @param {String} author The id of the author */ exports.getAuthor = async (author) => 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']); /** * 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( `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']); /** * 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( `globalAuthor:${author}`, ['name'], name); /** * Returns an array of all pads this author contributed to * @param {String} author The id of the author */ exports.listPadsOfAuthor = async (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 const 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 const 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 = async (authorID, padID) => { // get the entry const 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 await 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 = async (authorID, padID) => { const author = await db.get(`globalAuthor:${authorID}`); if (author == null) return; if (author.padIDs != null) { // remove pad from author delete author.padIDs[padID]; await db.set(`globalAuthor:${authorID}`, author); } };