From ed9f5052fade731b8eee72bb1e97ac8553d813b3 Mon Sep 17 00:00:00 2001 From: Robert Tansley Date: Fri, 18 Oct 2002 11:22:00 +0000 Subject: [PATCH] - Implemented e-mail subscription (new item notification). - Updated DB schema: * New "subscription" table * EPerson: renamed "active" to "can_log_in", and added "last_active", "self_registered" and "sub_frequency" columns (unused right now, but easier to make the changes at once) - Updated EPerson object to reflect table changes, and callers of EPerson object - Removed MIT-only filters from web.xml git-svn-id: http://scm.dspace.org/svn/repo/trunk@446 9c30dcfa-912a-0410-8fc2-9e0234be79fd --- dspace/bin/sub-daily | 51 +++ dspace/config/emails/subscription | 12 + dspace/etc/clean-database.sql | 2 + dspace/etc/database_schema.sql | 17 +- dspace/etc/update-sequences.sql | 1 + dspace/jsp/WEB-INF/web.xml | 54 +-- dspace/jsp/admin/list-epeople.jsp | 4 +- dspace/jsp/collection-home.jsp | 87 +++- dspace/jsp/mydspace/subscriptions.jsp | 138 ++++++ dspace/jsp/search/results.jsp | 16 +- .../administer/CreateAdministrator.java | 3 +- .../app/webui/servlet/LocationServlet.java | 103 ++++- .../app/webui/servlet/PasswordServlet.java | 2 +- .../app/webui/servlet/RegisterServlet.java | 12 +- .../webui/servlet/SimpleSearchServlet.java | 25 +- .../app/webui/servlet/SubscribeServlet.java | 169 +++++++ .../webui/servlet/X509CertificateServlet.java | 2 +- .../servlet/admin/EditEPersonServlet.java | 2 +- dspace/src/org/dspace/eperson/EPerson.java | 37 +- dspace/src/org/dspace/eperson/Subscribe.java | 411 ++++++++++++++++++ .../org/dspace/eperson/test/EPersonTest.java | 2 +- .../org/dspace/history/HistoryManager.java | 2 +- 22 files changed, 1030 insertions(+), 122 deletions(-) create mode 100755 dspace/bin/sub-daily create mode 100644 dspace/config/emails/subscription create mode 100644 dspace/jsp/mydspace/subscriptions.jsp create mode 100644 dspace/src/org/dspace/app/webui/servlet/SubscribeServlet.java create mode 100644 dspace/src/org/dspace/eperson/Subscribe.java diff --git a/dspace/bin/sub-daily b/dspace/bin/sub-daily new file mode 100755 index 0000000000..e24ec5e7d1 --- /dev/null +++ b/dspace/bin/sub-daily @@ -0,0 +1,51 @@ +#!/bin/sh + +########################################################################### +# +# install-configs +# +# Version: $Revision$ +# +# Date: $Date$ +# +# Copyright (c) 2001, Hewlett-Packard Company and Massachusetts +# Institute of Technology. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Hewlett-Packard Company nor the name of the +# Massachusetts Institute of Technology nor the names of their +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +########################################################################### + +# Shell script for processing daily subscriptions. Sends mail about new +# items that became available yesterday. + +# Get the DSPACE/bin directory +BINDIR=`dirname $0` + +$BINDIR/dsrun org.dspace.eperson.Subscribe diff --git a/dspace/config/emails/subscription b/dspace/config/emails/subscription new file mode 100644 index 0000000000..16ae1fdd39 --- /dev/null +++ b/dspace/config/emails/subscription @@ -0,0 +1,12 @@ +# E-mail sent to DSpace users when new items appear in collections they are +# subscribed to +# +# Parameters: {0} is the details of the new collections and items +# See org.dspace.core.Email for information on the format of this file. +# +Subject: DSpace Subscription +New items are available in the collections you have subscribed to: + +{0} + +DSpace @ MIT diff --git a/dspace/etc/clean-database.sql b/dspace/etc/clean-database.sql index 8be959464f..1bdc0787d7 100644 --- a/dspace/etc/clean-database.sql +++ b/dspace/etc/clean-database.sql @@ -72,6 +72,7 @@ DROP TABLE ItemsByTitle; DROP TABLE ItemsByAuthor; DROP TABLE HistoryState; DROP TABLE History; +DROP TABLE Subscription; DROP TABLE RegistrationData; DROP TABLE TasklistItem; DROP TABLE WorkflowItem; @@ -118,6 +119,7 @@ DROP SEQUENCE workspaceitem_seq; DROP SEQUENCE workflowitem_seq; DROP SEQUENCE tasklistitem_seq; DROP SEQUENCE registrationdata_seq; +DROP SEQUENCE subscription_seq; DROP SEQUENCE history_seq; DROP SEQUENCE historystate_seq; DROP SEQUENCE itemsbyauthor_seq; diff --git a/dspace/etc/database_schema.sql b/dspace/etc/database_schema.sql index 64d17dc4ce..c0405f176d 100644 --- a/dspace/etc/database_schema.sql +++ b/dspace/etc/database_schema.sql @@ -100,6 +100,7 @@ CREATE SEQUENCE workspaceitem_seq; CREATE SEQUENCE workflowitem_seq; CREATE SEQUENCE tasklistitem_seq; CREATE SEQUENCE registrationdata_seq; +CREATE SEQUENCE subscription_seq; CREATE SEQUENCE history_seq; CREATE SEQUENCE historystate_seq; CREATE SEQUENCE itemsbyauthor_seq; @@ -160,8 +161,11 @@ CREATE TABLE EPerson password VARCHAR(64), firstname VARCHAR(64), lastname VARCHAR(64), - active BOOL, + can_log_in BOOL, require_certificate BOOL, + self_registered BOOL, + last_active TIMESTAMP, + sub_frequency INTEGER, phone VARCHAR(32) ); @@ -391,6 +395,17 @@ CREATE TABLE RegistrationData ); +------------------------------------------------------- +-- Subscription table +------------------------------------------------------- +CREATE TABLE Subscription +( + subscription_id INTEGER PRIMARY KEY, + eperson_id INTEGER REFERENCES EPerson(eperson_id), + collection_id INTEGER REFERENCES Collection(collection_id) +); + + ------------------------------------------------------- -- History table ------------------------------------------------------- diff --git a/dspace/etc/update-sequences.sql b/dspace/etc/update-sequences.sql index 8ece0de66e..056223a81a 100644 --- a/dspace/etc/update-sequences.sql +++ b/dspace/etc/update-sequences.sql @@ -77,6 +77,7 @@ SELECT setval('workspaceitem_seq', max(workspace_item_id)) FROM workspaceitem; SELECT setval('workflowitem_seq', max(workflow_id)) FROM workflowitem; SELECT setval('tasklistitem_seq', max(tasklist_id)) FROM tasklistitem; SELECT setval('registrationdata_seq', max(registrationdata_id)) FROM registrationdata; +SELECT setval('subscription_seq', max(subscription_id)) FROM subscription; SELECT setval('history_seq', max(history_id)) FROM history; SELECT setval('historystate_seq', max(history_state_id)) FROM historystate; SELECT setval('itemsbyauthor_seq', max(items_by_author_id)) FROM itemsbyauthor; diff --git a/dspace/jsp/WEB-INF/web.xml b/dspace/jsp/WEB-INF/web.xml index 300591a895..aeaae4840c 100644 --- a/dspace/jsp/WEB-INF/web.xml +++ b/dspace/jsp/WEB-INF/web.xml @@ -92,48 +92,8 @@ - mit-only - /browse-author - - - - mit-only - /browse-date - - - - mit-only - /browse-title - - - - mit-only - /community-list - - - - mit-only - /display-item - - - - mit-only - /items-by-author - - - - mit-only - /communities/* - - - - mit-only - /retrieve/* - - - - mit-only - /simple-search + registered-only + /subscribe @@ -290,6 +250,11 @@ org.dspace.app.webui.servlet.SubmitServlet + + subscribe + org.dspace.app.webui.servlet.SubscribeServlet + + workflow org.dspace.app.webui.servlet.admin.WorkflowAbortServlet @@ -428,6 +393,11 @@ /submit + + subscribe + /subscribe + + workflow /admin/workflow diff --git a/dspace/jsp/admin/list-epeople.jsp b/dspace/jsp/admin/list-epeople.jsp index 1b49f695c5..6794d88ff7 100644 --- a/dspace/jsp/admin/list-epeople.jsp +++ b/dspace/jsp/admin/list-epeople.jsp @@ -79,7 +79,7 @@ E-mail Address Last Name First Name - Active? + Can Log In? Must Use Cert? Telephone   @@ -104,7 +104,7 @@ " size=12> - > + > > diff --git a/dspace/jsp/collection-home.jsp b/dspace/jsp/collection-home.jsp index 582e09b793..01bb41dea9 100644 --- a/dspace/jsp/collection-home.jsp +++ b/dspace/jsp/collection-home.jsp @@ -44,6 +44,10 @@ - Attributes required: - community - Collection to render home page for - collection - Community this collection is in + - last.submitted.titles - String[], titles of recent submissions + - last.submitted.urls - String[], corresponding URLs + - logged.in - Boolean, true if a user is logged in + - subscribed - Boolean, true if user is subscribed to this collection --%> <%@ taglib uri="http://www.dspace.org/dspace-tags.tld" prefix="dspace" %> @@ -61,6 +65,10 @@ request.getAttribute("last.submitted.titles"); String[] lastSubmittedURLs = (String[]) request.getAttribute("last.submitted.urls"); + boolean loggedIn = + ((Boolean) request.getAttribute("logged.in")).booleanValue(); + boolean subscribed = + ((Boolean) request.getAttribute("subscribed")).booleanValue(); // Put the metadata values into guaranteed non-null variables String name = collection.getMetadata("name"); @@ -102,26 +110,29 @@ - <%= intro %> - - <%-- Search --%> -
+ <%-- Search/Browse --%> + -
+ - - + + +
- Search:  + In: 
- for   + + Search for   +
+ or browse   
@@ -129,18 +140,49 @@
- -

Browse the collection by Title, - Author, or Date.

- <%-- HACK:
used for Netscape 4.x, which doesn't accept align=center + + + + + + +
+<%-- HACK:
used for Netscape 4.x, which doesn't accept align=center for a paragraph with a button in it --%> -
-
- -

-
-
+
+
+ + +
+
+
+
+ + + + + + +
+<% if (loggedIn && subscribed) + { %> + You are subscribed to this collection. See Subscriptions + + +<% } else { %> + + Subscribe to this collection to receive daily e-mail notification of new additions + + + +<% } %> +
+
+ + <%= intro %> + +

<%= copyright %>

@@ -155,7 +197,6 @@ } %> <%= sidebar %> - diff --git a/dspace/jsp/mydspace/subscriptions.jsp b/dspace/jsp/mydspace/subscriptions.jsp new file mode 100644 index 0000000000..3ba846f27a --- /dev/null +++ b/dspace/jsp/mydspace/subscriptions.jsp @@ -0,0 +1,138 @@ +<%-- + - subscription.jsp + - + - Version: $Revision$ + - + - Date: $Date$ + - + - Copyright (c) 2001, Hewlett-Packard Company and Massachusetts + - Institute of Technology. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions are + - met: + - + - - Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - + - - Redistributions in binary form must reproduce the above copyright + - notice, this list of conditions and the following disclaimer in the + - documentation and/or other materials provided with the distribution. + - + - - Neither the name of the Hewlett-Packard Company nor the name of the + - Massachusetts Institute of Technology nor the names of their + - contributors may be used to endorse or promote products derived from + - this software without specific prior written permission. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + - OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + - TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + - USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + - DAMAGE. + --%> + +<%-- + - Show a user's subscriptions and allow them to be modified + - + - Attributes: + - subscriptions - Collection[] - collections user is subscribed to + - communities - Community[] - communities that subscribed collections + - are in - i.e. subscriptions[0] is in communities[0]. + - updated - Boolean - if true, subscriptions have just been updated + --%> + +<%@ taglib uri="http://www.dspace.org/dspace-tags.tld" prefix="dspace" %> + +<%@ page import="org.dspace.content.Community" %> +<%@ page import="org.dspace.content.Collection" %> + +<% + Collection[] subscriptions = + (Collection[]) request.getAttribute("subscriptions"); + Community[] communities = + (Community[]) request.getAttribute("communities"); + boolean updated = + ((Boolean) request.getAttribute("updated")).booleanValue(); +%> + + + +

Your Subscriptions

+ +<% + if (updated) + { +%> +

Your subscriptions have been updated.

+<% + } + + if (subscriptions.length > 0) + { +%> +

Below are the collections you are subscribed to. You will be sent an + e-mail each day detailing new items that have become available in these + collection. On days that no new items have appeared, no e-mail will be + sent.

+ +
+ +<% + String row = "odd"; + + for (int i = 0; i < subscriptions.length; i++) + { +%> + + <%-- + - HACK: form shouldn't open here, but IE adds a carraige + - return where is placed, breaking our nice layout. + --%> + + + + + +<% + row = (row.equals("even") ? "odd" : "even" ); + } +%> +
+ <%= subscriptions[i].getMetadata("name") %> + + + +
+
+ +
+ +
+
+ +
+
+<% + } + else + { +%> +

You are not currently subscribed to any collections.

+<% + } +%> + +

Go to + My DSpace

+ +
diff --git a/dspace/jsp/search/results.jsp b/dspace/jsp/search/results.jsp index 04461a6fe8..e538feb692 100644 --- a/dspace/jsp/search/results.jsp +++ b/dspace/jsp/search/results.jsp @@ -98,12 +98,12 @@ // Scope of the search was all of DSpace. The scope control will list // "all of DSpace" and the communities. %> - + <% for (int i = 0; i < communityArray.length; i++) { %> - + <% } } @@ -112,13 +112,13 @@ // Scope of the search was within a community. Scope control will list // "all of DSpace", the community, and the collections within the community. %> - - + + <% for (int i = 0; i < collectionArray.length; i++) { %> - + <% } } @@ -126,9 +126,9 @@ { // Scope of the search is a specific collection %> - - - + + + <% } %> diff --git a/dspace/src/org/dspace/administer/CreateAdministrator.java b/dspace/src/org/dspace/administer/CreateAdministrator.java index 3a7b560480..b5e2e43c37 100644 --- a/dspace/src/org/dspace/administer/CreateAdministrator.java +++ b/dspace/src/org/dspace/administer/CreateAdministrator.java @@ -158,8 +158,9 @@ public class CreateAdministrator eperson.setLastName (lastName ); eperson.setFirstName(firstName); eperson.setPassword (password1); - eperson.setActive (true ); + eperson.setCanLogIn (true ); eperson.setRequireCertificate(false); + eperson.setSelfRegistered(false); eperson.update(); admins.addMember(eperson); diff --git a/dspace/src/org/dspace/app/webui/servlet/LocationServlet.java b/dspace/src/org/dspace/app/webui/servlet/LocationServlet.java index 57ac41d5b0..2688b7c9a4 100644 --- a/dspace/src/org/dspace/app/webui/servlet/LocationServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/LocationServlet.java @@ -41,6 +41,7 @@ package org.dspace.app.webui.servlet; import java.io.IOException; +import java.net.URLEncoder; import java.sql.SQLException; import java.util.List; import javax.servlet.ServletException; @@ -49,6 +50,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; +import org.dspace.app.webui.util.Authenticate; import org.dspace.app.webui.util.JSPManager; import org.dspace.app.webui.util.UIUtil; import org.dspace.authorize.AuthorizeException; @@ -62,6 +64,8 @@ import org.dspace.content.Item; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.LogManager; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.Subscribe; import org.dspace.handle.HandleManager; /** @@ -169,17 +173,102 @@ public class LocationServlet extends DSpaceServlet // Show community or collection home page? if (path.equals("/")) { - if (collection == null) + /* + * Work out if a browse or search was performed + * + * submit_search, submit_titles, submit_authors, submit_dates + * are the buttons + * location gives the value of the drop-down box in the form + * "/", + * "/communities/123/" or + * "/communities/123/collections/456/" + * which means it can just be prepended to the appropriate + * search or browse URL to get the right scope. + */ + String button = UIUtil.getSubmitButton(request, ""); + String location = request.getParameter("location"); + + if (button.equals("submit_titles")) { + // Redirect to browse by title + String url = request.getContextPath() + location + + "browse-title"; + response.sendRedirect(response.encodeRedirectURL(url)); + } + else if (button.equals("submit_authors")) + { + // Redirect to browse authors + String url = request.getContextPath() + location + + "browse-author"; + response.sendRedirect(response.encodeRedirectURL(url)); + } + else if (button.equals("submit_dates")) + { + // Redirect to browse by date + String url = request.getContextPath() + location + + "browse-date"; + response.sendRedirect(response.encodeRedirectURL(url)); + } + else if (button.equals("submit_search") || + request.getParameter("query") != null) + { + /* + * Have to check for search button and query - in some + * browsers, typing a query into the box and hitting + * return doesn't produce a submit button parameter. + * Redirect to appropriate search page + */ + String url = request.getContextPath() + location + + "simple-search?query=" + + URLEncoder.encode(request.getParameter("query")); + response.sendRedirect(response.encodeRedirectURL(url)); + } + else if (collection == null) + { + // Community home page, no button pressed showCommunityHome(context, request, response, community); } else { + boolean updated = false; + + // Collection home page. Check for collection home + // buttons + if (button.equals("submit_subscribe")) + { + if (context.getCurrentUser() == null) + { + // Only registered can subscribe + Authenticate.startAuthentication(context, request, + response); + return; + } + else + { + Subscribe.subscribe(context, + context.getCurrentUser(), + collection); + updated = true; + } + } + else if (button.equals("submit_unsubscribe")) + { + Subscribe.unsubscribe(context, + context.getCurrentUser(), + collection); + updated = true; + } + showCollectionHome(context, request, response, community, collection); + + if (updated) + { + context.complete(); + } } } else @@ -292,11 +381,21 @@ public class LocationServlet extends DSpaceServlet String[] itemTitles = getItemTitles(items); String[] itemLinks = getItemURLs(context, items); - // Forward to community home page + // Is the user logged in/subscribed? + EPerson e = context.getCurrentUser(); + boolean subscribed = false; + if (e != null) + { + subscribed = Subscribe.isSubscribed(context, e, collection); + } + + // Forward to collection home page request.setAttribute("last.submitted.titles", itemTitles); request.setAttribute("last.submitted.urls", itemLinks); request.setAttribute("community", community); request.setAttribute("collection", collection); + request.setAttribute("logged.in", new Boolean(e != null)); + request.setAttribute("subscribed", new Boolean(subscribed)); JSPManager.showJSP(request, response, "/collection-home.jsp"); } diff --git a/dspace/src/org/dspace/app/webui/servlet/PasswordServlet.java b/dspace/src/org/dspace/app/webui/servlet/PasswordServlet.java index f88c65bcff..e6a43fc83c 100644 --- a/dspace/src/org/dspace/app/webui/servlet/PasswordServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/PasswordServlet.java @@ -94,7 +94,7 @@ public class PasswordServlet extends DSpaceServlet boolean loggedIn = false; // Verify the password - if (eperson != null && eperson.getActive()) + if (eperson != null && eperson.canLogIn()) { // e-mail address corresponds to active account if (eperson.getRequireCertificate()) diff --git a/dspace/src/org/dspace/app/webui/servlet/RegisterServlet.java b/dspace/src/org/dspace/app/webui/servlet/RegisterServlet.java index 92fa0aab51..59ceadaa1c 100644 --- a/dspace/src/org/dspace/app/webui/servlet/RegisterServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/RegisterServlet.java @@ -145,9 +145,9 @@ public class RegisterServlet extends DSpaceServlet * An inactive (unregistered) eperson is trying to set a new p/w */ if (eperson == null || - (eperson.getActive() && registering) || - (eperson.getActive() && eperson.getRequireCertificate() && registering) || - (!registering && !eperson.getActive())) + (eperson.canLogIn() && registering) || + (eperson.canLogIn() && eperson.getRequireCertificate() && registering) || + (!registering && !eperson.canLogIn())) { // Invalid token JSPManager.showJSP(request, @@ -236,7 +236,7 @@ public class RegisterServlet extends DSpaceServlet if (eperson != null) { // Can't register an already active user - if (eperson.getActive() && registering) + if (eperson.canLogIn() && registering) { log.info(LogManager.getHeader(context, "already_registered", @@ -249,7 +249,7 @@ public class RegisterServlet extends DSpaceServlet } // Can't give new password to inactive user - if (!eperson.getActive() && !registering) + if (!eperson.canLogIn() && !registering) { log.info(LogManager.getHeader(context, "unregistered_forgot_password", @@ -407,7 +407,7 @@ public class RegisterServlet extends DSpaceServlet AccountManager.deleteToken(context, key); // Set the user as active - eperson.setActive(true); + eperson.setCanLogIn(true); eperson.update(); JSPManager.showJSP(request, response, "/register/registered.jsp"); diff --git a/dspace/src/org/dspace/app/webui/servlet/SimpleSearchServlet.java b/dspace/src/org/dspace/app/webui/servlet/SimpleSearchServlet.java index 0e6f0a3919..f1e41f985b 100644 --- a/dspace/src/org/dspace/app/webui/servlet/SimpleSearchServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/SimpleSearchServlet.java @@ -106,32 +106,9 @@ public class SimpleSearchServlet extends DSpaceServlet // do the search with the correct location. if (location != null && !location.equals("")) { - int slash = location.indexOf('/'); - - // We have a location parameter, so do a redirect - if (location.equals("ALL")) - { - // All of DSpace location - newURL = "/"; - } - else if (slash > -1) - { - // community and collection location - newURL = "/communities/" + - location.substring(0, slash) + - "/collections/" + - location.substring(slash + 1) + - "/"; - } - else - { - // community location - newURL = "/communities/" + location + "/"; - } - // Do the redirect response.sendRedirect(response.encodeRedirectURL( - request.getContextPath() + newURL + + request.getContextPath() + location + "simple-search?query=" + query)); return; diff --git a/dspace/src/org/dspace/app/webui/servlet/SubscribeServlet.java b/dspace/src/org/dspace/app/webui/servlet/SubscribeServlet.java new file mode 100644 index 0000000000..359b21fc40 --- /dev/null +++ b/dspace/src/org/dspace/app/webui/servlet/SubscribeServlet.java @@ -0,0 +1,169 @@ +/* + * SubscribeServlet.java + * + * Version: $Revision$ + * + * Date: $Date$ + * + * Copyright (c) 2001, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +package org.dspace.app.webui.servlet; + +import java.io.IOException; +import java.sql.SQLException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; + +import org.dspace.app.webui.util.JSPManager; +import org.dspace.app.webui.util.UIUtil; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Community; +import org.dspace.content.Collection; +import org.dspace.core.Constants; +import org.dspace.core.Context; +import org.dspace.core.LogManager; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.Subscribe; + +/** + * Servlet for constructing the components of the "My DSpace" page + * + * @author Robert Tansley + * @version $Revision$ + */ +public class SubscribeServlet extends DSpaceServlet +{ + /** Logger */ + private static Logger log = Logger.getLogger(SubscribeServlet.class); + + + protected void doDSGet(Context context, + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException, SQLException, AuthorizeException + { + // Simply show list of subscriptions + showSubscriptions(context, request, response, false); + } + + + protected void doDSPost(Context context, + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException, SQLException, AuthorizeException + { + /* + * Parameters: + * submit_unsubscribe - unsubscribe from a collection + * submit_clear - clear all subscriptions + * submit_cancel - cancel update - go to My DSpace. + */ + String submit = UIUtil.getSubmitButton(request, "submit"); + EPerson e = context.getCurrentUser(); + + if (submit.equals("submit_clear")) + { + // unsubscribe user from everything + Subscribe.unsubscribe(context, e, null); + + // Show the list of subscriptions + showSubscriptions(context, request, response, true); + + context.complete(); + } + else if (submit.equals("submit_unsubscribe")) + { + int collID = UIUtil.getIntParameter(request, "collection"); + Collection c = Collection.find(context, collID); + + // Sanity check - ignore duff values + if (c != null) + { + Subscribe.unsubscribe(context, e, c); + } + + // Show the list of subscriptions + showSubscriptions(context, request, response, true); + + context.complete(); + } + else + { + // Back to "My DSpace" + response.sendRedirect(response.encodeRedirectURL( + request.getContextPath() + "/mydspace")); + } + } + + + /** + * Show the list of subscriptions + * + * @param context DSpace context + * @param request HTTP request + * @param response HTTP response + * @param updated if true, write a message indicating that + * updated subscriptions have been stored + */ + private void showSubscriptions(Context context, + HttpServletRequest request, + HttpServletResponse response, + boolean updated) + throws ServletException, IOException, SQLException + { + // Subscribed collections + Collection[] subs = Subscribe.getSubscriptions(context, + context.getCurrentUser()); + + // Get corresponding communities to make the link + Community[] communities = new Community[subs.length]; + for (int i = 0; i < subs.length; i++) + { + Community[] theseComms = subs[i].getCommunities(); + // FIXME: Assume first community is container - maybe something + // more context sensitive? + communities[i] = theseComms[0]; + } + + request.setAttribute("subscriptions", subs); + request.setAttribute("communities", communities); + request.setAttribute("updated", new Boolean(updated)); + + JSPManager.showJSP(request, response, "/mydspace/subscriptions.jsp"); + } +} diff --git a/dspace/src/org/dspace/app/webui/servlet/X509CertificateServlet.java b/dspace/src/org/dspace/app/webui/servlet/X509CertificateServlet.java index bc09b6fa2c..8dd6af34c1 100644 --- a/dspace/src/org/dspace/app/webui/servlet/X509CertificateServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/X509CertificateServlet.java @@ -129,7 +129,7 @@ public class X509CertificateServlet extends DSpaceServlet EPerson eperson = X509Manager.getUser(context, certs[0]); // Do we have an e-person? - if (eperson != null && eperson.getActive()) + if (eperson != null && eperson.canLogIn()) { // Everything OK - log them in. Authenticate.loggedIn(context, request, eperson); diff --git a/dspace/src/org/dspace/app/webui/servlet/admin/EditEPersonServlet.java b/dspace/src/org/dspace/app/webui/servlet/admin/EditEPersonServlet.java index dd041e6bfd..72f35139fd 100644 --- a/dspace/src/org/dspace/app/webui/servlet/admin/EditEPersonServlet.java +++ b/dspace/src/org/dspace/app/webui/servlet/admin/EditEPersonServlet.java @@ -108,7 +108,7 @@ public class EditEPersonServlet extends DSpaceServlet ? null : request.getParameter("phone")); - e.setActive(request.getParameter("active") != null && + e.setCanLogIn(request.getParameter("active") != null && request.getParameter("active").equals("true")); e.setRequireCertificate( diff --git a/dspace/src/org/dspace/eperson/EPerson.java b/dspace/src/org/dspace/eperson/EPerson.java index 68be47231c..1c65871547 100644 --- a/dspace/src/org/dspace/eperson/EPerson.java +++ b/dspace/src/org/dspace/eperson/EPerson.java @@ -394,23 +394,23 @@ public class EPerson implements DSpaceObject /** - * Set active/inactive + * Indicate whether the user can log in * - * @param isactive boolean yes/no + * @param login boolean yes/no */ - public void setActive(boolean isactive) + public void setCanLogIn(boolean login) { - myRow.setColumn("active", isactive); + myRow.setColumn("can_log_in", login); } /** - * Get active/inactive + * Can the user log in? * - * @return isactive boolean, yes/no + * @return boolean, yes/no */ - public boolean getActive() + public boolean canLogIn() { - return myRow.getBooleanColumn("active"); + return myRow.getBooleanColumn("can_log_in"); } @@ -436,6 +436,27 @@ public class EPerson implements DSpaceObject } + /** + * Indicate whether the user self-registered + * + * @param sr boolean yes/no + */ + public void setSelfRegistered(boolean sr) + { + myRow.setColumn("self_registered", sr); + } + + /** + * Can the user log in? + * + * @return boolean, yes/no + */ + public boolean setSelfRegistered() + { + return myRow.getBooleanColumn("self_registered"); + } + + /** * Get the value of a metadata field * diff --git a/dspace/src/org/dspace/eperson/Subscribe.java b/dspace/src/org/dspace/eperson/Subscribe.java new file mode 100644 index 0000000000..872f8d049e --- /dev/null +++ b/dspace/src/org/dspace/eperson/Subscribe.java @@ -0,0 +1,411 @@ +/* + * Subscribe.java + * + * Version: $Revision$ + * + * Date: $Date$ + * + * Copyright (c) 2001, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +package org.dspace.eperson; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import javax.mail.MessagingException; + +import org.apache.log4j.Logger; + +import org.dspace.storage.rdbms.DatabaseManager; +import org.dspace.storage.rdbms.TableRow; +import org.dspace.storage.rdbms.TableRowIterator; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.AuthorizeManager; +import org.dspace.content.Collection; +import org.dspace.content.DCDate; +import org.dspace.content.DCValue; +import org.dspace.content.Item; +import org.dspace.core.ConfigurationManager; +import org.dspace.core.Context; +import org.dspace.core.Email; +import org.dspace.core.LogManager; +import org.dspace.handle.HandleManager; +import org.dspace.search.Harvest; +import org.dspace.search.HarvestedItemInfo; + + +/** + * Class defining methods for sending new item e-mail alerts to users + * + * @author Robert Tansley + * @version $Revision$ + */ +public class Subscribe +{ + /** log4j logger */ + private static Logger log = Logger.getLogger(Group.class); + + /** + * Subscribe an e-person to a collection. An e-mail will be sent every day + * a new item appears in the collection. + * + * @param context DSpace context + * @param eperson EPerson to subscribe + * @param collection Collection to subscribe to + */ + public static void subscribe(Context context, EPerson eperson, + Collection collection) + throws SQLException, AuthorizeException + { + // Check authorisation. Must be administrator, or the eperson. + if (AuthorizeManager.isAdmin(context) || + (context.getCurrentUser() != null && + context.getCurrentUser().getID() == eperson.getID())) + { + // already subscribed? + TableRowIterator r = DatabaseManager.query(context, + "SELECT * FROM subscription WHERE eperson_id=" + + eperson.getID() + " AND collection_id=" + + collection.getID()); + + if (!r.hasNext()) + { + // Not subscribed, so add them + TableRow row = DatabaseManager.create(context, "subscription"); + row.setColumn("eperson_id", eperson.getID()); + row.setColumn("collection_id", collection.getID()); + DatabaseManager.update(context, row); + + log.info(LogManager.getHeader(context, + "subscribe", + "eperson_id=" + eperson.getID() + ",collection_id=" + + collection.getID())); + } + } + else + { + throw new AuthorizeException( + "Only admin or e-person themselves can subscribe"); + } + } + + + /** + * Unsubscribe an e-person to a collection. Passing in null + * for the collection unsubscribes the e-person from all collections they + * are subscribed to. + * + * @param context DSpace context + * @param eperson EPerson to unsubscribe + * @param collection Collection to unsubscribe from + */ + public static void unsubscribe(Context context, EPerson eperson, + Collection collection) + throws SQLException, AuthorizeException + { + // Check authorisation. Must be administrator, or the eperson. + if (AuthorizeManager.isAdmin(context) || + (context.getCurrentUser() != null && + context.getCurrentUser().getID() == eperson.getID())) + { + if (collection == null) + { + // Unsubscribe from all + DatabaseManager.updateQuery(context, + "DELETE FROM subscription WHERE eperson_id=" + + eperson.getID()); + } + else + { + DatabaseManager.updateQuery(context, + "DELETE FROM subscription WHERE eperson_id=" + + eperson.getID() + " AND collection_id=" + + collection.getID()); + + log.info(LogManager.getHeader(context, + "unsubscribe", + "eperson_id=" + eperson.getID() + ",collection_id=" + + collection.getID())); + } + } + else + { + throw new AuthorizeException( + "Only admin or e-person themselves can unsubscribe"); + } + } + + + /** + * Find out which collections an e-person is subscribed to + * + * @param context DSpace context + * @param eperson EPerson + * @return array of collections e-person is subscribed to + */ + public static Collection[] getSubscriptions(Context context, + EPerson eperson) + throws SQLException + { + TableRowIterator tri = DatabaseManager.query(context, + "SELECT collection_id FROM subscription WHERE eperson_id=" + + eperson.getID()); + + List collections = new ArrayList(); + + while (tri.hasNext()) + { + TableRow row = tri.next(); + + collections.add(Collection.find(context, + row.getIntColumn("collection_id"))); + } + + Collection[] collArray = new Collection[collections.size()]; + return (Collection[]) collections.toArray(collArray); + } + + + /** + * Is that e-person subscribed to that collection? + * + * @param context DSpace context + * @param eperson find out if this e-person is subscribed + * @param collection find out if subscribed to this collection + * @return true if they are subscribed + */ + public static boolean isSubscribed(Context context, EPerson eperson, + Collection collection) + throws SQLException + { + TableRowIterator tri = DatabaseManager.query( + context, + "SELECT * FROM subscription WHERE eperson_id=" + eperson.getID() + + " AND collection_id=" + collection.getID()); + + return tri.hasNext(); + } + + + /** + * Process subscriptions. This must be invoked only once a day. + * Messages are only sent out when a collection has actually received + * new items, so that people's mailboxes are not clogged with many "no + * new items" mails.

+ * Yesterday's newly available items are included. If this is run + * at for example midday, any items that have been made available during + * the current day will not be included, but will be included in the next + * day's run.

+ * For example, if today's date is 2002-10-10 (in UTC) items made available + * during 2002-10-09 (UTC) will be included. + * + * @param context DSpace context object + */ + public static void processDaily(Context context) + throws SQLException, MessagingException, IOException + { + // Grab the subscriptions + TableRowIterator tri = DatabaseManager.query(context, + "SELECT * FROM subscription ORDER BY eperson_id"); + + EPerson currentEPerson = null; + List collections = null; // List of Collections + + // Go through the list collating subscriptions for each e-person + while (tri.hasNext()) + { + TableRow row = tri.next(); + + // Does this row relate to the same e-person as the last? + if (currentEPerson == null || + row.getIntColumn("eperson_id") != currentEPerson.getID()) + { + // New e-person. Send mail for previous e-person + if (currentEPerson != null) + { + sendEmail(context, currentEPerson, collections); + } + + currentEPerson = EPerson.find(context, + row.getIntColumn("eperson_id")); + collections = new ArrayList(); + + } + + collections.add( + Collection.find(context, + row.getIntColumn("collection_id"))); + } + + // Process the last person + if (currentEPerson != null) + { + sendEmail(context, currentEPerson, collections); + } + } + + + /** + * Sends an email to the given e-person with details of new items in the + * given collections, items that appeared yesterday. No e-mail is sent if + * there aren't any new items in any of the collections. + * + * @param context DSpace context object + * @param eperson eperson to send to + * @param collections List of collection IDs (Integers) + */ + public static void sendEmail(Context context, + EPerson eperson, List collections) + throws IOException, MessagingException, SQLException + { + // Get the start and end dates for yesterday + Date thisTimeYesterday = new Date( + System.currentTimeMillis() - (24 * 60 * 60 * 1000)); + + DCDate dcDateYesterday = new DCDate(thisTimeYesterday); + + // this time yesterday in ISO 8601, stripping the time + String isoDateYesterday = + dcDateYesterday.toString().substring(0, 10); + + String startDate = isoDateYesterday; + String endDate = isoDateYesterday + "T23:59:59Z"; + + + // FIXME: text of email should be more configurable from an + // i18n viewpoint + StringBuffer emailText = new StringBuffer(); + boolean isFirst = true; + + for (int i = 0; i < collections.size(); i++) + { + Collection c = (Collection) collections.get(i); + + List itemInfos = Harvest.harvest(context, + c, + startDate, + endDate, + true, // Need item objects + false); // But not containers + + // Only add to buffer if there are new items + if (itemInfos.size() > 0) + { + if (!isFirst) + { + emailText.append( + "\n---------------------------------------\n"); + } + else + { + isFirst = false; + } + + emailText.append("New items in collection ") + .append(c.getMetadata("name")) + .append(": ") + .append(itemInfos.size()) + .append("\n\n"); + + for (int j = 0; j < itemInfos.size(); j++) + { + HarvestedItemInfo hii = + (HarvestedItemInfo) itemInfos.get(j); + + DCValue[] titles = hii.item.getDC("title", null, Item.ANY); + emailText.append(" Title: "); + + if (titles.length > 0) + { + emailText.append(titles[0].value); + } + else + { + emailText.append("Untitled"); + } + + + DCValue[] authors = hii.item.getDC("contributor", Item.ANY, + Item.ANY); + + if (authors.length > 0) + { + emailText.append("\n Authors: ") + .append(authors[0].value); + for (int k = 1; k < authors.length; k++) + { + emailText.append("\n ") + .append(authors[k].value); + } + } + + emailText.append("\n ID: ") + .append(HandleManager.getCanonicalForm(hii.handle)) + .append("\n\n"); + } + } + } + + + // Send an e-mail if there were any new items + if (emailText.length() > 0) + { + Email email = ConfigurationManager.getEmail("subscription"); + + email.addArgument(emailText.toString()); + email.send(); + + log.info(LogManager.getHeader(context, + "sent_subscription", + "eperson_id=" + eperson.getID())); + } + } + + + /** + * Method for invoking subscriptions via the command line + * + * @param argv command-line arguments, none used yet + */ + public static void main(String[] argv) + throws Exception + { + Context context = new Context(); + processDaily(context); + // Nothing is actually written + context.abort(); + } +} diff --git a/dspace/src/org/dspace/eperson/test/EPersonTest.java b/dspace/src/org/dspace/eperson/test/EPersonTest.java index f2b2981ec4..8eef45bfd3 100644 --- a/dspace/src/org/dspace/eperson/test/EPersonTest.java +++ b/dspace/src/org/dspace/eperson/test/EPersonTest.java @@ -81,7 +81,7 @@ public class EPersonTest extends TestCase e1.setFirstName("Bob"); e1.setLastName("Brannigan"); e1.setPassword("password"); - e1.setActive(true); + e1.setCanLogIn(true); e1.setRequireCertificate(false); e1.update(); diff --git a/dspace/src/org/dspace/history/HistoryManager.java b/dspace/src/org/dspace/history/HistoryManager.java index 67416c3356..bf40616c14 100644 --- a/dspace/src/org/dspace/history/HistoryManager.java +++ b/dspace/src/org/dspace/history/HistoryManager.java @@ -806,7 +806,7 @@ public class HistoryManager eperson.getLastName()); model.add(res, model.createProperty(getPropertyId(shortname, "active")), - eperson.getActive()); + eperson.canLogIn()); model.add(res, model.createProperty(getPropertyId(shortname, "require_certificate")), eperson.getRequireCertificate());