Skip to content

Commit

Permalink
[#2939] Merged master
Browse files Browse the repository at this point in the history
  • Loading branch information
johnmartin committed Dec 13, 2012
2 parents 4c57d80 + b6dae85 commit 61c5d2b
Show file tree
Hide file tree
Showing 32 changed files with 685 additions and 443 deletions.
1 change: 1 addition & 0 deletions ckan/config/environment.py
Expand Up @@ -173,6 +173,7 @@ def find_controller(self, controller):
search.check_solr_schema_version()

config['routes.map'] = routing.make_map()
config['routes.named_routes'] = routing.named_routes
config['pylons.app_globals'] = app_globals.app_globals
# initialise the globals
config['pylons.app_globals']._init()
Expand Down
93 changes: 77 additions & 16 deletions ckan/config/routing.py
Expand Up @@ -5,13 +5,65 @@
refer to the routes manual at http://routes.groovie.org/docs/
"""
import re

from pylons import config
from routes import Mapper
from routes import Mapper as _Mapper

from ckan.plugins import PluginImplementations, IRoutes

named_routes = {}

routing_plugins = PluginImplementations(IRoutes)


class Mapper(_Mapper):
''' This Mapper allows us to intercept the connect calls used by routes
so that we can collect named routes and later use them to create links
via some helper functions like build_nav(). '''

def connect(self, *args, **kw):
'''Connect a new route, storing any named routes for later.
This custom connect() method wraps the standard connect() method,
and additionally saves any named routes that are connected in a dict
ckan.routing.named_routes, which ends up being accessible via the
Pylons config as config['routes.named_routes'].
Also takes some additional params:
:param ckan_icon: name of the icon to be associated with this route,
e.g. 'group', 'time'
:type ckan_icon: string
:param highlight_actions: space-separated list of controller actions
that should be treated as the same as this named route for menu
highlighting purposes, e.g. 'index search'
:type highlight_actions: string
'''
ckan_icon = kw.pop('ckan_icon', None)
highlight_actions = kw.pop('highlight_actions', kw.get('action', ''))
out = _Mapper.connect(self, *args, **kw)
if len(args) == 1 or args[0].startswith('_redirect_'):
return out
# we have a named route
needed = []
matches = re.findall('\{([^:}]*)(\}|:)', args[1])
for match in matches:
needed.append(match[0])
route_data = {
'icon': ckan_icon,
# needed lists the names of the parameters that need defining
# for the route to be generated
'needed': needed,
'controller': kw.get('controller'),
'action': kw.get('action', ''),
'highlight_actions': highlight_actions
}
named_routes[args[0]] = route_data
return out


def make_map():
"""Create, configure and return the routes Mapper"""
# import controllers here rather than at root level because
Expand Down Expand Up @@ -158,16 +210,18 @@ def make_map():
action='edit')
m.connect('related_delete', '/dataset/{id}/related/delete/{related_id}',
action='delete')
m.connect('related_list', '/dataset/{id}/related', action='list')
m.connect('related_list', '/dataset/{id}/related', action='list',
ckan_icon='picture')
m.connect('related_read', '/apps/{id}', action='read')
m.connect('related_dashboard', '/apps', action='dashboard')

with SubMapper(map, controller='package') as m:
m.connect('/dataset', action='search')
m.connect('search', '/dataset', action='search',
highlight_actions='index search')
m.connect('add dataset', '/dataset/new', action='new')
m.connect('/dataset/{action}',
requirements=dict(action='|'.join([
'list',
'new',
'autocomplete',
'search'
]))
Expand All @@ -188,18 +242,21 @@ def make_map():
'history',
'read_ajax',
'history_ajax',
'activity',
'followers',
'follow',
'activity',
'unfollow',
'delete',
'api_data',
]))
)
m.connect('dataset_followers', '/dataset/followers/{id}',
action='followers', ckan_icon='group')
m.connect('dataset_activity', '/dataset/activity/{id}',
action='activity', ckan_icon='time')
m.connect('/dataset/activity/{id}/{offset}', action='activity')
m.connect('/dataset/{id}.{format}', action='read')
m.connect('/dataset/{id}', action='read')
m.connect('dataset_read', '/dataset/{id}', action='read',
ckan_icon='sitemap')
m.connect('/dataset/{id}/resource/{resource_id}',
action='resource_read')
m.connect('/dataset/{id}/resource_delete/{resource_id}',
Expand All @@ -226,7 +283,8 @@ def make_map():
# These named routes are used for custom group forms which will use the
# names below based on the group.type ('group' is the default type)
with SubMapper(map, controller='group') as m:
m.connect('group_index', '/group', action='index')
m.connect('group_index', '/group', action='index',
highlight_actions='index search')
m.connect('group_list', '/group/list', action='list')
m.connect('group_new', '/group/new', action='new')
m.connect('group_action', '/group/{action}/{id}',
Expand All @@ -250,7 +308,7 @@ def make_map():

# organizations these basically end up being the same as groups
with SubMapper(map, controller='organization') as m:
m.connect('/organization', action='index')
m.connect('organizations_index', '/organization', action='index')
m.connect('/organization/list', action='list')
m.connect('/organization/new', action='new')
m.connect('/organization/{action}/{id}',
Expand Down Expand Up @@ -283,25 +341,28 @@ def make_map():
# Note: openid users have slashes in their ids, so need the wildcard
# in the route.
m.connect('/user/activity/{id}/{offset}', action='activity')
m.connect('/user/activity/{id}', action='activity')
m.connect('user_activity_stream', '/user/activity/{id}',
action='activity', ckan_icon='time')
m.connect('/dashboard/{offset}', action='dashboard')
m.connect('/dashboard', action='dashboard')
m.connect('/user/follow/{id}', action='follow')
m.connect('user_follow', '/user/follow/{id}', action='follow')
m.connect('/user/unfollow/{id}', action='unfollow')
m.connect('/user/followers/{id:.*}', action='followers')
m.connect('user_followers', '/user/followers/{id:.*}',
action='followers', ckan_icon='group')
m.connect('/user/edit/{id:.*}', action='edit')
m.connect('/user/reset/{id:.*}', action='perform_reset')
m.connect('/user/register', action='register')
m.connect('/user/login', action='login')
m.connect('register', '/user/register', action='register')
m.connect('login', '/user/login', action='login')
m.connect('/user/_logout', action='logout')
m.connect('/user/logged_in', action='logged_in')
m.connect('/user/logged_out', action='logged_out')
m.connect('/user/logged_out_redirect', action='logged_out_page')
m.connect('/user/reset', action='request_reset')
m.connect('/user/me', action='me')
m.connect('/user/set_lang/{lang}', action='set_lang')
m.connect('/user/{id:.*}', action='read')
m.connect('/user', action='index')
m.connect('user_datasets', '/user/{id:.*}', action='read',
ckan_icon='sitemap')
m.connect('user_index', '/user', action='index')

with SubMapper(map, controller='revision') as m:
m.connect('/revision', action='index')
Expand Down
1 change: 0 additions & 1 deletion ckan/controllers/related.py
Expand Up @@ -122,7 +122,6 @@ def list(self, id):
except logic.NotAuthorized:
base.abort(401, base._('Unauthorized to read package %s') % id)

c.action = 'related'
return base.render("package/related_list.html")

def _edit_or_new(self, id, related_id, is_edit):
Expand Down
3 changes: 2 additions & 1 deletion ckan/i18n/check_po_files.py
Expand Up @@ -11,7 +11,6 @@
'''
import re
import polib
import paste.script.command

def simple_conv_specs(s):
Expand Down Expand Up @@ -98,6 +97,8 @@ class CheckPoFiles(paste.script.command.Command):
parser = paste.script.command.Command.standard_parser(verbose=True)

def command(self):
import polib

test_simple_conv_specs()
test_mapping_keys()
test_replacement_fields()
Expand Down
65 changes: 49 additions & 16 deletions ckan/lib/app_globals.py
Expand Up @@ -29,6 +29,38 @@
'ckan.site_custom_css',
]

config_details = {
'ckan.favicon': {}, # default gets set in config.environment.py
'ckan.template_head_end': {},
'ckan.template_footer_end': {},
# has been setup in load_environment():
'ckan.site_id': {},
'ckan.recaptcha.publickey': {'name': 'recaptcha_publickey'},
'ckan.recaptcha.privatekey': {'name': 'recaptcha_publickey'},
'ckan.template_title_deliminater': {'default': '-'},
'ckan.template_head_end': {},
'ckan.template_footer_end': {},
'ckan.dumps_url': {},
'ckan.dumps_format': {},
'ckan.api_url': {},

# split string
'search.facets': {'default': 'groups tags res_format license',
'type': 'split',
'name': 'facets'},
'package_hide_extras': {'type': 'split'},
'plugins': {'type': 'split'},

# bool
'openid_enabled': {'default': 'true', 'type' : 'bool'},
'debug': {'default': 'false', 'type' : 'bool'},
'ckan.debug_supress_header' : {'default': 'false', 'type' : 'bool'},

# int
'ckan.datasets_per_page': {'default': '20', 'type': 'int'},
}


# A place to store the origional config options of we override them
_CONFIG_CACHE = {}

Expand Down Expand Up @@ -143,25 +175,26 @@ def _init(self):
facets = config.get('search.facets', 'groups tags res_format license capacity')
self.facets = facets.split()

# has been setup in load_environment():
self.site_id = config.get('ckan.site_id')

self.template_head_end = config.get('ckan.template_head_end', '')
self.template_footer_end = config.get('ckan.template_footer_end', '')

# hide these extras fields on package read
package_hide_extras = config.get('package_hide_extras', '').split()
self.package_hide_extras = package_hide_extras

self.openid_enabled = asbool(config.get('openid_enabled', 'true'))
# process the config_details to set globals
for name, options in config_details.items():
if 'name' in options:
key = options['name']
elif name.startswith('ckan.'):
key = name[5:]
else:
key = name
value = config.get(name, options.get('default', ''))

self.recaptcha_publickey = config.get('ckan.recaptcha.publickey', '')
self.recaptcha_privatekey = config.get('ckan.recaptcha.privatekey', '')
data_type = options.get('type')
if data_type == 'bool':
value = asbool(value)
elif data_type == 'int':
value = int(value)
elif data_type == 'split':
value = value.split()

datasets_per_page = int(config.get('ckan.datasets_per_page', '20'))
self.datasets_per_page = datasets_per_page
setattr(self, key, value)

self.debug_supress_header = asbool(config.get('ckan.debug_supress_header', 'false'))

app_globals = _Globals()
del _Globals
4 changes: 4 additions & 0 deletions ckan/lib/base.py
Expand Up @@ -128,6 +128,10 @@ def render_template():

# Jinja2 templates
if template_type == 'jinja2':
# We don't want to have the config in templates it should be
# accessed via g (app_globals) as this gives us flexability such
# as changing via database settings.
del globs['config']
# TODO should we raise error if genshi filters??
return render_jinja2(template_name, globs)

Expand Down

0 comments on commit 61c5d2b

Please sign in to comment.