/** * ============================================================================= * * 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.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParsePosition; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.FutureTask; import javax.annotation.Resource; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.orcid.core.adapter.Jaxb2JpaAdapter; import org.orcid.core.constants.DefaultPreferences; import org.orcid.core.exception.ExceedMaxNumberOfElementsException; import org.orcid.core.manager.LoadOptions; import org.orcid.core.manager.NotificationManager; import org.orcid.core.manager.OrcidGenerationManager; import org.orcid.core.manager.OrcidIndexManager; import org.orcid.core.manager.OrcidJaxbCopyManager; import org.orcid.core.manager.OrcidProfileCleaner; import org.orcid.core.manager.OrcidProfileManager; import org.orcid.core.manager.OrgManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.RecordNameManager; import org.orcid.core.manager.UpdateOptions; import org.orcid.core.security.OrcidWebRole; import org.orcid.core.security.visibility.OrcidVisibilityDefaults; import org.orcid.jaxb.model.message.ActivitiesContainer; import org.orcid.jaxb.model.message.Activity; import org.orcid.jaxb.model.message.Affiliation; import org.orcid.jaxb.model.message.Affiliations; import org.orcid.jaxb.model.message.Biography; import org.orcid.jaxb.model.message.ContactDetails; import org.orcid.jaxb.model.message.Contributor; import org.orcid.jaxb.model.message.ContributorOrcid; import org.orcid.jaxb.model.message.Country; import org.orcid.jaxb.model.message.CreditName; import org.orcid.jaxb.model.message.DeactivationDate; import org.orcid.jaxb.model.message.Email; import org.orcid.jaxb.model.message.EncryptedPassword; import org.orcid.jaxb.model.message.EncryptedSecurityAnswer; import org.orcid.jaxb.model.message.ExternalIdentifier; import org.orcid.jaxb.model.message.ExternalIdentifiers; import org.orcid.jaxb.model.message.FamilyName; import org.orcid.jaxb.model.message.Funding; import org.orcid.jaxb.model.message.FundingList; import org.orcid.jaxb.model.message.GivenNames; import org.orcid.jaxb.model.message.Keyword; import org.orcid.jaxb.model.message.Keywords; import org.orcid.jaxb.model.message.OrcidActivities; import org.orcid.jaxb.model.message.OrcidBio; import org.orcid.jaxb.model.message.OrcidHistory; import org.orcid.jaxb.model.message.OrcidInternal; import org.orcid.jaxb.model.message.OrcidProfile; import org.orcid.jaxb.model.message.OrcidType; import org.orcid.jaxb.model.message.OrcidWork; import org.orcid.jaxb.model.message.OrcidWorks; import org.orcid.jaxb.model.message.Organization; import org.orcid.jaxb.model.message.OtherName; import org.orcid.jaxb.model.message.OtherNames; import org.orcid.jaxb.model.message.PersonalDetails; import org.orcid.jaxb.model.message.Preferences; import org.orcid.jaxb.model.message.ResearcherUrl; import org.orcid.jaxb.model.message.ResearcherUrls; import org.orcid.jaxb.model.message.SecurityDetails; import org.orcid.jaxb.model.message.SecurityQuestionId; import org.orcid.jaxb.model.message.Source; import org.orcid.jaxb.model.message.SourceClientId; import org.orcid.jaxb.model.message.SourceOrcid; import org.orcid.jaxb.model.message.Title; import org.orcid.jaxb.model.message.Visibility; import org.orcid.jaxb.model.message.VisibilityType; import org.orcid.jaxb.model.message.WorkContributors; import org.orcid.jaxb.model.message.WorkExternalIdentifier; import org.orcid.jaxb.model.message.WorkExternalIdentifierType; import org.orcid.jaxb.model.message.WorkType; 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.persistence.dao.EmailDao; import org.orcid.persistence.dao.GenericDao; import org.orcid.persistence.dao.GivenPermissionToDao; import org.orcid.persistence.dao.OrcidOauth2TokenDetailDao; import org.orcid.persistence.dao.OrgAffiliationRelationDao; import org.orcid.persistence.dao.ProfileDao; import org.orcid.persistence.dao.ProfileFundingDao; import org.orcid.persistence.dao.UserConnectionDao; import org.orcid.persistence.dao.WorkDao; import org.orcid.persistence.jpa.entities.EmailEntity; import org.orcid.persistence.jpa.entities.EmailEventEntity; import org.orcid.persistence.jpa.entities.EmailEventType; import org.orcid.persistence.jpa.entities.IndexingStatus; import org.orcid.persistence.jpa.entities.OrcidGrantedAuthority; import org.orcid.persistence.jpa.entities.OrgAffiliationRelationEntity; 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.WorkEntity; import org.orcid.persistence.messaging.JmsMessageSender; import org.orcid.persistence.messaging.JmsMessageSender.JmsDestination; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.utils.DateUtils; import org.orcid.utils.OrcidStringUtils; import org.orcid.utils.listener.LastModifiedMessage; import org.springframework.beans.factory.annotation.Value; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import net.sf.ehcache.Element; /** * The profile manager is responsible for passing onto the persistence layer * after performing any data manipulation necessary before doing so. * <p/> * One of its functions is to persist visibilty options set by the user and/or * client. As this is VERY sensitive it is implied that any security has been * performed already, thus if a method is called within this class it is will * first check for visibility within the updated object, and persist them. If * none are found in the updated object, the existing visibility options are * used. If neither are present, the defaults are used. * * @author Declan Newman and Will Simpson */ @Deprecated public class OrcidProfileManagerImpl extends OrcidProfileManagerReadOnlyImpl implements OrcidProfileManager { private static final int INDEXING_BATCH_SIZE = 100; @Resource private OrcidGenerationManager orcidGenerationManager; @Resource private ProfileDao profileDao; @Resource private ProfileDao profileDaoReadOnly; @Resource private OrgAffiliationRelationDao orgAffiliationRelationDao; @Resource private ProfileFundingDao profileFundingDao; @Resource private EmailDao emailDao; @Resource private GivenPermissionToDao givenPermissionToDao; @Resource private OrcidOauth2TokenDetailDao orcidOauth2TokenDetailDao; @Resource private Jaxb2JpaAdapter jaxb2JpaAdapter; @Resource private OrcidIndexManager orcidIndexManager; @Resource private TransactionTemplate transactionTemplate; @Resource private NotificationManager notificationManager; @Resource private OrcidProfileCleaner orcidProfileCleaner; @Resource private GenericDao<EmailEventEntity, Long> emailEventDao; @Resource private OrcidJaxbCopyManager orcidJaxbCopyManager; @Resource private ProfileEntityManager profileEntityManager; @Resource private WorkDao workDao; @Resource private OrgManager orgManager; @Resource private UserConnectionDao userConnectionDao; @Resource private RecordNameManager recordNameManager; @Value("${org.orcid.core.works.compare.useScopusWay:false}") private boolean compareWorksUsingScopusWay; @Resource private JmsMessageSender messaging; private int claimReminderAfterDays = 8; private int verifyReminderAfterDays = 7; @Value("${org.orcid.core.activities.max:10000}") private long maxNumOfActivities; public NotificationManager getNotificationManager() { return notificationManager; } public void setNotificationManager(NotificationManager notificationManager) { this.notificationManager = notificationManager; } public void setOrcidIndexManager(OrcidIndexManager orcidIndexManager) { this.orcidIndexManager = orcidIndexManager; } public void setClaimReminderAfterDays(int claimReminderAfterDays) { this.claimReminderAfterDays = claimReminderAfterDays; } @Override public void setCompareWorksUsingScopusWay(boolean compareWorksUsingScopusWay) { this.compareWorksUsingScopusWay = compareWorksUsingScopusWay; } @Override @Transactional public OrcidProfile createOrcidProfile(OrcidProfile orcidProfile, boolean createdByMember, boolean usedCaptcha) { if (orcidProfile.getOrcidIdentifier() == null) { orcidProfile.setOrcidIdentifier(orcidGenerationManager.createNewOrcid()); } // Add source to works and affiliations String amenderOrcid = sourceManager.retrieveSourceOrcid(); addSourceToEmails(orcidProfile, amenderOrcid); addSourceToWorks(orcidProfile, amenderOrcid); addSourceToAffiliations(orcidProfile, amenderOrcid); addSourceToFundings(orcidProfile, amenderOrcid); Visibility defaultVisibility = null; if (orcidProfile.getOrcidInternal() !=null && orcidProfile.getOrcidInternal().getPreferences() !=null && orcidProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault() != null){ defaultVisibility = orcidProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault().getValue(); } //If it is created by member, it is not claimed addDefaultVisibilityToBioItems(orcidProfile, defaultVisibility, !createdByMember); ProfileEntity profileEntity = adapter.toProfileEntity(orcidProfile); profileEntity.setUsedRecaptchaOnRegistration(usedCaptcha); encryptAndMapFieldsForProfileEntityPersistence(orcidProfile, profileEntity); profileEntity.setAuthorities(getGrantedAuthorities(profileEntity)); setDefaultVisibility(profileEntity, createdByMember, defaultVisibility); profileDao.persist(profileEntity); profileDao.flush(); OrcidProfile updatedTranslatedOrcid = adapter.toOrcidProfile(profileEntity); return updatedTranslatedOrcid; } private void addDefaultVisibilityToBioItems(OrcidProfile orcidProfile, Visibility defaultActivityVis, Boolean isClaimed) { if (defaultActivityVis == null) { defaultActivityVis = Visibility.PRIVATE; } if(isClaimed == null) { isClaimed = false; } if (orcidProfile.getOrcidBio() != null) { if (orcidProfile.getOrcidBio().getBiography() != null) { if (isClaimed) { orcidProfile.getOrcidBio().getBiography().setVisibility(defaultActivityVis); } else { Visibility visibility = orcidProfile.getOrcidBio().getBiography().getVisibility(); orcidProfile.getOrcidBio().getBiography().setVisibility(visibility != null ? visibility : Visibility.PRIVATE); } } if (orcidProfile.getOrcidBio().getExternalIdentifiers() != null) { Visibility listVisibility = orcidProfile.getOrcidBio().getExternalIdentifiers().getVisibility(); for (ExternalIdentifier x : orcidProfile.getOrcidBio().getExternalIdentifiers().getExternalIdentifier()) { if (isClaimed) { x.setVisibility(defaultActivityVis); } else { x.setVisibility(listVisibility != null ? listVisibility : Visibility.PRIVATE); } } } if (orcidProfile.getOrcidBio().getKeywords() != null) { Visibility listVisibility = orcidProfile.getOrcidBio().getKeywords().getVisibility(); for (Keyword x : orcidProfile.getOrcidBio().getKeywords().getKeyword()) { if (isClaimed) { x.setVisibility(defaultActivityVis); } else { x.setVisibility(listVisibility != null ? listVisibility : Visibility.PRIVATE); } } } if (orcidProfile.getOrcidBio().getResearcherUrls() != null) { Visibility listVisibility = orcidProfile.getOrcidBio().getResearcherUrls().getVisibility(); for (ResearcherUrl x : orcidProfile.getOrcidBio().getResearcherUrls().getResearcherUrl()) { if (isClaimed) { x.setVisibility(defaultActivityVis); } else { x.setVisibility(listVisibility != null ? listVisibility : Visibility.PRIVATE); } } } if (orcidProfile.getOrcidBio().getPersonalDetails() != null && orcidProfile.getOrcidBio().getPersonalDetails().getOtherNames() != null) { Visibility listVisibility = orcidProfile.getOrcidBio().getPersonalDetails().getOtherNames().getVisibility(); for (OtherName x : orcidProfile.getOrcidBio().getPersonalDetails().getOtherNames().getOtherName()) { if (isClaimed) { x.setVisibility(defaultActivityVis); } else { x.setVisibility(listVisibility != null ? listVisibility : Visibility.PRIVATE); } } } if (orcidProfile.getOrcidBio().getContactDetails() != null && orcidProfile.getOrcidBio().getContactDetails().getAddress() != null && orcidProfile.getOrcidBio().getContactDetails().getAddress().getCountry() != null) { Country country = orcidProfile.getOrcidBio().getContactDetails().getAddress().getCountry(); if (isClaimed) { country.setVisibility(defaultActivityVis); } else { country.setVisibility(country.getVisibility() != null ? country.getVisibility() : Visibility.PRIVATE); } } } } @Override public OrcidProfile createOrcidProfileAndNotify(OrcidProfile orcidProfile) { OrcidProfile createdOrcidProfile = createOrcidProfile(orcidProfile, true, false); notificationManager.sendApiRecordCreationEmail(orcidProfile.getOrcidBio().getContactDetails().retrievePrimaryEmail().getValue(), orcidProfile); return createdOrcidProfile; } @Override @Transactional public OrcidProfile updateOrcidProfile(OrcidProfile orcidProfile) { return updateOrcidProfile(orcidProfile, UpdateOptions.ALL); } @Override @Transactional public OrcidProfile updateOrcidProfile(OrcidProfile orcidProfile, UpdateOptions updateOptions) { String amenderOrcid = sourceManager.retrieveSourceOrcid(); ProfileEntity existingProfileEntity = profileDao.find(orcidProfile.getOrcidIdentifier().getPath()); Visibility defaultVisibility = Visibility.fromValue(existingProfileEntity.getActivitiesVisibilityDefault().value()); if (existingProfileEntity != null) { //Dont delete the existing elements anymore //profileDao.removeChildrenWithGeneratedIds(existingProfileEntity); setWorkPrivacy(orcidProfile, defaultVisibility); setAffiliationPrivacy(orcidProfile, defaultVisibility); setFundingPrivacy(orcidProfile, defaultVisibility); } dedupeWorks(orcidProfile); dedupeAffiliations(orcidProfile); dedupeFundings(orcidProfile); addSourceToEmails(orcidProfile, existingProfileEntity, amenderOrcid); Boolean claimed = orcidProfile.getOrcidHistory() != null ? orcidProfile.getOrcidHistory().isClaimed() : existingProfileEntity.getClaimed(); if (orcidProfile.getOrcidInternal() !=null && orcidProfile.getOrcidInternal().getPreferences() !=null && orcidProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault() !=null){ defaultVisibility = orcidProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault().getValue(); } addDefaultVisibilityToBioItems(orcidProfile, defaultVisibility, claimed); ProfileEntity profileEntity = adapter.toProfileEntity(orcidProfile, existingProfileEntity, updateOptions); profileEntity.setLastModified(new Date()); profileEntity.setIndexingStatus(IndexingStatus.PENDING); profileDao.flush(); ProfileEntity updatedProfileEntity = profileDao.merge(profileEntity); profileDao.refresh(updatedProfileEntity); OrcidProfile updatedOrcidProfile = convertToOrcidProfile(updatedProfileEntity, LoadOptions.ALL); orcidProfileCacheManager.put(updatedOrcidProfile); return updatedOrcidProfile; } /** * Preserves existing source for existing emails, and adds specified source * for new emails * * @param orcidProfile * The incoming profile * @param existingProfileEntity * The existing profile entity from the DB * @param amenderOrcid * The source of new emails (from the security context) */ private void addSourceToEmails(OrcidProfile orcidProfile, ProfileEntity existingProfileEntity, String amenderOrcid) { Map<String, EmailEntity> existingMap = new HashMap<>(); Set<EmailEntity> existingEmails = existingProfileEntity.getEmails(); if (existingEmails != null) { existingMap = EmailEntity.mapByLowerCaseEmail(existingEmails); } OrcidBio orcidBio = orcidProfile.getOrcidBio(); if (orcidBio != null) { ContactDetails contactDetails = orcidBio.getContactDetails(); if (contactDetails != null) { for (Email email : contactDetails.getEmail()) { EmailEntity existingEmail = existingMap.get(email.getValue().toLowerCase()); if (existingEmail == null) { if (OrcidStringUtils.isValidOrcid(amenderOrcid)) { email.setSourceClientId(amenderOrcid); } else { email.setSource(amenderOrcid); } } else { email.setSource(existingEmail.getSourceId()); email.setSourceClientId(existingEmail.getClientSourceId()); } } } } } /** * Add source to each element in the bio that doesnt comes with a source * * @param orcidProfile * The profile * @param amenderOrcid * The orcid of the user or client that add the email to the * profile user * */ private void addSourceToBioElements(OrcidProfile orcidProfile, String amenderOrcid) { Source source = createSource(amenderOrcid); if(orcidProfile != null && orcidProfile.getOrcidBio() != null) { OrcidBio bio = orcidProfile.getOrcidBio(); //Other names if (bio.getPersonalDetails() != null && bio.getPersonalDetails().getOtherNames() != null && bio.getPersonalDetails().getOtherNames().getOtherName() != null && !bio.getPersonalDetails().getOtherNames().getOtherName().isEmpty()) { for(OtherName otherName : bio.getPersonalDetails().getOtherNames().getOtherName()) { if(otherName.getSource() == null || PojoUtil.isEmpty(otherName.getSource().retrieveSourcePath())) { otherName.setSource(source); } } } //Address if(bio.getContactDetails() != null && bio.getContactDetails().getAddress() != null && bio.getContactDetails().getAddress().getCountry() != null) { Country country = bio.getContactDetails().getAddress().getCountry(); if(country.getSource() == null || PojoUtil.isEmpty(country.getSource().retrieveSourcePath())) { country.setSource(source); } } //Keywords if(bio.getKeywords() != null && bio.getKeywords().getKeyword() != null && !bio.getKeywords().getKeyword().isEmpty()) { Keywords keywords = bio.getKeywords(); for(Keyword keyword : keywords.getKeyword()) { if (keyword.getSource() == null || PojoUtil.isEmpty(keyword.getSource().retrieveSourcePath())) { keyword.setSource(source); } } } //Researcher urls if(bio.getResearcherUrls() != null && bio.getResearcherUrls().getResearcherUrl() != null && !bio.getResearcherUrls().getResearcherUrl().isEmpty()) { ResearcherUrls rUrls = bio.getResearcherUrls(); for(ResearcherUrl rUrl : rUrls.getResearcherUrl()) { if(rUrl.getSource() == null || PojoUtil.isEmpty(rUrl.getSource().retrieveSourcePath())) { rUrl.setSource(source); } } } //External identifiers if (bio.getExternalIdentifiers() != null && bio.getExternalIdentifiers().getExternalIdentifier() != null && !bio.getExternalIdentifiers().getExternalIdentifier().isEmpty()) { for (ExternalIdentifier extId : bio.getExternalIdentifiers().getExternalIdentifier()) { if (extId.getSource() == null || PojoUtil.isEmpty(extId.getSource().retrieveSourcePath())) { extId.setSource(source); } } } } } /** * Add source to the profile emails * * @param orcidProfile * The profile * @param amenderOrcid * The orcid of the user or client that add the email to the * profile user * */ private void addSourceToEmails(OrcidProfile orcidProfile, String amenderOrcid) { if (orcidProfile != null && orcidProfile.getOrcidBio() != null && orcidProfile.getOrcidBio().getContactDetails() != null && orcidProfile.getOrcidBio().getContactDetails().getEmail() != null) { for (Email email : orcidProfile.getOrcidBio().getContactDetails().getEmail()) { if (OrcidStringUtils.isValidOrcid(amenderOrcid)) { email.setSource(amenderOrcid); } else { email.setSourceClientId(amenderOrcid); } } } } /** * Add source to the profile works * * @param orcidProfile * The profile * @param amenderOrcid * The orcid of the user or client that add the work to the * profile user * */ private void addSourceToWorks(OrcidProfile orcidProfile, String amenderOrcid) { OrcidWorks orcidWorks = orcidProfile.getOrcidActivities() == null ? null : orcidProfile.getOrcidActivities().getOrcidWorks(); addSourceToWorks(orcidWorks, amenderOrcid); } private void addSourceToWorks(OrcidWorks orcidWorks, String amenderOrcid) { if (orcidWorks != null && !orcidWorks.getOrcidWork().isEmpty() && amenderOrcid != null) { for (OrcidWork orcidWork : orcidWorks.getOrcidWork()) { Source source = createSource(amenderOrcid); orcidWork.setSource(source); } } } private Source createSource(String amenderOrcid) { Source source = new Source(); if (OrcidStringUtils.isValidOrcid(amenderOrcid)) { source.setSourceOrcid(new SourceOrcid(amenderOrcid)); source.setSourceClientId(null); } else { source.setSourceClientId(new SourceClientId(amenderOrcid)); source.setSourceOrcid(null); } return source; } /** * Add source to the affiliations * * @param orcidProfile * The profile * @param amenderOrcid * The orcid of the user or client that is adding the affiliation * to the profile user * */ private void addSourceToAffiliations(OrcidProfile orcidProfile, String amenderOrcid) { Affiliations affiliations = orcidProfile.getOrcidActivities() == null ? null : orcidProfile.getOrcidActivities().getAffiliations(); if (affiliations != null && !affiliations.getAffiliation().isEmpty()) { for (Affiliation affiliation : affiliations.getAffiliation()) { if (affiliation.getSource() == null || StringUtils.isEmpty(affiliation.retrieveSourcePath())) affiliation.setSource(new Source(amenderOrcid)); } } } @Deprecated public boolean exists(String orcid) { return profileDao.exists(orcid); } /** * Add source to the fundings * * @param orcidProfile * The profile * @param amenderOrcid * The orcid of the user or client that is adding the fundings to * the profile user * */ private void addSourceToFundings(OrcidProfile orcidProfile, String amenderOrcid) { FundingList fundings = orcidProfile.getOrcidActivities() == null ? null : orcidProfile.getOrcidActivities().getFundings(); if (fundings != null && !fundings.getFundings().isEmpty()) { for (Funding funding : fundings.getFundings()) { if (funding.getSource() == null || StringUtils.isEmpty(funding.retrieveSourcePath())) funding.setSource(new Source(amenderOrcid)); } } } private void setWorkPrivacy(OrcidProfile updatedOrcidProfile, Visibility defaultWorkVisibility) { OrcidHistory orcidHistory = updatedOrcidProfile.getOrcidHistory(); boolean isClaimed = orcidHistory != null ? orcidHistory.getClaimed().isValue() : false; OrcidActivities incomingActivities = updatedOrcidProfile.getOrcidActivities(); if (incomingActivities != null) { OrcidWorks incomingWorks = incomingActivities.getOrcidWorks(); if (incomingWorks != null) { setWorkPrivacy(incomingWorks, defaultWorkVisibility, isClaimed); } } } private void setWorkPrivacy(OrcidWorks incomingWorks, Visibility defaultWorkVisibility, boolean isClaimed) { for (OrcidWork incomingWork : incomingWorks.getOrcidWork()) { if (StringUtils.isBlank(incomingWork.getPutCode())) choosePrivacy(incomingWork, defaultWorkVisibility, isClaimed); } } private void choosePrivacy(Activity act, Visibility defaultWorkVisibility, boolean isClaimed) { if (isClaimed) act.setVisibility(defaultWorkVisibility); else act.setVisibility(act.getVisibility() !=null ? act.getVisibility():Visibility.PRIVATE); } @Override @Transactional public OrcidProfile retrieveOrcidProfileByEmail(String email) { return retrieveOrcidProfileByEmail(email, LoadOptions.ALL); } @Override @Transactional public OrcidProfile retrieveOrcidProfileByEmail(String email, LoadOptions loadOptions) { EmailEntity emailEntity = emailDao.findCaseInsensitive(email); if (emailEntity != null) { ProfileEntity profileEntity = emailEntity.getProfile(); OrcidProfile orcidProfile = adapter.toOrcidProfile(profileEntity, loadOptions); String verificationCode = profileEntity.getEncryptedVerificationCode(); String securityAnswer = profileEntity.getEncryptedSecurityAnswer(); orcidProfile.setVerificationCode(decrypt(verificationCode)); orcidProfile.setSecurityQuestionAnswer(decrypt(securityAnswer)); return orcidProfile; } else { return null; } } /** * Updates the ORCID works only * * @param updatedOrcidProfile * @return */ @Override @Transactional public OrcidProfile updateOrcidWorks(OrcidProfile updatedOrcidProfile) { OrcidProfile existingProfile = retrieveOrcidProfile(updatedOrcidProfile.getOrcidIdentifier().getPath()); if (existingProfile == null) { return null; } OrcidActivities updatedActivities = updatedOrcidProfile.getOrcidActivities(); if (updatedActivities == null) { return null; } OrcidWorks updatedOrcidWorks = updatedActivities.getOrcidWorks(); if (updatedOrcidWorks == null) { return null; } OrcidActivities existingActivities = existingProfile.getOrcidActivities(); if (existingActivities == null) { existingActivities = new OrcidActivities(); existingProfile.setOrcidActivities(existingActivities); } OrcidWorks existingOrcidWorks = existingActivities.getOrcidWorks(); if (existingOrcidWorks == null) { existingOrcidWorks = new OrcidWorks(); existingActivities.setOrcidWorks(existingOrcidWorks); } checkUserCanHoldMoreElement(existingProfile.retrieveOrcidWorks(), updatedOrcidProfile.retrieveOrcidWorks()); orcidJaxbCopyManager.copyUpdatedWorksPreservingVisbility(existingProfile.retrieveOrcidWorks(), updatedOrcidProfile.retrieveOrcidWorks()); OrcidProfile profileToReturn = updateOrcidProfile(existingProfile); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.WORK); return profileToReturn; } private void checkUserCanHoldMoreElement(ActivitiesContainer existingActivities, ActivitiesContainer updatedActivities) { long activitiesCount = 0; if(existingActivities != null) { if(existingActivities.retrieveActivities() != null) { activitiesCount = existingActivities.retrieveActivities().size(); } } if(activitiesCount > maxNumOfActivities) { throw new ExceedMaxNumberOfElementsException(); } if(updatedActivities != null) { if(updatedActivities.retrieveActivities() != null) { Collection<? extends Activity> elements = updatedActivities.retrieveActivities(); Iterator<? extends Activity> elementsIt = elements.iterator(); if(elementsIt != null) { while(elementsIt.hasNext()) { Activity activity = elementsIt.next(); if(activity != null && PojoUtil.isEmpty(activity.getPutCode())) { activitiesCount += 1; if(activitiesCount > maxNumOfActivities) { throw new ExceedMaxNumberOfElementsException(); } } } } } } } /** * Add new external identifiers to an existing profile * * @param updatedOrcidProfile * @return */ @Override @Transactional public OrcidProfile addExternalIdentifiers(OrcidProfile updatedOrcidProfile) { OrcidProfile existingProfile = retrieveOrcidProfile(updatedOrcidProfile.getOrcidIdentifier().getPath()); if (existingProfile != null && existingProfile.getOrcidBio() != null) { OrcidBio orcidBio = existingProfile.getOrcidBio(); ExternalIdentifiers externalIdentifiers = orcidBio.getExternalIdentifiers(); if (externalIdentifiers == null) { orcidBio.setExternalIdentifiers(new ExternalIdentifiers()); } ExternalIdentifiers externalIdentifier = updatedOrcidProfile.getOrcidBio().getExternalIdentifiers(); List<ExternalIdentifier> updatedExternalIdentifiers = externalIdentifier.getExternalIdentifier(); List<ExternalIdentifier> existingExternalIdentifiers = orcidBio.getExternalIdentifiers().getExternalIdentifier(); // Copy all the existing external identifiers to the updated profile for (ExternalIdentifier ei : existingExternalIdentifiers) { updatedExternalIdentifiers.add(ei); } orcidJaxbCopyManager.copyUpdatedExternalIdentifiersToExistingPreservingVisibility(orcidBio, updatedOrcidProfile.getOrcidBio()); OrcidProfile profileToReturn = updateOrcidProfile(existingProfile); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.EXTERNAL_IDENTIFIERS); return profileToReturn; } else { return null; } } /** * Updates the ORCID bio data * * @param updatedOrcidProfile * @return */ @Deprecated @Override @Transactional public OrcidProfile updateOrcidBio(OrcidProfile updatedOrcidProfile) { addSourceToBioElements(updatedOrcidProfile, sourceManager.retrieveSourceOrcid()); OrcidProfile existingProfile = retrieveOrcidProfile(updatedOrcidProfile.getOrcidIdentifier().getPath()); if (existingProfile == null) { return null; } // preserve the visibility settings orcidJaxbCopyManager.copyUpdatedBioToExistingWithVisibility(existingProfile.getOrcidBio(), updatedOrcidProfile.getOrcidBio()); OrcidProfile profileToReturn = updateOrcidProfile(existingProfile, UpdateOptions.NO_ACTIVITIES); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.BIO); return profileToReturn; } @Override @Transactional public OrcidProfile updateAffiliations(OrcidProfile updatedOrcidProfile) { OrcidProfile existingProfile = retrieveOrcidProfile(updatedOrcidProfile.getOrcidIdentifier().getPath()); if (existingProfile == null) { return null; } OrcidActivities updatedActivities = updatedOrcidProfile.getOrcidActivities(); if (updatedActivities == null) { return null; } Affiliations updatedAffiliations = updatedActivities.getAffiliations(); if (updatedAffiliations == null) { return null; } OrcidActivities existingActivities = existingProfile.getOrcidActivities(); if (existingActivities == null) { existingActivities = new OrcidActivities(); existingProfile.setOrcidActivities(existingActivities); } Affiliations existingAffiliations = existingActivities.getAffiliations(); if (existingAffiliations == null) { existingAffiliations = new Affiliations(); existingActivities.setAffiliations(existingAffiliations); } orcidJaxbCopyManager.copyAffiliationsToExistingPreservingVisibility(existingAffiliations, updatedAffiliations); OrcidProfile profileToReturn = updateOrcidProfile(existingProfile, UpdateOptions.AFFILIATIONS_ONLY); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.AFFILIATION); return profileToReturn; } @Override @Transactional public OrcidProfile updateFundings(OrcidProfile updatedOrcidProfile) { OrcidProfile existingProfile = retrieveOrcidProfile(updatedOrcidProfile.getOrcidIdentifier().getPath()); if (existingProfile == null) { return null; } FundingList updatedFundingList = updatedOrcidProfile.retrieveFundings(); if (updatedFundingList == null) { return null; } else { // Parse the amount in the new funding setFundingAmountsWithTheCorrectFormat(updatedOrcidProfile); // Update the funding list with the new values updatedFundingList = updatedOrcidProfile.retrieveFundings(); } OrcidActivities existingActivities = existingProfile.getOrcidActivities(); if (existingActivities == null) { existingActivities = new OrcidActivities(); existingProfile.setOrcidActivities(existingActivities); } FundingList existingFundingList = existingActivities.getFundings(); if (existingFundingList == null) { existingFundingList = new FundingList(); existingActivities.setFundings(existingFundingList); } orcidJaxbCopyManager.copyFundingListToExistingPreservingVisibility(existingFundingList, updatedFundingList); OrcidProfile profileToReturn = updateOrcidProfile(existingProfile, UpdateOptions.FUNDINGS_ONLY); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.FUNDING); return profileToReturn; } @Override @Transactional public void updatePasswordInformation(OrcidProfile updatedOrcidProfile) { String orcid = updatedOrcidProfile.getOrcidIdentifier().getPath(); String hashedPassword = hash(updatedOrcidProfile.getPassword()); profileDao.updateEncryptedPassword(orcid, hashedPassword); OrcidProfile cachedProfile = orcidProfileCacheManager.retrieve(orcid); if (cachedProfile != null) { profileDao.flush(); SecurityDetails securityDetails = initSecurityDetails(cachedProfile); securityDetails.setEncryptedPassword(new EncryptedPassword(hashedPassword)); cachedProfile.setPassword(hashedPassword); orcidProfileCacheManager.put(cachedProfile); } updateSecurityQuestionInformation(updatedOrcidProfile); } private SecurityDetails initSecurityDetails(OrcidProfile cachedProfile) { OrcidInternal internal = cachedProfile.getOrcidInternal(); if (internal == null) { internal = new OrcidInternal(); cachedProfile.setOrcidInternal(internal); } SecurityDetails securityDetails = internal.getSecurityDetails(); if (securityDetails == null) { securityDetails = new SecurityDetails(); internal.setSecurityDetails(securityDetails); } return securityDetails; } @Override public void updateSecurityQuestionInformation(OrcidProfile updatedOrcidProfile) { String orcid = updatedOrcidProfile.getOrcidIdentifier().getPath(); SecurityQuestionId securityQuestionId = updatedOrcidProfile.getOrcidInternal().getSecurityDetails().getSecurityQuestionId(); Integer questionId = null; if (securityQuestionId != null) { questionId = new Long(securityQuestionId.getValue()).intValue(); } String unencryptedAnswer = updatedOrcidProfile.getSecurityQuestionAnswer(); String encryptedAnswer = encrypt(unencryptedAnswer); profileDao.updateSecurityQuestion(orcid, questionId, questionId != null ? encryptedAnswer : null); OrcidProfile cachedProfile = orcidProfileCacheManager.retrieve(orcid); if (cachedProfile != null) { profileDao.flush(); SecurityDetails securityDetails = initSecurityDetails(cachedProfile); securityDetails.setSecurityQuestionId(questionId != null ? new SecurityQuestionId(questionId) : null); securityDetails.setEncryptedSecurityAnswer(encryptedAnswer != null ? new EncryptedSecurityAnswer(encryptedAnswer) : null); cachedProfile.setSecurityQuestionAnswer(encryptedAnswer != null ? unencryptedAnswer : null); orcidProfileCacheManager.put(cachedProfile); } } @Override @Transactional public void updatePreferences(String orcid, Preferences preferences) { boolean sendChangeNotifications = preferences.getSendChangeNotifications() == null ? DefaultPreferences.SEND_CHANGE_NOTIFICATIONS_DEFAULT : preferences .getSendChangeNotifications().isValue(); boolean sendAdministrativeChangeNotifications = preferences.getSendAdministrativeChangeNotifications() == null ? sendChangeNotifications : preferences .getSendAdministrativeChangeNotifications().isValue(); boolean sendOrcidNews = preferences.getSendOrcidNews() == null ? DefaultPreferences.SEND_ORCID_NEWS_DEFAULT : preferences.getSendOrcidNews().isValue(); boolean sendMemberUpdateRequests = preferences.getSendMemberUpdateRequests() == null ? DefaultPreferences.SEND_MEMBER_UPDATE_REQUESTS : preferences .getSendMemberUpdateRequests(); org.orcid.jaxb.model.common_v2.Visibility activitiesVisibilityDefault = (preferences.getActivitiesVisibilityDefault().getValue() == null) ? org.orcid.jaxb.model.common_v2.Visibility.PRIVATE : org.orcid.jaxb.model.common_v2.Visibility.fromValue(preferences.getActivitiesVisibilityDefault().getValue().value()); boolean developerToolsEnabled = preferences.getDeveloperToolsEnabled() == null ? DefaultPreferences.DEVELOPER_TOOLS_ENABLED_DEFAULT : preferences .getDeveloperToolsEnabled().isValue(); float sendEmailFrequencyDays = Float.valueOf(preferences.getSendEmailFrequencyDays() == null ? DefaultPreferences.SEND_EMAIL_FREQUENCY_DAYS : preferences .getSendEmailFrequencyDays()); profileDao.updatePreferences(orcid, sendChangeNotifications, sendAdministrativeChangeNotifications, sendOrcidNews, sendMemberUpdateRequests, activitiesVisibilityDefault, developerToolsEnabled, sendEmailFrequencyDays); } @Override @Transactional public OrcidProfile updateOrcidPreferences(OrcidProfile updatedOrcidProfile) { OrcidProfile existingProfile = retrieveOrcidProfile(updatedOrcidProfile.getOrcidIdentifier().getPath()); if (existingProfile == null) { return null; } existingProfile.setOrcidPreferences(updatedOrcidProfile.getOrcidPreferences()); OrcidProfile profileToReturn = updateOrcidProfile(existingProfile); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.PREFERENCES); return profileToReturn; } @Override @Transactional public void addOrcidWorks(OrcidProfile updatedOrcidProfile) { String orcid = updatedOrcidProfile.getOrcidIdentifier().getPath(); OrcidProfile existingProfile = retrieveOrcidProfile(orcid); if (existingProfile == null) { throw new IllegalArgumentException("No record found for " + orcid); } OrcidWorks existingOrcidWorks = existingProfile.retrieveOrcidWorks(); OrcidWorks updatedOrcidWorks = updatedOrcidProfile.retrieveOrcidWorks(); Visibility workVisibilityDefault = existingProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault().getValue(); Boolean claimed = existingProfile.getOrcidHistory().isClaimed(); setWorkPrivacy(updatedOrcidWorks, workVisibilityDefault, claimed == null ? false : claimed); String amenderOrcid = sourceManager.retrieveSourceOrcid(); addSourceToWorks(updatedOrcidWorks, amenderOrcid); updatedOrcidWorks = dedupeWorks(updatedOrcidWorks); List<OrcidWork> updatedOrcidWorksList = updatedOrcidWorks.getOrcidWork(); checkUserCanHoldMoreElement(existingProfile.retrieveOrcidWorks(), updatedOrcidProfile.retrieveOrcidWorks()); if (compareWorksUsingScopusWay) { checkForAlreadyExistingWorks(existingOrcidWorks, updatedOrcidWorksList); if (existingOrcidWorks != null) checkWorkExternalIdentifiersAreNotDuplicated(updatedOrcidWorksList, existingOrcidWorks.getOrcidWork()); else checkWorkExternalIdentifiersAreNotDuplicated(updatedOrcidWorksList, null); } else { checkForAlreadyExistingWorksLegacyMode(existingOrcidWorks, updatedOrcidWorksList); } //workDao.increaseDisplayIndexOnAllElements(orcid); persistAddedWorks(orcid, updatedOrcidWorksList); profileDao.flush(); boolean notificationsEnabled = existingProfile.getOrcidInternal().getPreferences().getNotificationsEnabled(); if (notificationsEnabled) { List<Item> activities = new ArrayList<>(); for (OrcidWork updatedWork : updatedOrcidWorksList) { Item activity = new Item(); activity.setItemName(updatedWork.getWorkTitle().getTitle().getContent()); activity.setItemType(ItemType.WORK); activity.setPutCode(updatedWork.getPutCode()); activities.add(activity); } notificationManager.sendAmendEmail(existingProfile, AmendedSection.WORK, activities); } } /** * Legacy mode to check if works are duplicated TODO: This must be removed * in a near future * */ private void checkForAlreadyExistingWorksLegacyMode(OrcidWorks existingOrcidWorks, List<OrcidWork> updatedOrcidWorksList) { if (existingOrcidWorks != null) { Set<OrcidWork> existingOrcidWorksSet = new HashSet<>(); for (OrcidWork existingWork : existingOrcidWorks.getOrcidWork()) { existingOrcidWorksSet.add(existingWork); } for (Iterator<OrcidWork> updatedWorkIterator = updatedOrcidWorksList.iterator(); updatedWorkIterator.hasNext();) { OrcidWork updatedWork = updatedWorkIterator.next(); for (OrcidWork orcidWork : existingOrcidWorksSet) { if (orcidWork.isDuplicatedLegacyMode(updatedWork)) { updatedWorkIterator.remove(); break; } } } } } private void checkForAlreadyExistingWorks(OrcidWorks existingOrcidWorks, List<OrcidWork> updatedOrcidWorksList) { if (existingOrcidWorks != null) { Set<OrcidWork> existingOrcidWorksSet = new HashSet<>(); for (OrcidWork existingWork : existingOrcidWorks.getOrcidWork()) { existingOrcidWorksSet.add(existingWork); } for (Iterator<OrcidWork> updatedWorkIterator = updatedOrcidWorksList.iterator(); updatedWorkIterator.hasNext();) { OrcidWork updatedWork = updatedWorkIterator.next(); for (OrcidWork orcidWork : existingOrcidWorksSet) { if (orcidWork.isDuplicated(updatedWork)) { // Update the existing work long workId = Long.valueOf(orcidWork.getPutCode()); WorkEntity workEntity = workDao.find(workId); workEntity.clean(); workEntity = jaxb2JpaAdapter.getWorkEntity(updatedWork, workEntity); workDao.persist(workEntity); // Since it was already updated, remove it from the list // of updated works updatedWorkIterator.remove(); break; } } } } } /** * Checks if the list of updated works contains any duplicated external * identifier, if so, it will throw an exception The newOrcidWorksList MUST * be deduped before getting into this method * * @param updatedOrcidWorksList * the deduped list of works * @throws IllegalArgumentException * if there is a duplicated external identifier * */ public void checkWorkExternalIdentifiersAreNotDuplicated(List<OrcidWork> newOrcidWorksList, List<OrcidWork> existingWorkList) { // Rules to define if two works have the same id: // 1) If the source is the same and // 2) any of the ext id matches // 3) but the tile is different // Then, if both works are already existing: Log an error message // If any of the works is new, or both are new, throw an exception // First compare new works, to verify they don't share any ext id // Since the new works comes from a single request, we know they are // from the same source, so, we can skip the work source comparison if (newOrcidWorksList != null) { for (int i = 0; i < newOrcidWorksList.size(); i++) { OrcidWork newWork = newOrcidWorksList.get(i); for (int j = 0; j < newOrcidWorksList.size(); j++) { // If they are not the same work if (i != j) { OrcidWork newWorkToCompare = newOrcidWorksList.get(j); // If newWork have external identifiers if (newWork.getWorkExternalIdentifiers() != null && newWork.getWorkExternalIdentifiers().getWorkExternalIdentifier() != null && !newWork.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) { // For each external id on the outer work for (WorkExternalIdentifier workExtId : newWork.getWorkExternalIdentifiers().getWorkExternalIdentifier()) { if (newWorkToCompare.getWorkExternalIdentifiers() != null && newWorkToCompare.getWorkExternalIdentifiers().getWorkExternalIdentifier() != null && !newWorkToCompare.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) { // Compare it against each external id in // the inner work for (WorkExternalIdentifier workExtIdToCompare : newWorkToCompare.getWorkExternalIdentifiers().getWorkExternalIdentifier()) { // If the ext ids are the same if (workExtId.equals(workExtIdToCompare)) { Title title = (newWork.getWorkTitle() == null || newWork.getWorkTitle().getTitle() == null) ? null : newWork.getWorkTitle() .getTitle(); Title titleToCompare = (newWorkToCompare.getWorkTitle() == null || newWorkToCompare.getWorkTitle().getTitle() == null) ? null : newWorkToCompare.getWorkTitle().getTitle(); if (!isTheSameTitle(title, titleToCompare) && !areBothExtIdsPartOf(newWork.getWorkType(), workExtId, workExtIdToCompare)) { String extIdContent = (workExtId.getWorkExternalIdentifierId() == null || PojoUtil.isEmpty(workExtId .getWorkExternalIdentifierId().getContent())) ? "" : workExtId.getWorkExternalIdentifierId().getContent(); String title1 = (title == null) ? "" : title.getContent(); String title2 = (titleToCompare == null) ? "" : titleToCompare.getContent(); String errorMessage = String.format("Works \"%s\" and \"%s\" have the same external id \"%s\"", title1, title2, extIdContent); throw new IllegalArgumentException(errorMessage); } } } } } } } } } } // Then, if it already have works if (existingWorkList != null && existingWorkList.size() > 0) { // Check for duplicates in existing works, if any is found, log it for (int i = 0; i < existingWorkList.size(); i++) { OrcidWork existingWork = existingWorkList.get(i); Source workSource = existingWork.getSource(); for (int j = 0; j < existingWorkList.size(); j++) { // If it is not the same index if (i != j) { OrcidWork existingWorkToCompare = existingWorkList.get(j); Source workSourceToCompare = existingWorkToCompare.getSource(); // If both works have the same source if (isTheSameSource(workSource, workSourceToCompare)) { // If the work have external identifiers if (existingWork.getWorkExternalIdentifiers() != null && existingWork.getWorkExternalIdentifiers().getWorkExternalIdentifier() != null && !existingWork.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) { // Compare each external identifier for (WorkExternalIdentifier workExtId : existingWork.getWorkExternalIdentifiers().getWorkExternalIdentifier()) { // If the workToCompare have ext ids if (existingWorkToCompare.getWorkExternalIdentifiers() != null && existingWorkToCompare.getWorkExternalIdentifiers().getWorkExternalIdentifier() != null && !existingWorkToCompare.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) { // Compare each ext ids, following the // rules: for (WorkExternalIdentifier workToCompareExtId : existingWorkToCompare.getWorkExternalIdentifiers().getWorkExternalIdentifier()) { // If the ext ids are the same if (workExtId.equals(workToCompareExtId)) { // Compare the titles, if they // are different, set it as // duplicated Title title = (existingWork.getWorkTitle() == null || existingWork.getWorkTitle().getTitle() == null) ? null : existingWork.getWorkTitle().getTitle(); Title titleToCompare = (existingWorkToCompare.getWorkTitle() == null || existingWorkToCompare.getWorkTitle().getTitle() == null) ? null : existingWorkToCompare.getWorkTitle().getTitle(); if (!isTheSameTitle(title, titleToCompare)) { String extIdContent = (workExtId.getWorkExternalIdentifierId() == null || PojoUtil.isEmpty(workExtId .getWorkExternalIdentifierId().getContent())) ? "" : workExtId.getWorkExternalIdentifierId().getContent(); LOG.error("Works {} and {} have the same external identifier {}", new Object[] { existingWork.getPutCode(), existingWorkToCompare.getPutCode(), extIdContent }); } } } } } } } } } } // Check for duplicates between the existing works and the new works if (newOrcidWorksList != null) { for (OrcidWork orcidWork : newOrcidWorksList) { Source workSource = orcidWork.getSource(); for (OrcidWork existingWork : existingWorkList) { Source existingWorkSource = existingWork.getSource(); // If both works have the same source if (isTheSameSource(workSource, existingWorkSource)) { // If the new work have external identifiers if (orcidWork.getWorkExternalIdentifiers() != null && orcidWork.getWorkExternalIdentifiers().getWorkExternalIdentifier() != null && !orcidWork.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) { // For each external identifier in the new work for (WorkExternalIdentifier newExternalIdentifier : orcidWork.getWorkExternalIdentifiers().getWorkExternalIdentifier()) { if (existingWork.getWorkExternalIdentifiers() != null && existingWork.getWorkExternalIdentifiers().getWorkExternalIdentifier() != null && !existingWork.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) { // Compare them against the existing // identifiers for (WorkExternalIdentifier existingExternalIdentifier : existingWork.getWorkExternalIdentifiers().getWorkExternalIdentifier()) { // If the ext ids are the same if (newExternalIdentifier.equals(existingExternalIdentifier)) { // Compare the titles, if they // are different, set it as // duplicated Title title = (orcidWork.getWorkTitle() == null || orcidWork.getWorkTitle().getTitle() == null) ? null : orcidWork .getWorkTitle().getTitle(); Title titleToCompare = (existingWork.getWorkTitle() == null || existingWork.getWorkTitle().getTitle() == null) ? null : existingWork.getWorkTitle().getTitle(); if (!isTheSameTitle(title, titleToCompare) && !areBothExtIdsPartOf(orcidWork.getWorkType(), existingExternalIdentifier, newExternalIdentifier)) { String extIdContent = (existingExternalIdentifier.getWorkExternalIdentifierId() == null || PojoUtil .isEmpty(existingExternalIdentifier.getWorkExternalIdentifierId().getContent())) ? "" : existingExternalIdentifier.getWorkExternalIdentifierId().getContent(); String title1 = (title == null) ? "" : title.getContent(); String title2 = (titleToCompare == null) ? "" : titleToCompare.getContent(); String errorMessage = String.format("Works \"%s\" and \"%s\"(put-code '%s') have the same external id \"%s\"", title1, title2, existingWork.getPutCode(), extIdContent); throw new IllegalArgumentException(errorMessage); } } } } } } } } } } } } private boolean areBothExtIdsPartOf(WorkType workType, WorkExternalIdentifier existing, WorkExternalIdentifier newer) { boolean isExistingPartOf = false; boolean isNewPartOf = false; if(WorkType.BOOK_CHAPTER.equals(workType)) { if(WorkExternalIdentifierType.ISBN.equals(existing.getWorkExternalIdentifierType())) { isExistingPartOf = true; } if(WorkExternalIdentifierType.ISBN.equals(newer.getWorkExternalIdentifierType())) { isNewPartOf = true; } } else if(WorkType.JOURNAL_ARTICLE.equals(workType)) { if(WorkExternalIdentifierType.ISSN.equals(existing.getWorkExternalIdentifierType())) { isExistingPartOf = true; } if(WorkExternalIdentifierType.ISSN.equals(newer.getWorkExternalIdentifierType())) { isNewPartOf = true; } } return (isExistingPartOf && isNewPartOf); } /** * Check if source1 and source2 are equals * * @param source1 * @param source2 * @return true if source1 is equals to source2 * */ private boolean isTheSameSource(Source source1, Source source2) { if (source1 == null) { if (source2 == null) return true; else return false; } else { if (source2 == null) return false; else return source1.equals(source2); } } /** * Check if title1 and title2 are equals * * @param title1 * @param title2 * @return true if title1 is equals to title2 * */ private boolean isTheSameTitle(Title title1, Title title2) { if (title1 == null) { if (title2 == null) return true; else return false; } else { if (title2 == null) return false; else return title1.equals(title2); } } private void persistAddedWorks(String orcid, List<OrcidWork> updatedOrcidWorksList) { ProfileEntity profileEntity = profileDao.find(orcid); Set<String> titles = new HashSet<String>(); for (OrcidWork updatedOrcidWork : updatedOrcidWorksList) { populateContributorInfo(updatedOrcidWork); // Create the work entity WorkEntity workEntity = jaxb2JpaAdapter.getWorkEntity(updatedOrcidWork, null); workEntity.setProfile(profileEntity); workDao.persist(workEntity); updatedOrcidWork.setPutCode(String.valueOf(workEntity.getId())); if (updatedOrcidWork.getWorkTitle() != null && updatedOrcidWork.getWorkTitle().getTitle() != null) { String title = updatedOrcidWork.getWorkTitle().getTitle().getContent(); if (titles.contains(title)) { LOG.warn("Request from {} contains dupplicated works on title '{}' and put-code '{}' \n {}", new Object[] { sourceManager.retrieveSourceOrcid(), title, workEntity.getId(), updatedOrcidWork }); } else { titles.add(title); } } } orcidProfileCacheManager.remove(orcid); } /** * Get each of the work and check the orcid and email parameters against * existing profile information. */ private void populateContributorInfo(OrcidWork work) { WorkContributors contributors = work.getWorkContributors(); if (contributors != null) { for (Contributor contributor : contributors.getContributor()) { // If contributor orcid is available, look for the profile // associated with that orcid if (contributor.getContributorOrcid() != null) { ProfileEntity profile = profileDao.find(contributor.getContributorOrcid().getPath()); if (profile != null) { if(profile.getRecordNameEntity() != null) { if (org.orcid.jaxb.model.common_v2.Visibility.PUBLIC.equals(profile.getRecordNameEntity().getVisibility())) { contributor.setCreditName(new CreditName(profile.getRecordNameEntity().getCreditName())); } } } } else if (contributor.getContributorEmail() != null) { // Else, if email is available, get the profile // associated with that email String email = contributor.getContributorEmail().getValue(); EmailEntity emailEntity = emailDao.findCaseInsensitive(email); if (emailEntity != null) { ProfileEntity profileEntity = emailEntity.getProfile(); contributor.setContributorOrcid(new ContributorOrcid(profileEntity.getId())); if(profileEntity.getRecordNameEntity() != null && org.orcid.jaxb.model.common_v2.Visibility.PUBLIC.equals(profileEntity.getRecordNameEntity().getVisibility())) { contributor.setCreditName(new CreditName(profileEntity.getRecordNameEntity().getCreditName())); } else { contributor.setCreditName(null); } } } } } } private void dedupeWorks(OrcidProfile orcidProfile) { OrcidActivities orcidActivities = orcidProfile.getOrcidActivities(); if (orcidActivities != null) { OrcidWorks orcidWorks = orcidActivities.getOrcidWorks(); if (orcidWorks != null) { OrcidWorks dedupedOrcidWorks = dedupeWorks(orcidWorks); orcidActivities.setOrcidWorks(dedupedOrcidWorks); } } } @Override public OrcidWorks dedupeWorks(OrcidWorks orcidWorks) { Set<OrcidWork> workSet = new LinkedHashSet<OrcidWork>(); for (OrcidWork orcidWork : orcidWorks.getOrcidWork()) { orcidProfileCleaner.clean(orcidWork); workSet.add(orcidWork); } OrcidWorks dedupedOrcidWorks = new OrcidWorks(); dedupedOrcidWorks.getOrcidWork().addAll(workSet); return dedupedOrcidWorks; } @Override @Transactional @Deprecated public OrcidProfile deactivateOrcidProfile(OrcidProfile existingOrcidProfile) { OrcidProfile blankedOrcidProfile = new OrcidProfile(); OrcidBio existingBio = existingOrcidProfile.getOrcidBio(); OrcidBio minimalBio = new OrcidBio(); ContactDetails minimalContactDetails = new ContactDetails(); minimalContactDetails.getEmail().addAll(existingBio.getContactDetails().getEmail()); OrcidInternal minimalOrcidInternal = new OrcidInternal(); minimalOrcidInternal.setSecurityDetails(existingOrcidProfile.getOrcidInternal().getSecurityDetails()); OrcidHistory deactivatedOrcidHistory = existingOrcidProfile.getOrcidHistory(); deactivatedOrcidHistory.setDeactivationDate(new DeactivationDate(DateUtils.convertToXMLGregorianCalendar(new Date()))); blankedOrcidProfile.setOrcidHistory(deactivatedOrcidHistory); // only names names from bio with a visibility setting PersonalDetails minimalPersonalDetails = new PersonalDetails(); minimalPersonalDetails.setOtherNames(null); CreditName creditName = new CreditName(); creditName.setVisibility(Visibility.PUBLIC); minimalPersonalDetails.setCreditName(creditName); minimalPersonalDetails.setGivenNames(new GivenNames("Given Names Deactivated")); minimalPersonalDetails.setFamilyName(new FamilyName("Family Name Deactivated")); for (Email email : minimalContactDetails.getEmail()) { setVisibilityToPrivate(email); } setVisibilityToPrivate(minimalPersonalDetails.getOtherNames()); if (minimalPersonalDetails.getOtherNames() != null && minimalPersonalDetails.getOtherNames().getOtherName() != null){ for (OtherName name : minimalPersonalDetails.getOtherNames().getOtherName()) setVisibilityToPrivate(name); } minimalBio.setPersonalDetails(minimalPersonalDetails); minimalBio.setContactDetails(minimalContactDetails); minimalBio.setBiography(new Biography()); minimalBio.setExternalIdentifiers(new ExternalIdentifiers()); blankedOrcidProfile.setOrcidBio(minimalBio); blankedOrcidProfile.setOrcidIdentifier(existingOrcidProfile.getOrcidIdentifier().getPath()); OrcidProfile profileToReturn = updateOrcidProfile(blankedOrcidProfile); userConnectionDao.deleteByOrcid(existingOrcidProfile.getOrcidIdentifier().getPath()); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.UNKNOWN); return profileToReturn; } /** * Set the locked status of an account to true * * @param orcid * the id of the profile that should be locked * @return true if the account was locked */ @Override public boolean lockProfile(String orcid, String lockReason, String description) { boolean wasLocked = profileDao.lockProfile(orcid, lockReason, description); if (wasLocked) { notificationManager.sendOrcidLockedEmail(orcid); } return wasLocked; } /** * Set the locked status of an account to false * * @param orcid * the id of the profile that should be unlocked * @return true if the account was unlocked */ @Override public boolean unlockProfile(String orcid) { return profileDao.unlockProfile(orcid); } /** * Check if a profile is locked * * @param orcid * the id of the profile to check * @return true if the account is locked */ @Override public boolean isLocked(String orcid) { if (PojoUtil.isEmpty(orcid)) return false; return profileDao.isLocked(orcid); } /** * Reactivate an inactive profile * */ public OrcidProfile reactivateOrcidProfile(OrcidProfile deactivatedOrcidProfile) { OrcidHistory deactivatedOrcidHistory = deactivatedOrcidProfile.getOrcidHistory(); deactivatedOrcidHistory.setDeactivationDate(null); OrcidProfile profileToReturn = updateOrcidProfile(deactivatedOrcidProfile); notificationManager.sendAmendEmail(profileToReturn, AmendedSection.UNKNOWN); return profileToReturn; } private void setVisibilityToPrivate(VisibilityType visibilityType) { if (visibilityType != null) { visibilityType.setVisibility(Visibility.PRIVATE); } } private void setVisibilityToPrivate(OtherNames visibilityType) { if (visibilityType != null) { visibilityType.setVisibility(Visibility.PRIVATE); } } /** * Checks that the email is not already being used * * @param email * the value to be used to check for an existing record */ @Override public boolean emailExists(String email) { return emailDao.emailExists(email); } /** * Adds a new {@link List<org.orcid.jaxb.model.message.Affiliation<} * to the {@link} OrcidProfile} and returns the updated values * * @param updatedOrcidProfile * @return */ @Override @Transactional public void addAffiliations(OrcidProfile updatedOrcidProfile) { String orcid = updatedOrcidProfile.getOrcidIdentifier().getPath(); OrcidProfile existingProfile = retrieveOrcidProfile(orcid); if (existingProfile == null) { throw new IllegalArgumentException("No record found for " + orcid); } Affiliations existingAffiliations = existingProfile.retrieveAffiliations(); Affiliations updatedAffiliations = updatedOrcidProfile.retrieveAffiliations(); Visibility workVisibilityDefault = existingProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault().getValue(); Boolean claimed = existingProfile.getOrcidHistory().isClaimed(); setAffiliationPrivacy(updatedAffiliations, workVisibilityDefault, claimed == null ? false : claimed); updatedAffiliations = dedupeAffiliations(updatedAffiliations); String amenderOrcid = sourceManager.retrieveSourceOrcid(); addSourceToAffiliations(updatedAffiliations, amenderOrcid); List<Affiliation> updatedAffiliationsList = updatedAffiliations.getAffiliation(); checkAndUpdateDisambiguatedOrganization(updatedAffiliationsList); checkForAlreadyExistingAffiliations(existingAffiliations, updatedAffiliationsList); persistAddedAffiliations(orcid, updatedAffiliationsList); profileDao.flush(); boolean notificationsEnabled = existingProfile.getOrcidInternal().getPreferences().getNotificationsEnabled(); if (notificationsEnabled) { notificationManager.sendAmendEmail(existingProfile, AmendedSection.AFFILIATION); } } private void checkAndUpdateDisambiguatedOrganization(List<Affiliation> affiliations) { if (affiliations != null && !affiliations.isEmpty()) { for (Affiliation affiliation : affiliations) { Organization org = affiliation.getOrganization(); OrgEntity orgEntity = orgManager.getOrgEntity(org); // If the org exists if (orgEntity != null) { // And it have a disambiguated org if (orgEntity.getOrgDisambiguated() != null) { // Update the desambiguated org org.setDisambiguatedOrganization(adapter.getDisambiguatedOrganization(orgEntity.getOrgDisambiguated())); } else { // Null the disambiguated organization org.setDisambiguatedOrganization(null); } } } } } /** * Adds a new {@link List<org.orcid.jaxb.model.message.FundingList<} * to the {@link} OrcidProfile} and returns the updated values * * @param updatedOrcidProfile * @return */ @Override @Transactional public void addFundings(OrcidProfile updatedOrcidProfile) { String orcid = updatedOrcidProfile.getOrcidIdentifier().getPath(); OrcidProfile existingProfile = retrieveOrcidProfile(orcid); if (existingProfile == null) { throw new IllegalArgumentException("No record found for " + orcid); } String amenderOrcid = sourceManager.retrieveSourceOrcid(); FundingList existingFundingList = existingProfile.retrieveFundings(); // updates the amount format to the right format according to the // current locale setFundingAmountsWithTheCorrectFormat(updatedOrcidProfile); FundingList updatedFundingList = updatedOrcidProfile.retrieveFundings(); Visibility workVisibilityDefault = existingProfile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault().getValue(); Boolean claimed = existingProfile.getOrcidHistory().isClaimed(); setFundingPrivacy(updatedFundingList, workVisibilityDefault, claimed == null ? false : claimed); updatedFundingList = dedupeFundings(updatedFundingList); addSourceToFundings(updatedFundingList, amenderOrcid); List<Funding> updatedList = updatedFundingList.getFundings(); checkForAlreadyExistingFundings(existingFundingList, updatedList); persistAddedFundings(orcid, updatedList); profileDao.flush(); boolean notificationsEnabled = existingProfile.getOrcidInternal().getPreferences().getNotificationsEnabled(); if (notificationsEnabled) { notificationManager.sendAmendEmail(existingProfile, AmendedSection.FUNDING); } } /** * Replace the funding amount string into the desired format * * @param updatedOrcidProfile * The profile containing the new funding * */ private void setFundingAmountsWithTheCorrectFormat(OrcidProfile updatedOrcidProfile) throws IllegalArgumentException { FundingList fundings = updatedOrcidProfile.retrieveFundings(); for (Funding funding : fundings.getFundings()) { // If the amount is not empty, update it if (funding.getAmount() != null && StringUtils.isNotBlank(funding.getAmount().getContent())) { String amount = funding.getAmount().getContent(); Locale locale = localeManager.getLocale(); ParsePosition parsePosition = new ParsePosition(0); DecimalFormat numberFormat = (DecimalFormat) NumberFormat.getNumberInstance(locale); DecimalFormatSymbols symbols = numberFormat.getDecimalFormatSymbols(); /** * When spaces are allowed, the grouping separator is the * character 160, which is a non-breaking space So, lets change * it so it uses the default space as a separator * */ if (symbols.getGroupingSeparator() == 160) { symbols.setGroupingSeparator(' '); } numberFormat.setDecimalFormatSymbols(symbols); Number number = numberFormat.parse(amount, parsePosition); String formattedAmount = number.toString(); if (parsePosition.getIndex() != amount.length()) { double example = 1234567.89; NumberFormat numberFormatExample = NumberFormat.getNumberInstance(localeManager.getLocale()); throw new IllegalArgumentException("The amount: " + amount + " doesn'n have the right format, it should use the format: " + numberFormatExample.format(example)); } funding.getAmount().setContent(formattedAmount); } } } private void setAffiliationPrivacy(OrcidProfile updatedOrcidProfile, Visibility defaultAffiliationVisibility) { OrcidHistory orcidHistory = updatedOrcidProfile.getOrcidHistory(); boolean isClaimed = orcidHistory != null ? orcidHistory.getClaimed().isValue() : false; OrcidActivities incomingActivities = updatedOrcidProfile.getOrcidActivities(); if (incomingActivities != null) { Affiliations incomingWorks = incomingActivities.getAffiliations(); if (incomingWorks != null) { setAffiliationPrivacy(incomingWorks, defaultAffiliationVisibility, isClaimed); } } } private void setFundingPrivacy(OrcidProfile updatedOrcidProfile, Visibility defaultFundingVisibility) { OrcidHistory orcidHistory = updatedOrcidProfile.getOrcidHistory(); boolean isClaimed = orcidHistory != null ? orcidHistory.getClaimed().isValue() : false; OrcidActivities incomingActivities = updatedOrcidProfile.getOrcidActivities(); if (incomingActivities != null) { FundingList incomingFundingList = incomingActivities.getFundings(); if (incomingFundingList != null) { setFundingPrivacy(incomingFundingList, defaultFundingVisibility, isClaimed); } } } private void setAffiliationPrivacy(Affiliations incomingAffiliations, Visibility defaultVisibility, boolean isClaimed) { for (Affiliation incomingAffiliation : incomingAffiliations.getAffiliation()) { if (StringUtils.isBlank(incomingAffiliation.getPutCode())) { choosePrivacy(incomingAffiliation, defaultVisibility, isClaimed); } } } private void setFundingPrivacy(FundingList incomingFundings, Visibility defaultVisibility, boolean isClaimed) { for (Funding incomingFunding : incomingFundings.getFundings()) { if (StringUtils.isBlank(incomingFunding.getPutCode())) { choosePrivacy(incomingFunding, defaultVisibility, isClaimed); } } } private void addSourceToAffiliations(Affiliations affiliations, String amenderOrcid) { if (affiliations != null && !affiliations.getAffiliation().isEmpty()) { for (Affiliation affiliation : affiliations.getAffiliation()) { affiliation.setSource(createSource(amenderOrcid)); } } } private void addSourceToFundings(FundingList fundings, String amenderOrcid) { if (fundings != null && !fundings.getFundings().isEmpty()) { for (Funding funding : fundings.getFundings()) { funding.setSource(createSource(amenderOrcid)); } } } private void dedupeAffiliations(OrcidProfile orcidProfile) { OrcidActivities orcidActivities = orcidProfile.getOrcidActivities(); if (orcidActivities != null) { Affiliations affiliations = orcidActivities.getAffiliations(); if (affiliations != null) { Affiliations dedupedAffiliations = dedupeAffiliations(affiliations); orcidActivities.setAffiliations(dedupedAffiliations); } } } private Affiliations dedupeAffiliations(Affiliations affiliations) { Set<Affiliation> affiliationSet = new LinkedHashSet<Affiliation>(); for (Affiliation affiliation : affiliations.getAffiliation()) { orcidProfileCleaner.clean(affiliation); affiliationSet.add(affiliation); } Affiliations dedupedAffiliations = new Affiliations(); dedupedAffiliations.getAffiliation().addAll(affiliationSet); return dedupedAffiliations; } private void checkForAlreadyExistingAffiliations(Affiliations existingAffiliations, List<Affiliation> updatedAffiliationsList) { if (existingAffiliations != null) { Set<Affiliation> existingAffiliationsSet = new HashSet<>(); for (Affiliation existingAffiliation : existingAffiliations.getAffiliation()) { existingAffiliationsSet.add(existingAffiliation); } for (Iterator<Affiliation> updatedAffiliationIterator = updatedAffiliationsList.iterator(); updatedAffiliationIterator.hasNext();) { Affiliation updatedAffiliation = updatedAffiliationIterator.next(); if (existingAffiliationsSet.contains(updatedAffiliation)) { updatedAffiliationIterator.remove(); } } } } private void persistAddedAffiliations(String orcid, List<Affiliation> updatedAffiliationsList) { ProfileEntity profileEntity = profileDao.find(orcid); for (Affiliation updatedAffiliation : updatedAffiliationsList) { OrgAffiliationRelationEntity orgAffiliationRelationEntity = jaxb2JpaAdapter.getNewOrgAffiliationRelationEntity(updatedAffiliation, profileEntity); orgAffiliationRelationDao.persist(orgAffiliationRelationEntity); } orcidProfileCacheManager.remove(orcid); } private void dedupeFundings(OrcidProfile orcidProfile) { OrcidActivities orcidActivities = orcidProfile.getOrcidActivities(); if (orcidActivities != null) { FundingList fungins = orcidActivities.getFundings(); if (fungins != null) { FundingList dedupedFundings = dedupeFundings(fungins); orcidActivities.setFundings(dedupedFundings); } } } private FundingList dedupeFundings(FundingList fundings) { Set<Funding> fundingsSet = new LinkedHashSet<Funding>(); for (Funding funding : fundings.getFundings()) { orcidProfileCleaner.clean(funding); fundingsSet.add(funding); } FundingList dedupedFundings = new FundingList(); dedupedFundings.getFundings().addAll(fundingsSet); return dedupedFundings; } private void checkForAlreadyExistingFundings(FundingList existingFundings, List<Funding> updatedFundingsList) { if (existingFundings != null) { Set<Funding> existingFundingsSet = new HashSet<>(); for (Funding existingFunding : existingFundings.getFundings()) { existingFundingsSet.add(existingFunding); } for (Iterator<Funding> updatedFundingIterator = updatedFundingsList.iterator(); updatedFundingIterator.hasNext();) { Funding updatedFunding = updatedFundingIterator.next(); for (Funding funding : existingFundingsSet) { if (funding.isDuplicated(updatedFunding)) { updatedFundingIterator.remove(); break; } } } } } private void persistAddedFundings(String orcid, List<Funding> updatedFundingList) { ProfileEntity profileEntity = profileDao.find(orcid); for (Funding updatedFunding : updatedFundingList) { ProfileFundingEntity profileFundingEntity = jaxb2JpaAdapter.getNewProfileFundingEntity(updatedFunding, profileEntity); // Save the profile grant profileFundingDao.addProfileFunding(profileFundingEntity); } orcidProfileCacheManager.remove(orcid); } @Override @Transactional public OrcidProfile revokeDelegate(String giverOrcid, String receiverOrcid) { profileDao.remove(giverOrcid, receiverOrcid); givenPermissionToDao.remove(giverOrcid, receiverOrcid); return retrieveOrcidProfile(giverOrcid); } @Override @Transactional public OrcidProfile deleteProfile(String orcid) { ProfileEntity profileEntity = profileDao.find(orcid); if (profileEntity == null) { LOG.debug("Asked to delete profile {}, but not found in DB", orcid); return null; } profileDao.remove(profileEntity); profileDao.flush(); orcidIndexManager.deleteOrcidProfile(orcid); orcidProfileCacheManager.remove(orcid); // There seems to be a Hibernate problem relating // OrcidOauth2TokenDetail, when getting and deleting in same // transaction. So not possible to return deleted profile, and probably // not really necessary either. // OrcidProfile orcidProfile = adapter.toOrcidProfile(profileEntity); return null; } static ExecutorService executorService = null; static Object executorServiceLock = new Object(); static ConcurrentHashMap<String, FutureTask<String>> futureHM = new ConcurrentHashMap<String, FutureTask<String>>(); /** Simple method to be called by scheduler. * Looks for profiles with REINDEX flag and adds LastModifiedMessages to the REINDEX queue * Then sets indexing flag to DONE (although there is no guarantee it will be done!) * */ private void processProfilesWithFlagAndAddToMessageQueue(IndexingStatus status, JmsDestination destination){ LOG.info("processing profiles with "+status.name()+" flag. sending to "+destination.name()); List<Pair<String, IndexingStatus>> orcidsForIndexing = new ArrayList<>(); List<IndexingStatus> indexingStatuses = new ArrayList<IndexingStatus>(1); indexingStatuses.add(status); boolean connectionIssue = false; do{ orcidsForIndexing = profileDaoReadOnly.findOrcidsByIndexingStatus(indexingStatuses, INDEXING_BATCH_SIZE, new ArrayList<String>()); LOG.info("processing batch of "+orcidsForIndexing.size()); for (Pair<String, IndexingStatus> p : orcidsForIndexing){ String orcid = p.getLeft(); Date last = profileDaoReadOnly.retrieveLastModifiedDate(orcid); LastModifiedMessage mess = new LastModifiedMessage(orcid,last); if (messaging.send(mess,destination)) profileDao.updateIndexingStatus(orcid, IndexingStatus.DONE); else connectionIssue = true; } }while (!connectionIssue && !orcidsForIndexing.isEmpty()); if (connectionIssue) LOG.warn("ABORTED processing profiles with "+status.name()+" flag. sending to "+destination.name()); } /** * TODO: Disabled until we get move our solr indexing to the message listener * */ @Override public void processProfilesWithPendingFlagAndAddToMessageQueue(){ this.processProfilesWithFlagAndAddToMessageQueue(IndexingStatus.PENDING, JmsDestination.UPDATED_ORCIDS); } /** * TODO: Disabled until we get move our solr indexing to the message listener * */ @Override public void processProfilesWithReindexFlagAndAddToMessageQueue(){ this.processProfilesWithFlagAndAddToMessageQueue(IndexingStatus.REINDEX, JmsDestination.REINDEX); } /** * TODO: Disabled until we get move our solr indexing to the message listener * */ @Override public void processProfilesWithFailedFlagAndAddToMessageQueue(){ this.processProfilesWithFlagAndAddToMessageQueue(IndexingStatus.FAILED, JmsDestination.UPDATED_ORCIDS); } public void processProfilePendingIndexingInTransaction(final String orcid, final IndexingStatus indexingStatus) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { LOG.info("About to index profile: {}", orcid); OrcidProfile orcidProfile = retrievePublicOrcidProfile(orcid); if (orcidProfile == null) { LOG.debug("Null profile found during indexing: {}", orcid); } else { LOG.debug("Got profile to index: {}", orcid); orcidIndexManager.persistProfileInformationForIndexingIfNecessary(orcidProfile); profileDao.updateIndexingStatus(orcid, IndexingStatus.DONE); } //TODO: This code exists so we can run old indexing and S3 updating in parallel // IF you just want MQ driven indexing, you need to disable the old calls in the // context and enable the new ones. if(messaging.isEnabled()) { Date lastModifiedFromDb = orcidProfile.getOrcidHistory().getLastModifiedDate().getValue().toGregorianCalendar().getTime(); LastModifiedMessage mess = new LastModifiedMessage(orcid, lastModifiedFromDb); JmsDestination jmsDestination = JmsDestination.REINDEX; if(IndexingStatus.PENDING.equals(indexingStatus)){ jmsDestination = JmsDestination.UPDATED_ORCIDS; } if (messaging.send(mess, jmsDestination)) { LOG.info("Record " + orcid + " was sent to the message queue"); } else { LOG.error("Record " + orcid + " couldnt been sent to the message queue"); profileDao.updateIndexingStatus(orcid, IndexingStatus.FAILED); } } } }); } @Override synchronized public void processUnclaimedProfilesToFlagForIndexing() { LOG.info("About to process unclaimed profiles to flag for indexing"); List<String> orcidsToFlag = Collections.<String> emptyList(); do { orcidsToFlag = profileDaoReadOnly.findUnclaimedNotIndexedAfterWaitPeriod(claimWaitPeriodDays, claimWaitPeriodDays * 2, INDEXING_BATCH_SIZE, orcidsToFlag); LOG.info("Got batch of {} unclaimed profiles to flag for indexing", orcidsToFlag.size()); for (String orcid : orcidsToFlag) { LOG.info("About to flag unclaimed profile for indexing: {}", orcid); profileEntityManager.updateLastModifed(orcid); } } while (!orcidsToFlag.isEmpty()); } @Override synchronized public void processUnclaimedProfilesForReminder() { LOG.info("About to process unclaimed profiles for reminder"); List<String> orcidsToRemind = Collections.<String> emptyList(); do { orcidsToRemind = profileDaoReadOnly.findUnclaimedNeedingReminder(claimReminderAfterDays, INDEXING_BATCH_SIZE, orcidsToRemind); LOG.info("Got batch of {} unclaimed profiles for reminder", orcidsToRemind.size()); for (final String orcid : orcidsToRemind) { processUnclaimedProfileForReminderInTransaction(orcid); } } while (!orcidsToRemind.isEmpty()); } @Override synchronized public void processUnverifiedEmails7Days() { LOG.info("About to process unclaimed profiles for reminder"); List<String> emails = Collections.<String> emptyList(); do { emails = profileDaoReadOnly.findEmailsUnverfiedDays(verifyReminderAfterDays, INDEXING_BATCH_SIZE, EmailEventType.VERIFY_EMAIL_7_DAYS_SENT); LOG.info("Got batch of {} unclaimed profiles for reminder", emails.size()); for (String email : emails) { processUnverifiedEmails7DaysInTransaction(email); } } while (!emails.isEmpty()); } private void processUnverifiedEmails7DaysInTransaction(final String email) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override @Transactional protected void doInTransactionWithoutResult(TransactionStatus status) { OrcidProfile orcidProfile = retrieveOrcidProfileByEmail(email); notificationManager.sendVerificationReminderEmail(orcidProfile, email); emailEventDao.persist(new EmailEventEntity(email, EmailEventType.VERIFY_EMAIL_7_DAYS_SENT)); emailEventDao.flush(); } }); } private void processUnclaimedProfileForReminderInTransaction(final String orcid) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { processUnclaimedProfileForReminder(orcid); } }); } private void processUnclaimedProfileForReminder(final String orcid) { LOG.info("About to process unclaimed profile for reminder: {}", orcid); OrcidProfile orcidProfile = retrieveOrcidProfile(orcid); notificationManager.sendClaimReminderEmail(orcidProfile, claimWaitPeriodDays - claimReminderAfterDays); } private Set<OrcidGrantedAuthority> getGrantedAuthorities(ProfileEntity profileEntity) { OrcidGrantedAuthority authority = new OrcidGrantedAuthority(); authority.setProfileEntity(profileEntity); OrcidType userType = (profileEntity.getOrcidType() == null) ? OrcidType.USER : OrcidType.fromValue(profileEntity.getOrcidType().value()); if (userType.equals(OrcidType.USER)) authority.setAuthority(OrcidWebRole.ROLE_USER.getAuthority()); else if (userType.equals(OrcidType.ADMIN)) authority.setAuthority(OrcidWebRole.ROLE_ADMIN.getAuthority()); else if (userType.equals(OrcidType.GROUP)) { switch (profileEntity.getGroupType()) { case BASIC: authority.setAuthority(OrcidWebRole.ROLE_BASIC.getAuthority()); break; case PREMIUM: authority.setAuthority(OrcidWebRole.ROLE_PREMIUM.getAuthority()); break; case BASIC_INSTITUTION: authority.setAuthority(OrcidWebRole.ROLE_BASIC_INSTITUTION.getAuthority()); break; case PREMIUM_INSTITUTION: authority.setAuthority(OrcidWebRole.ROLE_PREMIUM_INSTITUTION.getAuthority()); break; } } Set<OrcidGrantedAuthority> authorities = new HashSet<OrcidGrantedAuthority>(1); authorities.add(authority); return authorities; } private String hash(String unencrypted) { if (StringUtils.isNotBlank(unencrypted)) { return encryptionManager.hashForInternalUse(unencrypted); } else { return null; } } private String encrypt(String unencrypted) { if (StringUtils.isNotBlank(unencrypted)) { return encryptionManager.encryptForInternalUse(unencrypted); } else { return null; } } private void encryptAndMapFieldsForProfileEntityPersistence(OrcidProfile orcidProfile, ProfileEntity profileEntity) { String password = orcidProfile.getPassword(); profileEntity.setEncryptedPassword(password == null ? null : encryptionManager.hashForInternalUse(password)); String verificationCode = orcidProfile.getVerificationCode(); profileEntity.setEncryptedVerificationCode(verificationCode == null ? null : encryptionManager.encryptForInternalUse(verificationCode)); String securityAnswer = orcidProfile.getSecurityQuestionAnswer(); profileEntity.setEncryptedSecurityAnswer(securityAnswer == null ? null : encryptionManager.encryptForInternalUse(securityAnswer)); } @Override @Deprecated public void updateLastModifiedDate(String orcid) { profileEntityManager.updateLastModifed(orcid); } static public OrcidProfile toOrcidProfile(Element element) { return (OrcidProfile) (element != null ? element.getObjectValue() : null); } @Override public void clearOrcidProfileCache() { orcidProfileCacheManager.removeAll(); } /** * Sets the default visibility of each bio element present in the * orcidProfile object * * @param orcidProfile * */ private void setDefaultVisibility(ProfileEntity profileEntity, boolean useMemberDefaults, Visibility defaultVisibility) { if (profileEntity != null) { //Names should be public by default if (profileEntity.getRecordNameEntity() != null && profileEntity.getRecordNameEntity().getVisibility() == null) { profileEntity.getRecordNameEntity().setVisibility(org.orcid.jaxb.model.common_v2.Visibility.fromValue(OrcidVisibilityDefaults.NAMES_DEFAULT.getVisibility().value())); } if (profileEntity.getActivitiesVisibilityDefault() == null) { if(useMemberDefaults) { profileEntity.setActivitiesVisibilityDefault(org.orcid.jaxb.model.common_v2.Visibility.fromValue(OrcidVisibilityDefaults.CREATED_BY_MEMBER_DEFAULT.getVisibility().value())); } else { if(defaultVisibility != null) { profileEntity.setActivitiesVisibilityDefault(org.orcid.jaxb.model.common_v2.Visibility.fromValue(defaultVisibility.value())); } else { profileEntity.setActivitiesVisibilityDefault(org.orcid.jaxb.model.common_v2.Visibility.fromValue(OrcidVisibilityDefaults.ACTIVITIES_DEFAULT.getVisibility().value())); } } } if(profileEntity.getRecordNameEntity() != null) { if(profileEntity.getRecordNameEntity().getVisibility() == null) { profileEntity.getRecordNameEntity().setVisibility(org.orcid.jaxb.model.common_v2.Visibility.fromValue(OrcidVisibilityDefaults.NAMES_DEFAULT.getVisibility().value())); } } if(profileEntity.getBiographyEntity() != null) { if(profileEntity.getBiographyEntity().getVisibility() == null) { if(defaultVisibility != null) { profileEntity.getBiographyEntity().setVisibility(org.orcid.jaxb.model.common_v2.Visibility.fromValue(defaultVisibility.value())); } else { profileEntity.getBiographyEntity().setVisibility(org.orcid.jaxb.model.common_v2.Visibility.fromValue(OrcidVisibilityDefaults.BIOGRAPHY_DEFAULT.getVisibility().value())); } } } } } }