Blame pagure/pfmarkdown.py

Ralph Bean ad48e3
# This program is free software; you can redistribute it and/or
Ralph Bean ad48e3
# modify it under the terms of the GNU General Public License
Ralph Bean ad48e3
# as published by the Free Software Foundation; either version 2
Ralph Bean ad48e3
# of the License, or (at your option) any later version.
Ralph Bean ad48e3
#
Ralph Bean ad48e3
# This program is distributed in the hope that it will be useful,
Ralph Bean ad48e3
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Ralph Bean ad48e3
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Ralph Bean ad48e3
# GNU General Public License for more details.
Ralph Bean ad48e3
#
Ralph Bean ad48e3
# You should have received a copy of the GNU General Public License
Ralph Bean ad48e3
# along with this program; if not, write to the Free Software
Ralph Bean ad48e3
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
Ralph Bean ad48e3
# USA.
Ralph Bean ad48e3
Ralph Bean ad48e3
""" Pagure-flavored Markdown
Ralph Bean ad48e3
Ralph Bean ad48e3
Author: Ralph Bean <rbean@redhat.com></rbean@redhat.com>
Pierre-Yves Chibon 721198
        Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Ralph Bean ad48e3
"""
Ralph Bean ad48e3
Pierre-Yves Chibon 67d1cc
from __future__ import unicode_literals, absolute_import
Aurélien Bompard dcf6f6
Ralph Bean ad48e3
import flask
Ralph Bean ad48e3
Ralph Bean ad48e3
import markdown.inlinepatterns
Adam Williamson c9fff8
import markdown.preprocessors
Julen Landa Alustiza 890236
import markdown.postprocessors
Ralph Bean ad48e3
import markdown.util
Pierre-Yves Chibon be0baf
import pygit2
Adam Williamson c9fff8
import re
Aurélien Bompard 831553
import six
Ralph Bean ad48e3
Pierre-Yves Chibon 930073
import pagure.lib.query
Pierre-Yves Chibon b130e5
from pagure.config import config as pagure_config
Ralph Bean 6ac06e
Ralph Bean ad48e3
Pierre-Yves Chibon 8ed43b
try:
Pierre-Yves Chibon 8ed43b
    from markdown.inlinepatterns import ImagePattern as ImagePattern
Pierre-Yves Chibon 8ed43b
Pierre-Yves Chibon 8ed43b
    MK_VERSION = 2
Pierre-Yves Chibon 8ed43b
except ImportError:
Pierre-Yves Chibon 8ed43b
    from markdown.inlinepatterns import ImageInlineProcessor as ImagePattern
Pierre-Yves Chibon 8ed43b
Pierre-Yves Chibon 8ed43b
    MK_VERSION = 3
Pierre-Yves Chibon 8ed43b
Pierre-Yves Chibon 8ed43b
Adam Williamson 53d919
# the (?
Adam Williamson 53d919
# negative lookbehind assertion. It means 'match when the preceding
Adam Williamson 53d919
# character is not in the \w class'. This stops us from starting a
Adam Williamson 53d919
# match in the middle of a word (e.g. someone@something in the
Adam Williamson 53d919
# MENTION_RE regex). Note that it is a zero-length match - it does
Adam Williamson 53d919
# not capture or consume any of the string - and it does not appear
Adam Williamson 53d919
# as a group for the match object.
Pierre-Yves Chibon 3ff584
MENTION_RE = r"(?
Pierre-Yves Chibon e11381
# Each line below correspond to a line of the regex:
Adam Williamson 53d919
#  1) Don't start matching in the middle of a word
Pierre-Yves Chibon e11381
#  2) See if there is a `forks/` at the start
Pierre-Yves Chibon e11381
#  3) See if we have a `user/`
Pierre-Yves Chibon e11381
#  4) See if we have a `namespace/`
Pierre-Yves Chibon e11381
#  5) Get the last part `project`
Pierre-Yves Chibon e11381
#  6) Get the identifier `#<id>`</id>
Pierre-Yves Chibon 9c2953
EXPLICIT_LINK_RE = (
Pierre-Yves Chibon 9c2953
    r"(?
Pierre-Yves Chibon 9c2953
    r"(fork[s]?/)?"
Pierre-Yves Chibon 9c2953
    r"([a-zA-Z0-9_-]*?/)?"
Pierre-Yves Chibon 9c2953
    r"([a-zA-Z0-9_-]*?/)?"
Pierre-Yves Chibon 9c2953
    r"([a-zA-Z0-9_-]+)"
Pierre-Yves Chibon 9c2953
    r"#(?P<id>[0-9]+)"</id>
Pierre-Yves Chibon 9c2953
)
Pierre-Yves Chibon 9c2953
COMMIT_LINK_RE = (
Pierre-Yves Chibon 9c2953
    r"(?
Pierre-Yves Chibon 9c2953
    r"(fork[s]?/)?"
Pierre-Yves Chibon 9c2953
    r"([a-zA-Z0-9_-]*?/)?"
Pierre-Yves Chibon 9c2953
    r"([a-zA-Z0-9_-]*?/)?"
Pierre-Yves Chibon 9c2953
    r"([a-zA-Z0-9_-]+)"
Pierre-Yves Chibon 9c2953
    r"#(?P<id>[\w]{40})"</id>
Pierre-Yves Chibon 9c2953
)
Adam Williamson c9fff8
# PREPROCIMPLLINK is used by ImplicitIssuePreprocessor to replace the
Adam Williamson c9fff8
# '#' when a line starts with an implicit issue link, to prevent
Adam Williamson c9fff8
# markdown parsing it as a header; we have to handle it here
Pierre-Yves Chibon 9c2953
IMPLICIT_ISSUE_RE = r"(?
Pierre-Yves Chibon 9c2953
IMPLICIT_PR_RE = r"(?
Pierre-Yves Chibon 9c2953
IMPLICIT_COMMIT_RE = r"(?
Pierre-Yves Chibon 9c2953
STRIKE_THROUGH_RE = r"~~(.*?)~~"
Pierre-Yves Chibon b8c7ae
Pierre-Yves Chibon b8c7ae
Pierre-Yves Chibon 721198
class MentionPattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon 721198
    """ @user pattern class. """
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
    def handleMatch(self, m):
Pierre-Yves Chibon 721198
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon 98e061
        name = markdown.util.AtomicString(m.group(2))
Pierre-Yves Chibon 9c2953
        text = "@%s" % name
Pierre-Yves Chibon 930073
        user = pagure.lib.query.search_user(flask.g.session, username=name)
Pierre-Yves Chibon 721198
        if not user:
Pierre-Yves Chibon 721198
            return text
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
        element = markdown.util.etree.Element("a")
Pierre-Yves Chibon 9c2953
        base_url = pagure_config["APP_URL"]
Pierre-Yves Chibon 9c2953
        if base_url.endswith("/"):
Pierre-Yves Chibon 477d8f
            base_url = base_url[:-1]
Pierre-Yves Chibon 9c2953
        url = "%s/user/%s" % (base_url, user.username)
Pierre-Yves Chibon 9c2953
        element.set("href", url)
Pierre-Yves Chibon 721198
        element.text = text
Pierre-Yves Chibon 721198
        return element
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 10eda2
class ExplicitLinkPattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon 10eda2
    """ Explicit link pattern. """
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
    def handleMatch(self, m):
Pierre-Yves Chibon 721198
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon 10eda2
        is_fork = m.group(2)
Pierre-Yves Chibon 10eda2
        user = m.group(3)
Pierre-Yves Chibon 10eda2
        namespace = m.group(4)
Pierre-Yves Chibon 10eda2
        repo = m.group(5)
Pierre-Yves Chibon 10eda2
        idx = m.group(6)
Pierre-Yves Chibon 9c2953
        text = "%s#%s" % (repo, idx)
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 10eda2
        if not is_fork and user:
Pierre-Yves Chibon 10eda2
            namespace = user
Pierre-Yves Chibon 10eda2
            user = None
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 10eda2
        if namespace:
Pierre-Yves Chibon 9c2953
            namespace = namespace.rstrip("/")
Pierre-Yves Chibon 9c2953
            text = "%s/%s" % (namespace, text)
Pierre-Yves Chibon 10eda2
        if user:
Pierre-Yves Chibon 9c2953
            user = user.rstrip("/")
Pierre-Yves Chibon 9c2953
            text = "%s/%s" % (user.rstrip("/"), text)
Pierre-Yves Chibon 721198
Pierre-Yves Chibon ac8d80
        try:
Pierre-Yves Chibon ac8d80
            idx = int(idx)
Abhijeet Kasurde f4bf50
        except (ValueError, TypeError):
Pierre-Yves Chibon ac8d80
            return text
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 10eda2
        issue = _issue_exists(user, namespace, repo, idx)
Pierre-Yves Chibon 6c7986
        if issue:
Pierre-Yves Chibon 10eda2
            return _obj_anchor_tag(user, namespace, repo, issue, text)
Pierre-Yves Chibon 6c7986
Pierre-Yves Chibon 10eda2
        request = _pr_exists(user, namespace, repo, idx)
Pierre-Yves Chibon 6c7986
        if request:
Pierre-Yves Chibon 10eda2
            return _obj_anchor_tag(user, namespace, repo, request, text)
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 6c7986
        return text
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
Pierre-Yves Chibon dfd818
class CommitLinkPattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon dfd818
    """ Commit link pattern. """
Pierre-Yves Chibon dfd818
Pierre-Yves Chibon dfd818
    def handleMatch(self, m):
Pierre-Yves Chibon dfd818
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon dfd818
        is_fork = m.group(2)
Pierre-Yves Chibon dfd818
        user = m.group(3)
Pierre-Yves Chibon dfd818
        namespace = m.group(4)
Pierre-Yves Chibon dfd818
        repo = m.group(5)
Pierre-Yves Chibon dfd818
        commitid = m.group(6)
Pierre-Yves Chibon 9c2953
        text = "%s#%s" % (repo, commitid)
Pierre-Yves Chibon dfd818
Pierre-Yves Chibon dfd818
        if not is_fork and user:
Pierre-Yves Chibon dfd818
            namespace = user
Pierre-Yves Chibon dfd818
            user = None
Pierre-Yves Chibon dfd818
Pierre-Yves Chibon dfd818
        if namespace:
Pierre-Yves Chibon 9c2953
            namespace = namespace.rstrip("/")
Pierre-Yves Chibon 9c2953
            text = "%s/%s" % (namespace, text)
Pierre-Yves Chibon dfd818
        if user:
Pierre-Yves Chibon 9c2953
            user = user.rstrip("/")
Pierre-Yves Chibon 9c2953
            text = "%s/%s" % (user.rstrip("/"), text)
Pierre-Yves Chibon dfd818
Pierre-Yves Chibon 930073
        if pagure.lib.query.search_projects(
Pierre-Yves Chibon 9c2953
            flask.g.session,
Pierre-Yves Chibon 9c2953
            username=user,
Pierre-Yves Chibon 9c2953
            fork=is_fork,
Pierre-Yves Chibon 9c2953
            namespace=namespace,
Pierre-Yves Chibon 9c2953
            pattern=repo,
Pierre-Yves Chibon 9c2953
        ):
Pierre-Yves Chibon dfd818
            return _obj_anchor_tag(user, namespace, repo, commitid, text)
Pierre-Yves Chibon dfd818
Pierre-Yves Chibon dfd818
        return text
Pierre-Yves Chibon dfd818
Pierre-Yves Chibon dfd818
Adam Williamson c9fff8
class ImplicitIssuePreprocessor(markdown.preprocessors.Preprocessor):
Adam Williamson c9fff8
    """
Adam Williamson c9fff8
    Preprocessor which handles lines starting with an implicit
Adam Williamson c9fff8
    link. We have to modify these so that markdown doesn't interpret
Adam Williamson c9fff8
    them as headers.
Adam Williamson c9fff8
    """
Pierre-Yves Chibon 9c2953
Adam Williamson c9fff8
    def run(self, lines):
Adam Williamson c9fff8
        """
Adam Williamson c9fff8
        If a line starts with an implicit issue link like #152,
Adam Williamson c9fff8
        we replace the # with PREPROCIMPLLINK. This prevents markdown
Adam Williamson c9fff8
        parsing the line as a header. ImplicitIssuePattern will catch
Adam Williamson c9fff8
        and parse the text later. Otherwise, we change nothing.
Adam Williamson c9fff8
        """
Adam Williamson c9fff8
        # match a # character, then any number of digits
Pierre-Yves Chibon 9c2953
        regex = re.compile(r"#([0-9]+)")
Adam Williamson c9fff8
        new_lines = []
Adam Williamson c9fff8
        for line in lines:
Adam Williamson c9fff8
            # avoid calling the regex if line doesn't start with #
Pierre-Yves Chibon 9c2953
            if line.startswith("#"):
Adam Williamson c9fff8
                match = regex.match(line)
Adam Williamson c9fff8
                if match:
Adam Williamson c9fff8
                    idx = int(match.group(1))
Adam Williamson c9fff8
                    # we have to check if this is a real issue or PR now.
Adam Williamson c9fff8
                    # we can't just 'tag' the text somehow and leave it to
Adam Williamson c9fff8
                    # the pattern to check, as if it's *not* one we want
Adam Williamson c9fff8
                    # the line treated as a header, so we need the block
Adam Williamson c9fff8
                    # processor to see it unmodified.
Adam Williamson c9fff8
                    try:
Adam Williamson c9fff8
                        namespace, repo, user = _get_ns_repo_user()
Adam Williamson c9fff8
                    except RuntimeError:
Adam Williamson c9fff8
                        # non-match path, keep original line
Adam Williamson c9fff8
                        new_lines.append(line)
Adam Williamson c9fff8
                        continue
Pierre-Yves Chibon 9c2953
                    if _issue_exists(user, namespace, repo, idx) or _pr_exists(
Pierre-Yves Chibon 9c2953
                        user, namespace, repo, idx
Adam Williamson c9fff8
                    ):
Adam Williamson c9fff8
                        # tweak the text
Pierre-Yves Chibon 9c2953
                        new_lines.append("PREPROCIMPLLINK" + line[1:])
Adam Williamson c9fff8
                        continue
Adam Williamson c9fff8
            # this is a non-match path, keep original line
Adam Williamson c9fff8
            new_lines.append(line)
Adam Williamson c9fff8
            continue
Adam Williamson c9fff8
        return new_lines
Adam Williamson c9fff8
Pierre-Yves Chibon 1b30cb
Pierre-Yves Chibon 721198
class ImplicitIssuePattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon 721198
    """ Implicit issue pattern. """
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
    def handleMatch(self, m):
Pierre-Yves Chibon 721198
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon 721198
        idx = markdown.util.AtomicString(m.group(2))
Pierre-Yves Chibon 9c2953
        text = "#%s" % idx
Pierre-Yves Chibon ac8d80
        try:
Pierre-Yves Chibon ac8d80
            idx = int(idx)
Abhijeet Kasurde f4bf50
        except (ValueError, TypeError):
Pierre-Yves Chibon ac8d80
            return text
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 89dcc4
        try:
Pierre-Yves Chibon 17a1bf
            namespace, repo, user = _get_ns_repo_user()
Pierre-Yves Chibon 89dcc4
        except RuntimeError:
Pierre-Yves Chibon 89dcc4
            return text
Pierre-Yves Chibon aa74ce
Pierre-Yves Chibon 10eda2
        issue = _issue_exists(user, namespace, repo, idx)
Pierre-Yves Chibon 3549f5
        if issue:
Pierre-Yves Chibon 10eda2
            return _obj_anchor_tag(user, namespace, repo, issue, text)
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 10eda2
        request = _pr_exists(user, namespace, repo, idx)
Pierre-Yves Chibon 3549f5
        if request:
Pierre-Yves Chibon 10eda2
            return _obj_anchor_tag(user, namespace, repo, request, text)
Pierre-Yves Chibon 3549f5
Pierre-Yves Chibon 3549f5
        return text
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 1a226f
class ImplicitPRPattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon 1a226f
    """ Implicit pull-request pattern. """
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 1a226f
    def handleMatch(self, m):
Pierre-Yves Chibon 1a226f
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon 1a226f
        idx = markdown.util.AtomicString(m.group(2))
Pierre-Yves Chibon 9c2953
        text = "PR#%s" % idx
Pierre-Yves Chibon ac8d80
        try:
Pierre-Yves Chibon ac8d80
            idx = int(idx)
Abhijeet Kasurde f4bf50
        except (ValueError, TypeError):
Pierre-Yves Chibon ac8d80
            return text
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 89dcc4
        try:
Pierre-Yves Chibon 17a1bf
            namespace, repo, user = _get_ns_repo_user()
Pierre-Yves Chibon 89dcc4
        except RuntimeError:
Pierre-Yves Chibon 89dcc4
            return text
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 10eda2
        issue = _issue_exists(user, namespace, repo, idx)
Pierre-Yves Chibon 1a226f
        if issue:
Pierre-Yves Chibon 10eda2
            return _obj_anchor_tag(user, namespace, repo, issue, text)
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 10eda2
        request = _pr_exists(user, namespace, repo, idx)
Pierre-Yves Chibon 1a226f
        if request:
Pierre-Yves Chibon 10eda2
            return _obj_anchor_tag(user, namespace, repo, request, text)
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 1a226f
        return text
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 1a226f
Pierre-Yves Chibon 82e2f3
class ImplicitCommitPattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon 82e2f3
    """ Implicit commit pattern. """
Pierre-Yves Chibon 82e2f3
Pierre-Yves Chibon 82e2f3
    def handleMatch(self, m):
Pierre-Yves Chibon 82e2f3
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon 82e2f3
Pierre-Yves Chibon 82e2f3
        githash = markdown.util.AtomicString(m.group(2))
Pierre-Yves Chibon 9c2953
        text = "%s" % githash
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 82e2f3
        try:
Pierre-Yves Chibon 17a1bf
            namespace, repo, user = _get_ns_repo_user()
Pierre-Yves Chibon 82e2f3
        except RuntimeError:
Pierre-Yves Chibon 82e2f3
            return text
Pierre-Yves Chibon 30383f
Pierre-Yves Chibon 930073
        if pagure.lib.query.search_projects(
Pierre-Yves Chibon 9c2953
            flask.g.session, username=user, namespace=namespace, pattern=repo
Pierre-Yves Chibon 9c2953
        ) and _commit_exists(user, namespace, repo, githash):
Clement Verna 90bb3b
            return _obj_anchor_tag(user, namespace, repo, githash, text[:7])
Pierre-Yves Chibon 82e2f3
Pierre-Yves Chibon 82e2f3
        return text
Pierre-Yves Chibon 82e2f3
Pierre-Yves Chibon 82e2f3
Pierre-Yves Chibon e7a276
class StrikeThroughPattern(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon e7a276
    """ ~~striked~~ pattern class. """
Pierre-Yves Chibon e7a276
Pierre-Yves Chibon e7a276
    def handleMatch(self, m):
Pierre-Yves Chibon e7a276
        """ When the pattern matches, update the text. """
Pierre-Yves Chibon e7a276
        text = markdown.util.AtomicString(m.group(2))
Pierre-Yves Chibon e7a276
Pierre-Yves Chibon e7a276
        element = markdown.util.etree.Element("del")
Pierre-Yves Chibon e7a276
        element.text = text
Pierre-Yves Chibon e7a276
        return element
Pierre-Yves Chibon e7a276
Pierre-Yves Chibon e7a276
Pierre-Yves Chibon ded859
class AutolinkPattern2(markdown.inlinepatterns.Pattern):
Pierre-Yves Chibon ded859
    """ Return a link Element given an autolink (`<http: com="" example="">`). """</http:>
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon ded859
    def handleMatch(self, m):
Pierre-Yves Chibon ded859
        """ When the pattern matches, update the text.
Pierre-Yves Chibon ded859
Pierre-Yves Chibon ded859
        :arg m: the matched object
Pierre-Yves Chibon ded859
Pierre-Yves Chibon ded859
        """
Pierre-Yves Chibon ded859
        url = m.group(2)
Pierre-Yves Chibon 9c2953
        if url.startswith("<"):
Pierre-Yves Chibon ded859
            url = url[1:]
Pierre-Yves Chibon 9c2953
        if url.endswith(">"):
Pierre-Yves Chibon ded859
            url = url[:-1]
Pierre-Yves Chibon ded859
        el = markdown.util.etree.Element("a")
Pierre-Yves Chibon 9c2953
        el.set("href", self.unescape(url))
Pierre-Yves Chibon ded859
        el.text = markdown.util.AtomicString(url)
Pierre-Yves Chibon ded859
        return el
Pierre-Yves Chibon ded859
Pierre-Yves Chibon ded859
Pierre-Yves Chibon 8ed43b
class ImagePatternLazyLoad(ImagePattern):
Pierre-Yves Chibon 9f532e
    """ Customize the image element matched for lazyloading. """
Pierre-Yves Chibon 9f532e
Pierre-Yves Chibon 8ed43b
    def handleMatch(self, m, *args):
Pierre-Yves Chibon 8ed43b
        out = super(ImagePatternLazyLoad, self).handleMatch(m, *args)
Pierre-Yves Chibon 8ed43b
        if MK_VERSION == 3:
Pierre-Yves Chibon 8ed43b
            el = out[0]
Pierre-Yves Chibon 8ed43b
        else:
Pierre-Yves Chibon 8ed43b
            el = out
Pierre-Yves Chibon f5dd6e
Pierre-Yves Chibon f5dd6e
        # Add a noscript tag with the untouched img tag
Pierre-Yves Chibon f5dd6e
        noscript = markdown.util.etree.Element("noscript")
Pierre-Yves Chibon f5dd6e
        noscript.append(el)
Pierre-Yves Chibon f5dd6e
Pierre-Yves Chibon f5dd6e
        # Modify the origina img tag
Pierre-Yves Chibon f5dd6e
        img = markdown.util.etree.Element("img")
Pierre-Yves Chibon 9c2953
        img.set("data-src", el.get("src"))
Pierre-Yves Chibon 9c2953
        img.set("src", "")
Pierre-Yves Chibon 9c2953
        img.set("alt", el.get("alt"))
Pierre-Yves Chibon 9c2953
        img.set("class", "lazyload")
Pierre-Yves Chibon f5dd6e
Pierre-Yves Chibon f5dd6e
        # Create a global span in which we add both the new img tag and the
Pierre-Yves Chibon f5dd6e
        # noscript one
Pierre-Yves Chibon f5dd6e
        outel = markdown.util.etree.Element("span")
Pierre-Yves Chibon f5dd6e
        outel.append(img)
Pierre-Yves Chibon f5dd6e
        outel.append(noscript)
Pierre-Yves Chibon f5dd6e
Pierre-Yves Chibon 8ed43b
        output = outel
Pierre-Yves Chibon 8ed43b
        if MK_VERSION == 3:
Pierre-Yves Chibon 8ed43b
            output = (outel, out[1], out[2])
Pierre-Yves Chibon 8ed43b
Pierre-Yves Chibon 8ed43b
        return output
Pierre-Yves Chibon 9f532e
Pierre-Yves Chibon 9f532e
Julen Landa Alustiza 890236
class EncapsulateMarkdownPostprocessor(markdown.postprocessors.Postprocessor):
Julen Landa Alustiza 890236
    def run(self, text):
Julen Landa Alustiza 890236
        return '
' + text + "
"
Julen Landa Alustiza 890236
Julen Landa Alustiza 890236
Pierre-Yves Chibon 721198
class PagureExtension(markdown.extensions.Extension):
Pierre-Yves Chibon 721198
    def extendMarkdown(self, md, md_globals):
Pierre-Yves Chibon 721198
        # First, make it so that bare links get automatically linkified.
Pierre-Yves Chibon 9c2953
        AUTOLINK_RE = "(%s)" % "|".join(
Pierre-Yves Chibon 9c2953
            [
Pierre-Yves Chibon 9c2953
                r"<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^>]*)>",
Pierre-Yves Chibon 9c2953
                r"\b(?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^)<>\s]+[^.,)<>\s]",
Pierre-Yves Chibon 9c2953
                r"<(Ii][Rr][Cc][Ss]?://[^>]*)>",
Pierre-Yves Chibon 9c2953
                r"\b[Ii][Rr][Cc][Ss]?://[^)<>\s]+[^.,)<>\s]",
Pierre-Yves Chibon 9c2953
            ]
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon ded859
        markdown.inlinepatterns.AUTOLINK_RE = AUTOLINK_RE
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 9c2953
        md.preprocessors["implicit_issue"] = ImplicitIssuePreprocessor()
Adam Williamson c9fff8
Pierre-Yves Chibon 9c2953
        md.inlinePatterns["mention"] = MentionPattern(MENTION_RE)
Pierre-Yves Chibon 82e2f3
Pierre-Yves Chibon 9f532e
        # Customize the image linking to support lazy loading
Pierre-Yves Chibon 9f532e
        md.inlinePatterns["image_link"] = ImagePatternLazyLoad(
Pierre-Yves Chibon 9c2953
            markdown.inlinepatterns.IMAGE_LINK_RE, md
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
        md.inlinePatterns["implicit_commit"] = ImplicitCommitPattern(
Pierre-Yves Chibon 9c2953
            IMPLICIT_COMMIT_RE
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9c2953
        md.inlinePatterns["commit_links"] = CommitLinkPattern(COMMIT_LINK_RE)
Pierre-Yves Chibon 9c2953
        md.inlinePatterns["autolink"] = AutolinkPattern2(AUTOLINK_RE, md)
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
        if pagure_config.get("ENABLE_TICKETS", True):
Pierre-Yves Chibon 9c2953
            md.inlinePatterns["implicit_pr"] = ImplicitPRPattern(
Pierre-Yves Chibon 9c2953
                IMPLICIT_PR_RE
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 9c2953
            md.inlinePatterns["explicit_fork_issue"] = ExplicitLinkPattern(
Pierre-Yves Chibon 9c2953
                EXPLICIT_LINK_RE
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 9c2953
            md.inlinePatterns["implicit_issue"] = ImplicitIssuePattern(
Pierre-Yves Chibon 9c2953
                IMPLICIT_ISSUE_RE
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 9c2953
        md.inlinePatterns["striked"] = StrikeThroughPattern(STRIKE_THROUGH_RE)
Pierre-Yves Chibon e7a276
Julen Landa Alustiza 890236
        md.postprocessors["encapsulate"] = EncapsulateMarkdownPostprocessor()
Julen Landa Alustiza 890236
Pierre-Yves Chibon 721198
        md.registerExtension(self)
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
Pierre-Yves Chibon 721198
def makeExtension(*arg, **kwargs):
Pierre-Yves Chibon 721198
    return PagureExtension(**kwargs)
Ralph Bean ad48e3
Ralph Bean ad48e3
Pierre-Yves Chibon 10eda2
def _issue_exists(user, namespace, repo, idx):
Pierre-Yves Chibon b8c7ae
    """ Utility method checking if a given issue exists. """
Farhaan Bukhsh 72e9db
Pierre-Yves Chibon 930073
    repo_obj = pagure.lib.query.get_authorized_project(
Pierre-Yves Chibon 9c2953
        flask.g.session, project_name=repo, user=user, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Farhaan Bukhsh 72e9db
Ralph Bean debf63
    if not repo_obj:
Ralph Bean debf63
        return False
Ralph Bean debf63
Pierre-Yves Chibon 930073
    issue_obj = pagure.lib.query.search_issues(
Pierre-Yves Chibon 9c2953
        flask.g.session, repo=repo_obj, issueid=idx
Pierre-Yves Chibon 9c2953
    )
Ralph Bean debf63
    if not issue_obj:
Ralph Bean debf63
        return False
Ralph Bean debf63
Pierre-Yves Chibon 3549f5
    return issue_obj
Ralph Bean debf63
Ralph Bean debf63
Pierre-Yves Chibon 10eda2
def _pr_exists(user, namespace, repo, idx):
Pierre-Yves Chibon f4a3af
    """ Utility method checking if a given PR exists. """
Pierre-Yves Chibon 930073
    repo_obj = pagure.lib.query.get_authorized_project(
Pierre-Yves Chibon 9c2953
        flask.g.session, project_name=repo, user=user, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Farhaan Bukhsh 72e9db
Pierre-Yves Chibon 3549f5
    if not repo_obj:
Pierre-Yves Chibon 3549f5
        return False
Pierre-Yves Chibon 3549f5
Pierre-Yves Chibon 930073
    pr_obj = pagure.lib.query.search_pull_requests(
Pierre-Yves Chibon 9c2953
        flask.g.session, project_id=repo_obj.id, requestid=idx
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 3549f5
    if not pr_obj:
Pierre-Yves Chibon 3549f5
        return False
Pierre-Yves Chibon 3549f5
Pierre-Yves Chibon 3549f5
    return pr_obj
Pierre-Yves Chibon 3549f5
Pierre-Yves Chibon 3549f5
Pierre-Yves Chibon be0baf
def _commit_exists(user, namespace, repo, githash):
Pierre-Yves Chibon be0baf
    """ Utility method checking if a given commit exists. """
Pierre-Yves Chibon 930073
    repo_obj = pagure.lib.query.get_authorized_project(
Pierre-Yves Chibon 9c2953
        flask.g.session, project_name=repo, user=user, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon be0baf
    if not repo_obj:
Pierre-Yves Chibon be0baf
        return False
Pierre-Yves Chibon be0baf
Pierre-Yves Chibon b130e5
    reponame = pagure.utils.get_repo_path(repo_obj)
Pierre-Yves Chibon be0baf
    git_repo = pygit2.Repository(reponame)
Pierre-Yves Chibon be0baf
    return githash in git_repo
Pierre-Yves Chibon be0baf
Pierre-Yves Chibon be0baf
Pierre-Yves Chibon 10eda2
def _obj_anchor_tag(user, namespace, repo, obj, text):
Jeremy Cline e1630c
    """
Jeremy Cline e1630c
    Utility method generating the link to an issue or a PR.
Jeremy Cline e1630c
Jeremy Cline e1630c
    :return: An element tree containing the href to the issue or PR
Jeremy Cline e1630c
    :rtype:  xml.etree.ElementTree.Element
Jeremy Cline e1630c
    """
Aurélien Bompard 831553
    if isinstance(obj, six.string_types):
Pierre-Yves Chibon dfd818
        url = flask.url_for(
Pierre-Yves Chibon 9c2953
            "ui_ns.view_commit",
Pierre-Yves Chibon 9c2953
            username=user,
Pierre-Yves Chibon 9c2953
            namespace=namespace,
Pierre-Yves Chibon 9c2953
            repo=repo,
Pierre-Yves Chibon 9c2953
            commitid=obj,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9c2953
        title = "Commit %s" % obj
Pierre-Yves Chibon 9c2953
    elif obj.isa == "issue":
Pierre-Yves Chibon 3549f5
        url = flask.url_for(
Pierre-Yves Chibon 9c2953
            "ui_ns.view_issue",
Pierre-Yves Chibon 9c2953
            username=user,
Pierre-Yves Chibon 9c2953
            namespace=namespace,
Pierre-Yves Chibon 9c2953
            repo=repo,
Pierre-Yves Chibon 9c2953
            issueid=obj.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 859a72
        if obj.private:
Pierre-Yves Chibon 9c2953
            title = "Private issue"
Pierre-Yves Chibon 859a72
        else:
Pierre-Yves Chibon f8a683
            if obj.status:
Pierre-Yves Chibon 9c2953
                title = "[%s] %s" % (obj.status, obj.title)
Pierre-Yves Chibon f8a683
            else:
Pierre-Yves Chibon f8a683
                title = obj.title
Pierre-Yves Chibon 3549f5
    else:
Pierre-Yves Chibon 3549f5
        url = flask.url_for(
Pierre-Yves Chibon 9c2953
            "ui_ns.request_pull",
Pierre-Yves Chibon 9c2953
            username=user,
Pierre-Yves Chibon 9c2953
            namespace=namespace,
Pierre-Yves Chibon 9c2953
            repo=repo,
Pierre-Yves Chibon 9c2953
            requestid=obj.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon f8a683
        if obj.status:
Pierre-Yves Chibon 9c2953
            title = "[%s] %s" % (obj.status, obj.title)
Pierre-Yves Chibon f8a683
        else:
Pierre-Yves Chibon f8a683
            title = obj.title
Pierre-Yves Chibon 3549f5
Pierre-Yves Chibon b8c7ae
    element = markdown.util.etree.Element("a")
Pierre-Yves Chibon 9c2953
    element.set("href", url)
Pierre-Yves Chibon 9c2953
    element.set("title", title)
Pierre-Yves Chibon b8c7ae
    element.text = text
Pierre-Yves Chibon b8c7ae
    return element
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 17a1bf
def _get_ns_repo_user():
Pierre-Yves Chibon 17a1bf
    """ Return the namespace, repo, user corresponding to the given request
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 17a1bf
    :return: A tuple of three string corresponding to namespace, repo, user
Pierre-Yves Chibon 17a1bf
    :rtype: tuple(str, str, str)
Pierre-Yves Chibon 17a1bf
    """
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 17a1bf
    root = flask.request.url_root
Pierre-Yves Chibon 17a1bf
    url = flask.request.url
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 96be15
    user = flask.request.args.get("user") or None
Pierre-Yves Chibon 96be15
    namespace = flask.request.args.get("namespace") or None
Pierre-Yves Chibon 96be15
    repo = flask.request.args.get("repo") or None
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 17a1bf
    if not user and not repo:
Pierre-Yves Chibon 9c2953
        if "fork/" in url:
Pierre-Yves Chibon 9c2953
            user, ext = url.split("fork/")[1].split("/", 1)
Pierre-Yves Chibon 17a1bf
        else:
Pierre-Yves Chibon 17a1bf
            ext = url.split(root)[1]
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 9c2953
        if ext.count("/") >= 3:
Pierre-Yves Chibon 9c2953
            namespace, repo = ext.split("/", 2)[:2]
Pierre-Yves Chibon 17a1bf
        else:
Pierre-Yves Chibon 9c2953
            repo = ext.split("/", 1)[0]
Pierre-Yves Chibon 17a1bf
Pierre-Yves Chibon 17a1bf
    return (namespace, repo, user)