Skip to content

Commit

Permalink
Fixed merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
rossjones committed May 24, 2012
1 parent e245727 commit b7e235a
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 28 deletions.
48 changes: 20 additions & 28 deletions ckan/controllers/package.py
Expand Up @@ -4,6 +4,8 @@

from pylons import config
from pylons.i18n import _
from genshi.template import MarkupTemplate
from genshi.template.text import NewTextTemplate

from ckan.logic import get_action, check_access
from ckan.lib.helpers import date_str_to_datetime
Expand All @@ -19,6 +21,7 @@
import ckan.authz
import ckan.rating
import ckan.misc
import ckan.lib.accept as accept
from home import CACHE_PARAMETER

from ckan.lib.plugins import lookup_package_plugin
Expand Down Expand Up @@ -221,44 +224,33 @@ def pager_url(q=None, page=None):

return render( self._search_template(package_type) )

def _content_type_for_format(self, fmt):
def _content_type_from_extension(self, ext):
ct,mu,ext = accept.parse_extension(ext)
if not ct:
return None, None, None,
return ct, ext, (NewTextTemplate,MarkupTemplate)[mu]

def _content_type_from_accept(self):
"""
Given a requested format this method determines the content-type
to set and the genshi template loader to use in order to render
it accurately. TextTemplate must be used for non-xml templates
whilst all that are some sort of XML should use MarkupTemplate.
"""
from genshi.template import MarkupTemplate
from genshi.template.text import NewTextTemplate

types = {
"html": ("text/html; charset=utf-8", MarkupTemplate, 'html'),
"rdf" : ("application/rdf+xml; charset=utf-8", MarkupTemplate, 'rdf'),
"n3" : ("text/n3; charset=utf-8", NewTextTemplate, 'n3'),
"application/rdf+xml" : ("application/rdf+xml; charset=utf-8", MarkupTemplate,'rdf'),
"text/n3": ("text/n3; charset=utf-8", NewTextTemplate, 'n3'),
}
# Check the accept header first
accept = request.headers.get('Accept', '')
if accept and accept in types:
return types[accept][0], types[accept][2], types[accept][1]

if fmt in types:
return types[fmt][0], types[fmt][2], types[fmt][1]
return None, "html", (types["html"][1])
ct,mu,ext = accept.parse_header(request.headers.get('Accept', ''))
return ct, ext, (NewTextTemplate,MarkupTemplate)[mu]


def read(self, id, format='html'):
# Check we know the content type, if not then it is likely a revision
# and therefore we should merge the format onto the end of id
ctype,extension,loader = self._content_type_for_format(format)
if not ctype:
# Reconstitute the ID if we don't know what content type to use
ctype = "text/html; charset=utf-8"
id = "%s.%s" % (id, format)
format = 'html'
if not format == 'html':
ctype,extension,loader = self._content_type_from_extension(format)
if not ctype:
# An unknown format, we'll carry on in case it is a
# revision specifier and re-constitute the original id
id = "%s.%s" % (id, format)
ctype, format, loader = "text/html; charset=utf-8", "html", MarkupTemplate
else:
format = extension
ctype,extension,loader = self._content_type_from_accept()

response.headers['Content-Type'] = ctype

Expand Down
62 changes: 62 additions & 0 deletions ckan/lib/accept.py
@@ -0,0 +1,62 @@
"""
Simple accept header parsing to determins which content type we should deliver
back to the caller. This is mostly used by the rdf export functionality
"""
import re
import operator

# For parsing {name};q=x and {name} style fields from the accept header
accept_re = re.compile("^(?P<ct>[^;]+)[ \t]*(;[ \t]*q=(?P<q>[0-9.]+)){0,1}$")

accept_types = {
# Name : ContentType, Is Markup?, Extension
"text/html" : ("text/html; charset=utf-8", True, 'html'),
"text/n3" : ("text/n3; charset=utf-8", False, 'n3'),
"text/plain" : ("text/plain; charset=utf-8", False, 'txt'),
"application/rdf+xml" : ("application/rdf+xml; charset=utf-8", True, 'rdf'),
}
accept_by_extension = {
"rdf": "application/rdf+xml",
"n3" : "text/n3"
}

def parse_extension( file_ext ):
"""
If provided an extension, this function will return the details
for that extension, if we know about it.
"""
ext = accept_by_extension.get(file_ext,None)
if ext:
return accept_types[ext]
return (None,None,None,)


def parse_header( accept_header='' ):
"""
Parses the supplied accept header and tries to determine
which content types we can provide the response in that will keep the
client happy.
We will always provide html as the default if we can't see anything else
but we will also need to take into account the q score.
The return values are be content-type,is-markup,extension
"""
if accept_header is None:
accept_header = ""

acceptable = {}
for typ in accept_header.split(','):
m = accept_re.match(typ)
if m:
key = m.groups(0)[0]
qscore = m.groups(0)[2] or 1.0
acceptable[key] = float(qscore)

for ctype in sorted(acceptable.iteritems(),
key=operator.itemgetter(1),
reverse=True):
if ctype[0] in accept_types:
return accept_types[ctype[0]]

return accept_types["text/html"]
65 changes: 65 additions & 0 deletions ckan/tests/lib/test_accept.py
@@ -0,0 +1,65 @@
from nose.tools import assert_equal

import ckan.lib.accept as accept

class TestAccept:
def test_accept_invalid(self):
ct, markup, ext = accept.parse_header(None)
assert_equal( ct, "text/html; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "html")

def test_accept_invalid2(self):
ct, markup, ext = accept.parse_header("")
assert_equal( ct, "text/html; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "html")

def test_accept_invalid3(self):
ct, markup, ext = accept.parse_header("wombles")
assert_equal( ct, "text/html; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "html")


def test_accept_valid(self):
a = "text/turtle,application/turtle,application/rdf+xml,text/plain;q=0.8,*/*;q=.5"
ct, markup, ext = accept.parse_header(a)
assert_equal( ct, "application/rdf+xml; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "rdf")

def test_accept_valid2(self):
a = "text/turtle,application/turtle,application/rdf+xml;q=0.9,text/plain;q=0.8,*/*;q=.5"
ct, markup, ext = accept.parse_header(a)
assert_equal( ct, "application/rdf+xml; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "rdf")

def test_accept_valid4(self):
a = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
ct, markup, ext = accept.parse_header(a)
assert_equal( ct, "text/html; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "html")

def test_accept_valid5(self):
a = "application/rdf+xml;q=0.5,application/xhtml+xml,text/html;q=0.9"
ct, markup, ext = accept.parse_header(a)
assert_equal( ct, "text/html; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "html")

def test_accept_valid6(self):
a = "application/rdf+xml;q=0.9,application/xhtml+xml,text/html;q=0.5"
ct, markup, ext = accept.parse_header(a)
assert_equal( ct, "application/rdf+xml; charset=utf-8")
assert_equal( markup, True)
assert_equal( ext, "rdf")

def test_accept_valid7(self):
a = "text/turtle,application/turtle,application/rdf+xml;q=0.8,text/plain;q=0.9,*/*;q=.5"
ct, markup, ext = accept.parse_header(a)
assert_equal( ct, "text/plain; charset=utf-8")
assert_equal( markup, False)
assert_equal( ext, "txt")

0 comments on commit b7e235a

Please sign in to comment.