def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) PUT_POST = dict(method=['PUT','POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) from ckan.lib.plugins import register_package_plugins from ckan.lib.plugins import register_group_plugins map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.minimization = False map.explicit = True # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect('/error/{action}', controller='error') map.connect('/error/{action}/{id}', controller='error') map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in routing_plugins: map = plugin.before_map(map) map.connect('home', '/', controller='home', action='index') map.connect('about', '/about', controller='home', action='about') # CKAN API versioned. register_list = [ 'package', 'dataset', 'resource', 'tag', 'group', 'related', 'revision', 'licenses', 'rating', 'user', 'activity' ] register_list_str = '|'.join(register_list) # /api ver 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', ver='/3') as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: m.connect('', action='get_api') m.connect('/search/{register}', action='search') # /api ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/tag_counts', action='tag_counts') m.connect('/rest', action='index') m.connect('/qos/throughput/', action='throughput', conditions=GET) # /api/rest ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1', requirements=dict(register=register_list_str)) as m: m.connect('/rest/{register}', action='list', conditions=GET) m.connect('/rest/{register}', action='create', conditions=POST) m.connect('/rest/{register}/{id}', action='show', conditions=GET) m.connect('/rest/{register}/{id}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}', action='update', conditions=POST) m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE) m.connect('/rest/{register}/{id}/:subregister', action='list', conditions=GET) m.connect('/rest/{register}/{id}/:subregister', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='show', conditions=GET) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='delete', conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/util/user/autocomplete', action='user_autocomplete') m.connect('/util/is_slug_valid', action='is_slug_valid', conditions=GET) m.connect('/util/dataset/autocomplete', action='dataset_autocomplete', conditions=GET) m.connect('/util/tag/autocomplete', action='tag_autocomplete', conditions=GET) m.connect('/util/resource/format_autocomplete', action='format_autocomplete', conditions=GET) m.connect('/util/resource/format_icon', action='format_icon', conditions=GET) m.connect('/util/group/autocomplete', action='group_autocomplete') m.connect('/util/markdown', action='markdown') m.connect('/util/dataset/munge_name', action='munge_package_name') m.connect('/util/dataset/munge_title_to_name', action='munge_title_to_package_name') m.connect('/util/tag/munge', action='munge_tag') m.connect('/util/status', action='status') m.connect('/util/snippet/{snippet_path:.*}', action='snippet') m.connect('/i18n/{lang}', action='i18n_js_translations') ########### ## /END API ########### ## Webstore if config.get('ckan.datastore.enabled', False): with SubMapper(map, controller='datastore') as m: m.connect('datastore_read', '/api/data/{id}{url:(/.*)?}', action='read', url='', conditions=GET) m.connect('datastore_write', '/api/data/{id}{url:(/.*)?}', action='write', url='', conditions=PUT_POST_DELETE) m.connect('datastore_read_shortcut', '/dataset/{dataset}/resource/{id}/api{url:(/.*)?}', action='read', url='', conditions=GET) m.connect('datastore_write_shortcut', '/dataset/{dataset}/resource/{id}/api{url:(/.*)?}', action='write', url='', conditions=PUT_POST_DELETE) map.redirect('/packages', '/dataset') map.redirect('/packages/{url:.*}', '/dataset/{url}') map.redirect('/package', '/dataset') map.redirect('/package/{url:.*}', '/dataset/{url}') ##to get back formalchemy uncomment these lines ##map.connect('/package/new', controller='package_formalchemy', action='new') ##map.connect('/package/edit/{id}', controller='package_formalchemy', action='edit') with SubMapper(map, controller='related') as m: m.connect('related_new', '/dataset/{id}/related/new', action='new') m.connect('related_edit', '/dataset/{id}/related/edit/{related_id}', 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_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('/dataset/{action}', requirements=dict(action='|'.join([ 'list', 'new', 'autocomplete', 'search' ])) ) m.connect('/dataset/{action}/{id}/{revision}', action='read_ajax', requirements=dict(action='|'.join([ 'read', 'edit', 'authz', 'history', ])) ) m.connect('/dataset/{action}/{id}', requirements=dict(action='|'.join([ 'edit', 'new_metadata', 'new_resource', 'authz', 'history', 'read_ajax', 'history_ajax', 'activity', 'followers', 'follow', 'activity', 'unfollow', 'delete', 'api_data', ])) ) m.connect('/dataset/activity/{id}/{offset}', action='activity') m.connect('/dataset/{id}.{format}', action='read') m.connect('/dataset/{id}', action='read') m.connect('/dataset/{id}/resource/{resource_id}', action='resource_read') m.connect('/dataset/{id}/resource_delete/{resource_id}', action='resource_delete') m.connect('/dataset/{id}/resource_edit/{resource_id}', action='resource_edit') m.connect('/dataset/{id}/resource/{resource_id}/download', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/dataset/{id}/resource/{resource_id}/viewer', action='resource_embedded_dataviewer', width="960", height="800") m.connect('/dataset/{id}/resource/{resource_id}/preview/{preview_type}', action='resource_datapreview') # group map.redirect('/groups', '/group') map.redirect('/groups/{url:.*}', '/group/{url}') ##to get back formalchemy uncomment these lines ##map.connect('/group/new', controller='group_formalchemy', action='new') ##map.connect('/group/edit/{id}', controller='group_formalchemy', action='edit') # 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_list', '/group/list', action='list') m.connect('group_new', '/group/new', action='new') m.connect('group_action', '/group/{action}/{id}', requirements=dict(action='|'.join([ 'edit', 'authz', 'delete', 'history', 'followers', 'follow', 'unfollow', 'admins', 'about', 'activity', ])) ) m.connect('group_activity', '/group/activity/{id}/{offset}', action='activity'), m.connect('group_read', '/group/{id}', action='read') register_package_plugins(map) register_group_plugins(map) # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users map.redirect('/users/{url:.*}', '/user/{url}') map.redirect('/user/', '/user') with SubMapper(map, controller='user') as m: m.connect('/user/edit', action='edit') # 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('/dashboard/{offset}', action='dashboard') m.connect('/dashboard', action='dashboard') m.connect('/user/follow/{id}', action='follow') m.connect('/user/unfollow/{id}', action='unfollow') m.connect('/user/followers/{id:.*}', action='followers') 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('/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') with SubMapper(map, controller='revision') as m: m.connect('/revision', action='index') m.connect('/revision/edit/{id}', action='edit') m.connect('/revision/diff/{id}', action='diff') m.connect('/revision/list', action='list') m.connect('/revision/{id}', action='read') # feeds with SubMapper(map, controller='feed') as m: m.connect('/feeds/group/{id}.atom', action='group') m.connect('/feeds/tag/{id}.atom', action='tag') m.connect('/feeds/dataset.atom', action='general') m.connect('/feeds/custom.atom', action='custom') map.connect('ckanadmin_index', '/ckan-admin', controller='admin', action='index') map.connect('ckanadmin', '/ckan-admin/{action}', controller='admin') # Storage routes with SubMapper(map, controller='ckan.controllers.storage:StorageAPIController') as m: m.connect('storage_api', '/api/storage', action='index') m.connect('storage_api_set_metadata', '/api/storage/metadata/{label:.*}', action='set_metadata', conditions=PUT_POST) m.connect('storage_api_get_metadata', '/api/storage/metadata/{label:.*}', action='get_metadata', conditions=GET) m.connect('storage_api_auth_request', '/api/storage/auth/request/{label:.*}', action='auth_request') m.connect('storage_api_auth_form', '/api/storage/auth/form/{label:.*}', action='auth_form') with SubMapper(map, controller='ckan.controllers.storage:StorageController') as m: m.connect('storage_upload', '/storage/upload', action='upload') m.connect('storage_upload_handle', '/storage/upload_handle', action='upload_handle') m.connect('storage_upload_success', '/storage/upload/success', action='success') m.connect('storage_upload_success_empty', '/storage/upload/success_empty', action='success_empty') m.connect('storage_file', '/storage/f/{label:.*}', action='file') with SubMapper(map, controller='util') as m: m.connect('/i18n/strings_{lang}.js', action='i18n_js_strings') m.connect('/util/redirect', action='redirect') m.connect('/testing/primer', action='primer') m.connect('/testing/markup', action='markup') for plugin in routing_plugins: map = plugin.after_map(map) # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect('/favicon.ico', config.get('ckan.favicon')) map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently') map.connect('/*url', controller='template', action='view') return map
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=["GET"]) PUT = dict(method=["PUT"]) POST = dict(method=["POST"]) DELETE = dict(method=["DELETE"]) GET_POST = dict(method=["GET", "POST"]) PUT_POST = dict(method=["PUT", "POST"]) PUT_POST_DELETE = dict(method=["PUT", "POST", "DELETE"]) OPTIONS = dict(method=["OPTIONS"]) from ckan.lib.plugins import register_package_plugins from ckan.lib.plugins import register_group_plugins map = Mapper(directory=config["pylons.paths"]["controllers"], always_scan=config["debug"]) map.minimization = False map.explicit = True # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect("/error/{action}", controller="error") map.connect("/error/{action}/{id}", controller="error") map.connect("*url", controller="home", action="cors_options", conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in routing_plugins: map = plugin.before_map(map) map.connect("home", "/", controller="home", action="index") map.connect("about", "/about", controller="home", action="about") # CKAN API versioned. register_list = [ "package", "dataset", "resource", "tag", "group", "related", "revision", "licenses", "rating", "user", "activity", ] register_list_str = "|".join(register_list) # /api ver 3 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/3|}", ver="/3") as m: m.connect("/action/{logic_function}", action="action", conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/1|/2|/3|}", ver="/1") as m: m.connect("", action="get_api") m.connect("/search/{register}", action="search") # /api ver 1, 2 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/1|/2|}", ver="/1") as m: m.connect("/tag_counts", action="tag_counts") m.connect("/rest", action="index") m.connect("/qos/throughput/", action="throughput", conditions=GET) # /api/rest ver 1, 2 or none with SubMapper( map, controller="api", path_prefix="/api{ver:/1|/2|}", ver="/1", requirements=dict(register=register_list_str) ) as m: m.connect("/rest/{register}", action="list", conditions=GET) m.connect("/rest/{register}", action="create", conditions=POST) m.connect("/rest/{register}/{id}", action="show", conditions=GET) m.connect("/rest/{register}/{id}", action="update", conditions=PUT) m.connect("/rest/{register}/{id}", action="update", conditions=POST) m.connect("/rest/{register}/{id}", action="delete", conditions=DELETE) m.connect("/rest/{register}/{id}/:subregister", action="list", conditions=GET) m.connect("/rest/{register}/{id}/:subregister", action="create", conditions=POST) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="create", conditions=POST) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="show", conditions=GET) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="update", conditions=PUT) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="delete", conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/1|/2|}", ver="/1") as m: m.connect("/util/user/autocomplete", action="user_autocomplete") m.connect("/util/is_slug_valid", action="is_slug_valid", conditions=GET) m.connect("/util/dataset/autocomplete", action="dataset_autocomplete", conditions=GET) m.connect("/util/tag/autocomplete", action="tag_autocomplete", conditions=GET) m.connect("/util/resource/format_autocomplete", action="format_autocomplete", conditions=GET) m.connect("/util/resource/format_icon", action="format_icon", conditions=GET) m.connect("/util/group/autocomplete", action="group_autocomplete") m.connect("/util/markdown", action="markdown") m.connect("/util/dataset/munge_name", action="munge_package_name") m.connect("/util/dataset/munge_title_to_name", action="munge_title_to_package_name") m.connect("/util/tag/munge", action="munge_tag") m.connect("/util/status", action="status") m.connect("/util/snippet/{snippet_path:.*}", action="snippet") m.connect("/i18n/{lang}", action="i18n_js_translations") ########### ## /END API ########### ## Webstore if config.get("ckan.datastore.enabled", False): with SubMapper(map, controller="datastore") as m: m.connect("datastore_read", "/api/data/{id}{url:(/.*)?}", action="read", url="", conditions=GET) m.connect( "datastore_write", "/api/data/{id}{url:(/.*)?}", action="write", url="", conditions=PUT_POST_DELETE ) m.connect( "datastore_read_shortcut", "/dataset/{dataset}/resource/{id}/api{url:(/.*)?}", action="read", url="", conditions=GET, ) m.connect( "datastore_write_shortcut", "/dataset/{dataset}/resource/{id}/api{url:(/.*)?}", action="write", url="", conditions=PUT_POST_DELETE, ) map.redirect("/packages", "/dataset") map.redirect("/packages/{url:.*}", "/dataset/{url}") map.redirect("/package", "/dataset") map.redirect("/package/{url:.*}", "/dataset/{url}") ##to get back formalchemy uncomment these lines ##map.connect('/package/new', controller='package_formalchemy', action='new') ##map.connect('/package/edit/{id}', controller='package_formalchemy', action='edit') with SubMapper(map, controller="related") as m: m.connect("related_new", "/dataset/{id}/related/new", action="new") m.connect("related_edit", "/dataset/{id}/related/edit/{related_id}", 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_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("/dataset/{action}", requirements=dict(action="|".join(["list", "new", "autocomplete", "search"]))) m.connect( "/dataset/{action}/{id}/{revision}", action="read_ajax", requirements=dict(action="|".join(["read", "edit", "authz", "history"])), ) m.connect( "/dataset/{action}/{id}", requirements=dict( action="|".join( [ "edit", "new_metadata", "new_resource", "authz", "history", "read_ajax", "history_ajax", "followers", "follow", "unfollow", "delete", "api_data", ] ) ), ) m.connect("/dataset/{id}.{format}", action="read") m.connect("/dataset/{id}", action="read") m.connect("/dataset/{id}/resource/{resource_id}", action="resource_read") m.connect("/dataset/{id}/resource_delete/{resource_id}", action="resource_delete") m.connect("/dataset/{id}/resource_edit/{resource_id}", action="resource_edit") m.connect("/dataset/{id}/resource/{resource_id}/download", action="resource_download") m.connect("/dataset/{id}/resource/{resource_id}/embed", action="resource_embedded_dataviewer") m.connect( "/dataset/{id}/resource/{resource_id}/viewer", action="resource_embedded_dataviewer", width="960", height="800", ) m.connect("/dataset/{id}/resource/{resource_id}/preview/{preview_type}", action="resource_datapreview") # group map.redirect("/groups", "/group") map.redirect("/groups/{url:.*}", "/group/{url}") ##to get back formalchemy uncomment these lines ##map.connect('/group/new', controller='group_formalchemy', action='new') ##map.connect('/group/edit/{id}', controller='group_formalchemy', action='edit') # 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_list", "/group/list", action="list") m.connect("group_new", "/group/new", action="new") m.connect( "group_action", "/group/{action}/{id}", requirements=dict( action="|".join( [ "edit", "authz", "delete", "history", "followers", "follow", "unfollow", "admins", "about", "activity", ] ) ), ) m.connect("group_read", "/group/{id}", action="read") register_package_plugins(map) register_group_plugins(map) # tags map.redirect("/tags", "/tag") map.redirect("/tags/{url:.*}", "/tag/{url}") map.redirect("/tag/read/{url:.*}", "/tag/{url}", _redirect_code="301 Moved Permanently") map.connect("/tag", controller="tag", action="index") map.connect("/tag/{id}", controller="tag", action="read") # users map.redirect("/users/{url:.*}", "/user/{url}") map.redirect("/user/", "/user") with SubMapper(map, controller="user") as m: m.connect("/user/edit", action="edit") # Note: openid users have slashes in their ids, so need the wildcard # in the route. m.connect("/user/activity/{id}", action="activity") m.connect("/user/dashboard", action="dashboard") m.connect("/user/follow/{id}", action="follow") m.connect("/user/unfollow/{id}", action="unfollow") m.connect("/user/followers/{id:.*}", action="followers") 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("/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") with SubMapper(map, controller="revision") as m: m.connect("/revision", action="index") m.connect("/revision/edit/{id}", action="edit") m.connect("/revision/diff/{id}", action="diff") m.connect("/revision/list", action="list") m.connect("/revision/{id}", action="read") # feeds with SubMapper(map, controller="feed") as m: m.connect("/feeds/group/{id}.atom", action="group") m.connect("/feeds/tag/{id}.atom", action="tag") m.connect("/feeds/dataset.atom", action="general") m.connect("/feeds/custom.atom", action="custom") map.connect("ckanadmin_index", "/ckan-admin", controller="admin", action="index") map.connect("ckanadmin", "/ckan-admin/{action}", controller="admin") # Storage routes with SubMapper(map, controller="ckan.controllers.storage:StorageAPIController") as m: m.connect("storage_api", "/api/storage", action="index") m.connect( "storage_api_set_metadata", "/api/storage/metadata/{label:.*}", action="set_metadata", conditions=PUT_POST ) m.connect("storage_api_get_metadata", "/api/storage/metadata/{label:.*}", action="get_metadata", conditions=GET) m.connect("storage_api_auth_request", "/api/storage/auth/request/{label:.*}", action="auth_request") m.connect("storage_api_auth_form", "/api/storage/auth/form/{label:.*}", action="auth_form") with SubMapper(map, controller="ckan.controllers.storage:StorageController") as m: m.connect("storage_upload", "/storage/upload", action="upload") m.connect("storage_upload_handle", "/storage/upload_handle", action="upload_handle") m.connect("storage_upload_success", "/storage/upload/success", action="success") m.connect("storage_upload_success_empty", "/storage/upload/success_empty", action="success_empty") m.connect("storage_file", "/storage/f/{label:.*}", action="file") with SubMapper(map, controller="util") as m: m.connect("/i18n/strings_{lang}.js", action="i18n_js_strings") m.connect("/util/redirect", action="redirect") m.connect("/testing/primer", action="primer") m.connect("/testing/markup", action="markup") for plugin in routing_plugins: map = plugin.after_map(map) # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect("/favicon.ico", config.get("ckan.favicon")) map.redirect("/*(url)/", "/{url}", _redirect_code="301 Moved Permanently") map.connect("/*url", controller="template", action="view") return map
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) PUT_POST = dict(method=['PUT', 'POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) from ckan.lib.plugins import register_package_plugins from ckan.lib.plugins import register_group_plugins map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.minimization = False map.explicit = True # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect('/error/{action}', controller='error') map.connect('/error/{action}/{id}', controller='error') map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in routing_plugins: map = plugin.before_map(map) map.connect('home', '/home', controller='home', action='index') map.connect('about', '/about', controller='home', action='about') # CKAN API versioned. register_list = [ 'package', 'catalogo', 'resource', 'tag', 'group', 'related', 'revision', 'licenses', 'rating', 'user', 'activity' ] register_list_str = '|'.join(register_list) # /api ver 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', ver='/3') as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: m.connect('', action='get_api') m.connect('/search/{register}', action='search') m.connect('/mostDownloadedDataset', action='mostDownloadedDataset') m.connect('/mostRecentDataset', action='mostRecentDataset') m.connect('/getDataCount', action='getDataCount') # /api ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/tag_counts', action='tag_counts') m.connect('/rest', action='index') m.connect('/qos/throughput/', action='throughput', conditions=GET) # /api/rest ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1', requirements=dict(register=register_list_str) ) as m: m.connect('/rest/{register}', action='list', conditions=GET) m.connect('/rest/{register}', action='create', conditions=POST) m.connect('/rest/{register}/{id}', action='show', conditions=GET) m.connect('/rest/{register}/{id}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}', action='update', conditions=POST) m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE) m.connect('/rest/{register}/{id}/:subregister', action='list', conditions=GET) m.connect('/rest/{register}/{id}/:subregister', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='show', conditions=GET) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='delete', conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/util/user/autocomplete', action='user_autocomplete') m.connect('/util/is_slug_valid', action='is_slug_valid', conditions=GET) m.connect('/util/dataset/autocomplete', action='dataset_autocomplete', conditions=GET) m.connect('/util/tag/autocomplete', action='tag_autocomplete', conditions=GET) m.connect('/util/resource/format_autocomplete', action='format_autocomplete', conditions=GET) m.connect('/util/resource/format_icon', action='format_icon', conditions=GET) m.connect('/util/group/autocomplete', action='group_autocomplete') m.connect('/util/markdown', action='markdown') m.connect('/util/dataset/munge_name', action='munge_package_name') m.connect('/util/dataset/munge_title_to_name', action='munge_title_to_package_name') m.connect('/util/tag/munge', action='munge_tag') m.connect('/util/status', action='status') m.connect('/util/snippet/{snippet_path:.*}', action='snippet') m.connect('/i18n/{lang}', action='i18n_js_translations') ########### ## /END API ########### map.redirect('/packages', '/catalogo') map.redirect('/packages/{url:.*}', '/{url}') map.redirect('/package', '/catalogo') map.redirect('/package/{url:.*}', '/{url}') with SubMapper(map, controller='related') as m: m.connect('related_new', '/{id}/related/new', action='new') m.connect('related_edit', '/{id}/related/edit/{related_id}', action='edit') m.connect('related_delete', '/{id}/related/delete/{related_id}', action='delete') m.connect('related_list', '/{id}/related', action='list', ckan_icon='picture') m.connect('related_read', '/related/{id}', action='read') m.connect('related_dashboard', '/related', action='dashboard') # group map.redirect('/groups', '/group') map.redirect('/groups/{url:.*}', '/group/{url}') ##to get back formalchemy uncomment these lines ##map.connect('/group/new', controller='group_formalchemy', action='new') ##map.connect('/group/edit/{id}', controller='group_formalchemy', action='edit') # 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', 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}', requirements=dict(action='|'.join([ 'edit', 'delete', 'member_new', 'member_delete', 'history', 'followers', 'follow', 'unfollow', 'admins', 'activity', ]))) m.connect('group_about', '/group/about/{id}', action='about', ckan_icon='info-sign'), m.connect('group_edit', '/group/edit/{id}', action='edit', ckan_icon='edit') m.connect('group_members', '/group/members/{id}', action='members', ckan_icon='group'), m.connect('group_activity', '/group/activity/{id}/{offset}', action='activity', ckan_icon='time'), m.connect('group_read', '/group/{id}', action='read', ckan_icon='sitemap') # organizations these basically end up being the same as groups with SubMapper(map, controller='organization') as m: m.connect('organizations_index', '/organization', action='index') m.connect('/organization/list', action='list') m.connect('/organization/new', action='new') m.connect('/organization/{action}/{id}', requirements=dict(action='|'.join([ 'delete', 'admins', 'member_new', 'member_delete', 'history' ]))) m.connect('organization_activity', '/organization/activity/{id}', action='activity', ckan_icon='time') m.connect('organization_read', '/organization/{id}', action='read') m.connect('organization_about', '/organization/about/{id}', action='about', ckan_icon='info-sign') m.connect('organization_read', '/organization/{id}', action='read', ckan_icon='sitemap') m.connect('organization_edit', '/organization/edit/{id}', action='edit', ckan_icon='edit') m.connect('organization_members', '/organization/members/{id}', action='members', ckan_icon='group') m.connect('organization_bulk_process', '/organization/bulk_process/{id}', action='bulk_process', ckan_icon='sitemap') register_package_plugins(map) register_group_plugins(map) # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users map.redirect('/users/{url:.*}', '/user/{url}') map.redirect('/user/', '/user') with SubMapper(map, controller='user') as m: m.connect('/user/edit', action='edit') # 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_stream', '/user/activity/{id}', action='activity', ckan_icon='time') m.connect('user_dashboard', '/dashboard', action='dashboard', ckan_icon='list') m.connect('user_dashboard_datasets', '/dashboard/datasets', action='dashboard_datasets', ckan_icon='sitemap') m.connect('user_dashboard_groups', '/dashboard/groups', action='dashboard_groups', ckan_icon='group') m.connect('user_dashboard_organizations', '/dashboard/organizations', action='dashboard_organizations', ckan_icon='building') m.connect('/dashboard/{offset}', action='dashboard') m.connect('user_follow', '/user/follow/{id}', action='follow') m.connect('/user/unfollow/{id}', action='unfollow') m.connect('user_followers', '/user/followers/{id:.*}', action='followers', ckan_icon='group') m.connect('user_edit', '/user/edit/{id:.*}', action='edit', ckan_icon='cog') m.connect('/user/reset/{id:.*}', action='perform_reset') 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_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') m.connect('/revision/edit/{id}', action='edit') m.connect('/revision/diff/{id}', action='diff') m.connect('/revision/list', action='list') m.connect('/revision/{id}', action='read') # feeds with SubMapper(map, controller='feed') as m: m.connect('/feeds/group/{id}.atom', action='group') m.connect('/feeds/tag/{id}.atom', action='tag') m.connect('/feeds/dataset.atom', action='general') m.connect('/feeds/custom.atom', action='custom') map.connect('ckanadmin_index', '/ckan-admin', controller='admin', action='index', ckan_icon='legal') map.connect('ckanadmin_config', '/ckan-admin/config', controller='admin', action='config', ckan_icon='check') map.connect('ckanadmin', '/ckan-admin/{action}', controller='admin') # Storage routes with SubMapper(map, controller='ckan.controllers.storage:StorageAPIController') as m: m.connect('storage_api', '/api/storage', action='index') m.connect('storage_api_set_metadata', '/api/storage/metadata/{label:.*}', action='set_metadata', conditions=PUT_POST) m.connect('storage_api_get_metadata', '/api/storage/metadata/{label:.*}', action='get_metadata', conditions=GET) m.connect('storage_api_auth_request', '/api/storage/auth/request/{label:.*}', action='auth_request') m.connect('storage_api_auth_form', '/api/storage/auth/form/{label:.*}', action='auth_form') with SubMapper(map, controller='ckan.controllers.storage:StorageController') as m: m.connect('storage_upload', '/storage/upload', action='upload') m.connect('storage_upload_handle', '/storage/upload_handle', action='upload_handle') m.connect('storage_upload_success', '/storage/upload/success', action='success') m.connect('storage_upload_success_empty', '/storage/upload/success_empty', action='success_empty') m.connect('storage_file', '/storage/f/{label:.*}', action='file') with SubMapper(map, controller='util') as m: m.connect('/i18n/strings_{lang}.js', action='i18n_js_strings') m.connect('/util/redirect', action='redirect') m.connect('/testing/primer', action='primer') m.connect('/testing/markup', action='markup') with SubMapper(map, controller='package') as m: m.connect('search', '', action='search', highlight_actions='index search') m.connect('/', action='search', highlight_actions='index search') m.connect('/catalogo.rdf', action='metadata_catalog') m.connect('/catalogo.html', action='search', highlight_actions='index search') m.connect('/ciencia-tecnologia', action='searchAOD', tema='ciencia-tecnologia') m.connect('/comercio', action='searchAOD', tema='comercio') m.connect('/cultura-ocio', action='searchAOD', tema='cultura-ocio') m.connect('/demografia', action='searchAOD', tema='demografia') m.connect('/deporte', action='searchAOD', tema='deporte') m.connect('/economia', action='searchAOD', tema='economia') m.connect('/educacion', action='searchAOD', tema='educacion') m.connect('/empleo', action='searchAOD', tema='empleo') m.connect('/energia', action='searchAOD', tema='energia') m.connect('/hacienda', action='searchAOD', tema='hacienda') m.connect('/industria', action='searchAOD', tema='industria') m.connect('/legislacion-justicia', action='searchAOD', tema='legislacion-justicia') m.connect('/medio-ambiente', action='searchAOD', tema='medio-ambiente') m.connect('/medio-rural-pesca', action='searchAOD', tema='medio-rural-pesca') m.connect('/salud', action='searchAOD', tema='salud') m.connect('/sector-publico', action='searchAOD', tema='sector-publico') m.connect('/seguridad', action='searchAOD', tema='seguridad') m.connect('/sociedad-bienestar', action='searchAOD', tema='sociedad-bienestar') m.connect('/transporte', action='searchAOD', tema='transporte') m.connect('/turismo', action='searchAOD', tema='turismo') m.connect('/urbanismo-infraestructuras', action='searchAOD', tema='urbanismo-infraestructuras') m.connect('/vivienda', action='searchAOD', tema='vivienda') m.connect('/tablas', action='searchAOD', tipo='tablas') m.connect('/{tema}/tablas', action='searchAOD', tipo='tablas') m.connect('/arboles-de-datos', action='searchAOD', tipo='arboles-de-datos') m.connect('/{tema}/arboles-de-datos', action='searchAOD', tipo='arboles-de-datos') m.connect('/mapas', action='searchAOD', tipo='mapas') m.connect('/{tema}/mapas', action='searchAOD', tipo='mapas') m.connect('/fotos', action='searchAOD', tipo='fotos') m.connect('/{tema}/fotos', action='searchAOD', tipo='fotos') m.connect('/rss', action='searchAOD', tipo='rss') m.connect('/{tema}/rss', action='searchAOD', tipo='rss') m.connect('/informacion-estadistica', action='searchAOD', tipo='informacion-estadistica') m.connect('/{tema}/informacion-estadistica', action='searchAOD', tipo='informacion-estadistica') # m.connect('/bienal/{grupo}', action='searchAOD', tipo='bienal') # m.connect('/{tema}/bienal/{grupo}', action='searchAOD', tipo='bienal') # m.connect('/bienal/{grupo}/{subgrupo}', action='searchAOD', tipo='bienal') # m.connect('/{tema}/bienal/{grupo}/{subgrupo}', action='searchAOD', tipo='bienal') m.connect('add dataset', '/new', action='new') m.connect('/{action}', requirements=dict(action='|'.join([ 'list', 'autocomplete', 'search' ]))) m.connect('/{action}/{id}/{revision}', action='read_ajax', requirements=dict(action='|'.join([ 'read', 'edit', 'history', ]))) m.connect('/{action}/{id}', requirements=dict(action='|'.join([ 'edit', 'new_metadata', 'new_resource', 'history', 'read_ajax', 'history_ajax', 'follow', 'activity', 'unfollow', 'delete', 'api_data', ]))) m.connect('dataset_followers', '/followers/{id}', action='followers', ckan_icon='group') m.connect('dataset_activity', '/activity/{id}', action='activity', ckan_icon='time') m.connect('/activity/{id}/{offset}', action='activity') m.connect('/{id}.{format}', action='read') m.connect('dataset_read', '/{id}', action='read', ckan_icon='sitemap') m.connect('/{id}/resource/{resource_id}', action='resource_read') m.connect('/{id}/resource_delete/{resource_id}', action='resource_delete') m.connect('/{id}/resource_edit/{resource_id}', action='resource_edit') m.connect('/{id}/resource/{resource_id}/download', action='resource_download') m.connect('/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/{id}/resource/{resource_id}/viewer', action='resource_embedded_dataviewer', width="960", height="800") m.connect('/{id}/resource/{resource_id}/preview', action='resource_datapreview') for plugin in routing_plugins: map = plugin.after_map(map) # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect('/favicon.ico', config.get('ckan.favicon')) map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently') map.connect('/*url', controller='template', action='view') return map
def make_flask_stack(conf, **app_conf): """ This has to pass the flask app through all the same middleware that Pylons used """ root = os.path.dirname( os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) debug = asbool(conf.get('debug', conf.get('DEBUG', False))) testing = asbool(app_conf.get('testing', app_conf.get('TESTING', False))) app = flask_app = CKANFlask(__name__) app.debug = debug app.testing = testing app.template_folder = os.path.join(root, 'templates') app.app_ctx_globals_class = CKAN_AppCtxGlobals app.url_rule_class = CKAN_Rule app.jinja_options = jinja_extensions.get_jinja_env_options() # Update Flask config with the CKAN values. We use the common config # object as values might have been modified on `load_environment` if config: app.config.update(config) else: app.config.update(conf) app.config.update(app_conf) # Do all the Flask-specific stuff before adding other middlewares # Secret key needed for flask-debug-toolbar and sessions if not app.config.get('SECRET_KEY'): app.config['SECRET_KEY'] = config.get('beaker.session.secret') if not app.config.get('SECRET_KEY'): raise RuntimeError(u'You must provide a value for the secret key' ' with the SECRET_KEY config option') if debug: from flask_debugtoolbar import DebugToolbarExtension app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False DebugToolbarExtension(app) # Use Beaker as the Flask session interface class BeakerSessionInterface(SessionInterface): def open_session(self, app, request): if 'beaker.session' in request.environ: return request.environ['beaker.session'] def save_session(self, app, session, response): session.save() namespace = 'beaker.session.' session_opts = dict([(k.replace('beaker.', ''), v) for k, v in config.iteritems() if k.startswith(namespace)]) if (not session_opts.get('session.data_dir') and session_opts.get('session.type', 'file') == 'file'): cache_dir = app_conf.get('cache_dir') or app_conf.get('cache.dir') session_opts['session.data_dir'] = '{data_dir}/sessions'.format( data_dir=cache_dir) app.wsgi_app = SessionMiddleware(app.wsgi_app, session_opts) app.session_interface = BeakerSessionInterface() # Add Jinja2 extensions and filters app.jinja_env.filters['empty_and_escape'] = \ jinja_extensions.empty_and_escape # Common handlers for all requests app.before_request(ckan_before_request) app.after_request(ckan_after_request) # Template context processors app.context_processor(helper_functions) app.context_processor(c_object) @app.context_processor def ungettext_alias(): u''' Provide `ungettext` as an alias of `ngettext` for backwards compatibility ''' return dict(ungettext=ungettext) # Babel pairs = [(os.path.join(root, u'i18n'), 'ckan') ] + [(p.i18n_directory(), p.i18n_domain()) for p in PluginImplementations(ITranslation)] i18n_dirs, i18n_domains = zip(*pairs) app.config[u'BABEL_TRANSLATION_DIRECTORIES'] = ';'.join(i18n_dirs) app.config[u'BABEL_DOMAIN'] = 'ckan' app.config[u'BABEL_MULTIPLE_DOMAINS'] = ';'.join(i18n_domains) babel = CKANBabel(app) babel.localeselector(get_locale) @app.route('/hello', methods=['GET']) def hello_world(): return 'Hello World, this is served by Flask' @app.route('/hello', methods=['POST']) def hello_world_post(): return 'Hello World, this was posted to Flask' # Auto-register all blueprints defined in the `views` folder _register_core_blueprints(app) _register_error_handler(app) # Set up each IBlueprint extension as a Flask Blueprint for plugin in PluginImplementations(IBlueprint): if hasattr(plugin, 'get_blueprint'): app.register_extension_blueprint(plugin.get_blueprint()) lib_plugins.register_group_plugins(app) lib_plugins.register_package_plugins(app) # Set flask routes in named_routes for rule in app.url_map.iter_rules(): if '.' not in rule.endpoint: continue controller, action = rule.endpoint.split('.') needed = list(rule.arguments - set(rule.defaults or {})) route = { rule.endpoint: { 'action': action, 'controller': controller, 'highlight_actions': action, 'needed': needed } } config['routes.named_routes'].update(route) # Start other middleware for plugin in PluginImplementations(IMiddleware): app = plugin.make_middleware(app, config) # Fanstatic if debug: fanstatic_config = { 'versioning': True, 'recompute_hashes': True, 'minified': False, 'bottom': True, 'bundle': False, } else: fanstatic_config = { 'versioning': True, 'recompute_hashes': False, 'minified': True, 'bottom': True, 'bundle': True, } root_path = config.get('ckan.root_path', None) if root_path: root_path = re.sub('/{{LANG}}', '', root_path) fanstatic_config['base_url'] = root_path app = Fanstatic(app, **fanstatic_config) for plugin in PluginImplementations(IMiddleware): try: app = plugin.make_error_log_middleware(app, config) except AttributeError: log.critical('Middleware class {0} is missing the method' 'make_error_log_middleware.'.format( plugin.__class__.__name__)) # Initialize repoze.who who_parser = WhoConfig(conf['here']) who_parser.parse(open(app_conf['who.config_file'])) app = PluggableAuthenticationMiddleware( app, who_parser.identifiers, who_parser.authenticators, who_parser.challengers, who_parser.mdproviders, who_parser.request_classifier, who_parser.challenge_decider, logging.getLogger('repoze.who'), logging.WARN, # ignored who_parser.remote_user_key) # Update the main CKAN config object with the Flask specific keys # that were set here or autogenerated flask_config_keys = set(flask_app.config.keys()) - set(config.keys()) for key in flask_config_keys: config[key] = flask_app.config[key] # Add a reference to the actual Flask app so it's easier to access app._wsgi_app = flask_app return app
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) PUT_POST = dict(method=['PUT', 'POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) import ckan.lib.plugins as lib_plugins lib_plugins.reset_package_plugins() map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.minimization = False map.explicit = True # CUSTOM ROUTES HERE for plugin in p.PluginImplementations(p.IRoutes): map = plugin.before_map(map) # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect('/error/{action}', controller='error', ckan_core=True) map.connect('/error/{action}/{id}', controller='error', ckan_core=True) map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS, ckan_core=True) # Mark all routes added from extensions on the `before_map` extension point # as non-core for route in map.matchlist: if not hasattr(route, '_ckan_core'): route._ckan_core = False map.connect('invite', '/__invite__/', controller='partyline', action='join_party') map.connect('home', '/', controller='home', action='index') map.connect('about', '/about', controller='home', action='about') # CKAN API versioned. register_list = [ 'package', 'dataset', 'resource', 'tag', 'group', 'revision', 'licenses', 'rating', 'user', 'activity' ] register_list_str = '|'.join(register_list) # /api ver 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', ver='/3') as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: m.connect('/search/{register}', action='search') # /api ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/tag_counts', action='tag_counts') m.connect('/rest', action='index') m.connect('/qos/throughput/', action='throughput', conditions=GET) # /api/rest ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1', requirements=dict(register=register_list_str) ) as m: m.connect('/rest/{register}', action='list', conditions=GET) m.connect('/rest/{register}', action='create', conditions=POST) m.connect('/rest/{register}/{id}', action='show', conditions=GET) m.connect('/rest/{register}/{id}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}', action='update', conditions=POST) m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE) m.connect('/rest/{register}/{id}/:subregister', action='list', conditions=GET) m.connect('/rest/{register}/{id}/:subregister', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='show', conditions=GET) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='delete', conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/util/user/autocomplete', action='user_autocomplete') m.connect('/util/is_slug_valid', action='is_slug_valid', conditions=GET) m.connect('/util/dataset/autocomplete', action='dataset_autocomplete', conditions=GET) m.connect('/util/tag/autocomplete', action='tag_autocomplete', conditions=GET) m.connect('/util/resource/format_autocomplete', action='format_autocomplete', conditions=GET) m.connect('/util/resource/format_icon', action='format_icon', conditions=GET) m.connect('/util/group/autocomplete', action='group_autocomplete') m.connect('/util/organization/autocomplete', action='organization_autocomplete', conditions=GET) m.connect('/util/markdown', action='markdown') m.connect('/util/dataset/munge_name', action='munge_package_name') m.connect('/util/dataset/munge_title_to_name', action='munge_title_to_package_name') m.connect('/util/tag/munge', action='munge_tag') m.connect('/util/status', action='status') m.connect('/util/snippet/{snippet_path:.*}', action='snippet') m.connect('/i18n/{lang}', action='i18n_js_translations') ########### ## /END API ########### map.redirect('/packages', '/dataset') map.redirect('/packages/{url:.*}', '/dataset/{url}') map.redirect('/package', '/dataset') map.redirect('/package/{url:.*}', '/dataset/{url}') with SubMapper(map, controller='package') as m: 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', 'autocomplete', 'search' ]))) m.connect('/dataset/{action}/{id}/{revision}', action='read_ajax', requirements=dict(action='|'.join([ 'read', 'edit', 'history', ]))) m.connect('/dataset/{action}/{id}', requirements=dict(action='|'.join([ 'new_resource', 'history', 'read_ajax', 'history_ajax', 'follow', 'activity', 'groups', 'unfollow', 'delete', 'api_data', ]))) m.connect('dataset_edit', '/dataset/edit/{id}', action='edit', ckan_icon='pencil-square-o') m.connect('dataset_followers', '/dataset/followers/{id}', action='followers', ckan_icon='users') m.connect('dataset_activity', '/dataset/activity/{id}', action='activity', ckan_icon='clock-o') m.connect('/dataset/activity/{id}/{offset}', action='activity') m.connect('dataset_groups', '/dataset/groups/{id}', action='groups', ckan_icon='users') m.connect('dataset_resources', '/dataset/resources/{id}', action='resources', ckan_icon='bars') 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}', action='resource_delete') m.connect('resource_edit', '/dataset/{id}/resource_edit/{resource_id}', action='resource_edit', ckan_icon='pencil-square-o') m.connect('/dataset/{id}/resource/{resource_id}/download', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/download/{filename}', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/dataset/{id}/resource/{resource_id}/viewer', action='resource_embedded_dataviewer', width="960", height="800") m.connect('/dataset/{id}/resource/{resource_id}/preview', action='resource_datapreview') m.connect('views', '/dataset/{id}/resource/{resource_id}/views', action='resource_views', ckan_icon='bars') m.connect('new_view', '/dataset/{id}/resource/{resource_id}/new_view', action='edit_view', ckan_icon='pencil-square-o') m.connect('edit_view', '/dataset/{id}/resource/{resource_id}/edit_view/{view_id}', action='edit_view', ckan_icon='pencil-square-o') m.connect('resource_view', '/dataset/{id}/resource/{resource_id}/view/{view_id}', action='resource_view') m.connect('/dataset/{id}/resource/{resource_id}/view/', action='resource_view') # group map.redirect('/groups', '/group') map.redirect('/groups/{url:.*}', '/group/{url}') # 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', 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}', requirements=dict(action='|'.join([ 'edit', 'delete', 'member_new', 'member_delete', 'history', 'followers', 'follow', 'unfollow', 'admins', 'activity', ]))) m.connect('group_about', '/group/about/{id}', action='about', ckan_icon='info-circle'), m.connect('group_edit', '/group/edit/{id}', action='edit', ckan_icon='pencil-square-o') m.connect('group_members', '/group/members/{id}', action='members', ckan_icon='users'), m.connect('group_activity', '/group/activity/{id}/{offset}', action='activity', ckan_icon='clock-o'), m.connect('group_read', '/group/{id}', action='read', ckan_icon='sitemap') # organizations these basically end up being the same as groups with SubMapper(map, controller='organization') as m: m.connect('organizations_index', '/organization', action='index') m.connect('/organization/list', action='list') m.connect('/organization/new', action='new') m.connect('/organization/{action}/{id}', requirements=dict(action='|'.join([ 'delete', 'admins', 'member_new', 'member_delete', 'history' ]))) m.connect('organization_activity', '/organization/activity/{id}/{offset}', action='activity', ckan_icon='clock-o') m.connect('organization_read', '/organization/{id}', action='read') m.connect('organization_about', '/organization/about/{id}', action='about', ckan_icon='info-circle') m.connect('organization_read', '/organization/{id}', action='read', ckan_icon='sitemap') m.connect('organization_edit', '/organization/edit/{id}', action='edit', ckan_icon='pencil-square-o') m.connect('organization_members', '/organization/members/{id}', action='members', ckan_icon='users') m.connect('organization_bulk_process', '/organization/bulk_process/{id}', action='bulk_process', ckan_icon='sitemap') lib_plugins.register_package_plugins(map) lib_plugins.register_group_plugins(map) # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users map.redirect('/users/{url:.*}', '/user/{url}') map.redirect('/user/', '/user') with SubMapper(map, controller='user') as m: m.connect('/user/edit', action='edit') # Note: openid users have slashes in their ids, so need the wildcard # in the route. m.connect('user_generate_apikey', '/user/generate_key/{id}', action='generate_apikey') m.connect('/user/activity/{id}/{offset}', action='activity') m.connect('user_activity_stream', '/user/activity/{id}', action='activity', ckan_icon='clock-o') m.connect('user_dashboard', '/dashboard', action='dashboard', ckan_icon='list') m.connect('user_dashboard_datasets', '/dashboard/datasets', action='dashboard_datasets', ckan_icon='sitemap') m.connect('user_dashboard_groups', '/dashboard/groups', action='dashboard_groups', ckan_icon='users') m.connect('user_dashboard_organizations', '/dashboard/organizations', action='dashboard_organizations', ckan_icon='building-o') m.connect('/dashboard/{offset}', action='dashboard') m.connect('user_follow', '/user/follow/{id}', action='follow') m.connect('/user/unfollow/{id}', action='unfollow') m.connect('user_followers', '/user/followers/{id:.*}', action='followers', ckan_icon='users') m.connect('user_edit', '/user/edit/{id:.*}', action='edit', ckan_icon='cog') m.connect('user_delete', '/user/delete/{id}', action='delete') m.connect('/user/reset/{id:.*}', action='perform_reset') 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_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') m.connect('/revision/edit/{id}', action='edit') m.connect('/revision/diff/{id}', action='diff') m.connect('/revision/list', action='list') m.connect('/revision/{id}', action='read') # feeds with SubMapper(map, controller='feed') as m: m.connect('/feeds/group/{id}.atom', action='group') m.connect('/feeds/organization/{id}.atom', action='organization') m.connect('/feeds/tag/{id}.atom', action='tag') m.connect('/feeds/dataset.atom', action='general') m.connect('/feeds/custom.atom', action='custom') map.connect('ckanadmin_index', '/ckan-admin', controller='admin', action='index', ckan_icon='gavel') map.connect('ckanadmin_config', '/ckan-admin/config', controller='admin', action='config', ckan_icon='check-square-o') map.connect('ckanadmin_trash', '/ckan-admin/trash', controller='admin', action='trash', ckan_icon='trash-o') map.connect('ckanadmin', '/ckan-admin/{action}', controller='admin') with SubMapper(map, controller='ckan.controllers.storage:StorageController') as m: m.connect('storage_file', '/storage/f/{label:.*}', action='file') with SubMapper(map, controller='util') as m: m.connect('/i18n/strings_{lang}.js', action='i18n_js_strings') m.connect('/util/redirect', action='redirect') m.connect('/testing/primer', action='primer') m.connect('/testing/markup', action='markup') # robots.txt map.connect('/(robots.txt)', controller='template', action='view') # Mark all unmarked routes added up until now as core routes for route in map.matchlist: if not hasattr(route, '_ckan_core'): route._ckan_core = True for plugin in p.PluginImplementations(p.IRoutes): map = plugin.after_map(map) # Mark all routes added from extensions on the `after_map` extension point # as non-core for route in map.matchlist: if not hasattr(route, '_ckan_core'): route._ckan_core = False # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect('/favicon.ico', config.get('ckan.favicon')) map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently') map.connect('/*url', controller='template', action='view', ckan_core=True) return map
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) PUT_POST = dict(method=['PUT', 'POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) import ckan.lib.plugins as lib_plugins lib_plugins.reset_package_plugins() map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.minimization = False map.explicit = True # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect('/error/{action}', controller='error') map.connect('/error/{action}/{id}', controller='error') map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in p.PluginImplementations(p.IRoutes): map = plugin.before_map(map) map.connect('home', '/', controller='home', action='index') map.connect('about', '/about', controller='home', action='about') # CKAN API versioned. register_list = [ 'package', 'dataset', 'resource', 'tag', 'group', 'revision', 'licenses', 'rating', 'user', 'activity' ] register_list_str = '|'.join(register_list) # /api ver 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', ver='/3') as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: m.connect('', action='get_api') m.connect('/search/{register}', action='search') # /api ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/tag_counts', action='tag_counts') m.connect('/rest', action='index') m.connect('/qos/throughput/', action='throughput', conditions=GET) # /api/rest ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1', requirements=dict(register=register_list_str) ) as m: m.connect('/rest/{register}', action='list', conditions=GET) m.connect('/rest/{register}', action='create', conditions=POST) m.connect('/rest/{register}/{id}', action='show', conditions=GET) m.connect('/rest/{register}/{id}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}', action='update', conditions=POST) m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE) m.connect('/rest/{register}/{id}/:subregister', action='list', conditions=GET) m.connect('/rest/{register}/{id}/:subregister', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='show', conditions=GET) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='delete', conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/util/user/autocomplete', action='user_autocomplete') m.connect('/util/is_slug_valid', action='is_slug_valid', conditions=GET) m.connect('/util/dataset/autocomplete', action='dataset_autocomplete', conditions=GET) m.connect('/util/tag/autocomplete', action='tag_autocomplete', conditions=GET) m.connect('/util/resource/format_autocomplete', action='format_autocomplete', conditions=GET) m.connect('/util/resource/format_icon', action='format_icon', conditions=GET) m.connect('/util/group/autocomplete', action='group_autocomplete') m.connect('/util/organization/autocomplete', action='organization_autocomplete', conditions=GET) m.connect('/util/markdown', action='markdown') m.connect('/util/dataset/munge_name', action='munge_package_name') m.connect('/util/dataset/munge_title_to_name', action='munge_title_to_package_name') m.connect('/util/tag/munge', action='munge_tag') m.connect('/util/status', action='status') m.connect('/util/snippet/{snippet_path:.*}', action='snippet') m.connect('/i18n/{lang}', action='i18n_js_translations') ########### ## /END API ########### map.redirect('/packages', '/dataset') map.redirect('/packages/{url:.*}', '/dataset/{url}') map.redirect('/package', '/dataset') map.redirect('/package/{url:.*}', '/dataset/{url}') with SubMapper(map, controller='package') as m: 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', 'autocomplete', 'search' ]))) m.connect('/dataset/{action}/{id}/{revision}', action='read_ajax', requirements=dict(action='|'.join([ 'read', 'edit', 'history', ]))) m.connect('/dataset/{action}/{id}', requirements=dict(action='|'.join([ 'new_resource', 'history', 'read_ajax', 'history_ajax', 'follow', 'activity', 'groups', 'unfollow', 'delete', 'api_data', ]))) m.connect('dataset_edit', '/dataset/edit/{id}', action='edit', ckan_icon='edit') 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_groups', '/dataset/groups/{id}', action='groups', ckan_icon='group') m.connect('dataset_resources', '/dataset/resources/{id}', action='resources', ckan_icon='reorder') 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}', action='resource_delete') m.connect('resource_edit', '/dataset/{id}/resource_edit/{resource_id}', action='resource_edit', ckan_icon='edit') m.connect('/dataset/{id}/resource/{resource_id}/download', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/download/{filename}', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/dataset/{id}/resource/{resource_id}/viewer', action='resource_embedded_dataviewer', width="960", height="800") m.connect('/dataset/{id}/resource/{resource_id}/preview', action='resource_datapreview') m.connect('views', '/dataset/{id}/resource/{resource_id}/views', action='resource_views', ckan_icon='reorder') m.connect('new_view', '/dataset/{id}/resource/{resource_id}/new_view', action='edit_view', ckan_icon='edit') m.connect('edit_view', '/dataset/{id}/resource/{resource_id}/edit_view/{view_id}', action='edit_view', ckan_icon='edit') m.connect('resource_view', '/dataset/{id}/resource/{resource_id}/view/{view_id}', action='resource_view') m.connect('/dataset/{id}/resource/{resource_id}/view/', action='resource_view') # group map.redirect('/groups', '/group') map.redirect('/groups/{url:.*}', '/group/{url}') # 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', 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}', requirements=dict(action='|'.join([ 'edit', 'delete', 'member_new', 'member_delete', 'history', 'followers', 'follow', 'unfollow', 'admins', 'activity', ]))) m.connect('group_about', '/group/about/{id}', action='about', ckan_icon='info-sign'), m.connect('group_edit', '/group/edit/{id}', action='edit', ckan_icon='edit') m.connect('group_members', '/group/members/{id}', action='members', ckan_icon='group'), m.connect('group_activity', '/group/activity/{id}/{offset}', action='activity', ckan_icon='time'), m.connect('group_read', '/group/{id}', action='read', ckan_icon='sitemap') # organizations these basically end up being the same as groups with SubMapper(map, controller='organization') as m: m.connect('organizations_index', '/organization', action='index') m.connect('/organization/list', action='list') m.connect('/organization/new', action='new') m.connect('/organization/{action}/{id}', requirements=dict(action='|'.join([ 'delete', 'admins', 'member_new', 'member_delete', 'history' ]))) m.connect('organization_activity', '/organization/activity/{id}/{offset}', action='activity', ckan_icon='time') m.connect('organization_read', '/organization/{id}', action='read') m.connect('organization_about', '/organization/about/{id}', action='about', ckan_icon='info-sign') m.connect('organization_read', '/organization/{id}', action='read', ckan_icon='sitemap') m.connect('organization_edit', '/organization/edit/{id}', action='edit', ckan_icon='edit') m.connect('organization_members', '/organization/members/{id}', action='members', ckan_icon='group') m.connect('organization_bulk_process', '/organization/bulk_process/{id}', action='bulk_process', ckan_icon='sitemap') lib_plugins.register_package_plugins(map) lib_plugins.register_group_plugins(map) # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users map.redirect('/users/{url:.*}', '/user/{url}') map.redirect('/user/', '/user') with SubMapper(map, controller='user') as m: m.connect('/user/edit', action='edit') # Note: openid users have slashes in their ids, so need the wildcard # in the route. m.connect('user_generate_apikey', '/user/generate_key/{id}', action='generate_apikey') m.connect('/user/activity/{id}/{offset}', action='activity') m.connect('user_activity_stream', '/user/activity/{id}', action='activity', ckan_icon='time') m.connect('user_dashboard', '/dashboard', action='dashboard', ckan_icon='list') m.connect('user_dashboard_datasets', '/dashboard/datasets', action='dashboard_datasets', ckan_icon='sitemap') m.connect('user_dashboard_groups', '/dashboard/groups', action='dashboard_groups', ckan_icon='group') m.connect('user_dashboard_organizations', '/dashboard/organizations', action='dashboard_organizations', ckan_icon='building') m.connect('/dashboard/{offset}', action='dashboard') m.connect('user_follow', '/user/follow/{id}', action='follow') m.connect('/user/unfollow/{id}', action='unfollow') m.connect('user_followers', '/user/followers/{id:.*}', action='followers', ckan_icon='group') m.connect('user_edit', '/user/edit/{id:.*}', action='edit', ckan_icon='cog') m.connect('user_delete', '/user/delete/{id}', action='delete') m.connect('/user/reset/{id:.*}', action='perform_reset') 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_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') m.connect('/revision/edit/{id}', action='edit') m.connect('/revision/diff/{id}', action='diff') m.connect('/revision/list', action='list') m.connect('/revision/{id}', action='read') # feeds with SubMapper(map, controller='feed') as m: m.connect('/feeds/group/{id}.atom', action='group') m.connect('/feeds/organization/{id}.atom', action='organization') m.connect('/feeds/tag/{id}.atom', action='tag') m.connect('/feeds/dataset.atom', action='general') m.connect('/feeds/custom.atom', action='custom') map.connect('ckanadmin_index', '/ckan-admin', controller='admin', action='index', ckan_icon='legal') map.connect('ckanadmin_config', '/ckan-admin/config', controller='admin', action='config', ckan_icon='check') map.connect('ckanadmin_trash', '/ckan-admin/trash', controller='admin', action='trash', ckan_icon='trash') map.connect('ckanadmin', '/ckan-admin/{action}', controller='admin') with SubMapper(map, controller='ckan.controllers.storage:StorageController') as m: m.connect('storage_file', '/storage/f/{label:.*}', action='file') with SubMapper(map, controller='util') as m: m.connect('/i18n/strings_{lang}.js', action='i18n_js_strings') m.connect('/util/redirect', action='redirect') m.connect('/testing/primer', action='primer') m.connect('/testing/markup', action='markup') for plugin in p.PluginImplementations(p.IRoutes): map = plugin.after_map(map) # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect('/favicon.ico', config.get('ckan.favicon')) map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently') map.connect('/*url', controller='template', action='view') return map
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) PUT_POST = dict(method=['PUT', 'POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) import ckan.lib.plugins as lib_plugins lib_plugins.reset_package_plugins() map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.minimization = False map.explicit = True # CUSTOM ROUTES HERE for plugin in p.PluginImplementations(p.IRoutes): map = plugin.before_map(map) # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect('/error/{action}', controller='error', ckan_core=True) map.connect('/error/{action}/{id}', controller='error', ckan_core=True) map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS, ckan_core=True) # Mark all routes added from extensions on the `before_map` extension point # as non-core for route in map.matchlist: if not hasattr(route, '_ckan_core'): route._ckan_core = False # CKAN API versioned. register_list = [ 'package', 'dataset', 'resource', 'tag', 'group', 'revision', 'licenses', 'rating', 'user', 'activity' ] register_list_str = '|'.join(register_list) # /api ver 1, 2, 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: m.connect('/search/{register}', action='search') # /api/util ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/util/dataset/munge_name', action='munge_package_name') m.connect('/util/dataset/munge_title_to_name', action='munge_title_to_package_name') m.connect('/util/tag/munge', action='munge_tag') ########### ## /END API ########### map.redirect('/packages', '/dataset') map.redirect('/packages/{url:.*}', '/dataset/{url}') map.redirect('/package', '/dataset') map.redirect('/package/{url:.*}', '/dataset/{url}') with SubMapper(map, controller='package') as m: m.connect('search', '/dataset', action='search', highlight_actions='index search') m.connect('dataset_new', '/dataset/new', action='new') m.connect('/dataset/{action}', requirements=dict(action='|'.join([ 'list', 'autocomplete', 'search' ]))) m.connect('/dataset/{action}/{id}/{revision}', action='read_ajax', requirements=dict(action='|'.join([ 'read', 'edit', 'history', ]))) m.connect('/dataset/{action}/{id}', requirements=dict(action='|'.join([ 'new_resource', 'history', 'read_ajax', 'history_ajax', 'follow', 'activity', 'groups', 'unfollow', 'delete', 'api_data', ]))) m.connect('dataset_edit', '/dataset/edit/{id}', action='edit', ckan_icon='pencil-square-o') m.connect('dataset_followers', '/dataset/followers/{id}', action='followers', ckan_icon='users') m.connect('dataset_activity', '/dataset/activity/{id}', action='activity', ckan_icon='clock-o') m.connect('/dataset/activity/{id}/{offset}', action='activity') m.connect('dataset_topics', '/dataset/topics/{id}', action='groups', ckan_icon='users') m.connect('dataset_resources', '/dataset/resources/{id}', action='resources', ckan_icon='bars') 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}', action='resource_delete') m.connect('resource_edit', '/dataset/{id}/resource_edit/{resource_id}', action='resource_edit', ckan_icon='pencil-square-o') m.connect('/dataset/{id}/resource/{resource_id}/download', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/download/{filename}', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/dataset/{id}/resource/{resource_id}/viewer', action='resource_embedded_dataviewer', width="960", height="800") m.connect('/dataset/{id}/resource/{resource_id}/preview', action='resource_datapreview') m.connect('views', '/dataset/{id}/resource/{resource_id}/views', action='resource_views', ckan_icon='bars') m.connect('new_view', '/dataset/{id}/resource/{resource_id}/new_view', action='edit_view', ckan_icon='pencil-square-o') m.connect('edit_view', '/dataset/{id}/resource/{resource_id}/edit_view/{view_id}', action='edit_view', ckan_icon='pencil-square-o') m.connect('resource_view', '/dataset/{id}/resource/{resource_id}/view/{view_id}', action='resource_view') m.connect('/dataset/{id}/resource/{resource_id}/view/', action='resource_view') # group map.redirect('/topics', '/topic') map.redirect('/topics/{url:.*}', '/topic/{url}') # 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', '/topic', action='index', highlight_actions='index search') m.connect('group_list', '/topic/list', action='list') m.connect('group_new', '/topic/new', action='new') for action in [ 'edit', 'delete', 'member_new', 'member_delete', 'history', 'followers', 'follow', 'unfollow', 'admins', 'activity', ]: m.connect('group_' + action, '/topic/' + action + '/{id}', action=action) m.connect('group_about', '/topic/about/{id}', action='about', ckan_icon='info-circle'), m.connect('group_edit', '/topic/edit/{id}', action='edit', ckan_icon='pencil-square-o') m.connect('group_members', '/topic/members/{id}', action='members', ckan_icon='users'), m.connect('group_activity', '/topic/activity/{id}/{offset}', action='activity', ckan_icon='clock-o'), m.connect('group_read', '/topic/{id}', action='read', ckan_icon='sitemap') # organizations these basically end up being the same as groups with SubMapper(map, controller='organization') as m: m.connect('organizations_index', '/organization', action='index') m.connect('organization_index', '/organization', action='index') m.connect('organization_new', '/organization/new', action='new') for action in [ 'delete', 'admins', 'member_new', 'member_delete', 'history']: m.connect('organization_' + action, '/organization/' + action + '/{id}', action=action) m.connect('organization_activity', '/organization/activity/{id}/{offset}', action='activity', ckan_icon='clock-o') m.connect('organization_read', '/organization/{id}', action='read') m.connect('organization_about', '/organization/about/{id}', action='about', ckan_icon='info-circle') m.connect('organization_read', '/organization/{id}', action='read', ckan_icon='sitemap') m.connect('organization_edit', '/organization/edit/{id}', action='edit', ckan_icon='pencil-square-o') m.connect('organization_members', '/organization/members/{id}', action='members', ckan_icon='users') m.connect('organization_bulk_process', '/organization/bulk_process/{id}', action='bulk_process', ckan_icon='sitemap') lib_plugins.register_package_plugins(map) lib_plugins.register_group_plugins(map) # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users map.redirect('/users/{url:.*}', '/user/{url}') with SubMapper(map, controller='revision') as m: m.connect('/revision', action='index') m.connect('/revision/edit/{id}', action='edit') m.connect('/revision/diff/{id}', action='diff') m.connect('/revision/list', action='list') m.connect('/revision/{id}', action='read') with SubMapper(map, controller='ckan.controllers.storage:StorageController') as m: m.connect('storage_file', '/storage/f/{label:.*}', action='file') with SubMapper(map, controller='util') as m: m.connect('/i18n/strings_{lang}.js', action='i18n_js_strings') m.connect('/util/redirect', action='redirect') m.connect('/testing/primer', action='primer') m.connect('/testing/markup', action='markup') # robots.txt map.connect('/(robots.txt)', controller='template', action='view') # Mark all unmarked routes added up until now as core routes for route in map.matchlist: if not hasattr(route, '_ckan_core'): route._ckan_core = True for plugin in p.PluginImplementations(p.IRoutes): map = plugin.after_map(map) # Mark all routes added from extensions on the `after_map` extension point # as non-core for route in map.matchlist: if not hasattr(route, '_ckan_core'): route._ckan_core = False # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect('/favicon.ico', config.get('ckan.favicon')) map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently') map.connect('/*url', controller='template', action='view', ckan_core=True) return map
def update_config(): ''' This code needs to be run when the config is changed to take those changes into account. It is called whenever a plugin is loaded as the plugin might have changed the config values (for instance it might change ckan.site_url) ''' webassets_init() for plugin in p.PluginImplementations(p.IConfigurer): # must do update in place as this does not work: # config = plugin.update_config(config) plugin.update_config(config) # Set whitelisted env vars on config object # This is set up before globals are initialized ckan_db = os.environ.get('CKAN_DB', None) if ckan_db: msg = 'Setting CKAN_DB as an env var is deprecated and will be' \ ' removed in a future release. Use CKAN_SQLALCHEMY_URL instead.' log.warn(msg) config['sqlalchemy.url'] = ckan_db for option in CONFIG_FROM_ENV_VARS: from_env = os.environ.get(CONFIG_FROM_ENV_VARS[option], None) if from_env: config[option] = from_env root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) site_url = config.get('ckan.site_url', '') if not site_url: raise RuntimeError( 'ckan.site_url is not configured and it must have a value.' ' Please amend your .ini file.') if not site_url.lower().startswith('http'): raise RuntimeError( 'ckan.site_url should be a full URL, including the schema ' '(http or https)') display_timezone = config.get('ckan.display_timezone', '') if (display_timezone and display_timezone != 'server' and display_timezone not in pytz.all_timezones): raise CkanConfigurationException( "ckan.display_timezone is not 'server' or a valid timezone" ) # Remove backslash from site_url if present config['ckan.site_url'] = config['ckan.site_url'].rstrip('/') ckan_host = config['ckan.host'] = urlparse(site_url).netloc if config.get('ckan.site_id') is None: if ':' in ckan_host: ckan_host, port = ckan_host.split(':') assert ckan_host, 'You need to configure ckan.site_url or ' \ 'ckan.site_id for SOLR search-index rebuild to work.' config['ckan.site_id'] = ckan_host # ensure that a favicon has been set favicon = config.get('ckan.favicon', '/base/images/ckan.ico') config['ckan.favicon'] = favicon # Init SOLR settings and check if the schema is compatible # from ckan.lib.search import SolrSettings, check_solr_schema_version # lib.search is imported here as we need the config enabled and parsed search.SolrSettings.init(config.get('solr_url'), config.get('solr_user'), config.get('solr_password')) search.check_solr_schema_version() if six.PY2: routes_map = routing.make_map() lib_plugins.reset_package_plugins() lib_plugins.register_package_plugins() lib_plugins.reset_group_plugins() lib_plugins.register_group_plugins() if six.PY2: config['routes.map'] = routes_map # The RoutesMiddleware needs its mapper updating if it exists if 'routes.middleware' in config: config['routes.middleware'].mapper = routes_map # routes.named_routes is a CKAN thing config['routes.named_routes'] = routing.named_routes config['pylons.app_globals'] = app_globals.app_globals # initialise the globals app_globals.app_globals._init() helpers.load_plugin_helpers() config['pylons.h'] = helpers.helper_functions # Templates and CSS loading from configuration valid_base_templates_folder_names = ['templates'] templates = config.get('ckan.base_templates_folder', 'templates') config['ckan.base_templates_folder'] = templates if templates not in valid_base_templates_folder_names: raise CkanConfigurationException( 'You provided an invalid value for ckan.base_templates_folder. ' 'Possible values are: "templates".' ) jinja2_templates_path = os.path.join(root, templates) log.info('Loading templates from %s' % jinja2_templates_path) template_paths = [jinja2_templates_path] extra_template_paths = config.get('extra_template_paths', '') if extra_template_paths: # must be first for them to override defaults template_paths = extra_template_paths.split(',') + template_paths config['computed_template_paths'] = template_paths # Markdown ignores the logger config, so to get rid of excessive # markdown debug messages in the log, set it to the level of the # root logger. logging.getLogger("MARKDOWN").setLevel(logging.getLogger().level) if six.PY2: # Create Jinja2 environment env = jinja_extensions.Environment( **jinja_extensions.get_jinja_env_options()) env.install_gettext_callables(_, ungettext, newstyle=True) # custom filters env.policies['ext.i18n.trimmed'] = True env.filters['empty_and_escape'] = jinja_extensions.empty_and_escape config['pylons.app_globals'].jinja_env = env # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) # Enable pessimistic disconnect handling (added in SQLAlchemy 1.2) # to eliminate database errors due to stale pooled connections config.setdefault('pool_pre_ping', True) # Initialize SQLAlchemy engine = sqlalchemy.engine_from_config(config) model.init_model(engine) for plugin in p.PluginImplementations(p.IConfigurable): plugin.configure(config) # reset the template cache - we do this here so that when we load the # environment it is clean render.reset_template_info_cache() # clear other caches logic.clear_actions_cache() logic.clear_validators_cache() authz.clear_auth_functions_cache() # Here we create the site user if they are not already in the database try: logic.get_action('get_site_user')({'ignore_auth': True}, None) except (sqlalchemy.exc.ProgrammingError, sqlalchemy.exc.OperationalError): # (ProgrammingError for Postgres, OperationalError for SQLite) # The database is not initialised. This is a bit dirty. This occurs # when running tests. pass except sqlalchemy.exc.InternalError: # The database is not initialised. Travis hits this pass # Close current session and open database connections to ensure a clean # clean environment even if an error occurs later on model.Session.remove() model.Session.bind.dispose()
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) PUT_POST = dict(method=['PUT','POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) from ckan.lib.plugins import register_package_plugins from ckan.lib.plugins import register_group_plugins map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.minimization = False map.explicit = True # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect('/error/{action}', controller='error') map.connect('/error/{action}/{id}', controller='error') map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in routing_plugins: map = plugin.before_map(map) map.connect('home', '/', controller='home', action='index') map.connect('about', '/about', controller='home', action='about') # CKAN API versioned. register_list = [ 'package', 'dataset', 'resource', 'tag', 'group', 'related', 'revision', 'licenses', 'rating', 'user', 'activity' ] register_list_str = '|'.join(register_list) # /api ver 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', ver='/3') as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: m.connect('', action='get_api') m.connect('/search/{register}', action='search') # /api ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/tag_counts', action='tag_counts') m.connect('/rest', action='index') m.connect('/qos/throughput/', action='throughput', conditions=GET) # /api/rest ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1', requirements=dict(register=register_list_str)) as m: m.connect('/rest/{register}', action='list', conditions=GET) m.connect('/rest/{register}', action='create', conditions=POST) m.connect('/rest/{register}/{id}', action='show', conditions=GET) m.connect('/rest/{register}/{id}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}', action='update', conditions=POST) m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE) m.connect('/rest/{register}/{id}/:subregister', action='list', conditions=GET) m.connect('/rest/{register}/{id}/:subregister', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='create', conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='show', conditions=GET) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='update', conditions=PUT) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='delete', conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: m.connect('/util/user/autocomplete', action='user_autocomplete') m.connect('/util/is_slug_valid', action='is_slug_valid', conditions=GET) m.connect('/util/dataset/autocomplete', action='dataset_autocomplete', conditions=GET) m.connect('/util/tag/autocomplete', action='tag_autocomplete', conditions=GET) m.connect('/util/resource/format_autocomplete', action='format_autocomplete', conditions=GET) m.connect('/util/resource/format_icon', action='format_icon', conditions=GET) m.connect('/util/group/autocomplete', action='group_autocomplete') m.connect('/util/markdown', action='markdown') m.connect('/util/dataset/munge_name', action='munge_package_name') m.connect('/util/dataset/munge_title_to_name', action='munge_title_to_package_name') m.connect('/util/tag/munge', action='munge_tag') m.connect('/util/status', action='status') m.connect('/util/snippet/{snippet_path:.*}', action='snippet') m.connect('/i18n/{lang}', action='i18n_js_translations') ########### ## /END API ########### ## Webstore if config.get('ckan.datastore.enabled', False): with SubMapper(map, controller='datastore') as m: m.connect('datastore_read', '/api/data/{id}{url:(/.*)?}', action='read', url='', conditions=GET) m.connect('datastore_write', '/api/data/{id}{url:(/.*)?}', action='write', url='', conditions=PUT_POST_DELETE) m.connect('datastore_read_shortcut', '/dataset/{dataset}/resource/{id}/api{url:(/.*)?}', action='read', url='', conditions=GET) m.connect('datastore_write_shortcut', '/dataset/{dataset}/resource/{id}/api{url:(/.*)?}', action='write', url='', conditions=PUT_POST_DELETE) map.redirect('/packages', '/dataset') map.redirect('/packages/{url:.*}', '/dataset/{url}') map.redirect('/package', '/dataset') map.redirect('/package/{url:.*}', '/dataset/{url}') ##to get back formalchemy uncomment these lines ##map.connect('/package/new', controller='package_formalchemy', action='new') ##map.connect('/package/edit/{id}', controller='package_formalchemy', action='edit') with SubMapper(map, controller='related') as m: m.connect('related_new', '/dataset/{id}/related/new', action='new') m.connect('related_edit', '/dataset/{id}/related/edit/{related_id}', 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_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('/dataset/{action}', requirements=dict(action='|'.join([ 'list', 'new', 'autocomplete', 'search' ])) ) m.connect('/dataset/{action}/{id}/{revision}', action='read_ajax', requirements=dict(action='|'.join([ 'read', 'edit', 'authz', 'history', ])) ) m.connect('/dataset/{action}/{id}', requirements=dict(action='|'.join([ 'edit', 'new_metadata', 'new_resource', 'authz', 'history', 'read_ajax', 'history_ajax', 'followers', 'delete', 'api_data', ])) ) m.connect('/dataset/{id}.{format}', action='read') m.connect('/dataset/{id}', action='read') m.connect('/dataset/{id}/resource/{resource_id}', action='resource_read') m.connect('/dataset/{id}/resource_delete/{resource_id}', action='resource_delete') m.connect('/dataset/{id}/resource_edit/{resource_id}', action='resource_edit') m.connect('/dataset/{id}/resource/{resource_id}/download', action='resource_download') m.connect('/dataset/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/dataset/{id}/resource/{resource_id}/viewer', action='resource_embedded_dataviewer', width="960", height="800") # group map.redirect('/groups', '/group') map.redirect('/groups/{url:.*}', '/group/{url}') ##to get back formalchemy uncomment these lines ##map.connect('/group/new', controller='group_formalchemy', action='new') ##map.connect('/group/edit/{id}', controller='group_formalchemy', action='edit') # 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_list', '/group/list', action='list') m.connect('group_new', '/group/new', action='new') m.connect('group_action', '/group/{action}/{id}', requirements=dict(action='|'.join([ 'edit', 'authz', 'delete', 'history' ])) ) m.connect('group_read', '/group/{id}', action='read') register_package_plugins(map) register_group_plugins(map) # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users map.redirect('/users/{url:.*}', '/user/{url}') map.redirect('/user/', '/user') with SubMapper(map, controller='user') as m: m.connect('/user/edit', action='edit') # Note: openid users have slashes in their ids, so need the wildcard # in the route. m.connect('/user/dashboard', action='dashboard') m.connect('/user/followers/{id:.*}', action='followers') 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('/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') with SubMapper(map, controller='revision') as m: m.connect('/revision', action='index') m.connect('/revision/edit/{id}', action='edit') m.connect('/revision/diff/{id}', action='diff') m.connect('/revision/list', action='list') m.connect('/revision/{id}', action='read') # feeds with SubMapper(map, controller='feed') as m: m.connect('/feeds/group/{id}.atom', action='group') m.connect('/feeds/tag/{id}.atom', action='tag') m.connect('/feeds/dataset.atom', action='general') m.connect('/feeds/custom.atom', action='custom') map.connect('ckanadmin_index', '/ckan-admin', controller='admin', action='index') map.connect('ckanadmin', '/ckan-admin/{action}', controller='admin') # Storage routes with SubMapper(map, controller='ckan.controllers.storage:StorageAPIController') as m: m.connect('storage_api', '/api/storage', action='index') m.connect('storage_api_set_metadata', '/api/storage/metadata/{label:.*}', action='set_metadata', conditions=PUT_POST) m.connect('storage_api_get_metadata', '/api/storage/metadata/{label:.*}', action='get_metadata', conditions=GET) m.connect('storage_api_auth_request', '/api/storage/auth/request/{label:.*}', action='auth_request') m.connect('storage_api_auth_form', '/api/storage/auth/form/{label:.*}', action='auth_form') with SubMapper(map, controller='ckan.controllers.storage:StorageController') as m: m.connect('storage_upload', '/storage/upload', action='upload') m.connect('storage_upload_handle', '/storage/upload_handle', action='upload_handle') m.connect('storage_upload_success', '/storage/upload/success', action='success') m.connect('storage_upload_success_empty', '/storage/upload/success_empty', action='success_empty') m.connect('storage_file', '/storage/f/{label:.*}', action='file') with SubMapper(map, controller='util') as m: m.connect('/i18n/strings_{lang}.js', action='i18n_js_strings') m.connect('/util/redirect', action='redirect') m.connect('/testing/primer', action='primer') m.connect('/testing/markup', action='markup') for plugin in routing_plugins: map = plugin.after_map(map) # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect('/favicon.ico', config.get('ckan.favicon')) map.redirect('/*(url)/', '/{url}', _redirect_code='301 Moved Permanently') map.connect('/*url', controller='template', action='view') return map
def update_config(): ''' This code needs to be run when the config is changed to take those changes into account. It is called whenever a plugin is loaded as the plugin might have changed the config values (for instance it might change ckan.site_url) ''' webassets_init() for plugin in p.PluginImplementations(p.IConfigurer): # must do update in place as this does not work: # config = plugin.update_config(config) plugin.update_config(config) # Set whitelisted env vars on config object # This is set up before globals are initialized ckan_db = os.environ.get('CKAN_DB', None) if ckan_db: msg = 'Setting CKAN_DB as an env var is deprecated and will be' \ ' removed in a future release. Use CKAN_SQLALCHEMY_URL instead.' log.warn(msg) config['sqlalchemy.url'] = ckan_db for option in CONFIG_FROM_ENV_VARS: from_env = os.environ.get(CONFIG_FROM_ENV_VARS[option], None) if from_env: config[option] = from_env root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) site_url = config.get('ckan.site_url', '') if not site_url: raise RuntimeError( 'ckan.site_url is not configured and it must have a value.' ' Please amend your .ini file.') if not site_url.lower().startswith('http'): raise RuntimeError( 'ckan.site_url should be a full URL, including the schema ' '(http or https)') display_timezone = config.get('ckan.display_timezone', '') if (display_timezone and display_timezone != 'server' and display_timezone not in pytz.all_timezones): raise CkanConfigurationException( "ckan.display_timezone is not 'server' or a valid timezone" ) # Remove backslash from site_url if present config['ckan.site_url'] = config['ckan.site_url'].rstrip('/') ckan_host = config['ckan.host'] = urlparse(site_url).netloc if config.get('ckan.site_id') is None: if ':' in ckan_host: ckan_host, port = ckan_host.split(':') assert ckan_host, 'You need to configure ckan.site_url or ' \ 'ckan.site_id for SOLR search-index rebuild to work.' config['ckan.site_id'] = ckan_host # ensure that a favicon has been set favicon = config.get('ckan.favicon', '/base/images/ckan.ico') config['ckan.favicon'] = favicon # Init SOLR settings and check if the schema is compatible # from ckan.lib.search import SolrSettings, check_solr_schema_version # lib.search is imported here as we need the config enabled and parsed search.SolrSettings.init(config.get('solr_url'), config.get('solr_user'), config.get('solr_password')) search.check_solr_schema_version() routes_map = routing.make_map() lib_plugins.reset_package_plugins() lib_plugins.register_package_plugins() lib_plugins.reset_group_plugins() lib_plugins.register_group_plugins() config['routes.map'] = routes_map # The RoutesMiddleware needs its mapper updating if it exists if 'routes.middleware' in config: config['routes.middleware'].mapper = routes_map # routes.named_routes is a CKAN thing config['routes.named_routes'] = routing.named_routes config['pylons.app_globals'] = app_globals.app_globals # initialise the globals app_globals.app_globals._init() helpers.load_plugin_helpers() config['pylons.h'] = helpers.helper_functions # Templates and CSS loading from configuration valid_base_templates_folder_names = ['templates'] templates = config.get('ckan.base_templates_folder', 'templates') config['ckan.base_templates_folder'] = templates if templates not in valid_base_templates_folder_names: raise CkanConfigurationException( 'You provided an invalid value for ckan.base_templates_folder. ' 'Possible values are: "templates".' ) jinja2_templates_path = os.path.join(root, templates) log.info('Loading templates from %s' % jinja2_templates_path) template_paths = [jinja2_templates_path] extra_template_paths = config.get('extra_template_paths', '') if extra_template_paths: # must be first for them to override defaults template_paths = extra_template_paths.split(',') + template_paths config['computed_template_paths'] = template_paths # Set the default language for validation messages from formencode # to what is set as the default locale in the config default_lang = config.get('ckan.locale_default', 'en') formencode.api.set_stdtranslation(domain="FormEncode", languages=[default_lang]) # Markdown ignores the logger config, so to get rid of excessive # markdown debug messages in the log, set it to the level of the # root logger. logging.getLogger("MARKDOWN").setLevel(logging.getLogger().level) # Create Jinja2 environment env = jinja_extensions.Environment( **jinja_extensions.get_jinja_env_options()) env.install_gettext_callables(_, ungettext, newstyle=True) # custom filters env.filters['empty_and_escape'] = jinja_extensions.empty_and_escape config['pylons.app_globals'].jinja_env = env # CONFIGURATION OPTIONS HERE (note: all config options will override # any Pylons config options) # Initialize SQLAlchemy engine = sqlalchemy.engine_from_config(config) model.init_model(engine) for plugin in p.PluginImplementations(p.IConfigurable): plugin.configure(config) # reset the template cache - we do this here so that when we load the # environment it is clean render.reset_template_info_cache() # clear other caches logic.clear_actions_cache() logic.clear_validators_cache() authz.clear_auth_functions_cache() # Here we create the site user if they are not already in the database try: logic.get_action('get_site_user')({'ignore_auth': True}, None) except (sqlalchemy.exc.ProgrammingError, sqlalchemy.exc.OperationalError): # (ProgrammingError for Postgres, OperationalError for SQLite) # The database is not initialised. This is a bit dirty. This occurs # when running tests. pass except sqlalchemy.exc.InternalError: # The database is not initialised. Travis hits this pass # Close current session and open database connections to ensure a clean # clean environment even if an error occurs later on model.Session.remove() model.Session.bind.dispose()
def update_config(): ''' This code needs to be run when the config is changed to take those changes into account. It is called whenever a plugin is loaded as the plugin might have changed the config values (for instance it might change ckan.site_url) ''' config_declaration.setup() config_declaration.make_safe(config) config_declaration.normalize(config) webassets_init() for plugin in p.PluginImplementations(p.IConfigurer): # must do update in place as this does not work: # config = plugin.update_config(config) plugin.update_config(config) # Set whitelisted env vars on config object # This is set up before globals are initialized ckan_db = os.environ.get('CKAN_DB', None) if ckan_db: msg = 'Setting CKAN_DB as an env var is deprecated and will be' \ ' removed in a future release. Use CKAN_SQLALCHEMY_URL instead.' log.warn(msg) config['sqlalchemy.url'] = ckan_db for option in CONFIG_FROM_ENV_VARS: from_env = os.environ.get(CONFIG_FROM_ENV_VARS[option], None) if from_env: config[option] = from_env if config.get_value("config.mode") == "strict": _, errors = config_declaration.validate(config) if errors: msg = "\n".join( "{}: {}".format(key, "; ".join(issues)) for key, issues in errors.items() ) raise CkanConfigurationException(msg) root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) site_url = config.get_value('ckan.site_url') if not site_url: raise RuntimeError( 'ckan.site_url is not configured and it must have a value.' ' Please amend your .ini file.') if not site_url.lower().startswith('http'): raise RuntimeError( 'ckan.site_url should be a full URL, including the schema ' '(http or https)') # Remove backslash from site_url if present config['ckan.site_url'] = site_url.rstrip('/') display_timezone = config.get_value('ckan.display_timezone') if (display_timezone and display_timezone != 'server' and display_timezone not in pytz.all_timezones): raise CkanConfigurationException( "ckan.display_timezone is not 'server' or a valid timezone" ) # Init SOLR settings and check if the schema is compatible # from ckan.lib.search import SolrSettings, check_solr_schema_version # lib.search is imported here as we need the config enabled and parsed search.SolrSettings.init(config.get_value('solr_url'), config.get_value('solr_user'), config.get_value('solr_password')) search.check_solr_schema_version() lib_plugins.reset_package_plugins() lib_plugins.register_package_plugins() lib_plugins.reset_group_plugins() lib_plugins.register_group_plugins() # initialise the globals app_globals.app_globals._init() helpers.load_plugin_helpers() # Templates and CSS loading from configuration valid_base_templates_folder_names = ['templates'] templates = config.get_value('ckan.base_templates_folder') config['ckan.base_templates_folder'] = templates if templates not in valid_base_templates_folder_names: raise CkanConfigurationException( 'You provided an invalid value for ckan.base_templates_folder. ' 'Possible values are: "templates".' ) jinja2_templates_path = os.path.join(root, templates) log.info('Loading templates from %s' % jinja2_templates_path) template_paths = [jinja2_templates_path] extra_template_paths = config.get_value('extra_template_paths') if extra_template_paths: # must be first for them to override defaults template_paths = extra_template_paths.split(',') + template_paths config['computed_template_paths'] = template_paths # Enable pessimistic disconnect handling (added in SQLAlchemy 1.2) # to eliminate database errors due to stale pooled connections config.setdefault('sqlalchemy.pool_pre_ping', True) # Initialize SQLAlchemy engine = sqlalchemy.engine_from_config(config) model.init_model(engine) for plugin in p.PluginImplementations(p.IConfigurable): plugin.configure(config) # clear other caches logic.clear_actions_cache() logic.clear_validators_cache() authz.clear_auth_functions_cache() # Here we create the site user if they are not already in the database try: logic.get_action('get_site_user')({'ignore_auth': True}, None) except (sqlalchemy.exc.ProgrammingError, sqlalchemy.exc.OperationalError): # The database is not yet initialised. It happens in `ckan db init` pass except sqlalchemy.exc.IntegrityError: # Race condition, user already exists. pass # Close current session and open database connections to ensure a clean # clean environment even if an error occurs later on model.Session.remove() model.Session.bind.dispose()
def make_map(): """Create, configure and return the routes Mapper""" # import controllers here rather than at root level because # pylons config is initialised by this point. # Helpers to reduce code clutter GET = dict(method=["GET"]) PUT = dict(method=["PUT"]) POST = dict(method=["POST"]) DELETE = dict(method=["DELETE"]) GET_POST = dict(method=["GET", "POST"]) PUT_POST = dict(method=["PUT", "POST"]) PUT_POST_DELETE = dict(method=["PUT", "POST", "DELETE"]) OPTIONS = dict(method=["OPTIONS"]) import ckan.lib.plugins as lib_plugins lib_plugins.reset_package_plugins() map = Mapper(directory=config["pylons.paths"]["controllers"], always_scan=config["debug"]) map.minimization = False map.explicit = True # The ErrorController route (handles 404/500 error pages); it should # likely stay at the top, ensuring it can always be resolved. map.connect("/error/{action}", controller="error") map.connect("/error/{action}/{id}", controller="error") map.connect("*url", controller="home", action="cors_options", conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in p.PluginImplementations(p.IRoutes): map = plugin.before_map(map) map.connect("home", "/", controller="home", action="index") map.connect("about", "/about", controller="home", action="about") # CKAN API versioned. register_list = [ "package", "dataset", "resource", "tag", "group", "related", "revision", "licenses", "rating", "user", "activity", ] register_list_str = "|".join(register_list) # /api ver 3 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/3|}", ver="/3") as m: m.connect("/action/{logic_function}", action="action", conditions=GET_POST) # /api ver 1, 2, 3 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/1|/2|/3|}", ver="/1") as m: m.connect("", action="get_api") m.connect("/search/{register}", action="search") # /api ver 1, 2 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/1|/2|}", ver="/1") as m: m.connect("/tag_counts", action="tag_counts") m.connect("/rest", action="index") m.connect("/qos/throughput/", action="throughput", conditions=GET) # /api/rest ver 1, 2 or none with SubMapper( map, controller="api", path_prefix="/api{ver:/1|/2|}", ver="/1", requirements=dict(register=register_list_str) ) as m: m.connect("/rest/{register}", action="list", conditions=GET) m.connect("/rest/{register}", action="create", conditions=POST) m.connect("/rest/{register}/{id}", action="show", conditions=GET) m.connect("/rest/{register}/{id}", action="update", conditions=PUT) m.connect("/rest/{register}/{id}", action="update", conditions=POST) m.connect("/rest/{register}/{id}", action="delete", conditions=DELETE) m.connect("/rest/{register}/{id}/:subregister", action="list", conditions=GET) m.connect("/rest/{register}/{id}/:subregister", action="create", conditions=POST) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="create", conditions=POST) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="show", conditions=GET) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="update", conditions=PUT) m.connect("/rest/{register}/{id}/:subregister/{id2}", action="delete", conditions=DELETE) # /api/util ver 1, 2 or none with SubMapper(map, controller="api", path_prefix="/api{ver:/1|/2|}", ver="/1") as m: m.connect("/util/user/autocomplete", action="user_autocomplete") m.connect("/util/is_slug_valid", action="is_slug_valid", conditions=GET) m.connect("/util/dataset/autocomplete", action="dataset_autocomplete", conditions=GET) m.connect("/util/tag/autocomplete", action="tag_autocomplete", conditions=GET) m.connect("/util/resource/format_autocomplete", action="format_autocomplete", conditions=GET) m.connect("/util/resource/format_icon", action="format_icon", conditions=GET) m.connect("/util/group/autocomplete", action="group_autocomplete") m.connect("/util/organization/autocomplete", action="organization_autocomplete", conditions=GET) m.connect("/util/markdown", action="markdown") m.connect("/util/dataset/munge_name", action="munge_package_name") m.connect("/util/dataset/munge_title_to_name", action="munge_title_to_package_name") m.connect("/util/tag/munge", action="munge_tag") m.connect("/util/status", action="status") m.connect("/util/snippet/{snippet_path:.*}", action="snippet") m.connect("/i18n/{lang}", action="i18n_js_translations") ########### ## /END API ########### map.redirect("/packages", "/dataset") map.redirect("/packages/{url:.*}", "/dataset/{url}") map.redirect("/package", "/dataset") map.redirect("/package/{url:.*}", "/dataset/{url}") with SubMapper(map, controller="package") as m: 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", "autocomplete", "search"]))) m.connect( "/dataset/{action}/{id}/{revision}", action="read_ajax", requirements=dict(action="|".join(["read", "edit", "history"])), ) m.connect( "/dataset/{action}/{id}", requirements=dict( action="|".join( [ "new_resource", "history", "read_ajax", "history_ajax", "follow", "activity", "groups", "unfollow", "delete", "api_data", ] ) ), ) m.connect("dataset_edit", "/dataset/edit/{id}", action="edit", ckan_icon="edit") 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_groups", "/dataset/groups/{id}", action="groups", ckan_icon="group") m.connect("/dataset/{id}.{format}", action="read") m.connect("dataset_resources", "/dataset/resources/{id}", action="resources", ckan_icon="reorder") 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}", action="resource_delete") m.connect( "resource_edit", "/dataset/{id}/resource_edit/{resource_id}", action="resource_edit", ckan_icon="edit" ) m.connect("/dataset/{id}/resource/{resource_id}/download", action="resource_download") m.connect("/dataset/{id}/resource/{resource_id}/download/{filename}", action="resource_download") m.connect("/dataset/{id}/resource/{resource_id}/embed", action="resource_embedded_dataviewer") m.connect( "/dataset/{id}/resource/{resource_id}/viewer", action="resource_embedded_dataviewer", width="960", height="800", ) m.connect("/dataset/{id}/resource/{resource_id}/preview", action="resource_datapreview") m.connect("views", "/dataset/{id}/resource/{resource_id}/views", action="resource_views", ckan_icon="reorder") m.connect("new_view", "/dataset/{id}/resource/{resource_id}/new_view", action="edit_view", ckan_icon="edit") m.connect( "edit_view", "/dataset/{id}/resource/{resource_id}/edit_view/{view_id}", action="edit_view", ckan_icon="edit", ) m.connect("resource_view", "/dataset/{id}/resource/{resource_id}/view/{view_id}", action="resource_view") m.connect("/dataset/{id}/resource/{resource_id}/view/", action="resource_view") # group map.redirect("/groups", "/group") map.redirect("/groups/{url:.*}", "/group/{url}") # 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", 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}", requirements=dict( action="|".join( [ "edit", "delete", "member_new", "member_delete", "history", "followers", "follow", "unfollow", "admins", "activity", ] ) ), ) m.connect("group_about", "/group/about/{id}", action="about", ckan_icon="info-sign"), m.connect("group_edit", "/group/edit/{id}", action="edit", ckan_icon="edit") m.connect("group_members", "/group/members/{id}", action="members", ckan_icon="group"), m.connect("group_activity", "/group/activity/{id}/{offset}", action="activity", ckan_icon="time"), m.connect("group_read", "/group/{id}", action="read", ckan_icon="sitemap") # organizations these basically end up being the same as groups with SubMapper(map, controller="organization") as m: m.connect("organizations_index", "/organization", action="index") m.connect("/organization/list", action="list") m.connect("/organization/new", action="new") m.connect( "/organization/{action}/{id}", requirements=dict(action="|".join(["delete", "admins", "member_new", "member_delete", "history"])), ) m.connect("organization_activity", "/organization/activity/{id}/{offset}", action="activity", ckan_icon="time") m.connect("organization_read", "/organization/{id}", action="read") m.connect("organization_about", "/organization/about/{id}", action="about", ckan_icon="info-sign") m.connect("organization_read", "/organization/{id}", action="read", ckan_icon="sitemap") m.connect("organization_edit", "/organization/edit/{id}", action="edit", ckan_icon="edit") m.connect("organization_members", "/organization/members/{id}", action="members", ckan_icon="group") m.connect( "organization_bulk_process", "/organization/bulk_process/{id}", action="bulk_process", ckan_icon="sitemap" ) lib_plugins.register_package_plugins(map) lib_plugins.register_group_plugins(map) # tags map.redirect("/tags", "/tag") map.redirect("/tags/{url:.*}", "/tag/{url}") map.redirect("/tag/read/{url:.*}", "/tag/{url}", _redirect_code="301 Moved Permanently") map.connect("/tag", controller="tag", action="index") map.connect("/tag/{id}", controller="tag", action="read") # users map.redirect("/users/{url:.*}", "/user/{url}") map.redirect("/user/", "/user") with SubMapper(map, controller="user") as m: m.connect("/user/edit", action="edit") # Note: openid users have slashes in their ids, so need the wildcard # in the route. m.connect("user_generate_apikey", "/user/generate_key/{id}", action="generate_apikey") m.connect("/user/activity/{id}/{offset}", action="activity") m.connect("user_activity_stream", "/user/activity/{id}", action="activity", ckan_icon="time") m.connect("user_dashboard", "/dashboard", action="dashboard", ckan_icon="list") m.connect("user_dashboard_datasets", "/dashboard/datasets", action="dashboard_datasets", ckan_icon="sitemap") m.connect("user_dashboard_groups", "/dashboard/groups", action="dashboard_groups", ckan_icon="group") m.connect( "user_dashboard_organizations", "/dashboard/organizations", action="dashboard_organizations", ckan_icon="building", ) m.connect("/dashboard/{offset}", action="dashboard") m.connect("user_follow", "/user/follow/{id}", action="follow") m.connect("/user/unfollow/{id}", action="unfollow") m.connect("user_followers", "/user/followers/{id:.*}", action="followers", ckan_icon="group") m.connect("user_edit", "/user/edit/{id:.*}", action="edit", ckan_icon="cog") m.connect("user_delete", "/user/delete/{id}", action="delete") m.connect("/user/reset/{id:.*}", action="perform_reset") 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_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") m.connect("/revision/edit/{id}", action="edit") m.connect("/revision/diff/{id}", action="diff") m.connect("/revision/list", action="list") m.connect("/revision/{id}", action="read") # feeds with SubMapper(map, controller="feed") as m: m.connect("/feeds/group/{id}.atom", action="group") m.connect("/feeds/organization/{id}.atom", action="organization") m.connect("/feeds/tag/{id}.atom", action="tag") m.connect("/feeds/dataset.atom", action="general") m.connect("/feeds/custom.atom", action="custom") map.connect("ckanadmin_index", "/ckan-admin", controller="admin", action="index", ckan_icon="legal") map.connect("ckanadmin_config", "/ckan-admin/config", controller="admin", action="config", ckan_icon="check") map.connect("ckanadmin_trash", "/ckan-admin/trash", controller="admin", action="trash", ckan_icon="trash") map.connect("ckanadmin", "/ckan-admin/{action}", controller="admin") with SubMapper(map, controller="ckan.controllers.storage:StorageController") as m: m.connect("storage_file", "/storage/f/{label:.*}", action="file") with SubMapper(map, controller="util") as m: m.connect("/i18n/strings_{lang}.js", action="i18n_js_strings") m.connect("/util/redirect", action="redirect") m.connect("/testing/primer", action="primer") m.connect("/testing/markup", action="markup") for plugin in p.PluginImplementations(p.IRoutes): map = plugin.after_map(map) # sometimes we get requests for favicon.ico we should redirect to # the real favicon location. map.redirect("/favicon.ico", config.get("ckan.favicon")) map.redirect("/*(url)/", "/{url}", _redirect_code="301 Moved Permanently") map.connect("/*url", controller="template", action="view") return map