001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/velocity/tags/sakai_2-4-1/tool/src/java/org/sakaiproject/cheftool/NewPagedResourceAction.java $
003: * $Id: NewPagedResourceAction.java 7247 2006-03-29 21:09:51Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.cheftool;
021:
022: import java.util.List;
023: import java.util.Vector;
024:
025: import org.sakaiproject.cheftool.api.Menu;
026: import org.sakaiproject.cheftool.api.MenuItem;
027: import org.sakaiproject.cheftool.menu.MenuEntry;
028: import org.sakaiproject.cheftool.menu.MenuField;
029: import org.sakaiproject.courier.api.ObservingCourier;
030: import org.sakaiproject.entity.api.Entity;
031: import org.sakaiproject.event.api.SessionState;
032: import org.sakaiproject.util.StringUtil;
033:
034: /**
035: * <p>
036: * PagedResourceAction is a base class that handles paged display of lists of Resourecs.
037: * </p>
038: */
039: public abstract class NewPagedResourceAction extends
040: VelocityPortletPaneledAction {
041: /** The default number of items per page. */
042: protected static final int DEFAULT_PAGE_SIZE = 15;
043:
044: /** portlet configuration parameter names. */
045: protected static final String PARAM_PAGESIZE = "pagesize";
046:
047: /** state attribute names. */
048: protected static final String STATE_VIEW_ID = "view-id";
049:
050: protected static final String STATE_TOP_PAGE_ITEM = "item-top";
051:
052: protected static final String STATE_PAGESIZE = "page-size";
053:
054: protected static final String STATE_TOTAL_PAGENUMBER = "total_page_number";
055:
056: protected static final String STATE_NUM_ITEMS = "num-items";
057:
058: protected static final String STATE_NEXT_PAGE_EXISTS = "item-next-page";
059:
060: protected static final String STATE_PREV_PAGE_EXISTS = "item-prev-page";
061:
062: protected static final String STATE_GO_NEXT_PAGE = "item-go-next-page";
063:
064: protected static final String STATE_GO_PREV_PAGE = "item-go-prev-page";
065:
066: protected static final String STATE_GO_NEXT = "item-go-next";
067:
068: protected static final String STATE_GO_PREV = "item-go-prev";
069:
070: protected static final String STATE_NEXT_EXISTS = "item-next";
071:
072: protected static final String STATE_PREV_EXISTS = "item-prev";
073:
074: protected static final String STATE_GO_FIRST_PAGE = "item-go-first-page";
075:
076: protected static final String STATE_GO_LAST_PAGE = "item-go-last-page";
077:
078: protected static final String STATE_SEARCH = "search";
079:
080: protected static final String STATE_MANUAL_REFRESH = "manual";
081:
082: protected static final String STATE_GOTO_PAGE = "goto-page";
083:
084: protected static final String STATE_CURRENT_PAGE = "current-page";
085:
086: protected static final String STATE_SELECTED_VIEW = "selected_view";
087:
088: protected static final String STATE_PAGING = "paging";
089:
090: /** Form fields. */
091: protected static final String FORM_SEARCH = "search";
092:
093: protected static final String FORM_PAGE_NUMBER = "page_number";
094:
095: /**
096: * Implement this to return alist of all the resources that there are to page. Sort them as appropriate, and apply search criteria.
097: */
098: protected abstract List readAllResources(SessionState state);
099:
100: /**
101: * Return the total page number
102: */
103: protected int totalPageNumber(SessionState state) {
104: return ((Integer) state.getAttribute(STATE_TOTAL_PAGENUMBER))
105: .intValue();
106:
107: } // totalPageNumber
108:
109: /**
110: * Add the menus for search.
111: */
112: protected void addSearchMenus(Menu bar, SessionState state) {
113: bar.add(new MenuField(FORM_SEARCH, "2ndToolbarForm",
114: "doSearch", (String) state.getAttribute(STATE_SEARCH)));
115: bar.add(new MenuEntry("Search", null, true,
116: MenuItem.CHECKED_NA, "doSearch", "2ndToolbarForm"));
117: if (state.getAttribute(STATE_SEARCH) != null) {
118: bar.add(new MenuEntry("Clear Search", "doSearch_clear"));
119: }
120:
121: } // addSearchMenus
122:
123: /**
124: * Populate the state object, if needed, concerning paging
125: */
126: protected void initState(SessionState state,
127: VelocityPortlet portlet, JetspeedRunData rundata) {
128: super .initState(state, portlet, rundata);
129:
130: if (state.getAttribute(STATE_PAGESIZE) == null) {
131: PortletConfig config = portlet.getPortletConfig();
132:
133: try {
134: Integer size = new Integer(config
135: .getInitParameter(PARAM_PAGESIZE));
136: if (size.intValue() <= 0) {
137: size = new Integer(DEFAULT_PAGE_SIZE);
138: if (Log.getLogger("chef").isDebugEnabled())
139: Log
140: .debug(
141: "chef",
142: this
143: + ".initState: size parameter invalid 1: "
144: + config
145: .getInitParameter(PARAM_PAGESIZE));
146: }
147: state.setAttribute(STATE_PAGESIZE, size);
148: } catch (Exception any) {
149: if (Log.getLogger("chef").isDebugEnabled())
150: Log.debug("chef", this
151: + ".initState: size parameter invalid 2: "
152: + any.toString());
153: state.setAttribute(STATE_PAGESIZE, new Integer(
154: DEFAULT_PAGE_SIZE));
155: }
156: }
157:
158: if (state.getAttribute(STATE_CURRENT_PAGE) == null) {
159: state.setAttribute(STATE_CURRENT_PAGE, new Integer(1));
160: }
161:
162: if (state.getAttribute(STATE_PAGING) == null) {
163: state.setAttribute(STATE_PAGING, Boolean.FALSE);
164: }
165:
166: if (state.getAttribute(STATE_TOTAL_PAGENUMBER) == null) {
167: state.setAttribute(STATE_TOTAL_PAGENUMBER, new Integer(1));
168: }
169: } // initState
170:
171: /**
172: * Prepare the current page of items to display.
173: *
174: * @return List of items to display on this page.
175: */
176: protected List prepPage(SessionState state) {
177: List rv = new Vector();
178:
179: // read all items
180: List allItems = readAllResources(state);
181:
182: if (allItems == null) {
183: return rv;
184: }
185:
186: // access the page size
187: int pageSize = ((Integer) state.getAttribute(STATE_PAGESIZE))
188: .intValue();
189:
190: // set the total page number
191: int totalPageNumber = 1;
192: int listSize = allItems.size();
193: if ((listSize % pageSize) > 0) {
194: totalPageNumber = listSize / pageSize + 1;
195: } else {
196: totalPageNumber = listSize / pageSize;
197: }
198: state.setAttribute(STATE_TOTAL_PAGENUMBER, new Integer(
199: totalPageNumber));
200:
201: boolean paged = ((Boolean) state.getAttribute(STATE_PAGING))
202: .booleanValue();
203: if (!paged) {
204: // no paging, return all items
205: // if the total page is greater than 1, set the STATE_NEXT_PAGE_EXISTS
206: if (totalPageNumber > 1) {
207: state.setAttribute(STATE_NEXT_PAGE_EXISTS, "");
208: } else {
209: state.removeAttribute(STATE_NEXT_PAGE_EXISTS);
210: }
211: state.removeAttribute(STATE_PREV_PAGE_EXISTS);
212: return allItems;
213: } else {
214:
215: // cleanup prior prep
216: state.removeAttribute(STATE_NUM_ITEMS);
217:
218: // are we going next or prev, first or last page?
219: boolean goNextPage = state.getAttribute(STATE_GO_NEXT_PAGE) != null;
220: boolean goPrevPage = state.getAttribute(STATE_GO_PREV_PAGE) != null;
221: boolean goFirstPage = state
222: .getAttribute(STATE_GO_FIRST_PAGE) != null;
223: boolean goLastPage = state.getAttribute(STATE_GO_LAST_PAGE) != null;
224: state.removeAttribute(STATE_GO_NEXT_PAGE);
225: state.removeAttribute(STATE_GO_PREV_PAGE);
226: state.removeAttribute(STATE_GO_FIRST_PAGE);
227: state.removeAttribute(STATE_GO_LAST_PAGE);
228:
229: // are we going next or prev item?
230: boolean goNext = state.getAttribute(STATE_GO_NEXT) != null;
231: boolean goPrev = state.getAttribute(STATE_GO_PREV) != null;
232: state.removeAttribute(STATE_GO_NEXT);
233: state.removeAttribute(STATE_GO_PREV);
234:
235: boolean goViewPage = state.getAttribute(STATE_GOTO_PAGE) != null;
236:
237: // if we have no prev page and do have a top item, then we will stay "pined" to the top
238: boolean pinToTop = ((state
239: .getAttribute(STATE_TOP_PAGE_ITEM) != null)
240: && (state.getAttribute(STATE_PREV_PAGE_EXISTS) == null)
241: && !goNextPage
242: && !goPrevPage
243: && !goNext
244: && !goPrev
245: && !goFirstPage && !goLastPage && !goViewPage);
246:
247: // if we have no next page and do have a top item, then we will stay "pined" to the bottom
248: boolean pinToBottom = ((state
249: .getAttribute(STATE_TOP_PAGE_ITEM) != null)
250: && (state.getAttribute(STATE_NEXT_PAGE_EXISTS) == null)
251: && !goNextPage
252: && !goPrevPage
253: && !goNext
254: && !goPrev
255: && !goFirstPage && !goLastPage && !goViewPage);
256:
257: // how many items, total
258: int numItems = allItems.size();
259:
260: if (numItems == 0) {
261: return rv;
262: }
263:
264: // save the number of messges
265: state.setAttribute(STATE_NUM_ITEMS, new Integer(numItems));
266:
267: // find the position of the item that is the top first on the page
268: int posStart = 0;
269: String itemIdAtTheTopOfThePage = (String) state
270: .getAttribute(STATE_TOP_PAGE_ITEM);
271: if (itemIdAtTheTopOfThePage != null) {
272: // find the next page
273: posStart = findResourceInList(allItems,
274: itemIdAtTheTopOfThePage);
275:
276: // if missing, start at the top
277: if (posStart == -1) {
278: posStart = 0;
279: }
280: }
281:
282: // if going to the next page, adjust
283: if (state.getAttribute(STATE_GOTO_PAGE) != null) {
284: int gotoPage = ((Integer) state
285: .getAttribute(STATE_GOTO_PAGE)).intValue();
286: int currentPage = ((Integer) state
287: .getAttribute(STATE_CURRENT_PAGE)).intValue();
288: posStart += pageSize * (gotoPage - currentPage);
289: } else if (goNextPage) {
290: posStart += pageSize;
291: }
292:
293: // if going to the prev page, adjust
294: else if (goPrevPage) {
295: posStart -= pageSize;
296: if (posStart < 0)
297: posStart = 0;
298: }
299:
300: // if going to the first page, adjust
301: else if (goFirstPage) {
302: posStart = 0;
303: }
304:
305: // if going to the last page, adjust
306: else if (goLastPage) {
307: posStart = numItems - pageSize;
308: if (posStart < 0)
309: posStart = 0;
310: }
311:
312: // pinning
313: if (pinToTop) {
314: posStart = 0;
315: } else if (pinToBottom) {
316: posStart = numItems - pageSize;
317: if (posStart < 0)
318: posStart = 0;
319: }
320:
321: // get the last page fully displayed
322: /*
323: * if (posStart + pageSize > numItems) { posStart = numItems - pageSize; if (posStart < 0) posStart = 0; }
324: */
325:
326: // compute the end to a page size, adjusted for the number of items available
327: int posEnd = posStart + (pageSize - 1);
328: if (posEnd >= numItems)
329: posEnd = numItems - 1;
330: int numItemsOnThisPage = (posEnd - posStart) + 1;
331:
332: // select the items on this page
333: for (int i = posStart; i <= posEnd; i++) {
334: rv.add(allItems.get(i));
335: }
336:
337: // save which item is at the top of the page
338: Entity itemAtTheTopOfThePage = (Entity) allItems
339: .get(posStart);
340: state.setAttribute(STATE_TOP_PAGE_ITEM,
341: itemAtTheTopOfThePage.getId());
342:
343: // which item starts the next page (if any)
344: int next = posStart + pageSize;
345: if (next < numItems) {
346: state.setAttribute(STATE_NEXT_PAGE_EXISTS, "");
347: } else {
348: state.removeAttribute(STATE_NEXT_PAGE_EXISTS);
349: }
350:
351: // which item ends the prior page (if any)
352: int prev = posStart - 1;
353: if (prev >= 0) {
354: state.setAttribute(STATE_PREV_PAGE_EXISTS, "");
355: } else {
356: state.removeAttribute(STATE_PREV_PAGE_EXISTS);
357: }
358:
359: if (state.getAttribute(STATE_VIEW_ID) != null) {
360: int viewPos = findResourceInList(allItems,
361: (String) state.getAttribute(STATE_VIEW_ID));
362:
363: // are we moving to the next item
364: if (goNext) {
365: // advance
366: viewPos++;
367: if (viewPos >= numItems)
368: viewPos = numItems - 1;
369: }
370:
371: // are we moving to the prev item
372: if (goPrev) {
373: // retreat
374: viewPos--;
375: if (viewPos < 0)
376: viewPos = 0;
377: }
378:
379: // update the view item
380: state.setAttribute(STATE_VIEW_ID, ((Entity) allItems
381: .get(viewPos)).getId());
382:
383: // if the view item is no longer on the current page, adjust the page
384: // Note: next time through this will get processed
385: if (viewPos < posStart) {
386: state.setAttribute(STATE_GO_PREV_PAGE, "");
387: } else if (viewPos > posEnd) {
388: state.setAttribute(STATE_GO_NEXT_PAGE, "");
389: }
390:
391: if (viewPos > 0) {
392: state.setAttribute(STATE_PREV_EXISTS, "");
393: } else {
394: state.removeAttribute(STATE_PREV_EXISTS);
395: }
396:
397: if (viewPos < numItems - 1) {
398: state.setAttribute(STATE_NEXT_EXISTS, "");
399: } else {
400: state.removeAttribute(STATE_NEXT_EXISTS);
401: }
402: }
403:
404: if (state.getAttribute(STATE_GOTO_PAGE) != null) {
405: state.setAttribute(STATE_CURRENT_PAGE, state
406: .getAttribute(STATE_GOTO_PAGE));
407: state.removeAttribute(STATE_GOTO_PAGE);
408: }
409: return rv;
410: }
411:
412: } // prepPage
413:
414: /**
415: * Handle a view indecated page request
416: */
417: public void doView_page(RunData runData, Context context) {
418: // access the portlet element id to find our state
419: String peid = ((JetspeedRunData) runData).getJs_peid();
420: SessionState state = ((JetspeedRunData) runData)
421: .getPortletSessionState(peid);
422:
423: // set the flag to go to the next item on the next view
424: String page = runData.getParameters().getString(
425: FORM_PAGE_NUMBER);
426: state.setAttribute(STATE_GOTO_PAGE, new Integer(page));
427:
428: } // doView_page
429:
430: /**
431: * Handle a next-item (view) request.
432: */
433: public void doView_next(RunData runData, Context context) {
434: // access the portlet element id to find our state
435: String peid = ((JetspeedRunData) runData).getJs_peid();
436: SessionState state = ((JetspeedRunData) runData)
437: .getPortletSessionState(peid);
438:
439: // set the flag to go to the next item on the next view
440: state.setAttribute(STATE_GO_NEXT, "");
441:
442: // set the page number
443: int page = ((Integer) state.getAttribute(STATE_CURRENT_PAGE))
444: .intValue();
445: state.setAttribute(STATE_CURRENT_PAGE, new Integer(page + 1));
446:
447: } // doView_next
448:
449: /**
450: * Handle a first-item page (list) request.
451: */
452: public void doList_first(RunData runData, Context context) {
453: // access the portlet element id to find our state
454: String peid = ((JetspeedRunData) runData).getJs_peid();
455: SessionState state = ((JetspeedRunData) runData)
456: .getPortletSessionState(peid);
457:
458: // set the flag to go to the next item on the next view
459: state.setAttribute(STATE_GO_FIRST_PAGE, "");
460:
461: // set the page number
462: state.setAttribute(STATE_CURRENT_PAGE, new Integer(1));
463:
464: } // doList_first
465:
466: /**
467: * Handle a last-item page (list) request.
468: */
469: public void doList_last(RunData runData, Context context) {
470: // access the portlet element id to find our state
471: String peid = ((JetspeedRunData) runData).getJs_peid();
472: SessionState state = ((JetspeedRunData) runData)
473: .getPortletSessionState(peid);
474:
475: // set the flag to go to the next item on the next view
476: state.setAttribute(STATE_GO_LAST_PAGE, "");
477:
478: // set the page number
479: state.setAttribute(STATE_CURRENT_PAGE, new Integer(
480: totalPageNumber(state)));
481:
482: } // doList_last
483:
484: /**
485: * Handle a next-page (list) request.
486: */
487: public void doList_next(RunData runData, Context context) {
488: // access the portlet element id to find our state
489: String peid = ((JetspeedRunData) runData).getJs_peid();
490: SessionState state = ((JetspeedRunData) runData)
491: .getPortletSessionState(peid);
492:
493: // set the flag to go to the next page on the next list
494: state.setAttribute(STATE_GO_NEXT_PAGE, "");
495:
496: // set the page number
497: int page = ((Integer) state.getAttribute(STATE_CURRENT_PAGE))
498: .intValue();
499: state.setAttribute(STATE_CURRENT_PAGE, new Integer(page + 1));
500:
501: // %%% ?? doList(runData, context);
502:
503: } // doList_next
504:
505: /**
506: * Handle a prev-item (view) request.
507: */
508: public void doView_prev(RunData runData, Context context) {
509: // access the portlet element id to find our state
510: String peid = ((JetspeedRunData) runData).getJs_peid();
511: SessionState state = ((JetspeedRunData) runData)
512: .getPortletSessionState(peid);
513:
514: // set the flag to go to the prev item on the next view
515: state.setAttribute(STATE_GO_PREV, "");
516:
517: // set the page number
518: int page = ((Integer) state.getAttribute(STATE_CURRENT_PAGE))
519: .intValue();
520: state.setAttribute(STATE_CURRENT_PAGE, new Integer(page - 1));
521:
522: } // doView_prev
523:
524: /**
525: * Handle a prev-page (list) request.
526: */
527: public void doList_prev(RunData runData, Context context) {
528: // access the portlet element id to find our state
529: String peid = ((JetspeedRunData) runData).getJs_peid();
530: SessionState state = ((JetspeedRunData) runData)
531: .getPortletSessionState(peid);
532:
533: // set the flag to go to the prev page on the next list
534: state.setAttribute(STATE_GO_PREV_PAGE, "");
535:
536: // set the page number
537: int page = ((Integer) state.getAttribute(STATE_CURRENT_PAGE))
538: .intValue();
539: state.setAttribute(STATE_CURRENT_PAGE, new Integer(page - 1));
540:
541: } // doList_prev
542:
543: /**
544: * Handle a Search request.
545: */
546: public void doSearch(RunData runData, Context context) {
547: // access the portlet element id to find our state
548: String peid = ((JetspeedRunData) runData).getJs_peid();
549: SessionState state = ((JetspeedRunData) runData)
550: .getPortletSessionState(peid);
551:
552: // read the search form field into the state object
553: String search = StringUtil.trimToNull(runData.getParameters()
554: .getString(FORM_SEARCH));
555:
556: // set the flag to go to the prev page on the next list
557: if (search == null) {
558: state.removeAttribute(STATE_SEARCH);
559: } else {
560: state.setAttribute(STATE_SEARCH, search);
561: }
562:
563: // start paging again from the top of the list
564: resetPaging(state);
565:
566: // if we are searching, turn off auto refresh
567: if (search != null) {
568: ObservingCourier observer = (ObservingCourier) state
569: .getAttribute(STATE_OBSERVER);
570: if (observer != null) {
571: observer.disable();
572: }
573: }
574:
575: // else turn it back on
576: else {
577: enableObserver(state);
578: }
579:
580: } // doSearch
581:
582: /**
583: * Handle a Search Clear request.
584: */
585: public void doSearch_clear(RunData runData, Context context) {
586: // access the portlet element id to find our state
587: String peid = ((JetspeedRunData) runData).getJs_peid();
588: SessionState state = ((JetspeedRunData) runData)
589: .getPortletSessionState(peid);
590:
591: // clear the search
592: state.removeAttribute(STATE_SEARCH);
593:
594: // start paging again from the top of the list
595: resetPaging(state);
596:
597: // turn on auto refresh
598: enableObserver(state);
599:
600: } // doSearch_clear
601:
602: /**
603: * Reset to the first page
604: */
605: protected void resetPaging(SessionState state) {
606: // we are changing the sort, so start from the first page again
607: state.removeAttribute(STATE_TOP_PAGE_ITEM);
608: state.setAttribute(STATE_CURRENT_PAGE, new Integer(1));
609:
610: } // resetPaging
611:
612: /**
613: * Find the resource with this id in the list.
614: *
615: * @param items
616: * The list of items.
617: * @param id
618: * The item id.
619: * @return The index position in the list of the item with this id, or -1 if not found.
620: */
621: protected int findResourceInList(List resources, String id) {
622: for (int i = 0; i < resources.size(); i++) {
623: // if this is the one, return this index
624: if (((Entity) (resources.get(i))).getId().equals(id))
625: return i;
626: }
627:
628: // not found
629: return -1;
630:
631: } // findResourceInList
632:
633: /**
634: * Toggle auto-update
635: */
636: public void doAuto(RunData data, Context context) {
637: // access the portlet element id to find our state
638: String peid = ((JetspeedRunData) data).getJs_peid();
639: SessionState state = ((JetspeedRunData) data)
640: .getPortletSessionState(peid);
641:
642: // get the observer
643: ObservingCourier observer = (ObservingCourier) state
644: .getAttribute(STATE_OBSERVER);
645: if (observer != null) {
646: boolean enabled = observer.getEnabled();
647: if (enabled) {
648: observer.disable();
649: state.setAttribute(STATE_MANUAL_REFRESH, "manual");
650: } else {
651: observer.enable();
652: state.removeAttribute(STATE_MANUAL_REFRESH);
653: }
654: }
655:
656: } // doAuto
657:
658: /**
659: * The action for when the user want's an update
660: */
661: public void doRefresh(RunData data, Context context) {
662: // access the portlet element id to find our state
663: String peid = ((JetspeedRunData) data).getJs_peid();
664: SessionState state = ((JetspeedRunData) data)
665: .getPortletSessionState(peid);
666:
667: } // doRefresh
668:
669: /**
670: * Enable the observer, unless we are in search mode, where we want it disabled.
671: */
672: public void enableObserver(SessionState state) {
673: // get the observer
674: ObservingCourier observer = (ObservingCourier) state
675: .getAttribute(STATE_OBSERVER);
676: if (observer != null) {
677: // we leave it disabled if we are searching, or if the user has last selected to be manual
678: if ((state.getAttribute(STATE_SEARCH) != null)
679: || (state.getAttribute(STATE_MANUAL_REFRESH) != null)) {
680: observer.disable();
681: } else {
682: observer.enable();
683: }
684: }
685:
686: } // enableObserver
687:
688: /**
689: * The action for toggling paging status: show all(no paging) or paging
690: */
691: public void doToggle_paging(RunData data, Context context) {
692: // access the portlet element id to find our state
693: String peid = ((JetspeedRunData) data).getJs_peid();
694: SessionState state = ((JetspeedRunData) data)
695: .getPortletSessionState(peid);
696: Boolean paging_status = (Boolean) state
697: .getAttribute(STATE_PAGING);
698: state.setAttribute(STATE_PAGING, new Boolean(!(paging_status
699: .booleanValue())));
700: if (((Boolean) state.getAttribute(STATE_PAGING)).booleanValue()) {
701: resetPaging(state);
702: }
703:
704: } // doToggle_paging
705:
706: } // PagedResourceAction
|