Index: lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/ep.json =================================================================== diff -u --- lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/ep.json (revision 0) +++ lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/ep.json (revision fea3e6eb1a66370c10e3ae985c276f79a3629635) @@ -0,0 +1,34 @@ +{ + "parts": [ + { + "name": "ep_image_upload", + "client_hooks": { + "postToolbarInit": "ep_image_upload/static/js/toolbar", + "aceAttribsToClasses" : "ep_image_upload/static/js/clientHooks", + "aceDomLineProcessLineAttributes" : "ep_image_upload/static/js/clientHooks", + "aceInitialized": "ep_image_upload/static/js/clientHooks", + "aceRegisterBlockElements": "ep_image_upload/static/js/clientHooks", + "aceEditorCSS": "ep_image_upload/static/js/clientHooks", + "collectContentImage": "ep_image_upload/static/js/contentCollection", + "collectContentPre": "ep_image_upload/static/js/contentCollection", + "ccRegisterBlockElements": "ep_image_upload/static/js/contentCollection", + "collectContentPost": "ep_image_upload/static/js/contentCollection" + }, + "hooks":{ + "eejsBlock_styles": "ep_image_upload/index", + "eejsBlock_timesliderStyles": "ep_image_upload/index", + "clientVars": "ep_image_upload/index", + "eejsBlock_body": "ep_image_upload/index", + "collectContentImage": "ep_image_upload/static/js/contentCollection", + "getLineHTMLForExport": "ep_image_upload/exportHTML", + "stylesForExport": "ep_image_upload/exportHTML", + "expressConfigure": "ep_image_upload/index", + "eejsBlock_editbarMenuLeft": "ep_image_upload/editbar", + "collectContentPre": "ep_image_upload/static/js/contentCollection", + "collectContentPost": "ep_image_upload/static/js/contentCollection", + "ccRegisterBlockElements": "ep_image_upload/static/js/contentCollection", + "loadSettings": "ep_image_upload/settings" + } + } + ] +} Index: lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/exportHTML.js =================================================================== diff -u --- lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/exportHTML.js (revision 0) +++ lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/exportHTML.js (revision fea3e6eb1a66370c10e3ae985c276f79a3629635) @@ -0,0 +1,27 @@ +'use strict'; + +const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + +const _analyzeLine = (alineAttrs, apool) => { + let image = null; + if (alineAttrs) { + const opIter = Changeset.opIterator(alineAttrs); + if (opIter.hasNext()) { + const op = opIter.next(); + image = Changeset.opAttributeValue(op, 'img', apool); + } + } + + return image; +}; + +exports.getLineHTMLForExport = async (hook, context) => { + const image = _analyzeLine(context.attribLine, context.apool); + if (image) { + context.lineContent = ``; + } +}; + +exports.stylesForExport = (hook, padId, cb) => { + cb('img{max-width:100%;max-height:500px}'); +}; Index: lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/index.js =================================================================== diff -u --- lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/index.js (revision 0) +++ lams_build/conf/etherpad/etherpad-lite/node_modules/ep_image_upload/index.js (revision fea3e6eb1a66370c10e3ae985c276f79a3629635) @@ -0,0 +1,172 @@ +'use strict'; + +/** + * Server-side hooks + * + * @see {@link http://etherpad.org/doc/v1.5.7/#index_server_side_hooks} + */ + +const eejs = require('ep_etherpad-lite/node/eejs/'); +const settings = require('ep_etherpad-lite/node/utils/Settings'); +const Busboy = require('busboy'); +const StreamUpload = require('stream_upload'); +const uuid = require('uuid'); +const path = require('path'); +const mimetypes = require('mime-db'); +const url = require('url'); + +/** + * ClientVars hook + * + * Exposes plugin settings from settings.json to client code inside clientVars variable + * to be accessed from client side hooks + * + * @param {string} hookName Hook name ("clientVars"). + * @param {object} args Object containing the arguments passed to hook. {pad: {object}} + * @param {function} cb Callback + * + * @returns {*} callback + * + * @see {@link http://etherpad.org/doc/v1.5.7/#index_clientvars} + */ +exports.clientVars = (hookName, args, cb) => { + const pluginSettings = { + storageType: 'base64', + }; + if (!settings.ep_image_upload) { + settings.ep_image_upload = {}; + } + const keys = Object.keys(settings.ep_image_upload); + keys.forEach((key) => { + if (key !== 'storage') { + pluginSettings[key] = settings.ep_image_upload[key]; + } + }); + if (settings.ep_image_upload.storage && settings.ep_image_upload.storage.type !== 'base64') { + pluginSettings.storageType = settings.ep_image_upload.storage.type; + } + + if (!pluginSettings) { + console.warn(hookName, + 'ep_image_upload settings not found. The settings can be specified in EP settings.json.' + ); + + return cb(); + } + pluginSettings.mimeTypes = mimetypes; + + return cb({ep_image_upload: pluginSettings}); +}; + +exports.eejsBlock_body = (hookName, args, cb) => { + const modal = eejs.require('ep_image_upload/templates/modal.ejs'); + args.content += modal; + + return cb(); +}; + +exports.eejsBlock_styles = (hookName, args, cb) => { + const style = eejs.require('ep_image_upload/templates/styles.ejs'); + args.content += style; + + return cb(); +}; + +exports.eejsBlock_timesliderStyles = (hookName, args, cb) => { + args.content += ''; + return cb(); +}; + +const drainStream = (stream) => { + stream.on('readable', stream.read.bind(stream)); +}; + +exports.expressConfigure = (hookName, context) => { + context.app.post('/p/:padId/pluginfw/ep_image_upload/upload', (req, res, next) => { + const padId = req.params.padId; + let busboy; + const imageUpload = new StreamUpload({ + extensions: settings.ep_image_upload.fileTypes, + maxSize: settings.ep_image_upload.maxFileSize, + baseFolder: settings.ep_image_upload.storage.baseFolder, + storage: settings.ep_image_upload.storage, + }); + const storageConfig = settings.ep_image_upload.storage; + if (storageConfig) { + try { + busboy = new Busboy({ + headers: req.headers, + limits: { + fileSize: settings.ep_image_upload.maxFileSize, + }, + }); + } catch (error) { + console.error('EP_IMAGE_UPLOAD ERROR', error); + + return next(error); + } + + let isDone; + const done = (error) => { + if (error) { + console.error('EP_IMAGE_UPLOAD UPLOAD ERROR', error); + + return; + } + + if (isDone) return; + isDone = true; + + res.status(error.statusCode || 500).json(error); + req.unpipe(busboy); + drainStream(req); + busboy.removeAllListeners(); + }; + + let uploadResult; + const newFileName = uuid.v4(); + let accessPath = ''; + busboy.on('file', (fieldname, file, filename, encoding, mimetype) => { + let savedFilename = path.join(padId, newFileName + path.extname(filename)); + + if (settings.ep_image_upload.storage && settings.ep_image_upload.storage.type === 'local') { + let baseURL = settings.ep_image_upload.storage.baseURL; + if (baseURL.charAt(baseURL.length - 1) !== '/') { + baseURL += '/'; + } + accessPath = new url.URL(savedFilename, settings.ep_image_upload.storage.baseURL); + savedFilename = path.join(settings.ep_image_upload.storage.baseFolder, savedFilename); + } + file.on('limit', () => { + const error = new Error('File is too large'); + error.type = 'fileSize'; + error.statusCode = 403; + busboy.emit('error', error); + imageUpload.deletePartials(); + }); + file.on('error', (error) => { + busboy.emit('error', error); + }); + + uploadResult = imageUpload + .upload(file, {type: mimetype, filename: savedFilename}); + }); + + busboy.on('error', done); + busboy.on('finish', () => { + if (uploadResult) { + uploadResult + .then((data) => { + if (accessPath) { + data = accessPath; + } + + return res.status(201).json(data); + }) + .catch((err) => res.status(500).json(err)); + } + }); + req.pipe(busboy); + } + }); +};