Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
johnglover committed Mar 19, 2012
2 parents 7882f1d + 43ca08a commit b603b8f
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 59 deletions.
48 changes: 21 additions & 27 deletions ckan/controllers/package.py
Expand Up @@ -200,13 +200,23 @@ def _content_type_for_format(self, fmt):
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, TextTemplate
from genshi.template import MarkupTemplate
from genshi.template.text import NewTextTemplate

types = {
"html": ("text/html; charset=utf-8", MarkupTemplate),
"rdf" : ("application/rdf+xml; charset=utf-8", MarkupTemplate),
"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], fmt, types[fmt][1]
return types[fmt][0], types[fmt][2], types[fmt][1]
return None, "html", (types["html"][1])


Expand All @@ -219,6 +229,9 @@ def read(self, id, format='html'):
ctype = "text/html; charset=utf-8"
id = "%s.%s" % (id, format)
format = 'html'
else:
format = extension

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

package_type = self._get_package_type(id.split('@')[0])
Expand Down Expand Up @@ -268,14 +281,6 @@ def read(self, id, format='html'):
ckan.logic.action.get.package_activity_list_html(context,
{'id': c.current_package_id})

if config.get('rdf_packages'):
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

PackageSaver().render_package(c.pkg_dict, context)

template = self._read_template( package_type )
Expand Down Expand Up @@ -529,22 +534,11 @@ def _get_package_type(self, id):
based on the package's type name (type). The plugin found
will be returned, or None if there is no plugin associated with
the type.
Uses a minimal context to do so. The main use of this method
is for figuring out which plugin to delegate to.
aborts if an exception is raised.
"""
context = {'model': model, 'session': model.Session,
'user': c.user or c.author}
try:
data = get_action('package_show')(context, {'id': id})
except NotFound:
abort(404, _('Dataset not found'))
except NotAuthorized:
abort(401, _('Unauthorized to read package %s') % id)

return data.get('type', 'package')
pkg = model.Package.get(id)
if pkg:
return pkg.type or 'package'
return None

def _save_new(self, context, package_type=None):
from ckan.lib.search import SearchIndexError
Expand Down
3 changes: 3 additions & 0 deletions ckan/controllers/storage.py
Expand Up @@ -48,6 +48,9 @@ def fix_stupid_pylons_encoding(data):

def create_pairtree_marker(folder):
""" Creates the pairtree marker for tests if it doesn't exist """
if not folder[:-1] == '/':
folder = folder + '/'

directory = os.path.dirname(folder)
if not os.path.exists(directory):
os.makedirs(directory)
Expand Down
35 changes: 19 additions & 16 deletions ckan/lib/base.py
Expand Up @@ -16,6 +16,7 @@
from pylons.i18n import _, ungettext, N_, gettext
from pylons.templating import cached_template, pylons_globals
from genshi.template import MarkupTemplate
from genshi.template.text import NewTextTemplate
from webhelpers.html import literal

import ckan
Expand All @@ -39,14 +40,14 @@ def abort(status_code=None, detail='', headers=None, comment=None):
# #1267 Convert detail to plain text, since WebOb 0.9.7.1 (which comes
# with Lucid) causes an exception when unicode is received.
detail = detail.encode('utf8')
return _abort(status_code=status_code,
return _abort(status_code=status_code,
detail=detail,
headers=headers,
headers=headers,
comment=comment)

def render(template_name, extra_vars=None, cache_key=None, cache_type=None,
def render(template_name, extra_vars=None, cache_key=None, cache_type=None,
cache_expire=None, method='xhtml', loader_class=MarkupTemplate):

def render_template():
globs = extra_vars or {}
globs.update(pylons_globals())
Expand All @@ -60,21 +61,23 @@ def render_template():
template = globs['app_globals'].genshi_loader.load(template_name,
cls=loader_class)
stream = template.generate(**globs)

for item in PluginImplementations(IGenshiStreamFilter):
stream = item.filter(stream)


if loader_class == NewTextTemplate:
return literal(stream.render(method="text", encoding=None))
return literal(stream.render(method=method, encoding=None, strip_whitespace=False))

if 'Pragma' in response.headers:
del response.headers["Pragma"]
if cache_key is not None or cache_type is not None:
response.headers["Cache-Control"] = "public"
response.headers["Cache-Control"] = "public"

if cache_expire is not None:
response.headers["Cache-Control"] = "max-age=%s, must-revalidate" % cache_expire
return cached_template(template_name, render_template, cache_key=cache_key,

return cached_template(template_name, render_template, cache_key=cache_key,
cache_type=cache_type, cache_expire=cache_expire)
#, ns_options=('method'), method=method)

Expand Down Expand Up @@ -136,7 +139,7 @@ def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
# available in environ['pylons.routes_dict']
try:
return WSGIController.__call__(self, environ, start_response)
finally:
Expand Down Expand Up @@ -260,13 +263,13 @@ def _get_timing_cache_path(self):
return path

@classmethod
def _get_user_editable_groups(cls):
def _get_user_editable_groups(cls):
if not hasattr(c, 'user'):
c.user = model.PSEUDO_USER__VISITOR
import ckan.authz # Todo: Move import to top of this file?
groups = ckan.authz.Authorizer.authorized_query(c.user, model.Group,
groups = ckan.authz.Authorizer.authorized_query(c.user, model.Group,
action=model.Action.EDIT).all()
return [g for g in groups if g.state==model.State.ACTIVE]
return [g for g in groups if g.state==model.State.ACTIVE]

def _get_package_dict(self, *args, **kwds):
import ckan.forms
Expand Down Expand Up @@ -325,7 +328,7 @@ def _handle_update_of_authz(self, domain_object):
update_or_add = 'add'
else:
user_or_authgroup = None
update_or_add = None
update_or_add = None

# Work out what role checkboxes are checked or unchecked
checked_roles = [ box_id for (box_id, value) in request.params.items() \
Expand Down
2 changes: 1 addition & 1 deletion ckan/templates/layout_base.html
Expand Up @@ -31,7 +31,7 @@

<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/jqueryui/1.8.14/css/jquery-ui.custom.css')}" type="text/css" media="screen, print" />
<link rel="stylesheet" href="${h.url_for_static('/css/boilerplate.css?v=2')}" />
<link rel="stylesheet" href="${h.url_for_static('/css/bootstrap.css')}" type="text/css" media="screen projection" />
<link rel="stylesheet" href="${h.url_for_static('/css/bootstrap.css')}" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${h.url_for_static('/css/chosen.css')}" type="text/css" />
<link rel="stylesheet" href="${h.url_for_static('/css/blueprint/screen.css')}" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${h.url_for_static('/css/blueprint/print.css')}" type="text/css" media="print" />
Expand Down
44 changes: 44 additions & 0 deletions ckan/templates/package/read.n3
@@ -0,0 +1,44 @@
@prefix : <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

<${ g.site_url + h.url_for(controller='package',action='read',id=c.pkg_dict['name'])}> a dcat:Dataset;
dct:description "${c.pkg_dict['notes']}";
dct:identifier "${c.pkg_dict['name']}";
dct:relation [
rdf:value "";
:label "change_note" ],
[
rdf:value "";
:label "definition_note" ],
[
rdf:value "";
:label "editorial_note" ],
[
rdf:value "";
:label "example_note" ],
[
rdf:value "";
:label "history_note" ],
[
rdf:value "";
:label "scope_note" ],
[
rdf:value "";
:label "skos_note" ],
[
rdf:value "";
:label "temporal_granularity" ],
[
rdf:value "";
:label "type_of_dataset" ],
[
rdf:value "";
:label "update_frequency" ];
dct:title "${c.pkg_dict['title']}";
:label "${c.pkg_dict['name']}";
= <urn:uuid:${c.pkg_dict['id']}>;
foaf:homepage <http://127.0.0.1:5000/dataset/testt> .
20 changes: 9 additions & 11 deletions ckan/tests/functional/test_autoneg.py
Expand Up @@ -59,20 +59,18 @@ def test_html_rdf(self):
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
assert location.startswith('http://test.com/package/'), location
assert response.status == 200, response.status
ctype = response.header("Content-Type")
assert 'application/rdf+xml' in ctype, ctype

def test_turtle(self):
def test_n3(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
assert location.startswith('http://test.com/package/'), location
response = self.app.get(url, headers={"Accept": "text/n3"})
assert response.status == 200, response.status
ctype = response.header("Content-Type")
assert 'text/n3' in ctype, ctype

2 changes: 1 addition & 1 deletion ckan/tests/functional/test_package.py
Expand Up @@ -375,7 +375,7 @@ def test_read_plugin_hook(self):
# existed before. I don't know if this is a problem? I expect it
# can be fixed by allowing the package to be passed in to the plugin,
# either via the function argument, or adding it to the c object.
assert plugin.calls['read'] == 2, plugin.calls
assert plugin.calls['read'] == 1, plugin.calls
plugins.unload(plugin)

def test_resource_list(self):
Expand Down
1 change: 1 addition & 0 deletions ckan/tests/functional/test_storage.py
Expand Up @@ -18,6 +18,7 @@ def setup_class(cls):
config.local_conf['ofs.impl'] = 'pairtree'
config.local_conf['ckan.storage.bucket'] = 'ckantest'
config.local_conf['ofs.storage_dir'] = '/tmp/ckan-test-ckanext-storage'

create_pairtree_marker( config.local_conf['ofs.storage_dir'] )
wsgiapp = make_app(config.global_conf, **config.local_conf)
cls.app = paste.fixture.TestApp(wsgiapp)
Expand Down
9 changes: 6 additions & 3 deletions doc/linked-data-and-rdf.rst
Expand Up @@ -17,9 +17,7 @@ In CKAN >= 1.6.1, basic RDF support will be available directly in core.
Configuration
-------------

.. todo:: fill this in (imagine that config instructions for extension will be
in extension but for >= 1.6.1 we have stuff in core)

When using the built-in RDF support (CKAN >= 1.6.1) there is no configuration required. By default requests for RDF data will return the RDF generated from the built-in 'packages/read.rdf' template, which can be overridden using the extra-templates directive.

Accessing Linked Data
=====================
Expand All @@ -31,6 +29,11 @@ example::
curl -L -H "Accept: application/rdf+xml" http://thedatahub.org/dataset/gold-prices
curl -L -H "Accept: text/n3" http://thedatahub.org/dataset/gold-prices

An alternative method of retrieving the data is to add .rdf to the name of the dataset to download::

curl -L http://thedatahub.org/dataset/gold-prices.rdf
curl -L http://thedatahub.org/dataset/gold-prices.n3


Schema Mapping
==============
Expand Down

0 comments on commit b603b8f

Please sign in to comment.