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.commons;
029
030import org.opencms.db.CmsDbEntryNotFoundException;
031import org.opencms.file.CmsFile;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProject;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.CmsVfsResourceNotFoundException;
037import org.opencms.file.history.CmsHistoryProject;
038import org.opencms.file.history.CmsHistoryResourceHandler;
039import org.opencms.file.history.I_CmsHistoryResource;
040import org.opencms.jsp.CmsJspActionElement;
041import org.opencms.main.CmsException;
042import org.opencms.main.CmsLog;
043import org.opencms.main.CmsRuntimeException;
044import org.opencms.main.OpenCms;
045import org.opencms.security.CmsPrincipal;
046import org.opencms.util.CmsStringUtil;
047import org.opencms.util.CmsUUID;
048import org.opencms.workplace.comparison.CmsHistoryListUtil;
049import org.opencms.workplace.list.A_CmsListDialog;
050import org.opencms.workplace.list.CmsListColumnAlignEnum;
051import org.opencms.workplace.list.CmsListColumnDefinition;
052import org.opencms.workplace.list.CmsListDirectAction;
053import org.opencms.workplace.list.CmsListItem;
054import org.opencms.workplace.list.CmsListItemActionIconComparator;
055import org.opencms.workplace.list.CmsListItemDetails;
056import org.opencms.workplace.list.CmsListItemDetailsFormatter;
057import org.opencms.workplace.list.CmsListItemSelectionAction;
058import org.opencms.workplace.list.CmsListMetadata;
059import org.opencms.workplace.list.CmsListOrderEnum;
060import org.opencms.workplace.list.CmsListRadioMultiAction;
061import org.opencms.workplace.list.CmsListResourceIconAction;
062import org.opencms.workplace.list.I_CmsListFormatter;
063import org.opencms.workplace.tools.CmsToolDialog;
064
065import java.io.IOException;
066import java.util.ArrayList;
067import java.util.Arrays;
068import java.util.HashMap;
069import java.util.Iterator;
070import java.util.List;
071import java.util.Locale;
072import java.util.Map;
073
074import javax.servlet.ServletException;
075import javax.servlet.http.HttpServletRequest;
076import javax.servlet.http.HttpServletResponse;
077import javax.servlet.jsp.JspException;
078import javax.servlet.jsp.PageContext;
079
080import org.apache.commons.logging.Log;
081
082/**
083 * Displays the history of a file.<p>
084 *
085 * @since 6.0.2
086 */
087public class CmsHistoryList extends A_CmsListDialog {
088
089    /**
090     * Wrapper class for the version which is either an integer or the string "offline".<p>
091     */
092    public static class CmsVersionWrapper implements Comparable<Object> {
093
094        /** the version. */
095        private Integer m_version;
096
097        /**
098         * Constructs a new version wrapper.<p>
099         *
100         * @param version the version of the file
101         */
102        public CmsVersionWrapper(int version) {
103
104            m_version = new Integer(version);
105        }
106
107        /**
108         *
109         * @see java.lang.Comparable#compareTo(java.lang.Object)
110         */
111        public int compareTo(Object o) {
112
113            if (this == o) {
114                return 0;
115            }
116            if (o instanceof CmsVersionWrapper) {
117                CmsVersionWrapper version = (CmsVersionWrapper)o;
118                Integer v1 = m_version;
119                Integer v2 = version.getVersion();
120                if (v1.intValue() < 0) {
121                    v1 = new Integer(-1 * v1.intValue());
122                }
123                if (v2.intValue() < 0) {
124                    v2 = new Integer(-1 * v2.intValue());
125                }
126                return v1.compareTo(v2);
127            }
128            return 0;
129        }
130
131        /**
132         * @see java.lang.Object#equals(java.lang.Object)
133         */
134        @Override
135        public boolean equals(Object obj) {
136
137            if (this == obj) {
138                return true;
139            }
140            if (obj instanceof CmsVersionWrapper) {
141                CmsVersionWrapper version = (CmsVersionWrapper)obj;
142                return getVersion().equals(version.getVersion());
143            }
144            return false;
145        }
146
147        /**
148         * Returns the version of the file.<p>
149         *
150         * @return the version of the file
151         */
152        public Integer getVersion() {
153
154            return m_version;
155        }
156
157        /**
158         * @see java.lang.Object#hashCode()
159         */
160        @Override
161        public int hashCode() {
162
163            return getVersion().hashCode();
164        }
165
166        /**
167         *
168         * @see java.lang.Object#toString()
169         */
170        @Override
171        public String toString() {
172
173            return m_version.toString();
174        }
175
176    }
177
178    /** list item detail id constant. */
179    public static final String GUI_LIST_HISTORY_DETAIL_PROJECT_0 = "lhdp";
180
181    /** List action export. */
182    public static final String LIST_ACTION_RESTORE = "ar";
183
184    /** list action id constant. */
185    public static final String LIST_ACTION_VIEW = "av";
186
187    /** list column id constant. */
188    public static final String LIST_COLUMN_DATE_LAST_MODIFIED = "cm";
189
190    /** list column id constant. */
191    public static final String LIST_COLUMN_DATE_PUBLISHED = "cdp";
192
193    /** list column id constant. */
194    public static final String LIST_COLUMN_FILE_TYPE = "ct";
195
196    /** list column id constant. */
197    public static final String LIST_COLUMN_ICON = "ci";
198
199    /** list column id constant. */
200    public static final String LIST_COLUMN_PUBLISH_TAG = "cbt";
201
202    /** list column id constant. */
203    public static final String LIST_COLUMN_RESOURCE_PATH = "crp";
204
205    /** List column delete. */
206    public static final String LIST_COLUMN_RESTORE = "cr";
207
208    /** list column id constant. */
209    public static final String LIST_COLUMN_SEL1 = "cs1";
210
211    /** list column id constant. */
212    public static final String LIST_COLUMN_SEL2 = "cs2";
213
214    /** list column id constant. */
215    public static final String LIST_COLUMN_SIZE = "cs";
216
217    /** list column id constant. */
218    public static final String LIST_COLUMN_STRUCTURE_ID = "csi";
219
220    /** List column export. */
221    public static final String LIST_COLUMN_USER = "cu";
222
223    /** list column id constant. */
224    public static final String LIST_COLUMN_VERSION = "cv";
225
226    /** list column id constant. */
227    public static final String LIST_COLUMN_VERSION_ENABLE = "cve";
228
229    /** List column export. */
230    public static final String LIST_COLUMN_VIEW = "cp";
231
232    /** list id constant. */
233    public static final String LIST_ID = "him";
234
235    /** list independent action id constant. */
236    public static final String LIST_RACTION_SEL1 = "rs1";
237
238    /** list independent action id constant. */
239    public static final String LIST_RACTION_SEL2 = "rs2";
240
241    /** parameter for the path of the first resource. */
242    public static final String PARAM_ID_1 = "id1";
243
244    /** parameter for the path of the second resource. */
245    public static final String PARAM_ID_2 = "id2";
246
247    /** parameter for the version of the first resource. */
248    public static final String PARAM_VERSION_1 = "version1";
249
250    /** parameter for the version of the second resource. */
251    public static final String PARAM_VERSION_2 = "version2";
252
253    /** Path to the list buttons. */
254    public static final String PATH_BUTTONS = "buttons/";
255
256    /** list multi action id constant. */
257    private static final String LIST_MACTION_COMPARE = "mc";
258
259    /** The log object for this class. */
260    private static final Log LOG = CmsLog.getLog(CmsHistoryList.class);
261
262    /**
263     * Public constructor.<p>
264     *
265     * @param jsp an initialized JSP action element
266     */
267    public CmsHistoryList(CmsJspActionElement jsp) {
268
269        super(
270            jsp,
271            LIST_ID,
272            Messages.get().container(Messages.GUI_HISTORY_0),
273            LIST_COLUMN_VERSION,
274            CmsListOrderEnum.ORDER_DESCENDING,
275            null);
276    }
277
278    /**
279     * Public constructor with JSP variables.<p>
280     *
281     * @param context the JSP page context
282     * @param req the JSP request
283     * @param res the JSP response
284     */
285    public CmsHistoryList(PageContext context, HttpServletRequest req, HttpServletResponse res) {
286
287        this(new CmsJspActionElement(context, req, res));
288    }
289
290    /**
291     * @see org.opencms.workplace.list.A_CmsListDialog#actionDialog()
292     */
293    @Override
294    public void actionDialog() throws JspException, ServletException, IOException {
295
296        super.actionDialog();
297        // ensure the list is correcly sorted
298        if (CmsStringUtil.isEmptyOrWhitespaceOnly(getList().getSortedColumn())) {
299            getList().setSortedColumn(LIST_COLUMN_VERSION);
300        }
301    }
302
303    /**
304     * @see org.opencms.workplace.list.A_CmsListDialog#executeListMultiActions()
305     */
306    @Override
307    public void executeListMultiActions() throws IOException, ServletException {
308
309        if (getParamListAction().equals(LIST_MACTION_COMPARE)) {
310            CmsListItem item1 = getSelectedItems().get(0);
311            CmsListItem item2 = getSelectedItems().get(1);
312            Map<String, String[]> params = new HashMap<String, String[]>();
313            if (item2.get(LIST_COLUMN_VERSION) instanceof CmsVersionWrapper) {
314                CmsVersionWrapper ver = (CmsVersionWrapper)item2.get(LIST_COLUMN_VERSION);
315                if (ver.compareTo(item1.get(LIST_COLUMN_VERSION)) > 0) {
316                    params.put(PARAM_VERSION_1, new String[] {item1.get(LIST_COLUMN_VERSION).toString()});
317                    params.put(PARAM_VERSION_2, new String[] {item2.get(LIST_COLUMN_VERSION).toString()});
318                    params.put(PARAM_ID_1, new String[] {item1.get(LIST_COLUMN_STRUCTURE_ID).toString()});
319                    params.put(PARAM_ID_2, new String[] {item2.get(LIST_COLUMN_STRUCTURE_ID).toString()});
320                }
321            }
322            if (params.isEmpty()) {
323                params.put(PARAM_VERSION_1, new String[] {item2.get(LIST_COLUMN_VERSION).toString()});
324                params.put(PARAM_VERSION_2, new String[] {item1.get(LIST_COLUMN_VERSION).toString()});
325                params.put(PARAM_ID_1, new String[] {item2.get(LIST_COLUMN_STRUCTURE_ID).toString()});
326                params.put(PARAM_ID_2, new String[] {item1.get(LIST_COLUMN_STRUCTURE_ID).toString()});
327            }
328            params.put(PARAM_ACTION, new String[] {DIALOG_INITIAL});
329            params.put(PARAM_STYLE, new String[] {CmsToolDialog.STYLE_NEW});
330            params.put(PARAM_RESOURCE, new String[] {getParamResource()});
331            getToolManager().jspForwardTool(this, "/history/comparison", params);
332        }
333        refreshList();
334    }
335
336    /**
337     * @see org.opencms.workplace.list.A_CmsListDialog#executeListSingleActions()
338     */
339    @Override
340    public void executeListSingleActions() throws IOException, ServletException {
341
342        if (getParamListAction().equals(LIST_ACTION_RESTORE)) {
343            try {
344                performRestoreOperation();
345                Map<String, String[]> params = new HashMap<String, String[]>();
346                params.put(PARAM_ACTION, new String[] {DIALOG_INITIAL});
347                getToolManager().jspForwardPage(this, "/system/workplace/views/explorer/explorer_files.jsp", params);
348            } catch (CmsException e) {
349                LOG.error(e.getMessage(), e);
350                throw new CmsRuntimeException(e.getMessageContainer());
351            }
352        }
353        refreshList();
354    }
355
356    /**
357     * @see org.opencms.workplace.list.A_CmsListDialog#defaultActionHtmlStart()
358     */
359    @Override
360    protected String defaultActionHtmlStart() {
361
362        return getList().listJs() + dialogContentStart(getParamTitle());
363    }
364
365    /**
366     * @see org.opencms.workplace.list.A_CmsListDialog#fillDetails(java.lang.String)
367     */
368    @Override
369    protected void fillDetails(String detailId) {
370
371        // get content
372        List<CmsListItem> items = getList().getAllContent();
373        Iterator<CmsListItem> itItems = items.iterator();
374        CmsListItem item;
375        while (itItems.hasNext()) {
376            item = itItems.next();
377            if (detailId.equals(GUI_LIST_HISTORY_DETAIL_PROJECT_0)) {
378                fillDetailProject(item, detailId);
379            }
380        }
381    }
382
383    /**
384     * @see org.opencms.workplace.list.A_CmsListDialog#getListItems()
385     */
386    @Override
387    protected List<CmsListItem> getListItems() throws CmsException {
388
389        List<CmsListItem> result = new ArrayList<CmsListItem>();
390
391        List<I_CmsHistoryResource> historicalVersions = getCms().readAllAvailableVersions(getParamResource());
392        Iterator<I_CmsHistoryResource> itVersions = historicalVersions.iterator();
393        while (itVersions.hasNext()) {
394            I_CmsHistoryResource histRes = itVersions.next();
395
396            // determine if the item actions should be enabled
397            // true, if the history file resource has its content
398            // false, if the history resource has no content, so no history dialog action can be performed
399            boolean isEnabled = true;
400            CmsResource resource = (CmsResource)getCms().readResource(
401                getCms().readResource(histRes.getStructureId(), CmsResourceFilter.IGNORE_EXPIRATION).getStructureId(),
402                histRes.getVersion());
403            // not for folder
404            if (resource.isFile()) {
405                CmsFile file = getCms().readFile(resource);
406                byte[] content = file.getContents();
407                // disable item actions, if the file has no content
408                if (content.length < 1) {
409                    isEnabled = false;
410                }
411            }
412
413            // the publish tag for the history project
414            int publishTag = histRes.getPublishTag();
415
416            CmsHistoryProject project = getCms().readHistoryProject(publishTag);
417            String filetype = String.valueOf(histRes.getTypeId());
418            String dateLastModified = getMessages().getDateTime(histRes.getDateLastModified());
419            String datePublished = getMessages().getDateTime(project.getPublishingDate());
420
421            CmsListItem item = getList().newItem("" + histRes.getVersion());
422
423            int factor = 1;
424            if (result.isEmpty() && !histRes.getState().isDeleted()) {
425                factor = -1;
426            }
427            CmsVersionWrapper version = new CmsVersionWrapper(factor * histRes.getVersion());
428
429            //version
430            item.set(LIST_COLUMN_VERSION, version);
431            // publish date
432            item.set(LIST_COLUMN_DATE_PUBLISHED, datePublished);
433            // last modification date
434            item.set(LIST_COLUMN_DATE_LAST_MODIFIED, dateLastModified);
435            // resource type
436            item.set(LIST_COLUMN_FILE_TYPE, filetype);
437            // user
438            String user = histRes.getUserLastModified().toString();
439            try {
440                user = CmsPrincipal.readPrincipalIncludingHistory(getCms(), histRes.getUserLastModified()).getName();
441            } catch (CmsDbEntryNotFoundException e) {
442                // ignore
443            }
444            item.set(LIST_COLUMN_USER, user);
445            // path
446            item.set(LIST_COLUMN_RESOURCE_PATH, getCms().getRequestContext().removeSiteRoot(histRes.getRootPath()));
447            // size
448            item.set(LIST_COLUMN_SIZE, new Integer(histRes.getLength()).toString());
449            // invisible publish tag (for reading history project in fillDetails)
450            item.set(LIST_COLUMN_PUBLISH_TAG, new Integer(publishTag));
451            // invisible structure id
452            item.set(LIST_COLUMN_STRUCTURE_ID, histRes.getStructureId().toString());
453            // invisible version flag: true if history version has content and actions should be enabled, false otherwise
454            item.set(LIST_COLUMN_VERSION_ENABLE, new Boolean(isEnabled));
455
456            result.add(item);
457        }
458
459        if (result.isEmpty()) {
460            CmsResource onlineResource = null;
461
462            // this is to prevent problems after an update without keeping historical info
463            CmsProject project = getCms().getRequestContext().getCurrentProject();
464            try {
465                getCms().getRequestContext().setCurrentProject(getCms().readProject(CmsProject.ONLINE_PROJECT_ID));
466                onlineResource = getCms().readResource(getParamResource(), CmsResourceFilter.IGNORE_EXPIRATION);
467
468                CmsListItem item = getList().newItem("" + onlineResource.getVersion());
469                //version
470                item.set(LIST_COLUMN_VERSION, new CmsVersionWrapper(-1 * onlineResource.getVersion()));
471                // publish date
472                item.set(LIST_COLUMN_DATE_PUBLISHED, "-");
473                // last modification date
474                item.set(
475                    LIST_COLUMN_DATE_LAST_MODIFIED,
476                    getMessages().getDateTime(onlineResource.getDateLastModified()));
477                // resource type
478                item.set(LIST_COLUMN_FILE_TYPE, String.valueOf(onlineResource.getTypeId()));
479                // user
480                String user = onlineResource.getUserLastModified().toString();
481                try {
482                    user = CmsPrincipal.readPrincipalIncludingHistory(
483                        getCms(),
484                        onlineResource.getUserLastModified()).getName();
485                } catch (CmsDbEntryNotFoundException e) {
486                    // ignore
487                }
488                item.set(LIST_COLUMN_USER, user);
489                // size
490                item.set(LIST_COLUMN_SIZE, new Integer(onlineResource.getLength()).toString());
491                // path
492                item.set(LIST_COLUMN_RESOURCE_PATH, getCms().getSitePath(onlineResource));
493                // invisible structure id
494                item.set(LIST_COLUMN_STRUCTURE_ID, onlineResource.getStructureId().toString());
495                // invisible version flag: true
496                item.set(LIST_COLUMN_VERSION_ENABLE, new Boolean(true));
497
498                result.add(item);
499            } catch (CmsVfsResourceNotFoundException e) {
500                // ignore, most likely the file is new
501            } finally {
502                getCms().getRequestContext().setCurrentProject(project);
503            }
504        }
505        CmsResource offlineResource = getCms().readResource(getParamResource(), CmsResourceFilter.IGNORE_EXPIRATION);
506
507        // hide the size for folders
508        getList().getMetadata().getColumnDefinition(LIST_COLUMN_SIZE).setVisible(offlineResource.isFile());
509        // hide the preview button for folders
510        getList().getMetadata().getColumnDefinition(LIST_COLUMN_ICON).setVisible(offlineResource.isFile());
511
512        // display offline version, if state is not unchanged
513        if (!offlineResource.getState().isUnchanged()) {
514            CmsListItem item = getList().newItem("" + CmsHistoryResourceHandler.PROJECT_OFFLINE_VERSION);
515            //version
516            item.set(LIST_COLUMN_VERSION, new CmsVersionWrapper(CmsHistoryResourceHandler.PROJECT_OFFLINE_VERSION));
517            // publish date
518            item.set(LIST_COLUMN_DATE_PUBLISHED, "-");
519            // last modification date
520            item.set(LIST_COLUMN_DATE_LAST_MODIFIED, getMessages().getDateTime(offlineResource.getDateLastModified()));
521            // resource type
522            item.set(LIST_COLUMN_FILE_TYPE, String.valueOf(offlineResource.getTypeId()));
523            // user
524            String user = offlineResource.getUserLastModified().toString();
525            try {
526                user = CmsPrincipal.readPrincipalIncludingHistory(
527                    getCms(),
528                    offlineResource.getUserLastModified()).getName();
529            } catch (CmsDbEntryNotFoundException e) {
530                // ignore
531            }
532            item.set(LIST_COLUMN_USER, user);
533            // size
534            item.set(LIST_COLUMN_SIZE, new Integer(offlineResource.getLength()).toString());
535            // path
536            item.set(LIST_COLUMN_RESOURCE_PATH, getCms().getSitePath(offlineResource));
537            // invisible structure id
538            item.set(LIST_COLUMN_STRUCTURE_ID, offlineResource.getStructureId().toString());
539            // invisible version flag: true
540            item.set(LIST_COLUMN_VERSION_ENABLE, new Boolean(true));
541
542            result.add(item);
543        }
544
545        boolean comparable = (result.size() > 1);
546        getList().getMetadata().getColumnDefinition(LIST_COLUMN_SEL1).setVisible(comparable);
547        getList().getMetadata().getColumnDefinition(LIST_COLUMN_SEL2).setVisible(comparable);
548        getList().getMetadata().getMultiAction(LIST_MACTION_COMPARE).setVisible(comparable);
549
550        return result;
551    }
552
553    /**
554     * Restores a backed up resource version.<p>
555     *
556     * @throws CmsException if something goes wrong
557     */
558    protected void performRestoreOperation() throws CmsException {
559
560        CmsUUID structureId = new CmsUUID(getSelectedItem().get(LIST_COLUMN_STRUCTURE_ID).toString());
561        int version = Integer.parseInt(getSelectedItems().get(0).getId());
562        if (version == CmsHistoryResourceHandler.PROJECT_OFFLINE_VERSION) {
563            // it is not possible to restore the offline version
564            return;
565        }
566        CmsResource res = getCms().readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
567        checkLock(getCms().getSitePath(res));
568        getCms().restoreResourceVersion(res.getStructureId(), version);
569    }
570
571    /**
572     * @see org.opencms.workplace.list.A_CmsListDialog#setColumns(org.opencms.workplace.list.CmsListMetadata)
573     */
574    @Override
575    protected void setColumns(CmsListMetadata metadata) {
576
577        CmsListColumnDefinition previewCol = new CmsListColumnDefinition(LIST_COLUMN_VIEW);
578        previewCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_VIEW_0));
579        previewCol.setWidth("20");
580        previewCol.setVisible(false);
581        previewCol.setAlign(CmsListColumnAlignEnum.ALIGN_CENTER);
582        previewCol.setSorteable(false);
583
584        // create invisible coloumn for the version flag
585        CmsListColumnDefinition enableItemCol = new CmsListColumnDefinition(LIST_COLUMN_VERSION_ENABLE);
586        enableItemCol.setSorteable(false);
587        enableItemCol.setVisible(false);
588        metadata.addColumn(enableItemCol);
589
590        // create column for icon
591        CmsListColumnDefinition restoreCol = new CmsListColumnDefinition(LIST_COLUMN_RESTORE);
592        restoreCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_RESTORE_0));
593        restoreCol.setWidth("20");
594        restoreCol.setAlign(CmsListColumnAlignEnum.ALIGN_CENTER);
595        restoreCol.setListItemComparator(new CmsListItemActionIconComparator());
596        restoreCol.setSorteable(false);
597
598        // add icon action
599        CmsListDirectAction restoreAction = new CmsListDirectAction(LIST_ACTION_RESTORE) {
600
601            /**
602             * @see org.opencms.workplace.tools.I_CmsHtmlIconButton#getIconPath()
603             */
604            @Override
605            public String getIconPath() {
606
607                Boolean isEnabled = (Boolean)getItem().get(LIST_COLUMN_VERSION_ENABLE);
608                if (isEnabled.booleanValue()) {
609                    return "tools/ex_history/buttons/restore.png";
610                }
611                return "buttons/publish_in.png";
612            }
613
614            /**
615             * @see org.opencms.workplace.tools.I_CmsHtmlIconButton#isEnabled()
616             */
617            @Override
618            public boolean isEnabled() {
619
620                Boolean isEnabled = (Boolean)getItem().get(LIST_COLUMN_VERSION_ENABLE);
621                if (isEnabled.booleanValue()) {
622                    return true;
623                }
624                return false;
625            }
626        };
627        restoreAction.setName(Messages.get().container(Messages.GUI_HISTORY_RESTORE_VERSION_0));
628        restoreAction.setConfirmationMessage(Messages.get().container(Messages.GUI_HISTORY_CONFIRMATION_0));
629
630        restoreCol.addDirectAction(restoreAction);
631        // add it to the list definition
632        metadata.addColumn(restoreCol);
633
634        // create column for icon
635        CmsListColumnDefinition iconCol = new CmsListColumnDefinition(LIST_COLUMN_ICON);
636        iconCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_VIEW_0));
637        iconCol.setWidth("20");
638        iconCol.setAlign(CmsListColumnAlignEnum.ALIGN_CENTER);
639        iconCol.setListItemComparator(new CmsListItemActionIconComparator());
640
641        // add icon action
642        CmsListDirectAction fileAction = new CmsListResourceIconAction(
643            LIST_ACTION_VIEW,
644            LIST_COLUMN_FILE_TYPE,
645            getCms()) {
646
647            /**
648             * @see org.opencms.workplace.list.CmsListResourceIconAction#defButtonHtml(CmsObject, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, java.lang.String, boolean)
649             */
650            @Override
651            public String defButtonHtml(
652                CmsObject cms,
653                String id,
654                String helpId,
655                String name,
656                String helpText,
657                boolean enabled,
658                String iconPath,
659                String confirmationMessage,
660                String onClick,
661                boolean singleHelp) {
662
663                StringBuffer jsCode = new StringBuffer(512);
664                jsCode.append("window.open('");
665                CmsVersionWrapper version = (CmsVersionWrapper)getItem().get(LIST_COLUMN_VERSION);
666
667                // is the resource already a sibling already deleted?
668                jsCode.append(
669                    OpenCms.getLinkManager().substituteLink(
670                        cms,
671                        CmsHistoryListUtil.getHistoryLink(
672                            cms,
673                            new CmsUUID(getItem().get(LIST_COLUMN_STRUCTURE_ID).toString()),
674                            version.toString())));
675                jsCode.append("','version','scrollbars=yes, resizable=yes, width=800, height=600')");
676                return super.defButtonHtml(
677                    cms,
678                    id,
679                    helpId,
680                    name,
681                    helpText,
682                    enabled,
683                    iconPath,
684                    confirmationMessage,
685                    jsCode.toString(),
686                    singleHelp);
687            }
688
689            /**
690             * @see org.opencms.workplace.tools.I_CmsHtmlIconButton#getIconPath()
691             */
692            @Override
693            public String getIconPath() {
694
695                Boolean isEnabled = (Boolean)getItem().get(LIST_COLUMN_VERSION_ENABLE);
696                if (!isEnabled.booleanValue()) {
697                    return "filetypes/plain.gif";
698                }
699                return super.getIconPath();
700
701            }
702
703            /**
704             * @see org.opencms.workplace.tools.I_CmsHtmlIconButton#isEnabled()
705             */
706            @Override
707            public boolean isEnabled() {
708
709                Boolean isEnabled = (Boolean)getItem().get(LIST_COLUMN_VERSION_ENABLE);
710                if (isEnabled.booleanValue()) {
711                    return true;
712                }
713                return false;
714            }
715        };
716        fileAction.setName(Messages.get().container(Messages.GUI_HISTORY_PREVIEW_0));
717        iconCol.addDirectAction(fileAction);
718        // add it to the list definition
719        metadata.addColumn(iconCol);
720        iconCol.setPrintable(false);
721
722        // add column for version
723        CmsListColumnDefinition versionCol = new CmsListColumnDefinition(LIST_COLUMN_VERSION);
724        versionCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_VERSION_0));
725        versionCol.setWidth("5%");
726        versionCol.setAlign(CmsListColumnAlignEnum.ALIGN_CENTER);
727        versionCol.setFormatter(new I_CmsListFormatter() {
728
729            /**
730             * @see org.opencms.workplace.list.I_CmsListFormatter#format(java.lang.Object, java.util.Locale)
731             */
732            public String format(Object data, Locale locale) {
733
734                if ((data == null) || !(data instanceof CmsVersionWrapper)) {
735                    return "";
736                }
737                CmsVersionWrapper version = (CmsVersionWrapper)data;
738                return CmsHistoryListUtil.getDisplayVersion(version.toString(), locale);
739            }
740
741        });
742        metadata.addColumn(versionCol);
743
744        // add column for file type
745        CmsListColumnDefinition groupCol = new CmsListColumnDefinition(LIST_COLUMN_FILE_TYPE);
746        groupCol.setVisible(false);
747        metadata.addColumn(groupCol);
748
749        // add column for resource path
750        CmsListColumnDefinition pathCol = new CmsListColumnDefinition(LIST_COLUMN_RESOURCE_PATH);
751        pathCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_RESOURCE_PATH_0));
752        pathCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
753        pathCol.setWidth("40%");
754        metadata.addColumn(pathCol);
755
756        // add column for date published
757        CmsListColumnDefinition datePublishedCol = new CmsListColumnDefinition(LIST_COLUMN_DATE_PUBLISHED);
758        datePublishedCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_DATE_PUBLISHED_0));
759        datePublishedCol.setWidth("20%");
760        datePublishedCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
761        metadata.addColumn(datePublishedCol);
762
763        // add column for date last modified
764        CmsListColumnDefinition nicenameCol = new CmsListColumnDefinition(LIST_COLUMN_DATE_LAST_MODIFIED);
765        nicenameCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_DATE_LAST_MODIFIED_0));
766        nicenameCol.setWidth("20%");
767        nicenameCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
768        metadata.addColumn(nicenameCol);
769
770        // add column for user modified
771        CmsListColumnDefinition userCol = new CmsListColumnDefinition(LIST_COLUMN_USER);
772        userCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_USER_0));
773        userCol.setWidth("30%");
774        userCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
775        metadata.addColumn(userCol);
776
777        // add column for date last modified
778        CmsListColumnDefinition sizeCol = new CmsListColumnDefinition(LIST_COLUMN_SIZE);
779        sizeCol.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_SIZE_0));
780        sizeCol.setWidth("10%");
781        sizeCol.setAlign(CmsListColumnAlignEnum.ALIGN_LEFT);
782        metadata.addColumn(sizeCol);
783
784        // create column for radio button 1
785        CmsListColumnDefinition radioSel1Col = new CmsListColumnDefinition(LIST_COLUMN_SEL1);
786        radioSel1Col.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_VERSION1_0));
787        radioSel1Col.setWidth("20");
788        radioSel1Col.setAlign(CmsListColumnAlignEnum.ALIGN_CENTER);
789        radioSel1Col.setSorteable(false);
790
791        // add item selection action
792        CmsListItemSelectionAction sel1Action = new CmsListItemSelectionAction(LIST_RACTION_SEL1, LIST_MACTION_COMPARE);
793        sel1Action.setName(Messages.get().container(Messages.GUI_HISTORY_FIRST_VERSION_0));
794        sel1Action.setEnabled(true);
795        radioSel1Col.addDirectAction(sel1Action);
796        metadata.addColumn(radioSel1Col);
797
798        // create column for radio button 2
799        CmsListColumnDefinition radioSel2Col = new CmsListColumnDefinition(LIST_COLUMN_SEL2);
800        radioSel2Col.setName(Messages.get().container(Messages.GUI_HISTORY_COLS_VERSION2_0));
801        radioSel2Col.setWidth("20");
802        radioSel2Col.setAlign(CmsListColumnAlignEnum.ALIGN_CENTER);
803        radioSel2Col.setSorteable(false);
804
805        // add item selection action
806        CmsListItemSelectionAction sel2Action = new CmsListItemSelectionAction(LIST_RACTION_SEL2, LIST_MACTION_COMPARE);
807        sel2Action.setName(Messages.get().container(Messages.GUI_HISTORY_SECOND_VERSION_0));
808        sel2Action.setEnabled(true);
809        radioSel2Col.addDirectAction(sel2Action);
810        metadata.addColumn(radioSel2Col);
811
812        // create invisible publish tag column to allow fillDetails to be able to read the proper
813        // historical project
814        CmsListColumnDefinition publishTagCol = new CmsListColumnDefinition(LIST_COLUMN_PUBLISH_TAG);
815        publishTagCol.setSorteable(false);
816        publishTagCol.setVisible(false);
817        metadata.addColumn(publishTagCol);
818
819        // create invisible strcuture id column
820        CmsListColumnDefinition strIdCol = new CmsListColumnDefinition(LIST_COLUMN_STRUCTURE_ID);
821        strIdCol.setSorteable(false);
822        strIdCol.setVisible(false);
823        metadata.addColumn(strIdCol);
824    }
825
826    /**
827     * @see org.opencms.workplace.list.A_CmsListDialog#setIndependentActions(org.opencms.workplace.list.CmsListMetadata)
828     */
829    @Override
830    protected void setIndependentActions(CmsListMetadata metadata) {
831
832        // add index source details
833        CmsListItemDetails indexDetails = new CmsListItemDetails(GUI_LIST_HISTORY_DETAIL_PROJECT_0);
834        indexDetails.setAtColumn(LIST_COLUMN_VERSION);
835        indexDetails.setVisible(false);
836        indexDetails.setShowActionName(Messages.get().container(Messages.GUI_LIST_HISTORY_DETAIL_PROJECT_NAME_SHOW_0));
837        indexDetails.setShowActionHelpText(
838            Messages.get().container(Messages.GUI_LIST_HISTORY_DETAIL_PROJECT_SHOW_HELP_0));
839        indexDetails.setHideActionName(Messages.get().container(Messages.GUI_LIST_HISTORY_DETAIL_PROJECT_NAME_HIDE_0));
840        indexDetails.setHideActionHelpText(
841            Messages.get().container(Messages.GUI_LIST_HISTORY_DETAIL_PROJECT_HIDE_HELP_0));
842        indexDetails.setName(Messages.get().container(Messages.GUI_LIST_HISTORY_DETAIL_PROJECT_INFO_0));
843        indexDetails.setFormatter(
844            new CmsListItemDetailsFormatter(Messages.get().container(Messages.GUI_LIST_HISTORY_DETAIL_PROJECT_INFO_0)));
845        metadata.addItemDetails(indexDetails);
846    }
847
848    /**
849     * @see org.opencms.workplace.list.A_CmsListDialog#setMultiActions(org.opencms.workplace.list.CmsListMetadata)
850     */
851    @Override
852    protected void setMultiActions(CmsListMetadata metadata) {
853
854        // add compare action
855        CmsListRadioMultiAction compareAction = new CmsListRadioMultiAction(
856            LIST_MACTION_COMPARE,
857            Arrays.asList(new String[] {LIST_RACTION_SEL1, LIST_RACTION_SEL2}));
858        compareAction.setName(Messages.get().container(Messages.GUI_HISTORY_COMPARE_0));
859        compareAction.setIconPath("tools/ex_history/buttons/compare.png");
860        metadata.addMultiAction(compareAction);
861    }
862
863    /**
864     * Fills details of the project into the given item. <p>
865     *
866     * @param item the list item to fill
867     *
868     * @param detailId the id for the detail to fill
869     *
870     */
871    private void fillDetailProject(CmsListItem item, String detailId) {
872
873        StringBuffer html = new StringBuffer();
874
875        // search /read for the corresponding history project: it's tag id transmitted from getListItems()
876        // in a hidden column
877        Object tagIdObj = item.get(LIST_COLUMN_PUBLISH_TAG);
878        if (tagIdObj != null) {
879            // it is null if the offline version with changes is shown here: now history project available then
880            int tagId = ((Integer)tagIdObj).intValue();
881            try {
882                CmsHistoryProject project = getCms().readHistoryProject(tagId);
883                // output of project info
884                html.append(project.getName()).append("<br/>").append(project.getDescription());
885            } catch (CmsException cmse) {
886                html.append(cmse.getMessageContainer().key(getLocale()));
887            }
888        }
889        item.set(detailId, html.toString());
890    }
891}