/** * ============================================================================= * * 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.frontend.web.controllers; import java.util.ArrayList; import java.util.Currency; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeMap; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.orcid.core.adapter.Jaxb2JpaAdapter; import org.orcid.core.adapter.Jpa2JaxbAdapter; import org.orcid.core.locale.LocaleManager; import org.orcid.core.manager.ExternalIdentifierManager; import org.orcid.core.manager.IdentifierTypeManager; import org.orcid.core.manager.OtherNameManager; import org.orcid.core.manager.ProfileEntityCacheManager; import org.orcid.core.manager.ProfileEntityManager; import org.orcid.core.manager.ProfileKeywordManager; import org.orcid.core.manager.ResearcherUrlManager; import org.orcid.core.manager.ThirdPartyLinkManager; import org.orcid.core.manager.WorkManager; import org.orcid.frontend.web.util.LanguagesMap; import org.orcid.frontend.web.util.NumberList; import org.orcid.frontend.web.util.YearsList; import org.orcid.jaxb.model.clientgroup.OrcidClient; import org.orcid.jaxb.model.clientgroup.RedirectUri; import org.orcid.jaxb.model.message.AffiliationType; import org.orcid.jaxb.model.message.ContributorRole; import org.orcid.jaxb.model.message.FundingContributorRole; import org.orcid.jaxb.model.message.FundingType; import org.orcid.jaxb.model.message.SequenceType; import org.orcid.jaxb.model.record_v2.CitationType; import org.orcid.jaxb.model.record_v2.Keywords; import org.orcid.jaxb.model.record_v2.OtherNames; import org.orcid.jaxb.model.record_v2.PeerReviewType; import org.orcid.jaxb.model.record_v2.PersonExternalIdentifiers; import org.orcid.jaxb.model.record_v2.ResearcherUrls; import org.orcid.jaxb.model.record_v2.Role; import org.orcid.jaxb.model.record_v2.WorkCategory; import org.orcid.jaxb.model.record_v2.WorkType; import org.orcid.persistence.constants.SiteConstants; import org.orcid.persistence.jpa.entities.ProfileEntity; import org.orcid.pojo.IdentifierType; import org.orcid.pojo.ThirdPartyRedirect; import org.orcid.pojo.ajaxForm.ExternalIdentifierForm; import org.orcid.pojo.ajaxForm.ExternalIdentifiersForm; import org.orcid.pojo.ajaxForm.KeywordForm; import org.orcid.pojo.ajaxForm.KeywordsForm; import org.orcid.pojo.ajaxForm.OtherNameForm; import org.orcid.pojo.ajaxForm.OtherNamesForm; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.pojo.ajaxForm.Visibility; import org.orcid.pojo.ajaxForm.WebsiteForm; import org.orcid.pojo.ajaxForm.WebsitesForm; import org.orcid.utils.FunctionsOverCollections; import org.orcid.utils.solr.entities.OrgDisambiguatedSolrDocument; import org.springframework.context.NoSuchMessageException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; /** * @author Will Simpson */ @Controller("workspaceController") public class WorkspaceController extends BaseWorkspaceController { @Resource private ThirdPartyLinkManager thirdPartyLinkManager; @Resource private ExternalIdentifierManager externalIdentifierManager; @Resource private ProfileKeywordManager profileKeywordManager; @Resource private OtherNameManager otherNameManager; @Resource private Jpa2JaxbAdapter jpa2JaxbAdapter; @Resource private Jaxb2JpaAdapter jaxb2JpaAdapter; @Resource private WorkManager workManager; @Resource private ResearcherUrlManager researcherUrlManager; @Resource private LocaleManager localeManager; @Resource(name = "languagesMap") private LanguagesMap lm; @Resource private ProfileEntityCacheManager profileEntityCacheManager; @Resource(name = "profileEntityManager") private ProfileEntityManager profileEntityManager; @Resource private IdentifierTypeManager identifierTypeManager; private long getLastModifiedTime(String orcid) { return profileEntityManager.getLastModified(orcid); } @RequestMapping(value = { "/workspace/retrieve-work-impor-wizards.json" }, method = RequestMethod.GET) public @ResponseBody List<OrcidClient> retrieveWorkImportWizards() { return thirdPartyLinkManager.findOrcidClientsWithPredefinedOauthScopeWorksImport(); } @ModelAttribute("fundingImportWizards") public List<OrcidClient> retrieveFundingImportWizards() { return thirdPartyLinkManager.findOrcidClientsWithPredefinedOauthScopeFundingImport(); } @RequestMapping(value = { "/workspace/retrieve-peer-review-import-wizards.json" }, method = RequestMethod.GET) public @ResponseBody List<OrcidClient> retrievePeerReviewImportWizards() { return thirdPartyLinkManager.findOrcidClientsWithPredefinedOauthScopePeerReviewImport(); } @ModelAttribute("affiliationTypes") public Map<String, String> retrieveAffiliationTypesAsMap() { Map<String, String> affiliationTypes = new LinkedHashMap<String, String>(); for (AffiliationType affiliationType : AffiliationType.values()) { affiliationTypes.put(affiliationType.value(), getMessage(buildInternationalizationKey(org.orcid.jaxb.model.message.AffiliationType.class, affiliationType.value()))); } return FunctionsOverCollections.sortMapsByValues(affiliationTypes); } @ModelAttribute("fundingTypes") public Map<String, String> retrieveFundingTypesAsMap() { Map<String, String> grantTypes = new LinkedHashMap<String, String>(); for (FundingType fundingType : FundingType.values()) { grantTypes.put(fundingType.value(), getMessage(buildInternationalizationKey(org.orcid.jaxb.model.message.FundingType.class, fundingType.value()))); } return FunctionsOverCollections.sortMapsByValues(grantTypes); } @ModelAttribute("currencyCodeTypes") public Map<String, String> retrieveCurrencyCodesTypesAsMap() { Map<String, String> currencyCodeTypes = new LinkedHashMap<String, String>(); //Add an empty one currencyCodeTypes.put("", ""); for (Currency currency : Currency.getAvailableCurrencies()) { currencyCodeTypes.put(currency.getCurrencyCode(), currency.getCurrencyCode()); } return FunctionsOverCollections.sortMapsByValues(currencyCodeTypes); } @ModelAttribute("affiliationLongDescriptionTypes") public Map<String, String> retrieveAffiliationLongDescriptionTypesAsMap() { Map<String, String> organizationTypes = new LinkedHashMap<String, String>(); for (AffiliationType organizationType : AffiliationType.values()) { organizationTypes.put(organizationType.value(), getMessage(AffiliationType.class.getName() + '.' + "longDescription" + '.' + organizationType.value())); } return FunctionsOverCollections.sortMapsByValues(organizationTypes); } @ModelAttribute("workCategories") public Map<String, String> retrieveWorkCategoriesAsMap() { Map<String, String> workCategories = new LinkedHashMap<String, String>(); for (WorkCategory workCategory : WorkCategory.values()) { workCategories.put(workCategory.value(), getMessage(new StringBuffer("org.orcid.jaxb.model.record.WorkCategory.").append(workCategory.value()).toString())); } return workCategories; } @ModelAttribute("citationTypes") public Map<String, String> retrieveTypesAsMap() { Map<String, String> citationTypes = new LinkedHashMap<String, String>(); for (CitationType citationType : CitationType.values()) { citationTypes.put(citationType.value(), getMessage(new StringBuffer("org.orcid.jaxb.model.record.CitationType.").append(citationType.value()).toString())); } return FunctionsOverCollections.sortMapsByValues(citationTypes); } @ModelAttribute("years") public Map<String, String> retrieveYearsAsMap() { Map<String, String> map = new LinkedHashMap<String, String>(); List<String> list = YearsList.createList(); map.put("", getMessage("select.item.year")); for (String year : list) { map.put(year, year); } return map; } @ModelAttribute("fundingYears") public Map<String, String> retrieveFundingYearsAsMap() { Map<String, String> map = new LinkedHashMap<String, String>(); List<String> list = YearsList.createList(10); map.put("", getMessage("select.item.year")); for (String year : list) { map.put(year, year); } return map; } @ModelAttribute("months") public Map<String, String> retrieveMonthsAsMap() { Map<String, String> map = new LinkedHashMap<String, String>(); List<String> list = NumberList.createList(12); map.put("", getMessage("select.item.month")); for (String month : list) { map.put(month, month); } return map; } @ModelAttribute("days") public Map<String, String> retrieveDaysAsMap() { Map<String, String> map = new LinkedHashMap<String, String>(); List<String> list = NumberList.createList(31); map.put("", getMessage("select.item.day")); for (String day : list) { map.put(day, day); } return map; } /** * Generate a map with ID types. The map is different from the rest, because * it will be ordered in the form: value -> key, to keep the map alpha * ordered in UI. * */ @ModelAttribute("idTypes") public Map<String, String> retrieveIdTypesAsMap() { Map<String,String> map = new TreeMap<String,String>(); Map<String,IdentifierType> types = identifierTypeManager.fetchIdentifierTypesByAPITypeName(getLocale()); for (String type : types.keySet()) { try{ map.put(types.get(type).getDescription(), type); }catch (NoSuchMessageException e){ //we will skip these from UI for now. //map.put(type, type); } } return FunctionsOverCollections.sortMapsByValues(map); } @ModelAttribute("roles") public Map<String, String> retrieveRolesAsMap() { Map<String, String> map = new TreeMap<String, String>(); try{ for (ContributorRole contributorRole : ContributorRole.values()) { map.put(contributorRole.value(), getMessage(buildInternationalizationKey(org.orcid.jaxb.model.message.ContributorRole.class, contributorRole.value()))); } return FunctionsOverCollections.sortMapsByValues(map); }catch(Exception e){ e.printStackTrace(); } return map; } @ModelAttribute("fundingRoles") public Map<String, String> retrieveFundingRolesAsMap() { Map<String, String> map = new LinkedHashMap<String, String>(); for (FundingContributorRole contributorRole : FundingContributorRole.values()) { map.put(contributorRole.value(), getMessage(buildInternationalizationKey(org.orcid.jaxb.model.message.FundingContributorRole.class, contributorRole.value()))); } return map; } @ModelAttribute("sequences") public Map<String, String> retrieveSequencesAsMap() { Map<String, String> map = new LinkedHashMap<String, String>(); for (SequenceType sequenceType : SequenceType.values()) { map.put(sequenceType.value(), getMessage(buildInternationalizationKey(org.orcid.jaxb.model.message.SequenceType.class, sequenceType.value()))); } return FunctionsOverCollections.sortMapsByValues(map); } @ModelAttribute("languages") public Map<String, String> retrieveLocalesAsMap() { return lm.getLanguagesMap(localeManager.getLocale()); } @ModelAttribute("peerReviewRoles") public Map<String, String> retrievePeerReviewRolesAsMap() { Map<String, String> peerReviewRoles = new LinkedHashMap<String, String>(); for (Role role : Role.values()) { peerReviewRoles.put(role.value(), getMessage(new StringBuffer("org.orcid.jaxb.model.record.Role.").append(role.name()).toString())); } return FunctionsOverCollections.sortMapsByValues(peerReviewRoles); } @ModelAttribute("peerReviewTypes") public Map<String, String> retrievePeerReviewTypesAsMap() { Map<String, String> peerReviewTypes = new LinkedHashMap<String, String>(); for (PeerReviewType type : PeerReviewType.values()) { peerReviewTypes.put(type.value(), getMessage(new StringBuffer("org.orcid.jaxb.model.record.PeerReviewType.").append(type.name()).toString())); } return FunctionsOverCollections.sortMapsByValues(peerReviewTypes); } @ModelAttribute("workTypes") public Map<String, String> retrieveWorkTypesAsMap() { Map<String, String> types = new LinkedHashMap<String, String>(); for (WorkType type : WorkType.values()) { types.put(type.value(), getMessage(new StringBuffer("org.orcid.jaxb.model.record.WorkType.").append(type.value()).toString())); } return FunctionsOverCollections.sortMapsByValues(types); } @RequestMapping(value = {"/my-orcid3","/my-orcid", "/workspace"}, method = RequestMethod.GET) public ModelAndView viewWorkspace3(HttpServletRequest request, @RequestParam(value = "page", defaultValue = "1") int pageNo, @RequestParam(value = "maxResults", defaultValue = "200") int maxResults) { ModelAndView mav = new ModelAndView("workspace_v3"); mav.addObject("showPrivacy", true); mav.addObject("currentLocaleKey", localeManager.getLocale().toString()); mav.addObject("sendEmailFrequencies", retrieveEmailFrequenciesAsMap()); mav.addObject("currentLocaleValue", lm.buildLanguageValue(localeManager.getLocale(), localeManager.getLocale())); return mav; } @RequestMapping(value = "/my-orcid/keywordsForms.json", method = RequestMethod.GET) public @ResponseBody KeywordsForm getKeywordsFormJson(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { long lastModifiedTime = getLastModifiedTime(getCurrentUserOrcid()); Keywords keywords = profileKeywordManager.getKeywords(getCurrentUserOrcid(), lastModifiedTime); KeywordsForm form = KeywordsForm.valueOf(keywords); //Set the default visibility ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if(profile != null && profile.getActivitiesVisibilityDefault() != null) { form.setVisibility(Visibility.valueOf(profile.getActivitiesVisibilityDefault())); } return form; } @RequestMapping(value = "/my-orcid/keywordsForms.json", method = RequestMethod.POST) public @ResponseBody KeywordsForm setKeywordsFormJson(HttpServletRequest request, @RequestBody KeywordsForm kf) throws NoSuchRequestHandlingMethodException { kf.setErrors(new ArrayList<String>()); ProfileEntity profile = profileEntityCacheManager.retrieve(getEffectiveUserOrcid()); Visibility defaultVisibility = Visibility.valueOf(profile.getActivitiesVisibilityDefault()); if(kf != null) { Iterator<KeywordForm> it = kf.getKeywords().iterator(); while (it.hasNext()) { KeywordForm k = it.next(); if(!PojoUtil.isEmpty(k.getContent())) { if (k.getContent().length() > SiteConstants.KEYWORD_MAX_LENGTH) { k.setContent(k.getContent().substring(0,SiteConstants.KEYWORD_MAX_LENGTH)); } } else { it.remove(); } //Set default visibility in case it is null if(k.getVisibility() == null || k.getVisibility().getVisibility() == null) { k.setVisibility(defaultVisibility); } copyErrors(k, kf); } if (kf.getErrors().size()>0) { return kf; } Keywords updatedKeywords = kf.toKeywords(); profileKeywordManager.updateKeywords(getCurrentUserOrcid(), updatedKeywords); } return kf; } @RequestMapping(value = "/my-orcid/otherNamesForms.json", method = RequestMethod.GET) public @ResponseBody OtherNamesForm getOtherNamesFormJson(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { long lastModifiedTime = getLastModifiedTime(getCurrentUserOrcid()); OtherNames otherNames = otherNameManager.getOtherNames(getCurrentUserOrcid(), lastModifiedTime); OtherNamesForm form = OtherNamesForm.valueOf(otherNames); //Set the default visibility ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if(profile != null && profile.getActivitiesVisibilityDefault() != null) { form.setVisibility(Visibility.valueOf(profile.getActivitiesVisibilityDefault())); } return form; } @RequestMapping(value = "/my-orcid/otherNamesForms.json", method = RequestMethod.POST) public @ResponseBody OtherNamesForm setOtherNamesFormJson(@RequestBody OtherNamesForm onf) throws NoSuchRequestHandlingMethodException { onf.setErrors(new ArrayList<String>()); ProfileEntity profile = profileEntityCacheManager.retrieve(getEffectiveUserOrcid()); Visibility defaultVisibility = Visibility.valueOf(profile.getActivitiesVisibilityDefault()); if(onf != null) { Iterator<OtherNameForm> it = onf.getOtherNames().iterator(); while(it.hasNext()) { OtherNameForm form = it.next(); if(PojoUtil.isEmpty(form.getContent())) { it.remove(); continue; } if(form.getContent().length() > SiteConstants.MAX_LENGTH_255) { form.setContent(form.getContent().substring(0, SiteConstants.MAX_LENGTH_255)); } //Set default visibility in case it is null if(form.getVisibility() == null || form.getVisibility().getVisibility() == null) { form.setVisibility(defaultVisibility); } copyErrors(form, onf); } if (onf.getErrors().size()>0) { return onf; } OtherNames otherNames = onf.toOtherNames(); otherNameManager.updateOtherNames(getEffectiveUserOrcid(), otherNames); } return onf; } /** * Retrieve all external identifiers as a json string * */ @RequestMapping(value = "/my-orcid/websitesForms.json", method = RequestMethod.GET) public @ResponseBody WebsitesForm getWebsitesFormJson(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { ResearcherUrls rUrls = researcherUrlManager.getResearcherUrls(getCurrentUserOrcid(), getLastModifiedTime(getCurrentUserOrcid())); WebsitesForm form = WebsitesForm.valueOf(rUrls); //Set the default visibility ProfileEntity profile = profileEntityCacheManager.retrieve(getCurrentUserOrcid()); if(profile != null && profile.getActivitiesVisibilityDefault() != null) { form.setVisibility(Visibility.valueOf(profile.getActivitiesVisibilityDefault())); } return form; } /** * Retrieve all external identifiers as a json string * */ @RequestMapping(value = "/my-orcid/websitesForms.json", method = RequestMethod.POST) public @ResponseBody WebsitesForm setWebsitesFormJson(HttpServletRequest request, @RequestBody WebsitesForm ws) throws NoSuchRequestHandlingMethodException { ws.setErrors(new ArrayList<String>()); ProfileEntity profile = profileEntityCacheManager.retrieve(getEffectiveUserOrcid()); Visibility defaultVisibility = Visibility.valueOf(profile.getActivitiesVisibilityDefault()); if(ws != null) { Set<String> existingUrls = new HashSet<String>(); for (WebsiteForm w : ws.getWebsites()) { //Clean old errors w.setErrors(new ArrayList<String>()); //Validate if(!validateUrl(w.getUrl())) { w.getErrors().add(getMessage("common.invalid_url")); } if(isLongerThan(w.getUrlName(), SiteConstants.URL_MAX_LENGTH)) { w.getErrors().add(getMessage("manualWork.length_less_X")); } //Check there are no duplicates if(existingUrls.contains(w.getUrl())) { w.getErrors().add(getMessage("researcher_url.error.duplicated", w.getUrl())); } else { existingUrls.add(w.getUrl()); } //Set default visibility in case it is null if(w.getVisibility() == null || w.getVisibility().getVisibility() == null) { w.setVisibility(defaultVisibility); } copyErrors(w, ws); } if (ws.getErrors().size()>0) { return ws; } ResearcherUrls rUrls = ws.toResearcherUrls(); researcherUrlManager.updateResearcherUrls(getCurrentUserOrcid(), rUrls); } return ws; } /** * Retrieve all external identifiers as a json string * */ @RequestMapping(value = "/my-orcid/externalIdentifiers.json", method = RequestMethod.GET) public @ResponseBody ExternalIdentifiersForm getExternalIdentifiersJson(HttpServletRequest request) throws NoSuchRequestHandlingMethodException { PersonExternalIdentifiers extIds = externalIdentifierManager.getExternalIdentifiers(getCurrentUserOrcid(), getLastModifiedTime(getCurrentUserOrcid())); return ExternalIdentifiersForm.valueOf(extIds); } /** * Delete an external identifier * */ @RequestMapping(value = "/my-orcid/externalIdentifiers.json", method = RequestMethod.DELETE) public @ResponseBody ExternalIdentifierForm removeExternalIdentifierJson(HttpServletRequest request, @RequestBody ExternalIdentifierForm externalIdentifier) { if(externalIdentifier != null && externalIdentifier.getPutCode() != null) { externalIdentifierManager.deleteExternalIdentifier(getCurrentUserOrcid(), Long.valueOf(externalIdentifier.getPutCode()), false); } return externalIdentifier; } /** * Update the visibility of the given external identifeirs * */ @RequestMapping(value = "/my-orcid/externalIdentifiers.json", method = RequestMethod.POST) public @ResponseBody ExternalIdentifiersForm updateExternalIdentifierJson(HttpServletRequest request, @RequestBody ExternalIdentifiersForm externalIdentifiersForm) { PersonExternalIdentifiers externalIdentifiers = externalIdentifiersForm.toPersonExternalIdentifiers(); externalIdentifiers = externalIdentifierManager.updateExternalIdentifiers(getCurrentUserOrcid(), externalIdentifiers); return externalIdentifiersForm; } @RequestMapping(value = "/my-orcid/sourceGrantReadWizard.json", method = RequestMethod.GET) public @ResponseBody ThirdPartyRedirect getSourceGrantReadWizard() { ThirdPartyRedirect tpr = new ThirdPartyRedirect(); ProfileEntity profile = profileEntityCacheManager.retrieve(getEffectiveUserOrcid()); if(profile.getSource() == null || profile.getSource().getSourceId() == null) { return tpr; } String sourcStr = profile.getSource().getSourceId(); // Check that the cache is up to date evictThirdPartyLinkManagerCacheIfNeeded(); // Get list of clients List<OrcidClient> orcidClients = thirdPartyLinkManager.findOrcidClientsWithPredefinedOauthScopeReadAccess(); for (OrcidClient orcidClient : orcidClients) { if (sourcStr.equals(orcidClient.getClientId())) { RedirectUri ru = orcidClient.getRedirectUris().getRedirectUri().get(0); String redirect = getBaseUri() + "/oauth/authorize?client_id=" + orcidClient.getClientId() + "&response_type=code&scope=" + ru.getScopeAsSingleString() + "&redirect_uri=" + ru.getValue(); tpr.setUrl(redirect); tpr.setDisplayName(orcidClient.getDisplayName()); tpr.setShortDescription(orcidClient.getShortDescription()); return tpr; } } return tpr; } /** * Reads the latest cache version from database, compare it against the * local version; if they are different, evicts all caches. * * @return true if the local cache version is different than the one on * database * */ private boolean evictThirdPartyLinkManagerCacheIfNeeded() { long currentCachedVersion = thirdPartyLinkManager.getLocalCacheVersion(); long dbCacheVersion = thirdPartyLinkManager.getDatabaseCacheVersion(); if (currentCachedVersion < dbCacheVersion) { // If version changed, evict the cache thirdPartyLinkManager.evictAll(); return true; } return false; } }