diff --git a/README.rst b/README.rst index f4b54f428..49231a78c 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,21 @@ 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 +* 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 + +Find out more at `wagtail.io `_. Getting started ~~~~~~~~~~~~~~~ @@ -10,4 +23,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. diff --git a/setup.py b/setup.py index 74da04225..e92d9df23 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,6 @@ setup( "South>=0.8.4", "django-compressor>=1.3", "django-modelcluster>=0.1", - "Embedly>=0.5.0", "django-taggit>=0.11.2", "Pillow>=2.3.0", "beautifulsoup4>=4.3.2", 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.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/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/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" +} +] 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 ab3aa3271..1470f05ef 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, })