001: /*
002: * EPerson.java
003: *
004: * Version: $Revision: 2074 $
005: *
006: * Date: $Date: 2007-07-19 14:40:11 -0500 (Thu, 19 Jul 2007) $
007: *
008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040: package org.dspace.eperson;
041:
042: import java.sql.SQLException;
043: import java.util.List;
044: import java.util.Vector;
045:
046: import org.apache.log4j.Logger;
047: import org.dspace.authorize.AuthorizeException;
048: import org.dspace.authorize.AuthorizeManager;
049: import org.dspace.content.DSpaceObject;
050: import org.dspace.core.ConfigurationManager;
051: import org.dspace.core.Constants;
052: import org.dspace.core.Context;
053: import org.dspace.core.LogManager;
054: import org.dspace.core.Utils;
055: import org.dspace.event.Event;
056: import org.dspace.storage.rdbms.DatabaseManager;
057: import org.dspace.storage.rdbms.TableRow;
058: import org.dspace.storage.rdbms.TableRowIterator;
059:
060: /**
061: * Class representing an e-person.
062: *
063: * @author David Stuve
064: * @version $Revision: 2074 $
065: */
066: public class EPerson extends DSpaceObject {
067: /** The e-mail field (for sorting) */
068: public static final int EMAIL = 1;
069:
070: /** The last name (for sorting) */
071: public static final int LASTNAME = 2;
072:
073: /** The e-mail field (for sorting) */
074: public static final int ID = 3;
075:
076: /** The netid field (for sorting) */
077: public static final int NETID = 4;
078:
079: /** The e-mail field (for sorting) */
080: public static final int LANGUAGE = 5;
081:
082: /** log4j logger */
083: private static Logger log = Logger.getLogger(EPerson.class);
084:
085: /** Our context */
086: private Context myContext;
087:
088: /** The row in the table representing this eperson */
089: private TableRow myRow;
090:
091: /** Flag set when data is modified, for events */
092: private boolean modified;
093:
094: /** Flag set when metadata is modified, for events */
095: private boolean modifiedMetadata;
096:
097: /**
098: * Construct an EPerson
099: *
100: * @param context
101: * the context this object exists in
102: * @param row
103: * the corresponding row in the table
104: */
105: EPerson(Context context, TableRow row) {
106: myContext = context;
107: myRow = row;
108:
109: // Cache ourselves
110: context.cache(this , row.getIntColumn("eperson_id"));
111: modified = modifiedMetadata = false;
112: clearDetails();
113: }
114:
115: /**
116: * Get an EPerson from the database.
117: *
118: * @param context
119: * DSpace context object
120: * @param id
121: * ID of the EPerson
122: *
123: * @return the EPerson format, or null if the ID is invalid.
124: */
125: public static EPerson find(Context context, int id)
126: throws SQLException {
127: // First check the cache
128: EPerson fromCache = (EPerson) context.fromCache(EPerson.class,
129: id);
130:
131: if (fromCache != null) {
132: return fromCache;
133: }
134:
135: TableRow row = DatabaseManager.find(context, "eperson", id);
136:
137: if (row == null) {
138: return null;
139: } else {
140: return new EPerson(context, row);
141: }
142: }
143:
144: /**
145: * Find the eperson by their email address
146: *
147: * @return EPerson
148: */
149: public static EPerson findByEmail(Context context, String email)
150: throws SQLException, AuthorizeException {
151: TableRow row = DatabaseManager.findByUnique(context, "eperson",
152: "email", email);
153:
154: if (row == null) {
155: return null;
156: } else {
157: // First check the cache
158: EPerson fromCache = (EPerson) context.fromCache(
159: EPerson.class, row.getIntColumn("eperson_id"));
160:
161: if (fromCache != null) {
162: return fromCache;
163: } else {
164: return new EPerson(context, row);
165: }
166: }
167: }
168:
169: /**
170: * Find the eperson by their netid
171: *
172: * @param context
173: * DSpace context
174: * @param netid
175: * Network ID
176: *
177: * @return corresponding EPerson, or <code>null</code>
178: */
179: public static EPerson findByNetid(Context context, String netid)
180: throws SQLException {
181: if (netid == null)
182: return null;
183:
184: TableRow row = DatabaseManager.findByUnique(context, "eperson",
185: "netid", netid);
186:
187: if (row == null) {
188: return null;
189: } else {
190: // First check the cache
191: EPerson fromCache = (EPerson) context.fromCache(
192: EPerson.class, row.getIntColumn("eperson_id"));
193:
194: if (fromCache != null) {
195: return fromCache;
196: } else {
197: return new EPerson(context, row);
198: }
199: }
200: }
201:
202: /**
203: * Find the epeople that match the search query across firstname, lastname or email
204: *
205: * @param context
206: * DSpace context
207: * @param query
208: * The search string
209: *
210: * @return array of EPerson objects
211: */
212: public static EPerson[] search(Context context, String query)
213: throws SQLException {
214: return search(context, query, -1, -1);
215: }
216:
217: /**
218: * Find the epeople that match the search query across firstname, lastname or email.
219: * This method also allows offsets and limits for pagination purposes.
220: *
221: * @param context
222: * DSpace context
223: * @param query
224: * The search string
225: * @param offset
226: * Inclusive offset
227: * @param limit
228: * Maximum number of matches returned
229: *
230: * @return array of EPerson objects
231: */
232: public static EPerson[] search(Context context, String query,
233: int offset, int limit) throws SQLException {
234: String params = "%" + query.toLowerCase() + "%";
235: String dbquery = "SELECT * FROM eperson WHERE eperson_id = ? OR "
236: + "firstname ILIKE ? OR lastname ILIKE ? OR email ILIKE ? ORDER BY lastname, firstname ASC ";
237:
238: if (offset >= 0 && limit > 0) {
239: dbquery += "LIMIT " + limit + " OFFSET " + offset;
240: }
241:
242: // When checking against the eperson-id, make sure the query can be made into a number
243: Integer int_param;
244: try {
245: int_param = Integer.valueOf(query);
246: } catch (NumberFormatException e) {
247: int_param = new Integer(-1);
248: }
249:
250: // Get all the epeople that match the query
251: TableRowIterator rows = DatabaseManager.query(context, dbquery,
252: new Object[] { int_param, params, params, params });
253:
254: List epeopleRows = rows.toList();
255: EPerson[] epeople = new EPerson[epeopleRows.size()];
256:
257: for (int i = 0; i < epeopleRows.size(); i++) {
258: TableRow row = (TableRow) epeopleRows.get(i);
259:
260: // First check the cache
261: EPerson fromCache = (EPerson) context.fromCache(
262: EPerson.class, row.getIntColumn("eperson_id"));
263:
264: if (fromCache != null) {
265: epeople[i] = fromCache;
266: } else {
267: epeople[i] = new EPerson(context, row);
268: }
269: }
270:
271: return epeople;
272: }
273:
274: /**
275: * Returns the total number of epeople returned by a specific query, without the overhead
276: * of creating the EPerson objects to store the results.
277: *
278: * @param context
279: * DSpace context
280: * @param query
281: * The search string
282: *
283: * @return the number of epeople mathching the query
284: */
285: public static int searchResultCount(Context context, String query)
286: throws SQLException {
287: String dbquery = "%" + query.toLowerCase() + "%";
288: Long count;
289:
290: // When checking against the eperson-id, make sure the query can be made into a number
291: Integer int_param;
292: try {
293: int_param = Integer.valueOf(query);
294: } catch (NumberFormatException e) {
295: int_param = new Integer(-1);
296: }
297:
298: // Get all the epeople that match the query
299: TableRow row = DatabaseManager
300: .querySingle(
301: context,
302: "SELECT count(*) as count FROM eperson WHERE eperson_id = ? OR "
303: + "firstname ILIKE ? OR lastname ILIKE ? OR email ILIKE ?",
304: new Object[] { int_param, dbquery, dbquery,
305: dbquery });
306:
307: // use getIntColumn for Oracle count data
308: if ("oracle"
309: .equals(ConfigurationManager.getProperty("db.name"))) {
310: count = new Long(row.getIntColumn("count"));
311: } else //getLongColumn works for postgres
312: {
313: count = new Long(row.getLongColumn("count"));
314: }
315:
316: return count.intValue();
317: }
318:
319: /**
320: * Find all the epeople that match a particular query
321: * <ul>
322: * <li><code>ID</code></li>
323: * <li><code>LASTNAME</code></li>
324: * <li><code>EMAIL</code></li>
325: * <li><code>NETID</code></li>
326: * </ul>
327: *
328: * @return array of EPerson objects
329: */
330: public static EPerson[] findAll(Context context, int sortField)
331: throws SQLException {
332: String s;
333:
334: switch (sortField) {
335: case ID:
336: s = "eperson_id";
337: break;
338:
339: case EMAIL:
340: s = "email";
341: break;
342:
343: case LANGUAGE:
344: s = "language";
345: break;
346: case NETID:
347: s = "netid";
348: break;
349:
350: default:
351: s = "lastname";
352: }
353:
354: // NOTE: The use of 's' in the order by clause can not cause an sql
355: // injection because the string is derived from constant values above.
356: TableRowIterator rows = DatabaseManager.query(context,
357: "SELECT * FROM eperson ORDER BY " + s);
358:
359: List epeopleRows = rows.toList();
360:
361: EPerson[] epeople = new EPerson[epeopleRows.size()];
362:
363: for (int i = 0; i < epeopleRows.size(); i++) {
364: TableRow row = (TableRow) epeopleRows.get(i);
365:
366: // First check the cache
367: EPerson fromCache = (EPerson) context.fromCache(
368: EPerson.class, row.getIntColumn("eperson_id"));
369:
370: if (fromCache != null) {
371: epeople[i] = fromCache;
372: } else {
373: epeople[i] = new EPerson(context, row);
374: }
375: }
376:
377: return epeople;
378: }
379:
380: /**
381: * Create a new eperson
382: *
383: * @param context
384: * DSpace context object
385: */
386: public static EPerson create(Context context) throws SQLException,
387: AuthorizeException {
388: // authorized?
389: if (!AuthorizeManager.isAdmin(context)) {
390: throw new AuthorizeException(
391: "You must be an admin to create an EPerson");
392: }
393:
394: // Create a table row
395: TableRow row = DatabaseManager.create(context, "eperson");
396:
397: EPerson e = new EPerson(context, row);
398:
399: log.info(LogManager.getHeader(context, "create_eperson",
400: "eperson_id=" + e.getID()));
401:
402: context.addEvent(new Event(Event.CREATE, Constants.EPERSON, e
403: .getID(), null));
404:
405: return e;
406: }
407:
408: /**
409: * Delete an eperson
410: *
411: */
412: public void delete() throws SQLException, AuthorizeException,
413: EPersonDeletionException {
414: // authorized?
415: if (!AuthorizeManager.isAdmin(myContext)) {
416: throw new AuthorizeException(
417: "You must be an admin to delete an EPerson");
418: }
419:
420: // check for presence of eperson in tables that
421: // have constraints on eperson_id
422: Vector constraintList = getDeleteConstraints();
423:
424: // if eperson exists in tables that have constraints
425: // on eperson, throw an exception
426: if (constraintList.size() > 0) {
427: throw new EPersonDeletionException(constraintList);
428: }
429:
430: myContext.addEvent(new Event(Event.DELETE, Constants.EPERSON,
431: getID(), getEmail()));
432:
433: // Remove from cache
434: myContext.removeCached(this , getID());
435:
436: // XXX FIXME: This sidesteps the object model code so it won't
437: // generate REMOVE events on the affected Groups.
438:
439: // Remove any group memberships first
440: DatabaseManager
441: .updateQuery(
442: myContext,
443: "DELETE FROM EPersonGroup2EPerson WHERE eperson_id= ? ",
444: getID());
445:
446: // Remove any subscriptions
447: DatabaseManager.updateQuery(myContext,
448: "DELETE FROM subscription WHERE eperson_id= ? ",
449: getID());
450:
451: // Remove ourself
452: DatabaseManager.delete(myContext, myRow);
453:
454: log.info(LogManager.getHeader(myContext, "delete_eperson",
455: "eperson_id=" + getID()));
456: }
457:
458: /**
459: * Get the e-person's internal identifier
460: *
461: * @return the internal identifier
462: */
463: public int getID() {
464: return myRow.getIntColumn("eperson_id");
465: }
466:
467: /**
468: * Get the e-person's language
469: *
470: * @return language
471: */
472: public String getLanguage() {
473: return myRow.getStringColumn("language");
474: }
475:
476: /**
477: * Set the EPerson's laguage
478: *
479: * @param s
480: * language
481: */
482: public void setLanguage(String language) {
483: if (language != null) {
484: language = language.toLowerCase();
485: }
486:
487: myRow.setColumn("language", language);
488: }
489:
490: public String getHandle() {
491: // No Handles for e-people
492: return null;
493: }
494:
495: /**
496: * Get the e-person's email address
497: *
498: * @return their email address
499: */
500: public String getEmail() {
501: return myRow.getStringColumn("email");
502: }
503:
504: /**
505: * Set the EPerson's email
506: *
507: * @param s
508: * the new email
509: */
510: public void setEmail(String s) {
511: if (s != null) {
512: s = s.toLowerCase();
513: }
514:
515: myRow.setColumn("email", s);
516: modified = true;
517: }
518:
519: /**
520: * Get the e-person's netid
521: *
522: * @return their netid
523: */
524: public String getNetid() {
525: return myRow.getStringColumn("netid");
526: }
527:
528: /**
529: * Set the EPerson's netid
530: *
531: * @param s
532: * the new netid
533: */
534: public void setNetid(String s) {
535: if (s != null) {
536: s = s.toLowerCase();
537: }
538:
539: myRow.setColumn("netid", s);
540: modified = true;
541: }
542:
543: /**
544: * Get the e-person's full name, combining first and last name in a
545: * displayable string.
546: *
547: * @return their full name
548: */
549: public String getFullName() {
550: String f = myRow.getStringColumn("firstname");
551: String l = myRow.getStringColumn("lastname");
552:
553: if ((l == null) && (f == null)) {
554: return getEmail();
555: } else if (f == null) {
556: return l;
557: } else {
558: return (f + " " + l);
559: }
560: }
561:
562: /**
563: * Get the eperson's first name.
564: *
565: * @return their first name
566: */
567: public String getFirstName() {
568: return myRow.getStringColumn("firstname");
569: }
570:
571: /**
572: * Set the eperson's first name
573: *
574: * @param firstname
575: * the person's first name
576: */
577: public void setFirstName(String firstname) {
578: myRow.setColumn("firstname", firstname);
579: modified = true;
580: }
581:
582: /**
583: * Get the eperson's last name.
584: *
585: * @return their last name
586: */
587: public String getLastName() {
588: return myRow.getStringColumn("lastname");
589: }
590:
591: /**
592: * Set the eperson's last name
593: *
594: * @param lastname
595: * the person's last name
596: */
597: public void setLastName(String lastname) {
598: myRow.setColumn("lastname", lastname);
599: modified = true;
600: }
601:
602: /**
603: * Indicate whether the user can log in
604: *
605: * @param login
606: * boolean yes/no
607: */
608: public void setCanLogIn(boolean login) {
609: myRow.setColumn("can_log_in", login);
610: modified = true;
611: }
612:
613: /**
614: * Can the user log in?
615: *
616: * @return boolean, yes/no
617: */
618: public boolean canLogIn() {
619: return myRow.getBooleanColumn("can_log_in");
620: }
621:
622: /**
623: * Set require cert yes/no
624: *
625: * @param isrequired
626: * boolean yes/no
627: */
628: public void setRequireCertificate(boolean isrequired) {
629: myRow.setColumn("require_certificate", isrequired);
630: modified = true;
631: }
632:
633: /**
634: * Get require certificate or not
635: *
636: * @return boolean, yes/no
637: */
638: public boolean getRequireCertificate() {
639: return myRow.getBooleanColumn("require_certificate");
640: }
641:
642: /**
643: * Indicate whether the user self-registered
644: *
645: * @param sr
646: * boolean yes/no
647: */
648: public void setSelfRegistered(boolean sr) {
649: myRow.setColumn("self_registered", sr);
650: modified = true;
651: }
652:
653: /**
654: * Can the user log in?
655: *
656: * @return boolean, yes/no
657: */
658: public boolean getSelfRegistered() {
659: return myRow.getBooleanColumn("self_registered");
660: }
661:
662: /**
663: * Get the value of a metadata field
664: *
665: * @param field
666: * the name of the metadata field to get
667: *
668: * @return the value of the metadata field
669: *
670: * @exception IllegalArgumentException
671: * if the requested metadata field doesn't exist
672: */
673: public String getMetadata(String field) {
674: return myRow.getStringColumn(field);
675: }
676:
677: /**
678: * Set a metadata value
679: *
680: * @param field
681: * the name of the metadata field to get
682: * @param value
683: * value to set the field to
684: *
685: * @exception IllegalArgumentException
686: * if the requested metadata field doesn't exist
687: */
688: public void setMetadata(String field, String value) {
689: myRow.setColumn(field, value);
690: modifiedMetadata = true;
691: addDetails(field);
692: }
693:
694: /**
695: * Set the EPerson's password
696: *
697: * @param s
698: * the new email
699: */
700: public void setPassword(String s) {
701: // FIXME: encoding
702: String encoded = Utils.getMD5(s);
703:
704: myRow.setColumn("password", encoded);
705: modified = true;
706: }
707:
708: /**
709: * Check EPerson's password
710: *
711: * @param attempt
712: * the password attempt
713: * @return boolean successful/unsuccessful
714: */
715: public boolean checkPassword(String attempt) {
716: String encoded = Utils.getMD5(attempt);
717:
718: return (encoded.equals(myRow.getStringColumn("password")));
719: }
720:
721: /**
722: * Update the EPerson
723: */
724: public void update() throws SQLException, AuthorizeException {
725: // Check authorisation - if you're not the eperson
726: // see if the authorization system says you can
727: if (!myContext.ignoreAuthorization()
728: && ((myContext.getCurrentUser() == null) || (getID() != myContext
729: .getCurrentUser().getID()))) {
730: AuthorizeManager.authorizeAction(myContext, this ,
731: Constants.WRITE);
732: }
733:
734: DatabaseManager.update(myContext, myRow);
735:
736: log.info(LogManager.getHeader(myContext, "update_eperson",
737: "eperson_id=" + getID()));
738:
739: if (modified) {
740: myContext.addEvent(new Event(Event.MODIFY,
741: Constants.EPERSON, getID(), null));
742: modified = false;
743: }
744: if (modifiedMetadata) {
745: myContext.addEvent(new Event(Event.MODIFY_METADATA,
746: Constants.EPERSON, getID(), getDetails()));
747: modifiedMetadata = false;
748: clearDetails();
749: }
750: }
751:
752: /**
753: * Return <code>true</code> if <code>other</code> is the same EPerson as
754: * this object, <code>false</code> otherwise
755: *
756: * @param other
757: * object to compare to
758: *
759: * @return <code>true</code> if object passed in represents the same
760: * eperson as this object
761: */
762: public boolean obsolete_equals(Object other) {
763: if (!(other instanceof EPerson)) {
764: return false;
765: }
766:
767: return (getID() == ((EPerson) other).getID());
768: }
769:
770: /**
771: * return type found in Constants
772: */
773: public int getType() {
774: return Constants.EPERSON;
775: }
776:
777: /**
778: * Check for presence of EPerson in tables that have constraints on
779: * EPersons. Called by delete() to determine whether the eperson can
780: * actually be deleted.
781: *
782: * An EPerson cannot be deleted if it exists in the item, workflowitem, or
783: * tasklistitem tables.
784: *
785: * @return Vector of tables that contain a reference to the eperson.
786: */
787: public Vector getDeleteConstraints() throws SQLException {
788: Vector<String> tableList = new Vector<String>();
789:
790: // check for eperson in item table
791: TableRowIterator tri = DatabaseManager.query(myContext,
792: "SELECT * from item where submitter_id= ? ", getID());
793:
794: if (tri.hasNext()) {
795: tableList.add("item");
796: }
797:
798: tri.close();
799:
800: // check for eperson in workflowitem table
801: tri = DatabaseManager.query(myContext,
802: "SELECT * from workflowitem where owner= ? ", getID());
803:
804: if (tri.hasNext()) {
805: tableList.add("workflowitem");
806: }
807:
808: tri.close();
809:
810: // check for eperson in tasklistitem table
811: tri = DatabaseManager.query(myContext,
812: "SELECT * from tasklistitem where eperson_id= ? ",
813: getID());
814:
815: if (tri.hasNext()) {
816: tableList.add("tasklistitem");
817: }
818:
819: tri.close();
820:
821: // the list of tables can be used to construct an error message
822: // explaining to the user why the eperson cannot be deleted.
823: return tableList;
824: }
825:
826: public String getName() {
827: return getEmail();
828: }
829:
830: }
|