Esempio n. 1
0
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='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')

    # 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
Esempio n. 2
0
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
Esempio n. 3
0
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', '/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
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
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
Esempio n. 8
0
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',
          '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('/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
Esempio n. 9
0
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

    # 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()
Esempio n. 10
0
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_group_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}')

    # 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')

        for action in [
              'edit',
              'delete',
              'member_new',
              'member_delete',
              'history',
              'followers',
              'follow',
              'unfollow',
              'admins',
              'activity',
          ]:
            m.connect('group_' + action,
                      '/group/' + action + '/{id}',
                      action=action)

        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_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_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='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
Esempio n. 11
0
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()
Esempio n. 12
0
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