import walkRecursive from '../helpers/walkRecursive' // import { getByDot, setByDot, getItems, replaceItems } from 'feathers-hooks-common' import sanitizeHtml from 'sanitize-html' // import { isEmpty, intersection } from 'lodash' import cheerio from 'cheerio' import linkifyHtml from 'linkifyjs/html' const embedToAnchor = content => { const $ = cheerio.load(content) $('div[data-url-embed]').each((i, el) => { let url = el.attribs['data-url-embed'] let aTag = $(`${url}`) $(el).replaceWith(aTag) }) return $('body').html() } function clean(dirty) { if (!dirty) { return dirty } // Convert embeds to a-tags dirty = embedToAnchor(dirty) dirty = linkifyHtml(dirty) dirty = sanitizeHtml(dirty, { allowedTags: [ 'iframe', 'img', 'p', 'h3', 'h4', 'br', 'hr', 'b', 'i', 'em', 'strong', 'a', 'pre', 'ul', 'li', 'ol', 's', 'strike', 'span', 'blockquote', ], allowedAttributes: { a: ['href', 'class', 'target', 'data-*', 'contenteditable'], span: ['contenteditable', 'class', 'data-*'], img: ['src'], iframe: ['src', 'class', 'frameborder', 'allowfullscreen'], }, allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'], parser: { lowerCaseTags: true, }, transformTags: { iframe: function(tagName, attribs) { return { tagName: 'a', text: attribs.src, attribs: { href: attribs.src, target: '_blank', 'data-url-embed': '', }, } }, h1: 'h3', h2: 'h3', h3: 'h3', h4: 'h4', h5: 'strong', i: 'em', a: function(tagName, attribs) { return { tagName: 'a', attribs: { href: attribs.href, target: '_blank', rel: 'noopener noreferrer nofollow', }, } }, b: 'strong', s: 'strike', img: function(tagName, attribs) { let src = attribs.src if (!src) { // remove broken images return {} } // if (isEmpty(hook.result)) { // const config = hook.app.get('thumbor') // if (config && src.indexOf(config < 0)) { // // download image // // const ThumborUrlHelper = require('../helper/thumbor-helper') // // const Thumbor = new ThumborUrlHelper(config.key || null, config.url || null) // // src = Thumbor // // .setImagePath(src) // // .buildUrl('740x0') // } // } return { tagName: 'img', attribs: { // TODO: use environment variables src: `http://localhost:3050/images?url=${src}`, }, } }, }, }) // remove empty html tags and duplicated linebreaks and returns dirty = dirty // remove all tags with "space only" .replace(/<[a-z-]+>[\s]+<\/[a-z-]+>/gim, '') // remove all iframes .replace(/(]*)(>)[^>]*\/*>/gim, '') .replace(/[\n]{3,}/gim, '\n\n') .replace(/(\r\n|\n\r|\r|\n)/g, '
$1') // replace all p tags with line breaks (and spaces) only by single linebreaks // limit linebreaks to max 2 (equivalent to html "br" linebreak) .replace(/(
\s*){2,}/gim, '
') // remove additional linebreaks after p tags .replace(/<\/(p|div|th|tr)>\s*(
\s*)+\s*<(p|div|th|tr)>/gim, '

') // remove additional linebreaks inside p tags .replace(/<[a-z-]+>(<[a-z-]+>)*\s*(
\s*)+\s*(<\/[a-z-]+>)*<\/[a-z-]+>/gim, '') // remove additional linebreaks when first child inside p tags .replace(/

(\s*
\s*)+/gim, '

') // remove additional linebreaks when last child inside p tags .replace(/(\s*
\s*)+<\/p+>/gim, '

') return dirty } const fields = ['content', 'contentExcerpt'] export default { Mutation: async (resolve, root, args, context, info) => { args = walkRecursive(args, fields, clean) const result = await resolve(root, args, context, info) return result }, Query: async (resolve, root, args, context, info) => { const result = await resolve(root, args, context, info) return walkRecursive(result, fields, clean) }, }