diff --git a/packages/article-skeleton/src/article-skeleton.js b/packages/article-skeleton/src/article-skeleton.js index c70ae7380fb..0c80ff727c6 100644 --- a/packages/article-skeleton/src/article-skeleton.js +++ b/packages/article-skeleton/src/article-skeleton.js @@ -47,6 +47,7 @@ import insertDropcapIntoAST from "./contentModifiers/dropcap-util"; import insertNewsletterPuff from "./contentModifiers/newsletter-puff"; import insertInlineAd from "./contentModifiers/inline-ad"; import { getIsLiveOrBreakingFlag } from "./data-helper"; +import shouldIncludeDisclaimer from "./contentModifiers/should-include-disclaimer"; export const reduceArticleContent = (content, reducers) => content && @@ -149,12 +150,15 @@ const ArticleSkeleton = ({ const articleUrl = hostName && canonicalUrl ? `${hostName}${canonicalUrl}` : url; + //tu const articleContentReducers = [ + shouldIncludeDisclaimer, insertDropcapIntoAST(template, dropcapsDisabled), insertNewsletterPuff(section, isPreview, expirableFlags), insertInlineAd, tagLastParagraph ]; + //salje u article body const newContent = reduceArticleContent(content, articleContentReducers); const HeaderAdContainer = getHeaderAdStyles(template); diff --git a/packages/article-skeleton/src/contentModifiers/setExternalLinkTargets.js b/packages/article-skeleton/src/contentModifiers/setExternalLinkTargets.js new file mode 100644 index 00000000000..1131f4d3bee --- /dev/null +++ b/packages/article-skeleton/src/contentModifiers/setExternalLinkTargets.js @@ -0,0 +1,51 @@ +const contentChildren = [ + { + name: 'paragraph', + children: [ + { + name: 'link', + attributes: { + href: 'https://www.example.com', + }, + children: [], + }, + ], + }, +]; + +export const setExternalLinkTargets = (children) => { + const clonedChildren = [...children]; + const checkAndSetLinkTarget = (elements) => { + elements.forEach((el) => { + // Check if element is a link or an interactive element with a URL + if ( + (el.name === 'link' || (el.name === 'interactive' && el.attributes?.element?.value === 'times-travel-cta')) + ) { + const href = el.name === 'interactive' + ? el.attributes?.element?.attributes?.url ?? '' + : el.attributes?.href ?? ''; + + // If the link is external, set target to _blank + if (href && !href.startsWith('https://www.thetimes.co.uk') && !href.startsWith('https://www.thetimes.com')) { + el.attributes = { + ...el.attributes, + target: '_blank', + }; + } + } + + // Recursively check for nested children + if (el.children && el.children.length) { + checkAndSetLinkTarget(el.children); + } + }); + }; + + checkAndSetLinkTarget(clonedChildren); + return clonedChildren; +}; + +const updatedChildren = setExternalLinkTargets(contentChildren); +console.log(updatedChildren); + +export default setExternalLinkTargets; diff --git a/packages/article-skeleton/src/contentModifiers/should-include-disclaimer.js b/packages/article-skeleton/src/contentModifiers/should-include-disclaimer.js new file mode 100644 index 00000000000..1adaff817d4 --- /dev/null +++ b/packages/article-skeleton/src/contentModifiers/should-include-disclaimer.js @@ -0,0 +1,101 @@ +import setExternalLinkTargets from './setExternalLinkTargets'; + +const newDisclaimerText = { + name: 'paragraph', + children: [ + { + name: 'italic', + children: [ + { + name: 'text', + children: [], + attributes: { + value: 'All recommendations within this article are informed by expert editorial opinion. If you click on a link in this story we may earn affiliate revenue.' + } + } + ] + } + ] +} + + +const oldDisclaimerTexts = [ + 'This article contains affiliate links that can earn us revenue', + 'This article contains affiliate links that may earn us revenue', + 'This article contains affiliate links which may earn us revenue', + 'This article contains affiliate links, which can earn us revenue', + 'This article contains affiliate links, which may earn us revenue', + 'This article contains affiliate links, which may earn�us revenue', + 'This article contains affiliate links, which�may earn us revenue', + 'This article contains�affiliate links, which may earn us revenue', + 'This article contains affiliate links', + 'This article contains links that can earn us revenue', + 'This article contains links which may earn us revenue', + 'This article contains links, which can earn us revenue', + 'This article contains links, which may earn us revenu', + 'This article contains links from which we may earn revenue', +]; + +const shouldIncludeDisclaimer = (children) => { + const clonedChildren = [...children]; + let affiliateLinkExist = false; + let affiliateDisclaimerExist = false; + const checkForAffiliate = (elements) => { // eslint-disable-next-line array-callback-return, consistent-return + elements.some((el) => { + // Check if disclaimer text already exists. + if ( + !affiliateDisclaimerExist && + (el.name === 'text' || + (el.name === 'interactive' && el.attributes?.element?.value === 'times-text-collapse')) + ) { + const text = (el.name === 'interactive') ? (el.attributes?.element?.attributes?.disclaimer_text ?? '') : (el.attributes?.value ?? ''); + if (oldDisclaimerTexts.some((contentText) => text.includes(contentText))) { + affiliateDisclaimerExist = true; + } + } + if (affiliateDisclaimerExist) { + return true; + } + // Move to next iteration if element is not link, and doesn't have children. + if ( + el.name !== 'interactive' && + el.name !== 'link' && + (el.children === undefined || !el.children.length) + ) { + return false; + } + // Check for affiliate links. + if ( + !affiliateLinkExist && + ((el.name === 'interactive' && el.attributes?.element?.value === 'times-travel-cta') || + el.name === 'link') + ) { + const href = (el.name === 'interactive') ? (el.attributes?.element?.attributes?.url ?? '') : (el.attributes?.href ?? ''); + if ( + !href || + href.startsWith('https://www.thetimes.co.uk') || + href.startsWith('https://www.thetimes.com') + ) { + return false; + } + affiliateLinkExist = true; + } + // Check recursively for nested children. + if (el.children !== undefined && el.children.length) { + checkForAffiliate(el.children); + } + }); + return { + affiliateLinkExist, + affiliateDisclaimerExist + }; + } + const affiliatesCheck = checkForAffiliate(clonedChildren); + // Add disclaimer after first paragraph (in case disclaimer doesn't already exist). + if (!affiliatesCheck.affiliateDisclaimerExist && affiliatesCheck.affiliateLinkExist) { + const firstParagraph = clonedChildren.find((el) => el.name === 'paragraph'); + clonedChildren.splice(clonedChildren.indexOf(firstParagraph) + 1, 0, newDisclaimerText); + } + return clonedChildren; +} +export default shouldIncludeDisclaimer;