aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2010-12-30 13:52:46 +0000
committerTom Christie2010-12-30 13:52:46 +0000
commitabb55a490964790a65ad5ef32397c6046d03d889 (patch)
tree944ff8c725822390c5934e7d1c44e82b8b5b036a
parent8a12f89aaacfc0839d6ab1e62b4b5046930517ba (diff)
downloaddjango-rest-framework-abb55a490964790a65ad5ef32397c6046d03d889.tar.bz2
Add styling and urlizing to html views of resources
-rw-r--r--src/rest/resource.py10
-rw-r--r--src/rest/templates/emitter.html13
-rw-r--r--src/rest/templatetags/__init__.py0
-rw-r--r--src/rest/templatetags/__init__.pycbin0 -> 163 bytes
-rw-r--r--src/rest/templatetags/urlize_quoted_links.py96
-rw-r--r--src/rest/templatetags/urlize_quoted_links.pycbin0 -> 4515 bytes
-rw-r--r--src/testapp/urls.py9
-rw-r--r--src/testapp/views.py11
-rw-r--r--src/urls.py2
9 files changed, 130 insertions, 11 deletions
diff --git a/src/rest/resource.py b/src/rest/resource.py
index 18421a19..4e9c4e05 100644
--- a/src/rest/resource.py
+++ b/src/rest/resource.py
@@ -1,6 +1,8 @@
from django.http import HttpResponse
-from decimal import Decimal
+from django.core.urlresolvers import reverse
from rest import emitters, parsers
+from decimal import Decimal
+
class Resource(object):
@@ -29,6 +31,7 @@ class Resource(object):
def __new__(cls, request, *args, **kwargs):
self = object.__new__(cls)
self.__init__()
+ self._request = request
return self._handle_request(request, *args, **kwargs)
def __init__(self):
@@ -145,3 +148,8 @@ class Resource(object):
def delete(self, headers={}, *args, **kwargs):
return self._not_implemented('delete')
+
+ def reverse(self, view, *args, **kwargs):
+ """Return a fully qualified URI for a view, using the current request as the base URI.
+ """
+ return self._request.build_absolute_uri(reverse(view, *args, **kwargs))
diff --git a/src/rest/templates/emitter.html b/src/rest/templates/emitter.html
index 4c843aa3..b3a2d823 100644
--- a/src/rest/templates/emitter.html
+++ b/src/rest/templates/emitter.html
@@ -1,11 +1,18 @@
-<?xml version="1.0" encoding="UTF-8"?>
+{% load urlize_quoted_links %}<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <style>
+ pre {border: 1px solid black; padding: 1em; background: #ffd}
+ </style>
+ </head>
<body>
<h1>{{ resource_name }}</h1>
<p>{{ resource_doc }}</p>
- <pre>
-{% include 'emitter.txt' %} </pre>
+ <pre>{% autoescape off %}HTTP Status {{ status }}
+{% for key, val in headers.items %}{{ key }}: {{ val }}
+{% endfor %}
+{{ content|urlize_quoted_links }}{% endautoescape %} </pre>
</body>
</html> \ No newline at end of file
diff --git a/src/rest/templatetags/__init__.py b/src/rest/templatetags/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/rest/templatetags/__init__.py
diff --git a/src/rest/templatetags/__init__.pyc b/src/rest/templatetags/__init__.pyc
new file mode 100644
index 00000000..69527f63
--- /dev/null
+++ b/src/rest/templatetags/__init__.pyc
Binary files differ
diff --git a/src/rest/templatetags/urlize_quoted_links.py b/src/rest/templatetags/urlize_quoted_links.py
new file mode 100644
index 00000000..cef179bf
--- /dev/null
+++ b/src/rest/templatetags/urlize_quoted_links.py
@@ -0,0 +1,96 @@
+"""Adds the custom filter 'urlize_quoted_links'
+
+This is identical to the built-in filter 'urlize' with the exception that
+single and double quotes are permitted as leading or trailing punctuation.
+"""
+
+# Almost all of this code is copied verbatim from django.utils.html
+# LEADING_PUNCTUATION and TRAILING_PUNCTUATION have been modified
+import re
+import string
+
+from django.utils.safestring import SafeData, mark_safe
+from django.utils.encoding import force_unicode
+from django.utils.http import urlquote
+from django.utils.html import escape
+from django import template
+
+# Configuration for urlize() function.
+LEADING_PUNCTUATION = ['(', '<', '&lt;', '"', "'"]
+TRAILING_PUNCTUATION = ['.', ',', ')', '>', '\n', '&gt;', '"', "'"]
+
+# List of possible strings used for bullets in bulleted lists.
+DOTS = ['&middot;', '*', '\xe2\x80\xa2', '&#149;', '&bull;', '&#8226;']
+
+unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
+word_split_re = re.compile(r'(\s+)')
+punctuation_re = re.compile('^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % \
+ ('|'.join([re.escape(x) for x in LEADING_PUNCTUATION]),
+ '|'.join([re.escape(x) for x in TRAILING_PUNCTUATION])))
+simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
+link_target_attribute_re = re.compile(r'(<a [^>]*?)target=[^\s>]+')
+html_gunk_re = re.compile(r'(?:<br clear="all">|<i><\/i>|<b><\/b>|<em><\/em>|<strong><\/strong>|<\/?smallcaps>|<\/?uppercase>)', re.IGNORECASE)
+hard_coded_bullets_re = re.compile(r'((?:<p>(?:%s).*?[a-zA-Z].*?</p>\s*)+)' % '|'.join([re.escape(x) for x in DOTS]), re.DOTALL)
+trailing_empty_content_re = re.compile(r'(?:<p>(?:&nbsp;|\s|<br \/>)*?</p>\s*)+\Z')
+
+def urlize_quoted_links(text, trim_url_limit=None, nofollow=False, autoescape=False):
+ """
+ Converts any URLs in text into clickable links.
+
+ Works on http://, https://, www. links and links ending in .org, .net or
+ .com. Links can have trailing punctuation (periods, commas, close-parens)
+ and leading punctuation (opening parens) and it'll still do the right
+ thing.
+
+ If trim_url_limit is not None, the URLs in link text longer than this limit
+ will truncated to trim_url_limit-3 characters and appended with an elipsis.
+
+ If nofollow is True, the URLs in link text will get a rel="nofollow"
+ attribute.
+
+ If autoescape is True, the link text and URLs will get autoescaped.
+ """
+ trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
+ safe_input = isinstance(text, SafeData)
+ words = word_split_re.split(force_unicode(text))
+ nofollow_attr = nofollow and ' rel="nofollow"' or ''
+ for i, word in enumerate(words):
+ match = None
+ if '.' in word or '@' in word or ':' in word:
+ match = punctuation_re.match(word)
+ if match:
+ lead, middle, trail = match.groups()
+ # Make URL we want to point to.
+ url = None
+ if middle.startswith('http://') or middle.startswith('https://'):
+ url = urlquote(middle, safe='/&=:;#?+*')
+ elif middle.startswith('www.') or ('@' not in middle and \
+ middle and middle[0] in string.ascii_letters + string.digits and \
+ (middle.endswith('.org') or middle.endswith('.net') or middle.endswith('.com'))):
+ url = urlquote('http://%s' % middle, safe='/&=:;#?+*')
+ elif '@' in middle and not ':' in middle and simple_email_re.match(middle):
+ url = 'mailto:%s' % middle
+ nofollow_attr = ''
+ # Make link.
+ if url:
+ trimmed = trim_url(middle)
+ if autoescape and not safe_input:
+ lead, trail = escape(lead), escape(trail)
+ url, trimmed = escape(url), escape(trimmed)
+ middle = '<a href="%s"%s>%s</a>' % (url, nofollow_attr, trimmed)
+ words[i] = mark_safe('%s%s%s' % (lead, middle, trail))
+ else:
+ if safe_input:
+ words[i] = mark_safe(word)
+ elif autoescape:
+ words[i] = escape(word)
+ elif safe_input:
+ words[i] = mark_safe(word)
+ elif autoescape:
+ words[i] = escape(word)
+ return u''.join(words)
+
+# Register urlize_quoted_links as a custom filter
+# http://docs.djangoproject.com/en/dev/howto/custom-template-tags/
+register = template.Library()
+register.filter(urlize_quoted_links) \ No newline at end of file
diff --git a/src/rest/templatetags/urlize_quoted_links.pyc b/src/rest/templatetags/urlize_quoted_links.pyc
new file mode 100644
index 00000000..b49e16b6
--- /dev/null
+++ b/src/rest/templatetags/urlize_quoted_links.pyc
Binary files differ
diff --git a/src/testapp/urls.py b/src/testapp/urls.py
index a41c156b..a7d430bc 100644
--- a/src/testapp/urls.py
+++ b/src/testapp/urls.py
@@ -1,8 +1,7 @@
from django.conf.urls.defaults import patterns
-from testapp.views import ReadOnlyResource, MirroringWriteResource
-
-urlpatterns = patterns('',
- (r'^read-only$', ReadOnlyResource),
- (r'^mirroring-write$', MirroringWriteResource),
+urlpatterns = patterns('testapp.views',
+ (r'^$', 'RootResource'),
+ (r'^read-only$', 'ReadOnlyResource'),
+ (r'^mirroring-write$', 'MirroringWriteResource'),
)
diff --git a/src/testapp/views.py b/src/testapp/views.py
index f0174414..eca4c0ae 100644
--- a/src/testapp/views.py
+++ b/src/testapp/views.py
@@ -1,6 +1,15 @@
-from decimal import Decimal
from rest.resource import Resource
+class RootResource(Resource):
+ """This is my docstring
+ """
+ allowed_methods = ('GET',)
+
+ def read(self, headers={}, *args, **kwargs):
+ return (200, {'read-only-api': self.reverse(ReadOnlyResource),
+ 'write-only-api': self.reverse(MirroringWriteResource)}, {})
+
+
class ReadOnlyResource(Resource):
"""This is my docstring
"""
diff --git a/src/urls.py b/src/urls.py
index f95e9afa..41a32efa 100644
--- a/src/urls.py
+++ b/src/urls.py
@@ -5,7 +5,7 @@ admin.autodiscover()
urlpatterns = patterns('',
# Example:
- (r'^testapp/', include('testapp.urls')),
+ (r'^', include('testapp.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
(r'^admin/doc/', include('django.contrib.admindocs.urls')),