Skip to content

Commit

Permalink
Shift markdown parsing into helper function
Browse files Browse the repository at this point in the history
Slight changes in function in that table elements are now allowed in the footer as well as the main-narrative-markdown. This was commented as a "to-do" in the code
  • Loading branch information
jameshadfield committed Apr 28, 2020
1 parent 0d2eaf4 commit 83b71a4
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 67 deletions.
32 changes: 2 additions & 30 deletions src/components/framework/footer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import React from "react";
import { connect } from "react-redux";
import marked from "marked";
import dompurify from "dompurify";
import styled from 'styled-components';
import { withTranslation } from "react-i18next";
import { FaDownload } from "react-icons/fa";
Expand All @@ -13,6 +11,7 @@ import { version } from "../../version";
import { publications } from "../download/downloadModal";
import { isValueValid } from "../../util/globals";
import hardCodedFooters from "./footer-descriptions";
import { parseMarkdown } from "../../util/parseMarkdown";

const dot = (
<span style={{marginLeft: 10, marginRight: 10}}>
Expand Down Expand Up @@ -138,36 +137,9 @@ export const getAcknowledgments = (metadata, dispatch) => {
* Jover. December 2019.
*/
if (metadata.description) {
dompurify.addHook("afterSanitizeAttributes", (node) => {
// Set external links to open in a new tab
if ('href' in node && location.hostname !== node.hostname) {
node.setAttribute('target', '_blank');
node.setAttribute('rel', 'noreferrer nofollow');
}
// Find nodes that contain images and add imageContainer class to update styling
const nodeContainsImg = ([...node.childNodes].filter((child) => child.localName === 'img')).length > 0;
if (nodeContainsImg) {
// For special case of image links, set imageContainer on outer parent
if (node.localName === 'a') {
node.parentNode.className += ' imageContainer';
} else {
node.className += ' imageContainer';
}
}
});

const sanitizer = dompurify.sanitize;
const sanitizerConfig = {
ALLOWED_TAGS: ['div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'em', 'strong', 'del', 'ol', 'ul', 'li', 'a', 'img', '#text', 'code', 'pre', 'hr'],
ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'alt'],
KEEP_CONTENT: false,
ALLOW_DATA_ATTR: false
};

let cleanDescription;
try {
const rawDescription = marked(metadata.description);
cleanDescription = sanitizer(rawDescription, sanitizerConfig);
cleanDescription = parseMarkdown(metadata.description);
} catch (error) {
console.error(`Error parsing footer description: ${error}`);
cleanDescription = '<p>There was an error parsing the footer description.</p>';
Expand Down
39 changes: 2 additions & 37 deletions src/components/narrative/MainDisplayMarkdown.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React from "react";
import { connect } from "react-redux";
import marked from "marked";
import styled from 'styled-components';
import dompurify from "dompurify";
import { dataFont } from "../../globalStyles";

import { parseMarkdown } from "../../util/parseMarkdown";

/**
* The following code borrows heavily from the Footer
Expand Down Expand Up @@ -105,7 +103,7 @@ const Container = styled.div`
`;

const EXPERIMENTAL_MainDisplayMarkdown = ({narrativeBlock, width, mobile}) => {
const cleanHTML = mdToHtml(narrativeBlock.mainDisplayMarkdown);
const cleanHTML = parseMarkdown(narrativeBlock.mainDisplayMarkdown);
return (
<Container width={width} mobile={mobile}>
<div
Expand All @@ -120,36 +118,3 @@ const EXPERIMENTAL_MainDisplayMarkdown = ({narrativeBlock, width, mobile}) => {
export default connect((state) => ({
narrativeBlock: state.narrative.blocks[state.narrative.blockIdx]
}))(EXPERIMENTAL_MainDisplayMarkdown);

function mdToHtml(md) {
/* this is copy & pasted from `../framework/footer.js` and should be abstracted
into a function */
dompurify.addHook("afterSanitizeAttributes", (node) => {
// Set external links to open in a new tab
if ('href' in node && location.hostname !== node.hostname) {
node.setAttribute('target', '_blank');
node.setAttribute('rel', 'noreferrer nofollow');
}
// Find nodes that contain images and add imageContainer class to update styling
const nodeContainsImg = ([...node.childNodes].filter((child) => child.localName === 'img')).length > 0;
if (nodeContainsImg) {
// For special case of image links, set imageContainer on outer parent
if (node.localName === 'a') {
node.parentNode.className += ' imageContainer';
} else {
node.className += ' imageContainer';
}
}
});

const sanitizer = dompurify.sanitize;
const sanitizerConfig = {
ALLOWED_TAGS: ['div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'em', 'strong', 'del', 'ol', 'ul', 'li', 'a', 'img', '#text', 'pre', 'hr', 'table', 'thead', 'tbody', 'th', 'tr', 'td'],
ALLOWED_ATTR: ['href', 'src', 'width', 'height', 'alt'],
KEEP_CONTENT: false,
ALLOW_DATA_ATTR: false
};
const rawDescription = marked(md);
const cleanDescription = sanitizer(rawDescription, sanitizerConfig);
return cleanDescription;
}
33 changes: 33 additions & 0 deletions src/util/parseMarkdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import marked from "marked";
import dompurify from "dompurify";

dompurify.addHook("afterSanitizeAttributes", (node) => {
// Set external links to open in a new tab
if ('href' in node && location.hostname !== node.hostname) {
node.setAttribute('target', '_blank');
node.setAttribute('rel', 'noreferrer nofollow');
}
// Find nodes that contain images and add imageContainer class to update styling
const nodeContainsImg = ([...node.childNodes].filter((child) => child.localName === 'img')).length > 0;
if (nodeContainsImg) {
// For special case of image links, set imageContainer on outer parent
if (node.localName === 'a') {
node.parentNode.className += ' imageContainer';
} else {
node.className += ' imageContainer';
}
}
});

const ALLOWED_TAGS = ['div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'em', 'strong', 'del', 'ol', 'ul', 'li', 'a', 'img'];
ALLOWED_TAGS.push('#text', 'code', 'pre', 'hr', 'table', 'thead', 'tbody', 'th', 'tr', 'td');

const ALLOWED_ATTR = ['href', 'src', 'width', 'height', 'alt'];

export const parseMarkdown = (mdString) => {
const sanitizer = dompurify.sanitize;
const sanitizerConfig = {ALLOWED_TAGS, ALLOWED_ATTR, KEEP_CONTENT: false, ALLOW_DATA_ATTR: false};
const rawDescription = marked(mdString);
const cleanDescription = sanitizer(rawDescription, sanitizerConfig);
return cleanDescription;
};

0 comments on commit 83b71a4

Please sign in to comment.