From ad48e375651ece817684fe592b2c8d73cc84a45e Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 26 2015 14:03:12 +0000 Subject: [PATCH 1/7] Pagure-flavored markdown. --- diff --git a/pagure/__init__.py b/pagure/__init__.py index 1910fd9..090b053 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -343,6 +343,10 @@ def get_repo_path(repo): return repopath +# Install our markdown modifications +import pagure.pfmarkdown +pagure.pfmarkdown.inject() + # Import the application import pagure.ui.app import pagure.ui.admin diff --git a/pagure/pfmarkdown.py b/pagure/pfmarkdown.py new file mode 100644 index 0000000..1834f0d --- /dev/null +++ b/pagure/pfmarkdown.py @@ -0,0 +1,79 @@ +# 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 +""" + +import flask + +import markdown.inlinepatterns +import markdown.util + + +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): + el = markdown.util.etree.Element("a") + name = markdown.util.AtomicString(m.group(2)) + el.set('href', _user_url(name[1:])) + el.text = name + return el + + class ExplicitIssuePattern(markdown.inlinepatterns.Pattern): + def handleMatch(self, m): + el = markdown.util.etree.Element("a") + user = markdown.util.AtomicString(m.group(2)) + repo = markdown.util.AtomicString(m.group(3)) + idx = markdown.util.AtomicString(m.group(4)) + el.set('href', _issue_url(user, repo, idx)) + el.text = '%s/%s#%s' % (user, repo, idx) + return el + + MENTION_RE = r'(@\w+)' + EXPLICIT_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(md_instance, **kwargs): + patterns = original_builder(md_instance, **kwargs) + patterns['mention'] = MentionPattern( + MENTION_RE, md_instance) + patterns['explicit_issue'] = ExplicitIssuePattern( + EXPLICIT_ISSUE_RE, md_instance) + return patterns + + markdown.build_inlinepatterns = extended_builder + + +def _user_url(name): + return flask.url_for('view_user', username=name) + + +def _issue_url(user, repo, idx): + return flask.url_for('view_issue', username=user, repo=repo, issueid=idx) From e0525dab469b7c3833552bd801e17e8444f1af7d Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 26 2015 14:04:55 +0000 Subject: [PATCH 2/7] Remove redundant linking handled previously by markdown jinja filter. --- diff --git a/pagure/ui/filters.py b/pagure/ui/filters.py index 2e9d9f6..0bc7d5d 100644 --- a/pagure/ui/filters.py +++ b/pagure/ui/filters.py @@ -228,21 +228,7 @@ def markdown_filter(text): # Hack to allow blockquotes to be marked by ~~~ ntext = [] indent = False - regexes = [ - re.compile('.*\s(http(s)?:\/\/\S+).*'), - re.compile('^(http(s)?:\/\/\S+).*'), - re.compile('.*\s(ftp(s)?:\/\/\S+).*'), - re.compile('^(ftp(s)?:\/\/\S+).*'), - ] for line in text.split('\n'): - # Automatically link URLs - for regex in regexes: - if regex.match(line): - line = line.replace( - regex.match(line).group(1), - regex.sub(r'<\1>', line) - ) - if line.startswith('~~~'): indent = not indent continue From d851f9a2975b2d6a1d1ca31b32dba26a8f2e9fd6 Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 26 2015 14:24:20 +0000 Subject: [PATCH 3/7] Remove the now redundant crossref filter. --- diff --git a/pagure/templates/_formhelper.html b/pagure/templates/_formhelper.html index 74c22bc..cb8a220 100644 --- a/pagure/templates/_formhelper.html +++ b/pagure/templates/_formhelper.html @@ -79,7 +79,7 @@
{% autoescape false %} - {{ content | crossref | markdown }} + {{ content | markdown }} {% endautoescape %}
diff --git a/pagure/ui/filters.py b/pagure/ui/filters.py index 0bc7d5d..137c7be 100644 --- a/pagure/ui/filters.py +++ b/pagure/ui/filters.py @@ -189,36 +189,6 @@ def shorted_commit(cid): return cid[:APP.config['SHORT_LENGTH']] -@APP.template_filter('crossref') -def crossref_filter(text): - """ Template filter adding a link when the provided text references - another issue or pull-request. - """ - regex = re.compile('.*\s#(\d+)', re.I) - if text: - if regex.match(text): - - url = flask.request.url - username = None - if 'fork/' in flask.request.url: - username, project = url.split('fork/')[1].split('/', 2)[:2] - else: - project = url.split( - flask.request.url_root)[1].split('/', 1)[0] - - issueid = regex.match(text).group(1) - text = text.replace('#%s' % issueid, '[#%s](%s)' % ( - issueid, flask.url_for( - 'view_issue', username=username, repo=project, - issueid=issueid) - ) - ) - - return text - else: - return '' - - @APP.template_filter('markdown') def markdown_filter(text): """ Template filter converting a string into html content using the From 7cea35a842a4937fe64690a31b7d3488ed9e6f8b Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 26 2015 14:24:34 +0000 Subject: [PATCH 4/7] Add more and improve existing types of issue matching. --- diff --git a/pagure/pfmarkdown.py b/pagure/pfmarkdown.py index 1834f0d..d456a23 100644 --- a/pagure/pfmarkdown.py +++ b/pagure/pfmarkdown.py @@ -40,11 +40,11 @@ def inject(): def handleMatch(self, m): el = markdown.util.etree.Element("a") name = markdown.util.AtomicString(m.group(2)) - el.set('href', _user_url(name[1:])) - el.text = name + el.set('href', _user_url(name)) + el.text = ' @%s' % name return el - class ExplicitIssuePattern(markdown.inlinepatterns.Pattern): + class ExplicitForkIssuePattern(markdown.inlinepatterns.Pattern): def handleMatch(self, m): el = markdown.util.etree.Element("a") user = markdown.util.AtomicString(m.group(2)) @@ -54,18 +54,48 @@ def inject(): el.text = '%s/%s#%s' % (user, repo, idx) return el - MENTION_RE = r'(@\w+)' - EXPLICIT_ISSUE_RE = r'(\w+)/(\w+)#([0-9]+)' + class ExplicitMainIssuePattern(markdown.inlinepatterns.Pattern): + def handleMatch(self, m): + el = markdown.util.etree.Element("a") + repo = markdown.util.AtomicString(m.group(2)) + idx = markdown.util.AtomicString(m.group(3)) + el.set('href', _issue_url(None, repo, idx)) + el.text = ' %s#%s' % (repo, idx) + return el + + class ImplicitIssuePattern(markdown.inlinepatterns.Pattern): + def handleMatch(self, m): + el = markdown.util.etree.Element("a") + idx = markdown.util.AtomicString(m.group(2)) + + 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] + + el.set('href', _issue_url(user, repo, idx)) + el.text = ' #%s' % idx + return el + + MENTION_RE = r'[^|\w]@(\w+)' + EXPLICIT_FORK_ISSUE_RE = r'(\w+)/(\w+)#([0-9]+)' + EXPLICIT_MAIN_ISSUE_RE = r'[^|\w](? Date: Mar 26 2015 14:36:36 +0000 Subject: [PATCH 5/7] Make pfmarkdown database-aware. --- diff --git a/pagure/pfmarkdown.py b/pagure/pfmarkdown.py index d456a23..f7a02cf 100644 --- a/pagure/pfmarkdown.py +++ b/pagure/pfmarkdown.py @@ -23,6 +23,9 @@ 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. """ @@ -38,34 +41,62 @@ def inject(): # Second, build some Pattern objects for @mentions, #bugs, etc... class MentionPattern(markdown.inlinepatterns.Pattern): def handleMatch(self, m): - el = markdown.util.etree.Element("a") 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") el.set('href', _user_url(name)) - el.text = ' @%s' % name + el.text = text return el class ExplicitForkIssuePattern(markdown.inlinepatterns.Pattern): def handleMatch(self, m): - el = markdown.util.etree.Element("a") 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) + + repo_obj = pagure.lib.get_project( + pagure.SESSION, name=repo, user=user) + if not repo_obj: + return text + + issue_obj = pagure.lib.search_issues( + pagure.SESSION, repo=repo_obj, issueid=idx) + if not issue_obj: + return text + + el = markdown.util.etree.Element("a") el.set('href', _issue_url(user, repo, idx)) - el.text = '%s/%s#%s' % (user, repo, idx) + el.text = text return el class ExplicitMainIssuePattern(markdown.inlinepatterns.Pattern): def handleMatch(self, m): - el = markdown.util.etree.Element("a") repo = markdown.util.AtomicString(m.group(2)) idx = markdown.util.AtomicString(m.group(3)) + text = ' %s#%s' % (repo, idx) + + repo_obj = pagure.lib.get_project( + pagure.SESSION, name=repo) + if not repo_obj: + return text + + issue_obj = pagure.lib.search_issues( + pagure.SESSION, repo=repo_obj, issueid=idx) + if not issue_obj: + return text + + el = markdown.util.etree.Element("a") el.set('href', _issue_url(None, repo, idx)) - el.text = ' %s#%s' % (repo, idx) + el.text = text return el class ImplicitIssuePattern(markdown.inlinepatterns.Pattern): def handleMatch(self, m): - el = markdown.util.etree.Element("a") idx = markdown.util.AtomicString(m.group(2)) root = flask.request.url_root @@ -76,8 +107,21 @@ def inject(): else: repo = url.split(root)[1].split('/', 1)[0] + text = ' #%s' % idx + + repo_obj = pagure.lib.get_project( + pagure.SESSION, name=repo, user=user) + if not repo_obj: + return text + + issue_obj = pagure.lib.search_issues( + pagure.SESSION, repo=repo_obj, issueid=idx) + if not issue_obj: + return text + + el = markdown.util.etree.Element("a") el.set('href', _issue_url(user, repo, idx)) - el.text = ' #%s' % idx + el.text = text return el MENTION_RE = r'[^|\w]@(\w+)' From debf639715c098f49d7870842fe3f16066c599eb Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 26 2015 14:46:24 +0000 Subject: [PATCH 6/7] Re-use some code to make this cleaner. --- diff --git a/pagure/pfmarkdown.py b/pagure/pfmarkdown.py index f7a02cf..5b3420f 100644 --- a/pagure/pfmarkdown.py +++ b/pagure/pfmarkdown.py @@ -59,20 +59,10 @@ def inject(): idx = markdown.util.AtomicString(m.group(4)) text = '%s/%s#%s' % (user, repo, idx) - repo_obj = pagure.lib.get_project( - pagure.SESSION, name=repo, user=user) - if not repo_obj: + if not _issue_exists(user, repo, idx): return text - issue_obj = pagure.lib.search_issues( - pagure.SESSION, repo=repo_obj, issueid=idx) - if not issue_obj: - return text - - el = markdown.util.etree.Element("a") - el.set('href', _issue_url(user, repo, idx)) - el.text = text - return el + return _issue_anchor_tag(user, repo, idx, text) class ExplicitMainIssuePattern(markdown.inlinepatterns.Pattern): def handleMatch(self, m): @@ -80,24 +70,15 @@ def inject(): idx = markdown.util.AtomicString(m.group(3)) text = ' %s#%s' % (repo, idx) - repo_obj = pagure.lib.get_project( - pagure.SESSION, name=repo) - if not repo_obj: - return text - - issue_obj = pagure.lib.search_issues( - pagure.SESSION, repo=repo_obj, issueid=idx) - if not issue_obj: + if not _issue_exists(None, repo, idx): return text - el = markdown.util.etree.Element("a") - el.set('href', _issue_url(None, repo, idx)) - el.text = text - return el + 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 @@ -107,22 +88,10 @@ def inject(): else: repo = url.split(root)[1].split('/', 1)[0] - text = ' #%s' % idx - - repo_obj = pagure.lib.get_project( - pagure.SESSION, name=repo, user=user) - if not repo_obj: - return text - - issue_obj = pagure.lib.search_issues( - pagure.SESSION, repo=repo_obj, issueid=idx) - if not issue_obj: + if not _issue_exists(user, repo, idx): return text - el = markdown.util.etree.Element("a") - el.set('href', _issue_url(user, repo, idx)) - el.text = text - return el + return _issue_anchor_tag(user, repo, idx, text) MENTION_RE = r'[^|\w]@(\w+)' EXPLICIT_FORK_ISSUE_RE = r'(\w+)/(\w+)#([0-9]+)' @@ -151,3 +120,24 @@ def _user_url(name): def _issue_url(user, repo, idx): return flask.url_for('view_issue', username=user, repo=repo, issueid=idx) + + +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") + el.set('href', _issue_url(user, repo, idx)) + el.text = text + return el From 3e458c0dd2461fb3ba70ac70ab7d96b4a93d66bb Mon Sep 17 00:00:00 2001 From: Ralph Bean Date: Mar 26 2015 14:48:10 +0000 Subject: [PATCH 7/7] Cull unnecessary functions. --- diff --git a/pagure/pfmarkdown.py b/pagure/pfmarkdown.py index 5b3420f..d963835 100644 --- a/pagure/pfmarkdown.py +++ b/pagure/pfmarkdown.py @@ -48,7 +48,8 @@ def inject(): return text el = markdown.util.etree.Element("a") - el.set('href', _user_url(name)) + url = flask.url_for('view_user', username=name) + el.set('href', url) el.text = text return el @@ -114,14 +115,6 @@ def inject(): markdown.build_inlinepatterns = extended_builder -def _user_url(name): - return flask.url_for('view_user', username=name) - - -def _issue_url(user, repo, idx): - return flask.url_for('view_issue', username=user, repo=repo, issueid=idx) - - def _issue_exists(user, repo, idx): repo_obj = pagure.lib.get_project( pagure.SESSION, name=repo, user=user) @@ -138,6 +131,7 @@ def _issue_exists(user, repo, idx): def _issue_anchor_tag(user, repo, idx, text): el = markdown.util.etree.Element("a") - el.set('href', _issue_url(user, repo, idx)) + url = flask.url_for('view_issue', username=user, repo=repo, issueid=idx) + el.set('href', url) el.text = text return el