diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/controllers/home.py b/ckanext-d4science_theme/ckanext/d4science_theme/controllers/home.py index c5746d5..7e5f31f 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/controllers/home.py +++ b/ckanext-d4science_theme/ckanext/d4science_theme/controllers/home.py @@ -57,7 +57,7 @@ class d4SHomeController(): 'sort': 'views_recent desc', 'fq': 'capacity:"public"' } - query = logic.get_action('package_search')(context, data_dict) + query = logic.get_action('package_search')(context, data_dict or {}) c.search_facets = query['search_facets'] c.package_count = query['count'] c.datasets = query['results'] diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/controllers/systemtype.py b/ckanext-d4science_theme/ckanext/d4science_theme/controllers/systemtype.py index 8d94e5b..af16a6f 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/controllers/systemtype.py +++ b/ckanext-d4science_theme/ckanext/d4science_theme/controllers/systemtype.py @@ -71,7 +71,7 @@ class d4STypeController(): 'sort': 'views_recent desc', 'fq': 'capacity:"public"' } - query = logic.get_action('package_search')(context, data_dict) + query = logic.get_action('package_search')(context, data_dict or {}) c.search_facets = query['search_facets'] c.package_count = query['count'] c.datasets = query['results'] diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/db.py b/ckanext-d4science_theme/ckanext/d4science_theme/db.py new file mode 100644 index 0000000..034a3ac --- /dev/null +++ b/ckanext-d4science_theme/ckanext/d4science_theme/db.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014 CoNWeT Lab., Universidad Politécnica de Madrid + +# This file is part of CKAN Private Dataset Extension. + +# CKAN Private Dataset Extension is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# CKAN Private Dataset Extension is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with CKAN Private Dataset Extension. If not, see . + +from __future__ import absolute_import +from logging import getLogger + +import sqlalchemy as sa + +log = getLogger(__name__) + +AllowedUser = None + +def init_db(model): + log.debug("call initDB...") + global AllowedUser + if AllowedUser is None: + + class _AllowedUser(model.DomainObject): + + @classmethod + def get(cls, **kw): + '''Finds all the instances required.''' + query = model.Session.query(cls).autoflush(False) + results = query.filter_by(**kw).all() + log.debug("results in get %s", results) + if not isinstance(results, list): + log.debug("Errore: il risultato di get() non è una lista. Risultato:", results) + results = [] + return results + + AllowedUser = _AllowedUser + + log.debug("allowed user: %s", AllowedUser) + + # FIXME: Maybe a default value should not be included... + package_allowed_users_table = sa.Table( + 'package_allowed_users', + model.meta.metadata, + sa.Column('package_id', sa.types.UnicodeText, primary_key=True, default=u''), + sa.Column('user_name', sa.types.UnicodeText, primary_key=True, default=u''), + ) + + # Create the table only if it does not exist + package_allowed_users_table.create(checkfirst=True) + + model.meta.mapper(AllowedUser, package_allowed_users_table,) diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/helpers.py b/ckanext-d4science_theme/ckanext/d4science_theme/helpers.py index 2584283..e6ffcbd 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/helpers.py +++ b/ckanext-d4science_theme/ckanext/d4science_theme/helpers.py @@ -25,6 +25,8 @@ import collections import ckan.plugins.toolkit as tk import ckan.logic as logic +from ckanext.d4science_theme import db + log = getLogger(__name__) systemtype_field = 'systemtypefield' @@ -730,3 +732,77 @@ def get_site_statistics() -> dict[str, int]: 'group_count': len(logic.get_action('group_list')({}, {})), 'organization_count': len(logic.get_action('organization_list')({}, {})) } + +#private dataset + +def is_dataset_acquired(pkg_dict): + + log.debug("is_dataset package value: %s", pkg_dict) #restituisce True + + db.init_db(model) + log.debug("post init %s", db) + + #return False + if tk.c.user: + return len(db.AllowedUser.get(package_id=pkg_dict['id'], user_name=tk.c.user)) > 0 + else: + return False + +def is_owner(pkg_dict): + return False + #if tk.c.userobj is not None: + # return tk.c.userobj.id == pkg_dict['creator_user_id'] + #else: + # return False + + +def get_allowed_users_str(users): + if users: + return ','.join([user for user in users]) + else: + return '' + + +def can_read(pkg_dict): + try: + context = {'user': tk.c.user, 'userobj': tk.c.userobj, 'model': model} + return tk.check_access('package_show', context, pkg_dict) + except tk.NotAuthorized: + return False + + +def get_config_bool_value(config_name, default_value=False): + env_name = config_name.upper().replace('.', '_') + value = os.environ.get(env_name, tk.config.get(config_name, default_value)) + return value if type(value) == bool else value.strip().lower() in ('true', '1', 'on') + + +def show_acquire_url_on_create(): + return get_config_bool_value('ckan.privatedatasets.show_acquire_url_on_create') + + +def show_acquire_url_on_edit(): + return get_config_bool_value('ckan.privatedatasets.show_acquire_url_on_edit') + + +def acquire_button(package): + ''' + Return a Get Access button for the given package id when the dataset has + an acquisition URL. + + :param package: the the package to request access when the get access + button is clicked + :type package: Package + + :returns: a get access button as an HTML snippet + :rtype: string + + ''' + + if 'acquire_url' in package and request.path.startswith('/dataset')\ + and package['acquire_url'] != '': + url_dest = package['acquire_url'] + data = {'url_dest': url_dest} + return tk.render_snippet('snippets/acquire_button.html', data) + else: + return '' \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/plugin.py b/ckanext-d4science_theme/ckanext/d4science_theme/plugin.py index 5769285..2076b60 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/plugin.py +++ b/ckanext-d4science_theme/ckanext/d4science_theme/plugin.py @@ -4,27 +4,31 @@ from logging import getLogger import ckan.plugins as plugins from ckanext.d4science_theme import helpers +from ckanext.d4science_theme import validators import ckan.plugins.toolkit as toolkit import ckan.model as model from ckanext.d4science_theme.controllers.home import d4SHomeController from ckanext.d4science_theme.controllers.systemtype import d4STypeController from ckanext.d4science_theme.controllers.organization import OrganizationVREController +import sqlalchemy as sa +from ckan.config.middleware.common_middleware import TrackingMiddleware +import ckan.lib.dictization.model_save as model_save #from ckan.controllers.home import HomeController #from ckan.plugins import IRoutes from flask import Blueprint, render_template +from ckan.types import Context + +from typing import ( + Any, Collection, Optional, TYPE_CHECKING, Type, Union, cast, overload, + Literal, +) from ckan.common import ( g ) from flask import Flask, g -from ckan.lib.app_globals import app_globals -import ckan.plugins.toolkit as toolkit -# Created by Francesco Mangiacrapa -# francesco.mangiacrapa@isti.cnr.it -# ISTI-CNR Pisa (ITALY) - log = getLogger(__name__) d4s_ctg_namespaces_controller = None @@ -35,22 +39,17 @@ def remove_check_replicated_custom_key(schema): return schema -#CREATED BY FRANCESCO MANGIACRAPA FOR OVERRIDING THE package_extras_save FROM dictization.model_save.py -# Is this needed? -def _package_extras_save(extra_dicts, obj, context): - ''' It can save repeated extras as key-value ''' +def _package_extras_save( + extra_dicts: Optional[list[dict[str, Any]]], pkg: 'model.Package', + context: Context) -> None: allow_partial_update = context.get("allow_partial_update", False) if extra_dicts is None and allow_partial_update: return - model = context["model"] session = context["session"] - - #ADDED BY FRANCESCO MANGIACRAPA - log.debug("extra_dicts: "+ str(extra_dicts)) - #print "extra_dicts: "+str(extra_dicts) - - extras_list = obj.extras_list + model = context["model"] + #extras_list = obj.extras_list + extras_list = session.query(model.PackageExtra).filter_by(package_id=pkg.id).all() #extras = dict((extra.key, extra) for extra in extras_list) old_extras = {} extras = {} @@ -58,41 +57,27 @@ def _package_extras_save(extra_dicts, obj, context): old_extras.setdefault(extra.key, []).append(extra.value) extras.setdefault(extra.key, []).append(extra) - #ADDED BY FRANCESCO MANGIACRAPA - #print "old_extras: "+str(old_extras) - new_extras = {} for extra_dict in extra_dicts or []: - #print 'extra_dict key: '+extra_dict["key"] + ', value: '+extra_dict["value"] #new_extras.setdefault(extra_dict["key"], []).append(extra_dict["value"]) if extra_dict.get("deleted"): - log.debug("extra_dict deleted: "+str(extra_dict["key"])) - #print 'extra_dict deleted: '+extra_dict["key"] continue #if extra_dict['value'] is not None and not extra_dict["value"] == "": if extra_dict['value'] is not None: new_extras.setdefault(extra_dict["key"], []).append(extra_dict["value"]) - #ADDED BY FRANCESCO MANGIACRAPA - log.debug("new_extras: "+str(new_extras)) - #print "new_extras: "+str(new_extras) - #new for key in set(new_extras.keys()) - set(old_extras.keys()): state = 'active' - log.debug("adding key: "+str(key)) - #print "adding key: "+str(key) extra_lst = new_extras[key] for extra in extra_lst: - extra = model.PackageExtra(state=state, key=key, value=extra) + extra = model.PackageExtra(state=state, key=key, value=extra, package_id = pkg.id) session.add(extra) extras_list.append(extra) #deleted for key in set(old_extras.keys()) - set(new_extras.keys()): - log.debug("deleting key: "+str(key)) - #print "deleting key: "+str(key) extra_lst = extras[key] for extra in extra_lst: state = 'deleted' @@ -105,13 +90,9 @@ def _package_extras_save(extra_dicts, obj, context): for value in new_extras[key]: old_occur = old_extras[key].count(value) new_occur = new_extras[key].count(value) - log.debug("value: "+str(value) + ", new_occur: "+str(new_occur)+ ", old_occur: "+str(old_occur)) - #print "value: "+str(value) + ", new_occur: "+str(new_occur) + ", old_occur: "+str(old_occur) # it is an old value deleted or not if value in old_extras[key]: if old_occur == new_occur: - #print "extra - occurrences of: "+str(value) +", are equal into both list" - log.debug("extra - occurrences of: "+str(value) +", are equal into both list") #there is a little bug, this code return always the first element, so I'm fixing with #FIX-STATUS extra_values = get_package_for_value(extras[key], value) #extras_list.append(extra) @@ -119,47 +100,32 @@ def _package_extras_save(extra_dicts, obj, context): state = 'active' extra.state = state session.add(extra) - #print "extra updated: "+str(extra) - log.debug("extra updated: "+str(extra)) elif new_occur > old_occur: - #print "extra - a new occurrence of: "+str(value) +", is present into new list, adding it to old list" - log.debug("extra - a new occurrence of: "+str(value) +", is present into new list, adding it to old list") state = 'active' - extra = model.PackageExtra(state=state, key=key, value=value) + extra = model.PackageExtra(state=state, key=key, value=value, package_id = pkg.id) extra.state = state session.add(extra) extras_list.append(extra) old_extras[key].append(value) - log.debug("old extra values updated: "+str(old_extras[key])) - #print "old extra values updated: "+str(old_extras[key]) else: #remove all occurrences deleted - this code could be optimized, it is run several times but could be performed one shot countDelete = old_occur-new_occur - log.debug("extra - occurrence of: "+str(value) +", is not present into new list, removing "+str(countDelete) + " occurrence/s from old list") - #print "extra - occurrence of: "+str(value) +", is not present into new list, removing "+str(countDelete)+" occurrence/s from old list" extra_values = get_package_for_value(extras[key], value) for idx, extra in enumerate(extra_values): if idx < countDelete: - #print "extra - occurrence of: "+str(value) +", is not present into new list, removing it from old list" - log.debug("pkg extra deleting: "+str(extra.value)) - #print "pkg extra deleting: "+str(extra.value) state = 'deleted' extra.state = state else: - #print "pkg extra reactivating: "+str(extra.value) - log.debug("pkg extra reactivating: "+str(extra.value)) state = 'active' extra.state = state - session.add(extra) + session.add(extra) #valuta se metterlo dentro il for, ma fuori dall'else else: - #print "extra new value: "+str(value) - log.debug("extra new value: "+str(value)) state = 'active' - extra = model.PackageExtra(state=state, key=key, value=value) + extra = model.PackageExtra(state=state, key=key, value=value, package_id = pkg.id) extra.state = state session.add(extra) extras_list.append(extra) @@ -171,35 +137,52 @@ def _package_extras_save(extra_dicts, obj, context): if value not in new_extras[key]: extra_values = get_package_for_value(extras[key], value) for extra in extra_values: - #print "not present extra deleting: "+str(extra) - log.debug("not present extra deleting: "+str(extra)) state = 'deleted' extra.state = state + #add session.delete(extra)? -#ADDED BY FRANCESCO MANGIACRAPA def get_package_for_value(list_package, value): - ''' Returns a list of packages containing the value passed in input - ''' - lst = [] - for x in list_package: - if x.value == value: - lst.append(x) - else: - return lst - - return lst + '''Returns a list of packages containing the value passed in input''' + return [x for x in list_package if x.value == value] +#OVERRIDING BASE SQL ALCHEMY ENGINE INSTANCE serve per la connessione con gcube? +def _init_TrackingMiddleware(self, app, config): + self.app = app + log.debug('TrackingMiddleware d4Science instance') + sqlalchemy_url = config.get('sqlalchemy.url') + log.debug('sqlalchemy_url read: '+str(sqlalchemy_url)) + + sqlalchemy_pool = config.get('sqlalchemy.pool_size') + if sqlalchemy_pool is None: + sqlalchemy_pool = 5 + + log.debug('sqlalchemy_pool read: '+str(sqlalchemy_pool)) + sqlalchemy_overflow = config.get('sqlalchemy.max_overflow') + + if sqlalchemy_overflow is None: + sqlalchemy_overflow = 10 + + log.debug('sqlalchemy_overflow read: '+str(sqlalchemy_overflow)) + + try: + self.engine = sa.create_engine(sqlalchemy_url, pool_size=int(sqlalchemy_pool), max_overflow=int(sqlalchemy_overflow)) + except TypeError as e: + log.error('pool size does not work: ' +str(e.args)) + self.engine = sa.create_engine(sqlalchemy_url) + class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm): plugins.implements(plugins.IConfigurer) - plugins.implements(plugins.IDatasetForm) + plugins.implements(plugins.IDatasetForm, inherit=True) plugins.implements(plugins.ITemplateHelpers) plugins.implements(plugins.IFacets) #plugins.implements(IRoutes, inherit=True) #ckan 2.10 plugins.implements(plugins.IBlueprint) + plugins.implements(plugins.IValidators) + plugins.implements(plugins.IPackageController, inherit=True) # IConfigurer def update_config(self, config_): @@ -217,39 +200,117 @@ class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm) # that we'll use to refer to this fanstatic directory from CKAN # templates. toolkit.add_resource('assets', 'd4science_theme') - # toolkit.add_resource('assets', 'd4science_scripts') + def _modify_package_schema(self): + log.debug("*** modify package ***") + + # Personalizza il campo 'extras' rimuovendo il validatore extra_key_not_in_root_schema + + return { + 'private': [toolkit.get_validator('ignore_missing'), + toolkit.get_validator('boolean_validator')], + 'extras': { + 'id': [toolkit.get_validator('ignore')], # Ignora 'id' come prima + 'key': [toolkit.get_validator('not_empty'), toolkit.get_validator('unicode_safe'), validators.ignore_duplicate_keys], # Aggiunto ignore duplicate + 'value': [toolkit.get_validator('not_missing')] + } + } + #IDatasetForm def create_package_schema(self): # let's grab the default schema in our plugin + log.debug("creating package....") schema = super(D4Science_ThemePlugin, self).create_package_schema() + #log.debug("schema after create prima del validator %s", schema) schema = remove_check_replicated_custom_key(schema) + #log.debug("create_package1 (remove __before): %s", schema) + schema.update(self._modify_package_schema()) #d.package_dict_save = _package_dict_save + #log.debug("create_package2 (remove extras validator): %s", schema) return schema #IDatasetForm def update_package_schema(self): + log.debug("** update_package **") schema = super(D4Science_ThemePlugin, self).update_package_schema() schema = remove_check_replicated_custom_key(schema) + #log.debug("update_package1 (remove __before) %s", schema) + schema.update(self._modify_package_schema()) + #log.debug("update_package2 (remove extras validator) %s", schema) return schema #IDatasetForm def show_package_schema(self): + log.debug("** show package **") schema = super(D4Science_ThemePlugin, self).show_package_schema() + #log.debug("show_package1 %s", schema) schema = remove_check_replicated_custom_key(schema) + #log.debug("show_package1.5 no before %s", schema) + schema.update({ + 'extras': { + 'id': [toolkit.get_validator('ignore')], # Ignora 'id' come prima + 'key': [toolkit.get_validator('not_empty'), toolkit.get_validator('unicode_safe'), validators.ignore_duplicate_keys], # Aggiunto ignore duplicate + 'value': [toolkit.get_validator('not_missing')] + } + }) return schema #IDatasetForm def is_fallback(self): # Return True to register this plugin as the default handler for package types not handled by any other IDatasetForm plugin - return False + return True #IDatasetForm def package_types(self): # This plugin doesn't handle any special package types, it just # registers itself as the default (above). return [] + + def get_validators(self): + return { + 'ignore_duplicate_keys': validators.ignore_duplicate_keys + } + + def convert_to_boolean(self, value): + log.debug("value boolean %s", value) + if isinstance(value, str): + if value.lower() == 'true': + return True + elif value.lower() == 'false': + return False + return value + #IValidator + def validate(self, context, data_dict, schema, action): + # Modifica il comportamento del validatore per ignorare le chiavi duplicate + # Assicurati che 'private' sia un valore booleano + log.debug("calling, validate, questo è lo schema: %s", schema) + data_dict['private'] = self.convert_to_boolean(data_dict.get('private', False)) + errors = [] + #todo change this function + if 'extras' in data_dict: + log.debug("extras presente") + extras = data_dict['extras'] + new_extras = [] + log.debug("extras value before for: %s e lunghezza: %s", extras, len(extras)) + # Aggiungi ogni coppia chiave-valore alla nuova lista, mantenendo i duplicati + for extra in extras: + new_extras.append(extra) + log.debug("Aggiunta extra con chiave duplicata: %s -> %s", extra['key'], extra['value']) + + # Aggiorna il data_dict con la lista di extras che include i duplicati + data_dict['extras'] = new_extras + log.debug("new extras mantenendo i duplicati: %s", new_extras) + log.debug("pre return validate %s", data_dict) + return data_dict, errors + + def apply_custom_extras_validator(self, data_dict): + """ + Funzione helper per applicare il validator agli extras + """ + extras = data_dict.get('extras', []) + # Esegui il validator per assicurare il salvataggio dei duplicati + validators.ignore_duplicate_keys('extras', data_dict, [], {}) #ITemplateHelpers def get_helpers(self): @@ -290,11 +351,18 @@ class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm) 'd4science_get_location_to_bboxes' : helpers.get_location_to_bboxes, 'd4science_get_content_moderator_system_placeholder': helpers.get_content_moderator_system_placeholder, 'd4science_get_site_statistics': helpers.get_site_statistics, + 'is_dataset_acquired': helpers.is_dataset_acquired, + 'get_allowed_users_str': helpers.get_allowed_users_str, + 'is_owner': helpers.is_owner, + 'can_read': helpers.can_read, + 'show_acquire_url_on_create': helpers.show_acquire_url_on_create, + 'show_acquire_url_on_edit': helpers.show_acquire_url_on_edit, + 'acquire_button': helpers.acquire_button } #Overriding package_extras_save method # Is this needed? - # model_save.package_extras_save = _package_extras_save + model_save.package_extras_save = _package_extras_save #Overriding index home controller - rimosso in ckan 2.10 #d4sHC = d4SHomeController() @@ -302,6 +370,9 @@ class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm) global d4s_ctg_namespaces_controller + #OVERRIDING BASE SQL ALCHEMY ENGINE INSTANCE + TrackingMiddleware.__init__ = _init_TrackingMiddleware + #if d4s_ctg_namespaces_controller is None: # log.info("d4s_ctg_namespaces_controller instancing...") # d4s_ctg_namespaces_controller = helpers.get_d4s_namespace_controller() @@ -437,4 +508,3 @@ class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm) return facets_dict - diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/read.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/read.html index db5061f..a851298 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/read.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/read.html @@ -1,21 +1,24 @@ -{% extends "package/read_base.html" %} +{% ckan_extends %} +{% set dataset_extent = h.get_pkg_dict_extra(c.pkg_dict, 'spatial', '') %} +{% set d4science_cms_obj_placeholders = h.d4science_get_content_moderator_system_placeholder() %} +{% set moderation_item_status = h.get_pkg_dict_extra(c.pkg_dict,d4science_cms_obj_placeholders.item_status,'') %} {% block primary_content_inner %} - {{ super() }} - {% set dataset_extent = h.get_pkg_dict_extra(c.pkg_dict, 'spatial', '') %} - {% if dataset_extent %} - {% snippet "spatial/snippets/dataset_map.html", extent=dataset_extent %} - {% endif %} +{% if moderation_item_status %} + + {{ moderation_item_status }} + +{% endif %} {% block package_description %} {% if pkg.private %} - - + + {{ _('Private') }} {% endif %} -

+
{% block page_heading %} - {{ h.dataset_display_name(pkg) }} + {{ pkg.title or pkg.name }} {% if pkg.state.startswith('draft') %} [{{ _('Draft') }}] {% endif %} @@ -23,28 +26,34 @@ [{{ _('Deleted') }}] {% endif %} {% endblock %} -

+ {% block package_notes %} {% if pkg.notes %} -
- {{ h.render_markdown(h.get_translated(pkg, 'notes')) }} +
+ {{ h.render_markdown(pkg.notes) }}
{% endif %} {% endblock %} {# FIXME why is this here? seems wrong #} {% endblock %} + + + {% if dataset_extent %} + {% snippet "spatial/snippets/dataset_map.html", extent=dataset_extent %} + {% endif %} + + {% block package_tags %} +
{{_('Tags')}}
+ {% snippet "package/snippets/tags.html", tags=pkg.tags %} + {% endblock %} {% block package_resources %} {% snippet "package/snippets/resources_list.html", pkg=pkg, resources=pkg.resources %} {% endblock %} - {% block package_tags %} - {% snippet "package/snippets/tags.html", tags=pkg.tags %} - {% endblock %} - {% block package_additional_info %} {% snippet "package/snippets/additional_info.html", pkg_dict=pkg %} {% endblock %} - -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/additional_info.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/additional_info.html index d4070ce..e058199 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/additional_info.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/additional_info.html @@ -1,5 +1,74 @@ +{% set key_item_url = _('Item') + ' URL' %} + + + + +
-

{{ _('Additional Info') }}

+ {% block extras scoped %} + {# + This performs a sort + {% for extra in h.sorted_extras(pkg_dict.extras) %} + #} + {% if pkg_dict.extras %} + {# Added by Francesco Mangiacrapa, see 17901 #} + {% set extra_item_url = h.get_pkg_dict_extra(pkg_dict,key_item_url) %} + {% if extra_item_url %} +
{{ key_item_url }}
+ + + + + +
{{ extra_item_url }}{% snippet "package/snippets/qrcode_show.html", package_url=extra_item_url %}
+ {% endif %} + {% set extras_indexed_for_categories = h.d4science_get_extras_indexed_for_namespaces(pkg_dict.extras) %} + {% for k_cat in extras_indexed_for_categories %} + {% set category_idx = extras_indexed_for_categories[k_cat] %} + {% if(k_cat!='nocategory') %} +
{{category_idx.category.title}}
+ {% if category_idx.category.description %} +

Description: {{category_idx.category.description}}

+ {% endif %} + + + + + + + + + {% set my_extras = h.d4science_get_extra_for_category(extras_indexed_for_categories, k_cat) %} + {% snippet "package/snippets/extras_table.html", my_extras=my_extras, key_item_url=key_item_url %} + +
{{ _('Field') }}{{ _('Value') }}
+
+ {% endif %} + {% endfor %} + {% set my_extras = h.d4science_get_extra_for_category(extras_indexed_for_categories, 'nocategory') %} + {% if my_extras|length > 0 %} +
{{ _('Additional Info') }}
+ + + + + + + + + {% snippet "package/snippets/extras_table.html", my_extras=my_extras, key_item_url=key_item_url %} + +
{{ _('Field') }}{{ _('Value') }}
+
+ {% endif %} + {% endif %} + {% endblock %} +
{{ _('Management Info') }}
@@ -13,11 +82,7 @@ {% if h.is_url(pkg_dict.url) %} - + {% else %} {% endif %} @@ -35,16 +100,17 @@ {% endif %} - + {# Added by Francesco Mangiacrapa #} + {% set user_maintainer = h.d4science_get_user_info(pkg_dict.maintainer) %} {% if pkg_dict.maintainer_email %} - + {% elif pkg_dict.maintainer %} - + {% endif %} @@ -78,18 +144,7 @@ {% endif %} - - {% block extras scoped %} - {% for extra in h.sorted_extras(pkg_dict.extras) %} - {% set key, value = extra %} - - - - - {% endfor %} - {% endblock %} - - {% endblock %} - +
{{ _('Source') }} - - {{ pkg_dict.url }} - - {{ h.link_to(pkg_dict.url, pkg_dict.url, rel='foaf:homepage', target='_blank') }}{{ pkg_dict.url }}{{ pkg_dict.author }}
{{ _('Maintainer') }}{{ h.mail_to(email_address=pkg_dict.maintainer_email, name=pkg_dict.maintainer) }}{{ h.mail_to(email_address=pkg_dict.maintainer_email, name=user_maintainer.fullname if (user_maintainer and user_maintainer.fullname) else pkg_dict.maintainer) }}
{{ _('Maintainer') }}{{ pkg_dict.maintainer }}{{ user_maintainer.fullname if (user_maintainer and user_maintainer.fullname) else pkg_dict.maintainer }}
{{ _(key|e) }}{{ value }}
-
+ {% endblock %} + \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/extras_table.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/extras_table.html new file mode 100644 index 0000000..4a809cc --- /dev/null +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/extras_table.html @@ -0,0 +1,59 @@ + + + + {% set d4science_cms_obj_placeholders = h.d4science_get_content_moderator_system_placeholder() %} + {% for extra_lnk in my_extras %} + {% set index = loop.index %} + {% for k in extra_lnk.keys() %} + {# Added by Francesco Mangiacrapa, see: #21701 #} + {% set extra_value = extra_lnk[k] %} + + {% if extra_value is defined and extra_value|length %} + {# Added by Francesco Mangiacrapa, see: #7055 #} + {% set isHttp = extra_value.startswith(('http://', 'https://')) %} + + + {% if k != key_item_url and (not k.startswith(d4science_cms_obj_placeholders.prefix)) %} + {{ _(k) }} + {% if isHttp %} + {% if k == 'graphic-preview-file'%} + {{ + {% else %} + {{ extra_value }} + {% endif %} + {% elif k == 'responsible-party' %} +
{{ extra_value }}
+ {% elif k == 'dataset-reference-date' %} +
{{ extra_value }}
+ {% elif k == 'coupled-resource' %} +
{{ extra_value }}
+ {% elif k.startswith('Zenodo') %} + + {% elif k == 'spatial' %} +
{{ extra_value }}
+ {% else %} + {{ extra_value }} + {% endif %} + {% endif %} + {% endif %} + {% endfor %} + {% endfor %} \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/info.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/info.html index 20678ff..60420b8 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/info.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/info.html @@ -1,41 +1,48 @@ {# -Displays a sidebar module with information for given package - -pkg - The package dict that owns the resources. - -Example: - - {% snippet "package/snippets/info.html", pkg=pkg %} - -#} -{% block package_info %} - {% if pkg %} -
-
-
- {% block package_info_inner %} - {% block heading %} -

{{ h.dataset_display_name(pkg) }}

- {% endblock %} - {% block nums %} - {% set num_followers = h.follow_count('dataset', pkg.id) %} -
-
-
{{ _('Followers') }}
-
{{ h.SI_number_span(num_followers) }}
-
-
- {% endblock %} - {% block follow_button %} - {% if not hide_follow_button %} - -
- {% endif %} -{% endblock %} + + {% endif %} + {% endblock %} + \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/qrcdode_show.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/qrcdode_show.html new file mode 100644 index 0000000..a0ec361 --- /dev/null +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/qrcdode_show.html @@ -0,0 +1,6 @@ +{% set qr_code_image = h.d4science_get_qrcode_for_url(package_url) %} +{% block package_qrcode_for_url %} +
{{
+{% endblock %} + + \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resource_item.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resource_item.html index 31d1c56..0f668fc 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resource_item.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resource_item.html @@ -1,82 +1,108 @@ -{# - Renders a single resource with icons and view links. - - res - A resource dict to render - pkg - A package dict that the resource belongs to - can_edit - Whether the user is allowed to edit the resource - url_is_edit - Whether the link to the resource should be to editing it (set to False to make the link view the resource) - url - URL of the resource details page(resource edit/read depending on url_is_edit, by default). - - Example: - - {% snippet "package/snippets/resource_item.html", res=resource, pkg=pkg, can_edit=True, url_is_edit=False %} - -#} -{% set url_action = pkg.type ~ ('_resource.edit' if url_is_edit and can_edit else '_resource.read') %} -{% set url = url or h.url_for(url_action, id=pkg.name, resource_id=res.id) %} - +{% set can_edit = h.check_access('package_update', {'id':pkg.id }) %} +{% set url_action = 'resource_edit' if url_is_edit and can_edit else 'resource_read' %} +{# {% set url = h.url_for(controller='package', action=url_action, id=pkg.name, resource_id=res.id) %} #} +{% set url = h.url_for('dataset_resource.read', id=pkg.name, resource_id=res.id) %}
  • +{# Added by Francesco Mangiacrapa block custom_view_on_resources see:4851 #} +{% block custom_view_on_resources %} +{% set user = c.user %} +{% if user %} {% block resource_item_title %} - {{ h.resource_display_name(res) | truncate(50) }}{{ h.get_translated(res, 'format') }} - {{ h.popular('views', res.tracking_summary.total, min=10) if res.tracking_summary }} + {{ h.resource_display_name(res) | truncate(50) }}{{ res.format }} + {{ h.popular('views', res.tracking_summary.total, min=10) }} {% endblock %} {% block resource_item_description %}

    {% if res.description %} - {{ h.markdown_extract(h.get_translated(res, 'description'), extract_length=80) }} + {{ h.markdown_extract(res.description, extract_length=80) }} {% endif %}

    {% endblock %} {% block resource_item_explore %} {% if not url_is_edit %} + {# Only if can edit, explorer button is shown with several facility #} + {% if can_edit %} + {% else %} + + {{ _('Go to resource') }} + + {% endif %} {% endif %} {% endblock %} +{% else %} + {% block resource_item_title2 %} + {# Updated by Francesco Mangiacrapa, see: #10056 #} + + {{ h.resource_display_name(res) | truncate(50) }}{{ res.format }} + {{ h.popular('views', res.tracking_summary.total, min=10) }} + + {% endblock %} +
    + {% block resource_item_description2 %} +

    + {% if res.description %} + {{ h.markdown_extract(res.description, extract_length=80) }} + {% endif %} +

    + {% endblock %} + The resource: '{{ h.resource_display_name(res) | truncate(30) }}' is not accessible as guest user. You must login to access it! +
    +{% endif %} +{% endblock %}
  • + diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resources_list.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resources_list.html index 9c6dd63..3ab3b78 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resources_list.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/package/snippets/resources_list.html @@ -1,37 +1,77 @@ -{# +{# Renders a list of resources with icons and view links. - - resources - A list of resources (dicts) to render - pkg - A package dict that the resources belong to. - + + resources - A list of resources to render + pkg - A package object that the resources belong to. + Example: - - {% snippet "package/snippets/resources_list.html", pkg=pkg, resources=pkg.resources %} - + + {% snippet "package/snippets/resources_list.html", pkg=pkg, resources=pkg.resources %} + #} - -
    -

    {{ _('Data and Resources') }}

    - {% block resource_list %} - {% if resources %} -
      - {% block resource_list_inner %} - {% set can_edit = can_edit or h.check_access('package_update', {'id':pkg.id }) %} - {% for resource in resources %} - {% snippet 'package/snippets/resource_item.html', pkg=pkg, res=resource, can_edit=can_edit %} - {% endfor %} - {% endblock %} -
    - {% else %} - {% block resource_list_empty %} - {% if h.check_access('resource_create', {'package_id': pkg['id']}) %} - {% trans url=h.url_for(pkg.type ~ '_resource.new', id=pkg.name) %} -

    This dataset has no data, why not add some?

    - {% endtrans %} - {% else %} -

    {{ _('This dataset has no data') }}

    - {% endif %} - {% endblock %} + + + +
    +
    {{ _('Data and Resources') }}
    + {% set user = c.user %} + {# Added by Francesco Mangiacrapa #10389 #} + {% if not user %} +
    To access the resources you must log in
    {% endif %} - {% endblock %} -
    + {# end #} + {% block resource_list %} + {% if resources %} +
      + {% block resource_list_inner %} + {% for resource in resources %} + {% snippet 'package/snippets/resource_item.html', pkg=pkg, res=resource %} + {% endfor %} + {% endblock %} +
    + {% else %} + {% if h.check_access('resource_create', {'package_id': pkg['id']}) %} + {% trans url=h.url_for(controller='package', action='new_resource', id=pkg.name) %} +

    This dataset has no data, why not add some?

    + {% endtrans %} + {% else %} +

    {{ _('This dataset has no data') }}

    + {% endif %} + {% endif %} + {% endblock %} +
    + \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/package_item.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/package_item.html index 801d9c8..0e16bed 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/package_item.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/package_item.html @@ -1,70 +1,139 @@ {# -Displays a single of dataset. - -package - A package to display. -item_class - The class name to use on the list item. -hide_resources - If true hides the resources (default: false). - -Example: - - {% snippet 'snippets/package_item.html', package=c.datasets[0] %} - -#} -{% set title = package.title or package.name %} -{% set notes = h.markdown_extract(package.notes, extract_length=180) %} - -{% block package_item %} + Displays a single of dataset. + + package - A package to display. + item_class - The class name to use on the list item. + hide_resources - If true hides the resources (default: false). + banner - If true displays a popular banner (default: false). + truncate - The length to trucate the description to (default: 180) + truncate_title - The length to truncate the title to (default: 80). + + Example: + + {% snippet 'snippets/package_item.html', package=c.datasets[0] %} + + #} + + {% set truncate = truncate or 180 %} + {% set truncate_title = truncate_title or 80 %} + {% set title = package.title or package.name %} + {% set notes = h.markdown_extract(package.notes, extract_length=truncate) %} + + + {# + {% resource 'd4science_theme/custom.css' %} + CHANGED BY FRANCESCO.MANGIACRAPA + #} + + {% block package_item_content %} + + {% if package.private and not h.can_read(package) %}
  • - {% block content %}
    - {% block heading %} -

    - {% block heading_private %} - {% if package.private %} - - - {{ _('Private') }} - - {% endif %} - {% endblock %} - {% block heading_title %} - - {{title|truncate(80)}} - - {% endblock %} - {% block heading_meta %} - {% if package.get('state', '').startswith('draft') %} - {{ _('Draft') }} - {% elif package.get('state', '').startswith('deleted') %} - {{ _('Deleted') }} - {% endif %} - {{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }} - {% endblock %} -

    - {% endblock %} - {% block notes %} - {% if notes %} -
    {{ notes|urlize }}
    - {% else %} -

    {{ h.humanize_entity_type('package', package.type, 'no description') or _("There is no description for this dataset") }}

    +

    + {% if package.private and not h.can_read(package) %} + + + {{ _('Private') }} + {% endif %} - {% endblock %} -

    - {% block resources %} - {% if package.resources and not hide_resources %} - {% block resources_outer %} -
      - {% block resources_inner %} - {% for resource in h.dict_list_reduce(package.resources, 'format') %} -
    • - {{ resource }} -
    • - {% endfor %} - {% endblock %} -
    - {% endblock %} + + + + {% if package.private and not h.can_read(package) %} + {# {{ _(h.truncate(title, truncate_title)) }} #} + Dataset +
    + {{ h.acquire_button(package) }} + {% else %} + {# {{ h.link_to(h.truncate(title, truncate_title), h.url_for(controller='package', action='read', id=package.name)) }} #} + {{ h.link_to(h.truncate(title, truncate_title), url_for('dataset.read', id=package.name)) }} + {% endif %} + + + {% if package.get('state', '').startswith('draft') %} + {{ _('Draft') }} + {% elif package.get('state', '').startswith('deleted') %} + {{ _('Deleted') }} + {% endif %} + {{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }} + + {% if banner %} + {% endif %} - {% endblock %} - {% endblock %} + {% if notes %} + + {% endif %} +
  • -{% endblock %} + {% else %} +
  • +
    +

    + {% if package.private and not h.can_read(package) %} + + + {{ _('Private') }} + + {% endif %} + + + + {% if package.private and not h.can_read(package) %} + {{ _(h.truncate(title, truncate_title)) }} +
    + {{ h.acquire_button(package) }} + {% else %} + {# {{ h.link_to(h.truncate(title, truncate_title), h.url_for(controller='package', action='read', id=package.name)) }} #} + {{ h.link_to(h.truncate(title, truncate_title), url_for('dataset.read', id=package.name)) }} + {% endif %} + + + {% if package.get('state', '').startswith('draft') %} + {{ _('Draft') }} + {% elif package.get('state', '').startswith('deleted') %} + {{ _('Deleted') }} + {% endif %} + {{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }} +

    + {% if banner %} + + {% endif %} + {% if notes %} +
    {{ notes|urlize }}
    + {% endif %} +
    + {% if package.resources and not hide_resources %} + + {% endif %} + +
  • + {% endif %} + {% endblock %} \ No newline at end of file diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/tag_list.html b/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/tag_list.html index 77f753e..52c9c8d 100644 --- a/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/tag_list.html +++ b/ckanext-d4science_theme/ckanext/d4science_theme/templates/snippets/tag_list.html @@ -13,7 +13,7 @@ diff --git a/ckanext-d4science_theme/ckanext/d4science_theme/validators.py b/ckanext-d4science_theme/ckanext/d4science_theme/validators.py new file mode 100644 index 0000000..0168c54 --- /dev/null +++ b/ckanext-d4science_theme/ckanext/d4science_theme/validators.py @@ -0,0 +1,24 @@ +import ckan.plugins.toolkit as toolkit +from logging import getLogger + +log = getLogger(__name__) + +def ignore_duplicate_keys(key, data, errors, context): + """ + Validator che consente chiavi duplicate negli extras. + """ + # Estrarre gli extras dalla richiesta + log.debug("controllo extra (data) %s", data) + extras = data.get(key, []) + + # Log per il debugging + log.debug(f"Contenuto di 'data' per la chiave '{key}': {extras} (tipo: {type(extras)})") + + # Se ci sono duplicati, evita di sollevare un'eccezione + if isinstance(extras, list): # Verifica che extras sia una lista + log.debug(f"Ignorando chiave duplicata: {extra['key']}") + else: + log.debug(f"Errore: 'extras' non è una lista, ma è di tipo {type(extras)}") + + ## Mantieni la lista aggiornata, che consente chiavi duplicate + #data[key] = extras \ No newline at end of file diff --git a/ckanext-d4science_theme/requirements.txt b/ckanext-d4science_theme/requirements.txt index 55f2a9b..0e23e19 100644 --- a/ckanext-d4science_theme/requirements.txt +++ b/ckanext-d4science_theme/requirements.txt @@ -1,3 +1,4 @@ webhelpers2 xmltodict pyqrcode +sqlalchemy diff --git a/ckanext-d4science_theme/setup.py b/ckanext-d4science_theme/setup.py index d3a3ba1..a616430 100644 --- a/ckanext-d4science_theme/setup.py +++ b/ckanext-d4science_theme/setup.py @@ -69,8 +69,7 @@ setup( # installed, specify them here. If using Python 2.6 or less, then these # have to be included in MANIFEST.in as well. include_package_data=True, - package_data={ - }, + #package_data={}, # Although 'package_data' is the preferred approach, in some case you may # need to place data files outside of your packages.