diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java index f60ac3c98edb..e36e2c42c79b 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/ContentGenerator.java @@ -11,27 +11,20 @@ import static org.apache.commons.lang.StringUtils.EMPTY; import java.io.ByteArrayOutputStream; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; -import java.util.Optional; -import javax.annotation.Resource; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.dspace.content.Item; -import org.dspace.content.crosswalk.StreamDisseminationCrosswalk; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; import org.dspace.core.Email; import org.dspace.core.I18nUtil; -import org.dspace.discovery.IndexableObject; import org.dspace.eperson.EPerson; -import org.dspace.subscriptions.service.SubscriptionGenerator; -import org.springframework.beans.factory.annotation.Autowired; - +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; /** @@ -39,31 +32,30 @@ * which will handle the logic of sending the emails * in case of 'content' subscriptionType */ -@SuppressWarnings("rawtypes") -public class ContentGenerator implements SubscriptionGenerator { +public class ContentGenerator { private final Logger log = LogManager.getLogger(ContentGenerator.class); + private final ConfigurationService configurationService = DSpaceServicesFactory.getInstance() + .getConfigurationService(); - @SuppressWarnings("unchecked") - @Resource(name = "entityDissemination") - private Map entityType2Disseminator = new HashMap(); - - @Autowired - private ItemService itemService; - @Override - public void notifyForSubscriptions(Context context, EPerson ePerson, - List indexableComm, - List indexableColl, - List indexableItems) { + public void notifyForSubscriptions(EPerson ePerson, + List indexableComm, + List indexableColl, + Map> indexableEntityByType) { try { if (Objects.nonNull(ePerson)) { Locale supportedLocale = I18nUtil.getEPersonLocale(ePerson); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "subscriptions_content")); email.addRecipient(ePerson.getEmail()); - email.addArgument(generateBodyMail(context, indexableComm)); - email.addArgument(generateBodyMail(context, indexableColl)); - email.addArgument(generateBodyMail(context, indexableItems)); + email.addArgument(configurationService.getProperty("subscription.url")); + email.addArgument(generateBodyMail("Community", indexableComm)); + email.addArgument(generateBodyMail("Collection", indexableColl)); + email.addArgument( + indexableEntityByType.entrySet().stream() + .map(entry -> generateBodyMail(entry.getKey(), entry.getValue())) + .collect(Collectors.joining("\n\n")) + ); email.send(); } } catch (Exception e) { @@ -72,18 +64,22 @@ public void notifyForSubscriptions(Context context, EPerson ePerson, } } - private String generateBodyMail(Context context, List indexableObjects) { + private String generateBodyMail(String type, List subscriptionItems) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write("\n".getBytes(UTF_8)); - if (indexableObjects.size() > 0) { - for (IndexableObject indexableObject : indexableObjects) { + if (!subscriptionItems.isEmpty()) { + out.write(("\nYou have " + subscriptionItems.size() + " subscription(s) active to type " + type + "\n") + .getBytes(UTF_8)); + for (SubscriptionItem item : subscriptionItems) { out.write("\n".getBytes(UTF_8)); - Item item = (Item) indexableObject.getIndexedObject(); - String entityType = itemService.getEntityTypeLabel(item); - Optional.ofNullable(entityType2Disseminator.get(entityType)) - .orElseGet(() -> entityType2Disseminator.get("Item")) - .disseminate(context, item, out); + out.write("List of new content for the\n".getBytes(UTF_8)); + out.write((type + " " + item.getName() + " - " + item.getUrl() + "\n") + .getBytes(UTF_8)); + + for (Entry entry : item.getItemUrlsByItemName().entrySet()) { + out.write("\n".getBytes(UTF_8)); + out.write((entry.getKey() + " - " + entry.getValue()).getBytes(UTF_8)); + } } return out.toString(); } else { @@ -96,8 +92,4 @@ private String generateBodyMail(Context context, List indexable return EMPTY; } - public void setEntityType2Disseminator(Map entityType2Disseminator) { - this.entityType2Disseminator = entityType2Disseminator; - } - } diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java index c1f9be368e27..842ff9aa0e8f 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/StatisticsGenerator.java @@ -27,7 +27,6 @@ import org.dspace.core.Email; import org.dspace.eperson.EPerson; import org.dspace.services.ConfigurationService; -import org.dspace.subscriptions.service.SubscriptionGenerator; import org.springframework.beans.factory.annotation.Autowired; @@ -38,19 +37,16 @@ * * @author Alba Aliu */ -public class StatisticsGenerator implements SubscriptionGenerator { +public class StatisticsGenerator { private static final Logger log = LogManager.getLogger(StatisticsGenerator.class); @Autowired private ConfigurationService configurationService; - @Override - public void notifyForSubscriptions(Context c, EPerson ePerson, List crisMetricsList, - List crisMetricsList1, List crisMetricsList2) { - // find statistics for all the subscribed objects + public void notifyForSubscriptions(Context c, EPerson ePerson, List crisMetricsList) { try { // send the notification to the user - if (Objects.nonNull(ePerson) && crisMetricsList.size() > 0) { + if (Objects.nonNull(ePerson) && !crisMetricsList.isEmpty()) { Email email = new Email(); String name = configurationService.getProperty("dspace.name"); File attachment = generateExcel(crisMetricsList, c); diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationService.java b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationService.java index 95272235095a..7a7c36491278 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationService.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationService.java @@ -7,7 +7,7 @@ */ package org.dspace.subscriptions; -import java.util.Set; +import java.util.List; import org.dspace.core.Context; import org.dspace.scripts.handler.DSpaceRunnableHandler; @@ -32,6 +32,6 @@ public interface SubscriptionEmailNotificationService { /** * returns a set of supported SubscriptionTypes */ - public Set getSupportedSubscriptionTypes(); + public List getSupportedSubscriptionTypes(); } diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java index 2a30b89af3f5..78024bfdd640 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationServiceImpl.java @@ -7,10 +7,12 @@ */ package org.dspace.subscriptions; +import static org.dspace.content.Item.ANY; import static org.dspace.core.Constants.COLLECTION; import static org.dspace.core.Constants.COMMUNITY; import static org.dspace.core.Constants.ITEM; import static org.dspace.core.Constants.READ; +import static org.dspace.subscriptions.SubscriptionItem.fromItem; import java.sql.SQLException; import java.util.ArrayList; @@ -18,16 +20,18 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.dspace.app.metrics.CrisMetrics; +import org.dspace.app.metrics.service.CrisMetricsService; import org.dspace.authorize.service.AuthorizeService; import org.dspace.content.Collection; import org.dspace.content.Community; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.factory.ContentServiceFactory; import org.dspace.core.Context; import org.dspace.discovery.IndexableObject; import org.dspace.eperson.EPerson; @@ -36,7 +40,6 @@ import org.dspace.scripts.DSpaceRunnable; import org.dspace.scripts.handler.DSpaceRunnableHandler; import org.dspace.subscriptions.service.DSpaceObjectUpdates; -import org.dspace.subscriptions.service.SubscriptionGenerator; import org.springframework.beans.factory.annotation.Autowired; /** @@ -48,87 +51,114 @@ public class SubscriptionEmailNotificationServiceImpl implements SubscriptionEma private static final Logger log = LogManager.getLogger(SubscriptionEmailNotificationServiceImpl.class); - private Map contentUpdates = new HashMap<>(); - @SuppressWarnings("rawtypes") - private Map subscriptionType2generators = new HashMap<>(); + private final Map contentUpdates; + private final ContentGenerator contentGenerator; + private final StatisticsGenerator statisticsGenerator; + private final List supportedSubscriptionTypes; @Autowired private AuthorizeService authorizeService; @Autowired private SubscribeService subscribeService; + @Autowired + private CrisMetricsService crisMetricsService; - @SuppressWarnings("rawtypes") public SubscriptionEmailNotificationServiceImpl(Map contentUpdates, - Map subscriptionType2generators) { + ContentGenerator contentGenerator, + StatisticsGenerator statisticsGenerator, + List supportedSubscriptionTypes) { this.contentUpdates = contentUpdates; - this.subscriptionType2generators = subscriptionType2generators; + this.contentGenerator = contentGenerator; + this.statisticsGenerator = statisticsGenerator; + this.supportedSubscriptionTypes = supportedSubscriptionTypes; } - @SuppressWarnings({ "rawtypes", "unchecked" }) public void perform(Context context, DSpaceRunnableHandler handler, String subscriptionType, String frequency) { - List communityItems = new ArrayList<>(); - List collectionsItems = new ArrayList<>(); - List items = new ArrayList<>(); + // Verify if subscriptionType is "content" or "subscription" + if (supportedSubscriptionTypes.get(0).equals(subscriptionType)) { + performForContent(context, handler, subscriptionType, frequency); + } else if (supportedSubscriptionTypes.get(1).equals(subscriptionType)) { + performForStatistics(context, subscriptionType, frequency); + } else { + throw new IllegalArgumentException( + "Currently this SubscriptionType:" + subscriptionType + " is not supported!"); + } + } + + @SuppressWarnings({ "rawtypes" }) + private void performForContent(Context context, DSpaceRunnableHandler handler, + String subscriptionType, String frequency) { try { List subscriptions = - findAllSubscriptionsBySubscriptionTypeAndFrequency(context, subscriptionType, frequency); - // Here is verified if SubscriptionType is "content" Or "statistics" as them are configured - if (subscriptionType2generators.keySet().contains(subscriptionType)) { - // the list of the person who has subscribed - int iterator = 0; - for (Subscription subscription : subscriptions) { - DSpaceObject dSpaceObject = subscription.getDSpaceObject(); - EPerson ePerson = subscription.getEPerson(); - - if (!authorizeService.authorizeActionBoolean(context, ePerson, dSpaceObject, READ, true)) { - iterator++; - continue; - } + findAllSubscriptionsBySubscriptionTypeAndFrequency(context, subscriptionType, frequency); + List communityItems = new ArrayList<>(); + List collectionsItems = new ArrayList<>(); + Map> entityItemsByEntityType = new HashMap<>(); + int iterator = 0; - if (dSpaceObject.getType() == COMMUNITY) { - List indexableCommunityItems = contentUpdates - .get(Community.class.getSimpleName().toLowerCase()) - .findUpdates(context, dSpaceObject, frequency); - communityItems.addAll(getItems(context, ePerson, indexableCommunityItems)); - } else if (dSpaceObject.getType() == COLLECTION) { - List indexableCollectionItems = contentUpdates - .get(Collection.class.getSimpleName().toLowerCase()) - .findUpdates(context, dSpaceObject, frequency); - collectionsItems.addAll(getItems(context, ePerson, indexableCollectionItems)); - } else if (dSpaceObject.getType() == ITEM) { - List indexableCollectionItems = contentUpdates - .get(Item.class.getSimpleName().toLowerCase()) - .findUpdates(context, dSpaceObject, frequency); - items.addAll(getItems(context, ePerson, indexableCollectionItems)); - } else { + for (Subscription subscription : subscriptions) { + DSpaceObject dSpaceObject = subscription.getDSpaceObject(); + EPerson ePerson = subscription.getEPerson(); + + if (!authorizeService.authorizeActionBoolean(context, ePerson, dSpaceObject, READ, true)) { + iterator++; + continue; + } + + switch (dSpaceObject.getType()) { + case COMMUNITY: + List indexableCommunityItems = getItems( + context, ePerson, + contentUpdates.get(Community.class.getSimpleName().toLowerCase()) + .findUpdates(context, dSpaceObject, frequency) + ); + communityItems.add(fromItem(dSpaceObject, indexableCommunityItems)); + break; + case COLLECTION: + List indexableCollectionItems = getItems( + context, ePerson, + contentUpdates.get(Collection.class.getSimpleName().toLowerCase()) + .findUpdates(context, dSpaceObject, frequency) + ); + collectionsItems.add(fromItem(dSpaceObject, indexableCollectionItems)); + break; + case ITEM: + List indexableEntityItems = getItems( + context, ePerson, contentUpdates.get(Item.class.getSimpleName().toLowerCase()) + .findUpdates(context, dSpaceObject, frequency) + ); + String dspaceType = ContentServiceFactory + .getInstance().getDSpaceObjectService(dSpaceObject) + .getMetadataFirstValue(dSpaceObject, "dspace", "entity", "type", ANY); + + entityItemsByEntityType.computeIfAbsent(dspaceType, k -> new ArrayList<>()) + .add(fromItem(dSpaceObject, indexableEntityItems)); + break; + default: log.warn("found an invalid DSpace Object type ({}) among subscriptions to send", dSpaceObject.getType()); continue; - } + } - if (iterator < subscriptions.size() - 1) { - // as the subscriptions are ordered by eperson id, so we send them by ePerson - if (ePerson.equals(subscriptions.get(iterator + 1).getEPerson())) { - iterator++; - continue; - } else { - subscriptionType2generators.get(subscriptionType) - .notifyForSubscriptions(context, ePerson, communityItems, - collectionsItems, items); - communityItems.clear(); - collectionsItems.clear(); - } + if (iterator < subscriptions.size() - 1) { + // as the subscriptions are ordered by eperson id, so we send them by ePerson + if (ePerson.equals(subscriptions.get(iterator + 1).getEPerson())) { + iterator++; + continue; } else { - //in the end of the iteration - subscriptionType2generators.get(subscriptionType) - .notifyForSubscriptions(context, ePerson, communityItems, - collectionsItems, items); + contentGenerator.notifyForSubscriptions( + ePerson, communityItems, collectionsItems, entityItemsByEntityType + ); + communityItems.clear(); + collectionsItems.clear(); } - iterator++; + } else { + //in the end of the iteration + contentGenerator.notifyForSubscriptions( + ePerson, communityItems, collectionsItems, entityItemsByEntityType + ); } - } else { - throw new IllegalArgumentException("Currently this SubscriptionType:" + subscriptionType + - " is not supported!"); + iterator++; } } catch (Exception e) { log.error(e.getMessage(), e); @@ -137,14 +167,43 @@ public void perform(Context context, DSpaceRunnableHandler handler, String subsc } } + private void performForStatistics(Context context, String subscriptionType, String frequency) { + List subscriptions = + findAllSubscriptionsBySubscriptionTypeAndFrequency(context, subscriptionType, frequency); + List crisMetricsList = new ArrayList<>(); + int iterator = 0; + + for (Subscription subscription : subscriptions) { + EPerson ePerson = subscription.getEPerson(); + DSpaceObject dSpaceObject = subscription.getDSpaceObject(); + try { + crisMetricsList.addAll(crisMetricsService.findAllByDSO(context, dSpaceObject)); + } catch (Exception e) { + log.error(e.getMessage()); + } + if (iterator < subscriptions.size() - 1) { + if (ePerson.equals(subscriptions.get(iterator + 1).getEPerson())) { + iterator++; + continue; + } else { + statisticsGenerator.notifyForSubscriptions(context, ePerson, crisMetricsList); + } + } else { + //in the end of the iteration + statisticsGenerator.notifyForSubscriptions(context, ePerson, crisMetricsList); + } + iterator++; + } + } + @SuppressWarnings("rawtypes") private List getItems(Context context, EPerson ePerson, List indexableItems) throws SQLException { List items = new ArrayList(); - for (IndexableObject indexableitem : indexableItems) { - Item item = (Item) indexableitem.getIndexedObject(); + for (IndexableObject indexableItem : indexableItems) { + Item item = (Item) indexableItem.getIndexedObject(); if (authorizeService.authorizeActionBoolean(context, ePerson, item, READ, true)) { - items.add(indexableitem); + items.add(indexableItem); } } return items; @@ -157,25 +216,25 @@ private List getItems(Context context, EPerson ePerson, List findAllSubscriptionsBySubscriptionTypeAndFrequency(Context context, String subscriptionType, String frequency) { try { return subscribeService.findAllSubscriptionsBySubscriptionTypeAndFrequency(context, subscriptionType, - frequency) + frequency) .stream() .sorted(Comparator.comparing(s -> s.getEPerson().getID())) .collect(Collectors.toList()); } catch (SQLException e) { log.error(e.getMessage(), e); } - return new ArrayList(); + return new ArrayList<>(); } @Override - public Set getSupportedSubscriptionTypes() { - return subscriptionType2generators.keySet(); + public List getSupportedSubscriptionTypes() { + return supportedSubscriptionTypes; } } diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionItem.java b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionItem.java new file mode 100644 index 000000000000..3254635b015f --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionItem.java @@ -0,0 +1,74 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.subscriptions; + +import static java.util.stream.Collectors.toMap; + +import java.util.List; +import java.util.Map; + +import org.dspace.content.DSpaceObject; +import org.dspace.content.Item; +import org.dspace.discovery.IndexableObject; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; + +public class SubscriptionItem { + + private static final ConfigurationService configurationService = DSpaceServicesFactory.getInstance() + .getConfigurationService(); + + private String name; + private String url; + private Map itemUrlsByItemName; + + public SubscriptionItem(String name, String url, Map itemUrlsByItemName) { + this.name = name; + this.url = url; + this.itemUrlsByItemName = itemUrlsByItemName; + } + + @SuppressWarnings({ "rawtypes" }) + static SubscriptionItem fromItem(DSpaceObject dSpaceObject, List relatedItems) { + return new SubscriptionItem( + dSpaceObject.getName(), + buildUrlForItem(dSpaceObject.getHandle()), + relatedItems.stream() + .map(obj -> (Item) obj.getIndexedObject()) + .collect(toMap(Item::getName, item -> buildUrlForItem(item.getHandle()))) + ); + } + + private static String buildUrlForItem(String handle) { + return configurationService.getProperty("dspace.ui.url") + "/handle/" + handle; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Map getItemUrlsByItemName() { + return itemUrlsByItemName; + } + + public void setItemUrlsByItemName(Map itemUrlsByItemName) { + this.itemUrlsByItemName = itemUrlsByItemName; + } +} diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/service/SubscriptionGenerator.java b/dspace-api/src/main/java/org/dspace/subscriptions/service/SubscriptionGenerator.java deleted file mode 100644 index 994ada75b61b..000000000000 --- a/dspace-api/src/main/java/org/dspace/subscriptions/service/SubscriptionGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE and NOTICE files at the root of the source - * tree and available online at - * - * http://www.dspace.org/license/ - */ -package org.dspace.subscriptions.service; - -import java.util.List; - -import org.dspace.core.Context; -import org.dspace.eperson.EPerson; - -/** - * Interface Class which will be used to send email notifications to ePerson - * containing information for all list of objects. - * - * @author Alba Aliu - */ -public interface SubscriptionGenerator { - - public void notifyForSubscriptions(Context c, EPerson ePerson, List comm, List coll, List items); - -} \ No newline at end of file diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index d78479c2f23d..ffec78c450a2 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -47,6 +47,9 @@ dspace.name = DSpace at My University # Default language for metadata values default.language = en_US +# Url of subscriptions page +subscription.url = ${dspace.ui.url}/subscriptions + # Solr server/webapp. # DSpace uses Solr for all search/browse capability (and for usage statistics). # Since DSpace 7, SOLR must be installed as a stand-alone service. diff --git a/dspace/config/emails/subscriptions_content b/dspace/config/emails/subscriptions_content index fc186abbb0b9..d29f6d3debb4 100644 --- a/dspace/config/emails/subscriptions_content +++ b/dspace/config/emails/subscriptions_content @@ -1,19 +1,23 @@ ## E-mail sent to designated address about updates on subscribed items ## -## Parameters: {0} Collections updates -## {1} Communities updates -## {2} Items updates +## Parameters: {0} Link to subscriptions page +## {1} Collections updates block +## {2} Communities updates block +## {3} Entity updates block This email is sent from DSpace-CRIS based on the chosen subscription preferences. +You can manage your subscription preferences from ${params[0]} Communities ------------ -List of changed items : ${params[0]} +------------------- +${params[1]} + Collections ------------ -List of changed items : ${params[1]} +------------------- +${params[2]} + -Items ------ -List of changed items : ${params[2]} \ No newline at end of file +Entities +------------------- +${params[3]} \ No newline at end of file diff --git a/dspace/config/spring/api/subscriptions_email_configuration.xml b/dspace/config/spring/api/subscriptions_email_configuration.xml index c946a29bec7e..d64a78d5e363 100644 --- a/dspace/config/spring/api/subscriptions_email_configuration.xml +++ b/dspace/config/spring/api/subscriptions_email_configuration.xml @@ -14,16 +14,8 @@ http://www.springframework.org/schema/util/spring-util.xsd"> - - - - - - - + + @@ -32,10 +24,16 @@ + + + content + statistics + + - +