From 8ed12e4242d7c2a6bb93832c19e0e30de1bf6a7d Mon Sep 17 00:00:00 2001 From: yiqing_jin Date: Tue, 26 Jan 2016 11:36:59 -0800 Subject: [PATCH 1/5] support oembed url in task description, this enables embedding videos from youtube, vimeo, etc. --- osmtm/mako_filters.py | 8 ++- osmtm/markdown_extensions.py | 75 +++++++++++++++++++++++ osmtm/static/html/markdown_quick_ref.html | 9 +++ setup.py | 1 + 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 osmtm/markdown_extensions.py diff --git a/osmtm/mako_filters.py b/osmtm/mako_filters.py index 54756bd1..65e6febf 100644 --- a/osmtm/mako_filters.py +++ b/osmtm/mako_filters.py @@ -6,14 +6,18 @@ User, ) +from markdown_extensions import OEmbedExtension + amp = re.compile('&') +md = markdown.Markdown(extensions=[OEmbedExtension()]) + def markdown_filter(text): ''' Mako filter for markdown and bleach ''' cleaned = bleach.clean(text, strip=True) - parsed = markdown.markdown(cleaned) + parsed = md.convert(cleaned) return re.sub(amp, '&', parsed) @@ -23,8 +27,8 @@ def markdown_filter(text): def convert_mentions(request): ''' Mako filter to convert any @id mention to link to user profile ''' - def d(text): + def d(text): def repl(val): user_id = val.group()[1:] user = DBSession.query(User).get(user_id) diff --git a/osmtm/markdown_extensions.py b/osmtm/markdown_extensions.py new file mode 100644 index 00000000..c77229fe --- /dev/null +++ b/osmtm/markdown_extensions.py @@ -0,0 +1,75 @@ +from markdown import Extension +from markdown.inlinepatterns import Pattern +import oembed + +DEFAULT_ENDPOINTS = [ + # Youtube + oembed.OEmbedEndpoint('http://www.youtube.com/oembed', [ + 'https?://(*.)?youtube.com/*', + 'https?://youtu.be/*', + ]), + + # Flickr + oembed.OEmbedEndpoint('http://www.flickr.com/services/oembed/', [ + 'https?://*.flickr.com/*', + ]), + + # Vimeo + oembed.OEmbedEndpoint('http://vimeo.com/api/oembed.json', [ + 'https?://vimeo.com/*', + ]), +] + +OEMBED_LINK_RE = r'\!\[([^\]]*)\]\(((?:https?:)?//[^\)]*)' \ + r'(?%s" % html + placeholder = self.markdown.htmlStash.store(html, True) + return placeholder + + def get_oembed_html_for_match(self, match): + url = match.group(3).strip() + try: + response = self.consumer.embed(url) + except oembed.OEmbedNoEndpoint: + return None + else: + return response['html'] + + +class OEmbedExtension(Extension): + def __init__(self, **kwargs): + self.config = { + 'allowed_endpoints': [ + DEFAULT_ENDPOINTS, + "A list of oEmbed endpoints to allow. Defaults to " + "endpoints.DEFAULT_ENDPOINTS" + ], + } + super(OEmbedExtension, self).__init__(**kwargs) + self.oembed_consumer = self.prepare_oembed_consumer() + + def extendMarkdown(self, md, md_globals): + link_pattern = OEmbedLinkPattern(OEMBED_LINK_RE, md, self.oembed_consumer) + md.inlinePatterns.add('oembed_link', link_pattern, 'Sub-heading alternate + +
+
+
![video](http://www.youtube.com/watch?v=StTqXEQ2l-Y)
+
+
+
oEmbed link, currently support Youtube, Flickr and Vimeo. 
+
+
diff --git a/setup.py b/setup.py index cb3984dd..c3a4e568 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ 'pygments', 'gitversion', 'APScheduler==3.0.3', + 'python-oembed == 0.2.1', ] setup(name='osmtm', From 6371f0c7aa1a8b291f9ef62bb9794d4fba5e3d28 Mon Sep 17 00:00:00 2001 From: yiqing_jin Date: Tue, 26 Jan 2016 11:41:47 -0800 Subject: [PATCH 2/5] clean up format --- osmtm/mako_filters.py | 1 - 1 file changed, 1 deletion(-) diff --git a/osmtm/mako_filters.py b/osmtm/mako_filters.py index 65e6febf..28945b71 100644 --- a/osmtm/mako_filters.py +++ b/osmtm/mako_filters.py @@ -27,7 +27,6 @@ def markdown_filter(text): def convert_mentions(request): ''' Mako filter to convert any @id mention to link to user profile ''' - def d(text): def repl(val): user_id = val.group()[1:] From 1b4e82148eaa0ff23c8c152bc76f85f4245f510e Mon Sep 17 00:00:00 2001 From: yiqing_jin Date: Tue, 26 Jan 2016 12:59:32 -0800 Subject: [PATCH 3/5] fix test failure --- osmtm/markdown_extensions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osmtm/markdown_extensions.py b/osmtm/markdown_extensions.py index c77229fe..4209ceac 100644 --- a/osmtm/markdown_extensions.py +++ b/osmtm/markdown_extensions.py @@ -61,11 +61,13 @@ def __init__(self, **kwargs): self.oembed_consumer = self.prepare_oembed_consumer() def extendMarkdown(self, md, md_globals): - link_pattern = OEmbedLinkPattern(OEMBED_LINK_RE, md, self.oembed_consumer) + link_pattern = OEmbedLinkPattern(OEMBED_LINK_RE, md, + self.oembed_consumer) md.inlinePatterns.add('oembed_link', link_pattern, ' Date: Tue, 26 Jan 2016 17:42:13 -0800 Subject: [PATCH 4/5] also support preview for youtube link by updating showdown and using showdown-youtube extension. --- .gitmodules | 3 + osmtm/static/js/lib/showdown | 1 + osmtm/static/js/lib/showdown-youtube.js | 105 ++++++++++++++++++++++++ osmtm/templates/base.mako | 4 +- osmtm/templates/project.edit.mako | 2 +- 5 files changed, 113 insertions(+), 2 deletions(-) create mode 160000 osmtm/static/js/lib/showdown create mode 100644 osmtm/static/js/lib/showdown-youtube.js diff --git a/.gitmodules b/.gitmodules index 66a8f5a8..56b0ad3b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "osmtm/static/js/lib/leaflet-control-osm-geocoder"] path = osmtm/static/js/lib/leaflet-control-osm-geocoder url = https://github.com/k4r573n/leaflet-control-osm-geocoder.git +[submodule "osmtm/static/js/lib/showdown"] + path = osmtm/static/js/lib/showdown + url = https://github.com:showdownjs/showdown.git diff --git a/osmtm/static/js/lib/showdown b/osmtm/static/js/lib/showdown new file mode 160000 index 00000000..f4d50dc1 --- /dev/null +++ b/osmtm/static/js/lib/showdown @@ -0,0 +1 @@ +Subproject commit f4d50dc13bfb5e9e3b74c3e51656456fa3a137a2 diff --git a/osmtm/static/js/lib/showdown-youtube.js b/osmtm/static/js/lib/showdown-youtube.js new file mode 100644 index 00000000..5cc995a6 --- /dev/null +++ b/osmtm/static/js/lib/showdown-youtube.js @@ -0,0 +1,105 @@ +/** + * Youtube Extension. + * Uses image syntax to embed videos + * Usage: + * ![youtube video][http://youtu.be/dQw4w9WgXcQ] + * + * or + * + * ![youtube video][1] + * [1]: http://youtu.be/dQw4w9WgXcQ + */ +(function (extension) { + 'use strict'; + + if (showdown) { + // global (browser or nodejs global) + extension(showdown); + } else if (typeof define === 'function' && define.amd) { + // AMD + define(['showdown'], extension); + } else if (typeof exports === 'object') { + // Node, CommonJS-like + module.exports = extension(require('showdown')); + } else { + // showdown was not found so we throw + throw Error('Could not find showdown library'); + } + +}(function (showdown) { + + var svg = + '
' + + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + '' + + '' + + '
', + iframe = '', + img = '', + imgRegex = /(?:

)?(?:<\/p>)?/gi, + fullLinkRegex = /(?:(?:https?:)?(?:\/\/)?)(?:(?:www)?\.)?youtube\.(?:.+?)\/(?:(?:watch\?v=)|(?:embed\/))([a-zA-Z0-9_-]{11})/i, + shortLinkRegex = /(?:(?:https?:)?(?:\/\/)?)?youtu\.be\/([a-zA-Z0-9_-]{11})/i; + + function parseDimensions(rest) { + var width, + height, + d; + + if (rest) { + width = (d = /width="(.+?)"/.exec(rest)) ? d[1] : '420'; + height = (d = /height="(.+?)"/.exec(rest)) ? d[1] : '315'; + } + + // add units so they can be used in css + if (/^\d+$/gm.exec(width)) { + width += 'px'; + } + if (/^\d+$/gm.exec(height)) { + height += 'px'; + } + + return { + width: width, + height: height + }; + } + + /** + * Replace with video iframes + */ + showdown.extension('youtube', function () { + return [ + { + // It's a bit hackish but we let the core parsers replace the reference image for an image tag + // then we replace the full img tag in the output with our iframe + type: 'output', + filter: function (text, converter, options) { + var tag = iframe; + if (options.smoothLivePreview) { + tag = (options.youtubeUseSimpleImg) ? img : svg; + } + return text.replace(imgRegex, function (match, url, rest) { + var d = parseDimensions(rest), + m; + if ((m = shortLinkRegex.exec(url)) || (m = fullLinkRegex.exec(url))) { + return tag.replace(/%1/g, m[1]).replace('%2', d.width).replace('%3', d.height); + } else { + return match; + } + }); + } + } + ]; + }); +})); diff --git a/osmtm/templates/base.mako b/osmtm/templates/base.mako index 21cca054..93b0bc6e 100644 --- a/osmtm/templates/base.mako +++ b/osmtm/templates/base.mako @@ -9,7 +9,8 @@ - + + <% timeago_locale_baseurl = 'osmtm:static/js/lib/jquery-timeago/locales/jquery.timeago.%s.js' @@ -24,6 +25,7 @@ + <% from osmtm.models import DBSession, TaskComment login_url= request.route_path('login', _query=[('came_from', request.url)]) diff --git a/osmtm/templates/project.edit.mako b/osmtm/templates/project.edit.mako index f95a162e..9bcf41b0 100644 --- a/osmtm/templates/project.edit.mako +++ b/osmtm/templates/project.edit.mako @@ -73,7 +73,7 @@