Merge branch 'master' into sass

pull/137/head
Matt Westcott 2014-02-13 19:48:02 +00:00
commit 7113fe75d5
21 zmienionych plików z 804 dodań i 37 usunięć

Wyświetl plik

@ -15,7 +15,7 @@ lint:
flake8 wagtail
test:
python setup.py test
python runtests.py
test-all:
tox

Wyświetl plik

@ -1,7 +1,26 @@
Wagtail CMS
===========
Wagtail is a Django content management system focused on flexibility and user experience. Find out more at `wagtail.io <http://wagtail.io/>`_
or use the instructions at `torchbox.github.io/wagtail <http://torchbox.github.io/wagtail/>`_ to get started.
Wagtail is a Django content management system built originally for the `Royal College of Art <http://www.rca.ac.uk/>`_ and focused on flexibility and user experience. Its features include:
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.
* 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 <http://www.elasticsearch.org/>`_ 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 <https://www.varnish-cache.org/>`_-friendly if you need it
* Tests! But not enough; we're working hard to improve this
Find out more at `wagtail.io <http://wagtail.io/>`_.
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 <https://github.com/torchbox/wagtaildemo/>`_; see the `README <https://github.com/torchbox/wagtaildemo/blob/master/README.md>`_ 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 <https://twitter.com/WagtailCMS/status/432166799464210432/photo/1>`_. Our immediate priorities are better docs, more tests, internationalisation and localisation.

63
runtests.py 100755
Wyświetl plik

@ -0,0 +1,63 @@
#!/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:]
try:
execute_from_command_line(argv)
finally:
shutil.rmtree(STATIC_ROOT, ignore_errors=True)
if __name__ == '__main__':
runtests()

Wyświetl plik

@ -43,10 +43,7 @@ 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",

Wyświetl plik

Wyświetl plik

@ -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)),
)

Wyświetl plik

@ -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})

Wyświetl plik

@ -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'])

Wyświetl plik

@ -1,2 +1,2 @@
from .models import Embed
from .embeds import get_embed
from .embeds.embed import get_embed

Wyświetl plik

@ -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

Wyświetl plik

@ -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 = '<img src="%s" />' % (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

Wyświetl plik

@ -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/.+$"
]
}

Wyświetl plik

@ -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()

Wyświetl plik

@ -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()

Wyświetl plik

@ -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"
}
]

Wyświetl plik

@ -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 '<div style="padding-bottom: %s;" class="responsive-object">%s</div>' % (ratio, embed.html)
else:
# Build html
return '<div style="padding-bottom: %s;" class="responsive-object">%s</div>' % (ratio, embed.html)
else:
return ''
except:
return ''

Wyświetl plik

@ -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.embeds.embed 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 ''

Wyświetl plik

@ -1,10 +1,13 @@
from django.test import TestCase
from .embeds import get_embed
#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)')

Wyświetl plik

@ -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,
})

Wyświetl plik

@ -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