[ckan-dev] patch-1088-autoneg-is-wonky

William Waites ww at styx.org
Sun Apr 17 10:30:08 UTC 2011


Right, so I've fixed autoneg on ckan, following as best as
I understand the process... I've made a branch called 
bug-1088-autoneg-is-wonky and merged it into default.

It would be very good if it were merged into release as
well before the next release... If someone could do this
and look at what I've done with the pip-requirements I
would be grateful.

I've patched the main ckan.net by hand with the attached
patch, because that was where the problem mostly was.
Beware of this when you upgrade it - in order not to lose
the patch it will obviously have to be merged into whatever
ckan is upgraded to...

Cheers,
-w
-- 
William Waites                <mailto:ww at styx.org>
http://river.styx.org/ww/        <sip:ww at styx.org>
F4B3 39BF E775 CF42 0BAB  3DF0 BE40 A6DF B06F FD45
-------------- next part --------------
diff -r 8c42f555b5f6 ckan/controllers/package.py
--- a/ckan/controllers/package.py	Fri Apr 15 17:20:26 2011 +0100
+++ b/ckan/controllers/package.py	Sun Apr 17 11:22:58 2011 +0100
@@ -7,6 +7,7 @@
 import genshi
 from pylons import config, cache
 from pylons.i18n import get_lang, _
+from autoneg.accept import negotiate
 
 from ckan.lib.base import *
 from ckan.lib.search import query_for, QueryOptions, SearchError
@@ -26,6 +27,15 @@
                     for k, v in params]
     return url + u'?' + urlencode(params)
 
+autoneg_cfg = [
+    ("application", "xhtml+xml", ["html"]),
+    ("text", "html", ["html"]),
+    ("application", "rdf+xml", ["rdf"]),
+    ("application", "turtle", ["ttl"]),
+    ("text", "plain", ["nt"]),
+    ("text", "x-graphviz", ["dot"]),
+    ]
+
 class PackageController(BaseController):
     authorizer = ckan.authz.Authorizer()
     extensions = PluginImplementations(IPackageController)
@@ -129,13 +139,14 @@
         # used by disqus plugin
         c.current_package_id = c.pkg.id
         
-        if config.get('rdf_packages'):
-            accept_headers = request.headers.get('Accept', '')
-            if 'application/rdf+xml' in accept_headers and \
-                   not 'text/html' in accept_headers:
-                rdf_url = '%s%s' % (config['rdf_packages'], c.pkg.name)
-                redirect(rdf_url, code=303)
-
+        if config.get('rdf_packages') is not None:
+            accept_header = request.headers.get('Accept', '*/*')
+            for content_type, exts in negotiate(autoneg_cfg, accept_header):
+                if "html" not in exts: 
+                    rdf_url = '%s%s.%s' % (config['rdf_packages'], c.pkg.id, exts[0])
+                    redirect(rdf_url, code=303)
+                break
+            
         #is the user allowed to see this package?
         auth_for_read = self.authorizer.am_authorized(c, model.Action.READ, c.pkg)
         if not auth_for_read:
diff -r 8c42f555b5f6 ckan/templates/package/read.html
--- a/ckan/templates/package/read.html	Fri Apr 15 17:20:26 2011 +0100
+++ b/ckan/templates/package/read.html	Sun Apr 17 11:22:58 2011 +0100
@@ -98,7 +98,8 @@
 
   <py:if test="config.get('rdf_packages')">
     <py:def function="optional_head">
-      <link rel="alternate" type="application/rdf+xml" title="RDF Version" href="${config['rdf_packages'] + c.pkg.name[0] + '/' + c.pkg.name[1] + '/' + c.pkg.name}" />
+      <link rel="alternate" type="application/rdf+xml" title="RDF/XML" href="${config['rdf_packages'] + '/' + c.pkg.id + '.rdf' }" />
+      <link rel="alternate" type="application/turtle" title="RDF/Turtle" href="${config['rdf_packages'] + '/' + c.pkg.id + '.ttl' }" />
     </py:def>
   </py:if>
 
diff -r 8c42f555b5f6 ckan/templates/package/read_core.html
--- a/ckan/templates/package/read_core.html	Fri Apr 15 17:20:26 2011 +0100
+++ b/ckan/templates/package/read_core.html	Sun Apr 17 11:22:58 2011 +0100
@@ -126,7 +126,9 @@
             </code>
             <py:if test="config.get('rdf_packages')">
                 <h5>RDF:</h5>
-                <code><a href="${config.get('rdf_packages') + c.pkg.name[0] + '/' + c.pkg.name[1] + '/' + c.pkg.name}">${config.get('rdf_packages') + c.pkg.name[0] + '/' + c.pkg.name[1] + '/' + c.pkg.name}</a></code>
+                <code><a href="${config.get('rdf_packages') + '/' + c.pkg.id + '.rdf'}">RDF/XML</a></code>
+                <code><a href="${config.get('rdf_packages') + '/' + c.pkg.id + '.ttl'}">Turtle</a></code>
+                <code><a href="${config.get('rdf_packages') + '/' + c.pkg.id + '.nt'}">N-Triples</a></code>
             </py:if>
             <p>
                 The information on this page and the downloads / resources are also available using the 
diff -r 8c42f555b5f6 ckan/tests/functional/test_autoneg.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ckan/tests/functional/test_autoneg.py	Sun Apr 17 11:22:58 2011 +0100
@@ -0,0 +1,77 @@
+import cgi
+
+from paste.fixture import AppError
+from pylons import config
+from pylons import c
+from genshi.core import escape as genshi_escape
+from difflib import unified_diff
+from nose.plugins.skip import SkipTest
+
+from ckan.tests import *
+from ckan.tests.html_check import HtmlCheckMethods
+from ckan.tests.pylons_controller import PylonsTestCase
+from ckan.lib.create_test_data import CreateTestData
+from ckan import model
+from test_package import TestPackageForm as _TestPackageForm
+
+class TestAutoneg(_TestPackageForm, PylonsTestCase):
+    @classmethod
+    def setup_class(cls):
+        PylonsTestCase.setup_class()
+        CreateTestData.create()
+
+    @classmethod
+    def teardown_class(cls):
+        CreateTestData.delete()
+        model.repo.rebuild_db()
+
+    def test_default(self):
+        url = url_for(controller='package', action='read', id='annakarenina')
+        response = self.app.get(url)
+        assert response.status == 200, response.status
+        content_type = response.header("Content-Type")
+        assert "html" in content_type, content_type
+
+    def test_chrome(self):
+        url = url_for(controller='package', action='read', id='annakarenina')
+        ## this is what chrome sends... notice how it prefers pictures of web pages
+        ## to web pages
+        accept = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+        response = self.app.get(url, headers={"Accept": accept})
+        assert response.status == 200, response.status
+        content_type = response.header("Content-Type")
+        assert "html" in content_type, content_type
+
+    def test_firefox(self):
+        url = url_for(controller='package', action='read', id='annakarenina')
+        ## this is what firefox sends
+        accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
+        response = self.app.get(url, headers={"Accept": accept})
+        assert response.status == 200, response.status
+        content_type = response.header("Content-Type")
+        assert "html" in content_type, content_type
+
+    def test_html_rdf(self):
+        url = url_for(controller='package', action='read', id='annakarenina')
+        ## this is an important test. rdf appears first, but with a lower priority
+        ## than html. we expect to get html back
+        accept = "application/rdf+xml;q=0.5,application/xhtml+xml,text/html;q=0.9"
+        response = self.app.get(url, headers={"Accept": accept})
+        assert response.status == 200, response.status
+        content_type = response.header("Content-Type")
+        assert "html" in content_type, content_type
+        
+    def test_rdfxml(self):
+        url = url_for(controller='package', action='read', id='annakarenina')
+        response = self.app.get(url, headers={"Accept": "application/rdf+xml"})
+        assert response.status == 303, response.status
+        location = response.header("Location")
+        assert location.endswith(".rdf"), location
+
+    def test_turtle(self):
+        url = url_for(controller='package', action='read', id='annakarenina')
+        response = self.app.get(url, headers={"Accept": "application/turtle"})
+        assert response.status == 303, response.status
+        location = response.header("Location")
+        assert location.endswith(".ttl"), location
+
diff -r 8c42f555b5f6 requires/lucid_missing.txt
--- a/requires/lucid_missing.txt	Fri Apr 15 17:20:26 2011 +0100
+++ b/requires/lucid_missing.txt	Sun Apr 17 11:22:58 2011 +0100
@@ -1,6 +1,7 @@
 # These are packages that we rely on that aren't present in Lucid. We package
 # them and put them in our own CKAN repository
 
+autoneg>=0.5
 # pyutilib.component.core>=4.1,<4.1.99
 -e svn+https://software.sandia.gov/svn/public/pyutilib/pyutilib.component.core/trunk@1972#egg=pyutilib.component.core
 # licenses==0.4,<0.6.99


More information about the ckan-dev mailing list