package org.gbif.ipt.action.admin;
import org.gbif.api.model.common.DOI;
import org.gbif.ipt.action.POSTAction;
import org.gbif.ipt.config.AppConfig;
import org.gbif.ipt.config.Constants;
import org.gbif.ipt.model.Organisation;
import org.gbif.ipt.model.Resource;
import org.gbif.ipt.model.voc.DOIRegistrationAgency;
import org.gbif.ipt.service.AlreadyExistingException;
import org.gbif.ipt.service.DeletionNotAllowedException;
import org.gbif.ipt.service.RegistryException;
import org.gbif.ipt.service.admin.RegistrationManager;
import org.gbif.ipt.service.manage.ResourceManager;
import org.gbif.ipt.service.registry.RegistryManager;
import org.gbif.ipt.struts2.SimpleTextProvider;
import org.gbif.ipt.validation.OrganisationSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import com.google.inject.servlet.SessionScoped;
import org.apache.log4j.Logger;
/**
* The Action responsible for all user input relating to the organisations allowed in the IPT.
*/
public class OrganisationsAction extends POSTAction {
// logging
private static final Logger LOG = Logger.getLogger(OrganisationsAction.class);
/**
* A session scoped cache of the organisations from the GBIF registry.
*/
@SessionScoped
public static class RegisteredOrganisations {
private List<Organisation> organisations = new ArrayList<Organisation>();
private final RegistryManager registryManager;
@Inject
public RegisteredOrganisations(RegistryManager registryManager) {
this.registryManager = registryManager;
}
public boolean isLoaded() {
return !organisations.isEmpty();
}
/**
* Invalidates the session scoped cache of organisations.
*/
public void clearCache() {
organisations = new ArrayList<Organisation>();
}
public void load() throws RuntimeException {
LOG.debug("getting list of organisations from registry");
List<Organisation> tempOrganisations;
tempOrganisations = registryManager.getOrganisations();
// empty <option></option> needed by Select2 jquery library, to be able to display placeholder "Select an org.."
Organisation o = new Organisation();
o.setName("");
organisations.add(o);
organisations.addAll(tempOrganisations);
LOG.debug("organisations returned: " + organisations.size());
}
}
private static final long serialVersionUID = 7297470324204084809L;
private ResourceManager resourceManager;
private final OrganisationSupport organisationValidation;
private Organisation organisation;
private List<Organisation> linkedOrganisations;
private final RegisteredOrganisations orgSession;
private static final List<String> DOI_REGISTRATION_AGENCIES =
ImmutableList.of(DOIRegistrationAgency.DATACITE.name(), DOIRegistrationAgency.EZID.name());
@Inject
public OrganisationsAction(SimpleTextProvider textProvider, AppConfig cfg, RegistrationManager registrationManager,
OrganisationSupport organisationValidation, RegisteredOrganisations orgSession, ResourceManager resourceManager) {
super(textProvider, cfg, registrationManager);
this.organisationValidation = organisationValidation;
this.orgSession = orgSession;
this.resourceManager = resourceManager;
}
/**
* @return a list of DOI registration agencies that the IPT supports
*/
public List<String> getDoiRegistrationAgencies() {
return DOI_REGISTRATION_AGENCIES;
}
@Override
public String delete() {
try {
Organisation removedOrganisation = registrationManager.delete(id);
if (removedOrganisation == null) {
return NOT_FOUND;
}
// force a reload of the cached organisation
orgSession.clearCache();
registrationManager.save();
addActionMessage(getText("admin.organisation.deleted"));
return SUCCESS;
} catch (DeletionNotAllowedException e) {
if (e.getReason().equals(DeletionNotAllowedException.Reason.RESOURCE_REGISTERED_WITH_ORGANISATION)) {
addActionError(getText("admin.organisation.deleted.cant.resources"));
} else if (e.getReason().equals(DeletionNotAllowedException.Reason.IPT_REGISTERED_WITH_ORGANISATION)) {
addActionError(getText("admin.organisation.deleted.cant.ipt"));
}
addActionExceptionWarning(e);
} catch (IOException e) {
addActionError(getText("admin.organisation.cantSave") + ": " + e.getMessage());
}
return INPUT;
}
/**
* @return the linkedOrganisations
*/
public List<Organisation> getLinkedOrganisations() {
return linkedOrganisations;
}
/**
* @return the organisation
*/
public Organisation getOrganisation() {
return organisation;
}
/**
* @return all non-deleted registered organisations in GBIF Registry, excluding those already associated to the IPT.
*/
public List<Organisation> getOrganisations() {
List<Organisation> allOrganisations = orgSession.organisations;
for (Organisation linkedOrganisation : getLinkedOrganisations()) {
if (allOrganisations.contains(linkedOrganisation)) {
allOrganisations.remove(linkedOrganisation);
}
}
return allOrganisations;
}
public String getRegistryURL() {
return cfg.getRegistryUrl() + "/registry/";
}
/**
* @return the resourceManager
*/
public ResourceManager getResourceManager() {
return resourceManager;
}
public String list() {
return SUCCESS;
}
@Override
public void prepare() {
super.prepare();
// load orgs from registry if not done yet
if (!orgSession.isLoaded()) {
try {
orgSession.load();
} catch (RegistryException e) {
String msg = getText("admin.registration.error.registry");
LOG.error(msg, e);
addActionError(msg);
}
}
linkedOrganisations = registrationManager.listAll();
// remove default organisation named "no organisation" from list of editable organisations
for (Iterator<Organisation> iter = linkedOrganisations.listIterator(); iter.hasNext(); ) {
Organisation entry = iter.next();
if (entry.getKey().equals(Constants.DEFAULT_ORG_KEY)) {
iter.remove();
}
}
if (id == null) {
// if no id was submitted we wanted to create a new organisation
organisation = new Organisation();
} else {
if (!isHttpPost()) {
// load existing organisation from disk
Organisation fromDisk = registrationManager.getFromDisk(id);
organisation = new Organisation(fromDisk);
}
}
}
@Override
public String save() {
try {
if (id == null) {
if (registrationManager.get(organisation.getKey()) != null) {
LOG.error("The organisation association already exists");
addActionError(getText("admin.organisation.error.existing"));
return INPUT;
}
registrationManager.addAssociatedOrganisation(organisation);
addActionMessage(getText("admin.organisation.associated.ipt"));
} else {
// update associated organisations
registrationManager.addAssociatedOrganisation(organisation);
addActionMessage(getText("admin.organisation.updated.ipt"));
}
registrationManager.save();
return SUCCESS;
} catch (IOException e) {
LOG.error("The organisation association couldn't be saved: " + e.getMessage(), e);
addActionError(getText("admin.organisation.error.save"));
addActionError(e.getMessage());
return INPUT;
} catch (AlreadyExistingException e) {
addActionError(getText("admin.organisation.exists", new String[] {id}));
return INPUT;
}
}
/**
* @param linkedOrganisations the linkedOrganisations to set
*/
public void setLinkedOrganisations(List<Organisation> linkedOrganisations) {
this.linkedOrganisations = linkedOrganisations;
}
/**
* @param organisation the organisation to set
*/
public void setOrganisation(Organisation organisation) {
this.organisation = organisation;
}
/**
* @param resourceManager the resourceManager to set
*/
public void setResourceManager(ResourceManager resourceManager) {
this.resourceManager = resourceManager;
}
@Override
public void validate() {
if (isHttpPost()) {
boolean validated = true;
if (organisation.isAgencyAccountPrimary()) {
// ensure only one DOI account is selected as primary!
for (Organisation org : linkedOrganisations) {
if (!organisation.getKey().equals(org.getKey()) && org.isAgencyAccountPrimary()) {
validated = false;
addFieldError("organisation.agencyAccountPrimary",
getText("admin.organisation.doiAccount.activated.exists"));
break;
}
}
}
// if organisation doesn't validate prevent DOI account from being selected as primary!
if (!organisationValidation.validate(this, organisation)) {
validated = false;
} else {
if (organisation.isAgencyAccountPrimary()) {
// if organisation validated, and this account has been selected as the primary DOI account,
// make sure all existing DOIs comply with this account as its being saved
if (isAnotherAccountInUseAlready(organisation)) {
validated = false;
}
}
}
// be sure to deactivate account as primary DOI account in case validation failed
if (!validated) {
organisation.setAgencyAccountPrimary(false);
}
}
}
/**
* Make sure all DOIs in this IPT correspond to the account being saved. Otherwise, the user could switch the
* account type from DataCite to EZID, and render all DataCite DOIs unable to be updated.
*
* @return true if DOIs assigned using another account are found in the IPT, false otherwise
*/
@VisibleForTesting
protected boolean isAnotherAccountInUseAlready(Organisation organisation) {
// iterate through all resources, including deleted ones since they can be undeleted
for (Resource resource : resourceManager.list()) {
DOI doi = resource.getDoi();
// clone organisation being saved to new organisation to compare against organisation on disk
Organisation fromDisk = registrationManager.getFromDisk(organisation.getKey().toString());
if (doi != null && fromDisk != null) {
// the doi agency account must be the same
if (organisation.getDoiRegistrationAgency() != null && fromDisk.getDoiRegistrationAgency() != null) {
if (!organisation.getDoiRegistrationAgency().equals(fromDisk.getDoiRegistrationAgency())) {
String msg = getText("admin.organisation.doiAccount.differentTypeInUse",
new String[] {fromDisk.getDoiRegistrationAgency().toString().toLowerCase(), doi.toString()});
LOG.error(msg);
addActionError(msg);
return true;
}
}
}
}
return false;
}
}