001: /*
002: * EditItemServlet.java
003: *
004: * Version: $Revision: 2642 $
005: *
006: * Date: $Date: 2008-02-11 23:07:57 -0600 (Mon, 11 Feb 2008) $
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.app.webui.servlet.admin;
041:
042: import java.io.BufferedInputStream;
043: import java.io.File;
044: import java.io.FileInputStream;
045: import java.io.IOException;
046: import java.io.InputStream;
047: import java.sql.SQLException;
048: import java.util.Collections;
049: import java.util.Enumeration;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.LinkedList;
053: import java.util.List;
054: import java.util.StringTokenizer;
055:
056: import javax.servlet.ServletException;
057: import javax.servlet.http.HttpServletRequest;
058: import javax.servlet.http.HttpServletResponse;
059:
060: import org.apache.log4j.Logger;
061: import org.dspace.app.webui.servlet.DSpaceServlet;
062: import org.dspace.app.webui.util.FileUploadRequest;
063: import org.dspace.app.webui.util.JSPManager;
064: import org.dspace.app.webui.util.UIUtil;
065: import org.dspace.authorize.AuthorizeException;
066: import org.dspace.authorize.AuthorizeManager;
067: import org.dspace.content.Bitstream;
068: import org.dspace.content.BitstreamFormat;
069: import org.dspace.content.Bundle;
070: import org.dspace.content.Collection;
071: import org.dspace.content.DSpaceObject;
072: import org.dspace.content.FormatIdentifier;
073: import org.dspace.content.Item;
074: import org.dspace.content.MetadataField;
075: import org.dspace.content.MetadataSchema;
076: import org.dspace.core.Constants;
077: import org.dspace.core.Context;
078: import org.dspace.core.LogManager;
079: import org.dspace.handle.HandleManager;
080: import org.dspace.license.CreativeCommons;
081:
082: /**
083: * Servlet for editing and deleting (expunging) items
084: *
085: * @author Robert Tansley
086: * @version $Revision: 2642 $
087: */
088: public class EditItemServlet extends DSpaceServlet {
089: /** User wants to delete (expunge) an item */
090: public static final int START_DELETE = 1;
091:
092: /** User confirms delete (expunge) of item */
093: public static final int CONFIRM_DELETE = 2;
094:
095: /** User updates item */
096: public static final int UPDATE_ITEM = 3;
097:
098: /** User starts withdrawal of item */
099: public static final int START_WITHDRAW = 4;
100:
101: /** User confirms withdrawal of item */
102: public static final int CONFIRM_WITHDRAW = 5;
103:
104: /** User reinstates a withdrawn item */
105: public static final int REINSTATE = 6;
106:
107: /** User starts the movement of an item */
108: public static final int START_MOVE_ITEM = 7;
109:
110: /** User confirms the movement of the item */
111: public static final int CONFIRM_MOVE_ITEM = 8;
112:
113: /** Logger */
114: private static Logger log = Logger
115: .getLogger(EditCommunitiesServlet.class);
116:
117: protected void doDSGet(Context context, HttpServletRequest request,
118: HttpServletResponse response) throws ServletException,
119: IOException, SQLException, AuthorizeException {
120: /*
121: * GET with no parameters displays "find by handle/id" form parameter
122: * item_id -> find and edit item with internal ID item_id parameter
123: * handle -> find and edit corresponding item if internal ID or Handle
124: * are invalid, "find by handle/id" form is displayed again with error
125: * message
126: */
127: int internalID = UIUtil.getIntParameter(request, "item_id");
128: String handle = request.getParameter("handle");
129: boolean showError = false;
130:
131: // See if an item ID or Handle was passed in
132: Item itemToEdit = null;
133:
134: if (internalID > 0) {
135: itemToEdit = Item.find(context, internalID);
136:
137: showError = (itemToEdit == null);
138: } else if ((handle != null) && !handle.equals("")) {
139: // resolve handle
140: DSpaceObject dso = HandleManager.resolveToObject(context,
141: handle.trim());
142:
143: // make sure it's an ITEM
144: if ((dso != null) && (dso.getType() == Constants.ITEM)) {
145: itemToEdit = (Item) dso;
146: showError = false;
147: } else {
148: showError = true;
149: }
150: }
151:
152: // Show edit form if appropriate
153: if (itemToEdit != null) {
154: // now check to see if person can edit item
155: checkEditAuthorization(context, itemToEdit);
156: showEditForm(context, request, response, itemToEdit);
157: } else {
158: if (showError) {
159: request.setAttribute("invalid.id", new Boolean(true));
160: }
161:
162: JSPManager.showJSP(request, response,
163: "/tools/get-item-id.jsp");
164: }
165: }
166:
167: protected void doDSPost(Context context,
168: HttpServletRequest request, HttpServletResponse response)
169: throws ServletException, IOException, SQLException,
170: AuthorizeException {
171: // First, see if we have a multipart request (uploading a new bitstream)
172: String contentType = request.getContentType();
173:
174: if ((contentType != null)
175: && (contentType.indexOf("multipart/form-data") != -1)) {
176: // This is a multipart request, so it's a file upload
177: processUploadBitstream(context, request, response);
178:
179: return;
180: }
181:
182: /*
183: * Then we check for a "cancel" button - if it's been pressed, we simply
184: * return to the "find by handle/id" page
185: */
186: if (request.getParameter("submit_cancel") != null) {
187: JSPManager.showJSP(request, response,
188: "/tools/get-item-id.jsp");
189:
190: return;
191: }
192:
193: /*
194: * Respond to submitted forms. Each form includes an "action" parameter
195: * indicating what needs to be done (from the constants above.)
196: */
197: int action = UIUtil.getIntParameter(request, "action");
198:
199: Item item = Item.find(context, UIUtil.getIntParameter(request,
200: "item_id"));
201:
202: String handle = HandleManager.findHandle(context, item);
203:
204: // now check to see if person can edit item
205: checkEditAuthorization(context, item);
206:
207: request.setAttribute("item", item);
208: request.setAttribute("handle", handle);
209:
210: switch (action) {
211: case START_DELETE:
212:
213: // Show "delete item" confirmation page
214: JSPManager.showJSP(request, response,
215: "/tools/confirm-delete-item.jsp");
216:
217: break;
218:
219: case CONFIRM_DELETE:
220:
221: // Delete the item - if "cancel" was pressed this would be
222: // picked up above
223: // FIXME: Don't know if this does all it should - remove Handle?
224: Collection[] collections = item.getCollections();
225:
226: // Remove item from all the collections it's in
227: for (int i = 0; i < collections.length; i++) {
228: collections[i].removeItem(item);
229: }
230:
231: JSPManager.showJSP(request, response,
232: "/tools/get-item-id.jsp");
233: context.complete();
234:
235: break;
236:
237: case UPDATE_ITEM:
238: processUpdateItem(context, request, response, item);
239:
240: break;
241:
242: case START_WITHDRAW:
243:
244: // Show "withdraw item" confirmation page
245: JSPManager.showJSP(request, response,
246: "/tools/confirm-withdraw-item.jsp");
247:
248: break;
249:
250: case CONFIRM_WITHDRAW:
251:
252: // Withdraw the item
253: item.withdraw();
254: JSPManager.showJSP(request, response,
255: "/tools/get-item-id.jsp");
256: context.complete();
257:
258: break;
259:
260: case REINSTATE:
261: item.reinstate();
262: JSPManager.showJSP(request, response,
263: "/tools/get-item-id.jsp");
264: context.complete();
265:
266: break;
267:
268: case START_MOVE_ITEM:
269: if (AuthorizeManager.isAdmin(context)) {
270: // Display move collection page with fields of collections and communities
271: Collection[] notLinkedCollections = item
272: .getCollectionsNotLinked();
273: Collection[] linkedCollections = item.getCollections();
274:
275: request.setAttribute("linkedCollections",
276: linkedCollections);
277: request.setAttribute("notLinkedCollections",
278: notLinkedCollections);
279:
280: JSPManager.showJSP(request, response,
281: "/tools/move-item.jsp");
282: } else {
283: throw new ServletException(
284: "You must be an administrator to move an item");
285: }
286:
287: break;
288:
289: case CONFIRM_MOVE_ITEM:
290: if (AuthorizeManager.isAdmin(context)) {
291: Collection fromCollection = Collection.find(context,
292: UIUtil.getIntParameter(request,
293: "collection_from_id"));
294: Collection toCollection = Collection.find(context,
295: UIUtil.getIntParameter(request,
296: "collection_to_id"));
297:
298: if (fromCollection == null || toCollection == null) {
299: throw new ServletException(
300: "Missing or incorrect collection IDs for moving item");
301: }
302:
303: item.move(fromCollection, toCollection);
304:
305: showEditForm(context, request, response, item);
306:
307: context.complete();
308: } else {
309: throw new ServletException(
310: "You must be an administrator to move an item");
311: }
312:
313: break;
314:
315: default:
316:
317: // Erm... weird action value received.
318: log.warn(LogManager.getHeader(context, "integrity_error",
319: UIUtil.getRequestLogInfo(request)));
320: JSPManager.showIntegrityError(request, response);
321: }
322: }
323:
324: /**
325: * Throw an exception if user isn't authorized to edit this item
326: *
327: * @param context
328: * @param item
329: */
330: private void checkEditAuthorization(Context c, Item item)
331: throws AuthorizeException, java.sql.SQLException {
332: if (!item.canEdit()) {
333: int userID = 0;
334:
335: // first, check if userid is set
336: if (c.getCurrentUser() != null) {
337: userID = c.getCurrentUser().getID();
338: }
339:
340: // show an error or throw an authorization exception
341: throw new AuthorizeException("EditItemServlet: User "
342: + userID + " not authorized to edit item "
343: + item.getID());
344: }
345: }
346:
347: /**
348: * Show the item edit form for a particular item
349: *
350: * @param context
351: * DSpace context
352: * @param request
353: * the HTTP request containing posted info
354: * @param response
355: * the HTTP response
356: * @param item
357: * the item
358: */
359: private void showEditForm(Context context,
360: HttpServletRequest request, HttpServletResponse response,
361: Item item) throws ServletException, IOException,
362: SQLException, AuthorizeException {
363: if (request.getParameter("cc_license_url") != null) {
364: // set or replace existing CC license
365: CreativeCommons.setLicense(context, item, request
366: .getParameter("cc_license_url"));
367: context.commit();
368: }
369:
370: // Get the handle, if any
371: String handle = HandleManager.findHandle(context, item);
372:
373: // Collections
374: Collection[] collections = item.getCollections();
375:
376: // All DC types in the registry
377: MetadataField[] types = MetadataField.findAll(context);
378:
379: // Get a HashMap of metadata field ids and a field name to display
380: HashMap metadataFields = new HashMap();
381:
382: // Get all existing Schemas
383: MetadataSchema[] schemas = MetadataSchema.findAll(context);
384: for (int i = 0; i < schemas.length; i++) {
385: String schemaName = schemas[i].getName();
386: // Get all fields for the given schema
387: MetadataField[] fields = MetadataField.findAllInSchema(
388: context, schemas[i].getSchemaID());
389: for (int j = 0; j < fields.length; j++) {
390: Integer fieldID = new Integer(fields[j].getFieldID());
391: String displayName = "";
392: displayName = schemaName
393: + "."
394: + fields[j].getElement()
395: + (fields[j].getQualifier() == null ? "" : "."
396: + fields[j].getQualifier());
397: metadataFields.put(fieldID, displayName);
398: }
399: }
400:
401: request.setAttribute("item", item);
402: request.setAttribute("handle", handle);
403: request.setAttribute("collections", collections);
404: request.setAttribute("dc.types", types);
405: request.setAttribute("metadataFields", metadataFields);
406:
407: JSPManager.showJSP(request, response,
408: "/tools/edit-item-form.jsp");
409: }
410:
411: /**
412: * Process input from the edit item form
413: *
414: * @param context
415: * DSpace context
416: * @param request
417: * the HTTP request containing posted info
418: * @param response
419: * the HTTP response
420: * @param item
421: * the item
422: */
423: private void processUpdateItem(Context context,
424: HttpServletRequest request, HttpServletResponse response,
425: Item item) throws ServletException, IOException,
426: SQLException, AuthorizeException {
427: String button = UIUtil.getSubmitButton(request, "submit");
428: /*
429: * "Cancel" handled above, so whatever happens, we need to update the
430: * item metadata. First, we remove it all, then build it back up again.
431: */
432: item.clearMetadata(Item.ANY, Item.ANY, Item.ANY, Item.ANY);
433:
434: // We'll sort the parameters by name. This ensures that DC fields
435: // of the same element/qualifier are added in the correct sequence.
436: // Get the parameters names
437: Enumeration unsortedParamNames = request.getParameterNames();
438:
439: // Put them in a list
440: List sortedParamNames = new LinkedList();
441:
442: while (unsortedParamNames.hasMoreElements()) {
443: sortedParamNames.add(unsortedParamNames.nextElement());
444: }
445:
446: // Sort the list
447: Collections.sort(sortedParamNames);
448:
449: Iterator iterator = sortedParamNames.iterator();
450:
451: while (iterator.hasNext()) {
452: String p = (String) iterator.next();
453:
454: if (p.startsWith("value")) {
455: /*
456: * It's a metadata value - it will be of the form
457: * value_element_1 OR value_element_qualifier_2 (the number
458: * being the sequence number) We use a StringTokenizer to
459: * extract these values
460: */
461: StringTokenizer st = new StringTokenizer(p, "_");
462:
463: st.nextToken(); // Skip "value"
464:
465: String schema = st.nextToken();
466:
467: String element = st.nextToken();
468:
469: String qualifier = null;
470:
471: if (st.countTokens() == 2) {
472: qualifier = st.nextToken();
473: }
474:
475: String sequenceNumber = st.nextToken();
476:
477: // Get a string with "element" for unqualified or
478: // "element_qualifier"
479: String key = MetadataField.formKey(schema, element,
480: qualifier);
481:
482: // Get the language
483: String language = request.getParameter("language_"
484: + key + "_" + sequenceNumber);
485:
486: // Empty string language = null
487: if ((language != null) && language.equals("")) {
488: language = null;
489: }
490:
491: // Get the value
492: String value = request.getParameter(p).trim();
493:
494: // If remove button pressed for this value, we don't add it
495: // back to the item. We also don't add empty values.
496: if (!(value.equals("") || button
497: .equals("submit_remove_" + key + "_"
498: + sequenceNumber))) {
499: // Value is empty, or remove button for this wasn't pressed
500: item.addMetadata(schema, element, qualifier,
501: language, value);
502: }
503: }
504: // only process bitstreams if admin
505: else if (p.startsWith("bitstream_name")
506: && AuthorizeManager.isAdmin(context)) {
507: // We have bitstream metadata
508: // First, get the bundle and bitstream ID
509: // Parameter name is bitstream_name_(bundleID)_(bitstreamID)
510: StringTokenizer st = new StringTokenizer(p, "_");
511:
512: // Ignore "bitstream" and "name"
513: st.nextToken();
514: st.nextToken();
515:
516: // Bundle ID and bitstream ID next
517: int bundleID = Integer.parseInt(st.nextToken());
518: int bitstreamID = Integer.parseInt(st.nextToken());
519:
520: Bundle bundle = Bundle.find(context, bundleID);
521: Bitstream bitstream = Bitstream.find(context,
522: bitstreamID);
523:
524: // Get the string "(bundleID)_(bitstreamID)" for finding other
525: // parameters related to this bitstream
526: String key = String.valueOf(bundleID) + "_"
527: + bitstreamID;
528:
529: // Update bitstream metadata, or delete?
530: if (button.equals("submit_delete_bitstream_" + key)) {
531: // "delete" button pressed
532: bundle.removeBitstream(bitstream);
533:
534: // Delete bundle too, if empty
535: if (bundle.getBitstreams().length == 0) {
536: item.removeBundle(bundle);
537: }
538: } else {
539: // Update the bitstream metadata
540: String name = request.getParameter(p);
541: String source = request
542: .getParameter("bitstream_source_" + key);
543: String desc = request
544: .getParameter("bitstream_description_"
545: + key);
546: int formatID = UIUtil.getIntParameter(request,
547: "bitstream_format_id_" + key);
548: String userFormatDesc = request
549: .getParameter("bitstream_user_format_description_"
550: + key);
551: int primaryBitstreamID = UIUtil
552: .getIntParameter(request, bundleID
553: + "_primary_bitstream_id");
554:
555: // Empty strings become non-null
556: if (source.equals("")) {
557: source = null;
558: }
559:
560: if (desc.equals("")) {
561: desc = null;
562: }
563:
564: if (userFormatDesc.equals("")) {
565: userFormatDesc = null;
566: }
567:
568: bitstream.setName(name);
569: bitstream.setSource(source);
570: bitstream.setDescription(desc);
571: bitstream.setFormat(BitstreamFormat.find(context,
572: formatID));
573:
574: if (primaryBitstreamID > 0) {
575: bundle
576: .setPrimaryBitstreamID(primaryBitstreamID);
577: }
578:
579: if (userFormatDesc != null) {
580: bitstream
581: .setUserFormatDescription(userFormatDesc);
582: }
583:
584: bitstream.update();
585: bundle.update();
586: }
587: }
588: }
589:
590: /*
591: * Now respond to button presses, other than "Remove" or "Delete" button
592: * presses which were dealt with in the above loop.
593: */
594: if (button.equals("submit_addfield")) {
595: // Adding a metadata field
596: int dcTypeID = UIUtil.getIntParameter(request,
597: "addfield_dctype");
598: String value = request.getParameter("addfield_value")
599: .trim();
600: String lang = request.getParameter("addfield_language");
601:
602: // Empty language = null
603: if (lang.equals("")) {
604: lang = null;
605: }
606:
607: MetadataField field = MetadataField.find(context, dcTypeID);
608: MetadataSchema schema = MetadataSchema.find(context, field
609: .getSchemaID());
610: item.addMetadata(schema.getName(), field.getElement(),
611: field.getQualifier(), lang, value);
612: }
613:
614: item.update();
615:
616: if (button.equals("submit_addcc")) {
617: // Show cc-edit page
618: request.setAttribute("item", item);
619: JSPManager.showJSP(request, response,
620: "/tools/creative-commons-edit.jsp");
621: }
622:
623: if (button.equals("submit_addbitstream")) {
624: // Show upload bitstream page
625: request.setAttribute("item", item);
626: JSPManager.showJSP(request, response,
627: "/tools/upload-bitstream.jsp");
628: } else {
629: // Show edit page again
630: showEditForm(context, request, response, item);
631: }
632:
633: // Complete transaction
634: context.complete();
635: }
636:
637: /**
638: * Process the input from the upload bitstream page
639: *
640: * @param context
641: * current DSpace context
642: * @param request
643: * current servlet request object
644: * @param response
645: * current servlet response object
646: */
647: private void processUploadBitstream(Context context,
648: HttpServletRequest request, HttpServletResponse response)
649: throws ServletException, IOException, SQLException,
650: AuthorizeException {
651: // Wrap multipart request to get the submission info
652: FileUploadRequest wrapper = new FileUploadRequest(request);
653: Bitstream b = null;
654:
655: Item item = Item.find(context, UIUtil.getIntParameter(wrapper,
656: "item_id"));
657:
658: File temp = wrapper.getFile("file");
659:
660: // Read the temp file as logo
661: InputStream is = new BufferedInputStream(new FileInputStream(
662: temp));
663:
664: // now check to see if person can edit item
665: checkEditAuthorization(context, item);
666:
667: // do we already have an ORIGINAL bundle?
668: Bundle[] bundles = item.getBundles("ORIGINAL");
669:
670: if (bundles.length < 1) {
671: // set bundle's name to ORIGINAL
672: b = item.createSingleBitstream(is, "ORIGINAL");
673: } else {
674: // we have a bundle already, just add bitstream
675: b = bundles[0].createBitstream(is);
676: }
677:
678: // Strip all but the last filename. It would be nice
679: // to know which OS the file came from.
680: String noPath = wrapper.getFilesystemName("file");
681:
682: while (noPath.indexOf('/') > -1) {
683: noPath = noPath.substring(noPath.indexOf('/') + 1);
684: }
685:
686: while (noPath.indexOf('\\') > -1) {
687: noPath = noPath.substring(noPath.indexOf('\\') + 1);
688: }
689:
690: b.setName(noPath);
691: b.setSource(wrapper.getFilesystemName("file"));
692:
693: // Identify the format
694: BitstreamFormat bf = FormatIdentifier.guessFormat(context, b);
695: b.setFormat(bf);
696: b.update();
697:
698: item.update();
699:
700: // Back to edit form
701: showEditForm(context, request, response, item);
702:
703: // Remove temp file
704: temp.delete();
705:
706: // Update DB
707: context.complete();
708: }
709: }
|