124 lines
3.6 KiB
Python
124 lines
3.6 KiB
Python
'''
|
|
PClinks Extension for Python-Markdown
|
|
======================================
|
|
|
|
Converts [[type:id]] to relative links.
|
|
|
|
Based on <https://Python-Markdown.github.io/extensions/wikilinks>.
|
|
|
|
Original code Copyright [Waylan Limberg](http://achinghead.com/).
|
|
|
|
License: [BSD](https://opensource.org/licenses/bsd-license.php)
|
|
'''
|
|
|
|
from markdown.extensions import Extension
|
|
from markdown.inlinepatterns import InlineProcessor
|
|
import xml.etree.ElementTree as etree
|
|
from flask import url_for, render_template
|
|
from app.utils.unicode_names import normalize
|
|
from app.models.poll import Poll
|
|
from app.models.topic import Topic
|
|
from app.models.user import Member
|
|
|
|
|
|
class PCLinkExtension(Extension):
|
|
def __init__(self, **kwargs):
|
|
self.config = {
|
|
# 'base_url': ['/', 'String to append to beginning or URL.'],
|
|
# 'end_url': ['/', 'String to append to end of URL.'],
|
|
# 'html_class': ['pclink', 'CSS hook. Leave blank for none.'],
|
|
}
|
|
super().__init__(**kwargs)
|
|
|
|
def extendMarkdown(self, md):
|
|
self.md = md
|
|
|
|
# append to end of inline patterns
|
|
PCLINK_RE = r'\[\[([a-z]+): ?(\w+)\]\]'
|
|
pclinkPattern = PCLinksInlineProcessor(PCLINK_RE, self.getConfigs())
|
|
pclinkPattern.md = md
|
|
md.inlinePatterns.register(pclinkPattern, 'pclink', 135)
|
|
|
|
|
|
class PCLinksInlineProcessor(InlineProcessor):
|
|
def __init__(self, pattern, config):
|
|
super().__init__(pattern)
|
|
self.config = config
|
|
self.handles = {
|
|
'membre': handleUser, 'user': handleUser, 'u': handleUser,
|
|
'sondage': handlePoll, 'poll': handlePoll,
|
|
'topic': handleTopic, 't': handleTopic,
|
|
}
|
|
|
|
def handleMatch(self, m, data):
|
|
link_type = m.group(1).strip()
|
|
if link_type in self.handles:
|
|
content_id = m.group(2).strip()
|
|
a = self.handles[link_type](content_id, data)
|
|
else:
|
|
a = ''
|
|
return a, m.start(0), m.end(0)
|
|
|
|
|
|
# pclinks are links defined as [[type:content_id]]
|
|
# To add a custom handle, create a function and add it to processor's handles
|
|
# A custom handle takes two arguments:
|
|
# - content_id: as defined
|
|
# - context: the block in which the link has been found
|
|
# It should return:
|
|
# - either a string, which will be html-escaped
|
|
# - either an xml.etree.ElementTree
|
|
|
|
def handlePoll(content_id, context):
|
|
if not context.startswith("[[") or not context.endswith("]]"):
|
|
return "[Sondage invalide]"
|
|
try:
|
|
id = int(content_id)
|
|
except ValueError:
|
|
return "[ID du sondage invalide]"
|
|
|
|
poll = Poll.query.get(content_id)
|
|
|
|
if poll is None:
|
|
return "[Sondage non trouvé]"
|
|
|
|
html = render_template('widgets/poll.html', poll=poll)
|
|
html = html.replace('\n', '') # Needed to avoid lots of <br> due to etree
|
|
return etree.fromstring(html)
|
|
|
|
def handleTopic(content_id, context):
|
|
try:
|
|
id = int(content_id)
|
|
except ValueError:
|
|
return "[ID du topic invalide]"
|
|
|
|
topic = Topic.query.get(content_id)
|
|
|
|
if topic is None:
|
|
return "[Topic non trouvé]"
|
|
|
|
a = etree.Element('a')
|
|
a.text = topic.title
|
|
a.set('href', url_for('forum_topic', f=topic.forum, page=(topic,1)))
|
|
a.set('class', 'topic-link')
|
|
|
|
return a
|
|
|
|
def handleUser(content_id, context):
|
|
try:
|
|
norm = normalize(content_id)
|
|
except ValueError:
|
|
return "[Nom d'utilisateur invalide]"
|
|
|
|
member = Member.query.filter_by(norm=norm).first()
|
|
|
|
if member is None:
|
|
return "[Utilisateur non trouvé]"
|
|
|
|
a = etree.Element('a')
|
|
a.text = member.name
|
|
a.set('href', url_for('user', username=member.norm))
|
|
a.set('class', 'profile-link')
|
|
|
|
return a
|