From be67b59b07c8c711fac3aef35be6fb96e2f29db4 Mon Sep 17 00:00:00 2001 From: Tom Dyson Date: Wed, 12 Feb 2014 18:21:16 +0000 Subject: [PATCH 01/13] Update README.rst link to wagtaildemo and make t-shirt promise. --- README.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index fc7582fd1..f4b54f428 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,12 @@ Wagtail CMS =========== Wagtail is a Django content management system focused on flexibility and user experience. Find out more at `wagtail.io `_ -or use the instructions at `torchbox.github.io/wagtail `_ to get started. +and `torchbox.github.io/wagtail `_. -If you're a Python or Django developer, fork the repo and get stuck in! We're keen to foster a development community and we welcome all your pull requests. +Getting started +~~~~~~~~~~~~~~~ +To get you up and running quickly, we've provided a demonstration site with all the configuration in place, at `github.com/torchbox/wagtaildemo `_; see the `README `_ for installation instructions. + +Contributing +~~~~~~~~~~~~ +If you're a Python or Django developer, fork the repo and get stuck in! Send us a useful pull request and we'll post you a `t-shirt `_. From 0ddc5a6ff60f95efc69b7c608a7efa6e89789a0f Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Wed, 12 Feb 2014 22:21:18 +0200 Subject: [PATCH 02/13] Added the endpoints.json and modified embeds.py Now embeds.py has the infrastructre to select a different get_embed function depending on if embedly should be used or not. the different get_embed (get_embed_oembed) function should be written now... --- wagtail/wagtailembeds/embeds.py | 25 +++++- wagtail/wagtailembeds/endpoints.json | 114 +++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 wagtail/wagtailembeds/endpoints.json diff --git a/wagtail/wagtailembeds/embeds.py b/wagtail/wagtailembeds/embeds.py index 59d349a26..a2cff4d62 100644 --- a/wagtail/wagtailembeds/embeds.py +++ b/wagtail/wagtailembeds/embeds.py @@ -1,12 +1,18 @@ from datetime import datetime -from embedly import Embedly + from django.conf import settings from .models import Embed +import os +module_dir = os.path.dirname(__file__) # get current directory +file_path = os.path.join(module_dir, 'endpoints.json') +print file_path +print open(file_path).read() -def get_embed(url, max_width=None): + +def get_embed_embedly(url, max_width=None): # Check database try: return Embed.objects.get(url=url, max_width=max_width) @@ -52,3 +58,18 @@ def get_embed(url, max_width=None): # Return new embed return row + +def get_embed_oembed(url, max_width=None): + pass + +get_embed = get_embed_oembed +try: + from embedly import Embedly + if hasattr(settings,'EMBEDLY_KEY'): + get_embed = get_embed_embedly +except: + pass + +print get_embed + + \ No newline at end of file diff --git a/wagtail/wagtailembeds/endpoints.json b/wagtail/wagtailembeds/endpoints.json new file mode 100644 index 000000000..7cee0cec7 --- /dev/null +++ b/wagtail/wagtailembeds/endpoints.json @@ -0,0 +1,114 @@ +[ +{ +"url": "http://*.blip.tv/*", +"url_re": "blip\\.tv/.+", +"example_url": "http://pycon.blip.tv/file/2058801/", +"endpoint_url": "http://blip.tv/oembed/", +"title": "blip.tv" +}, +{ +"url": "http://*.dailymotion.com/*", +"url_re": "dailymotion\\.com/.+", +"example_url": "http://www.dailymotion.com/video/x5ioet_phoenix-mars-lander_tech", +"endpoint_url": "http://www.dailymotion.com/api/oembed/", +"title": "Dailymotion" +}, +{ +"url": "http://*.flickr.com/photos/*", +"url_re": "flickr\\.com/photos/[-.\\w@]+/\\d+/?", +"example_url": "http://www.flickr.com/photos/fuffer2005/2435339994/", +"endpoint_url": "http://www.flickr.com/services/oembed/", +"title": "Flickr Photos" +}, +{ +"url": "http://www.hulu.com/watch/*", +"url_re": "hulu\\.com/watch/.*", +"example_url": "http://www.hulu.com/watch/20807/late-night-with-conan", +"endpoint_url": "http://www.hulu.com/api/oembed.json", +"title": "Hulu" +}, +{ +"url": "http://*.nfb.ca/film/*", +"url_re": "nfb\\.ca/film/[-\\w]+/?", +"example_url": "http://www.nfb.ca/film/blackfly/", +"endpoint_url": "http://www.nfb.ca/remote/services/oembed/", +"title": "National Film Board of Canada" +}, +{ +"url": "http://qik.com/*", +"url_re": "qik\\.com/\\w+", +"example_url": "http://qik.com/video/86776", +"endpoint_url": "http://qik.com/api/oembed.json", +"title": "Qik Video" +}, +{ +"url": "http://*.revision3.com/*", +"url_re": "revision3\\.com/.+", +"example_url": "http://revision3.com/diggnation/2008-04-17xsanned/", +"endpoint_url": "http://revision3.com/api/oembed/", +"title": "Revision3" +}, +{ +"url": "http://*.scribd.com/*", +"url_re": "scribd\\.com/.+", +"example_url": "http://www.scribd.com/doc/17896323/Indian-Automobile-industryPEST", +"endpoint_url": "http://www.scribd.com/services/oembed", +"title": "Scribd" +}, +{ +"url": "http://*.viddler.com/explore/*", +"url_re": "viddler\\.com/explore/.*/videos/\\w+/?", +"example_url": "http://www.viddler.com/explore/engadget/videos/14/", +"endpoint_url": "http://lab.viddler.com/services/oembed/", +"title": "Viddler Video" +}, +{ +"url": "http://www.vimeo.com/* and http://www.vimeo.com/groups/*/videos/*", +"url_re": "vimeo\\.com/.*", +"example_url": "http://www.vimeo.com/1211060", +"endpoint_url": "http://www.vimeo.com/api/oembed.json", +"title": "Vimeo" +}, +{ +"url": "http://*.youtube.com/watch*", +"url_re": "youtube\\.com/watch.+v=[\\w-]+&?", +"example_url": "http://www.youtube.com/watch?v=vk1HvP7NO5w", +"endpoint_url": "http://www.youtube.com/oembed", +"title": "YouTube" +}, +{ +"url": "http://dotsub.com/view/*", +"url_re": "dotsub\\.com/view/[-\\da-zA-Z]+$", +"example_url": "http://dotsub.com/view/10e3cb5e-96c7-4cfb-bcea-8ab11e04e090", +"endpoint_url": "http://dotsub.com/services/oembed", +"title": "dotSUB.com" +}, +{ +"url": "http://yfrog.(com|ru|com.tr|it|fr|co.il|co.uk|com.pl|pl|eu|us)/*", +"url_re": "yfrog\\.(com|ru|com\\.tr|it|fr|co\\.il|co\\.uk|com\\.pl|pl|eu|us)/[a-zA-Z0-9]+$", +"example_url": "http://yfrog.com/0wgvcpj", +"endpoint_url": "http://www.yfrog.com/api/oembed", +"title": "YFrog" +}, +{ +"url": "http://*.clikthrough.com/theater/video/*", +"url_re": "clikthrough\\.com/theater/video/\\d+$", +"example_url": "http://www.clikthrough.com/theater/video/55", +"endpoint_url": "http://clikthrough.com/services/oembed", +"title": "Clikthrough" +}, +{ +"url": "http://*.kinomap.com/*", +"url_re": "kinomap\\.com/.+", +"example_url": "http://www.kinomap.com/kms-vzkpc7", +"endpoint_url": "http://www.kinomap.com/oembed", +"title": "Kinomap" +}, +{ +"url": "http://*.photobucket.com/albums/*|http://*.photobucket.com/groups/*", +"url_re": "photobucket\\.com/(albums|groups)/.+$", +"example_url": "http://img.photobucket.com/albums/v211/JAV123/Michael%20Holland%20Candle%20Burning/_MG_5661.jpg", +"endpoint_url": "http://photobucket.com/oembed", +"title": "Photobucket" +} +] From 74b9f4340120962d099a346ef3422f19db2918b2 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Thu, 13 Feb 2014 04:01:51 +0200 Subject: [PATCH 03/13] Make embedly optional and refactor code This fixes #26. First of all there is some refactoring: All low level embed functions have been moved to the wagtail.wagtailembeds.embeds package. There you will see: . embed.py (which is more or less a copy of the old embeds.py) . oembed_api.py which includes some low level code for using embedding with the help of oembed, without any external dependencies (python-oembed was not working very well and since oembed is just a URL get to a specific URL I implemented it with urllib2 and json), . endpoints.json which is a list of oembed endpoints I got from https://github.com/panzi/oembedendpoints/blob/master/endpoints-regexp.json . unittests.py with some tests to check that well known sites like youtube, vimeo etc work fine with the oembed_api The code refactoring also includes a number of exceptions. The get_embed function now is included in try / except blocks and if an exception occurs then the output will be an emtpy string (this was the behavior before the refactor). However, in the chooser.py function the type of the exception will be checked and a nice message will be shown to the editor. Finally, to choose between embedly and the oembed a check is made to see if the embedly library has been installed and also check if EMBEDLY_KEY has been set in the settings. If these two checks are both true then the get_embed will be assigned to get_embed_embedly -- else it will be assigned to get_embed_oembed. --- wagtail/wagtailcore/rich_text.py | 5 +- wagtail/wagtailembeds/__init__.py | 2 +- wagtail/wagtailembeds/embeds/__init__.py | 0 wagtail/wagtailembeds/embeds/embed.py | 80 +++++ wagtail/wagtailembeds/embeds/endpoints.json | 295 ++++++++++++++++++ wagtail/wagtailembeds/embeds/oembed_api.py | 52 +++ wagtail/wagtailembeds/embeds/unittests.py | 66 ++++ wagtail/wagtailembeds/format.py | 25 +- .../templatetags/embed_filters.py | 11 +- wagtail/wagtailembeds/tests.py | 2 +- wagtail/wagtailembeds/views/chooser.py | 25 +- 11 files changed, 539 insertions(+), 24 deletions(-) create mode 100644 wagtail/wagtailembeds/embeds/__init__.py create mode 100644 wagtail/wagtailembeds/embeds/embed.py create mode 100644 wagtail/wagtailembeds/embeds/endpoints.json create mode 100644 wagtail/wagtailembeds/embeds/oembed_api.py create mode 100644 wagtail/wagtailembeds/embeds/unittests.py diff --git a/wagtail/wagtailcore/rich_text.py b/wagtail/wagtailcore/rich_text.py index bd00ee503..aa8f25336 100644 --- a/wagtail/wagtailcore/rich_text.py +++ b/wagtail/wagtailcore/rich_text.py @@ -52,7 +52,10 @@ class ImageEmbedHandler(object): format = get_image_format(attrs['format']) if for_editor: - return format.image_to_editor_html(image, attrs['alt']) + try: + return format.image_to_editor_html(image, attrs['alt']) + except: + return '' else: return format.image_to_html(image, attrs['alt']) diff --git a/wagtail/wagtailembeds/__init__.py b/wagtail/wagtailembeds/__init__.py index b75cbc491..8e3708ca9 100644 --- a/wagtail/wagtailembeds/__init__.py +++ b/wagtail/wagtailembeds/__init__.py @@ -1,2 +1,2 @@ from .models import Embed -from .embeds import get_embed +from .embeds.embed import get_embed diff --git a/wagtail/wagtailembeds/embeds/__init__.py b/wagtail/wagtailembeds/embeds/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/wagtail/wagtailembeds/embeds/embed.py b/wagtail/wagtailembeds/embeds/embed.py new file mode 100644 index 000000000..3d91a7fae --- /dev/null +++ b/wagtail/wagtailembeds/embeds/embed.py @@ -0,0 +1,80 @@ +from datetime import datetime +from django.conf import settings +from ..models import Embed +import oembed_api + +class EmbedlyException(Exception): pass +class AccessDeniedEmbedlyException(Exception): pass +class NotFoundEmbedlyException(Exception): pass + +def get_embed_embedly(url, max_width=None): + # Check database + try: + return Embed.objects.get(url=url, max_width=max_width) + except Embed.DoesNotExist: + pass + + client = Embedly(key=settings.EMBEDLY_KEY) + + if max_width is not None: + oembed = client.oembed(url, maxwidth=max_width, better=False) + else: + oembed = client.oembed(url, better=False) + + # Check for error + if oembed.get('error'): + if oembed['error_code'] in [401,403]: + raise AccessDeniedEmbedlyException + elif oembed['error_code'] == 404: + raise NotFoundEmbedlyException + else: + raise EmbedlyException + + return save_embed(url, max_width, oembed) + + +def get_embed_oembed(url, max_width=None): + # Check database + try: + return Embed.objects.get(url=url, max_width=max_width) + except Embed.DoesNotExist: + pass + + oembed = oembed_api.get_embed_oembed(url, max_width) + return save_embed(url, max_width, oembed) + + +def save_embed(url, max_width, oembed): + row, created = Embed.objects.get_or_create( + url=url, + max_width=max_width, + defaults={ + 'type': oembed['type'], + 'title': oembed['title'], + 'thumbnail_url': oembed.get('thumbnail_url'), + 'width': oembed.get('width'), + 'height': oembed.get('height') + } + ) + + if oembed['type'] == 'photo': + html = '' % (oembed['url'], ) + else: + html = oembed.get('html') + + if html: + row.html = html + row.last_updated = datetime.now() + row.save() + + return row + +# As a default use oembed +get_embed = get_embed_oembed +try: + from embedly import Embedly + # if EMBEDLY_KEY is set and embedly library found the use embedly + if hasattr(settings,'EMBEDLY_KEY'): + get_embed = get_embed_embedly +except: + pass diff --git a/wagtail/wagtailembeds/embeds/endpoints.json b/wagtail/wagtailembeds/embeds/endpoints.json new file mode 100644 index 000000000..c289b7183 --- /dev/null +++ b/wagtail/wagtailembeds/embeds/endpoints.json @@ -0,0 +1,295 @@ +{ + "https://speakerdeck.com/oembed.{format}": [ + "^http(?:s)?://speakerdeck\\.com/.+$" + ], + "https://alpha-api.app.net/oembed": [ + "^http(?:s)?://alpha\\.app\\.net/[^#?/]+/post/.+$", + "^http(?:s)?://photos\\.app\\.net/[^#?/]+/.+$" + ], + "http://www.youtube.com/oembed": [ + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/watch.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/v/.+$", + "^http(?:s)?://youtu\\.be/.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/user/.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/[^#?/]+#[^#?/]+/.+$", + "^http(?:s)?://m\\.youtube\\.com/index.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/profile.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/view_play_list.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?youtube\\.com/playlist.+$" + ], + "http://backend.deviantart.com/oembed": [ + "^http://(?:[-\\w]+\\.)?deviantart\\.com/art/.+$", + "^http://fav\\.me/.+$", + "^http://sta\\.sh/.+$", + "^http://(?:[-\\w]+\\.)?deviantart\\.com/[^#?/]+#/d.+$" + ], + "http://blip.tv/oembed/": [ + "^http://[-\\w]+\\.blip\\.tv/.+$" + ], + "http://www.dailymotion.com/api/oembed/": [ + "^http://[-\\w]+\\.dailymotion\\.com/.+$" + ], + "http://www.flickr.com/services/oembed/": [ + "^http://[-\\w]+\\.flickr\\.com/photos/.+$", + "^http://flic\\.kr\\.com/.+$" + ], + "http://www.hulu.com/api/oembed.{format}": [ + "^http://www\\.hulu\\.com/watch/.+$" + ], + "http://www.nfb.ca/remote/services/oembed/": [ + "^http://(?:[-\\w]+\\.)?nfb\\.ca/film/.+$" + ], + "http://qik.com/api/oembed.{format}": [ + "^http://qik\\.com/.+$", + "^http://qik\\.ly/.+$" + ], + "http://revision3.com/api/oembed/": [ + "^http://[-\\w]+\\.revision3\\.com/.+$" + ], + "http://www.scribd.com/services/oembed": [ + "^http://[-\\w]+\\.scribd\\.com/.+$" + ], + "http://www.viddler.com/oembed/": [ + "^http://[-\\w]+\\.viddler\\.com/v/.+$", + "^http://[-\\w]+\\.viddler\\.com/explore/.+$" + ], + "http://www.vimeo.com/api/oembed.{format}": [ + "^http(?:s)?://(?:www\\.)?vimeo\\.com/.+$", + "^http(?:s)?://player\\.vimeo\\.com/.+$" + ], + "http://dotsub.com/services/oembed": [ + "^http://dotsub\\.com/view/.+$" + ], + "http://www.yfrog.com/api/oembed": [ + "^http(?:s)?://(?:www\\.)?yfrog\\.com/.+$", + "^http(?:s)?://(?:www\\.)?yfrog\\.us/.+$" + ], + "http://clikthrough.com/services/oembed": [ + "^http(?:s)?://(?:[-\\w]+\\.)?clikthrough\\.com/.+$" + ], + "http://www.kinomap.com/oembed": [ + "^http://[-\\w]+\\.kinomap\\.com/.+$" + ], + "https://photobucket.com/oembed": [ + "^http://(?:[-\\w]+\\.)?photobucket\\.com/albums/.+$", + "^http://(?:[-\\w]+\\.)?photobucket\\.com/groups/.+$" + ], + "http://api.instagram.com/oembed": [ + "^http://instagr\\.am/p/.+$", + "^http://instagram\\.com/p/.+$" + ], + "https://www.slideshare.net/api/oembed/2": [ + "^http://www\\.slideshare\\.net/.+$" + ], + "http://tv.majorleaguegaming.com/oembed": [ + "^http://mlg\\.tv/.+$", + "^http://tv\\.majorleaguegaming\\.com/.+$" + ], + "http://my.opera.com/service/oembed": [ + "^http://my\\.opera\\.com/.+$" + ], + "http://skitch.com/oembed": [ + "^http(?:s)?://(?:www\\.)?skitch\\.com/.+$", + "^http://skit\\.ch/.+$" + ], + "https://api.twitter.com/1/statuses/oembed.{format}": [ + "^http(?:s)?://twitter\\.com/(?:#!)?[^#?/]+/status/.+$" + ], + "https://soundcloud.com/oembed": [ + "^https://soundcloud\\.com/[^#?/]+/.+$" + ], + "http://www.collegehumor.com/oembed.{format}": [ + "^http://(?:www\\.)?collegehumor\\.com/video/.+$", + "^http://(?:www\\.)?collegehumor\\.com/video:.+$" + ], + "http://www.polleverywhere.com/services/oembed/": [ + "^http://www\\.polleverywhere\\.com/polls/.+$", + "^http://www\\.polleverywhere\\.com/multiple_choice_polls/.+$", + "^http://www\\.polleverywhere\\.com/free_text_polls/.+$" + ], + "http://www.ifixit.com/Embed": [ + "^http://www\\.ifixit\\.com/[^#?/]+/[^#?/]+/.+$" + ], + "http://api.smugmug.com/services/oembed/": [ + "^http(?:s)?://(?:www\\.)?smugmug\\.com/[^#?/]+/.+$" + ], + "https://github.com/api/oembed": [ + "^http(?:s)?://gist\\.github\\.com/.+$" + ], + "http://animoto.com/services/oembed": [ + "^http://animoto\\.com/play/.+$" + ], + "http://www.rdio.com/api/oembed": [ + "^http://(?:wwww\\.)?rdio\\.com/people/[^#?/]+/playlists/.+$", + "^http://[-\\w]+\\.rdio\\.com/artist/[^#?/]+/album/.+$" + ], + "http://api.5min.com/oembed.{format}": [ + "^http://www\\.5min\\.com/video/.+$" + ], + "http://500px.com/photo/{1}/oembed.{format}": [ + "^http://500px\\.com/photo/([^#?/]+)(?:.+)?$" + ], + "http://api.dipdive.com/oembed.{format}": [ + "^http://[-\\w]+\\.dipdive\\.com/media/.+$" + ], + "http://video.yandex.ru/oembed.{format}": [ + "^http://video\\.yandex\\.ru/users/[^#?/]+/view/.+$" + ], + "http://www.mixcloud.com/oembed/": [ + "^http://www\\.mixcloud\\.com/oembed/[^#?/]+/.+$" + ], + "http://www.kickstarter.com/services/oembed": [ + "^http(?:s)://[-\\w]+\\.kickstarter\\.com/projects/.+$" + ], + "http://coub.com/api/oembed.{format}": [ + "^http(?:s)?://coub\\.com/view/.+$", + "^http(?:s)?://coub\\.com/embed/.+$" + ], + "http://www.screenr.com/api/oembed.{format}": [ + "^http://www\\.screenr\\.com/.+$" + ], + "http://www.funnyordie.com/oembed.{format}": [ + "^http://www\\.funnyordie\\.com/videos/.+$" + ], + "http://fast.wistia.com/oembed.{format}": [ + "^http://[-\\w]+\\.wista\\.com/medias/.+$" + ], + "http://www.ustream.tv/oembed": [ + "^http(?:s)?://(?:www\\.)?ustream\\.tv/.+$", + "^http(?:s)?://(?:www\\.)?ustream\\.com/.+$", + "^http://ustre\\.am/.+$" + ], + "http://wordpress.tv/oembed/": [ + "^http://wordpress\\.tv/.+$" + ], + "http://polldaddy.com/oembed/": [ + "^http(?:s)?://(?:[-\\w]+\\.)?polldaddy\\.com/.+$" + ], + "http://api.bambuser.com/oembed.{format}": [ + "^http://bambuser\\.com/channel/[^#?/]+/broadcast/.+$", + "^http://bambuser\\.com/channel/.+$", + "^http://bambuser\\.com/v/.+$" + ], + "http://www.ted.com/talks/oembed.{format}": [ + "^http(?:s)?://(?:www\\.)?ted\\.com/talks/.+$", + "^http(?:s)?://(?:www\\.)?ted\\.com/talks/lang/[^#?/]+/.+$", + "^http(?:s)?://(?:www\\.)?ted\\.com/index\\.php/talks/.+$", + "^http(?:s)?://(?:www\\.)?ted\\.com/index\\.php/talks/lang/[^#?/]+/.+$" + ], + "http://chirb.it/oembed.{format}": [ + "^http://chirb\\.it/.+$" + ], + "https://www.circuitlab.com/circuit/oembed/": [ + "^http(?:s)?://(?:www\\.)?circuitlab\\.com/circuit/.+$" + ], + "http://api.geograph.org.uk/api/oembed": [ + "^http://(?:[-\\w]+\\.)?geograph\\.org\\.uk/.+$", + "^http://(?:[-\\w]+\\.)?geograph\\.co\\.uk/.+$", + "^http://(?:[-\\w]+\\.)?geograph\\.ie/.+$" + ], + "http://geo.hlipp.de/restapi.php/api/oembed": [ + "^http://geo-en\\.hlipp\\.de/.+$", + "^http://geo\\.hlipp\\.de/.+$", + "^http://germany\\.geograph\\.org/.+$" + ], + "http://www.geograph.org.gg/api/oembed": [ + "^http://(?:[-\\w]+\\.)?geograph\\.org\\.gg/.+$", + "^http://(?:[-\\w]+\\.)?geograph\\.org\\.je/.+$", + "^http://channel-islands\\.geograph\\.org/.+$", + "^http://channel-islands\\.geographs\\.org/.+$", + "^http://(?:[-\\w]+\\.)?channel\\.geographs\\.org/.+$" + ], + "http://vzaar.com/api/videos/{1}.{format}": [ + "^http://(?:www\\.)?vzaar\\.com/videos/([^#?/]+)(?:.+)?$", + "^http://www\\.vzaar\\.tv/([^#?/]+)(?:.+)?$", + "^http://vzaar\\.tv/([^#?/]+)(?:.+)?$", + "^http://vzaar\\.me/([^#?/]+)(?:.+)?$", + "^http://[-\\w]+\\.vzaar\\.me/([^#?/]+)(?:.+)?$" + ], + "http://api.minoto-video.com/services/oembed.{format}": [ + "^http://api\\.minoto-video\\.com/publishers/[^#?/]+/videos/.+$", + "^http://dashboard\\.minoto-video\\.com/main/video/details/.+$", + "^http://embed\\.minoto-video\\.com/.+$" + ], + "http://www.videojug.com/oembed.{format}": [ + "^http(?:s)?://(?:[-\\w]+\\.)?videojug\\.com/film/.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?videojug\\.com/payer/.+$", + "^http(?:s)?://(?:[-\\w]+\\.)?videojug\\.com/interview/.+$" + ], + "http://videos.sapo.pt/oembed": [ + "^http(?:s)?://videos\\.sapo\\.pt/.+$" + ], + "http://vhx.tv/services/oembed.{format}": [ + "^http(?:s)?://(?:www\\.)?vhx\\.tv/.+$" + ], + "http://api.justin.tv/api/embed/from_url.{format}": [ + "^http(?:s)?://(?:www\\.)?justin\\.tv/.+$" + ], + "http://official.fm/services/oembed.{format}": [ + "^http(?:s)?://official\\.fm/.+$" + ], + "http://huffduffer.com/oembed": [ + "^http(?:s)?://(?:www\\.)?huffduffer\\.com/[^#?/]+/.+$" + ], + "https://embed.spotify.com/oembed/": [ + "^http(?:s)?://open\\.spotify\\.com/.+$", + "^http(?:s)?://spoti\\.fi/.+$" + ], + "http://shoudio.com/api/oembed": [ + "^http://shoudio\\.com/.+$", + "^http://shoud\\.io/.+$" + ], + "http://api.mobypicture.com/oEmbed": [ + "^http(?:s)?://(?:www\\.)?mobypicture\\.com/user/[^#?/]+/view/.+$", + "^http(?:s)?://(?:www\\.)?moby\\.to/.+$" + ], + "http://www.23hq.com/23/oembed": [ + "^http(?:s)?://(?:www\\.)?23hq\\.com/[^#?/]+/photo/.+$" + ], + "http://gmep.org/oembed.{format}": [ + "^http(?:s)?://(?:www\\.)?gmep\\.org/.+$", + "^http(?:s)?://gmep\\.imeducate\\.com/.+$" + ], + "http://oembed.urtak.com/1/oembed": [ + "^http(?:s)?://(?:[-\\w]+\\.)?urtak\\.com/.+$" + ], + "http://cacoo.com/oembed.{format}": [ + "^http(?:s)?://cacoo\\.com/.+$" + ], + "http://api.dailymile.com/oembed": [ + "^http(?:s)?://(?:www\\.)?dailymile\\.com/people/[^#?/]+/entries/.+$" + ], + "http://www.dipity.com/oembed/timeline/": [ + "^http(?:s)?://(?:www\\.)?dipity\\.com/timeline/.+$", + "^http(?:s)?://(?:www\\.)?dipity\\.com/voaweb/.+$" + ], + "https://sketchfab.com/oembed": [ + "^http(?:s)?://sketchfab\\.com/show/.+$" + ], + "https://api.meetup.com/oembed": [ + "^http(?:s)?://(?:www\\.)?meetup\\.com/.+$", + "^http(?:s)?://(?:www\\.)?meetup\\.ps/.+$" + ], + "https://roomshare.jp/oembed.{format}": [ + "^http(?:s)?://(?:www\\.)?roomshare\\.jp/(?:en/)?post/.+$" + ], + "http://crowdranking.com/api/oembed.{format}": [ + "^http(?:s)?://crowdranking\\.com/crowdrankings/.+$", + "^http(?:s)?://crowdranking\\.com/rankings/.+$", + "^http(?:s)?://crowdranking\\.com/topics/.+$", + "^http(?:s)?://crowdranking\\.com/widgets/.+$", + "^http(?:s)?://crowdranking\\.com/r/.+$" + ], + "http://openapi.etsy.com/svc/oembed/": [ + "^http(?:s)?://(?:www\\.)?etsy\\.com/listing/.+$" + ], + "https://audioboo.fm/publishing/oembed.{format}": [ + "^http(?:s)?://audioboo\\.fm/boos/.+$" + ], + "http://demo.clikthrough.com/services/oembed/": [ + "^http(?:s)?://demo\\.clikthrough\\.com/theater/video/.+$" + ], + "http://www.ifttt.com/oembed/": [ + "^http(?:s)?://ifttt\\.com/recipes/.+$" + ] +} \ No newline at end of file diff --git a/wagtail/wagtailembeds/embeds/oembed_api.py b/wagtail/wagtailembeds/embeds/oembed_api.py new file mode 100644 index 000000000..8f7945577 --- /dev/null +++ b/wagtail/wagtailembeds/embeds/oembed_api.py @@ -0,0 +1,52 @@ +import os, re +import urllib2, urllib +from datetime import datetime +import json + +class NotImplementedOembedException(Exception): + pass + +ENDPOINTS = {} + +def get_embed_oembed(url, max_width=None): + provider = None + for endpoint in ENDPOINTS.keys(): + for pattern in ENDPOINTS[endpoint]: + if re.match(pattern, url): + provider = endpoint + break + if not provider: + raise NotImplementedOembedException + params = {'url': url, 'format': 'json', } + if max_width: + params['maxwidth'] = max_width + req = provider+'?' +urllib.urlencode(params) + request = urllib2.Request(req) + opener = urllib2.build_opener() + # Some provicers were not working without a user agent + request.add_header('User-Agent','Mozilla/5.0') + return json.loads(opener.open(request).read()) + + +# Uses the public domain collection of oembed endpoints by Mathias Panzenbpeck (panzi) +# at https://github.com/panzi/oembedendpoints/blob/master/endpoints-regexp.json + +def load_oembed_endpoints(): + module_dir = os.path.dirname(__file__) + endpoints_path = os.path.join(module_dir, 'endpoints.json') + with open( endpoints_path) as f: + endpoints = json.loads(f.read()) + + for endpoint in endpoints.keys(): + endpoint_key = endpoint.replace('{format}', 'json') + + ENDPOINTS[endpoint_key]=[] + for pattern in endpoints[endpoint]: + ENDPOINTS[endpoint_key].append(re.compile(pattern)) + + + +load_oembed_endpoints() + + + \ No newline at end of file diff --git a/wagtail/wagtailembeds/embeds/unittests.py b/wagtail/wagtailembeds/embeds/unittests.py new file mode 100644 index 000000000..4c7653b99 --- /dev/null +++ b/wagtail/wagtailembeds/embeds/unittests.py @@ -0,0 +1,66 @@ +import unittest +import oembed + +# Test that a bunch of oembed examples is working +# If any of these is removed or changed then the unit test will fail +# This is a unittest TestCase (and not a django.test one) since django +# database is not actually needed for these tests + +TEST_DATA = [ + { + 'url':'http://www.youtube.com/watch?v=S3xAeTmsJfg', + 'title':'Animation: Ferret dance (A series of tubes)' + }, + { + 'url':'http://vimeo.com/86036070', + 'title':'Wagtail: A new Django CMS' + }, + { + 'url':'https://speakerdeck.com/harmstyler/an-introduction-to-django', + 'title':'An Introduction to Django' + }, + { + 'url':'https://ifttt.com/recipes/144705-new-twitter-followers-in-a-google-spreadsheet', + 'title':'New Twitter followers in a Google spreadsheet' + }, + { + 'url':'http://www.hulu.com/watch/20807/late-night-with-conan-obrien-wed-may-21-2008', + 'title':'Wed, May 21, 2008 (Late Night With Conan O\'Brien)' + }, + { + 'url':'http://www.flickr.com/photos/dfluke/5995957175/', + 'title':'Django pony!?' + }, + { + 'url':'http://www.slideshare.net/simon/the-django-web-application-framework', + 'title':'The Django Web Application Framework' + }, + { + 'url':'http://www.rdio.com/artist/The_Black_Keys/album/Brothers/', + 'title':'Brothers' + }, + { + 'url':'http://instagram.com/p/kFKCcEKmBq/', + 'title':'Family holidays in #Greece!' + }, + { + 'url':'https://www.kickstarter.com/projects/noujaimfilms/the-square-a-film-about-the-egyptian-revolution', + 'title':'Sundance Award Winning Film on the Egyptian Revolution' + }, + { + 'url':'http://www.dailymotion.com/video/xoxulz_babysitter_animals', + 'title':'Babysitter!' + } +] + +class TestEmbeds(unittest.TestCase): + def test_get_embed_oembed(self): + for td in TEST_DATA: + embed = oembed.get_embed_oembed_low(td['url']) + self.assertEqual(embed['title'], td['title'] ) + self.assertIsNotNone(embed['type'] ) + self.assertIsNotNone(embed['width'] ) + self.assertIsNotNone(embed['height'] ) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/wagtail/wagtailembeds/format.py b/wagtail/wagtailembeds/format.py index 3c050a83d..6fac83bb3 100644 --- a/wagtail/wagtailembeds/format.py +++ b/wagtail/wagtailembeds/format.py @@ -2,21 +2,24 @@ from __future__ import division # Use true division from django.utils.html import escape -from .embeds import get_embed +from .embeds.embed import get_embed def embed_to_frontend_html(url): - embed = get_embed(url) - if embed is not None: - # Work out ratio - if embed.width and embed.height: - ratio = str(embed.height / embed.width * 100) + "%" - else: - ratio = "0" + try: + embed = get_embed(url) + if embed is not None: + # Work out ratio + if embed.width and embed.height: + ratio = str(embed.height / embed.width * 100) + "%" + else: + ratio = "0" - # Build html - return '
%s
' % (ratio, embed.html) - else: + # Build html + return '
%s
' % (ratio, embed.html) + else: + return '' + except: return '' diff --git a/wagtail/wagtailembeds/templatetags/embed_filters.py b/wagtail/wagtailembeds/templatetags/embed_filters.py index e526bb9b6..dac91cda0 100644 --- a/wagtail/wagtailembeds/templatetags/embed_filters.py +++ b/wagtail/wagtailembeds/templatetags/embed_filters.py @@ -1,7 +1,7 @@ from django import template from django.utils.safestring import mark_safe -from wagtail.wagtailembeds.embeds import get_embed +from wagtail.wagtailembeds.embed.embeds import get_embed register = template.Library() @@ -10,9 +10,12 @@ register = template.Library() @register.filter def embed(url, max_width=None): embed = get_embed(url, max_width=max_width) - if embed is not None: - return mark_safe(embed.html) - else: + try: + if embed is not None: + return mark_safe(embed.html) + else: + return '' + except: return '' diff --git a/wagtail/wagtailembeds/tests.py b/wagtail/wagtailembeds/tests.py index 5cb8fb874..2a613cf74 100644 --- a/wagtail/wagtailembeds/tests.py +++ b/wagtail/wagtailembeds/tests.py @@ -1,4 +1,4 @@ -from django.test import TestCase +from django.test import TestCasez from .embeds import get_embed diff --git a/wagtail/wagtailembeds/views/chooser.py b/wagtail/wagtailembeds/views/chooser.py index 13a835acb..94695f1a3 100644 --- a/wagtail/wagtailembeds/views/chooser.py +++ b/wagtail/wagtailembeds/views/chooser.py @@ -5,6 +5,10 @@ from wagtail.wagtailadmin.modal_workflow import render_modal_workflow from wagtail.wagtailembeds.forms import EmbedForm from wagtail.wagtailembeds.format import embed_to_editor_html +from wagtail.wagtailembeds.embeds.oembed_api import NotImplementedOembedException +from wagtail.wagtailembeds.embeds.embed import EmbedlyException, AccessDeniedEmbedlyException, NotFoundEmbedlyException + + def chooser(request): form = EmbedForm() @@ -19,18 +23,27 @@ def chooser_upload(request): form = EmbedForm(request.POST, request.FILES) if form.is_valid(): - embed_html = embed_to_editor_html(form.cleaned_data['url']) - if embed_html != "": + try: + embed_html = embed_to_editor_html(form.cleaned_data['url']) return render_modal_workflow( request, None, 'wagtailembeds/chooser/embed_chosen.js', {'embed_html': embed_html} ) - else: + except Exception as e : + #print e + #import traceback + #traceback.print_exc() errors = form._errors.setdefault('url', ErrorList()) - if not hasattr(settings, 'EMBEDLY_KEY'): - errors.append('Please set EMBEDLY_KEY in your settings') + if type(e) == NotImplementedOembedException: + errors.append("This URL is not supported by an oembed provider. You may try embedding it using Embedly by setting a propery EMBEDLY_KEY in your settings.") + elif type(e) == AccessDeniedEmbedlyException: + errors.append("There seems to be a problem with your embedly API key. Please check your settings.") + elif type(e) == NotFoundEmbedlyException: + errors.append("The URL you are trying to embed cannot be found.") + elif type(e) == EmbedlyException: + errors.append("There seems to be an error with Embedly while trying to embed this URL. Please try again later.") else: - errors.append('This URL is not recognised') + errors.append(str(e) ) return render_modal_workflow(request, 'wagtailembeds/chooser/chooser.html', 'wagtailembeds/chooser/chooser.js', { 'form': form, }) From ead0ebeb4eff5ee2056f4c75114a14432861bc00 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Thu, 13 Feb 2014 04:09:18 +0200 Subject: [PATCH 04/13] Remove embedly from setup.pu requirements --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index e5cd3d5f5..09994f7e3 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,6 @@ setup( "django-modelcluster>=0.1", "elasticutils>=0.8.2", "pyelasticsearch>=0.6.1", - "Embedly>=0.5.0", "django-taggit==0.10", "Pillow>=2.3.0", "beautifulsoup4>=4.3.2", From a48cbd68e27e40b5e3ad5a3a4812cd6aaedcc6cd Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 10:53:58 +0000 Subject: [PATCH 05/13] update requirements in setup.py --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index e5cd3d5f5..74da04225 100644 --- a/setup.py +++ b/setup.py @@ -43,10 +43,8 @@ setup( "South>=0.8.4", "django-compressor>=1.3", "django-modelcluster>=0.1", - "elasticutils>=0.8.2", - "pyelasticsearch>=0.6.1", "Embedly>=0.5.0", - "django-taggit==0.10", + "django-taggit>=0.11.2", "Pillow>=2.3.0", "beautifulsoup4>=4.3.2", "lxml>=3.3.0", From 02e99b9e580c5bf05a7705994ae461f0d78d7b79 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 11:00:05 +0000 Subject: [PATCH 06/13] remove stray print statement from gravatar tag --- wagtail/wagtailadmin/templatetags/gravatar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wagtail/wagtailadmin/templatetags/gravatar.py b/wagtail/wagtailadmin/templatetags/gravatar.py index ed22637ea..5c69f4fd2 100644 --- a/wagtail/wagtailadmin/templatetags/gravatar.py +++ b/wagtail/wagtailadmin/templatetags/gravatar.py @@ -29,7 +29,6 @@ class GravatarUrlNode(template.Node): default = "blank" size = int(self.size) * 2 # requested at retina size by default and scaled down at point of use with css - print size; gravatar_url = "//www.gravatar.com/avatar/" + hashlib.md5(email.lower()).hexdigest() + "?" gravatar_url += urllib.urlencode({'s': str(size), 'd': default}) From a51e609f3f4d261379bce40b793cdc7b9e59aac7 Mon Sep 17 00:00:00 2001 From: Tom Dyson Date: Thu, 13 Feb 2014 11:41:56 +0000 Subject: [PATCH 07/13] Update README.rst with feature list --- README.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index f4b54f428..0a6350570 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,19 @@ Wagtail CMS =========== -Wagtail is a Django content management system focused on flexibility and user experience. Find out more at `wagtail.io `_ -and `torchbox.github.io/wagtail `_. +Wagtail is a Django content management system built originally for the `Royal College of Art `_ and focused on flexibility and user experience. Its features include: + +* A fast, attractive editor interface +* Complete control over design with standard Django templates +* Configure content types through standard Django models +* Tightly integrated search (with an `Elasticsearch `_ backend for production) +* Strong document and image management +* Wide support for embedded content +* Optional preview->submit->approve workflow +* Fast out of the box. `Varnish `_-friendly if you need it +* Tests! But not enough; we're working hard to improve this + +Find out more at `wagtail.io `_. Getting started ~~~~~~~~~~~~~~~ @@ -10,4 +21,4 @@ To get you up and running quickly, we've provided a demonstration site with all Contributing ~~~~~~~~~~~~ -If you're a Python or Django developer, fork the repo and get stuck in! Send us a useful pull request and we'll post you a `t-shirt `_. +If you're a Python or Django developer, fork the repo and get stuck in! Send us a useful pull request and we'll post you a `t-shirt `_. Our immediate priorities are better docs, more tests, internationalisation and localisation. From 7d4696c83ae3c50f2f90e604444cf2535fba0be1 Mon Sep 17 00:00:00 2001 From: Tom Dyson Date: Thu, 13 Feb 2014 12:08:43 +0000 Subject: [PATCH 08/13] Tree support in feature list. --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 0a6350570..49231a78c 100644 --- a/README.rst +++ b/README.rst @@ -9,6 +9,8 @@ Wagtail is a Django content management system built originally for the `Royal Co * Tightly integrated search (with an `Elasticsearch `_ backend for production) * Strong document and image management * Wide support for embedded content +* Simple, configurable permissions +* Support for tree-based content organisation * Optional preview->submit->approve workflow * Fast out of the box. `Varnish `_-friendly if you need it * Tests! But not enough; we're working hard to improve this From a77977a31cdfcdf955586ee796fa0ec406ae4b4a Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 12:44:49 +0000 Subject: [PATCH 09/13] add a general test runner for wagtail, accessible by running './runtests.py' or 'make test' from the wagtail repo, or 'manage.py test wagtail' from a django project such as wagtaildemo --- Makefile | 2 +- runtests.py | 61 ++++++++++++++++++++++++++++++++++ wagtail/tests/__init__.py | 8 +++++ wagtail/tests/urls.py | 35 +++++++++++++++++++ wagtail/wagtailembeds/tests.py | 5 ++- wagtail/wagtailsearch/tests.py | 3 +- 6 files changed, 111 insertions(+), 3 deletions(-) create mode 100755 runtests.py create mode 100644 wagtail/tests/__init__.py create mode 100644 wagtail/tests/urls.py diff --git a/Makefile b/Makefile index 712af70b9..8ac7d206c 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ lint: flake8 wagtail test: - python setup.py test + python runtests.py test-all: tox diff --git a/runtests.py b/runtests.py new file mode 100755 index 000000000..8bd7a7614 --- /dev/null +++ b/runtests.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +import sys, os, shutil + +from django.conf import settings, global_settings +from django.core.management import execute_from_command_line + +WAGTAIL_ROOT = os.path.dirname(__file__) +STATIC_ROOT = os.path.join(WAGTAIL_ROOT, 'test-static') + +if not settings.configured: + settings.configure( + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'wagtaildemo', + 'USER': 'postgres', + } + }, + ROOT_URLCONF='wagtail.tests.urls', + STATIC_URL='/static/', + STATIC_ROOT=STATIC_ROOT, + STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + 'compressor.finders.CompressorFinder', + ), + TEMPLATE_CONTEXT_PROCESSORS=global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( + 'django.core.context_processors.request', + ), + INSTALLED_APPS=[ + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.auth', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + 'taggit', + 'south', + 'compressor', + + 'wagtail.wagtailcore', + 'wagtail.wagtailadmin', + 'wagtail.wagtaildocs', + 'wagtail.wagtailsnippets', + 'wagtail.wagtailusers', + 'wagtail.wagtailimages', + 'wagtail.wagtailembeds', + 'wagtail.wagtailsearch', + 'wagtail.wagtailredirects', + ] + ) + + +def runtests(): + argv = sys.argv[:1] + ['test'] + sys.argv[1:] + execute_from_command_line(argv) + shutil.rmtree(STATIC_ROOT, ignore_errors=True) + + +if __name__ == '__main__': + runtests() + diff --git a/wagtail/tests/__init__.py b/wagtail/tests/__init__.py new file mode 100644 index 000000000..f250cfdac --- /dev/null +++ b/wagtail/tests/__init__.py @@ -0,0 +1,8 @@ +import wagtail.wagtailcore.tests +import wagtail.wagtailadmin.tests +import wagtail.wagtaildocs.tests +import wagtail.wagtailembeds.tests +import wagtail.wagtailimages.tests +import wagtail.wagtailredirects.tests +import wagtail.wagtailsearch.tests +import wagtail.wagtailsnippets.tests diff --git a/wagtail/tests/urls.py b/wagtail/tests/urls.py new file mode 100644 index 000000000..d133a4bf8 --- /dev/null +++ b/wagtail/tests/urls.py @@ -0,0 +1,35 @@ +from django.conf.urls import patterns, include, url + +from wagtail.wagtailcore import urls as wagtail_urls +from wagtail.wagtailadmin import urls as wagtailadmin_urls +from wagtail.wagtailimages import urls as wagtailimages_urls +from wagtail.wagtailembeds import urls as wagtailembeds_urls +from wagtail.wagtaildocs import admin_urls as wagtaildocs_admin_urls +from wagtail.wagtaildocs import urls as wagtaildocs_urls +from wagtail.wagtailsnippets import urls as wagtailsnippets_urls +from wagtail.wagtailsearch.urls import frontend as wagtailsearch_frontend_urls, admin as wagtailsearch_admin_urls +from wagtail.wagtailusers import urls as wagtailusers_urls +from wagtail.wagtailredirects import urls as wagtailredirects_urls + +# Signal handlers +from wagtail.wagtailsearch import register_signal_handlers as wagtailsearch_register_signal_handlers +wagtailsearch_register_signal_handlers() + + +urlpatterns = patterns('', + url(r'^admin/images/', include(wagtailimages_urls)), + url(r'^admin/embeds/', include(wagtailembeds_urls)), + url(r'^admin/documents/', include(wagtaildocs_admin_urls)), + url(r'^admin/snippets/', include(wagtailsnippets_urls)), + url(r'^admin/search/', include(wagtailsearch_admin_urls)), + url(r'^admin/users/', include(wagtailusers_urls)), + url(r'^admin/redirects/', include(wagtailredirects_urls)), + url(r'^admin/', include(wagtailadmin_urls)), + url(r'^search/', include(wagtailsearch_frontend_urls)), + + url(r'^documents/', include(wagtaildocs_urls)), + + # For anything not caught by a more specific rule above, hand over to + # Wagtail's serving mechanism + url(r'', include(wagtail_urls)), +) diff --git a/wagtail/wagtailembeds/tests.py b/wagtail/wagtailembeds/tests.py index 5cb8fb874..ab3aa3271 100644 --- a/wagtail/wagtailembeds/tests.py +++ b/wagtail/wagtailembeds/tests.py @@ -4,7 +4,10 @@ from .embeds import get_embed class TestEmbeds(TestCase): - def test_get_embed(self): + # FIXME: test currently depends on a valid EMBEDLY_KEY being set - we don't particularly + # want to put one in runtests.py. See https://github.com/torchbox/wagtail/issues/26 for + # progress on eliminating Embedly as a dependency + def DISABLEDtest_get_embed(self): # This test will fail if the video is removed or the title is changed embed = get_embed('http://www.youtube.com/watch?v=S3xAeTmsJfg') self.assertEqual(embed.title, 'Animation: Ferret dance (A series of tubes)') diff --git a/wagtail/wagtailsearch/tests.py b/wagtail/wagtailsearch/tests.py index af33946a9..972528331 100644 --- a/wagtail/wagtailsearch/tests.py +++ b/wagtail/wagtailsearch/tests.py @@ -371,7 +371,8 @@ class TestAdmin(TestCase): # Setup client self.c = Client() - self.c.login(username='test', password='password') + login = self.c.login(username='test', password='password') + self.assertEqual(login, True) def test_editors_picks(self): # Test index From f1e9a4703d9102702b5dbdd46fc76302aa62bc02 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 12:53:18 +0000 Subject: [PATCH 10/13] apparently we don't need imports in tests/__init__.py --- wagtail/tests/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/wagtail/tests/__init__.py b/wagtail/tests/__init__.py index f250cfdac..e69de29bb 100644 --- a/wagtail/tests/__init__.py +++ b/wagtail/tests/__init__.py @@ -1,8 +0,0 @@ -import wagtail.wagtailcore.tests -import wagtail.wagtailadmin.tests -import wagtail.wagtaildocs.tests -import wagtail.wagtailembeds.tests -import wagtail.wagtailimages.tests -import wagtail.wagtailredirects.tests -import wagtail.wagtailsearch.tests -import wagtail.wagtailsnippets.tests From e419cc06184880a1068c45d4d69b8a69eba0f8f6 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 12:53:52 +0000 Subject: [PATCH 11/13] ensure that test-static gets torn down even if tests fail --- runtests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtests.py b/runtests.py index 8bd7a7614..e27f67aa8 100755 --- a/runtests.py +++ b/runtests.py @@ -52,8 +52,10 @@ if not settings.configured: def runtests(): argv = sys.argv[:1] + ['test'] + sys.argv[1:] - execute_from_command_line(argv) - shutil.rmtree(STATIC_ROOT, ignore_errors=True) + try: + execute_from_command_line(argv) + finally: + shutil.rmtree(STATIC_ROOT, ignore_errors=True) if __name__ == '__main__': From ddd4b8a9c1bf71b29245a6f8d1193aea824e51ec Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 12:55:08 +0000 Subject: [PATCH 12/13] temporarily disable outdated wagtailembeds test --- wagtail/wagtailembeds/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wagtail/wagtailembeds/tests.py b/wagtail/wagtailembeds/tests.py index 1470f05ef..233156ea2 100644 --- a/wagtail/wagtailembeds/tests.py +++ b/wagtail/wagtailembeds/tests.py @@ -1,6 +1,6 @@ -from django.test import TestCasez +from django.test import TestCase -from .embeds import get_embed +#from .embeds import get_embed class TestEmbeds(TestCase): From 1e932c0b0b8fa945ac6588fdb5335445fe927ded Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 13 Feb 2014 14:44:05 +0000 Subject: [PATCH 13/13] fix typo in emed_filters typo --- wagtail/wagtailembeds/templatetags/embed_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wagtail/wagtailembeds/templatetags/embed_filters.py b/wagtail/wagtailembeds/templatetags/embed_filters.py index dac91cda0..5ca14a7cf 100644 --- a/wagtail/wagtailembeds/templatetags/embed_filters.py +++ b/wagtail/wagtailembeds/templatetags/embed_filters.py @@ -1,7 +1,7 @@ from django import template from django.utils.safestring import mark_safe -from wagtail.wagtailembeds.embed.embeds import get_embed +from wagtail.wagtailembeds.embeds.embed import get_embed register = template.Library()