/**
* =============================================================================
*
* 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.io.UnsupportedEncodingException;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.orcid.core.api.OrcidApiConstants;
import org.orcid.core.manager.ClientDetailsEntityCacheManager;
import org.orcid.core.manager.EncryptionManager;
import org.orcid.core.manager.LoadOptions;
import org.orcid.core.manager.NotificationManager;
import org.orcid.core.manager.ProfileEntityCacheManager;
import org.orcid.core.manager.TemplateManager;
import org.orcid.core.manager.impl.OrcidUrlManager;
import org.orcid.core.oauth.OrcidProfileUserDetails;
import org.orcid.frontend.web.controllers.helper.UserSession;
import org.orcid.jaxb.model.common_v2.Source;
import org.orcid.jaxb.model.message.OrcidProfile;
import org.orcid.jaxb.model.message.Preferences;
import org.orcid.jaxb.model.notification.amended_v2.NotificationAmended;
import org.orcid.jaxb.model.notification.custom_v2.NotificationCustom;
import org.orcid.jaxb.model.notification.permission_v2.NotificationPermission;
import org.orcid.jaxb.model.notification_v2.Notification;
import org.orcid.jaxb.model.notification_v2.NotificationType;
import org.orcid.model.notification.institutional_sign_in_v2.NotificationInstitutionalConnection;
import org.orcid.persistence.jpa.entities.ActionableNotificationEntity;
import org.orcid.persistence.jpa.entities.ClientDetailsEntity;
import org.springframework.stereotype.Controller;
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.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping({ "/inbox", "/notifications" })
@SessionAttributes("primaryEmail")
public class NotificationController extends BaseController {
@Resource
private NotificationManager notificationManager;
@Resource
private TemplateManager templateManager;
@Resource
private EncryptionManager encryptionManager;
@Resource
private OrcidUrlManager orcidUrlManager;
@Resource
private ProfileEntityCacheManager profileEntityCacheManager;
@Resource
private ClientDetailsEntityCacheManager clientDetailsEntityCacheManager;
@Resource
private UserSession userSession;
@RequestMapping
public ModelAndView getNotifications() {
ModelAndView mav = new ModelAndView("notifications");
OrcidProfile profile = orcidProfileManager.retrieveOrcidProfile(getCurrentUserOrcid(), LoadOptions.BIO_AND_INTERNAL_ONLY);
mav.addObject("profile", profile);
return mav;
}
@RequestMapping("/notifications.json")
public @ResponseBody List<Notification> getNotificationsJson(@RequestParam(value = "firstResult", defaultValue = "0") int firstResult,
@RequestParam(value = "maxResults", defaultValue = "10") int maxResults,
@RequestParam(value = "includeArchived", defaultValue = "false") boolean includeArchived) {
String currentOrcid = getCurrentUserOrcid();
List<Notification> notifications = notificationManager.findByOrcid(currentOrcid, includeArchived, firstResult, maxResults);
notifications = archiveObsoleteNotifications(currentOrcid, notifications);
addSubjectToNotifications(notifications);
setOverwrittenSourceName(notifications);
return notifications;
}
@RequestMapping("/notification-alerts.json")
public @ResponseBody List<Notification> getNotificationAlertJson() {
String currentOrcid = getCurrentUserOrcid();
List<Notification> notifications = notificationManager.findNotificationAlertsByOrcid(currentOrcid);
notifications = archiveObsoleteNotifications(currentOrcid, notifications);
notifications = notifications.stream().filter(n -> !userSession.getSuppressedNotificationAlertIds().contains(n.getPutCode())).collect(Collectors.toList());
addSubjectToNotifications(notifications);
return notifications;
}
private List<Notification> archiveObsoleteNotifications(String currentOrcid, List<Notification> notifications) {
if (!userSession.isObsoleteNotificationAlertsCheckDone()) {
notifications = notificationManager.filterActionedNotificationAlerts(notifications, currentOrcid);
userSession.setObsoleteNotificationAlertsCheckDone(true);
}
return notifications;
}
private void setOverwrittenSourceName(List<Notification> notifications) {
for(Notification notification : notifications) {
if(notification instanceof NotificationCustom) {
NotificationCustom nc = (NotificationCustom) notification;
if(getMessage("email.subject.auto_deprecate").equals(nc.getSubject())) {
nc.setOverwrittenSourceName("ORCID");
}
}
}
}
private void addSubjectToNotifications(List<Notification> notifications) {
for (Notification notification : notifications) {
if (notification instanceof NotificationPermission) {
NotificationPermission naa = (NotificationPermission) notification;
String customSubject = naa.getNotificationSubject();
if (StringUtils.isNotBlank(customSubject)) {
naa.setSubject(customSubject);
} else {
naa.setSubject(getMessage(buildInternationalizationKey(NotificationType.class, naa.getNotificationType().value())));
}
} else if (notification instanceof NotificationAmended) {
NotificationAmended na = (NotificationAmended) notification;
na.setSubject(getMessage(buildInternationalizationKey(NotificationType.class, na.getNotificationType().value())));
} else if (notification instanceof NotificationInstitutionalConnection) {
NotificationInstitutionalConnection nic = (NotificationInstitutionalConnection) notification;
nic.setSubject(getMessage(buildInternationalizationKey(NotificationType.class, nic.getNotificationType().value())));
}
}
}
@RequestMapping("/unreadCount.json")
public @ResponseBody int getUnreadCountJson() {
String currentOrcid = getCurrentUserOrcid();
return notificationManager.getUnreadCount(currentOrcid);
}
@RequestMapping(value = "/CUSTOM/{id}/notification.html", produces = OrcidApiConstants.HTML_UTF)
public @ResponseBody String getCustomNotificationHtml(HttpServletResponse response, @PathVariable("id") String id) {
Notification notification = notificationManager.findByOrcidAndId(getCurrentUserOrcid(), Long.valueOf(id));
response.addHeader("X-Robots-Tag", "noindex");
if (notification instanceof NotificationCustom) {
return ((NotificationCustom) notification).getBodyHtml();
} else {
return "Notification is of wrong type";
}
}
@RequestMapping(value = "/PERMISSION/{id}/notification.html", produces = OrcidApiConstants.HTML_UTF)
public ModelAndView getPermissionNotificationHtml(@PathVariable("id") String id) {
ModelAndView mav = new ModelAndView();
Notification notification = notificationManager.findByOrcidAndId(getCurrentUserOrcid(), Long.valueOf(id));
addSourceDescription(notification);
mav.addObject("notification", notification);
mav.setViewName("notification/add_activities_notification");
mav.addObject("noIndex", true);
return mav;
}
@RequestMapping(value = "/AMENDED/{id}/notification.html", produces = OrcidApiConstants.HTML_UTF)
public ModelAndView getAmendedNotificationHtml(@PathVariable("id") String id) {
ModelAndView mav = new ModelAndView();
Notification notification = notificationManager.findByOrcidAndId(getCurrentUserOrcid(), Long.valueOf(id));
addSourceDescription(notification);
mav.addObject("notification", notification);
mav.addObject("emailName", notificationManager.deriveEmailFriendlyName(getEffectiveProfile()));
mav.setViewName("notification/amended_notification");
mav.addObject("noIndex", true);
return mav;
}
@RequestMapping(value = "/INSTITUTIONAL_CONNECTION/{id}/notification.html", produces = OrcidApiConstants.HTML_UTF)
public ModelAndView getInstitutionalConnectionNotificationHtml(@PathVariable("id") String id) throws UnsupportedEncodingException {
ModelAndView mav = new ModelAndView();
Notification notification = notificationManager.findByOrcidAndId(getCurrentUserOrcid(), Long.valueOf(id));
String clientId = notification.getSource().retrieveSourcePath();
ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(clientId);
String authorizationUrl = notificationManager.buildAuthorizationUrlForInstitutionalSignIn(clientDetails);
addSourceDescription(notification);
mav.addObject("notification", notification);
mav.addObject("baseUri", getBaseUri());
mav.addObject("clientId", clientId);
mav.addObject("authorizationUrl", authorizationUrl);
mav.setViewName("notification/institutional_connection_notification");
mav.addObject("noIndex", true);
return mav;
}
@RequestMapping(value = "{id}/read.json")
public @ResponseBody Notification flagAsRead(HttpServletResponse response, @PathVariable("id") String id) {
String currentUserOrcid = getCurrentUserOrcid();
notificationManager.flagAsRead(currentUserOrcid, Long.valueOf(id));
response.addHeader("X-Robots-Tag", "noindex");
return notificationManager.findByOrcidAndId(currentUserOrcid, Long.valueOf(id));
}
@RequestMapping(value = "{id}/archive.json")
public @ResponseBody Notification flagAsArchived(HttpServletResponse response, @PathVariable("id") String id) {
String currentUserOrcid = getCurrentUserOrcid();
notificationManager.flagAsArchived(currentUserOrcid, Long.valueOf(id), false);
response.addHeader("X-Robots-Tag", "noindex");
return notificationManager.findByOrcidAndId(currentUserOrcid, Long.valueOf(id));
}
@RequestMapping(value = "{id}/action", method = RequestMethod.GET)
public ModelAndView executeAction(@PathVariable("id") String id, @RequestParam(value = "target") String redirectUri) {
notificationManager.setActionedAndReadDate(getCurrentUserOrcid(), Long.valueOf(id));
return new ModelAndView("redirect:" + redirectUri);
}
@RequestMapping(value = "/encrypted/{encryptedId}/action", method = RequestMethod.GET)
public ModelAndView executeAction(@PathVariable("encryptedId") String encryptedId) {
String idString;
try {
idString = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedId), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Problem decoding " + encryptedId, e);
}
Long id = Long.valueOf(idString);
ActionableNotificationEntity notification = (ActionableNotificationEntity) notificationManager.findActionableNotificationEntity(id);
String redirectUrl = notification.getAuthorizationUrl();
String notificationOrcid = notification.getProfile().getId();
OrcidProfileUserDetails user = getCurrentUser();
if (user != null) {
// The user is logged in
if (!user.getOrcid().equals(notificationOrcid)) {
return new ModelAndView("wrong_user");
}
} else {
redirectUrl += "&orcid=" + notificationOrcid;
}
notificationManager.setActionedAndReadDate(notificationOrcid, id);
return new ModelAndView("redirect:" + redirectUrl);
}
@RequestMapping(value = "{id}/suppressAlert.json")
public @ResponseBody void suppressAlert(HttpServletResponse response, @PathVariable("id") String id) {
userSession.getSuppressedNotificationAlertIds().add(Long.valueOf(id));
response.addHeader("X-Robots-Tag", "noindex");
}
@RequestMapping(value = "/frequencies/{encryptedEmail}/email-frequencies.json", method = RequestMethod.GET)
public @ResponseBody Preferences getDefaultPreference(HttpServletRequest request, HttpServletResponse response, @PathVariable("encryptedEmail") String encryptedEmail) throws UnsupportedEncodingException {
String decryptedEmail = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedEmail), "UTF-8"));
OrcidProfile profile = orcidProfileManager.retrieveOrcidProfileByEmail(decryptedEmail);
response.addHeader("X-Robots-Tag", "noindex");
Preferences pref = profile.getOrcidInternal().getPreferences();
return pref != null ? pref : new Preferences();
}
@RequestMapping(value = "/frequencies/{encryptedEmail}/email-frequencies.json", method = RequestMethod.POST)
public @ResponseBody Preferences setPreference(HttpServletRequest request, HttpServletResponse response, @RequestBody Preferences preferences, @PathVariable("encryptedEmail") String encryptedEmail) throws UnsupportedEncodingException {
String decryptedEmail = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedEmail), "UTF-8"));
OrcidProfile profile = orcidProfileManager.retrieveOrcidProfileByEmail(decryptedEmail);
orcidProfileManager.updatePreferences(profile.getOrcidIdentifier().getPath(), preferences);
response.addHeader("X-Robots-Tag", "noindex");
return preferences;
}
@RequestMapping(value = "/frequencies/{encryptedEmail}", method = RequestMethod.GET)
public ModelAndView getNotificationFrequenciesWindow(HttpServletRequest request,
@PathVariable("encryptedEmail") String encryptedEmail) throws Exception {
ModelAndView result = null;
String decryptedEmail = encryptionManager.decryptForExternalUse(new String(Base64.decodeBase64(encryptedEmail), "UTF-8"));
OrcidProfile profile = orcidProfileManager.retrieveOrcidProfileByEmail(decryptedEmail);
String primaryEmail = profile.getOrcidBio().getContactDetails().retrievePrimaryEmail().getValue();
if (decryptedEmail.equals(primaryEmail)) {
result = new ModelAndView("email_frequency");
result.addObject("primaryEmail", primaryEmail);
result.addObject("noIndex", true);
}
return result;
}
private void addSourceDescription(Notification notification) {
Source source = notification.getSource();
if (source != null) {
String sourcePath = source.retrieveSourcePath();
if (sourcePath != null) {
ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(sourcePath);
if (clientDetails != null) {
notification.setSourceDescription(clientDetails.getClientDescription());
}
}
}
}
}