/***************************************************************************
* Copyright 2010 Global Biodiversity Information Facility Secretariat
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
***************************************************************************/
package org.gbif.ipt.action.manage;
import org.gbif.api.vocabulary.Country;
import org.gbif.api.vocabulary.DatasetSubtype;
import org.gbif.common.parsers.CountryParser;
import org.gbif.common.parsers.core.ParseResult;
import org.gbif.ipt.config.AppConfig;
import org.gbif.ipt.config.ConfigWarnings;
import org.gbif.ipt.config.Constants;
import org.gbif.ipt.model.Organisation;
import org.gbif.ipt.model.Resource;
import org.gbif.ipt.model.Resource.CoreRowType;
import org.gbif.ipt.model.voc.IdentifierStatus;
import org.gbif.ipt.model.voc.MetadataSection;
import org.gbif.ipt.service.InvalidConfigException;
import org.gbif.ipt.service.admin.RegistrationManager;
import org.gbif.ipt.service.admin.VocabulariesManager;
import org.gbif.ipt.service.manage.ResourceManager;
import org.gbif.ipt.struts2.SimpleTextProvider;
import org.gbif.ipt.utils.LangUtils;
import org.gbif.ipt.utils.MapUtils;
import org.gbif.ipt.validation.EmlValidator;
import org.gbif.ipt.validation.ResourceValidator;
import org.gbif.metadata.eml.Agent;
import org.gbif.metadata.eml.Eml;
import org.gbif.metadata.eml.JGTICuratorialUnitType;
import org.gbif.metadata.eml.TemporalCoverageType;
import org.gbif.metadata.eml.UserId;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
public class MetadataAction extends ManagerBaseAction {
private static final Logger LOG = Logger.getLogger(MetadataAction.class);
private final ResourceValidator validatorRes = new ResourceValidator();
private final EmlValidator emlValidator;
private final VocabulariesManager vocabManager;
private static final String LICENSES_PROPFILE_PATH = "/org/gbif/metadata/eml/licenses.properties";
private static final String LICENSE_NAME_PROPERTY_PREFIX = "license.name.";
private static final String LICENSE_TEXT_PROPERTY_PREFIX = "license.text.";
private static final String DIRECTORIES_PROPFILE_PATH = "/org/gbif/metadata/eml/UserDirectories.properties";
private MetadataSection section = MetadataSection.BASIC_SECTION;
private MetadataSection next = MetadataSection.GEOGRAPHIC_COVERAGE_SECTION;
private Map<String, String> languages;
private Map<String, String> countries;
private Map<String, String> ranks;
private Map<String, String> roles;
private Map<String, String> preservationMethods;
private Map<String, String> types;
private Map<String, String> datasetSubtypes;
private Map<String, String> frequencies;
private Map<String, String> organisations;
// to group dataset subtype vocabulary keys
private List<String> checklistSubtypeKeys;
private List<String> occurrenceSubtypeKeys;
private static final CountryParser COUNTRY_PARSER = CountryParser.getInstance();
private Agent primaryContact;
private boolean doiReservedOrAssigned = false;
private ConfigWarnings warnings;
private static Properties licenseProperties;
private static Properties directoriesProperties;
private static Map<String, String> licenses;
private static Map<String, String> licenseTexts;
private static Map<String, String> userIdDirectories;
@Inject
public MetadataAction(SimpleTextProvider textProvider, AppConfig cfg, RegistrationManager registrationManager,
ResourceManager resourceManager, VocabulariesManager vocabManager, ConfigWarnings warnings) {
super(textProvider, cfg, registrationManager, resourceManager);
this.vocabManager = vocabManager;
this.emlValidator = new EmlValidator(cfg, registrationManager, textProvider);
this.warnings = warnings;
}
/**
* @return a map of countries
*/
public Map<String, String> getCountries() {
return countries;
}
public String getCurrentSideMenu() {
return section.getName();
}
public Eml getEml() {
return resource.getEml();
}
public Map<String, String> getJGTICuratorialUnitTypeOptions() {
return JGTICuratorialUnitType.HTML_SELECT_MAP;
}
public String getLanguageIso3() {
String iso3 = LangUtils.iso3(resource.getEml().getLanguage());
if (languages.containsKey(iso3)) {
return iso3;
}
return null;
}
public Map<String, String> getLanguages() {
return languages;
}
/*
* Called from Basic Metadata page.
*
* Determine which license is specified in the intellectual rights. If the intellectual rights contains the name of
* a license the IPT supports (e.g. CC-BY 4.0), the key corresponding to that license (e.g. ccby) is returned. This
* is used to pre-select the license drop down when the basic metadata page loads.
*/
public String getLicenseKeySelected() {
String licenseText = resource.getEml().getIntellectualRights();
if (!Strings.isNullOrEmpty(licenseText)) {
for (Map.Entry<String, String> entry: licenses.entrySet()) {
String licenseName = entry.getValue();
if (!Strings.isNullOrEmpty(licenseName) && licenseText.contains(licenseName)) {
return entry.getKey();
}
}
}
return null;
}
public Map<String, String> getLicenses() {
return licenses;
}
public Map<String, String> getLicenseTexts() {
return licenseTexts;
}
/**
* Returns a Map containing dataset subtype entries. The entries returned depending on the core type.
* For example, if the core type is Occurrence, the Map will only contain occurrence dataset subtypes.
* This method is called by Struts.
*
* @return Map of dataset subtypes
*/
public Map<String, String> getListSubtypes() {
if (resource.getCoreType() == null) {
if (resource.getCoreTypeTerm() != null) {
String core = resource.getCoreTypeTerm().simpleName().toLowerCase();
if (Constants.DWC_ROWTYPE_TAXON.toLowerCase().contains(core)) {
return getChecklistSubtypesMap();
} else if (Constants.DWC_ROWTYPE_OCCURRENCE.toLowerCase().contains(core)) {
return getOccurrenceSubtypesMap();
} else if (Constants.DWC_ROWTYPE_EVENT.toLowerCase().contains(core)) {
return getEmptySubtypeMap(); // because there are currently no dataset subtypes for sampling event datasets
}
}
} else {
if (resource.getCoreType().equalsIgnoreCase(CoreRowType.CHECKLIST.toString())) {
return getChecklistSubtypesMap();
} else if (resource.getCoreType().equalsIgnoreCase(CoreRowType.OCCURRENCE.toString())) {
return getOccurrenceSubtypesMap();
} else if (resource.getCoreType().equalsIgnoreCase(CoreRowType.SAMPLINGEVENT.toString())) {
return getEmptySubtypeMap(); // because there are currently no dataset subtypes for sampling event datasets
} else if (CoreRowType.OTHER.toString().equalsIgnoreCase(resource.getCoreType())) {
return getEmptySubtypeMap();
}
}
return new LinkedHashMap<String, String>();
}
public String getMetadataLanguageIso3() {
String iso3 = LangUtils.iso3(resource.getEml().getMetadataLanguage());
if (languages.containsKey(iso3)) {
return iso3;
}
return null;
}
public String getNext() {
return next.getName();
}
/**
* @return a map of preservation methods
*/
public Map<String, String> getPreservationMethods() {
return preservationMethods;
}
/**
* @return a map of Ranks
*/
public Map<String, String> getRanks() {
return ranks;
}
@Override
public Resource getResource() {
return resource;
}
public Map<String, String> getRoles() {
return roles;
}
public String getSection() {
return section.getName();
}
public Map<String, String> getTempTypes() {
return TemporalCoverageType.HTML_SELECT_MAP;
}
public Map<String, String> getTypes() {
return types;
}
@Override
public void prepare() {
super.prepare();
// take the section parameter from the requested url
section = MetadataSection.fromName(StringUtils.substringBetween(req.getRequestURI(), "metadata-", "."));
switch (section) {
case BASIC_SECTION:
// Dataset core type list, derived from XML vocabulary, and displayed in drop-down on Basic Metadata page
types = new LinkedHashMap<String, String>();
types.put("", getText("resource.coreType.selection"));
types.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_DATASET_TYPE, getLocaleLanguage(), false));
types = MapUtils.getMapWithLowercaseKeys(types);
// Dataset Subtypes list, derived from XML vocabulary, and displayed in drop-down on Basic Metadata page
datasetSubtypes = new LinkedHashMap<String, String>();
datasetSubtypes.put("", getText("resource.subtype.selection"));
datasetSubtypes.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_DATASET_SUBTYPES, getLocaleLanguage(), false));
datasetSubtypes = MapUtils.getMapWithLowercaseKeys(datasetSubtypes);
// group subtypes into Checklist and Occurrence - used for getOccurrenceSubtypeKeys() and getChecklistSubtypeKeys()
groupDatasetSubtypes();
// update frequencies list, derived from XML vocabulary, and displayed in drop-down on basic metadata page
frequencies = new LinkedHashMap<String, String>();
frequencies.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_UPDATE_FREQUENCIES, getLocaleLanguage(), false));
// sanitize intellectualRights - pre-v2.2 text was manually entered and may have characters that break js
if (getEml().getIntellectualRights() != null) {
getEml().setIntellectualRights(removeNewlineCharacters(getEml().getIntellectualRights()));
}
// populate agent vocabularies
loadAgentVocabularies();
// load license maps
try {
loadLicenseMaps(getText("eml.intellectualRights.nolicenses"));
} catch (InvalidConfigException e) {
warnings.addStartupError(e.getMessage(), e);
}
// load directories map
try {
loadDirectories(getText("eml.contact.noDirectory"));
} catch (InvalidConfigException e) {
warnings.addStartupError(e.getMessage(), e);
}
// load organisations map
loadOrganisations();
// if IPT isn't registered there are no publishing organisations to choose from, so set to "No organisation"
if (getRegisteredIpt() == null && getDefaultOrganisation() != null) {
resource.setOrganisation(getDefaultOrganisation());
addActionWarning(getText("manage.overview.visibility.missing.organisation"));
}
if (isHttpPost()) {
resource.getEml().getDescription().clear();
resource.getEml().getContacts().clear();
resource.getEml().getCreators().clear();
resource.getEml().getMetadataProviders().clear();
resource.getEml().setIntellectualRights(null);
// publishing organisation, if provided must match organisation
String id = getId();
Organisation organisation = (id == null) ? null : registrationManager.get(id);
if (organisation != null) {
// set organisation: note organisation is locked after 1) DOI assigned, or 2) after registration with GBIF
if (!resource.isAlreadyAssignedDoi() && !resource.isRegistered()) {
resource.setOrganisation(organisation);
}
}
}
break;
case GEOGRAPHIC_COVERAGE_SECTION:
if (isHttpPost()) {
resource.getEml().getGeospatialCoverages().clear();
}
break;
case TAXANOMIC_COVERAGE_SECTION:
// ranks list, derived from XML vocabulary, and displayed on Taxonomic Coverage Page
ranks = new LinkedHashMap<String, String>();
ranks.put("", getText("eml.rank.selection"));
ranks.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_RANKS, getLocaleLanguage(), false));
if (isHttpPost()) {
resource.getEml().getTaxonomicCoverages().clear();
}
break;
case TEMPORAL_COVERAGE_SECTION:
if (isHttpPost()) {
resource.getEml().getTemporalCoverages().clear();
}
break;
case KEYWORDS_SECTION:
if (isHttpPost()) {
resource.getEml().getKeywords().clear();
}
break;
case PARTIES_SECTION:
// populate agent vocabularies
loadAgentVocabularies();
if (isHttpPost()) {
resource.getEml().getAssociatedParties().clear();
}
break;
case PROJECT_SECTION:
// populate agent vocabularies
loadAgentVocabularies();
if (isHttpPost()) {
resource.getEml().getProject().getPersonnel().clear();
}
break;
case METHODS_SECTION:
if (isHttpPost()) {
resource.getEml().getMethodSteps().clear();
}
break;
case CITATIONS_SECTION:
if (isHttpPost()) {
resource.getEml().getBibliographicCitationSet().getBibliographicCitations().clear();
}
doiReservedOrAssigned = hasDoiReservedOrAssigned(resource);
if (doiReservedOrAssigned && !isHttpPost()) {
addActionMessage(
"The DOI reserved or registered for this resource is being used as the citation identifier");
}
break;
case COLLECTIONS_SECTION:
// preservation methods list, derived from XML vocabulary, and displayed in drop-down on Collections Data Page.
preservationMethods = new LinkedHashMap<String, String>();
preservationMethods.put("", getText("eml.preservation.methods.selection"));
preservationMethods.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_PRESERVATION_METHOD, getLocaleLanguage(), false));
if (isHttpPost()) {
resource.getEml().getCollections().clear();
resource.getEml().getSpecimenPreservationMethods().clear();
resource.getEml().getJgtiCuratorialUnits().clear();
}
break;
case PHYSICAL_SECTION:
if (isHttpPost()) {
resource.getEml().getPhysicalData().clear();
}
break;
case ADDITIONAL_SECTION:
if (isHttpPost()) {
resource.getEml().getAlternateIdentifiers().clear();
}
break;
default: break;
}
}
@Override
public String save() throws Exception {
// before saving, the minimum amount of mandatory metadata must have been provided, and ALL metadata sections must
// be valid, otherwise an error is displayed
if (emlValidator.areAllSectionsValid(this, resource)) {
// Save metadata information (eml.xml)
resourceManager.saveEml(resource);
// save date metadata was last modified
resource.setMetadataModified(new Date());
// Alert user of successful save
addActionMessage(getText("manage.success", new String[] {getText("submenu." + section.getName())}));
// Save resource information (resource.xml)
resourceManager.save(resource);
// progress to next section, since save succeeded
switch (section) {
case BASIC_SECTION:
next = MetadataSection.GEOGRAPHIC_COVERAGE_SECTION;
break;
case GEOGRAPHIC_COVERAGE_SECTION:
next = MetadataSection.TAXANOMIC_COVERAGE_SECTION;
break;
case TAXANOMIC_COVERAGE_SECTION:
next = MetadataSection.TEMPORAL_COVERAGE_SECTION;
break;
case TEMPORAL_COVERAGE_SECTION:
next = MetadataSection.KEYWORDS_SECTION;
break;
case KEYWORDS_SECTION:
next = MetadataSection.PARTIES_SECTION;
break;
case PARTIES_SECTION:
next = MetadataSection.PROJECT_SECTION;
break;
case PROJECT_SECTION:
next = MetadataSection.METHODS_SECTION;
break;
case METHODS_SECTION:
next = MetadataSection.CITATIONS_SECTION;
break;
case CITATIONS_SECTION:
next = MetadataSection.COLLECTIONS_SECTION;
break;
case COLLECTIONS_SECTION:
next = MetadataSection.PHYSICAL_SECTION;
break;
case PHYSICAL_SECTION:
next = MetadataSection.ADDITIONAL_SECTION;
break;
case ADDITIONAL_SECTION:
next = MetadataSection.BASIC_SECTION;
break;
default: break;
}
} else {
// stay on the same section, since save failed
next = section;
}
return SUCCESS;
}
@Override
public void validateHttpPostOnly() {
validatorRes.validate(this, resource);
emlValidator.validate(this, resource, section);
}
/**
* A list of dataset subtypes used to populate the dataset subtype dropdown on the Basic Metadata page.
*
* @return list of dataset subtypes
*/
public Map<String, String> getDatasetSubtypes() {
return datasetSubtypes;
}
/**
* Exclude all known Checklist subtypes from the complete Map of Occurrence dataset subtypes, and return it. To
* exclude a newly added Checklist subtype, just extend the static list above. Called from Struts, so must be public.
*
* @return Occurrence subtypes Map
*/
public Map<String, String> getOccurrenceSubtypesMap() {
// exclude subtypes known to relate to Checklist type
Map<String, String> datasetSubtypesCopy = new LinkedHashMap<String, String>(datasetSubtypes);
for (String key : checklistSubtypeKeys) {
if (datasetSubtypesCopy.containsKey(key)) {
datasetSubtypesCopy.remove(key);
}
}
return datasetSubtypesCopy;
}
/**
* Exclude all known Occurrence subtypes from the complete Map of Checklist dataset subtypes, and return it. To
* exclude a newly added Occurrence subtype, just extend the static list above. Called from Struts, so must be
* public.
*
* @return Checklist subtypes Map
*/
public Map<String, String> getChecklistSubtypesMap() {
// exclude subtypes known to relate to Checklist type
Map<String, String> datasetSubtypesCopy = new LinkedHashMap<String, String>(datasetSubtypes);
for (String key : occurrenceSubtypeKeys) {
if (datasetSubtypesCopy.containsKey(key)) {
datasetSubtypesCopy.remove(key);
}
}
return datasetSubtypesCopy;
}
/**
* Returns a Map representing with only a single entry indicating that there is no subtype to choose from. Called
* from Struts, so must be public.
*
* @return a Map representing an empty set of dataset subtypes.
*/
public Map<String, String> getEmptySubtypeMap() {
Map<String, String> subtypeMap = new LinkedHashMap<String, String>();
subtypeMap.put("", getText("resource.subtype.none"));
return subtypeMap;
}
/**
* Group dataset subtypes into 2 lists: one for checklist subtypes and the other for occurrence subtype keys.
* The way this grouping is done, is that DatasetSubtype Enum.name gets converted into vocabulary.identifier,
* for ex: TAXONOMIC_IDENTIFIER -> taxonomiciIdentifier
* As long as the DatasetSubtype is extended properly in accordance with the DatasetSubtype vocabulary, simply
* updating the version of gbif-common-api dependency is the only change needed to update the subtype list.
* This is a workaround to the limitation we currently have with our XML vocabularies, in that concepts can't be
* grouped.
*/
void groupDatasetSubtypes() {
List<String> occurrenceKeys = new LinkedList<String>();
for (DatasetSubtype type : DatasetSubtype.OCCURRENCE_DATASET_SUBTYPES) {
occurrenceKeys.add(type.name().replaceAll("_", "").toLowerCase());
}
occurrenceSubtypeKeys = Collections.unmodifiableList(occurrenceKeys);
List<String> checklistKeys = new LinkedList<String>();
for (DatasetSubtype type : DatasetSubtype.CHECKLIST_DATASET_SUBTYPES) {
checklistKeys.add(type.name().replaceAll("_", "").toLowerCase());
}
checklistSubtypeKeys = Collections.unmodifiableList(checklistKeys);
}
void setDatasetSubtypes(Map<String, String> datasetSubtypes) {
this.datasetSubtypes = datasetSubtypes;
}
List<String> getChecklistSubtypeKeys() {
return checklistSubtypeKeys;
}
List<String> getOccurrenceSubtypeKeys() {
return occurrenceSubtypeKeys;
}
/**
* On the basic metadata page, this variable determines whether the core type dropdown is
* disabled or not.
*
* @return "true" or "false" - does the resource have a core mapping yet?
*/
public String getResourceHasCore() {
return (resource.hasCore()) ? "true" : "false";
}
/**
* On the basic metadata page, this map populates the update frequencies dropdown. The map is derived from the
* vocabulary {@link -linkoffline http://rs.gbif.org/vocabulary/eml/update_frequency.xml}.
*
* @return update frequencies map
*/
public Map<String, String> getFrequencies() {
return frequencies;
}
/**
* @return the very first contact entered, or a new instance of Agent if no contacts exist yet. The first contact
* entered is considered the primary contact. Other contact forms can be entered by copying from the primary contact.
*/
public Agent getPrimaryContact() {
return primaryContact;
}
public void setPrimaryContact(Agent primaryContact) {
this.primaryContact = primaryContact;
}
public Map<String, String> getUserIdDirectories() {
return userIdDirectories;
}
/**
* @return Properties from licenses properties file
*
* @throws InvalidConfigException if licenses properties file could not be loaded
*/
@Singleton
public static synchronized Properties licenseProperties() throws InvalidConfigException {
if (licenseProperties == null) {
Properties p = new Properties();
try {
p.load(MetadataAction.class.getResourceAsStream(LICENSES_PROPFILE_PATH));
LOG.debug("Loaded licenses from " + LICENSES_PROPFILE_PATH);
} catch (IOException e) {
throw new InvalidConfigException(InvalidConfigException.TYPE.INVALID_PROPERTIES_FILE,
"Failed to load licenses from " + LICENSES_PROPFILE_PATH);
} finally {
licenseProperties = p;
}
}
return licenseProperties;
}
/**
* @return Properties from directories properties file
*
* @throws InvalidConfigException if directories properties file could not be loaded
*/
@Singleton
public static synchronized Properties directoriesProperties() throws InvalidConfigException {
if (directoriesProperties == null) {
Properties p = new Properties();
try {
p.load(MetadataAction.class.getResourceAsStream(DIRECTORIES_PROPFILE_PATH));
LOG.debug("Loaded directories from " + DIRECTORIES_PROPFILE_PATH);
} catch (IOException e) {
throw new InvalidConfigException(InvalidConfigException.TYPE.INVALID_PROPERTIES_FILE,
"Failed to load directories from " + DIRECTORIES_PROPFILE_PATH);
} finally {
directoriesProperties = p;
}
}
return directoriesProperties;
}
/**
* Load license maps: #1) license names - used to populate select on Basic Metadata Page
* #2) license texts - used to populate text area on Basic Metadata Page
*
* @param firstOption the default select option for the license names (e.g. no license selected)
*
* @throws InvalidConfigException if the licenses properties file was badly configured
*/
@Singleton
public static synchronized void loadLicenseMaps(String firstOption) throws InvalidConfigException {
if (licenses == null || licenseTexts == null) {
licenses = new TreeMap<String, String>();
licenses.put("", (firstOption == null) ? "-" : firstOption);
licenseTexts = new TreeMap<String, String>();
Properties properties = licenseProperties();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = StringUtils.trim((String) entry.getKey());
String value = StringUtils.trim((String) entry.getValue());
if (key != null && key.startsWith(LICENSE_NAME_PROPERTY_PREFIX) && value != null) {
String keyMinusPrefix = StringUtils.trimToNull(key.replace(LICENSE_NAME_PROPERTY_PREFIX, ""));
if (keyMinusPrefix != null) {
String licenseText =
StringUtils.trimToNull((properties.getProperty(LICENSE_TEXT_PROPERTY_PREFIX + keyMinusPrefix)));
if (licenseText != null) {
licenses.put(keyMinusPrefix, value);
licenseTexts.put(keyMinusPrefix, licenseText);
}
} else {
String error = LICENSES_PROPFILE_PATH + " has been been configured wrong.";
LOG.error(error);
throw new InvalidConfigException(InvalidConfigException.TYPE.INVALID_PROPERTIES_FILE, error);
}
}
}
if ((licenses.size() - 1) == 0) {
String error = "No licenses could be loaded from " + LICENSES_PROPFILE_PATH + ". Please check configuration.";
LOG.error(error);
throw new InvalidConfigException(InvalidConfigException.TYPE.INVALID_PROPERTIES_FILE, error);
}
}
}
/**
* Load directories map used to populate select on Basic Metadata Page
*
* @param firstOption the default select option for the directory names (e.g. no directory selected)
*
* @throws InvalidConfigException if the directories properties file was badly configured
*/
@Singleton
public static synchronized void loadDirectories(String firstOption) throws InvalidConfigException {
if (userIdDirectories == null) {
userIdDirectories = new TreeMap<String, String>();
userIdDirectories.put("", (firstOption == null) ? "-" : firstOption);
Properties properties = directoriesProperties();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = StringUtils.trim((String) entry.getKey());
String value = StringUtils.trim((String) entry.getValue());
if (key != null && value != null) {
userIdDirectories.put(key, value);
} else {
String error = DIRECTORIES_PROPFILE_PATH + " has been been configured wrong.";
LOG.error(error);
throw new InvalidConfigException(InvalidConfigException.TYPE.INVALID_PROPERTIES_FILE, error);
}
}
if ((userIdDirectories.size() - 1) == 0) {
String error = "No licenses could be loaded from " + DIRECTORIES_PROPFILE_PATH + ". Please check configuration.";
LOG.error(error);
throw new InvalidConfigException(InvalidConfigException.TYPE.INVALID_PROPERTIES_FILE, error);
}
}
}
/**
* Method that loads all vocabularies, and primary contact needed for the agent form.
*/
private void loadAgentVocabularies() {
// languages list, derived from XML vocabulary, and displayed in drop-down on Basic Metadata page
languages = vocabManager.getI18nVocab(Constants.VOCAB_URI_LANGUAGE, getLocaleLanguage(), true);
// countries list, derived from XML vocabulary, and displayed in drop-down where new contacts are created
countries = new LinkedHashMap<String, String>();
countries.put("", getText("eml.country.selection"));
countries.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_COUNTRY, getLocaleLanguage(), true));
// roles list, derived from XML vocabulary, and displayed in drop-down where new contacts are created
roles = new LinkedHashMap<String, String>();
roles.put("", getText("eml.agent.role.selection"));
roles.putAll(vocabManager.getI18nVocab(Constants.VOCAB_URI_ROLES, getLocaleLanguage(), false));
if (resource != null && resource.getEml() != null) {
// create Agent equal to current user
Agent current = new Agent();
current.setFirstName(getCurrentUser().getFirstname());
current.setLastName(getCurrentUser().getLastname());
current.setEmail(getCurrentUser().getEmail());
// contacts list
Agent firstContact = null;
if (!resource.getEml().getContacts().isEmpty()) {
for (Agent contact: resource.getEml().getContacts()) {
// capture first contact, used as primary contact to copy in contact form details
if (firstContact == null) {
firstContact = contact;
}
String countryValue = contact.getAddress().getCountry();
if (countryValue != null) {
ParseResult<Country> result = COUNTRY_PARSER.parse(countryValue);
if (result.isSuccessful()) {
contact.getAddress().setCountry(result.getPayload().getIso2LetterCode());
}
}
}
}
// always populate the primary contact, used to auto-fill parties and project personnel
if (firstContact == null) {
firstContact = new Agent();
}
if (firstContact.getUserIds().isEmpty()) {
firstContact.setUserIds(Lists.newArrayList(new UserId()));
}
setPrimaryContact(firstContact);
// creator list
if (!resource.getEml().getCreators().isEmpty()) {
for (Agent creator: resource.getEml().getCreators()) {
String countryValue = creator.getAddress().getCountry();
if (countryValue != null) {
ParseResult<Country> result = COUNTRY_PARSER.parse(countryValue);
creator.getAddress().setCountry(result.getPayload().getIso2LetterCode());
}
}
}
// metadata provider list
if (!resource.getEml().getMetadataProviders().isEmpty()) {
for (Agent metadataProvider: resource.getEml().getMetadataProviders()) {
String countryValue = metadataProvider.getAddress().getCountry();
if (countryValue != null) {
ParseResult<Country> result = COUNTRY_PARSER.parse(countryValue);
metadataProvider.getAddress().setCountry(result.getPayload().getIso2LetterCode());
}
}
} else {
// add current user to metadataProviders list
resource.getEml().addMetadataProvider(current);
}
// auto populate user with current user if associated parties list is empty, and eml.xml hasn't been written yet
if (!resourceManager.isEmlExisting(resource.getShortname()) && resource.getEml().getAssociatedParties().isEmpty()) {
current.setRole("user");
resource.getEml().getAssociatedParties().add(current);
}
// otherwise, ensure associated parties' country value get converted into 2 letter iso code for proper display
else if (!resource.getEml().getAssociatedParties().isEmpty()) {
for (Agent party : resource.getEml().getAssociatedParties()) {
String countryValue = party.getAddress().getCountry();
if (countryValue != null) {
ParseResult<Country> result = COUNTRY_PARSER.parse(countryValue);
if (result.isSuccessful()) {
party.getAddress().setCountry(result.getPayload().getIso2LetterCode());
}
}
}
}
}
}
/**
* @return list of organisations associated to IPT that can publish resources
*/
public Map<String, String> getOrganisations() {
return organisations;
}
/**
* Determine whether a DOI has been reserved or registered for the resource.
*
* @return true if a DOI has been reserved or registered for the resource, or false otherwise
*/
@VisibleForTesting
public boolean hasDoiReservedOrAssigned(Resource resource) {
return (resource.getDoi() != null && resource.getIdentifierStatus() != IdentifierStatus.UNRESERVED);
}
/**
* Used to activate, deactivate the citation identifier field on the citation metadata page.
*
* @return true if a DOI has been reserved or registered for the resource, or false otherwise
*/
public boolean isDoiReservedOrAssigned() {
return doiReservedOrAssigned;
}
/**
* Populate organisations dropdown options/list, with placeholder option, followed by list of organisations able to
* host resources. There must be more than the default organisation "No organisation" in order to include the
* placeholder option.
*/
private void loadOrganisations() {
List<Organisation> associatedOrganisations = registrationManager.list();
organisations = Maps.newLinkedHashMap();
if (!associatedOrganisations.isEmpty()) {
// add placeholder if there is more than the default organisation "No organisation"
if (associatedOrganisations.size() > 1) {
organisations.put("", getText("admin.organisation.name.select"));
}
// add default organisation "No organisation" as first option
Organisation noOrganisation = getDefaultOrganisation();
if (noOrganisation != null) {
organisations.put(noOrganisation.getKey().toString(), getText("eml.publishingOrganisation.none"));
}
// then add remaining organisations in the order they have been sorted, excluding the default organisation
for (Organisation o : associatedOrganisations) {
if (!o.getKey().equals(noOrganisation.getKey())) {
organisations.put(o.getKey().toString(), o.getName());
}
}
}
}
/**
* Remove all newline characters from string. Used to sanitize string for javascript, otherwise an
* "Unexpected Token ILLEGAL" error may occur.
*/
@VisibleForTesting
protected String removeNewlineCharacters(String s) {
if (s != null) {
s = s.replaceAll("\\r\\n|\\r|\\n", " ");
}
return s;
}
}