001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (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, 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.ui.components;
029
030import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_CACHE;
031import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_CATEGORIES;
032import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_COPYRIGHT;
033import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_CREATED;
034import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_EXPIRED;
035import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_MODIFIED;
036import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_DATE_RELEASED;
037import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT;
038import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INTERNAL_RESOURCE_TYPE;
039import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_IN_NAVIGATION;
040import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_IS_FOLDER;
041import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION;
042import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT;
043import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_PERMISSIONS;
044import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_PROJECT;
045import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RELEASED_NOT_EXPIRED;
046import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RESOURCE_NAME;
047import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_RESOURCE_TYPE;
048import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_SIZE;
049import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_STATE;
050import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_STATE_NAME;
051import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_TITLE;
052import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_TYPE_ICON;
053import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_CREATED;
054import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_LOCKED;
055import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_USER_MODIFIED;
056
057import org.opencms.db.CmsResourceState;
058import org.opencms.file.CmsObject;
059import org.opencms.file.CmsResource;
060import org.opencms.file.CmsResourceFilter;
061import org.opencms.file.CmsVfsResourceNotFoundException;
062import org.opencms.main.CmsException;
063import org.opencms.main.CmsLog;
064import org.opencms.main.OpenCms;
065import org.opencms.ui.A_CmsUI;
066import org.opencms.ui.CmsVaadinUtils;
067import org.opencms.ui.I_CmsDialogContext;
068import org.opencms.ui.I_CmsEditPropertyContext;
069import org.opencms.ui.actions.I_CmsDefaultAction;
070import org.opencms.ui.apps.CmsFileExplorerSettings;
071import org.opencms.ui.apps.I_CmsContextProvider;
072import org.opencms.ui.contextmenu.CmsContextMenu;
073import org.opencms.ui.contextmenu.CmsResourceContextMenuBuilder;
074import org.opencms.ui.contextmenu.I_CmsContextMenuBuilder;
075import org.opencms.ui.util.I_CmsItemSorter;
076import org.opencms.util.CmsStringUtil;
077import org.opencms.util.CmsUUID;
078
079import java.io.ByteArrayOutputStream;
080import java.io.OutputStreamWriter;
081import java.nio.charset.StandardCharsets;
082import java.text.DateFormat;
083import java.text.SimpleDateFormat;
084import java.util.ArrayList;
085import java.util.Arrays;
086import java.util.Collection;
087import java.util.Collections;
088import java.util.Date;
089import java.util.HashSet;
090import java.util.LinkedHashMap;
091import java.util.List;
092import java.util.Locale;
093import java.util.Map;
094import java.util.Map.Entry;
095import java.util.Set;
096import java.util.TimeZone;
097
098import org.apache.commons.logging.Log;
099
100import com.google.common.base.Function;
101import com.google.common.collect.Lists;
102import com.vaadin.event.FieldEvents.BlurEvent;
103import com.vaadin.event.FieldEvents.BlurListener;
104import com.vaadin.event.ShortcutAction.KeyCode;
105import com.vaadin.event.ShortcutListener;
106import com.vaadin.shared.MouseEventDetails.MouseButton;
107import com.vaadin.shared.Registration;
108import com.vaadin.ui.Component;
109import com.vaadin.ui.themes.ValoTheme;
110import com.vaadin.v7.data.Container;
111import com.vaadin.v7.data.Container.Filter;
112import com.vaadin.v7.data.Item;
113import com.vaadin.v7.data.Property.ValueChangeEvent;
114import com.vaadin.v7.data.Property.ValueChangeListener;
115import com.vaadin.v7.data.util.DefaultItemSorter;
116import com.vaadin.v7.data.util.IndexedContainer;
117import com.vaadin.v7.data.util.filter.Or;
118import com.vaadin.v7.data.util.filter.SimpleStringFilter;
119import com.vaadin.v7.event.ItemClickEvent;
120import com.vaadin.v7.event.ItemClickEvent.ItemClickListener;
121import com.vaadin.v7.ui.AbstractTextField.TextChangeEventMode;
122import com.vaadin.v7.ui.DefaultFieldFactory;
123import com.vaadin.v7.ui.Field;
124import com.vaadin.v7.ui.Table;
125import com.vaadin.v7.ui.Table.TableDragMode;
126import com.vaadin.v7.ui.TextField;
127
128import au.com.bytecode.opencsv.CSVWriter;
129
130/**
131 * Table for displaying resources.<p>
132 */
133public class CmsFileTable extends CmsResourceTable {
134
135    /**
136     * File edit handler.<p>
137     */
138    public class FileEditHandler implements BlurListener {
139
140        /** The serial version id. */
141        private static final long serialVersionUID = -2286815522247807054L;
142
143        /**
144         * @see com.vaadin.event.FieldEvents.BlurListener#blur(com.vaadin.event.FieldEvents.BlurEvent)
145         */
146        public void blur(BlurEvent event) {
147
148            stopEdit();
149        }
150    }
151
152    /**
153     * Field factory to enable inline editing of individual file properties.<p>
154     */
155    public class FileFieldFactory extends DefaultFieldFactory {
156
157        /** The serial version id. */
158        private static final long serialVersionUID = 3079590603587933576L;
159
160        /**
161         * @see com.vaadin.ui.DefaultFieldFactory#createField(com.vaadin.v7.data.Container, java.lang.Object, java.lang.Object, com.vaadin.ui.Component)
162         */
163        @Override
164        public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) {
165
166            Field<?> result = null;
167            if (itemId.equals(getEditItemId().toString()) && isEditProperty((CmsResourceTableProperty)propertyId)) {
168                result = super.createField(container, itemId, propertyId, uiContext);
169                result.addStyleName(OpenCmsTheme.INLINE_TEXTFIELD);
170                result.addValidator(m_editHandler);
171                if (result instanceof TextField) {
172                    ((TextField)result).setComponentError(null);
173                    clearColumnEditActions();
174                    // we attach the shortcuts to the table, not the textbox, so that they still trigger if the change from the edited field would cause the current row
175                    // to be filtered by the current container filter.
176                    m_columnEditEscRegistration = CmsFileTable.this.addShortcutListener(
177                        new ShortcutListener("Cancel edit", KeyCode.ESCAPE, null) {
178
179                            private static final long serialVersionUID = 1L;
180
181                            @Override
182                            public void handleAction(Object sender, Object target) {
183
184                                cancelEdit();
185                            }
186                        });
187
188                    m_columnEditEnterRegistration = CmsFileTable.this.addShortcutListener(
189                        new ShortcutListener("Save", KeyCode.ENTER, null) {
190
191                            private static final long serialVersionUID = 1L;
192
193                            @Override
194                            public void handleAction(Object sender, Object target) {
195
196                                stopEdit();
197                            }
198                        });
199
200                    ((TextField)result).addBlurListener(m_fileEditHandler);
201                    ((TextField)result).setTextChangeEventMode(TextChangeEventMode.LAZY);
202                    ((TextField)result).addTextChangeListener(m_editHandler);
203                }
204                result.focus();
205            }
206            return result;
207        }
208    }
209
210    /**
211     * Extends the default sorting to differentiate between files and folder when sorting by name.<p>
212     * Also allows sorting by navPos property for the Resource icon column.<p>
213     */
214    public static class FileSorter extends DefaultItemSorter implements I_CmsItemSorter {
215
216        /** The serial version id. */
217        private static final long serialVersionUID = 1L;
218
219        /**
220         * @see org.opencms.ui.util.I_CmsItemSorter#getSortableContainerPropertyIds(com.vaadin.v7.data.Container)
221         */
222        public Collection<?> getSortableContainerPropertyIds(Container container) {
223
224            Set<Object> result = new HashSet<Object>();
225            for (Object propId : container.getContainerPropertyIds()) {
226                Class<?> propertyType = container.getType(propId);
227                if (Comparable.class.isAssignableFrom(propertyType)
228                    || propertyType.isPrimitive()
229                    || (propId.equals(CmsResourceTableProperty.PROPERTY_TYPE_ICON)
230                        && container.getContainerPropertyIds().contains(
231                            CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION))) {
232                    result.add(propId);
233                }
234            }
235            return result;
236        }
237
238        /**
239         * @see com.vaadin.v7.data.util.DefaultItemSorter#compareProperty(java.lang.Object, boolean, com.vaadin.v7.data.Item, com.vaadin.v7.data.Item)
240         */
241        @Override
242        protected int compareProperty(Object propertyId, boolean sortDirection, Item item1, Item item2) {
243
244            //@formatter:off
245            if (CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.equals(propertyId)) {
246                Boolean isFolder1 = (Boolean)item1.getItemProperty(
247                    CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue();
248                Boolean isFolder2 = (Boolean)item2.getItemProperty(
249                    CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue();
250                if (!isFolder1.equals(isFolder2)) {
251                    int result = isFolder1.booleanValue() ? -1 : 1;
252                    if (!sortDirection) {
253                        result = result * (-1);
254                    }
255                    return result;
256                }
257            } else if ((CmsResourceTableProperty.PROPERTY_TYPE_ICON.equals(propertyId)
258                || CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.equals(propertyId))
259                && (item1.getItemProperty(CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION) != null)) {
260                int result;
261                Float pos1 = (Float)item1.getItemProperty(
262                    CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).getValue();
263                Float pos2 = (Float)item2.getItemProperty(
264                    CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).getValue();
265                if (pos1 == null) {
266                    result = pos2 == null
267                    ? compareProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, true, item1, item2)
268                    : 1;
269                } else {
270                    result = pos2 == null ? -1 : Float.compare(pos1.floatValue(), pos2.floatValue());
271                }
272                if (!sortDirection) {
273                    result = result * (-1);
274                }
275                return result;
276            } else if (((CmsResourceTableProperty)propertyId).getColumnType().equals(String.class)) {
277                String value1 = (String)item1.getItemProperty(propertyId).getValue();
278                String value2 = (String)item2.getItemProperty(propertyId).getValue();
279                // Java collators obtained by java.text.Collator.getInstance(...) ignore spaces, and we don't want to ignore them, so we use
280                // ICU collators instead
281                com.ibm.icu.text.Collator collator = com.ibm.icu.text.Collator.getInstance(
282                    com.ibm.icu.util.ULocale.ROOT);
283                int result = collator.compare(value1, value2);
284                if (!sortDirection) {
285                    result = -result;
286                }
287                return result;
288            }
289            return super.compareProperty(propertyId, sortDirection, item1, item2);
290            //@formatter:on
291        }
292    }
293
294    /**
295     * Handles folder selects in the file table.<p>
296     */
297    public interface I_FolderSelectHandler {
298
299        /**
300         * Called when the folder name is left clicked.<p>
301         *
302         * @param folderId the selected folder id
303         */
304        void onFolderSelect(CmsUUID folderId);
305    }
306
307    /** The default file table columns. */
308    public static final Map<CmsResourceTableProperty, Integer> DEFAULT_TABLE_PROPERTIES;
309
310    /** The logger instance for this class. */
311    static final Log LOG = CmsLog.getLog(CmsFileTable.class);
312
313    /** The serial version id. */
314    private static final long serialVersionUID = 5460048685141699277L;
315
316    /** The selected resources. */
317    protected List<CmsResource> m_currentResources = new ArrayList<CmsResource>();
318
319    /** The default action column property. */
320    CmsResourceTableProperty m_actionColumnProperty;
321
322    /** The additional cell style generators. */
323    List<Table.CellStyleGenerator> m_additionalStyleGenerators;
324
325    /** The current file property edit handler. */
326    I_CmsFilePropertyEditHandler m_editHandler;
327
328    /** File edit event handler. */
329    FileEditHandler m_fileEditHandler = new FileEditHandler();
330
331    /** The context menu. */
332    CmsContextMenu m_menu;
333
334    /** The context menu builder. */
335    I_CmsContextMenuBuilder m_menuBuilder;
336
337    /** The table drag mode, stored during item editing. */
338    private TableDragMode m_beforEditDragMode;
339
340    /** Action registration for pressing Enter during column editing. */
341    private Registration m_columnEditEnterRegistration;
342
343    /** Action registration for pressing Esc during column editing. */
344    private Registration m_columnEditEscRegistration;
345
346    /** The dialog context provider. */
347    private I_CmsContextProvider m_contextProvider;
348
349    /** The edited item id. */
350    private CmsUUID m_editItemId;
351
352    /** The edited property id. */
353    private CmsResourceTableProperty m_editProperty;
354
355    /** Stack of saved container filters. */
356    private List<Collection<Filter>> m_filterStack = new ArrayList<>();
357
358    /** The folder select handler. */
359    private I_FolderSelectHandler m_folderSelectHandler;
360
361    /** The original edit value. */
362    private String m_originalEditValue;
363
364    /**
365     * Default constructor.<p>
366     *
367     * @param contextProvider the dialog context provider
368     */
369    public CmsFileTable(I_CmsContextProvider contextProvider) {
370
371        this(contextProvider, DEFAULT_TABLE_PROPERTIES);
372    }
373
374    /**
375     * Default constructor.<p>
376     *
377     * @param contextProvider the dialog context provider
378     * @param tableColumns the table columns to show
379     */
380    public CmsFileTable(I_CmsContextProvider contextProvider, Map<CmsResourceTableProperty, Integer> tableColumns) {
381
382        super();
383        m_additionalStyleGenerators = new ArrayList<Table.CellStyleGenerator>();
384        m_actionColumnProperty = PROPERTY_RESOURCE_NAME;
385        m_contextProvider = contextProvider;
386        m_container.setItemSorter(new FileSorter());
387        m_fileTable.addStyleName(ValoTheme.TABLE_BORDERLESS);
388        m_fileTable.addStyleName(OpenCmsTheme.SIMPLE_DRAG);
389        m_fileTable.setSizeFull();
390        m_fileTable.setColumnCollapsingAllowed(true);
391        m_fileTable.setSelectable(true);
392        m_fileTable.setMultiSelect(true);
393
394        m_fileTable.setTableFieldFactory(new FileFieldFactory());
395        ColumnBuilder builder = new ColumnBuilder();
396        for (Entry<CmsResourceTableProperty, Integer> entry : tableColumns.entrySet()) {
397            builder.column(entry.getKey(), entry.getValue().intValue());
398        }
399        builder.buildColumns();
400
401        m_fileTable.setSortContainerPropertyId(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME);
402        m_menu = new CmsContextMenu();
403        m_fileTable.addValueChangeListener(new ValueChangeListener() {
404
405            private static final long serialVersionUID = 1L;
406
407            public void valueChange(ValueChangeEvent event) {
408
409                @SuppressWarnings("unchecked")
410                Set<String> selectedIds = (Set<String>)event.getProperty().getValue();
411                List<CmsResource> selectedResources = new ArrayList<CmsResource>();
412                for (String id : selectedIds) {
413                    try {
414                        CmsResource resource = A_CmsUI.getCmsObject().readResource(
415                            getUUIDFromItemID(id),
416                            CmsResourceFilter.ALL);
417                        selectedResources.add(resource);
418                    } catch (CmsException e) {
419                        LOG.error(e.getLocalizedMessage(), e);
420                    }
421
422                }
423                m_currentResources = selectedResources;
424
425                rebuildMenu();
426            }
427        });
428
429        m_fileTable.addItemClickListener(new ItemClickListener() {
430
431            private static final long serialVersionUID = 1L;
432
433            public void itemClick(ItemClickEvent event) {
434
435                handleFileItemClick(event);
436            }
437        });
438
439        m_fileTable.setCellStyleGenerator(new Table.CellStyleGenerator() {
440
441            private static final long serialVersionUID = 1L;
442
443            public String getStyle(Table source, Object itemId, Object propertyId) {
444
445                Item item = m_container.getItem(itemId);
446                String style = getStateStyle(item);
447                if (m_actionColumnProperty == propertyId) {
448                    style += " " + OpenCmsTheme.HOVER_COLUMN;
449                } else if ((CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT == propertyId)
450                    || (CmsResourceTableProperty.PROPERTY_TITLE == propertyId)) {
451                    if ((item.getItemProperty(CmsResourceTableProperty.PROPERTY_IN_NAVIGATION) != null)
452                        && ((Boolean)item.getItemProperty(
453                            CmsResourceTableProperty.PROPERTY_IN_NAVIGATION).getValue()).booleanValue()) {
454                        style += " " + OpenCmsTheme.IN_NAVIGATION;
455                    }
456                }
457                for (Table.CellStyleGenerator generator : m_additionalStyleGenerators) {
458                    String additional = generator.getStyle(source, itemId, propertyId);
459                    if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(additional)) {
460                        style += " " + additional;
461                    }
462                }
463                return style;
464            }
465        });
466
467        m_menu.setAsTableContextMenu(m_fileTable);
468    }
469
470    static {
471        Map<CmsResourceTableProperty, Integer> defaultProps = new LinkedHashMap<CmsResourceTableProperty, Integer>();
472        defaultProps.put(PROPERTY_TYPE_ICON, Integer.valueOf(0));
473        defaultProps.put(PROPERTY_PROJECT, Integer.valueOf(COLLAPSED));
474        defaultProps.put(PROPERTY_RESOURCE_NAME, Integer.valueOf(0));
475        defaultProps.put(PROPERTY_TITLE, Integer.valueOf(0));
476        try {
477            if (OpenCms.getWorkplaceManager().isExplorerCategoriesEnabled()) {
478                defaultProps.put(PROPERTY_CATEGORIES, Integer.valueOf(COLLAPSED));
479            }
480        } catch (Exception e) {
481            // ignore
482        }
483        defaultProps.put(PROPERTY_NAVIGATION_TEXT, Integer.valueOf(COLLAPSED));
484        defaultProps.put(PROPERTY_NAVIGATION_POSITION, Integer.valueOf(INVISIBLE));
485        defaultProps.put(PROPERTY_IN_NAVIGATION, Integer.valueOf(INVISIBLE));
486        defaultProps.put(PROPERTY_COPYRIGHT, Integer.valueOf(COLLAPSED));
487        defaultProps.put(PROPERTY_CACHE, Integer.valueOf(COLLAPSED));
488        defaultProps.put(PROPERTY_RESOURCE_TYPE, Integer.valueOf(0));
489        defaultProps.put(PROPERTY_INTERNAL_RESOURCE_TYPE, Integer.valueOf(COLLAPSED));
490        defaultProps.put(PROPERTY_SIZE, Integer.valueOf(0));
491        defaultProps.put(PROPERTY_PERMISSIONS, Integer.valueOf(COLLAPSED));
492        defaultProps.put(PROPERTY_DATE_MODIFIED, Integer.valueOf(0));
493        defaultProps.put(PROPERTY_USER_MODIFIED, Integer.valueOf(COLLAPSED));
494        defaultProps.put(PROPERTY_DATE_CREATED, Integer.valueOf(COLLAPSED));
495        defaultProps.put(PROPERTY_USER_CREATED, Integer.valueOf(COLLAPSED));
496        defaultProps.put(PROPERTY_DATE_RELEASED, Integer.valueOf(0));
497        defaultProps.put(PROPERTY_DATE_EXPIRED, Integer.valueOf(0));
498        defaultProps.put(PROPERTY_STATE_NAME, Integer.valueOf(0));
499        defaultProps.put(PROPERTY_USER_LOCKED, Integer.valueOf(0));
500        defaultProps.put(PROPERTY_IS_FOLDER, Integer.valueOf(INVISIBLE));
501        defaultProps.put(PROPERTY_STATE, Integer.valueOf(INVISIBLE));
502        defaultProps.put(PROPERTY_INSIDE_PROJECT, Integer.valueOf(INVISIBLE));
503        defaultProps.put(PROPERTY_RELEASED_NOT_EXPIRED, Integer.valueOf(INVISIBLE));
504        DEFAULT_TABLE_PROPERTIES = Collections.unmodifiableMap(defaultProps);
505    }
506
507    /**
508     * Returns the resource state specific style name.<p>
509     *
510     * @param resourceItem the resource item
511     *
512     * @return the style name
513     */
514    public static String getStateStyle(Item resourceItem) {
515
516        String result = "";
517        if (resourceItem != null) {
518            if ((resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT) == null)
519                || ((Boolean)resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT).getValue()).booleanValue()) {
520
521                CmsResourceState state = (CmsResourceState)resourceItem.getItemProperty(
522                    CmsResourceTableProperty.PROPERTY_STATE).getValue();
523                result = getStateStyle(state);
524            } else {
525                result = OpenCmsTheme.PROJECT_OTHER;
526            }
527            if ((resourceItem.getItemProperty(PROPERTY_RELEASED_NOT_EXPIRED) != null)
528                && !((Boolean)resourceItem.getItemProperty(PROPERTY_RELEASED_NOT_EXPIRED).getValue()).booleanValue()) {
529                result += " " + OpenCmsTheme.EXPIRED;
530            }
531            if ((resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_DISABLED) != null)
532                && ((Boolean)resourceItem.getItemProperty(
533                    CmsResourceTableProperty.PROPERTY_DISABLED).getValue()).booleanValue()) {
534                result += " " + OpenCmsTheme.DISABLED;
535            }
536        }
537        return result;
538    }
539
540    /**
541     * Adds an additional cell style generator.<p>
542     *
543     * @param styleGenerator the cell style generator
544     */
545    public void addAdditionalStyleGenerator(Table.CellStyleGenerator styleGenerator) {
546
547        m_additionalStyleGenerators.add(styleGenerator);
548    }
549
550    /**
551     * Applies settings generally used within workplace app file lists.<p>
552     */
553    public void applyWorkplaceAppSettings() {
554
555        // add site path property to container
556        m_container.addContainerProperty(
557            CmsResourceTableProperty.PROPERTY_SITE_PATH,
558            CmsResourceTableProperty.PROPERTY_SITE_PATH.getColumnType(),
559            CmsResourceTableProperty.PROPERTY_SITE_PATH.getDefaultValue());
560
561        // replace the resource name column with the path column
562        Object[] visibleCols = m_fileTable.getVisibleColumns();
563        for (int i = 0; i < visibleCols.length; i++) {
564            if (CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.equals(visibleCols[i])) {
565                visibleCols[i] = CmsResourceTableProperty.PROPERTY_SITE_PATH;
566            }
567        }
568        m_fileTable.setVisibleColumns(visibleCols);
569        m_fileTable.setColumnCollapsible(CmsResourceTableProperty.PROPERTY_SITE_PATH, false);
570        m_fileTable.setColumnHeader(
571            CmsResourceTableProperty.PROPERTY_SITE_PATH,
572            CmsVaadinUtils.getMessageText(CmsResourceTableProperty.PROPERTY_SITE_PATH.getHeaderKey()));
573
574        // update column visibility according to the latest file explorer settings
575        CmsFileExplorerSettings settings;
576        try {
577            settings = OpenCms.getWorkplaceAppManager().getAppSettings(
578                A_CmsUI.getCmsObject(),
579                CmsFileExplorerSettings.class);
580
581            setTableState(settings);
582        } catch (Exception e) {
583            LOG.error("Error while reading file explorer settings from user.", e);
584        }
585        m_fileTable.setSortContainerPropertyId(CmsResourceTableProperty.PROPERTY_SITE_PATH);
586        setActionColumnProperty(CmsResourceTableProperty.PROPERTY_SITE_PATH);
587        setMenuBuilder(new CmsResourceContextMenuBuilder());
588    }
589
590    /**
591     * Clears all container filters.
592     */
593    public void clearFilters() {
594
595        IndexedContainer container = (IndexedContainer)m_fileTable.getContainerDataSource();
596        container.removeAllContainerFilters();
597    }
598
599    /**
600     * Checks if the file table has a row for the resource with the given structure id.
601     *
602     * @param structureId a structure id
603     * @return true if the file table has a row for the resource with the given id
604     */
605    public boolean containsId(CmsUUID structureId) {
606
607        return m_fileTable.getContainerDataSource().getItem("" + structureId) != null;
608    }
609
610    /**
611    * Filters the displayed resources.<p>
612    * Only resources where either the resource name, the title or the nav-text contains the given substring are shown.<p>
613    *
614    * @param search the search term
615    */
616    public void filterTable(String search) {
617
618        m_container.removeAllContainerFilters();
619        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(search)) {
620            m_container.addContainerFilter(
621                new Or(
622                    new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, search, true, false),
623                    new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT, search, true, false),
624                    new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_TITLE, search, true, false),
625                    new SimpleStringFilter(CmsResourceTableProperty.PROPERTY_CATEGORIES, search, true, false)));
626        }
627        if ((m_fileTable.getValue() != null) & !((Set<?>)m_fileTable.getValue()).isEmpty()) {
628            m_fileTable.setCurrentPageFirstItemId(((Set<?>)m_fileTable.getValue()).iterator().next());
629        }
630    }
631
632    /**
633     * Generates UTF-8 encoded CSV for currently active table columns (standard columns only).
634     *
635     * <p>Note: the generated CSV takes the active filters into account.
636     *
637     * @return the generated CSV data
638     */
639    public byte[] generateCsv() {
640
641        try {
642            Container container = m_fileTable.getContainerDataSource();
643            Object[] columnArray = m_fileTable.getVisibleColumns();
644            Set<Object> columns = new HashSet<>(Arrays.asList(columnArray));
645            LinkedHashMap<Object, Function<Object, String>> columnFormatters = new LinkedHashMap<>();
646            ByteArrayOutputStream baos = new ByteArrayOutputStream();
647            CSVWriter writer = new CSVWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
648            List<String> csvHeaders = new ArrayList<>();
649            List<CmsResourceTableProperty> csvColumns = new ArrayList<>();
650
651            for (Object propId : m_fileTable.getVisibleColumns()) {
652                if (propId instanceof CmsResourceTableProperty) {
653                    CmsResourceTableProperty tableProp = (CmsResourceTableProperty)propId;
654                    if (!m_fileTable.isColumnCollapsed(propId)) {
655                        Class<?> colType = tableProp.getColumnType();
656                        // skip columns with Vaadin types - currently this is just the project flag
657                        if (!colType.getName().contains("vaadin")) {
658                            // always use English column headers, as external tools using the CSV may use the column labels as IDs
659                            String colHeader = OpenCms.getWorkplaceManager().getMessages(Locale.ENGLISH).key(
660                                tableProp.getHeaderKey());
661                            csvHeaders.add(colHeader);
662                            csvColumns.add(tableProp);
663                        }
664                    }
665                }
666            }
667
668            final String[] emptyArray = {};
669            writer.writeNext(csvHeaders.toArray(emptyArray));
670            final DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
671            iso8601.setTimeZone(TimeZone.getTimeZone("UTC"));
672
673            Set<CmsResourceTableProperty> dateCols = new HashSet<>(
674                Arrays.asList(
675                    CmsResourceTableProperty.PROPERTY_DATE_CREATED,
676                    CmsResourceTableProperty.PROPERTY_DATE_MODIFIED,
677                    CmsResourceTableProperty.PROPERTY_DATE_RELEASED,
678                    CmsResourceTableProperty.PROPERTY_DATE_EXPIRED));
679            for (Object itemId : m_fileTable.getContainerDataSource().getItemIds()) {
680                Item item = m_fileTable.getContainerDataSource().getItem(itemId);
681                List<String> row = new ArrayList<>();
682                for (CmsResourceTableProperty col : csvColumns) {
683                    Object value = item.getItemProperty(col).getValue();
684                    // render nulls as empty strings, some special "Long"-valued date columns as ISO8601 time stamps, and just use toString() for everything else
685                    String csvValue = "";
686                    if (value != null) {
687                        if (dateCols.contains(col) && (value instanceof Long)) {
688                            csvValue = iso8601.format(new Date((Long)value));
689                        } else {
690                            csvValue = value.toString();
691                        }
692                    }
693                    row.add(csvValue);
694                }
695                writer.writeNext(row.toArray(emptyArray));
696            }
697            writer.flush();
698            byte[] result = baos.toByteArray();
699            return result;
700        } catch (Exception e) {
701            LOG.error(e.getLocalizedMessage(), e);
702            return null;
703        }
704    }
705
706    /**
707     * Returns the dialog context provider.<p>
708     *
709     * @return the dialog context provider
710     */
711    public I_CmsContextProvider getContextProvider() {
712
713        return m_contextProvider;
714    }
715
716    /**
717     * Returns the index of the first visible item.<p>
718     *
719     * @return the first visible item
720     */
721    public int getFirstVisibleItemIndex() {
722
723        return m_fileTable.getCurrentPageFirstItemIndex();
724    }
725
726    /**
727     * Gets the selected structure ids.<p>
728     *
729     * @return the set of selected structure ids
730     */
731    @SuppressWarnings("unchecked")
732    public Collection<CmsUUID> getSelectedIds() {
733
734        return itemIdsToUUIDs((Collection<String>)m_fileTable.getValue());
735    }
736
737    /**
738     * Gets the list of selected resources.<p>
739     *
740     * @return the list of selected resources
741     */
742    public List<CmsResource> getSelectedResources() {
743
744        return m_currentResources;
745    }
746
747    /**
748     * Returns the current table state.<p>
749     *
750     * @return the table state
751     */
752    public CmsFileExplorerSettings getTableSettings() {
753
754        CmsFileExplorerSettings fileTableState = new CmsFileExplorerSettings();
755
756        fileTableState.setSortAscending(m_fileTable.isSortAscending());
757        fileTableState.setSortColumnId((CmsResourceTableProperty)m_fileTable.getSortContainerPropertyId());
758        List<CmsResourceTableProperty> collapsedCollumns = new ArrayList<>();
759        List<CmsResourceTableProperty> uncollapsedColumns = new ArrayList<>();
760        Object[] visibleCols = m_fileTable.getVisibleColumns();
761        for (int i = 0; i < visibleCols.length; i++) {
762            if (m_fileTable.isColumnCollapsed(visibleCols[i])) {
763                collapsedCollumns.add((CmsResourceTableProperty)visibleCols[i]);
764            } else {
765                uncollapsedColumns.add((CmsResourceTableProperty)visibleCols[i]);
766            }
767        }
768        fileTableState.setCollapsedColumns(collapsedCollumns);
769        fileTableState.setUncollapsedColumns(uncollapsedColumns);
770        return fileTableState;
771    }
772
773    /**
774     * Handles the item selection.<p>
775     *
776     * @param itemId the selected item id
777     */
778    public void handleSelection(String itemId) {
779
780        Collection<?> selection = (Collection<?>)m_fileTable.getValue();
781        if (selection == null) {
782            m_fileTable.select(itemId);
783        } else if (!selection.contains(itemId)) {
784            m_fileTable.setValue(null);
785            m_fileTable.select(itemId);
786        }
787    }
788
789    /**
790     * Returns if a file property is being edited.<p>
791     * @return <code>true</code> if a file property is being edited
792     */
793    public boolean isEditing() {
794
795        return m_editItemId != null;
796    }
797
798    /**
799     * Returns if the given property is being edited.<p>
800     *
801     * @param propertyId the property id
802     *
803     * @return <code>true</code> if the given property is being edited
804     */
805    public boolean isEditProperty(CmsResourceTableProperty propertyId) {
806
807        return (m_editProperty != null) && m_editProperty.equals(propertyId);
808    }
809
810    /**
811     * Opens the context menu.<p>
812     *
813     * @param event the click event
814     */
815    public void openContextMenu(ItemClickEvent event) {
816
817        m_menu.openForTable(event, m_fileTable);
818    }
819
820    /**
821     * Removes the given cell style generator.<p>
822     *
823     * @param styleGenerator the cell style generator to remove
824     */
825    public void removeAdditionalStyleGenerator(Table.CellStyleGenerator styleGenerator) {
826
827        m_additionalStyleGenerators.remove(styleGenerator);
828    }
829
830    /**
831     * Restores container filters to the ones previously saved via saveFilters().
832     */
833    public void restoreFilters() {
834
835        if (m_filterStack.size() > 0) {
836            IndexedContainer container = (IndexedContainer)m_fileTable.getContainerDataSource();
837            container.removeAllContainerFilters();
838            Collection<Filter> filters = m_filterStack.remove(m_filterStack.size() - 1);
839            for (Filter filter : filters) {
840                container.addContainerFilter(filter);
841            }
842        } else {
843            LOG.error("restoreFilter called but no saved filters available");
844        }
845    }
846
847    /**
848     * Saves currently active filters.<p>
849     */
850    public void saveFilters() {
851
852        IndexedContainer container = (IndexedContainer)m_fileTable.getContainerDataSource();
853        Collection<Filter> filters = container.getContainerFilters();
854        m_filterStack.add(new ArrayList<>(filters)); // we need to make a copy because the list returned by getContainerFilters() is changed dynamically by the container
855    }
856
857    /**
858     * Sets the default action column property.<p>
859     *
860     * @param actionColumnProperty the default action column property
861     */
862    public void setActionColumnProperty(CmsResourceTableProperty actionColumnProperty) {
863
864        m_actionColumnProperty = actionColumnProperty;
865    }
866
867    /**
868     * Sets the dialog context provider.<p>
869     *
870     * @param provider the dialog context provider
871     */
872    public void setContextProvider(I_CmsContextProvider provider) {
873
874        m_contextProvider = provider;
875    }
876
877    /**
878     * Sets the first visible item index.<p>
879     *
880     * @param i the item index
881     */
882    public void setFirstVisibleItemIndex(int i) {
883
884        m_fileTable.setCurrentPageFirstItemIndex(i);
885    }
886
887    /**
888     * Sets the folder select handler.<p>
889     *
890     * @param folderSelectHandler the folder select handler
891     */
892    public void setFolderSelectHandler(I_FolderSelectHandler folderSelectHandler) {
893
894        m_folderSelectHandler = folderSelectHandler;
895    }
896
897    /**
898     * Sets the menu builder.<p>
899     *
900     * @param builder the menu builder
901     */
902    public void setMenuBuilder(I_CmsContextMenuBuilder builder) {
903
904        m_menuBuilder = builder;
905    }
906
907    /**
908     * Sets the table state.<p>
909     *
910     * @param state the table state
911     */
912    public void setTableState(CmsFileExplorerSettings state) {
913
914        if (state != null) {
915            m_fileTable.setSortContainerPropertyId(state.getSortColumnId());
916            m_fileTable.setSortAscending(state.isSortAscending());
917            Object[] visibleCols = m_fileTable.getVisibleColumns();
918            for (int i = 0; i < visibleCols.length; i++) {
919                boolean isCollapsed;
920                // Originally, just the collapsed columns would be stored in the user settings.
921                // The problem with this is that if new columns were added in a new version of OpenCms,
922                // those would be active by default for users who had stored table settings.
923                // So we now also store the uncollapsed columns, and prioritize this list for deciding whether
924                // a column should be visible.
925                if (state.getUncollapsedColumns() != null) {
926                    isCollapsed = !state.getUncollapsedColumns().contains(visibleCols[i]);
927                } else {
928                    isCollapsed = state.getCollapsedColumns().contains(visibleCols[i])
929                        || CmsResourceTableProperty.PROPERTY_CATEGORIES.equals(visibleCols[i]);
930                }
931                m_fileTable.setColumnCollapsed(visibleCols[i], isCollapsed);
932            }
933        }
934    }
935
936    /**
937     * Starts inline editing of the given file property.<p>
938     *
939     * @param itemId the item resource structure id
940     * @param propertyId the property to edit
941     * @param editHandler the edit handler
942     */
943    public void startEdit(
944        CmsUUID itemId,
945        CmsResourceTableProperty propertyId,
946        I_CmsFilePropertyEditHandler editHandler) {
947
948        m_editItemId = itemId;
949        m_editProperty = propertyId;
950        m_originalEditValue = (String)m_container.getItem(m_editItemId.toString()).getItemProperty(
951            m_editProperty).getValue();
952        m_editHandler = editHandler;
953
954        // storing current drag mode and setting it to none to avoid text selection issues in IE11
955        m_beforEditDragMode = m_fileTable.getDragMode();
956        m_fileTable.setDragMode(TableDragMode.NONE);
957
958        m_fileTable.setEditable(true);
959    }
960
961    /**
962     * Stops the current edit process to save the changed property value.<p>
963     */
964    public void stopEdit() {
965
966        if (m_editHandler != null) {
967            saveFilters();
968            clearFilters();
969            try {
970                String value = (String)m_container.getItem(m_editItemId.toString()).getItemProperty(
971                    m_editProperty).getValue();
972                if (!value.equals(m_originalEditValue)) {
973                    m_editHandler.validate(value);
974                    m_editHandler.save(value);
975                } else {
976                    // call cancel to ensure unlock
977                    m_editHandler.cancel();
978                }
979            } finally {
980                restoreFilters();
981            }
982        }
983        clearEdit();
984
985        // restoring drag mode
986        m_fileTable.setDragMode(m_beforEditDragMode);
987
988        m_beforEditDragMode = null;
989    }
990
991    /**
992     * Updates all items with ids from the given list.<p>
993     *
994     * @param ids the resource structure ids to update
995     * @param remove true if the item should be removed only
996     */
997    public void update(Collection<CmsUUID> ids, boolean remove) {
998
999        for (CmsUUID id : ids) {
1000            updateItem(id, remove);
1001        }
1002        rebuildMenu();
1003    }
1004
1005    /**
1006     * Updates the column widths.<p>
1007     *
1008     * The reason this is needed is that the Vaadin table does not support minimum widths for columns,
1009     * so expanding columns get squished when most of the horizontal space is used by other columns.
1010     * So we try to determine whether the expanded columns would have enough space, and if not, give them a
1011     * fixed width.
1012     *
1013     * @param estimatedSpace the estimated horizontal space available for the table.
1014     */
1015    public void updateColumnWidths(int estimatedSpace) {
1016
1017        Object[] cols = m_fileTable.getVisibleColumns();
1018        List<CmsResourceTableProperty> expandCols = Lists.newArrayList();
1019        int nonExpandWidth = 0;
1020        int totalExpandMinWidth = 0;
1021        for (Object colObj : cols) {
1022            if (m_fileTable.isColumnCollapsed(colObj)) {
1023                continue;
1024            }
1025            CmsResourceTableProperty prop = (CmsResourceTableProperty)colObj;
1026            if (0 < m_fileTable.getColumnExpandRatio(prop)) {
1027                expandCols.add(prop);
1028                totalExpandMinWidth += getAlternativeWidthForExpandingColumns(prop);
1029            } else {
1030                nonExpandWidth += prop.getColumnWidth();
1031            }
1032        }
1033        if (estimatedSpace < (totalExpandMinWidth + nonExpandWidth)) {
1034            for (CmsResourceTableProperty expandCol : expandCols) {
1035                m_fileTable.setColumnWidth(expandCol, getAlternativeWidthForExpandingColumns(expandCol));
1036            }
1037        }
1038    }
1039
1040    /**
1041     * Updates the file table sorting.<p>
1042     */
1043    public void updateSorting() {
1044
1045        m_fileTable.sort();
1046    }
1047
1048    /**
1049     * Cancels the current edit process.<p>
1050     */
1051    void cancelEdit() {
1052
1053        if (m_editHandler != null) {
1054            m_editHandler.cancel();
1055        }
1056        clearEdit();
1057    }
1058
1059    /**
1060     * Returns the edit item id.<p>
1061     *
1062     * @return the edit item id
1063     */
1064    CmsUUID getEditItemId() {
1065
1066        return m_editItemId;
1067    }
1068
1069    /**
1070     * Returns the edit property id.<p>
1071     *
1072     * @return the edit property id
1073     */
1074    CmsResourceTableProperty getEditProperty() {
1075
1076        return m_editProperty;
1077    }
1078
1079    /**
1080     * Handles the file table item click.<p>
1081     *
1082     * @param event the click event
1083     */
1084    void handleFileItemClick(ItemClickEvent event) {
1085
1086        if (isEditing()) {
1087            stopEdit();
1088
1089        } else if (!event.isCtrlKey() && !event.isShiftKey()) {
1090            // don't interfere with multi-selection using control key
1091            String itemId = (String)event.getItemId();
1092            CmsUUID structureId = getUUIDFromItemID(itemId);
1093            boolean openedFolder = false;
1094            if (event.getButton().equals(MouseButton.RIGHT)) {
1095                handleSelection(itemId);
1096                openContextMenu(event);
1097            } else {
1098                if ((event.getPropertyId() == null)
1099                    || CmsResourceTableProperty.PROPERTY_TYPE_ICON.equals(event.getPropertyId())) {
1100                    handleSelection(itemId);
1101                    openContextMenu(event);
1102                } else {
1103                    if (m_actionColumnProperty.equals(event.getPropertyId())) {
1104                        Boolean isFolder = (Boolean)event.getItem().getItemProperty(
1105                            CmsResourceTableProperty.PROPERTY_IS_FOLDER).getValue();
1106                        if ((isFolder != null) && isFolder.booleanValue()) {
1107                            if (m_folderSelectHandler != null) {
1108                                m_folderSelectHandler.onFolderSelect(structureId);
1109                            }
1110                            openedFolder = true;
1111                        } else {
1112                            try {
1113                                CmsObject cms = A_CmsUI.getCmsObject();
1114                                CmsResource res = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
1115                                m_currentResources = Collections.singletonList(res);
1116                                I_CmsDialogContext context = m_contextProvider.getDialogContext();
1117                                I_CmsDefaultAction action = OpenCms.getWorkplaceAppManager().getDefaultAction(
1118                                    context,
1119                                    m_menuBuilder);
1120                                if (action != null) {
1121                                    action.executeAction(context);
1122                                    return;
1123                                }
1124                            } catch (CmsVfsResourceNotFoundException e) {
1125                                LOG.info(e.getLocalizedMessage(), e);
1126                            } catch (CmsException e) {
1127                                LOG.error(e.getLocalizedMessage(), e);
1128                            }
1129                        }
1130                    } else {
1131                        I_CmsDialogContext context = m_contextProvider.getDialogContext();
1132                        if ((m_currentResources.size() == 1)
1133                            && m_currentResources.get(0).getStructureId().equals(structureId)
1134                            && (context instanceof I_CmsEditPropertyContext)
1135                            && ((I_CmsEditPropertyContext)context).isPropertyEditable(event.getPropertyId())) {
1136
1137                            ((I_CmsEditPropertyContext)context).editProperty(event.getPropertyId());
1138                        }
1139                    }
1140                }
1141            }
1142            // update the item on click to show any available changes
1143            if (!openedFolder) {
1144                update(Collections.singletonList(structureId), false);
1145            }
1146        }
1147    }
1148
1149    /**
1150     * Rebuilds the context menu.<p>
1151     */
1152    void rebuildMenu() {
1153
1154        if (!getSelectedIds().isEmpty() && (m_menuBuilder != null)) {
1155            m_menu.removeAllItems();
1156            m_menuBuilder.buildContextMenu(getContextProvider().getDialogContext(), m_menu);
1157        }
1158    }
1159
1160    /**
1161     * Clears the actions that were set for editing a column.
1162     */
1163    private void clearColumnEditActions() {
1164
1165        if (m_columnEditEnterRegistration != null) {
1166            m_columnEditEnterRegistration.remove();
1167            m_columnEditEnterRegistration = null;
1168        }
1169        if (m_columnEditEscRegistration != null) {
1170            m_columnEditEscRegistration.remove();
1171            m_columnEditEscRegistration = null;
1172        }
1173    }
1174
1175    /**
1176     * Clears the current edit process.<p>
1177     */
1178    private void clearEdit() {
1179
1180        m_fileTable.setEditable(false);
1181        if (m_editItemId != null) {
1182            try {
1183                // current filter may prevent item from being updated
1184                saveFilters();
1185                clearFilters();
1186                updateItem(m_editItemId, false);
1187            } finally {
1188                restoreFilters();
1189            }
1190        }
1191        m_editItemId = null;
1192        m_editProperty = null;
1193        m_editHandler = null;
1194        clearColumnEditActions();
1195        updateSorting();
1196    }
1197
1198    /**
1199     * Gets alternative width for expanding table columns which is used when there is not enough space for
1200     * all visible columns.<p>
1201     *
1202     * @param prop the table property
1203     * @return the alternative column width
1204     */
1205    private int getAlternativeWidthForExpandingColumns(CmsResourceTableProperty prop) {
1206
1207        if (prop.getId().equals(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME.getId())) {
1208            return 200;
1209        }
1210        if (prop.getId().equals(CmsResourceTableProperty.PROPERTY_TITLE.getId())) {
1211            return 300;
1212        }
1213        if (prop.getId().equals(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.getId())) {
1214            return 200;
1215        }
1216        return 200;
1217    }
1218
1219    /**
1220     * Updates the given item in the file table.<p>
1221     *
1222     * @param itemId the item id
1223     * @param remove true if the item should be removed only
1224     */
1225    private void updateItem(CmsUUID itemId, boolean remove) {
1226
1227        if (remove) {
1228            String idStr = itemId != null ? itemId.toString() : null;
1229            m_container.removeItem(idStr);
1230            return;
1231        }
1232
1233        CmsObject cms = A_CmsUI.getCmsObject();
1234        try {
1235            CmsResource resource = cms.readResource(itemId, CmsResourceFilter.ALL);
1236            fillItem(cms, resource, OpenCms.getWorkplaceManager().getWorkplaceLocale(cms));
1237
1238        } catch (CmsVfsResourceNotFoundException e) {
1239            if (null != itemId) {
1240                m_container.removeItem(itemId.toString());
1241            }
1242            LOG.debug("Failed to update file table item, removing it from view.", e);
1243        } catch (CmsException e) {
1244            LOG.error(e.getLocalizedMessage(), e);
1245        }
1246    }
1247
1248}