Merge pull request 'Add duplicate keys management for extras' (#1) from feature/implement_validators into main

Reviewed-on: #1
Reviewed-by: Marco Carollo <m.carollo498f0@code-repo-noreply@d4science.org>
This commit is contained in:
Alessio Fabrizio 2024-11-29 16:09:41 +01:00
commit 22cdaf4c18
17 changed files with 804 additions and 301 deletions

View File

@ -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']

View File

@ -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']

View File

@ -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 <http://www.gnu.org/licenses/>.
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,)

View File

@ -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 ''

View File

@ -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
'''Returns a list of packages containing the value passed in input'''
return [x for x in list_package if x.value == value]
return lst
#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,32 +200,65 @@ 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):
@ -250,6 +266,51 @@ class D4Science_ThemePlugin(plugins.SingletonPlugin, toolkit.DefaultDatasetForm)
# 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

View File

@ -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 %}
<span class="label pull-right" style='margin-left:5px; padding:3px;'>
{{ moderation_item_status }}
</span>
{% endif %}
{% block package_description %}
{% if pkg.private %}
<span class="dataset-private badge badge-inverse pull-right">
<i class="fa fa-lock"></i>
<span class="dataset-private label label-inverse pull-right">
<i class="icon-lock"></i>
{{ _('Private') }}
</span>
{% endif %}
<h1>
<div class="principaltitle">
{% block page_heading %}
{{ h.dataset_display_name(pkg) }}
{{ pkg.title or pkg.name }}
{% if pkg.state.startswith('draft') %}
[{{ _('Draft') }}]
{% endif %}
@ -23,11 +26,11 @@
[{{ _('Deleted') }}]
{% endif %}
{% endblock %}
</h1>
</div>
{% block package_notes %}
{% if pkg.notes %}
<div class="notes embedded-content">
{{ h.render_markdown(h.get_translated(pkg, 'notes')) }}
<div class="notes embedded-content" style="white-space:pre-line;">
{{ h.render_markdown(pkg.notes) }}
</div>
{% endif %}
{% endblock %}
@ -35,14 +38,20 @@
<span class="insert-comment-thread"></span>
{% endblock %}
{% block package_resources %}
{% snippet "package/snippets/resources_list.html", pkg=pkg, resources=pkg.resources %}
{% endblock %}
{% if dataset_extent %}
{% snippet "spatial/snippets/dataset_map.html", extent=dataset_extent %}
{% endif %}
{% block package_tags %}
<div class="sectiontitle">{{_('Tags')}}</div>
{% 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_additional_info %}
{% snippet "package/snippets/additional_info.html", pkg_dict=pkg %}
{% endblock %}

View File

@ -1,5 +1,74 @@
{% set key_item_url = _('Item') + ' URL' %}
<script type="text/javascript" src="/fanstatic/d4science_theme/d4science_scripts.js"></script>
<script>
//ADDED by Francesco Mangiacrapa
jsonToHTML = function(containerID, cssClassToTable) {
CKAN_D4S_JSON_Util.jsonToHTML(containerID, cssClassToTable);
}
</script>
<section class="additional-info">
<h3>{{ _('Additional Info') }}</h3>
{% 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 %}
<div><div class="sectiontitle">{{ key_item_url }}</div>
<table class="qr-code-table">
<tr>
<td><a href={{ extra_item_url }} target="_blank">{{ extra_item_url }}</a></td>
<td>{% snippet "package/snippets/qrcode_show.html", package_url=extra_item_url %}</td>
</tr>
</table></div>
{% 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') %}
<div><div class="sectiontitle">{{category_idx.category.title}}</div>
{% if category_idx.category.description %}
<div class="notes embedded-content"><p>Description: {{category_idx.category.description}}</p></div>
{% endif %}
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th scope="col">{{ _('Field') }}</th>
<th scope="col">{{ _('Value') }}</th>
</tr>
</thead>
<tbody>
{% 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 %}
</tbody>
</table>
</div>
{% endif %}
{% endfor %}
{% set my_extras = h.d4science_get_extra_for_category(extras_indexed_for_categories, 'nocategory') %}
{% if my_extras|length > 0 %}
<div><div class="sectiontitle">{{ _('Additional Info') }}</div>
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th scope="col">{{ _('Field') }}</th>
<th scope="col">{{ _('Value') }}</th>
</tr>
</thead>
<tbody>
{% snippet "package/snippets/extras_table.html", my_extras=my_extras, key_item_url=key_item_url %}
</tbody>
</table>
</div>
{% endif %}
{% endif %}
{% endblock %}
<div class="sectiontitle">{{ _('Management Info') }}</div>
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
@ -13,11 +82,7 @@
<tr>
<th scope="row" class="dataset-label">{{ _('Source') }}</th>
{% if h.is_url(pkg_dict.url) %}
<td class="dataset-details" property="foaf:homepage">
<a href="{{ pkg_dict.url }}" rel="foaf:homepage" target="_blank">
{{ pkg_dict.url }}
</a>
</td>
<td class="dataset-details" property="foaf:homepage">{{ h.link_to(pkg_dict.url, pkg_dict.url, rel='foaf:homepage', target='_blank') }}</td>
{% else %}
<td class="dataset-details" property="foaf:homepage">{{ pkg_dict.url }}</td>
{% endif %}
@ -35,16 +100,17 @@
<td class="dataset-details" property="dc:creator">{{ pkg_dict.author }}</td>
</tr>
{% endif %}
{# Added by Francesco Mangiacrapa #}
{% set user_maintainer = h.d4science_get_user_info(pkg_dict.maintainer) %}
{% if pkg_dict.maintainer_email %}
<tr>
<th scope="row" class="dataset-label">{{ _('Maintainer') }}</th>
<td class="dataset-details" property="dc:contributor">{{ h.mail_to(email_address=pkg_dict.maintainer_email, name=pkg_dict.maintainer) }}</td>
<td class="dataset-details" property="dc:contributor">{{ h.mail_to(email_address=pkg_dict.maintainer_email, name=user_maintainer.fullname if (user_maintainer and user_maintainer.fullname) else pkg_dict.maintainer) }}</td>
</tr>
{% elif pkg_dict.maintainer %}
<tr>
<th scope="row" class="dataset-label">{{ _('Maintainer') }}</th>
<td class="dataset-details" property="dc:contributor">{{ pkg_dict.maintainer }}</td>
<td class="dataset-details" property="dc:contributor">{{ user_maintainer.fullname if (user_maintainer and user_maintainer.fullname) else pkg_dict.maintainer }}</td>
</tr>
{% endif %}
@ -78,18 +144,7 @@
</td>
</tr>
{% endif %}
{% block extras scoped %}
{% for extra in h.sorted_extras(pkg_dict.extras) %}
{% set key, value = extra %}
<tr rel="dc:relation" resource="_:extra{{ i }}">
<th scope="row" class="dataset-label" property="rdfs:label">{{ _(key|e) }}</th>
<td class="dataset-details" property="rdf:value">{{ value }}</td>
</tr>
{% endfor %}
{% endblock %}
{% endblock %}
</tbody>
</tbody>
</table>
{% endblock %}
</section>

View File

@ -0,0 +1,59 @@
<!-- Applying "Show more"/"Show less" to 'spatial field', see #23298 -->
<script type="text/javascript">
function manageLongField(containerID, n) {
try {
var str = document.getElementById(containerID).innerHTML;
if (str && str.length <= n) {
return str;
}
var newContent = "<div><input type='checkbox' class='read-more-state' id='post-"+containerID+"'' /><label for='post-"+containerID+"'' class='read-more-trigger'></label><p class='read-more-wrap'>";
newContent += str.substring(0, n);
newContent += "<span class='read-more-target'>";
newContent += str.substring(n, str.length);
newContent += "</span></p></div>"
document.getElementById(containerID).innerHTML = newContent
} catch (e) {
console.log('invalid containerID ' + containerID, e);
}
}
</script>
<!-- reading placeholder used by D4Science Content Moderator System, see #22854 -->
{% 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] %}
<!-- showing only extra fields with no empty values -->
{% if extra_value is defined and extra_value|length %}
{# Added by Francesco Mangiacrapa, see: #7055 #}
{% set isHttp = extra_value.startswith(('http://', 'https://')) %}
<tr rel="dc:relation" resource="_:extra{{ i }}">
<!-- no showing ITEM URL and D4Science CMS fields into table -->
{% if k != key_item_url and (not k.startswith(d4science_cms_obj_placeholders.prefix)) %}
<th scope="row" class="dataset-label" property="rdfs:label">{{ _(k) }}</th>
{% if isHttp %}
{% if k == 'graphic-preview-file'%}
<td class="dataset-details" property="rdf:value"><img src={{ extra_value }} alt={{ extra_value }}></td>
{% else %}
<td class="dataset-details" property="rdf:value"><a href={{ extra_value }} target="_blank">{{ extra_value }}</a></td>
{% endif %}
{% elif k == 'responsible-party' %}
<td class="dataset-details" property="rdf:value"><div id="responsible-party-extra-{{index}}" class="d4s-json-table">{{ extra_value }}</div><script>jsonToHTML('responsible-party-extra-{{index}}')</script></td>
{% elif k == 'dataset-reference-date' %}
<td class="dataset-details" property="rdf:value"><div id="dataset-reference-date-extra-{{index}}" class="d4s-json-table">{{ extra_value }}</div><script>jsonToHTML('dataset-reference-date-extra-{{index}}')</script></td>
{% elif k == 'coupled-resource' %}
<td class="dataset-details" property="rdf:value"><div id="coupled-resource-date-extra-{{index}}" class="d4s-json-table">{{ extra_value }}</div><script>jsonToHTML('coupled-resource-date-extra-{{index}}')</script></td>
{% elif k.startswith('Zenodo') %}
<td class="dataset-details" property="rdf:value"><div id="related-identifier-extra-{{index}}" class="d4s-json-table">{{ extra_value }}</div><script>jsonToHTML('related-identifier-extra-{{index}}')</script></td>
{% elif k == 'spatial' %}
<td class="dataset-details" property="rdf:value"><div id='spatial-{{index}}'>{{ extra_value }}</div><script>manageLongField('spatial-{{index}}', 150)</script></td>
{% else %}
<td class="dataset-details" property="rdf:value">{{ extra_value }}</td>
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}

View File

@ -1,41 +1,48 @@
{#
Displays a sidebar module with information for given package
Displays a sidebard module with information for given package
pkg - The package dict that owns the resources.
pkg - The package dict that owns the resources.
Example:
Example:
{% snippet "package/snippets/info.html", pkg=pkg %}
{% snippet "package/snippets/info.html", pkg=pkg %}
#}
{% block package_info %}
{% if pkg %}
<section class="module module-narrow">
<div class="module context-info">
<div class="module-content">
{% block package_info_inner %}
{% block heading %}
<h1 class="heading">{{ h.dataset_display_name(pkg) }}</h1>
{% endblock %}
{% block nums %}
{% set num_followers = h.follow_count('dataset', pkg.id) %}
<div class="nums">
<dl>
<dt>{{ _('Followers') }}</dt>
<dd data-module="followers-counter" data-module-id="{{ pkg.id }}" data-module-num_followers="{{ num_followers }}">{{ h.SI_number_span(num_followers) }}</dd>
</dl>
</div>
{% endblock %}
{% block follow_button %}
{% if not hide_follow_button %}
<div class="follow_button">
{{ h.follow_button('dataset', pkg.id) }}
#}
{% block package_info %}
{% if pkg %}
<section class="module module-narrow">
<div class="module context-info">
<div class="module-content">
{% block package_info_inner %}
{% block heading %}
<div class="infotitle">{{ h.dataset_display_name(pkg) }}</div>
{% endblock %}
{% if pkg.extras %}
{% for extra in pkg.extras if extra['key'] == 'graphic-preview-file' %}
<div class="graphic-preview-style">
<div><a href={{ extra['value'] }} target="_blank"><img src={{ extra['value'] }} alt='graphic-preview-file'>Show Graphic</a></div>
</div>
{% endfor %}
{% endif %}
{% block nums %}
<div class="nums">
<dl>
<dt>{{ _('Followers') }}</dt>
<dd>{{ h.SI_number_span(h.follow_count('dataset', pkg.id)) }}</dd>
</dl>
</div>
{% endif %}
{% endblock %}
{% block follow_button %}
{% if not hide_follow_button %}
<div class="follow_button">
{{ h.follow_button('dataset', pkg.name) }}
</div>
{% endif %}
{% endblock %}
{% endblock %}
{% endblock %}
</div>
</div>
</div>
</section>
{% endif %}
{% endblock %}
</section>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,6 @@
{% set qr_code_image = h.d4science_get_qrcode_for_url(package_url) %}
{% block package_qrcode_for_url %}
<div style="padding: 1px;"><img src='data:image/svg+xml;base64, {{ qr_code_image }}' alt={{ package_url }}> </div>
{% endblock %}

View File

@ -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) %}
<li class="resource-item" data-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 %}
<a class="heading" href="{{ url }}" title="{{ res.name or res.description }}">
{{ h.resource_display_name(res) | truncate(50) }}<span class="format-label" property="dc:format" data-format="{{ res.format.lower() or 'data' }}">{{ h.get_translated(res, 'format') }}</span>
{{ h.popular('views', res.tracking_summary.total, min=10) if res.tracking_summary }}
{{ h.resource_display_name(res) | truncate(50) }}<span class="format-label" property="dc:format" data-format="{{ res.format.lower() or 'data' }}">{{ res.format }}</span>
{{ h.popular('views', res.tracking_summary.total, min=10) }}
</a>
{% endblock %}
{% block resource_item_description %}
<p class="description">
{% if res.description %}
{{ h.markdown_extract(h.get_translated(res, 'description'), extract_length=80) }}
{{ h.markdown_extract(res.description, extract_length=80) }}
{% endif %}
</p>
{% endblock %}
{% block resource_item_explore %}
{% if not url_is_edit %}
{# Only if can edit, explorer button is shown with several facility #}
{% if can_edit %}
<div class="dropdown btn-group">
<a href="#" class="btn btn-primary dropdown-toggle" type="button" id="dropdownExplorer" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-share"></i>
<a href="#" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
<i class="icon-share-alt"></i>
{{ _('Explore') }}
<span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="dropdownExplorer">
<ul class="dropdown-menu">
{% block resource_item_explore_links %}
{% block explore_view %}
<li>
<a class="dropdown-item" href="{{ url }}">
{% if res.has_views %}
<i class="fa fa-chart-bar"></i>
{{ _('Preview') }}
{% else %}
<i class="fa fa-info-circle"></i>
{{ _('More information') }}
{% endif %}
</a>
</li>
{% endblock explore_view %}
{% if res.url and h.is_url(res.url) %}
<li>
<a class="dropdown-item resource-url-analytics" href="{{ res.url }}" target="_blank" rel="noreferrer">
{% if res.has_views or res.url_type == 'upload' %}
<i class="fa fa-arrow-circle-down"></i>
{{ _('Download') }}
<a href="{{ url }}">
{% if res.has_views %}
<i class="icon-bar-chart"></i>
{{ _('Preview') }}
{% else %}
<i class="fa fa-external-link"></i>
{{ _('Go to resource') }}
<i class="icon-info-sign"></i>
{{ _('More information') }}
{% endif %}
</a>
</li>
{% if res.url and h.is_url(res.url) %}
{# Added by Francesco Mangiacrapa #}
<script type="text/javascript" >
//handles the click event
function handleClick(event, element, url) {
window.open(url, "_blank", null);
return false;
}
</script>
{# Comment by Francesco Mangiacrapa, in order to avoid "Download" and "Go to Resource" facilities through the Explore button #}
<!--<li>
<a class="resource-url-analytics" href="#" onclick="return handleClick(event, this, '{{ res.url }}');">
{% if res.has_views %}
<i class="icon-download"></i>
{{ _('Download') }}
{% else %}
<i class="icon-external-link"></i>
{{ _('Go to resource') }}
{% endif %}
</a>
</li>-->
{% endif %}
{% if can_edit %}
{#{% if can_edit %}#}
<li>
<a class="dropdown-item" href="{{ h.url_for(pkg.type ~ '_resource.edit', id=pkg.name, resource_id=res.id) }}">
<i class="fa fa-pencil-square"></i>
{# <a href="{{ h.url_for(controller='package', action='resource_edit', id=pkg.name, resource_id=res.id) }}"> #}
<a href="{{ h.url_for('dataset_resource.edit', id=pkg.name, resource_id=res.id) }}">
<i class="icon-edit"></i>
{{ _('Edit') }}
</a>
</li>
{% endif %}
{#{% endif %}#}
{% endblock %}
</ul>
</div>
{% else %}
<a href="{{ url }}" title="Go to {{ url }}">
{{ _('Go to resource') }}
</a>
{% endif %}
{% endif %}
{% endblock %}
{% else %}
{% block resource_item_title2 %}
{# Updated by Francesco Mangiacrapa, see: #10056 #}
<a class="heading" href="javascript:CKAN_D4S_Functions_Util.showPopupD4S(null, 'myPopup_{{res.id}}', '250px');">
{{ h.resource_display_name(res) | truncate(50) }}<span class="format-label" property="dc:format" data-format="{{ res.format.lower() or 'data' }}">{{ res.format }}</span>
{{ h.popular('views', res.tracking_summary.total, min=10) }}
</a>
{% endblock %}
<div class="popupD4S" onclick="CKAN_D4S_Functions_Util.showPopupD4S(event, 'myPopup_{{res.id}}', '250px')">
{% block resource_item_description2 %}
<p class="description">
{% if res.description %}
{{ h.markdown_extract(res.description, extract_length=80) }}
{% endif %}
</p>
{% endblock %}
<span class="popuptext" id="myPopup_{{res.id}}">The resource: '{{ h.resource_display_name(res) | truncate(30) }}' is not accessible as guest user. You must login to access it!</span>
</div>
{% endif %}
{% endblock %}
</li>

View File

@ -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 %}
#}
<section id="dataset-resources" class="resources">
<h2>{{ _('Data and Resources') }}</h2>
{% block resource_list %}
{% if resources %}
<ul class="{% block resource_list_class %}resource-list{% endblock %}">
{% 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 %}
</ul>
{% 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) %}
<p class="empty">This dataset has no data, <a href="{{ url }}">why not add some?</a></p>
{% endtrans %}
{% else %}
<p class="empty">{{ _('This dataset has no data') }}</p>
{% endif %}
{% endblock %}
<script type="text/javascript" >
//Task #10389
window.addEventListener("message",
function (e) {
var curr_loc = window.location.toString()
var orgin = e.origin.toString()
if(curr_loc.startsWith(orgin)){
//alert("ignoring message from myself");
return;
}
//console.log("origin: "+e.data)
if(e.data == null)
return;
var pMess = JSON.parse(e.data)
//console.log(pMess.explore_vres_landing_page)
window.linktogateway = pMess.explore_vres_landing_page;
goToHomeLink("resource-private")
},false);
//Task #10389
goToHomeLink = function (divId) {
var myDiv = document.getElementById(divId);
var myHost = window.linktogateway.substring(0, window.linktogateway.lastIndexOf("/"));
console.log("my host: "+myHost)
if(myDiv && myHost){
myDiv.innerHTML= myDiv.innerHTML + ". <a target=\"_blank\" href="+myHost+"/home"+">Go to Login...</a>";
}
}
</script>
<section id="dataset-resources" class="resources">
<div class="sectiontitle">{{ _('Data and Resources') }}</div>
{% set user = c.user %}
{# Added by Francesco Mangiacrapa #10389 #}
{% if not user %}
<div id="resource-private" class="required-access">To access the resources you must log in</div>
{% endif %}
{% endblock %}
</section>
{# end #}
{% block resource_list %}
{% if resources %}
<ul class="{% block resource_list_class %}resource-list{% endblock %}">
{% block resource_list_inner %}
{% for resource in resources %}
{% snippet 'package/snippets/resource_item.html', pkg=pkg, res=resource %}
{% endfor %}
{% endblock %}
</ul>
{% else %}
{% if h.check_access('resource_create', {'package_id': pkg['id']}) %}
{% trans url=h.url_for(controller='package', action='new_resource', id=pkg.name) %}
<p class="empty">This dataset has no data, <a href="{{ url }}">why not add some?</a></p>
{% endtrans %}
{% else %}
<p class="empty">{{ _('This dataset has no data') }}</p>
{% endif %}
{% endif %}
{% endblock %}
</section>

View File

@ -1,70 +1,139 @@
{#
Displays a single of dataset.
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).
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:
Example:
{% snippet 'snippets/package_item.html', package=c.datasets[0] %}
{% 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 %}
{% 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) %}
<!-- TODO: remove this comments after implementing privateDatasets
{% set acquired = h.is_dataset_acquired(package) %}
{% set owner = h.is_owner(package) %} -->
{#
{% resource 'd4science_theme/custom.css' %}
CHANGED BY FRANCESCO.MANGIACRAPA
#}
{% block package_item_content %}
{% if package.private and not h.can_read(package) %}
<li class="{{ item_class or "dataset-item" }}">
{% block content %}
<div class="dataset-content">
{% block heading %}
<h2 class="dataset-heading">
{% block heading_private %}
{% if package.private %}
<span class="dataset-private badge bg-secondary">
<i class="fa fa-lock"></i>
{{ _('Private') }}
</span>
{% endif %}
{% endblock %}
{% block heading_title %}
<a href="{{ h.url_for('%s.read' % package.type, id=package.name) }}" title="{{ title }}">
{{title|truncate(80)}}
</a>
{% endblock %}
{% block heading_meta %}
{% if package.get('state', '').startswith('draft') %}
<span class="badge bg-info">{{ _('Draft') }}</span>
{% elif package.get('state', '').startswith('deleted') %}
<span class="badge bg-danger">{{ _('Deleted') }}</span>
{% endif %}
{{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }}
{% endblock %}
</h2>
{% endblock %}
{% block notes %}
{% if notes %}
<div>{{ notes|urlize }}</div>
{% else %}
<p class="empty">{{ h.humanize_entity_type('package', package.type, 'no description') or _("There is no description for this dataset") }}</p>
<h3 class="dataset-heading">
{% if package.private and not h.can_read(package) %}
<span class="dataset-private label label-inverse">
<i class="icon-lock"></i>
{{ _('Private') }}
</span>
{% endif %}
{% endblock %}
</div>
{% block resources %}
{% if package.resources and not hide_resources %}
{% block resources_outer %}
<ul class="dataset-resources list-unstyled">
{% block resources_inner %}
{% for resource in h.dict_list_reduce(package.resources, 'format') %}
<li>
<a href="{{ h.url_for(package.type ~ '.read', id=package.name) }}" class="badge badge-default" data-format="{{ resource.lower() }}">{{ resource }}</a>
</li>
{% endfor %}
{% endblock %}
</ul>
{% endblock %}
<!-- TODO: remove this comments after implementing privateDatasets
{% if acquired and not owner %}
<span class="dataset-private label label-acquired">
<i class="icon-shopping-cart"></i>
{{ _('Acquired') }}
</span>
{% endif %} -->
<!-- Customizations Acquire Button -->
{% if package.private and not h.can_read(package) %}
{# {{ _(h.truncate(title, truncate_title)) }} #} <!-- THIS IS PACKAGE TITLE -->
Dataset
<div class="divider"/>
{{ 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 %}
<!-- End of customizations Acquire Button -->
{% if package.get('state', '').startswith('draft') %}
<span class="label label-info">{{ _('Draft') }}</span>
{% elif package.get('state', '').startswith('deleted') %}
<span class="label label-important">{{ _('Deleted') }}</span>
{% endif %}
{{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }}
</h3>
{% if banner %}
<span class="banner">{{ _('Popular') }}</span>
{% endif %}
{% endblock %}
{% endblock %}
{% if notes %}
<!-- <div>{{ notes|urlize }}</div>--><!-- THIS IS PACKAGE DESCRIPTION -->
{% endif %}
</div>
</li>
{% endblock %}
{% else %}
<li class="{{ item_class or "dataset-item" }}">
<div class="dataset-content">
<h3 class="dataset-heading">
{% if package.private and not h.can_read(package) %}
<span class="dataset-private label label-inverse">
<i class="icon-lock"></i>
{{ _('Private') }}
</span>
{% endif %}
<!-- TODO: remove this comments after implementing privateDatasets
{% if acquired and not owner %}
<span class="dataset-private label label-acquired">
<i class="icon-shopping-cart"></i>
{{ _('Acquired') }}
</span>
{% endif %}
{% if owner %}
<span class="dataset-private label label-owner">
<i class="icon-user"></i>
{{ _('Owner') }}
</span>
{% endif %} -->
<!-- Customizations Acquire Button -->
{% if package.private and not h.can_read(package) %}
{{ _(h.truncate(title, truncate_title)) }}
<div class="divider"/>
{{ 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 %}
<!-- End of customizations Acquire Button -->
{% if package.get('state', '').startswith('draft') %}
<span class="label label-info">{{ _('Draft') }}</span>
{% elif package.get('state', '').startswith('deleted') %}
<span class="label label-important">{{ _('Deleted') }}</span>
{% endif %}
{{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }}
</h3>
{% if banner %}
<span class="banner">{{ _('Popular') }}</span>
{% endif %}
{% if notes %}
<div>{{ notes|urlize }}</div>
{% endif %}
</div>
{% if package.resources and not hide_resources %}
<ul class="dataset-resources unstyled">
{% for resource in h.dict_list_reduce(package.resources, 'format') %}
<li>
{# <a href="{{ h.url_for(controller='package', action='read', id=package.name) }}" class="label" data-format="{{ resource.lower() }}">{{ resource }}</a> #}
<a href="{{ url_for('dataset.read', id=package.name) }}" class="label" data-format="{{ resource.lower() }}">{{ resource }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endblock %}

View File

@ -13,7 +13,7 @@
<ul class="{{ _class }}">
{% for tag in tags %}
<li>
<a class="{% block tag_list_item_class %}tag{% endblock %}" href="{% url_for 'dataset.search', tags=tag.name %}" title="{{ tag.display_name }}">{{ tag.display_name|truncate(22) }}</a>
<a class="{% block tag_list_item_class %}tag{% endblock %}" href="{% url_for 'dataset.search', tags=tag.name %}" title="{{ tag.display_name }}"></a>
</li>
{% endfor %}
</ul>

View File

@ -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

View File

@ -1,3 +1,4 @@
webhelpers2
xmltodict
pyqrcode
sqlalchemy

View File

@ -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.