diff --git a/ckanext/odsh/controller.py b/ckanext/odsh/controller.py index 62712202988208cc4298bcef7073227bffb06d6c..594ffcb08fe0a389e59f60b018c7855093e195b0 100644 --- a/ckanext/odsh/controller.py +++ b/ckanext/odsh/controller.py @@ -1,7 +1,10 @@ +from types import FunctionType import ckan.lib.base as base +import decorator from ckan.controllers.home import HomeController from ckan.controllers.user import UserController from ckan.controllers.api import ApiController +from ckan.controllers.group import GroupController from ckanext.harvest.controllers.view import ViewController as HarvestController from ckan.controllers.feed import FeedController from ckan.controllers.package import PackageController @@ -10,7 +13,7 @@ import ckan.lib.helpers as h import ckan.authz as authz from ckan.common import c import logging -import matomo +import matomo import ckan.logic as logic from ckan.common import c, request, config import hashlib @@ -19,17 +22,22 @@ from ckanext.dcat.controllers import DCATController from ckan.lib.search.common import ( make_connection, SearchError, SearchQueryError ) +import ckan.model as model import pysolr abort = base.abort log = logging.getLogger(__name__) +render = base.render +get_action = logic.get_action class OdshRouteController(HomeController): def info_page(self): h.redirect_to('http://www.schleswig-holstein.de/odpinfo') + def start(self): h.redirect_to('http://www.schleswig-holstein.de/odpstart') + def not_found(self): abort(404) @@ -38,7 +46,8 @@ class OdshUserController(UserController): def index(self): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).index() + return super(OdshUserController, self).index() + def me(self, locale=None): if not c.user: h.redirect_to(locale=locale, controller='user', action='login', @@ -49,44 +58,118 @@ class OdshUserController(UserController): def dashboard(self, id=None, offset=0): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).dashboard(id,offset) + return super(OdshUserController, self).dashboard(id, offset) def dashboard_datasets(self): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).dashboard_datasets(id) + return super(OdshUserController, self).dashboard_datasets(id) def read(self, id=None): if not c.user: h.redirect_to(controller='user', action='login') - return super(OdshUserController,self).read(id) + return super(OdshUserController, self).read(id) def follow(self, id): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).follow(id) + return super(OdshUserController, self).follow(id) def unfollow(self, id): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).unfollow(id) + return super(OdshUserController, self).unfollow(id) def activity(self, id, offset=0): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).activity(id, offset) + return super(OdshUserController, self).activity(id, offset) def register(self, data=None, errors=None, error_summary=None): if not authz.is_sysadmin(c.user): abort(404) - return super(OdshUserController,self).register(data, errors, error_summary) + return super(OdshUserController, self).register(data, errors, error_summary) class OdshPackageController(PackageController): def edit_view(self, id, resource_id, view_id=None): if not authz.is_sysadmin(c.user): abort(403) - return super(OdshPackageController,self).edit_view(id, resource_id, view_id) + return super(OdshPackageController, self).edit_view(id, resource_id, view_id) + + +class OdshGroupController(GroupController): + def index(self): + group_type = self._guess_group_type() + + page = h.get_page_number(request.params) or 1 + items_per_page = 21 + + context = {'model': model, 'session': model.Session, + 'user': c.user, 'for_view': True, + 'with_private': False} + + query = c.q = request.params.get('q', '') + sort_by = c.sort_by_selected = request.params.get('sort') + try: + self._check_access('site_read', context) + self._check_access('group_list', context) + except NotAuthorized: + abort(403, _('Not authorized to see this page')) + + # pass user info to context as needed to view private datasets of + # orgs correctly + if c.userobj: + context['user_id'] = c.userobj.id + context['user_is_admin'] = c.userobj.sysadmin + + for q in query.split(' '): + try: + data_dict_global_results = { + 'all_fields': False, + 'q': q, + 'sort': sort_by, + 'type': group_type or 'group', + } + print("QUERY") + print(group_type) + print(q) + global_results = self._action('group_list')( + context, data_dict_global_results) + except ValidationError as e: + if e.error_dict and e.error_dict.get('message'): + msg = e.error_dict['message'] + else: + msg = str(e) + h.flash_error(msg) + c.page = h.Page([], 0) + return render(self._index_template(group_type), + extra_vars={'group_type': group_type}) + + data_dict_page_results = { + 'all_fields': True, + 'q': q, + 'sort': sort_by, + 'type': group_type or 'group', + 'limit': items_per_page, + 'offset': items_per_page * (page - 1), + 'include_extras': True + } + page_results = self._action('group_list')(context, + data_dict_page_results) + + print("GROUPS") + print(global_results) + c.page = h.Page( + collection=global_results, + page=page, + url=h.pager_url, + items_per_page=items_per_page, + ) + + c.page.items = page_results + return render(self._index_template(group_type), + extra_vars={'group_type': group_type}) class OdshApiController(ApiController): @@ -102,29 +185,30 @@ class OdshApiController(ApiController): id = request_data['q'] if 'query' in request_data: id = request_data['query'] - userid=None + userid = None if c.user: - userid=hashlib.md5(c.user).hexdigest()[:16] + userid = hashlib.md5(c.user).hexdigest()[:16] matomo.create_matomo_request(userid) else: matomo.create_matomo_request() except Exception, e: log.error(e) - + return ApiController.action(self, logic_function, ver) class OdshDCATController(DCATController): def read_catalog(self, _format): matomo.create_matomo_request() - return DCATController.read_catalog(self,_format) + return DCATController.read_catalog(self, _format) class OdshFeedController(FeedController): def custom(self): matomo.create_matomo_request() - extra_fields=['ext_startdate', 'ext_enddate', 'ext_bbox', 'ext_prev_extent'] + extra_fields = ['ext_startdate', 'ext_enddate', + 'ext_bbox', 'ext_prev_extent'] q = request.params.get('q', u'') fq = '' search_params = {} @@ -135,8 +219,8 @@ class OdshFeedController(FeedController): search_params[param] = value fq += ' %s:"%s"' % (param, value) if param in extra_fields: - extras[param]=value - search_params['extras']=extras + extras[param] = value + search_params['extras'] = extras page = h.get_page_number(request.params) @@ -197,5 +281,22 @@ class OdshAutocompleteController(ApiController): suggest = solr_response.raw_response.get('spellcheck') return base.response.body_file.write(str(suggest)) + +def only_admin(func, *args, **kwargs): + if not authz.is_sysadmin(c.user): + abort(404) + return func(*args, **kwargs) + +class MetaClass(type): + def __new__(meta, classname, bases, classDict): + newClassDict = {} + wdec = decorator.decorator(only_admin) + for attributeName, attribute in bases[0].__dict__.items(): + if isinstance(attribute, FunctionType) and not attributeName.startswith('_'): + print(attribute) + attribute = wdec(attribute) + newClassDict[attributeName] = attribute + return type.__new__(meta, classname, bases, newClassDict) + class OdshHarvestController(HarvestController): - pass + __metaclass__ = MetaClass # wrap all the methods \ No newline at end of file diff --git a/ckanext/odsh/harvest_templates/source/search.html b/ckanext/odsh/harvest_templates/source/search.html new file mode 100644 index 0000000000000000000000000000000000000000..699fb0e45de450c5e014710c3791baa302dfe178 --- /dev/null +++ b/ckanext/odsh/harvest_templates/source/search.html @@ -0,0 +1,99 @@ +{# this template checks for sysadmin and shows a 404 if not. This is a hack as the harvest extension has no way for restricting access #} +{% extends "page.html" %} + +{% block subtitle %} + {% if c.userobj.sysadmin %} + {{ _("Harvest sources") }} + {% else %} + {{ gettext('Error %(error_code)s', error_code=c.code[0]) }} + {% endif %} +{% endblock %} + + +{% block breadcrumb_content %} + <li class="active">{{ h.nav_link(_('Harvest Sources'), named_route='{0}_search'.format(c.dataset_type)) }}</li> +{% endblock %} + +{% if g.ckan_base_version.startswith('2.0') %} + {# CKAN 2.0 #} + + {% block add_action_content %} + {{ h.snippet('snippets/add_source_button.html', dataset_type=c.dataset_type) }} + {% endblock %} +{% endif %} + +{% block primary_content %} + {% if c.userobj.sysadmin %} + {% if g.ckan_base_version.startswith('2.0') %} + {# CKAN 2.0 #} + + {% include 'source/search_2.0.html' %} + + {% else %} + {# > CKAN 2.0 #} + + <section class="module"> + <div class="module-content"> + {# + {% block page_primary_action %} + <div class="page_primary_action"> + {{ h.snippet('snippets/add_source_button.html', dataset_type=c.dataset_type) }} + </div> + {% endblock %} + {% set facets = { + 'fields': c.fields_grouped, + 'search': c.search_facets, + 'titles': c.facet_titles, + 'translated_fields': c.translated_fields, + 'remove_field': c.remove_field } + %} + {% set sorting = [ + (_('Relevance'), 'score desc, metadata_modified desc'), + (_('Name Ascending'), 'title_string asc'), + (_('Name Descending'), 'title_string desc'), + (_('Last Modified'), 'metadata_modified desc'), + (_('Popular'), 'views_recent desc') if g.tracking_enabled else (false, false) ] + %} + {% snippet 'snippets/search_form.html', type='harvest', query=c.q, sorting=sorting, sorting_selected=c.sort_by_selected, count=c.page.item_count, facets=facets, show_empty=request.params, error=c.query_error, placeholder=_("Search harvest sources...") %} + #} + + {{ h.snippet('snippets/source_list.html', sources=c.page.items, show_organization=true) }} + + </div> + + {{ c.page.pager(q=c.q) }} + </section> + + {% endif %} + {% else %} + <div class="module-content error-page"> + <div class="error-title"> + HTTP Status 404 + <div class="error-body"><h2>Seite nicht gefunden</h2> + <h3>Wie finde ich die gesuchten Inhalte im Landesportal?</h3> + + <p><a class="" href="http://www.schleswig-holstein.de/odpstart" title="Zur Startseite">Zur Startseite des Open-Data-Portals</a></p> + + <h3>Kontakt</h3> + <p>Bei Fragen oder Problemen mit dem Open-Data-Portal schicken Sie bitte eine E-Mail an die Adresse opendata@lr.landsh.de oder verwenden das Kontaktformular:</p> + <p><a class="" href="https://www.schleswig-holstein.de/odpkontakt" title="Kontakt">Zum Kontaktformular</a></p> + </div> + </div> + </div> + {% endif %} + +{% endblock %} + +{% block breadcrumb %} +{% endblock %} + +{% block secondary %}{% endblock %} +{# +{% block secondary_content %} + {% if c.userobj.sysadmin %} + {% for facet in c.facet_titles %} + {{ h.snippet('snippets/facet_list.html', title=c.facet_titles[facet], name=facet, alternative_url=h.url_for('{0}_search'.format(c.dataset_type))) }} + {% endfor %} + {% endif %} +{% endblock %} +#} \ No newline at end of file diff --git a/ckanext/odsh/plugin.py b/ckanext/odsh/plugin.py index 6a2991173930385ec03c61e0513d249557a23600..a27c6da428ec30d3fb1636c11273e1f7941de119 100644 --- a/ckanext/odsh/plugin.py +++ b/ckanext/odsh/plugin.py @@ -92,6 +92,39 @@ class OdshAutocompletePlugin(plugins.SingletonPlugin): map.connect('/autocomplete/{q}', controller=controller, action='autocomplete') return map +class OdshHarvestPlugin(plugins.SingletonPlugin): + plugins.implements(plugins.IRoutes, inherit=True) + plugins.implements(plugins.IConfigurer) + + def update_config(self, config_): + toolkit.add_template_directory(config_, 'harvest_templates') + plugins.implements(plugins.IRoutes, inherit=True) + def before_map(self, map): + DATASET_TYPE_NAME='harvest' + controller = 'ckanext.odsh.controller:OdshHarvestController' + + # map.connect('{0}_delete'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/delete/:id',controller=controller, action='delete') + # map.connect('{0}_refresh'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/refresh/:id',controller=controller, + # action='refresh') + # map.connect('{0}_admin'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/admin/:id', controller=controller, action='admin') + map.connect('{0}_about'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/about/:id', controller=controller, action='about') + # map.connect('{0}_clear'.format(DATASET_TYPE_NAME), '/' + DATASET_TYPE_NAME + '/clear/:id', controller=controller, action='clear') + + # map.connect('harvest_job_list', '/' + DATASET_TYPE_NAME + '/{source}/job', controller=controller, action='list_jobs') + # map.connect('harvest_job_show_last', '/' + DATASET_TYPE_NAME + '/{source}/job/last', controller=controller, action='show_last_job') + # map.connect('harvest_job_show', '/' + DATASET_TYPE_NAME + '/{source}/job/{id}', controller=controller, action='show_job') + # map.connect('harvest_job_abort', '/' + DATASET_TYPE_NAME + '/{source}/job/{id}/abort', controller=controller, action='abort_job') + + # map.connect('harvest_object_show', '/' + DATASET_TYPE_NAME + '/object/:id', controller=controller, action='show_object') + # map.connect('harvest_object_for_dataset_show', '/dataset/harvest_object/:id', controller=controller, action='show_object', ref_type='dataset') + + # org_controller = 'ckanext.harvest.controllers.organization:OrganizationController' + # map.connect('{0}_org_list'.format(DATASET_TYPE_NAME), '/organization/' + DATASET_TYPE_NAME + '/' + '{id}', controller=org_controller, action='source_list') + return map + + def after_map(self, map): + return map + class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm): plugins.implements(plugins.IConfigurer) @@ -174,11 +207,14 @@ class OdshPlugin(plugins.SingletonPlugin, DefaultTranslation, DefaultDatasetForm m.connect('/feeds/custom.atom', action='custom') # with SubMapper(map, controller='ckanext.odsh.controller:OdshHarvestController') as m: - # m.connect('/harvest', action='custom') + # m.connect('/harvest', action='index') with SubMapper(map, controller='ckanext.odsh.controller:OdshPackageController') as m: m.connect('new_view', '/dataset/{id}/resource/{resource_id}/new_view', action='edit_view', ckan_icon='pencil-square-o') + # with SubMapper(map, controller='ckanext.odsh.controller:OdshGroupController') as m: + # m.connect('organizations_index', '/organization', action='index') + # redirect all user routes to custom controller with SubMapper(map, controller='ckanext.odsh.controller:OdshUserController') as m: m.connect('user_index', '/user', action='index') diff --git a/setup.py b/setup.py index 2ecc2219a828bd1e9fb3315d0cc0bdbcd5614915..c01509648e39c46ed28d298e6d73489e774f7747 100755 --- a/setup.py +++ b/setup.py @@ -85,6 +85,7 @@ setup( statistikamtnord_harvester=ckanext.odsh.harvesters:StatistikamtNordHarvester kiel_harvester=ckanext.odsh.harvesters:KielHarvester odsh_autocomplete=ckanext.odsh.plugin:OdshAutocompletePlugin + odsh_harvest=ckanext.odsh.plugin:OdshHarvestPlugin [paste.paster_command] odsh_initialization = ckanext.odsh.commands.initialization:Initialization