Blob Blame History Raw
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
# USA.

""" Pagure-flavored Markdown

Author: Ralph Bean <rbean@redhat.com>
"""

import flask

import markdown.inlinepatterns
import markdown.util

import pagure
import pagure.lib


def inject():
    """ Hack out python-markdown to do the autolinking that we want. """

    # First, make it so that bare links get automatically linkified.
    markdown.inlinepatterns.AUTOLINK_RE = '(%s)' % '|'.join([
        r'<(?:f|ht)tps?://[^>]*>',
        r'\b(?:f|ht)tps?://[^)<>\s]+[^.,)<>\s]',
        r'\bwww\.[^)<>\s]+[^.,)<>\s]',
        r'[^(<\s]+\.(?:com|net|org)\b',
    ])

    # Second, build some Pattern objects for @mentions, #bugs, etc...
    class MentionPattern(markdown.inlinepatterns.Pattern):
        def handleMatch(self, m):
            name = markdown.util.AtomicString(m.group(2))
            text = ' @%s' % name
            user = pagure.lib.search_user(pagure.SESSION, username=name)
            if not user:
                return text

            el = markdown.util.etree.Element("a")
            url = flask.url_for('view_user', username=name)
            el.set('href', url)
            el.text = text
            return el

    class ExplicitForkIssuePattern(markdown.inlinepatterns.Pattern):
        def handleMatch(self, m):
            user = markdown.util.AtomicString(m.group(2))
            repo = markdown.util.AtomicString(m.group(3))
            idx = markdown.util.AtomicString(m.group(4))
            text = '%s/%s#%s' % (user, repo, idx)

            if not _issue_exists(user, repo, idx):
                return text

            return _issue_anchor_tag(user, repo, idx, text)

    class ExplicitMainIssuePattern(markdown.inlinepatterns.Pattern):
        def handleMatch(self, m):
            repo = markdown.util.AtomicString(m.group(2))
            idx = markdown.util.AtomicString(m.group(3))
            text = ' %s#%s' % (repo, idx)

            if not _issue_exists(None, repo, idx):
                return text

            return _issue_anchor_tag(None, repo, idx, text)

    class ImplicitIssuePattern(markdown.inlinepatterns.Pattern):
        def handleMatch(self, m):
            idx = markdown.util.AtomicString(m.group(2))
            text = ' #%s' % idx

            root = flask.request.url_root
            url = flask.request.url
            user = None
            if 'fork/' in flask.request.url:
                user, repo = url.split('fork/')[1].split('/', 2)[:2]
            else:
                repo = url.split(root)[1].split('/', 1)[0]

            if not _issue_exists(user, repo, idx):
                return text

            return _issue_anchor_tag(user, repo, idx, text)

    MENTION_RE = r'[^|\w]@(\w+)'
    EXPLICIT_FORK_ISSUE_RE = r'(\w+)/(\w+)#([0-9]+)'
    EXPLICIT_MAIN_ISSUE_RE = r'[^|\w](?<!\/)(\w+)#([0-9]+)'
    IMPLICIT_ISSUE_RE = r'[^|\w](?<!\w)#([0-9]+)'

    # Lastly, monkey-patch the build_inlinepatterns func to insert our patterns
    original_builder = markdown.build_inlinepatterns

    def extended_builder(m, **kwargs):
        patterns = original_builder(m, **kwargs)
        patterns['mention'] = MentionPattern(MENTION_RE, m)
        patterns['explicit_fork_issue'] = ExplicitForkIssuePattern(
            EXPLICIT_FORK_ISSUE_RE, m)
        patterns['explicit_main_issue'] = ExplicitMainIssuePattern(
            EXPLICIT_MAIN_ISSUE_RE, m)
        patterns['implicit_issue'] = ImplicitIssuePattern(IMPLICIT_ISSUE_RE, m)
        return patterns

    markdown.build_inlinepatterns = extended_builder


def _issue_exists(user, repo, idx):
    repo_obj = pagure.lib.get_project(
        pagure.SESSION, name=repo, user=user)
    if not repo_obj:
        return False

    issue_obj = pagure.lib.search_issues(
        pagure.SESSION, repo=repo_obj, issueid=idx)
    if not issue_obj:
        return False

    return True


def _issue_anchor_tag(user, repo, idx, text):
    el = markdown.util.etree.Element("a")
    url = flask.url_for('view_issue', username=user, repo=repo, issueid=idx)
    el.set('href', url)
    el.text = text
    return el