-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add rudimentary UIs for metadata records
- Loading branch information
1 parent
2717c6a
commit 1654b56
Showing
22 changed files
with
601 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
# encoding: utf-8 | ||
|
||
import ckan.plugins.toolkit as tk | ||
import ckan.model as model | ||
import ckan.lib.helpers as helpers | ||
import ckan.authz as authz | ||
from ckan.logic import clean_dict, tuplize_dict, parse_params | ||
import ckan.lib.navl.dictization_functions as dict_fns | ||
|
||
|
||
class MetadataRecordController(tk.BaseController): | ||
|
||
# Note: URLs must be constructed using metadata_record.id rather than metadata_record.name, | ||
# because name can be mapped from a metadata JSON element which we cannot rely on to be | ||
# URL safe; e.g. if name gets the DOI it will contain a '/'. | ||
|
||
@staticmethod | ||
def _set_containers_on_context(organization_id, metadata_collection_id): | ||
context = {'model': model, 'session': model.Session, 'user': tk.c.user} | ||
|
||
if organization_id and not tk.c.organization: | ||
data_dict = {'id': organization_id} | ||
try: | ||
tk.c.organization = tk.get_action('organization_show')(context, data_dict) | ||
except tk.ObjectNotFound: | ||
tk.abort(404, tk._('Organization not found')) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('Not authorized to see this page')) | ||
|
||
if metadata_collection_id and not tk.c.metadata_collection: | ||
data_dict = {'id': metadata_collection_id} | ||
try: | ||
tk.c.metadata_collection = tk.get_action('metadata_collection_show')(context, data_dict) | ||
except tk.ObjectNotFound: | ||
tk.abort(404, tk._('Metadata collection not found')) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('Not authorized to see this page')) | ||
|
||
if not organization_id: | ||
tk.abort(400, tk._('Organization not specified')) | ||
org = tk.c.organization | ||
if tk.c.metadata_collection['organization_id'] not in (org['id'], org['name']): | ||
tk.abort(400, tk._('Metadata collection does not belong to the specified organization')) | ||
|
||
def index(self, organization_id=None, metadata_collection_id=None): | ||
self._set_containers_on_context(organization_id, metadata_collection_id) | ||
|
||
page = tk.h.get_page_number(tk.request.params) or 1 | ||
items_per_page = 21 | ||
|
||
context = {'model': model, 'session': model.Session, | ||
'user': tk.c.user, 'for_view': True} | ||
|
||
q = tk.c.q = tk.request.params.get('q', '') | ||
sort_by = tk.c.sort_by_selected = tk.request.params.get('sort') | ||
try: | ||
tk.check_access('site_read', context) | ||
tk.check_access('metadata_record_list', context) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('Not authorized to see this page')) | ||
|
||
if tk.c.userobj: | ||
context['user_id'] = tk.c.userobj.id | ||
context['user_is_admin'] = tk.c.userobj.sysadmin | ||
|
||
try: | ||
data_dict_global_results = { | ||
'owner_org': organization_id, | ||
'metadata_collection_id': metadata_collection_id, | ||
'all_fields': False, | ||
'q': q, | ||
'sort': sort_by, | ||
'type': 'metadata_record', | ||
} | ||
global_results = tk.get_action('metadata_record_list')(context, data_dict_global_results) | ||
except tk.ValidationError as e: | ||
if e.error_dict and e.error_dict.get('message'): | ||
msg = e.error_dict['message'] | ||
else: | ||
msg = str(e) | ||
tk.h.flash_error(msg) | ||
tk.c.page = helpers.Page([], 0) | ||
return tk.render('metadata_record/index.html') | ||
|
||
data_dict_page_results = { | ||
'owner_org': organization_id, | ||
'metadata_collection_id': metadata_collection_id, | ||
'all_fields': True, | ||
'q': q, | ||
'sort': sort_by, | ||
'limit': items_per_page, | ||
'offset': items_per_page * (page - 1), | ||
} | ||
page_results = tk.get_action('metadata_record_list')(context, data_dict_page_results) | ||
|
||
tk.c.page = helpers.Page( | ||
collection=global_results, | ||
page=page, | ||
url=tk.h.pager_url, | ||
items_per_page=items_per_page, | ||
) | ||
|
||
tk.c.page.items = page_results | ||
return tk.render('metadata_record/index.html') | ||
|
||
def new(self, data=None, errors=None, error_summary=None, organization_id=None, metadata_collection_id=None): | ||
self._set_containers_on_context(organization_id, metadata_collection_id) | ||
|
||
context = {'model': model, 'session': model.Session, 'user': tk.c.user, | ||
'save': 'save' in tk.request.params} | ||
try: | ||
tk.check_access('metadata_record_create', context) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('Unauthorized to create a metadata record')) | ||
|
||
if context['save'] and not data and tk.request.method == 'POST': | ||
return self._save_new(context) | ||
|
||
data = data or {} | ||
errors = errors or {} | ||
error_summary = error_summary or {} | ||
vars = {'data': data, 'errors': errors, 'error_summary': error_summary, 'action': 'new', | ||
'metadata_standard_lookup_list': self._metadata_standard_lookup_list(), | ||
'infrastructure_lookup_list': self._infrastructure_lookup_list()} | ||
|
||
tk.c.is_sysadmin = authz.is_sysadmin(tk.c.user) | ||
tk.c.form = tk.render('metadata_record/edit_form.html', extra_vars=vars) | ||
return tk.render('metadata_record/new.html') | ||
|
||
def edit(self, id, data=None, errors=None, error_summary=None, organization_id=None, metadata_collection_id=None): | ||
self._set_containers_on_context(organization_id, metadata_collection_id) | ||
|
||
context = {'model': model, 'session': model.Session, 'user': tk.c.user, | ||
'save': 'save' in tk.request.params, 'for_edit': True} | ||
data_dict = {'id': id} | ||
|
||
if context['save'] and not data and tk.request.method == 'POST': | ||
return self._save_edit(id, context) | ||
|
||
try: | ||
old_data = tk.get_action('metadata_record_show')(context, data_dict) | ||
data = data or old_data | ||
except (tk.ObjectNotFound, tk.NotAuthorized): | ||
tk.abort(404, tk._('Metadata record not found')) | ||
|
||
tk.c.metadata_record = old_data | ||
try: | ||
tk.check_access('metadata_record_update', context) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('User %r not authorized to edit %s') % (tk.c.user, id)) | ||
|
||
errors = errors or {} | ||
vars = {'data': data, 'errors': errors, 'error_summary': error_summary, 'action': 'edit', | ||
'metadata_standard_lookup_list': self._metadata_standard_lookup_list(), | ||
'infrastructure_lookup_list': self._infrastructure_lookup_list(), | ||
'selected_infrastructure_ids': [i['id'] for i in data['infrastructures']]} | ||
|
||
tk.c.form = tk.render('metadata_record/edit_form.html', extra_vars=vars) | ||
return tk.render('metadata_record/edit.html') | ||
|
||
def delete(self, id, organization_id=None, metadata_collection_id=None): | ||
if 'cancel' in tk.request.params: | ||
tk.h.redirect_to('metadata_record_edit', id=id, organization_id=organization_id, metadata_collection_id=metadata_collection_id) | ||
|
||
context = {'model': model, 'session': model.Session, 'user': tk.c.user} | ||
try: | ||
tk.check_access('metadata_record_delete', context, {'id': id}) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('Unauthorized to delete metadata record')) | ||
|
||
try: | ||
if tk.request.method == 'POST': | ||
tk.get_action('metadata_record_delete')(context, {'id': id}) | ||
tk.h.flash_notice(tk._('Metadata Record has been deleted.')) | ||
tk.h.redirect_to('metadata_record_index', organization_id=organization_id, metadata_collection_id=metadata_collection_id) | ||
tk.c.metadata_record = tk.get_action('metadata_record_show')(context, {'id': id}) | ||
except tk.NotAuthorized: | ||
tk.abort(403, tk._('Unauthorized to delete metadata record')) | ||
except tk.ObjectNotFound: | ||
tk.abort(404, tk._('Metadata_record not found')) | ||
return tk.render('metadata_record/confirm_delete.html') | ||
|
||
def read(self, id, organization_id=None, metadata_collection_id=None): | ||
self._set_containers_on_context(organization_id, metadata_collection_id) | ||
context = {'model': model, 'session': model.Session, 'user': tk.c.user, 'for_view': True} | ||
tk.c.metadata_record = tk.get_action('metadata_record_show')(context, {'id': id}) | ||
return tk.render('metadata_record/read.html') | ||
|
||
def activity(self, id, organization_id=None, metadata_collection_id=None): | ||
self._set_containers_on_context(organization_id, metadata_collection_id) | ||
context = {'model': model, 'session': model.Session, 'user': tk.c.user, 'for_view': True} | ||
tk.c.metadata_record = tk.get_action('metadata_record_show')(context, {'id': id}) | ||
return tk.render('metadata_record/activity_stream.html') | ||
|
||
@staticmethod | ||
def _metadata_standard_lookup_list(): | ||
""" | ||
Return a list of {'value': name, 'text': display_name} dicts for populating the | ||
metadata standard select control. | ||
""" | ||
context = {'model': model, 'session': model.Session, 'user': tk.c.user} | ||
metadata_standards = tk.get_action('metadata_standard_list')(context, {'all_fields': True}) | ||
return [{'value': '', 'text': tk._('(None)')}] + \ | ||
[{'value': metadata_standard['name'], 'text': metadata_standard['display_name']} | ||
for metadata_standard in metadata_standards] | ||
|
||
@staticmethod | ||
def _infrastructure_lookup_list(): | ||
""" | ||
Return a list of {'value': name, 'text': display_name} dicts for populating the | ||
infrastructure select control. | ||
""" | ||
context = {'model': model, 'session': model.Session, 'user': tk.c.user} | ||
infrastructures = tk.get_action('infrastructure_list')(context, {'all_fields': True}) | ||
return [{'value': infrastructure['name'], 'text': infrastructure['display_name']} | ||
for infrastructure in infrastructures] | ||
|
||
def _save_new(self, context): | ||
try: | ||
data_dict = clean_dict(dict_fns.unflatten(tuplize_dict(parse_params(tk.request.params)))) | ||
data_dict['infrastructures'] = self._parse_infrastructure_ids(data_dict.get('infrastructure_ids')) | ||
context['message'] = data_dict.get('log_message', '') | ||
metadata_record = tk.get_action('metadata_record_create')(context, data_dict) | ||
tk.h.redirect_to('metadata_record_read', id=metadata_record['id'], | ||
organization_id=tk.c.organization['name'], | ||
metadata_collection_id=tk.c.metadata_collection['name']) | ||
except (tk.ObjectNotFound, tk.NotAuthorized): | ||
tk.abort(404, tk._('Metadata record not found')) | ||
except dict_fns.DataError: | ||
tk.abort(400, tk._(u'Integrity Error')) | ||
except tk.ValidationError, e: | ||
errors = e.error_dict | ||
error_summary = e.error_summary | ||
return self.new(data_dict, errors, error_summary) | ||
|
||
def _save_edit(self, id, context): | ||
try: | ||
data_dict = clean_dict(dict_fns.unflatten(tuplize_dict(parse_params(tk.request.params)))) | ||
data_dict['id'] = id | ||
data_dict['infrastructures'] = self._parse_infrastructure_ids(data_dict.get('infrastructure_ids')) | ||
context['message'] = data_dict.get('log_message', '') | ||
context['allow_partial_update'] = True | ||
metadata_record = tk.get_action('metadata_record_update')(context, data_dict) | ||
tk.h.redirect_to('metadata_record_read', id=metadata_record['id'], | ||
organization_id=tk.c.organization['name'], | ||
metadata_collection_id=tk.c.metadata_collection['name']) | ||
except (tk.ObjectNotFound, tk.NotAuthorized), e: | ||
tk.abort(404, tk._('Metadata record not found')) | ||
except dict_fns.DataError: | ||
tk.abort(400, tk._(u'Integrity Error')) | ||
except tk.ValidationError, e: | ||
errors = e.error_dict | ||
error_summary = e.error_summary | ||
return self.edit(id, data_dict, errors, error_summary) | ||
|
||
@staticmethod | ||
def _parse_infrastructure_ids(infrastructure_ids): | ||
if not infrastructure_ids: | ||
return [] | ||
if isinstance(infrastructure_ids, basestring): | ||
return [{'id': infrastructure_ids}] | ||
return [{'id': infrastructure_id} for infrastructure_id in infrastructure_ids] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
metadata_schema: https://json-schema.org/assets/logo.svg | ||
metadata_standard: Icon made by Smashicons from www.flaticon.com (search term: standard) | ||
metadata_record: adapted from https://json-schema.org/assets/logo.svg | ||
workflow_state: Icon made by Freepik from www.flaticon.com (search term: to do list) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions
10
ckanext/metadata/templates/metadata_record/activity_stream.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{% extends "metadata_record/read_base.html" %} | ||
|
||
{% block subtitle %}{{ _('Activity Stream') }} - {{ super() }}{% endblock %} | ||
|
||
{% block primary_content_inner %} | ||
<h2 class="hide-heading">{% block page_heading %}{{ _('Activity Stream') }}{% endblock %}</h2> | ||
{% block activity_stream %} | ||
{{ c.metadata_record_activity_stream | safe }} | ||
{% endblock %} | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{% extends "metadata_record/edit_base.html" %} | ||
|
||
{% block page_heading_class %}hide-heading{% endblock %} | ||
{% block page_heading %}{{ _('Edit Metadata Record') }}{% endblock %} | ||
{% block subtitle %} | ||
{{ _('Manage') }} - {{ c.metadata_record.display_name }} - {{ _('Metadata Records') }} | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{% extends "page.html" %} | ||
|
||
{% block breadcrumb_content %} | ||
{% snippet "metadata_record/snippets/breadcrumb_content_outer.html" %} | ||
{% snippet "metadata_record/snippets/breadcrumb_content_item.html" %} | ||
{% snippet "metadata_record/snippets/breadcrumb_content_manage.html" %} | ||
{% endblock %} | ||
|
||
{% block page_primary_action %}{% endblock %} | ||
|
||
{% block primary_content_inner %} | ||
<h1 class="{% block page_heading_class %}page-heading{% endblock %}">{% block page_heading %}{{ _('Metadata Record Form') }}{% endblock %}</h1> | ||
{% block form %} | ||
{{ c.form | safe }} | ||
{% endblock %} | ||
{% endblock %} | ||
|
||
{% block content_action %} | ||
{% link_for _('View'), controller='ckanext.metadata.controllers.metadata_record:MetadataRecordController', | ||
action='read', id=c.metadata_record.id, organization_id=c.organization.name, metadata_collection_id=c.metadata_collection.name, class_='btn', icon='eye' %} | ||
{% endblock %} | ||
|
||
{% block content_primary_nav %} | ||
{{ h.build_nav_icon('metadata_record_edit', _('Edit'), id=c.metadata_record.id, organization_id=c.organization.name, metadata_collection_id=c.metadata_collection.name) }} | ||
{% endblock %} | ||
|
||
{% block secondary_content %} | ||
{% snippet "metadata_record/snippets/info.html", metadata_record=c.metadata_record %} | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{% import "macros/form.html" as form %} | ||
|
||
<form id="metadata-record-form" class="form-horizontal" action="" method="post"> | ||
{{ form.errors(error_summary) }} | ||
|
||
{{ form.hidden('owner_org', c.organization.name) }} | ||
{{ form.hidden('metadata_collection_id', c.metadata_collection.name) }} | ||
|
||
{{ form.select('metadata_standard_id', label=_('Metadata Standard'), id='field-metadata-standard', | ||
options=metadata_standard_lookup_list, selected=data.metadata_standard_id, error=errors.metadata_standard_id, | ||
classes=['control-medium'], is_required=true) }} | ||
|
||
{{ form.textarea('metadata_json', label=_('Metadata JSON'), id='field-metadata-json', | ||
placeholder=_('The JSON metadata dictionary.'), | ||
value=data.metadata_json, error=errors.metadata_json, is_required=true, | ||
attrs={'style': 'font-family: monospace'}, rows=12) }} | ||
|
||
{% call form.multiselect('infrastructure_ids', label=_('Infrastructures'), id='field-infrastructures', | ||
options=infrastructure_lookup_list, selected=selected_infrastructure_ids, error=errors.infrastructures, | ||
classes=['control-medium']) %} | ||
{{ form.info(_('Select the infrastructure(s) with which to associate the metadata record. Use Ctrl+Click to select / deselect individual items.')) }} | ||
{% endcall %} | ||
|
||
{{ form.required_message() }} | ||
|
||
<div class="form-actions"> | ||
{% if action == "edit" %} | ||
{% if h.check_access('metadata_record_delete', {'id': data.id}) %} | ||
<a class="btn btn-danger pull-left" data-module="confirm-action" data-module-content="{{ _('Are you sure you want to delete this Metadata Record?') }}" | ||
href="{% url_for controller='ckanext.metadata.controllers.metadata_record:MetadataRecordController', action='delete', id=data.id, organization_id=data.owner_org, metadata_collection_id=data.metadata_collection_id %}"> | ||
{% block delete_button_text %}{{ _('Delete') }}{% endblock %}</a> | ||
{% endif %} | ||
{% endif %} | ||
<button class="btn btn-primary" name="save" type="submit"> | ||
{%- if action == "edit" -%} | ||
{{ _('Update Metadata Record') }} | ||
{%- else -%} | ||
{{ _('Create Metadata Record') }} | ||
{%- endif -%} | ||
</button> | ||
</div> | ||
</form> |
Oops, something went wrong.