From 804fcecd3a3b476038e467d9587339634cb680c4 Mon Sep 17 00:00:00 2001
From: mwallschlaeger
Date: Thu, 24 Nov 2022 09:45:57 +0100
Subject: [PATCH 01/49]
issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity
---
geonode/base/api/serializers.py | 18 ++-
geonode/base/forms.py | 7 +-
geonode/base/models.py | 134 ++++++++++++------
.../backends/pycsw_local_mappings.py | 15 +-
geonode/geoserver/helpers.py | 4 +-
geonode/layers/views.py | 5 +-
6 files changed, 122 insertions(+), 61 deletions(-)
diff --git a/geonode/base/api/serializers.py b/geonode/base/api/serializers.py
index 714709a2949..102e2501b06 100644
--- a/geonode/base/api/serializers.py
+++ b/geonode/base/api/serializers.py
@@ -368,7 +368,7 @@ def get_attribute(self, instance):
return getattr(instance, self.contat_type)
def to_representation(self, value):
- return UserSerializer(embed=True, many=False).to_representation(value)
+ return [UserSerializer(embed=True, many=False).to_representation(v) for v in value]
class DataBlobField(DynamicRelationField):
@@ -447,9 +447,17 @@ def __init__(self, *args, **kwargs):
self.fields['uuid'] = serializers.CharField(read_only=True)
self.fields['resource_type'] = serializers.CharField(required=False)
self.fields['polymorphic_ctype_id'] = serializers.CharField(read_only=True)
- self.fields['owner'] = DynamicRelationField(UserSerializer, embed=True, many=False, read_only=True, required=False)
- self.fields['poc'] = ContactRoleField('poc', read_only=True)
- self.fields['metadata_author'] = ContactRoleField('metadata_author', read_only=True)
+ self.fields['owner'] = DynamicRelationField(UserSerializer, embed=True, many=False, read_only=True)
+ self.fields['metadata_author'] = ContactRoleField('metadata_author', read_only=True, required=False)
+ self.fields['processor'] = ContactRoleField('processor', read_only=True, required=False)
+ self.fields['publisher'] = ContactRoleField('publisher', read_only=True, required=False)
+ self.fields['custodian'] = ContactRoleField('custodian', read_only=True, required=False)
+ self.fields['poc'] = ContactRoleField('poc', read_only=True, required=False)
+ self.fields['distributor'] = ContactRoleField('distributor', read_only=True, required=False)
+ self.fields['resource_user'] = ContactRoleField('resource_user', read_only=True, required=False)
+ self.fields['resource_provider'] = ContactRoleField('resource_provider', read_only=True, required=False)
+ self.fields['originator'] = ContactRoleField('originator', read_only=True, required=False)
+ self.fields['principal_investigator'] = ContactRoleField('principal_investigator', read_only=True, required=False)
self.fields['title'] = serializers.CharField()
self.fields['abstract'] = serializers.CharField(required=False)
self.fields['attribution'] = serializers.CharField(required=False)
@@ -520,7 +528,7 @@ class Meta:
view_name = 'base-resources-list'
fields = (
'pk', 'uuid', 'resource_type', 'polymorphic_ctype_id', 'perms',
- 'owner', 'poc', 'metadata_author',
+ 'owner', 'poc', 'metadata_author', 'processor', 'publisher', 'custodian', 'distributor', 'resource_user', 'resource_provider', 'originator', 'principal_investigator',
'keywords', 'tkeywords', 'regions', 'category',
'title', 'abstract', 'attribution', 'alternate', 'doi', 'bbox_polygon', 'll_bbox_polygon', 'srid',
'date', 'date_type', 'edition', 'purpose', 'maintenance_frequency',
diff --git a/geonode/base/forms.py b/geonode/base/forms.py
index a3146b6ecd9..67ff67c7e9b 100644
--- a/geonode/base/forms.py
+++ b/geonode/base/forms.py
@@ -440,7 +440,6 @@ class ResourceBaseForm(TranslationModelForm):
input_formats=['%Y-%m-%d %H:%M %p'],
widget=ResourceBaseDateTimePicker(options={"format": "YYYY-MM-DD HH:mm a"})
)
-
poc = forms.ModelChoiceField(
empty_label=_("Person outside GeoNode (fill form)"),
label=_("Point of Contact"),
@@ -449,13 +448,13 @@ class ResourceBaseForm(TranslationModelForm):
username='AnonymousUser'),
widget=autocomplete.ModelSelect2(url='autocomplete_profile'))
- metadata_author = forms.ModelChoiceField(
- empty_label=_("Person outside GeoNode (fill form)"),
+ metadata_author = forms.ModelMultipleChoiceField(
+ #empty_label=_("Person outside GeoNode (fill form)"),
label=_("Metadata Author"),
required=False,
queryset=get_user_model().objects.exclude(
username='AnonymousUser'),
- widget=autocomplete.ModelSelect2(url='autocomplete_profile'))
+ widget=TaggitSelect2Custom(url='autocomplete_profile'))
keywords = TagField(
label=_("Free-text Keywords"),
diff --git a/geonode/base/models.py b/geonode/base/models.py
index 99207f07269..0624e83b395 100644
--- a/geonode/base/models.py
+++ b/geonode/base/models.py
@@ -24,6 +24,7 @@
import uuid
import logging
import traceback
+from typing import Union, List, Optional
from sequences.models import Sequence
from sequences import get_next_value
@@ -1278,14 +1279,6 @@ def organizationname(self):
def restriction_code(self):
return self.restriction_code_type.gn_description if self.restriction_code_type else None
- @property
- def publisher(self):
- return self.poc.get_full_name() or self.poc.username
-
- @property
- def contributor(self):
- return self.metadata_author.get_full_name() or self.metadata_author.username
-
@property
def topiccategory(self):
return self.category.identifier if self.category else None
@@ -1847,54 +1840,105 @@ def maintenance_frequency_title(self):
def language_title(self):
return [v for v in enumerations.ALL_LANGUAGES if v[0] == self.language][0][1].title()
- def _set_poc(self, poc):
- # reset any poc assignation to this resource
- ContactRole.objects.filter(
- role='pointOfContact',
- resource=self).delete()
- # create the new assignation
- ContactRole.objects.create(
- role='pointOfContact',
- resource=self,
- contact=poc)
+ def add_missing_metadata_author_or_poc(self):
+ """
+ Set metadata_author and/or point of contact (poc) to a resource when any of them is missing
+ """
+ if not self.metadata_author:
+ self.metadata_author = [self.owner]
+ if not self.poc:
+ self.poc = [self.owner]
+
+ def _get_contact_role_elements(self, role: str) -> List[Optional[ContactRole]]:
+ """
+ generell getter of for all contact roles except owner
- def _get_poc(self):
+ param role (str): string coresponding to ROLE_VALUES in geonode/people/enumarations, defining which propery is requested
+ return List(ContactRole): returns the requested contact role from the database
+ """
try:
- the_poc = ContactRole.objects.get(
- role='pointOfContact', resource=self).contact
+ contact_role = ContactRole.objects.filter(
+ role=role, resource=self)
+ contacts = [cr.contact for cr in contact_role]
except ContactRole.DoesNotExist:
- the_poc = None
- return the_poc
+ contacts = None
+ return contacts
- poc = property(_get_poc, _set_poc)
+ def _set_contact_role_element(self, user_profile, role: str):
+ """
+ generell setter for all contact roles except owner in resource base
- def _set_metadata_author(self, metadata_author):
- # reset any metadata_author assignation to this resource
- ContactRole.objects.filter(role='author', resource=self).delete()
+ param contact_role (ContactRole):
+ param role (str): string coresponding to ROLE_VALUES in geonode/people/enumarations, defining which propery is to set
+ """
+ if not isinstance(user_profile, get_user_model()) :
+ return None
+ ContactRole.objects.filter(role=role, resource=self).delete()
# create the new assignation
ContactRole.objects.create(
- role='author',
+ role=role,
resource=self,
- contact=metadata_author)
-
- def _get_metadata_author(self):
- try:
- the_ma = ContactRole.objects.get(
- role='author', resource=self).contact
- except ContactRole.DoesNotExist:
- the_ma = None
- return the_ma
+ contact=user_profile)
- def add_missing_metadata_author_or_poc(self):
- """
- Set metadata_author and/or point of contact (poc) to a resource when any of them is missing
- """
- if not self.metadata_author:
- self.metadata_author = self.owner
- if not self.poc:
- self.poc = self.owner
+ def _get_poc(self): return self._get_contact_role_elements(role="pointOfContact")
+ def _set_poc(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="pointOfContact")
+ poc = property(_get_poc, _set_poc)
+ @property
+ def poc_csv(self): return ','.join(p.get_full_name() or p.username for p in self.poc)
+ def _get_metadata_author(self): return self._get_contact_role_elements(role="author")
+ def _set_metadata_author(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="author")
metadata_author = property(_get_metadata_author, _set_metadata_author)
+ @property
+ def metadata_author_csv(self): return ','.join(p.get_full_name() or p.username for p in self.metadata_author)
+
+ def _get_processor(self): return self._get_contact_role_elements(role="processor")
+ def _set_processor(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="processor")
+ processor = property(_get_processor, _set_processor)
+ @property
+ def processor_csv(self): return ','.join(p.get_full_name() or p.username for p in self.processor)
+
+ def _get_publisher(self): return self._get_contact_role_elements(role="publisher")
+ def _set_publisher(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="publisher")
+ publisher = property(_get_publisher, _set_publisher)
+ @property
+ def publisher_csv(self): return ','.join(p.get_full_name() or p.username for p in self.publisher)
+
+ def _get_custodian(self): return self._get_contact_role_elements(role="custodian")
+ def _set_custodian(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="custodian")
+ custodian = property(_get_custodian, _set_custodian)
+ @property
+ def custodian_csv(self): return ','.join(p.get_full_name() or p.username for p in self.custodian)
+
+ def _get_distributor(self): return self._get_contact_role_elements(role="distributor")
+ def _set_distributor(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="distributor")
+ distributor = property(_get_distributor, _set_distributor)
+ @property
+ def distributor_csv(self): return ','.join(p.get_full_name() or p.username for p in self.distributor)
+
+ def _get_resource_user(self): return self._get_contact_role_elements(role="resource_user")
+ def _set_resource_user(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="resource_user")
+ resource_user = property(_get_resource_user, _set_resource_user)
+ @property
+ def resource_user_csv(self): return ','.join(p.get_full_name() or p.username for p in self.resource_user)
+
+ def _get_resource_provider(self): return self._get_contact_role_elements(role="resource_provider")
+ def _set_resource_provider(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="resource_provider")
+ resource_provider = property(_get_resource_provider, _set_resource_provider)
+ @property
+ def resource_provider_csv(self): return ','.join(p.get_full_name() or p.username for p in self.resource_provider)
+
+ def _get_originator(self): return self._get_contact_role_elements(role="originator")
+ def _set_originator(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="originator")
+ originator = property(_get_originator, _set_originator)
+ @property
+ def originator_csv(self): return ','.join(p.get_full_name() or p.username for p in self.originator)
+
+ def _get_principal_investigator(self): return self._get_contact_role_elements(role="principal_investigator")
+ def _set_principal_investigator(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="principal_investigator")
+ principal_investigator = property(_get_principal_investigator, _set_principal_investigator)
+ @property
+ def principal_investigator_csv(self): return ','.join(p.get_full_name() or p.username for p in self.principal_investigator)
class LinkManager(models.Manager):
diff --git a/geonode/catalogue/backends/pycsw_local_mappings.py b/geonode/catalogue/backends/pycsw_local_mappings.py
index ead12f37b9a..71cb6dafaba 100644
--- a/geonode/catalogue/backends/pycsw_local_mappings.py
+++ b/geonode/catalogue/backends/pycsw_local_mappings.py
@@ -17,6 +17,7 @@
#
#########################################################################
+# based on https://github.com/geopython/pycsw/blob/master/pycsw/core/config.py
MD_CORE_MODEL = {
'typename': 'pycsw:CoreMetadata',
'outputschema': 'http://pycsw.org/metadata',
@@ -74,8 +75,18 @@
'pycsw:SpecificationDate': 'specificationdate',
'pycsw:SpecificationDateType': 'specificationdatetype',
'pycsw:Creator': 'creator',
- 'pycsw:Publisher': 'publisher',
- 'pycsw:Contributor': 'contributor',
+ 'pycsw:Publisher': 'publisher_csv',
+ 'pycsw:Contributor': 'contributor_csv',
+ 'pycsw:Processor': 'processor_csw',
+
+ # 'pycsw:MetadataAuthor': 'metadata_author_csv',
+ # 'pycsw:Custodian': 'custodian_csv',
+ # 'pycsw:Distributor': 'distributor_csv',
+ # 'pycsw:ResourceUser': 'resource_user_csv',
+ # 'pycsw:ResourceProvider': 'resource_provider_csv',
+ # 'pycsw:Originator': 'originator_csv',
+ # 'pycsw:PrincipalInvestigator': 'principal_investigator_csv',
+
'pycsw:Relation': 'relation',
'pycsw:Links': 'download_links',
}
diff --git a/geonode/geoserver/helpers.py b/geonode/geoserver/helpers.py
index ba235b0dce6..2b4c3d949f3 100755
--- a/geonode/geoserver/helpers.py
+++ b/geonode/geoserver/helpers.py
@@ -2123,13 +2123,13 @@ def sync_instance_with_geoserver(
if instance.poc:
# gsconfig now utilizes an attribution dictionary
gs_resource.attribution = {
- 'title': str(instance.poc),
+ 'title': str(instance.poc_csv),
'width': None,
'height': None,
'href': None,
'url': None,
'type': None}
- profile = get_user_model().objects.get(username=instance.poc.username)
+ profile = get_user_model().objects.get(username=instance.poc[0].username)
site_url = settings.SITEURL.rstrip('/') if settings.SITEURL.startswith('http') else settings.SITEURL
gs_resource.attribution_link = site_url + profile.get_absolute_url()
diff --git a/geonode/layers/views.py b/geonode/layers/views.py
index c94feccb9fe..8b66844a68f 100644
--- a/geonode/layers/views.py
+++ b/geonode/layers/views.py
@@ -630,7 +630,6 @@ def dataset_metadata(
values = []
values = [keyword.id for keyword in topic_thesaurus if int(tid) == keyword.thesaurus.id]
tkeywords_form.fields[tid].initial = values
-
if request.method == "POST" and dataset_form.is_valid() and attribute_form.is_valid(
) and category_form.is_valid() and tkeywords_form.is_valid() and timeseries_form.is_valid():
new_poc = dataset_form.cleaned_data['poc']
@@ -778,7 +777,7 @@ def dataset_metadata(
dataset_form.fields['is_approved'].widget.attrs.update({'disabled': 'true'})
if poc is not None:
- dataset_form.fields['poc'].initial = poc.id
+ dataset_form.fields['poc'].initial = poc[0].id # [ p.username for p in poc ]
poc_form = ProfileForm(prefix="poc")
poc_form.hidden = True
else:
@@ -786,7 +785,7 @@ def dataset_metadata(
poc_form.hidden = False
if metadata_author is not None:
- dataset_form.fields['metadata_author'].initial = metadata_author.id
+ dataset_form.fields['metadata_author'].initial = [ma.username for ma in metadata_author]
author_form = ProfileForm(prefix="author")
author_form.hidden = True
else:
From 7e23afbed3fa24b5b441925b7408477572180db3 Mon Sep 17 00:00:00 2001
From: mwallschlaeger
Date: Fri, 25 Nov 2022 14:33:07 +0100
Subject: [PATCH 02/49]
issue#10290_complete_ISO_contact_roles_per_ressource_base_with_multiplicity
---
geonode/base/forms.py | 85 +++++++++++++--
geonode/base/models.py | 64 ++++++++---
geonode/base/widgets.py | 16 +++
.../templates/datasets/dataset_metadata.html | 8 +-
.../datasets/dataset_metadata_advanced.html | 8 +-
geonode/layers/templates/layouts/panels.html | 83 +++++++++++++--
geonode/layers/views.py | 100 ++++++------------
7 files changed, 263 insertions(+), 101 deletions(-)
diff --git a/geonode/base/forms.py b/geonode/base/forms.py
index 67ff67c7e9b..0d1abd2c8ab 100644
--- a/geonode/base/forms.py
+++ b/geonode/base/forms.py
@@ -47,7 +47,7 @@
License, Region, ResourceBase, Thesaurus,
ThesaurusKeyword, ThesaurusKeywordLabel, ThesaurusLabel,
TopicCategory)
-from geonode.base.widgets import TaggitSelect2Custom
+from geonode.base.widgets import TaggitSelect2Custom, TaggitProfileSelect2Custom
from geonode.base.fields import MultiThesauriField
from geonode.documents.models import Document
from geonode.layers.models import Dataset
@@ -369,6 +369,18 @@ def _get_thesauro_title_label(item, lang):
return tname.first()
+class ContactRoleMultipleChoiceField(forms.ModelMultipleChoiceField):
+ # TODO ERROR HANDLING
+ def clean(self, value):
+ # try:
+ users = get_user_model().objects.filter(username__in=value)
+ # except:
+ # raise forms.ValidationError(_("Something went wrong in finding the profiles"))
+ # if len(users) < len(value):
+ # raise forms.ValidationError(_("not alle given profiles are found, maybe a typo?"))
+ return users
+
+
class ResourceBaseDateTimePicker(DateTimePicker):
def build_attrs(self, base_attrs=None, extra_attrs=None, **kwargs):
@@ -440,21 +452,76 @@ class ResourceBaseForm(TranslationModelForm):
input_formats=['%Y-%m-%d %H:%M %p'],
widget=ResourceBaseDateTimePicker(options={"format": "YYYY-MM-DD HH:mm a"})
)
- poc = forms.ModelChoiceField(
- empty_label=_("Person outside GeoNode (fill form)"),
- label=_("Point of Contact"),
+
+ metadata_author = ContactRoleMultipleChoiceField(
+ label=_("Metadata Author"),
required=False,
queryset=get_user_model().objects.exclude(
username='AnonymousUser'),
- widget=autocomplete.ModelSelect2(url='autocomplete_profile'))
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
- metadata_author = forms.ModelMultipleChoiceField(
- #empty_label=_("Person outside GeoNode (fill form)"),
- label=_("Metadata Author"),
+ processor = ContactRoleMultipleChoiceField(
+ label=_("Processor"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ publisher = ContactRoleMultipleChoiceField(
+ label=_("Publisher"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ custodian = ContactRoleMultipleChoiceField(
+ label=_("Custodian"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ poc = ContactRoleMultipleChoiceField(
+ label=_("Person of Contact"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ distributor = ContactRoleMultipleChoiceField(
+ label=_("Distributor"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ resource_user = ContactRoleMultipleChoiceField(
+ label=_("Resource User"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ resource_provider = ContactRoleMultipleChoiceField(
+ label=_("Resource Provider"),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ originator = ContactRoleMultipleChoiceField(
+ label=_('Originator'),
+ required=False,
+ queryset=get_user_model().objects.exclude(
+ username='AnonymousUser'),
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
+
+ principal_investigator = ContactRoleMultipleChoiceField(
+ label=_('Principal Investigator'),
required=False,
queryset=get_user_model().objects.exclude(
username='AnonymousUser'),
- widget=TaggitSelect2Custom(url='autocomplete_profile'))
+ widget=TaggitProfileSelect2Custom(url='autocomplete_profile'))
keywords = TagField(
label=_("Free-text Keywords"),
diff --git a/geonode/base/models.py b/geonode/base/models.py
index 0624e83b395..51b96b0e3e3 100644
--- a/geonode/base/models.py
+++ b/geonode/base/models.py
@@ -24,7 +24,7 @@
import uuid
import logging
import traceback
-from typing import Union, List, Optional
+from typing import List, Optional
from sequences.models import Sequence
from sequences import get_next_value
@@ -39,6 +39,7 @@
from django.contrib.auth.models import Group
from django.core.files.base import ContentFile
from django.contrib.auth import get_user_model
+from django.db.models.query import QuerySet
from django.db.models.fields.json import JSONField
from django.utils.functional import cached_property, classproperty
from django.contrib.gis.geos import Polygon, Point
@@ -1845,9 +1846,41 @@ def add_missing_metadata_author_or_poc(self):
Set metadata_author and/or point of contact (poc) to a resource when any of them is missing
"""
if not self.metadata_author:
- self.metadata_author = [self.owner]
+ self.metadata_author = self.owner
if not self.poc:
- self.poc = [self.owner]
+ self.poc = self.owner
+
+ def get_multivalue_role_property_names(self) -> List[str]:
+ """ _summary_: returns list of property names for all contact roles able to
+ handle multiple profile_users
+
+ Returns:
+ _type_: List(str)
+ _description: list of names
+ """
+ return [
+ 'metadata_author',
+ 'publisher',
+ 'custodian',
+ 'poc',
+ 'distributor',
+ 'resource_user',
+ 'resource_provider',
+ 'originator',
+ 'principal_investigator'
+ ]
+
+ def get_multivalue_required_role_property_names(self) -> List[str]:
+ """ _summary_: returns list of property names for all contact roles that are required
+
+ Returns:
+ _type_: List(str)
+ _description: list of names
+ """
+ return [
+ 'metadata_author',
+ 'poc',
+ ]
def _get_contact_role_elements(self, role: str) -> List[Optional[ContactRole]]:
"""
@@ -1866,19 +1899,26 @@ def _get_contact_role_elements(self, role: str) -> List[Optional[ContactRole]]:
def _set_contact_role_element(self, user_profile, role: str):
"""
- generell setter for all contact roles except owner in resource base
+ general setter for all contact roles except owner in resource base
param contact_role (ContactRole):
param role (str): string coresponding to ROLE_VALUES in geonode/people/enumarations, defining which propery is to set
"""
- if not isinstance(user_profile, get_user_model()) :
- return None
- ContactRole.objects.filter(role=role, resource=self).delete()
- # create the new assignation
- ContactRole.objects.create(
- role=role,
- resource=self,
- contact=user_profile)
+
+ def __create_role__(resource, role, user_profile):
+ ContactRole.objects.create(
+ role=role,
+ resource=resource,
+ contact=user_profile)
+
+ if isinstance(user_profile, QuerySet):
+ ContactRole.objects.filter(role=role, resource=self).delete()
+ [__create_role__(self, role, user) for user in user_profile]
+ elif isinstance(user_profile, get_user_model()):
+ ContactRole.objects.filter(role=role, resource=self).delete()
+ __create_role__(self, role, user_profile)
+ else:
+ logger.error(f'Bad profile format for role: {role} ...')
def _get_poc(self): return self._get_contact_role_elements(role="pointOfContact")
def _set_poc(self, user_profile): return self._set_contact_role_element(user_profile=user_profile, role="pointOfContact")
diff --git a/geonode/base/widgets.py b/geonode/base/widgets.py
index 08e41930e23..5e7b9597d86 100644
--- a/geonode/base/widgets.py
+++ b/geonode/base/widgets.py
@@ -20,3 +20,19 @@ def value_from_datadict(self, data, files, name):
return value
except TypeError:
return ""
+
+
+class TaggitProfileSelect2Custom(TaggitSelect2):
+ """Overriding Select2 tag widget for ContactRoleField.
+ """
+
+ def value_from_datadict(self, data, files, name):
+ """Handle multi-profiles.
+
+ returns list of selected elements
+ """
+ try:
+ ret_list = data.getlist(name)
+ return ret_list
+ except TypeError:
+ return []
diff --git a/geonode/layers/templates/datasets/dataset_metadata.html b/geonode/layers/templates/datasets/dataset_metadata.html
index 9a1fb5aaaee..945c5d5f9b2 100644
--- a/geonode/layers/templates/datasets/dataset_metadata.html
+++ b/geonode/layers/templates/datasets/dataset_metadata.html
@@ -43,12 +43,12 @@ {% trans "Metadata" %} {% blocktrans with dataset.ti
Some of your original metadata may have been lost.{% endblocktrans %}
{% endif %}
- {% if dataset_form.errors or attribute_form.errors or category_form.errors or author_form.errors or poc.errors or tkeywords_form.errors %}
+ {% if dataset_form.errors or attribute_form.errors or category_form.errors or metadata_author_form.errors or poc.errors or tkeywords_form.errors %}
{% blocktrans %}Error updating metadata. Please check the following fields: {% endblocktrans %}