001: /*
002: * ItemListTag.java
003: *
004: * Version: $Revision: 2553 $
005: *
006: * Date: $Date: 2008-01-16 10:46:44 -0600 (Wed, 16 Jan 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.jsptag;
041:
042: import org.apache.log4j.Logger;
043: import org.dspace.app.webui.util.UIUtil;
044:
045: import org.dspace.authorize.AuthorizeManager;
046: import org.dspace.browse.BrowseException;
047: import org.dspace.browse.BrowseIndex;
048: import org.dspace.browse.CrossLinks;
049:
050: import org.dspace.content.Bitstream;
051: import org.dspace.content.Bundle;
052: import org.dspace.content.DCDate;
053: import org.dspace.content.DCValue;
054: import org.dspace.content.Item;
055: import org.dspace.content.Thumbnail;
056: import org.dspace.content.service.ItemService;
057:
058: import org.dspace.core.ConfigurationManager;
059: import org.dspace.core.Constants;
060: import org.dspace.core.Context;
061: import org.dspace.core.Utils;
062:
063: import org.dspace.sort.SortOption;
064: import org.dspace.storage.bitstore.BitstreamStorageManager;
065:
066: import java.awt.image.BufferedImage;
067:
068: import java.io.IOException;
069: import java.io.InputStream;
070: import java.io.UnsupportedEncodingException;
071:
072: import java.sql.SQLException;
073: import java.util.StringTokenizer;
074:
075: import javax.imageio.ImageIO;
076:
077: import javax.servlet.http.HttpServletRequest;
078: import javax.servlet.jsp.JspException;
079: import javax.servlet.jsp.JspWriter;
080: import javax.servlet.jsp.jstl.fmt.LocaleSupport;
081: import javax.servlet.jsp.tagext.TagSupport;
082:
083: /**
084: * Tag for display a list of items
085: *
086: * @author Robert Tansley
087: * @version $Revision: 2553 $
088: */
089: public class ItemListTag extends TagSupport {
090: private static Logger log = Logger.getLogger(ItemListTag.class);
091:
092: /** Items to display */
093: private Item[] items;
094:
095: /** Row to highlight, -1 for no row */
096: private int highlightRow = -1;
097:
098: /** Column to emphasise - null, "title" or "date" */
099: private String emphColumn;
100:
101: /** Config value of thumbnail view toggle */
102: private boolean showThumbs;
103:
104: /** Config browse/search width and height */
105: private int thumbItemListMaxWidth;
106:
107: private int thumbItemListMaxHeight;
108:
109: /** Config browse/search thumbnail link behaviour */
110: private boolean linkToBitstream = false;
111:
112: /** Config to include an edit link */
113: private boolean linkToEdit = false;
114:
115: /** Config to disable cross links */
116: private boolean disableCrossLinks = false;
117:
118: /** The default fields to be displayed when listing items */
119: private static String listFields = "dc.date.issued(date), dc.title, dc.contributor.*";
120:
121: /** The default field which is bound to the browse by date */
122: private static String dateField = "dc.date.issued";
123:
124: /** The default field which is bound to the browse by title */
125: private static String titleField = "dc.title";
126:
127: private static String authorField = "dc.contributor.*";
128:
129: private static int authorLimit = -1;
130:
131: private SortOption sortOption = null;
132:
133: public ItemListTag() {
134: super ();
135: getThumbSettings();
136: }
137:
138: public int doStartTag() throws JspException {
139: JspWriter out = pageContext.getOut();
140: HttpServletRequest hrq = (HttpServletRequest) pageContext
141: .getRequest();
142:
143: boolean emphasiseDate = false;
144: boolean emphasiseTitle = false;
145:
146: if (emphColumn != null) {
147: emphasiseDate = emphColumn.equalsIgnoreCase("date");
148: emphasiseTitle = emphColumn.equalsIgnoreCase("title");
149: }
150:
151: // get the elements to display
152: String configLine = null;
153: if (sortOption != null) {
154: if (configLine == null)
155: configLine = ConfigurationManager
156: .getProperty("webui.itemlist.sort."
157: + sortOption.getName() + ".columns");
158:
159: if (configLine == null)
160: configLine = ConfigurationManager
161: .getProperty("webui.itemlist."
162: + sortOption.getName() + ".columns");
163: }
164:
165: if (configLine == null) {
166: configLine = ConfigurationManager
167: .getProperty("webui.itemlist.columns");
168: }
169:
170: if (configLine != null) {
171: listFields = configLine;
172: }
173:
174: // get the date and title fields
175: String dateLine = ConfigurationManager
176: .getProperty("webui.browse.index.date");
177: if (dateLine != null) {
178: dateField = dateLine;
179: }
180:
181: String titleLine = ConfigurationManager
182: .getProperty("webui.browse.index.title");
183: if (titleLine != null) {
184: titleField = titleLine;
185: }
186:
187: String authorLine = ConfigurationManager
188: .getProperty("webui.browse.author-field");
189: if (authorLine != null) {
190: authorField = authorLine;
191: }
192:
193: StringTokenizer st = new StringTokenizer(listFields, ",");
194:
195: // make an array to hold all the frags that we will use
196: int columns = st.countTokens();
197: if (linkToEdit)
198: columns++;
199: String[] frags = new String[columns * items.length];
200:
201: try {
202: CrossLinks cl = new CrossLinks();
203:
204: out
205: .println("<table align=\"center\" class=\"miscTable\" summary=\"This table browse all dspace content\">");
206: out.println("<tr>");
207:
208: // Write the column headings
209: int colCount = 1;
210: boolean isDate = false;
211: boolean emph = false;
212: boolean isAuthor = false;
213:
214: while (st.hasMoreTokens()) {
215: String field = st.nextToken().toLowerCase().trim();
216: String cOddOrEven = ((colCount % 2) == 0 ? "Odd"
217: : "Even");
218:
219: // find out if the field is a date
220: if (field.indexOf("(date)") > 0) {
221: field = field.replaceAll("\\(date\\)", "");
222: isDate = true;
223: }
224:
225: // find out if this is the author column
226: if (field.equals(authorField)) {
227: isAuthor = true;
228: }
229:
230: // find out if this field needs to link out to other browse views
231: String browseType = "";
232: boolean viewFull = false;
233: if (cl.hasLink(field)) {
234: browseType = cl.getLinkType(field);
235: viewFull = BrowseIndex.getBrowseIndex(browseType)
236: .isItemIndex();
237: }
238:
239: // get the schema and the element qualifier pair
240: // (Note, the schema is not used for anything yet)
241: // (second note, I hate this bit of code. There must be
242: // a much more elegant way of doing this. Tomcat has
243: // some weird problems with variations on this code that
244: // I tried, which is why it has ended up the way it is)
245: StringTokenizer eq = new StringTokenizer(field, ".");
246:
247: String[] tokens = { "", "", "" };
248: int k = 0;
249: while (eq.hasMoreTokens()) {
250: tokens[k] = eq.nextToken().toLowerCase().trim();
251: k++;
252: }
253: String schema = tokens[0];
254: String element = tokens[1];
255: String qualifier = tokens[2];
256:
257: // find out if we are emphasising this field
258: if (field.equals(emphColumn)) {
259: emph = true;
260: } else if ((field.equals(dateField) && emphasiseDate)
261: || (field.equals(titleField) && emphasiseTitle)) {
262: emph = true;
263: }
264:
265: // prepare the strings for the header
266: String id = "t" + Integer.toString(colCount);
267: String css = "oddRow" + cOddOrEven + "Col";
268: String message = "itemlist." + field;
269:
270: // output the header
271: out.print("<th id=\""
272: + id
273: + "\" class=\""
274: + css
275: + "\">"
276: + (emph ? "<strong>" : "")
277: + LocaleSupport.getLocalizedMessage(
278: pageContext, message)
279: + (emph ? "</strong>" : "") + "</th>");
280:
281: // now prepare the frags for each of the table elements
282: for (int i = 0; i < items.length; i++) {
283: // first get hold of the relevant metadata for this column
284: DCValue[] metadataArray;
285: if (qualifier.equals("*")) {
286: metadataArray = items[i].getMetadata(schema,
287: element, Item.ANY, Item.ANY);
288: } else if (qualifier.equals("")) {
289: metadataArray = items[i].getMetadata(schema,
290: element, null, Item.ANY);
291: } else {
292: metadataArray = items[i].getMetadata(schema,
293: element, qualifier, Item.ANY);
294: }
295:
296: // save on a null check which would make the code untidy
297: if (metadataArray == null) {
298: metadataArray = new DCValue[0];
299: }
300:
301: // now prepare the content of the table division
302: String metadata = "-";
303: if (metadataArray.length > 0) {
304: // format the date field correctly
305: if (isDate) {
306: // this is to be consistent with the existing setup.
307: // seems like an odd place to put it though (FIXME)
308: String thumbs = "";
309: if (showThumbs) {
310: thumbs = getThumbMarkup(hrq, items[i]);
311: }
312: DCDate dd = new DCDate(
313: metadataArray[0].value);
314: metadata = UIUtil.displayDate(dd, false,
315: false,
316: (HttpServletRequest) pageContext
317: .getRequest())
318: + thumbs;
319: }
320: // format the title field correctly for withdrawn items (ie. don't link)
321: else if (field.equals(titleField)
322: && items[i].isWithdrawn()) {
323: metadata = Utils
324: .addEntities(metadataArray[0].value);
325: }
326: // format the title field correctly
327: else if (field.equals(titleField)) {
328: metadata = "<a href=\""
329: + hrq.getContextPath()
330: + "/handle/"
331: + items[i].getHandle()
332: + "\">"
333: + Utils
334: .addEntities(metadataArray[0].value)
335: + "</a>";
336: }
337: // format all other fields
338: else {
339: // limit the number of records if this is the author field (if
340: // -1, then the limit is the full list)
341: boolean truncated = false;
342: int loopLimit = metadataArray.length;
343: if (isAuthor) {
344: int fieldMax = (authorLimit > 0 ? authorLimit
345: : metadataArray.length);
346: loopLimit = (fieldMax > metadataArray.length ? metadataArray.length
347: : fieldMax);
348: truncated = (fieldMax < metadataArray.length);
349: log
350: .debug("Limiting output of field "
351: + field
352: + " to "
353: + Integer
354: .toString(loopLimit)
355: + " from an original "
356: + Integer
357: .toString(metadataArray.length));
358: }
359:
360: StringBuffer sb = new StringBuffer();
361: for (int j = 0; j < loopLimit; j++) {
362: String startLink = "";
363: String endLink = "";
364: if (!"".equals(browseType)
365: && !disableCrossLinks) {
366: String argument = "value";
367: if (viewFull) {
368: argument = "vfocus";
369: }
370: startLink = "<a href=\""
371: + hrq.getContextPath()
372: + "/browse?type="
373: + browseType
374: + "&"
375: + argument
376: + "="
377: + Utils
378: .addEntities(metadataArray[j].value);
379:
380: if (metadataArray[j].language != null) {
381: startLink = startLink
382: + "&"
383: + argument
384: + "_lang="
385: + Utils
386: .addEntities(metadataArray[j].language)
387: + "\">";
388: } else {
389: startLink = startLink + "\">";
390: }
391: endLink = "</a>";
392: }
393: sb.append(startLink);
394: sb
395: .append(Utils
396: .addEntities(metadataArray[j].value));
397: sb.append(endLink);
398: if (j < (loopLimit - 1)) {
399: sb.append("; ");
400: }
401: }
402: if (truncated) {
403: String etal = LocaleSupport
404: .getLocalizedMessage(
405: pageContext,
406: "itemlist.et-al");
407: sb.append(", " + etal);
408: }
409: metadata = "<em>" + sb.toString() + "</em>";
410: }
411: }
412:
413: // now prepare the XHTML frag for this division
414: String rOddOrEven;
415: if (i == highlightRow) {
416: rOddOrEven = "highlight";
417: } else {
418: rOddOrEven = ((i % 2) == 1 ? "odd" : "even");
419: }
420:
421: // prepare extra special layout requirements for dates
422: String extras = "";
423: if (isDate) {
424: extras = "nowrap=\"nowrap\" align=\"right\"";
425: }
426:
427: int idx = ((i + 1) * columns) - columns + colCount
428: - 1;
429: frags[idx] = "<td headers=\"" + id + "\" class=\""
430: + rOddOrEven + "Row" + cOddOrEven
431: + "Col\" " + extras + ">"
432: + (emph ? "<strong>" : "") + metadata
433: + (emph ? "</strong>" : "") + "</td>";
434:
435: }
436:
437: colCount++;
438: isDate = false;
439: emph = false;
440: isAuthor = false;
441: }
442:
443: // Add column for 'edit item' links
444: if (linkToEdit) {
445: String cOddOrEven = ((columns % 2) == 0 ? "Odd"
446: : "Even");
447: String id = "t" + Integer.toString(colCount);
448: String css = "oddRow" + cOddOrEven + "Col";
449:
450: // output the header
451: out.print("<th id=\"" + id + "\" class=\"" + css
452: + "\">" + (emph ? "<strong>" : "") + " " //LocaleSupport.getLocalizedMessage(pageContext, message)
453: + (emph ? "</strong>" : "") + "</th>");
454:
455: for (int i = 0; i < items.length; i++) {
456: // now prepare the XHTML frag for this division
457: String rOddOrEven;
458: if (i == highlightRow) {
459: rOddOrEven = "highlight";
460: } else {
461: rOddOrEven = ((i % 2) == 1 ? "odd" : "even");
462: }
463:
464: int idx = ((i + 1) * columns) - 1;
465: frags[idx] = "<td headers=\""
466: + id
467: + "\" class=\""
468: + rOddOrEven
469: + "Row"
470: + cOddOrEven
471: + "Col\" nowrap>"
472: + "<form method=get action=\""
473: + hrq.getContextPath()
474: + "/tools/edit-item\">"
475: + "<input type=\"hidden\" name=\"handle\" value=\""
476: + items[i].getHandle()
477: + "\" />"
478: + "<input type=\"submit\" value=\"Edit Item\" /></form>"
479: + "</td>";
480: }
481: }
482:
483: out.println("</tr>");
484:
485: // now output all the frags in the right order for the page
486: for (int i = 0; i < frags.length; i++) {
487: if ((i + 1) % columns == 1) {
488: out.println("<tr>");
489: }
490: out.println(frags[i]);
491: if ((i + 1) % columns == 0) {
492: out.println("</tr>");
493: }
494: }
495:
496: // close the table
497: out.println("</table>");
498: } catch (IOException ie) {
499: throw new JspException(ie);
500: } catch (BrowseException e) {
501: throw new JspException(e);
502: }
503:
504: return SKIP_BODY;
505: }
506:
507: public int getAuthorLimit() {
508: return authorLimit;
509: }
510:
511: public void setAuthorLimit(int al) {
512: authorLimit = al;
513: }
514:
515: public boolean getLinkToEdit() {
516: return linkToEdit;
517: }
518:
519: public void setLinkToEdit(boolean edit) {
520: this .linkToEdit = edit;
521: }
522:
523: public boolean getDisableCrossLinks() {
524: return disableCrossLinks;
525: }
526:
527: public void setDisableCrossLinks(boolean links) {
528: this .disableCrossLinks = links;
529: }
530:
531: public SortOption getSortOption() {
532: return sortOption;
533: }
534:
535: public void setSortOption(SortOption so) {
536: sortOption = so;
537: }
538:
539: /**
540: * Get the items to list
541: *
542: * @return the items
543: */
544: public Item[] getItems() {
545: return items;
546: }
547:
548: /**
549: * Set the items to list
550: *
551: * @param itemsIn
552: * the items
553: */
554: public void setItems(Item[] itemsIn) {
555: items = itemsIn;
556: }
557:
558: /**
559: * Get the row to highlight - null or -1 for no row
560: *
561: * @return the row to highlight
562: */
563: public String getHighlightrow() {
564: return String.valueOf(highlightRow);
565: }
566:
567: /**
568: * Set the row to highlight
569: *
570: * @param highlightRowIn
571: * the row to highlight or -1 for no highlight
572: */
573: public void setHighlightrow(String highlightRowIn) {
574: if ((highlightRowIn == null) || highlightRowIn.equals("")) {
575: highlightRow = -1;
576: } else {
577: try {
578: highlightRow = Integer.parseInt(highlightRowIn);
579: } catch (NumberFormatException nfe) {
580: highlightRow = -1;
581: }
582: }
583: }
584:
585: /**
586: * Get the column to emphasise - "title", "date" or null
587: *
588: * @return the column to emphasise
589: */
590: public String getEmphcolumn() {
591: return emphColumn;
592: }
593:
594: /**
595: * Set the column to emphasise - "title", "date" or null
596: *
597: * @param emphColumnIn
598: * column to emphasise
599: */
600: public void setEmphcolumn(String emphColumnIn) {
601: emphColumn = emphColumnIn;
602: }
603:
604: public void release() {
605: highlightRow = -1;
606: emphColumn = null;
607: items = null;
608: }
609:
610: /* get the required thumbnail config items */
611: private void getThumbSettings() {
612: showThumbs = ConfigurationManager
613: .getBooleanProperty("webui.browse.thumbnail.show");
614:
615: if (showThumbs) {
616: thumbItemListMaxHeight = ConfigurationManager
617: .getIntProperty("webui.browse.thumbnail.maxheight");
618:
619: if (thumbItemListMaxHeight == 0) {
620: thumbItemListMaxHeight = ConfigurationManager
621: .getIntProperty("thumbnail.maxheight");
622: }
623:
624: thumbItemListMaxWidth = ConfigurationManager
625: .getIntProperty("webui.browse.thumbnail.maxwidth");
626:
627: if (thumbItemListMaxWidth == 0) {
628: thumbItemListMaxWidth = ConfigurationManager
629: .getIntProperty("thumbnail.maxwidth");
630: }
631: }
632:
633: String linkBehaviour = ConfigurationManager
634: .getProperty("webui.browse.thumbnail.linkbehaviour");
635:
636: if (linkBehaviour != null) {
637: if (linkBehaviour.equals("bitstream")) {
638: linkToBitstream = true;
639: }
640: }
641: }
642:
643: /*
644: * Get the (X)HTML width and height attributes. As the browser is being used
645: * for scaling, we only scale down otherwise we'll get hideously chunky
646: * images. This means the media filter should be run with the maxheight and
647: * maxwidth set greater than or equal to the size of the images required in
648: * the search/browse
649: */
650: private String getScalingAttr(HttpServletRequest hrq,
651: Bitstream bitstream) throws JspException {
652: BufferedImage buf;
653:
654: try {
655: Context c = UIUtil.obtainContext(hrq);
656:
657: InputStream is = BitstreamStorageManager.retrieve(c,
658: bitstream.getID());
659:
660: //AuthorizeManager.authorizeAction(bContext, this, Constants.READ);
661: // read in bitstream's image
662: buf = ImageIO.read(is);
663: is.close();
664: } catch (SQLException sqle) {
665: throw new JspException(sqle.getMessage());
666: } catch (IOException ioe) {
667: throw new JspException(ioe.getMessage());
668: }
669:
670: // now get the image dimensions
671: float xsize = (float) buf.getWidth(null);
672: float ysize = (float) buf.getHeight(null);
673:
674: // scale by x first if needed
675: if (xsize > (float) thumbItemListMaxWidth) {
676: // calculate scaling factor so that xsize * scale = new size (max)
677: float scale_factor = (float) thumbItemListMaxWidth / xsize;
678:
679: // now reduce x size and y size
680: xsize = xsize * scale_factor;
681: ysize = ysize * scale_factor;
682: }
683:
684: // scale by y if needed
685: if (ysize > (float) thumbItemListMaxHeight) {
686: float scale_factor = (float) thumbItemListMaxHeight / ysize;
687:
688: // now reduce x size
689: // and y size
690: xsize = xsize * scale_factor;
691: ysize = ysize * scale_factor;
692: }
693:
694: StringBuffer sb = new StringBuffer("width=\"").append(xsize)
695: .append("\" height=\"").append(ysize).append("\"");
696:
697: return sb.toString();
698: }
699:
700: /* generate the (X)HTML required to show the thumbnail */
701: private String getThumbMarkup(HttpServletRequest hrq, Item item)
702: throws JspException {
703: try {
704: Context c = UIUtil.obtainContext(hrq);
705: Thumbnail thumbnail = ItemService.getThumbnail(c, item
706: .getID(), linkToBitstream);
707:
708: if (thumbnail == null) {
709: return "";
710: }
711: StringBuffer thumbFrag = new StringBuffer();
712:
713: if (linkToBitstream) {
714: Bitstream original = thumbnail.getOriginal();
715: String link = hrq.getContextPath()
716: + "/bitstream/"
717: + item.getHandle()
718: + "/"
719: + original.getSequenceID()
720: + "/"
721: + UIUtil.encodeBitstreamName(
722: original.getName(),
723: Constants.DEFAULT_ENCODING);
724: thumbFrag.append("<a target=\"_blank\" href=\"" + link
725: + "\" />");
726: } else {
727: String link = hrq.getContextPath() + "/handle/"
728: + item.getHandle();
729: thumbFrag.append("<a href=\"" + link + "\" />");
730: }
731:
732: Bitstream thumb = thumbnail.getThumb();
733: String img = hrq.getContextPath()
734: + "/retrieve/"
735: + thumb.getID()
736: + "/"
737: + UIUtil.encodeBitstreamName(thumb.getName(),
738: Constants.DEFAULT_ENCODING);
739: String alt = thumb.getName();
740: String scAttr = getScalingAttr(hrq, thumb);
741: thumbFrag.append("<img src=\"").append(img).append(
742: "\" alt=\"").append(alt + "\" ").append(scAttr)
743: .append("/></a>");
744:
745: return thumbFrag.toString();
746: } catch (SQLException sqle) {
747: throw new JspException(sqle.getMessage());
748: } catch (UnsupportedEncodingException e) {
749: throw new JspException(
750: "Server does not support DSpace's default encoding. ",
751: e);
752: }
753: }
754: }
|