001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.list;
029
030import org.opencms.i18n.CmsMessageContainer;
031import org.opencms.jsp.CmsJspActionElement;
032import org.opencms.main.CmsException;
033import org.opencms.main.CmsLog;
034import org.opencms.main.CmsRuntimeException;
035import org.opencms.util.CmsCollectionsGenericWrapper;
036import org.opencms.util.CmsStringUtil;
037import org.opencms.workplace.CmsDialog;
038import org.opencms.workplace.CmsWorkplaceSettings;
039import org.opencms.workplace.tools.CmsToolDialog;
040
041import java.io.IOException;
042import java.util.ArrayList;
043import java.util.HashMap;
044import java.util.Hashtable;
045import java.util.Iterator;
046import java.util.List;
047import java.util.Map;
048
049import javax.servlet.ServletException;
050import javax.servlet.http.HttpServletRequest;
051import javax.servlet.jsp.JspException;
052import javax.servlet.jsp.JspWriter;
053
054import org.apache.commons.logging.Log;
055
056/**
057 * Provides a dialog with a list widget.<p>
058 *
059 * @since 6.0.0
060 */
061public abstract class A_CmsListDialog extends CmsDialog {
062
063    /** Value for the action: execute a list item independent action of the list. */
064    public static final int ACTION_LIST_INDEPENDENT_ACTION = 83;
065
066    /** Value for the action: execute an multi action of the list. */
067    public static final int ACTION_LIST_MULTI_ACTION = 85;
068
069    /** Value for the action: search the list. */
070    public static final int ACTION_LIST_SEARCH = 81;
071
072    /** Value for the action: go to a page. */
073    public static final int ACTION_LIST_SELECT_PAGE = 82;
074
075    /** Value for the action: execute a single action of the list. */
076    public static final int ACTION_LIST_SINGLE_ACTION = 84;
077
078    /** Value for the action: sort the list. */
079    public static final int ACTION_LIST_SORT = 80;
080
081    /** Standard list button location. */
082    public static final String ICON_ACTIVE = "list/active.png";
083
084    /** Standard list button location. */
085    public static final String ICON_ADD = "list/add.png";
086
087    /** Standard list button location. */
088    public static final String ICON_DELETE = "list/delete.png";
089
090    /** Standard list button location. */
091    public static final String ICON_DETAILS_HIDE = "list/details_hide.png";
092
093    /** Standard list button location. */
094    public static final String ICON_DETAILS_SHOW = "list/details_show.png";
095
096    /** Standard list button location. */
097    public static final String ICON_DISABLED = "list/disabled.png";
098
099    /** Standard list button location. */
100    public static final String ICON_INACTIVE = "list/inactive.png";
101
102    /** Standard list button location. */
103    public static final String ICON_MINUS = "list/minus.png";
104
105    /** Standard list button location. */
106    public static final String ICON_MULTI_ACTIVATE = "list/multi_activate.png";
107
108    /** Standard list button location. */
109    public static final String ICON_MULTI_ADD = "list/multi_add.png";
110
111    /** Standard list button location. */
112    public static final String ICON_MULTI_DEACTIVATE = "list/multi_deactivate.png";
113
114    /** Standard list button location. */
115    public static final String ICON_MULTI_DELETE = "list/multi_delete.png";
116
117    /** Standard list button location. */
118    public static final String ICON_MULTI_MINUS = "list/multi_minus.png";
119
120    /** Request parameter value for the list action: a list item independent action has been triggered. */
121    public static final String LIST_INDEPENDENT_ACTION = "listindependentaction";
122
123    /** Request parameter value for the list action: a multi action has been triggered. */
124    public static final String LIST_MULTI_ACTION = "listmultiaction";
125
126    /** Request parameter value for the list action: search/filter. */
127    public static final String LIST_SEARCH = "listsearch";
128
129    /** Request parameter value for the list action: select a page. */
130    public static final String LIST_SELECT_PAGE = "listselectpage";
131
132    /** Request parameter value for the list action: a single action has been triggered. */
133    public static final String LIST_SINGLE_ACTION = "listsingleaction";
134
135    /** Request parameter value for the list action: sort. */
136    public static final String LIST_SORT = "listsort";
137
138    /** Request parameter key for the requested page. */
139    public static final String PARAM_FORMNAME = "formname";
140
141    /** Request parameter key for the list action. */
142    public static final String PARAM_LIST_ACTION = "listaction";
143
144    /** Request parameter key for the requested page. */
145    public static final String PARAM_PAGE = "page";
146
147    /** Request parameter key for search the filter. */
148    public static final String PARAM_SEARCH_FILTER = "searchfilter";
149
150    /** Request parameter key for the selected item(s). */
151    public static final String PARAM_SEL_ITEMS = "selitems";
152
153    /** Request parameter key for the column to sort the list. */
154    public static final String PARAM_SORT_COL = "sortcol";
155
156    /** The log object for this class. */
157    private static final Log LOG = CmsLog.getLog(A_CmsListDialog.class);
158
159    public static String KEY_META_DATA_CACHE = "key_meta_data_cache";
160
161    /** metadata map for all used list metadata objects. */
162    private Map<String, CmsListMetadata> m_metadatas;
163
164    /** A flag which indicates whether the list should use database paging (only supported for some lists) .**/
165    protected boolean m_lazy;
166
167    /** Activation decision Flag. */
168    private boolean m_active;
169
170    /** the internal list. */
171    private CmsHtmlList m_list;
172
173    /** The id of the list. */
174    private String m_listId;
175
176    /** Cached List state in case of {@link #refreshList()} method call. */
177    private CmsListState m_listState;
178
179    /** The displayed page. */
180    private String m_paramFormName;
181
182    /** The list action. */
183    private String m_paramListAction;
184
185    /** The displayed page. */
186    private String m_paramPage;
187
188    /** The search filter text. */
189    private String m_paramSearchFilter;
190
191    /** The selected items, comma separated list. */
192    private String m_paramSelItems;
193
194    /** The column to sort the list. */
195    private String m_paramSortCol;
196
197    /** The column to search the list. */
198    private String m_searchColId;
199
200    /**
201     * Public constructor.<p>
202     *
203     * @param jsp an initialized JSP action element
204     * @param listId the id of the displayed list
205     * @param listName the name of the list
206     * @param sortedColId the a priory sorted column
207     * @param sortOrder the order of the sorted column
208     * @param searchableColId the column to search into
209     */
210    protected A_CmsListDialog(
211        CmsJspActionElement jsp,
212        String listId,
213        CmsMessageContainer listName,
214        String sortedColId,
215        CmsListOrderEnum sortOrder,
216        String searchableColId) {
217
218        this(jsp, listId, listName, sortedColId, sortOrder, searchableColId, false);
219    }
220
221    /**
222     * Public constructor.<p>
223     *
224     * @param jsp an initialized JSP action element
225     * @param listId the id of the displayed list
226     * @param listName the name of the list
227     * @param sortedColId the a priory sorted column
228     * @param sortOrder the order of the sorted column
229     * @param searchableColId the column to search into
230     * @param lazy if this parameter is true, the list should load only load the list items of the current page, if possible
231     */
232    @SuppressWarnings("unchecked")
233    protected A_CmsListDialog(
234        CmsJspActionElement jsp,
235        String listId,
236        CmsMessageContainer listName,
237        String sortedColId,
238        CmsListOrderEnum sortOrder,
239        String searchableColId,
240        boolean lazy) {
241
242        super(jsp);
243        m_lazy = lazy;
244        m_metadatas = (Map<String, CmsListMetadata>)jsp.getRequest().getSession().getAttribute(KEY_META_DATA_CACHE);
245        if (m_metadatas == null) {
246            m_metadatas = new HashMap<String, CmsListMetadata>();
247            jsp.getRequest().getSession().setAttribute(KEY_META_DATA_CACHE, m_metadatas);
248        }
249        if (LOG.isDebugEnabled()) {
250            LOG.debug(Messages.get().getBundle().key(Messages.LOG_START_INIT_LIST_1, listId));
251        }
252        // set list id
253        m_listId = listId;
254        // set active flag for 2 lists dialog
255        m_active = (getListId() + "-form").equals(getParamFormName());
256        setParamFormName(getListId() + "-form");
257        // abort if already forwarded
258        if (isForwarded()) {
259            return;
260        }
261        m_searchColId = searchableColId;
262        // try to read the list from the session
263        listRecovery(listId);
264        // initialization
265        if (getList() == null) {
266            // create the list
267            setList(new CmsHtmlList(listId, listName, getMetadata(this.getClass().getName(), listId)));
268            // set the number of items per page from the user settings
269            getList().setMaxItemsPerPage(getSettings().getUserSettings().getExplorerFileEntries());
270            // sort the list
271            if ((sortedColId != null) && (getList().getMetadata().getColumnDefinition(sortedColId) != null)) {
272                getList().setWp(this);
273                getList().setSortedColumn(sortedColId);
274                if ((sortOrder != null) && (sortOrder == CmsListOrderEnum.ORDER_DESCENDING)) {
275                    getList().setSortedColumn(sortedColId);
276                }
277            }
278            // save the current state of the list
279            listSave();
280        }
281        getList().setWp(this);
282        if (LOG.isDebugEnabled()) {
283            LOG.debug(Messages.get().getBundle().key(Messages.LOG_END_INIT_LIST_1, listId));
284        }
285    }
286
287    /**
288     * Returns the list object for the given list dialog, or <code>null</code>
289     * if no list object has been set.<p>
290     *
291     * @param listDialog the list dialog class
292     * @param settings the wp settings for accessing the session
293     *
294     * @return the list object for this list dialog, or <code>null</code>
295     */
296    public static CmsHtmlList getListObject(Class<?> listDialog, CmsWorkplaceSettings settings) {
297
298        return getListObjectMap(settings).get(listDialog.getName());
299    }
300
301    /**
302     * Returns the (internal use only) map of list objects.<p>
303     *
304     * @param settings the wp settings for accessing the session
305     *
306     * @return the (internal use only) map of list objects
307     */
308    private static Map<String, CmsHtmlList> getListObjectMap(CmsWorkplaceSettings settings) {
309
310        Map<String, CmsHtmlList> objects = CmsCollectionsGenericWrapper.map(settings.getListObject());
311        if (objects == null) {
312            // using hashtable as most efficient version of a synchronized map
313            objects = new Hashtable<String, CmsHtmlList>();
314            settings.setListObject(objects);
315        }
316        return objects;
317    }
318
319    /**
320     * Performs the dialog actions depending on the initialized action.<p>
321     *
322     * @throws JspException if dialog actions fail
323     * @throws IOException in case of errors forwarding to the required result page
324     * @throws ServletException in case of errors forwarding to the required result page
325     */
326    public void actionDialog() throws JspException, ServletException, IOException {
327
328        if (isForwarded()) {
329            return;
330        }
331        if (getAction() == ACTION_CANCEL) {
332            // ACTION: cancel button pressed
333            actionCloseDialog();
334            return;
335        }
336
337        if (LOG.isDebugEnabled()) {
338            LOG.debug(
339                Messages.get().getBundle().key(
340                    Messages.LOG_START_ACTION_LIST_2,
341                    getListId(),
342                    Integer.valueOf(getAction())));
343        }
344        switch (getAction()) {
345            //////////////////// ACTION: default actions
346            case ACTION_LIST_SEARCH:
347            case ACTION_LIST_SORT:
348            case ACTION_LIST_SELECT_PAGE:
349                executeDefaultActions();
350                break;
351
352            //////////////////// ACTION: execute single list action
353            case ACTION_LIST_SINGLE_ACTION:
354                if (getSelectedItem() != null) {
355                    executeListSingleActions();
356                }
357                break;
358
359            //////////////////// ACTION: execute multiple list actions
360            case ACTION_LIST_MULTI_ACTION:
361                executeListMultiActions();
362                break;
363
364            //////////////////// ACTION: execute independent list actions
365            case ACTION_LIST_INDEPENDENT_ACTION:
366                executeListIndepActions();
367                break;
368
369            case ACTION_DEFAULT:
370            default:
371                // ACTION: show dialog (default)
372                setParamAction(DIALOG_INITIAL);
373        }
374        if (LOG.isDebugEnabled()) {
375            LOG.debug(
376                Messages.get().getBundle().key(Messages.LOG_END_ACTION_LIST_2, getListId(), Integer.valueOf(getAction())));
377        }
378        refreshList();
379    }
380
381    /**
382     * Generates the dialog starting html code.<p>
383     *
384     * @return html code
385     */
386    public String defaultActionHtml() {
387
388        if ((getList() != null) && getList().getAllContent().isEmpty()) {
389            // TODO: check the need for this
390            refreshList();
391        }
392        StringBuffer result = new StringBuffer(2048);
393        result.append(defaultActionHtmlStart());
394        result.append(customHtmlStart());
395        result.append(defaultActionHtmlContent());
396        result.append(customHtmlEnd());
397        result.append(defaultActionHtmlEnd());
398        return result.toString();
399    }
400
401    /**
402     * Performs the dialog actions depending on the initialized action and displays the dialog form.<p>
403     *
404     * @throws JspException if dialog actions fail
405     * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page
406     * @throws ServletException in case of errors forwarding to the required result page
407     */
408    public void displayDialog() throws JspException, IOException, ServletException {
409
410        displayDialog(false);
411    }
412
413    /**
414     * Performs the dialog actions depending on the initialized action and displays the dialog form if needed.<p>
415     *
416     * @param writeLater if <code>true</code> no output is written,
417     *                   you have to call manually the <code>{@link #defaultActionHtml()}</code> method.
418     *
419     * @throws JspException if dialog actions fail
420     * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page
421     * @throws ServletException in case of errors forwarding to the required result page
422     */
423    public void displayDialog(boolean writeLater) throws JspException, IOException, ServletException {
424
425        actionDialog();
426        if (writeLater) {
427            return;
428        }
429        writeDialog();
430    }
431
432    /**
433     * This method execute the default actions for searching, sorting and paging.<p>
434     */
435    public void executeDefaultActions() {
436
437        switch (getAction()) {
438
439            case ACTION_LIST_SEARCH:
440                executeSearch();
441                break;
442            case ACTION_LIST_SORT:
443                executeSort();
444                break;
445            case ACTION_LIST_SELECT_PAGE:
446                executeSelectPage();
447                break;
448            default:
449                // ignore
450        }
451        listSave();
452    }
453
454    /**
455     * This method should handle the default list independent actions,
456     * by comparing <code>{@link #getParamListAction()}</code> with the id
457     * of the action to execute.<p>
458     *
459     * if you want to handle additional independent actions, override this method,
460     * handling your actions and FINALLY calling <code>super.executeListIndepActions();</code>.<p>
461     */
462    public void executeListIndepActions() {
463
464        if (getList().getMetadata().getItemDetailDefinition(getParamListAction()) != null) {
465            // toggle item details
466            getList().getMetadata().toogleDetailState(getParamListAction());
467            // lazy initialization
468            initializeDetail(getParamListAction());
469        }
470        listSave();
471    }
472
473    /**
474     * This method should handle every defined list multi action,
475     * by comparing <code>{@link #getParamListAction()}</code> with the id
476     * of the action to execute.<p>
477     *
478     * @throws IOException in case of errors when including a required sub-element
479     * @throws ServletException in case of errors when including a required sub-element
480     * @throws CmsRuntimeException to signal that an action is not supported
481     */
482    public abstract void executeListMultiActions() throws IOException, ServletException, CmsRuntimeException;
483
484    /**
485     * This method should handle every defined list single action,
486     * by comparing <code>{@link #getParamListAction()}</code> with the id
487     * of the action to execute.<p>
488     *
489     * @throws IOException in case of errors when including a required sub-element
490     * @throws ServletException in case of errors when including a required sub-element
491     * @throws CmsRuntimeException to signal that an action is not supported
492     */
493    public abstract void executeListSingleActions() throws IOException, ServletException, CmsRuntimeException;
494
495    /**
496     * Returns the list.<p>
497     *
498     * @return the list
499     */
500    public CmsHtmlList getList() {
501
502        if ((m_list != null) && (m_list.getMetadata() == null)) {
503            m_list.setMetadata(getMetadata(getClass().getName(), m_list.getId()));
504        }
505        return m_list;
506    }
507
508    /**
509     * Returns the Id of the list.<p>
510     *
511     * @return the list Id
512     */
513    public final String getListId() {
514
515        return m_listId;
516    }
517
518    /**
519     * Returns the list metadata object for the given dialog.<p>
520     *
521     * @param listDialogName the dialog class name
522     *
523     * @return the list metadata object
524     */
525    public CmsListMetadata getMetadata(String listDialogName) {
526
527        return getMetadataCache().get(listDialogName);
528    }
529
530    /**
531     * Should generate the metadata definition for the list, and return the
532     * corresponding <code>{@link CmsListMetadata}</code> object.<p>
533     *
534     * @param listDialogName the name of the class generating the list
535     * @param listId the id of the list
536     *
537     * @return The metadata for the given list
538     */
539    public synchronized CmsListMetadata getMetadata(String listDialogName, String listId) {
540
541        getSettings();
542        String metaDataKey = listDialogName + listId;
543
544        if ((getMetadataCache().get(metaDataKey) == null) || getMetadataCache().get(metaDataKey).isVolatile()) {
545            if (LOG.isDebugEnabled()) {
546                LOG.debug(Messages.get().getBundle().key(Messages.LOG_START_METADATA_LIST_1, getListId()));
547            }
548            CmsListMetadata metadata = new CmsListMetadata(listId);
549
550            setColumns(metadata);
551            // always check the search action
552            setSearchAction(metadata, m_searchColId);
553            setIndependentActions(metadata);
554            metadata.addIndependentAction(new CmsListPrintIAction());
555            setMultiActions(metadata);
556            metadata.checkIds();
557            getMetadataCache().put(metaDataKey, metadata);
558            if (LOG.isDebugEnabled()) {
559                LOG.debug(Messages.get().getBundle().key(Messages.LOG_END_METADATA_LIST_1, getListId()));
560            }
561        }
562        return getMetadata(metaDataKey);
563    }
564
565    /**
566     * Returns the form name.<p>
567     *
568     * @return the form name
569     */
570    public String getParamFormName() {
571
572        return m_paramFormName;
573    }
574
575    /**
576     * Returns the List Action.<p>
577     *
578     * @return the List Action
579     */
580    public String getParamListAction() {
581
582        return m_paramListAction;
583    }
584
585    /**
586     * Returns the current Page.<p>
587     *
588     * @return the current Page
589     */
590    public String getParamPage() {
591
592        return m_paramPage;
593    }
594
595    /**
596     * Returns the Search Filter.<p>
597     *
598     * @return the Search Filter
599     */
600    public String getParamSearchFilter() {
601
602        return m_paramSearchFilter;
603    }
604
605    /**
606     * Returns the selected Items.<p>
607     *
608     * @return the selected Items
609     */
610    public String getParamSelItems() {
611
612        return m_paramSelItems;
613    }
614
615    /**
616     * Returns the sorted Column.<p>
617     *
618     * @return the sorted Column
619     */
620    public String getParamSortCol() {
621
622        return m_paramSortCol;
623    }
624
625    /**
626     * Returns the current selected item.<p>
627     *
628     * @return the current selected item
629     */
630    public CmsListItem getSelectedItem() {
631
632        try {
633            return getList().getItem(
634                CmsStringUtil.splitAsArray(getParamSelItems(), CmsHtmlList.ITEM_SEPARATOR)[0].trim());
635        } catch (Exception e) {
636            try {
637                return getList().getItem("");
638            } catch (Exception e1) {
639                return null;
640            }
641        }
642    }
643
644    /**
645     * Returns a list of current selected items.<p>
646     *
647     * @return a list of current selected items
648     */
649    public List<CmsListItem> getSelectedItems() {
650
651        Iterator<String> it = CmsStringUtil.splitAsList(
652            getParamSelItems(),
653            CmsHtmlList.ITEM_SEPARATOR,
654            true).iterator();
655        List<CmsListItem> items = new ArrayList<CmsListItem>();
656        while (it.hasNext()) {
657            String id = it.next();
658            items.add(getList().getItem(id));
659        }
660        return items;
661    }
662
663    /**
664     * Returns the activation flag.<p>
665     *
666     * Useful for dialogs with several lists.<p>
667     *
668     * Is <code></code> if the original <code>formname</code> parameter
669     * is equals to <code>${listId}-form</code>.<p>
670     *
671     * @return the activation flag
672     */
673    public boolean isActive() {
674
675        return m_active;
676    }
677
678    /**
679     * This method re-read the rows of the list, the user should call this method after executing an action
680     * that add or remove rows to the list.<p>
681     */
682    public synchronized void refreshList() {
683
684        if (getList() == null) {
685            return;
686        }
687        if (LOG.isDebugEnabled()) {
688            LOG.debug(Messages.get().getBundle().key(Messages.LOG_START_REFRESH_LIST_1, getListId()));
689        }
690        m_listState = getList().getState();
691        getList().clear();
692        fillList();
693        getList().setState(m_listState);
694        m_listState = null;
695        listSave();
696        if (LOG.isDebugEnabled()) {
697            LOG.debug(Messages.get().getBundle().key(Messages.LOG_END_REFRESH_LIST_1, getListId()));
698        }
699    }
700
701    /**
702     * Removes the list from the workplace settings.<p>
703     *
704     * Next time the list is displayed the list will be reloaded.<p>
705     */
706    public void removeList() {
707
708        setList(null);
709        listSave();
710    }
711
712    /**
713     * Sets the list.<p>
714     *
715     * @param list the list to set
716     */
717    public void setList(CmsHtmlList list) {
718
719        m_list = list;
720    }
721
722    /**
723     * Stores the given object as "list object" for the given list dialog in the current users session.<p>
724     *
725     * @param listDialog the list dialog class
726     * @param listObject the list to store
727     */
728    public void setListObject(Class<?> listDialog, CmsHtmlList listObject) {
729
730        if (listObject == null) {
731            // null object: remove the entry from the map
732            getListObjectMap(getSettings()).remove(listDialog.getName());
733        } else {
734            if ((listObject.getMetadata() != null) && listObject.getMetadata().isVolatile()) {
735                listObject.setMetadata(null);
736            }
737            getListObjectMap(getSettings()).put(listDialog.getName(), listObject);
738        }
739    }
740
741    /**
742     * Sets the form name.<p>
743     *
744     * @param formName the form name to set
745     */
746    public void setParamFormName(String formName) {
747
748        m_paramFormName = formName;
749    }
750
751    /**
752     * Sets the List Action.<p>
753     *
754     * @param listAction the list Action to set
755     */
756    public void setParamListAction(String listAction) {
757
758        m_paramListAction = listAction;
759    }
760
761    /**
762     * Sets the current Page.<p>
763     *
764     * @param page the current Page to set
765     */
766    public void setParamPage(String page) {
767
768        m_paramPage = page;
769    }
770
771    /**
772     * Sets the Search Filter.<p>
773     *
774     * @param searchFilter the Search Filter to set
775     */
776    public void setParamSearchFilter(String searchFilter) {
777
778        m_paramSearchFilter = searchFilter;
779    }
780
781    /**
782     * Sets the selected Items.<p>
783     *
784     * @param paramSelItems the selected Items to set
785     */
786    public void setParamSelItems(String paramSelItems) {
787
788        m_paramSelItems = paramSelItems;
789    }
790
791    /**
792     * Sets the sorted Column.<p>
793     *
794     * @param sortCol the sorted Column to set
795     */
796    public void setParamSortCol(String sortCol) {
797
798        m_paramSortCol = sortCol;
799    }
800
801    /**
802     * Writes the dialog html code, only if the <code>{@link #ACTION_DEFAULT}</code> is set.<p>
803     *
804     * @throws IOException if writing to the JSP out fails, or in case of errros forwarding to the required result page
805     */
806    public void writeDialog() throws IOException {
807
808        if (isForwarded()) {
809            return;
810        }
811        if (LOG.isDebugEnabled()) {
812            LOG.debug(Messages.get().getBundle().key(Messages.LOG_START_WRITE_LIST_1, getListId()));
813        }
814        JspWriter out = getJsp().getJspContext().getOut();
815        out.print(defaultActionHtml());
816        if (LOG.isDebugEnabled()) {
817            LOG.debug(Messages.get().getBundle().key(Messages.LOG_END_WRITE_LIST_1, getListId()));
818        }
819    }
820
821    /**
822     * Can be overwritten to add some code after the list.<p>
823     *
824     * @return custom html code
825     */
826    protected String customHtmlEnd() {
827
828        return dialogContentEnd();
829    }
830
831    /**
832     * Can be overwritten to add some code before the list.<p>
833     *
834     * @return custom html code
835     */
836    protected String customHtmlStart() {
837
838        return "";
839    }
840
841    /**
842     * Returns the html code for the default action content.<p>
843     *
844     * @return html code
845     */
846    protected String defaultActionHtmlContent() {
847
848        StringBuffer result = new StringBuffer(2048);
849        result.append("<form name='");
850        result.append(getList().getId());
851        result.append("-form' action='");
852        result.append(getDialogRealUri());
853        result.append("' method='post' class='nomargin'");
854        if (getList().getMetadata().isSearchable()) {
855            result.append(" onsubmit=\"listSearchAction('");
856            result.append(getList().getId());
857            result.append("', '");
858            result.append(getList().getMetadata().getSearchAction().getId());
859            result.append("', '");
860            result.append(getList().getMetadata().getSearchAction().getConfirmationMessage().key(getLocale()));
861            result.append("');\"");
862        }
863        result.append(">\n");
864        result.append(allParamsAsHidden());
865        result.append("\n");
866        getList().setWp(this);
867        result.append(getList().listHtml());
868        result.append("\n</form>\n");
869        return result.toString();
870    }
871
872    /**
873     * Generates the dialog ending html code.<p>
874     *
875     * @return html code
876     */
877    protected String defaultActionHtmlEnd() {
878
879        StringBuffer result = new StringBuffer(2048);
880        result.append(dialogEnd());
881        result.append(bodyEnd());
882        result.append(htmlEnd());
883        return result.toString();
884    }
885
886    /**
887     * Generates the dialog starting html code.<p>
888     *
889     * @return html code
890     */
891    protected String defaultActionHtmlStart() {
892
893        StringBuffer result = new StringBuffer(2048);
894        result.append(htmlStart(null));
895        result.append(getList().listJs());
896        result.append(bodyStart("dialog", null));
897        result.append(dialogStart());
898        result.append(dialogContentStart(getParamTitle()));
899        return result.toString();
900    }
901
902    /**
903     * Filter a list, given the action is set to <code>LIST_SEARCH</code> and
904     * the filter text is set in the <code>PARAM_SEARCH_FILTER</code> parameter.<p>
905     */
906    protected void executeSearch() {
907
908        getList().setSearchFilter(getParamSearchFilter());
909    }
910
911    /**
912     * Select a page, given the action is set to <code>LIST_SELECT_PAGE</code> and
913     * the page to go to is set in the <code>PARAM_PAGE</code> parameter.<p>
914     */
915    protected void executeSelectPage() {
916
917        int page = Integer.valueOf(getParamPage()).intValue();
918        getList().setCurrentPage(page);
919    }
920
921    /**
922     * Sort the list, given the action is set to <code>LIST_SORT</code> and
923     * the sort column is set in the <code>PARAM_SORT_COL</code> parameter.<p>
924     */
925    protected void executeSort() {
926
927        getList().setSortedColumn(getParamSortCol());
928    }
929
930    /**
931     * Lazy initialization for detail data.<p>
932     *
933     * Should fill the given detail column for every list item in <code>{@link CmsHtmlList#getContent()}</code>
934     *
935     * Should not throw any kind of exception.<p>
936     *
937     * @param detailId the id of the detail to initialize
938     */
939    protected abstract void fillDetails(String detailId);
940
941    /**
942     * Calls the <code>{@link #getListItems}</code> method and catches any exception.<p>
943     */
944    protected void fillList() {
945
946        try {
947            getList().setContent(getListItems());
948            // initialize detail columns
949            Iterator<CmsListItemDetails> itDetails = getList().getMetadata().getItemDetailDefinitions().iterator();
950            while (itDetails.hasNext()) {
951                initializeDetail(itDetails.next().getId());
952            }
953        } catch (Exception e) {
954            throw new CmsRuntimeException(
955                Messages.get().container(Messages.ERR_LIST_FILL_1, getList().getName().key(getLocale()), null),
956                e);
957        }
958    }
959
960    /**
961     * Should generate a list with the list items to be displayed.<p>
962     *
963     * @return a list of <code>{@link CmsListItem}</code>s
964     *
965     * @throws CmsException if something goes wrong
966     */
967    protected abstract List<CmsListItem> getListItems() throws CmsException;
968
969    /**
970     * Returns the current list state.<p>
971     *
972     * @return the current list state
973     */
974    protected CmsListState getListState() {
975
976        if (m_listState != null) {
977            // in case of refreshList call
978            return m_listState;
979        }
980        return getList().getState();
981    }
982
983    /**
984     * Gets the list metadata cache.<p>
985     *
986     * @return the list metadata cache
987     */
988    protected Map<String, CmsListMetadata> getMetadataCache() {
989
990        return m_metadatas;
991    }
992
993    /**
994     * Lazy details initialization.<p>
995     *
996     * @param detailId the id of the detail column
997     */
998    protected void initializeDetail(String detailId) {
999
1000        // if detail column visible or printable
1001        CmsListItemDetails details = getList().getMetadata().getItemDetailDefinition(detailId);
1002        if (details.isVisible() || details.isPrintable()) {
1003            // if the list is not empty
1004            if (getList().getTotalSize() > 0) {
1005                // if the detail column has not been previously initialized
1006                if (getList().getAllContent().get(0).get(detailId) == null) {
1007                    if (LOG.isDebugEnabled()) {
1008                        LOG.debug(
1009                            Messages.get().getBundle().key(Messages.LOG_START_DETAILS_LIST_2, getListId(), detailId));
1010                    }
1011                    fillDetails(detailId);
1012                    if (LOG.isDebugEnabled()) {
1013                        LOG.debug(
1014                            Messages.get().getBundle().key(Messages.LOG_END_DETAILS_LIST_2, getListId(), detailId));
1015                    }
1016                }
1017            }
1018        }
1019    }
1020
1021    /**
1022     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
1023     */
1024    @Override
1025    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
1026
1027        super.initWorkplaceRequestValues(settings, request);
1028        // set the action for the JSP switch
1029        if (LIST_SEARCH.equals(getParamAction())) {
1030            setAction(ACTION_LIST_SEARCH);
1031        } else if (LIST_SORT.equals(getParamAction())) {
1032            setAction(ACTION_LIST_SORT);
1033        } else if (LIST_SELECT_PAGE.equals(getParamAction())) {
1034            setAction(ACTION_LIST_SELECT_PAGE);
1035        } else if (LIST_INDEPENDENT_ACTION.equals(getParamAction())) {
1036            setAction(ACTION_LIST_INDEPENDENT_ACTION);
1037        } else if (LIST_SINGLE_ACTION.equals(getParamAction())) {
1038            setAction(ACTION_LIST_SINGLE_ACTION);
1039        } else if (LIST_MULTI_ACTION.equals(getParamAction())) {
1040            setAction(ACTION_LIST_MULTI_ACTION);
1041        }
1042        setParamStyle(CmsToolDialog.STYLE_NEW);
1043        // test the needed parameters
1044        try {
1045            validateParamaters();
1046        } catch (Exception e) {
1047            // redirect to parent if parameters not available
1048            setAction(ACTION_CANCEL);
1049            try {
1050                actionCloseDialog();
1051            } catch (JspException e1) {
1052                // noop
1053            }
1054            return;
1055        }
1056    }
1057
1058    /**
1059     * Recover the last list instance that is read from the request attributes.<p>
1060     *
1061     * This is required for keep the whole list in memory while you browse a page.<p>
1062     *
1063     * @param listId the id of the expected list
1064     */
1065    protected synchronized void listRecovery(String listId) {
1066
1067        CmsHtmlList list = getListObject(this.getClass(), getSettings());
1068        if ((list != null) && !list.getId().equals(listId)) {
1069            list = null;
1070        }
1071        setList(list);
1072    }
1073
1074    /**
1075     * Save the state of the list in the session.<p>
1076     */
1077    protected synchronized void listSave() {
1078
1079        setListObject(this.getClass(), getList());
1080    }
1081
1082    /**
1083     * Should create the columns and add them to the given list metadata object.<p>
1084     *
1085     * This method will be just executed once, the first time the constructor is called.<p>
1086     *
1087     * @param metadata the list metadata
1088     */
1089    protected abstract void setColumns(CmsListMetadata metadata);
1090
1091    /**
1092     * Should add the independent actions to the given list metadata object.<p>
1093     *
1094     * This method will be just executed once, the first time the constructor is called.<p>
1095     *
1096     * @param metadata the list metadata
1097     */
1098    protected abstract void setIndependentActions(CmsListMetadata metadata);
1099
1100    /**
1101     * Should add the multi actions to the given list metadata object.<p>
1102     *
1103     * This method will be just executed once, the first time the constructor is called.<p>
1104     *
1105     * @param metadata the list metadata
1106     */
1107    protected abstract void setMultiActions(CmsListMetadata metadata);
1108
1109    /**
1110     * Creates the default search action.<p>
1111     *
1112     * Can be overridden for more sophisticated search.<p>
1113     *
1114     * @param metadata the metadata of the list to do searchable
1115     * @param columnId the if of the column to search into
1116     */
1117    protected void setSearchAction(CmsListMetadata metadata, String columnId) {
1118
1119        CmsListColumnDefinition col = metadata.getColumnDefinition(columnId);
1120        if ((columnId != null) && (col != null)) {
1121            if (metadata.getSearchAction() == null) {
1122                // makes the list searchable
1123                CmsListSearchAction searchAction = new CmsListSearchAction(col);
1124                searchAction.useDefaultShowAllAction();
1125                metadata.setSearchAction(searchAction);
1126            }
1127        }
1128    }
1129
1130    /**
1131     * A convenient method to throw a list unsupported
1132     * action runtime exception.<p>
1133     *
1134     * Should be triggered if your list implementation does not
1135     * support the <code>{@link #getParamListAction()}</code>
1136     * action.<p>
1137     *
1138     * @throws CmsRuntimeException always to signal that this operation is not supported
1139     */
1140    protected void throwListUnsupportedActionException() throws CmsRuntimeException {
1141
1142        throw new CmsRuntimeException(
1143            Messages.get().container(
1144                Messages.ERR_LIST_UNSUPPORTED_ACTION_2,
1145                getList().getName().key(getLocale()),
1146                getParamListAction()));
1147    }
1148
1149    /**
1150     * Should be overridden for parameter validation.<p>
1151     *
1152     * @throws Exception if the parameters are not valid
1153     */
1154    protected void validateParamaters() throws Exception {
1155
1156        // valid by default
1157    }
1158}