~widelands-dev/widelands-website/trunk

« back to all changes in this revision

Viewing changes to mainpage/templatetags/wl_markdown.py

  • Committer: franku
  • Date: 2016-12-13 18:28:51 UTC
  • mto: This revision was merged to the branch mainline in revision 443.
  • Revision ID: somal@arcor.de-20161213182851-bo5ebf8pdvw5beua
run the script

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# Try to get a not so fully broken markdown module
19
19
import markdown
20
20
if markdown.version_info[0] < 2:
21
 
    raise ImportError, "Markdown library to old!"
 
21
    raise ImportError, 'Markdown library to old!'
22
22
from markdown import markdown
23
23
import re
24
24
import bleach
37
37
# We will also need the site domain
38
38
from django.contrib.sites.models import Site
39
39
from settings import SITE_ID, SMILEYS, SMILEY_DIR, \
40
 
        SMILEY_PREESCAPING, BZR_URL
 
40
    SMILEY_PREESCAPING, BZR_URL
41
41
 
42
42
try:
43
43
    _domain = Site.objects.get(pk=SITE_ID).domain
44
44
except:
45
 
    _domain = ""
 
45
    _domain = ''
46
46
 
47
47
# Getting local domain lists
48
48
try:
49
49
    from settings import LOCAL_DOMAINS as _LOCAL_DOMAINS
50
 
    LOCAL_DOMAINS = [ _domain ] + _LOCAL_DOMAINS
 
50
    LOCAL_DOMAINS = [_domain] + _LOCAL_DOMAINS
51
51
except ImportError:
52
 
    LOCAL_DOMAINS = [ _domain ]
 
52
    LOCAL_DOMAINS = [_domain]
53
53
 
54
54
 
55
55
register = template.Library()
56
56
 
57
 
def _insert_smileys( text ):
58
 
    """
59
 
    This searches for smiley symbols in the current text
60
 
    and replaces them with the correct images.
 
57
 
 
58
def _insert_smileys(text):
 
59
    """This searches for smiley symbols in the current text and replaces them
 
60
    with the correct images.
 
61
 
61
62
    Only replacing if smiley symbols aren't in a word (e.g. http://....)
 
63
 
62
64
    """
63
 
    words = text.split(" ")
64
 
    for sc,img in SMILEYS:
 
65
    words = text.split(' ')
 
66
    for sc, img in SMILEYS:
65
67
        if sc in words:
66
 
            words[words.index(sc)] = "<img src='%s%s' alt='%s' />" % ( SMILEY_DIR, img, img )
67
 
    text = " ".join(words)
 
68
            words[words.index(
 
69
                sc)] = "<img src='%s%s' alt='%s' />" % (SMILEY_DIR, img, img)
 
70
    text = ' '.join(words)
68
71
    return text
69
72
 
70
 
def _insert_smiley_preescaping( text ):
71
 
    """
72
 
    This searches for smiley symbols in the current text
73
 
    and replaces them with the correct images
74
 
    """
75
 
    for before,after in SMILEY_PREESCAPING:
76
 
        text = text.replace(before,after)
 
73
 
 
74
def _insert_smiley_preescaping(text):
 
75
    """This searches for smiley symbols in the current text and replaces them
 
76
    with the correct images."""
 
77
    for before, after in SMILEY_PREESCAPING:
 
78
        text = text.replace(before, after)
77
79
    return text
78
80
 
79
81
 
80
82
revisions_re = [
81
 
    re.compile( "bzr:r(\d+)" ),
 
83
    re.compile('bzr:r(\d+)'),
82
84
]
83
85
 
84
 
def _insert_revision( text ):
 
86
 
 
87
def _insert_revision(text):
85
88
    for r in revisions_re:
86
89
        text = r.sub( lambda m: """<a href="%s">r%s</a>""" % (
87
90
            settings.BZR_URL % m.group(1), m.group(1)), text)
88
91
    return text
89
92
 
90
 
def _classify_link( tag ):
91
 
    """
92
 
    Returns a classname to insert if this link is in any way
93
 
    special (external or missing wikipages)
 
93
 
 
94
def _classify_link(tag):
 
95
    """Returns a classname to insert if this link is in any way special
 
96
    (external or missing wikipages)
94
97
 
95
98
    tag to classify for
 
99
 
96
100
    """
97
101
    # No class change for image links
98
 
    if tag.findChild("img") != None:
 
102
    if tag.findChild('img') != None:
99
103
        return None
100
104
 
101
 
    href = tag["href"].lower()
 
105
    href = tag['href'].lower()
102
106
 
103
107
    # Check for external link
104
 
    if href.startswith("http"):
 
108
    if href.startswith('http'):
105
109
        for domain in LOCAL_DOMAINS:
106
110
            external = True
107
111
            if href.find(domain) != -1:
108
112
                external = False
109
113
                break
110
114
        if external:
111
 
            return { 'class': "externalLink", 'title': "This link refers to outer space" }
112
 
    
113
 
    if "/profile/" in (tag["href"]):
114
 
        return { 'class': "userLink", 'title': "This link refers to a userpage" }
115
 
       
116
 
 
117
 
    if check_for_missing_wikipages and href.startswith("/wiki/"):
118
 
        
 
115
            return {'class': 'externalLink', 'title': 'This link refers to outer space'}
 
116
 
 
117
    if '/profile/' in (tag['href']):
 
118
        return {'class': 'userLink', 'title': 'This link refers to a userpage'}
 
119
 
 
120
    if check_for_missing_wikipages and href.startswith('/wiki/'):
 
121
 
119
122
        # Check for missing wikilink /wiki/PageName[/additionl/stuff]
120
123
        # Using href because we need cAsEs here
121
 
        pn = tag["href"][6:].split('/',1)[0]
122
 
        
123
 
        if not len(pn): # Wiki root link is not a page
124
 
            return { 'class': "wrongLink", 'title': "This Link misses an articlename"}
125
 
            
 
124
        pn = tag['href'][6:].split('/', 1)[0]
 
125
 
 
126
        if not len(pn):  # Wiki root link is not a page
 
127
            return {'class': 'wrongLink', 'title': 'This Link misses an articlename'}
 
128
 
126
129
        # Wiki special pages are also not counted
127
 
        if pn in ["list","search","history","feeds","observe","edit" ]:
128
 
            return { 'class': "specialLink" }
129
 
        
 
130
        if pn in ['list', 'search', 'history', 'feeds', 'observe', 'edit']:
 
131
            return {'class': 'specialLink'}
 
132
 
130
133
        # Check for a redirect
131
134
        try:
132
135
            # try to get the article id; if this fails an IndexError is raised
133
 
            a_id = ChangeSet.objects.filter( old_title=pn ).values_list('article_id')[0]
134
 
            
 
136
            a_id = ChangeSet.objects.filter(
 
137
                old_title=pn).values_list('article_id')[0]
 
138
 
135
139
            # get actual title of article
136
 
            act_t = Article.objects.get( id=a_id[0] ).title
 
140
            act_t = Article.objects.get(id=a_id[0]).title
137
141
            if pn != act_t:
138
 
                return { 'title': "This is a redirect and points to \"" + act_t + "\"" }
 
142
                return {'title': "This is a redirect and points to \"" + act_t + "\""}
139
143
            else:
140
144
                return None
141
145
        except IndexError:
142
146
            pass
143
 
        
 
147
 
144
148
        # article missing (or misspelled)
145
149
        if Article.objects.filter(title=pn).count() == 0:
146
 
            return { 'class': "missingLink", 'title': "This Link is misspelled or missing. Click to create it anyway." }
 
150
            return {'class': 'missingLink', 'title': 'This Link is misspelled or missing. Click to create it anyway.'}
147
151
 
148
152
    return None
149
153
 
150
 
def _clickable_image( tag ):
 
154
 
 
155
def _clickable_image(tag):
151
156
    # is external link?
152
 
    if tag["src"].startswith("http"):
153
 
    # is allways a link?
 
157
    if tag['src'].startswith('http'):
 
158
        # is allways a link?
154
159
        if tag.parent.name != 'a':
155
160
            # add link to image
156
 
            text = "<a href=" + tag["src"] +"><img src=" + tag["src"] + "></a>"
 
161
            text = '<a href=' + tag['src'] + \
 
162
                '><img src=' + tag['src'] + '></a>'
157
163
            return text
158
164
    return None
159
165
 
162
168
    # Wikiwordification
163
169
    # Match a wiki page link LikeThis. All !WikiWords (with a !
164
170
    # in front) are ignored
165
 
    "wikiwords": (re.compile(r"(!?)(\b[A-Z][a-z]+[A-Z]\w+\b)"), lambda m:
166
 
        m.group(2) if m.group(1) == '!' else
167
 
            u"""<a href="/wiki/%(match)s">%(match)s</a>""" %
168
 
            {"match": m.group(2) }),
 
171
    'wikiwords': (re.compile(r"(!?)(\b[A-Z][a-z]+[A-Z]\w+\b)"), lambda m:
 
172
                  m.group(2) if m.group(1) == '!' else
 
173
                  u"""<a href="/wiki/%(match)s">%(match)s</a>""" %
 
174
                  {'match': m.group(2)}),
169
175
 
170
176
}
171
177
 
172
 
def do_wl_markdown( value, *args, **keyw ):
 
178
 
 
179
def do_wl_markdown(value, *args, **keyw):
173
180
    # Do Preescaping for markdown, so that some things stay intact
174
181
    # This is currently only needed for this smiley ">:-)"
175
 
    value = _insert_smiley_preescaping( value )
 
182
    value = _insert_smiley_preescaping(value)
176
183
    custom = keyw.pop('custom', True)
177
 
    html = smart_str(markdown(value, extensions=["extra","toc"], *args, **keyw))
 
184
    html = smart_str(markdown(value, extensions=[
 
185
                     'extra', 'toc'], *args, **keyw))
178
186
 
179
187
    # Sanitize posts from potencial untrusted users (Forum/Wiki/Maps)
180
188
    if 'bleachit' in args:
181
 
        html = mark_safe(bleach.clean(html, tags=BLEACH_ALLOWED_TAGS, attributes=BLEACH_ALLOWED_ATTRIBUTES))
 
189
        html = mark_safe(bleach.clean(
 
190
            html, tags=BLEACH_ALLOWED_TAGS, attributes=BLEACH_ALLOWED_ATTRIBUTES))
182
191
 
183
192
    # Since we only want to do replacements outside of tags (in general) and not between
184
193
    # <a> and </a> we partition our site accordingly
190
199
 
191
200
    for text in soup.findAll(text=True):
192
201
        # Do not replace inside a link
193
 
        if text.parent.name == "a":
 
202
        if text.parent.name == 'a':
194
203
            continue
195
204
 
196
205
        # We do our own small preprocessing of the stuff we got, after markdown
198
207
        # links [blah](blkf)
199
208
        if custom:
200
209
            # Replace bzr revisions
201
 
            rv = _insert_revision( text )
 
210
            rv = _insert_revision(text)
202
211
            # Replace smileys; only outside "code-tags"
203
 
            if not text.parent.name == "code":
204
 
                rv = _insert_smileys( rv )
 
212
            if not text.parent.name == 'code':
 
213
                rv = _insert_smileys(rv)
205
214
 
206
 
            for name, (pattern,replacement) in custom_filters.iteritems():
 
215
            for name, (pattern, replacement) in custom_filters.iteritems():
207
216
                if not len(text.strip()) or not keyw.get(name, True):
208
217
                    continue
209
218
 
210
219
                rv = pattern.sub(replacement, rv)
211
220
            text.replaceWith(rv)
212
 
 
 
221
 
213
222
    # This call slows the whole function down...
214
223
    # unicode->reparsing.
215
224
    # The function goes from .5 ms to 1.5ms on my system
217
226
    # What a waste of cycles :(
218
227
    soup = BeautifulSoup(unicode(soup))
219
228
    # We have to go over this to classify links
220
 
    for tag in soup.findAll("a"):
 
229
    for tag in soup.findAll('a'):
221
230
        rv = _classify_link(tag)
222
231
        if rv:
223
232
            for attribute in rv.iterkeys():
225
234
 
226
235
    # All external images gets clickable
227
236
    # This applies only in forum
228
 
    for tag in soup.findAll("img"):
 
237
    for tag in soup.findAll('img'):
229
238
        link = _clickable_image(tag)
230
239
        if link:
231
240
            tag.replaceWith(link)
235
244
 
236
245
@register.filter
237
246
def wl_markdown(content, arg=''):
238
 
    """
239
 
    A Filter which decides when to 'bleach' the content.
240
 
    """
 
247
    """A Filter which decides when to 'bleach' the content."""
241
248
    if arg == 'bleachit':
242
249
        return mark_safe(do_wl_markdown(content, 'bleachit'))
243
250
    else:
244
251
        return mark_safe(do_wl_markdown(content))
245