/**
* =============================================================================
*
* ORCID (R) Open Source
* http://orcid.org
*
* Copyright (c) 2012-2014 ORCID, Inc.
* Licensed under an MIT-Style License (MIT)
* http://orcid.org/open-source-license
*
* This copyright and license information (including a link to the full license)
* shall be included in its entirety in all copies or substantial portion of
* the software.
*
* =============================================================================
*/
package org.orcid.core.manager.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import org.apache.commons.io.IOUtils;
import org.orcid.core.manager.NotificationManager;
import org.orcid.core.manager.OrcidSecurityManager;
import org.orcid.core.manager.OrgManager;
import org.orcid.core.manager.ProfileEntityCacheManager;
import org.orcid.core.manager.ProfileFundingManager;
import org.orcid.core.manager.SourceManager;
import org.orcid.core.manager.read_only.impl.ProfileFundingManagerReadOnlyImpl;
import org.orcid.core.manager.validator.ActivityValidator;
import org.orcid.core.utils.DisplayIndexCalculatorHelper;
import org.orcid.jaxb.model.common_v2.Visibility;
import org.orcid.jaxb.model.notification.amended_v2.AmendedSection;
import org.orcid.jaxb.model.notification.permission_v2.Item;
import org.orcid.jaxb.model.notification.permission_v2.ItemType;
import org.orcid.jaxb.model.record_v2.Funding;
import org.orcid.persistence.dao.FundingSubTypeToIndexDao;
import org.orcid.persistence.jpa.entities.OrgEntity;
import org.orcid.persistence.jpa.entities.ProfileEntity;
import org.orcid.persistence.jpa.entities.ProfileFundingEntity;
import org.orcid.persistence.jpa.entities.SourceEntity;
import org.orcid.utils.solr.entities.OrgDefinedFundingTypeSolrDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
public class ProfileFundingManagerImpl extends ProfileFundingManagerReadOnlyImpl implements ProfileFundingManager {
private static final Logger LOGGER = LoggerFactory.getLogger(ProfileFundingManagerImpl.class);
@Resource
private FundingSubTypeToIndexDao fundingSubTypeToIndexDao;
@Resource
private FundingSubTypeToIndexDao fundingSubTypeToIndexDaoReadOnly;
@Resource
private OrgManager orgManager;
@Resource
private SourceManager sourceManager;
@Resource
private OrcidSecurityManager orcidSecurityManager;
@Resource
private ActivityValidator activityValidator;
@Resource
private ProfileEntityCacheManager profileEntityCacheManager;
@Resource
private NotificationManager notificationManager;
/**
* Removes the relationship that exists between a funding and a profile.
*
* @param profileFundingId
* The id of the profileFunding that will be removed from the
* client profile
* @param clientOrcid
* The client orcid
* @return true if the relationship was deleted
* */
public boolean removeProfileFunding(String clientOrcid, Long profileFundingId) {
return profileFundingDao.removeProfileFunding(clientOrcid, profileFundingId);
}
/**
* Updates the visibility of an existing profile funding relationship
*
* @param clientOrcid
* The client orcid
*
* @param profileFundingId
* The id of the profile funding that will be updated
*
* @param visibility
* The new visibility value for the profile profileFunding object
*
* @return true if the relationship was updated
* */
public boolean updateProfileFundingVisibility(String clientOrcid, Long profileFundingId, Visibility visibility) {
return profileFundingDao.updateProfileFundingVisibility(clientOrcid, profileFundingId, visibility);
}
/**
* Add a new funding subtype to the list of pending for indexing subtypes
* */
public void addFundingSubType(String subtype, String orcid) {
fundingSubTypeToIndexDao.addSubTypes(subtype, orcid);
}
/**
* A process that will process all funding subtypes, filter and index them.
* */
public void indexFundingSubTypes() {
LOGGER.info("Indexing funding subtypes");
List<String> subtypes = fundingSubTypeToIndexDaoReadOnly.getSubTypes();
List<String> wordsToFilter = new ArrayList<String>();
try {
wordsToFilter = IOUtils.readLines(getClass().getResourceAsStream("words_to_filter.txt"));
} catch (IOException e) {
throw new RuntimeException("Problem reading words_to_filter.txt from classpath", e);
}
for(String subtype : subtypes) {
try {
boolean isInappropriate = false;
//All filter words are in lower case, so, lowercase the subtype before comparing
for(String wordToFilter : wordsToFilter) {
if(wordToFilter.matches(".*\\b" + Pattern.quote(subtype) + "\\b.*")) {
isInappropriate = true;
break;
}
}
if(!isInappropriate){
OrgDefinedFundingTypeSolrDocument document = new OrgDefinedFundingTypeSolrDocument();
document.setOrgDefinedFundingType(subtype);
fundingSubTypeSolrDao.persist(document);
} else {
LOGGER.warn("A word have been flaged as inappropiate: " + subtype);
}
fundingSubTypeToIndexDao.removeSubTypes(subtype);
} catch (Exception e) {
//If any exception happens, log the error and continue with the next one
LOGGER.warn("Unable to process subtype " + subtype, e);
}
}
LOGGER.info("Funding subtypes have been correcly indexed");
}
public boolean updateToMaxDisplay(String orcid, Long fundingId) {
return profileFundingDao.updateToMaxDisplay(orcid, fundingId);
}
/**
* Add a new funding to the given user
* @param orcid
* The user to add the funding
* @param funding
* The funding to add
* @return the added funding
* */
@Override
@Transactional
public Funding createFunding(String orcid, Funding funding, boolean isApiRequest) {
SourceEntity sourceEntity = sourceManager.retrieveSourceEntity();
activityValidator.validateFunding(funding, sourceEntity, true, isApiRequest, null);
//Check for duplicates
List<ProfileFundingEntity> existingFundings = profileFundingDao.getByUser(orcid);
List<Funding> fundings = jpaJaxbFundingAdapter.toFunding(existingFundings);
if(fundings != null && isApiRequest) {
for(Funding exstingFunding : fundings) {
activityValidator.checkFundingExternalIdentifiersForDuplicates(funding.getExternalIdentifiers(),
exstingFunding.getExternalIdentifiers(), exstingFunding.getSource(), sourceEntity);
}
}
ProfileFundingEntity profileFundingEntity = jpaJaxbFundingAdapter.toProfileFundingEntity(funding);
//Updates the give organization with the latest organization from database
OrgEntity updatedOrganization = orgManager.getOrgEntity(funding);
profileFundingEntity.setOrg(updatedOrganization);
//Set the source
if(sourceEntity.getSourceProfile() != null) {
profileFundingEntity.setSourceId(sourceEntity.getSourceProfile().getId());
}
if(sourceEntity.getSourceClient() != null) {
profileFundingEntity.setClientSourceId(sourceEntity.getSourceClient().getId());
}
ProfileEntity profile = profileEntityCacheManager.retrieve(orcid);
profileFundingEntity.setProfile(profile);
setIncomingWorkPrivacy(profileFundingEntity, profile);
DisplayIndexCalculatorHelper.setDisplayIndexOnNewEntity(profileFundingEntity, isApiRequest);
profileFundingDao.persist(profileFundingEntity);
profileFundingDao.flush();
if(isApiRequest) {
notificationManager.sendAmendEmail(orcid, AmendedSection.FUNDING, createItem(profileFundingEntity));
}
return jpaJaxbFundingAdapter.toFunding(profileFundingEntity);
}
private void setIncomingWorkPrivacy(ProfileFundingEntity profileFundingEntity, ProfileEntity profile) {
Visibility incomingWorkVisibility = profileFundingEntity.getVisibility();
Visibility defaultWorkVisibility = profile.getActivitiesVisibilityDefault();
if (profile.getClaimed()) {
profileFundingEntity.setVisibility(defaultWorkVisibility);
} else if (incomingWorkVisibility == null) {
profileFundingEntity.setVisibility(Visibility.PRIVATE);
}
}
/**
* Updates a funding that belongs to the given user
* @param orcid
* The user
* @param funding
* The funding to update
* @return the updated funding
* */
@Override
public Funding updateFunding(String orcid, Funding funding, boolean isApiRequest) {
SourceEntity sourceEntity = sourceManager.retrieveSourceEntity();
ProfileFundingEntity pfe = profileFundingDao.getProfileFunding(orcid, funding.getPutCode());
Visibility originalVisibility = pfe.getVisibility();
//Save the original source
String existingSourceId = pfe.getSourceId();
String existingClientSourceId = pfe.getClientSourceId();
activityValidator.validateFunding(funding, sourceEntity, false, isApiRequest, originalVisibility);
if(!isApiRequest) {
List<ProfileFundingEntity> existingFundings = profileFundingDao.getByUser(orcid);
for(ProfileFundingEntity existingFunding : existingFundings) {
Funding existing = jpaJaxbFundingAdapter.toFunding(existingFunding);
if(!existing.getPutCode().equals(funding.getPutCode())) {
activityValidator.checkFundingExternalIdentifiersForDuplicates(funding.getExternalIdentifiers(),
existing.getExternalIdentifiers(), existing.getSource(), sourceEntity);
}
}
}
orcidSecurityManager.checkSource(pfe);
jpaJaxbFundingAdapter.toProfileFundingEntity(funding, pfe);
pfe.setVisibility(originalVisibility);
//Be sure it doesn't overwrite the source
pfe.setSourceId(existingSourceId);
pfe.setClientSourceId(existingClientSourceId);
//Updates the give organization with the latest organization from database, or, create a new one
OrgEntity updatedOrganization = orgManager.getOrgEntity(funding);
pfe.setOrg(updatedOrganization);
pfe = profileFundingDao.merge(pfe);
profileFundingDao.flush();
if(!isApiRequest) {
notificationManager.sendAmendEmail(orcid, AmendedSection.FUNDING, createItem(pfe));
}
return jpaJaxbFundingAdapter.toFunding(pfe);
}
/**
* Deletes a given funding, if and only if, the client that requested the delete is the source of the funding
* @param orcid
* the funding owner
* @param fundingId
* The funding id
* @return true if the funding was deleted, false otherwise
* */
@Override
@Transactional
public boolean checkSourceAndDelete(String orcid, Long fundingId) {
ProfileFundingEntity pfe = profileFundingDao.getProfileFunding(orcid, fundingId);
orcidSecurityManager.checkSource(pfe);
Item item = createItem(pfe);
boolean result = profileFundingDao.removeProfileFunding(orcid, fundingId);
notificationManager.sendAmendEmail(orcid, AmendedSection.FUNDING, item);
return result;
}
private Item createItem(ProfileFundingEntity profileFundingEntity) {
Item item = new Item();
item.setItemName(profileFundingEntity.getTitle());
item.setItemType(ItemType.FUNDING);
item.setPutCode(String.valueOf(profileFundingEntity.getId()));
return item;
}
}