''' PClinks Extension for Python-Markdown ====================================== Converts [[type:id]] to relative links. Based on . 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.utils.filters.humanize import humanize from app.models.poll import Poll from app.models.topic import Topic from app.models.user import Member from app.models.attachment import Attachment 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, 'fichier': handleFile, 'file': handleFile, 'f': handleFile, } 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
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 def handleFile(content_id, context): try: content_id = int(content_id) except ValueError: return "[Fichier invalide]" file = Attachment.query.get(content_id) if file is None: return "[Fichier inconnu]" # Build element manually to avoid code injection container = etree.Element('div') container.set('class', 'dl-button') xtitle = etree.SubElement(container, 'a') xtitle.text = file.name xtitle.set('href', file.url) xsize = etree.SubElement(container, 'span') xsize.text = humanize(file.size, unit='o') # # TLTitre.g3a # {{ file.size | humanize(unit='o') }} # return container