Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
14 changes: 13 additions & 1 deletion cfgov/core/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@

from wagtail.rich_text import expand_db_html

from core.utils import add_link_markup, get_body_html, get_link_tags
from core.utils import (
add_cfpb_link_markup,
add_link_markup,
get_body_html,
get_cfpb_link_tags,
get_link_tags,
)


class DownstreamCacheControlMiddleware:
Expand Down Expand Up @@ -52,6 +58,12 @@ def parse_links(html, request_path=None, encoding=None):
if tag_with_markup:
expanded_html = expanded_html.replace(tag, tag_with_markup)

cfpb_link_tags = get_cfpb_link_tags(body_html)
for tag in cfpb_link_tags:
tag_with_markup = add_cfpb_link_markup(tag, request_path)
if tag_with_markup:
expanded_html = expanded_html.replace(tag, tag_with_markup)

return expanded_html


Expand Down
63 changes: 63 additions & 0 deletions cfgov/core/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
ASK_CFPB_LINKS,
NON_CFPB_LINKS,
UNSAFE_CHARACTERS,
add_cfpb_link_markup,
add_link_markup,
extract_answers_from_request,
format_file_size,
get_body_html,
get_cfpb_link_tags,
get_link_tags,
make_safe,
text_matches_href,
Expand Down Expand Up @@ -77,6 +79,61 @@ def test_format_file_size_terabytes(self):
self.assertEqual(format_file_size(1024 * 9000000000), "8 TB")


class CfpbLinkUtilsTests(SimpleTestCase):
def test_get_cfpb_link_tags(self):
self.assertEqual(
get_cfpb_link_tags(
'outer <cfpb-link><a href="">inner</a></cfpb-link>'
),
['<cfpb-link><a href="">inner</a></cfpb-link>'],
)

def test_add_cfpb_link_markup_invalid(self):
tag = "not a valid tag"
path = "/about-us/blog/"
self.assertIsNone(add_cfpb_link_markup(tag, path))

def test_add_cfpb_link_markup_anchor(self):
tag = '<cfpb-link><a href="/about-us/blog/#anchor">bar</a></cfpb-link>'
path = "/about-us/blog/"
self.assertEqual(
add_cfpb_link_markup(tag, path),
'<cfpb-link><a href="#anchor">bar</a></cfpb-link>',
)

def check_external_cfpb_link(self, url):
tag = f'<cfpb-link><a href="{url}">foo</a></cfpb-link>'
path = "/about-us/blog/"

expected_html = (
'<cfpb-link link-variant="external">'
f'<a href="{url}">foo</a></cfpb-link>'
)
expected_tag = BeautifulSoup(expected_html, "html.parser")

self.assertEqual(add_cfpb_link_markup(tag, path), str(expected_tag))

def check_download_cfpb_link(self, url):
tag = f'<cfpb-link><a href="{url}">foo</a></cfpb-link>'
path = "/about-us/blog/"

expected_html = (
'<cfpb-link link-variant="download">'
f'<a href="{url}">foo</a></cfpb-link>'
)
expected_tag = BeautifulSoup(expected_html, "html.parser")

self.assertEqual(add_cfpb_link_markup(tag, path), str(expected_tag))

def test_usa_gov(self):
url = "https://www.usa.gov"
self.check_external_cfpb_link(url)

def test_test_pdf(self):
url = "test.pdf"
self.check_download_cfpb_link(url)


class LinkUtilsTests(SimpleTestCase):
@classmethod
def setUpClass(cls):
Expand Down Expand Up @@ -138,6 +195,12 @@ def test_get_link_tags_spacing(self):
],
)

def test_get_link_tags_does_not_match_wc(self):
self.assertEqual(
get_link_tags('outer <cfpb-link><a href="">inner</a></cfpb-link>'),
[],
)

def test_add_link_markup_invalid(self):
tag = "not a valid tag"
path = "/about-us/blog/"
Expand Down
58 changes: 55 additions & 3 deletions cfgov/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
# Match <a…>…</a>
A_TAG_RE = re.compile(TAG_RE.format(tag_name="a"))

# Match <cfpb-link>…</cfpb-link>
CFPB_LINK_TAG_RE = re.compile(TAG_RE.format(tag_name="cfpb-link"))

# If a link contains these elements, it should *not* get an icon
ICONLESS_LINK_CHILD_ELEMENTS = [
"img",
Expand Down Expand Up @@ -90,6 +93,8 @@

MAX_CHARS = 75

BEAUTIFUL_SOUP_PARSER = "html.parser"


def make_safe(term):
for char in UNSAFE_CHARACTERS:
Expand Down Expand Up @@ -128,7 +133,18 @@


def get_link_tags(html):
return A_TAG_RE.findall(html)
without_components = re.sub(
r"<cfpb-link\b[^>]*>.*?</cfpb-link>",
"",
html,
flags=re.DOTALL | re.IGNORECASE,
)
links = A_TAG_RE.findall(without_components)
return links


def get_cfpb_link_tags(html):
return CFPB_LINK_TAG_RE.findall(html)


def text_matches_href(text, href):
Expand Down Expand Up @@ -177,6 +193,42 @@
return normalize((text or "").strip()) == normalize((href or "").strip())


def add_cfpb_link_markup(tag, request_path):
"""Add necessary attributes to the given cfpb-link.

Return cfpb-link if modified.
"""
soup = BeautifulSoup(tag, BEAUTIFUL_SOUP_PARSER)
tag = soup.find("cfpb-link")

if tag is None:
return None

a_tag = tag.find("a")
href = a_tag["href"]

if request_path is not None:
# Strips the path of the current page from hrefs that are internal page
# anchor links.
# TODO: Remove that functionality when we get to Wagtail>=2.7, which

Check warning on line 213 in cfgov/core/utils.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=cfpb_consumerfinance.gov&issues=AZ8-dTELFt_APKFbr48C&open=AZ8-dTELFt_APKFbr48C&pullRequest=9134
# adds the ability to create anchor links.
in_page_anchor_pattern = request_path + "#"
if a_tag["href"].startswith(in_page_anchor_pattern):
# Strip current path from in-page anchor links
a_tag["href"] = href.replace(request_path, "")
return str(tag)

if NON_CFPB_LINKS.match(href):
# Sets the icon to indicate you're leaving consumerfinance.gov
tag["link-variant"] = "external"

elif DOWNLOAD_LINKS.search(href):
# Sets the icon to indicate you're downloading a file
tag["link-variant"] = "download"

return str(tag)


def add_link_markup(tag, request_path):
"""Add necessary markup to the given link and return if modified.

Expand All @@ -190,7 +242,7 @@
"""
icon = False

soup = BeautifulSoup(tag, "html.parser")
soup = BeautifulSoup(tag, BEAUTIFUL_SOUP_PARSER)
tag = soup.find("a", href=True)

if tag is None:
Expand Down Expand Up @@ -252,7 +304,7 @@
icon_classes = {"class": LINK_ICON_TEXT_CLASSES}
spans = tag.findAll("span", icon_classes)

icon_soup = BeautifulSoup(svg_icon(icon), "html.parser")
icon_soup = BeautifulSoup(svg_icon(icon), BEAUTIFUL_SOUP_PARSER)

# If this is an <a class="a-btn"> tag without a span inside, we want to
# add proper markup so that the link appears as a button with the icon on
Expand Down
Loading
Loading