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