• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ephios-dev / ephios / 12294321176

12 Dec 2024 10:17AM UTC coverage: 84.862% (-0.008%) from 84.87%
12294321176

Pull #1446

github

web-flow
Merge e3cf4e820 into 6a86a2b75
Pull Request #1446: Open external links in new tab

2963 of 3511 branches covered (84.39%)

Branch coverage included in aggregate %.

13 of 15 new or added lines in 1 file covered. (86.67%)

12033 of 14160 relevant lines covered (84.98%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.8
/ephios/extra/templatetags/rich_text.py
1
from urllib.parse import urlparse
1✔
2

3
import bleach
1✔
4
import markdown
1✔
5
from bleach.linkifier import DEFAULT_CALLBACKS
1✔
6
from django import template
1✔
7
from django.conf import settings
1✔
8
from django.utils.http import url_has_allowed_host_and_scheme
1✔
9
from django.utils.safestring import mark_safe
1✔
10

11
register = template.Library()
1✔
12

13
ALLOWED_TAGS = {
1✔
14
    "a",
15
    "abbr",
16
    "acronym",
17
    "b",
18
    "blockquote",
19
    "br",
20
    "code",
21
    "div",
22
    "em",
23
    "h1",
24
    "h2",
25
    "h3",
26
    "h4",
27
    "h5",
28
    "h6",
29
    "hr",
30
    "i",
31
    "li",
32
    "ol",
33
    "p",
34
    "pre",
35
    "span",
36
    "strong",
37
    "table",
38
    "tbody",
39
    "td",
40
    "th",
41
    "thead",
42
    "tr",
43
    "ul",
44
}
45

46
ALLOWED_ATTRIBUTES = {
1✔
47
    "a": ["href", "title", "class"],
48
    "abbr": ["title"],
49
    "acronym": ["title"],
50
    "table": ["width"],
51
    "td": ["width", "align"],
52
    "div": ["class"],
53
    "p": ["class"],
54
    "span": ["class", "title"],
55
}
56

57
ALLOWED_PROTOCOLS = {"http", "https", "mailto", "tel"}
1✔
58

59

60
def markdown_compile(source, excluded_tags=""):
1✔
61
    extensions = ["markdown.extensions.sane_lists", "markdown.extensions.nl2br"]
1✔
62
    tags = ALLOWED_TAGS - set(excluded_tags.split(","))
1✔
63
    return bleach.clean(
1✔
64
        markdown.markdown(source, extensions=extensions),
65
        tags=tags,
66
        attributes=ALLOWED_ATTRIBUTES,
67
        protocols=ALLOWED_PROTOCOLS,
68
    )
69

70

71
def safelink_callback(attrs, new=False):
1✔
72
    """
73
    Links to a different domain should open with target=_blank
74
    """
75
    url = attrs.get((None, "href"), None)
1✔
76
    if url is None:
1!
NEW
77
        return attrs
×
78
    if url.startswith("mailto:") or url.startswith("tel:"):
1!
NEW
79
        return attrs
×
80
    if not url_has_allowed_host_and_scheme(
1!
81
        url,
82
        allowed_hosts=[
83
            urlparse(settings.GET_SITE_URL()).netloc,
84
        ],
85
    ):
86
        attrs[None, "target"] = "_blank"
1✔
87
        attrs[None, "rel"] = "noopener"
1✔
88
    return attrs
1✔
89

90

91
@register.filter
1✔
92
def rich_text(text: str, excluded_tags=""):
1✔
93
    """
94
    Processes markdown and cleans HTML in a text input.
95
    """
96
    text = str(text)
1✔
97
    linker = bleach.Linker(
1✔
98
        parse_email=True,
99
        callbacks=DEFAULT_CALLBACKS + [safelink_callback],
100
    )
101
    body_md = linker.linkify(markdown_compile(text, excluded_tags=excluded_tags))
1✔
102
    return mark_safe(body_md)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc