001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, 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.fileselect;
029
030import static org.opencms.ui.components.CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT;
031
032import org.opencms.db.CmsResourceState;
033import org.opencms.file.CmsObject;
034import org.opencms.file.CmsPropertyDefinition;
035import org.opencms.file.CmsResource;
036import org.opencms.file.CmsResourceFilter;
037import org.opencms.file.CmsVfsResourceNotFoundException;
038import org.opencms.jsp.CmsJspNavBuilder;
039import org.opencms.jsp.CmsJspNavElement;
040import org.opencms.main.CmsException;
041import org.opencms.main.CmsLog;
042import org.opencms.main.OpenCms;
043import org.opencms.ui.CmsVaadinUtils;
044import org.opencms.ui.apps.Messages;
045import org.opencms.ui.components.CmsErrorDialog;
046import org.opencms.ui.components.CmsResourceIcon;
047import org.opencms.ui.components.CmsResourceTableProperty;
048import org.opencms.ui.util.I_CmsItemSorter;
049import org.opencms.util.CmsUUID;
050import org.opencms.workplace.explorer.CmsResourceUtil;
051
052import java.util.ArrayList;
053import java.util.Collection;
054import java.util.List;
055
056import org.apache.commons.logging.Log;
057
058import com.google.common.collect.Lists;
059import com.vaadin.v7.data.Container;
060import com.vaadin.v7.data.Item;
061import com.vaadin.v7.data.util.HierarchicalContainer;
062
063/**
064 * The data container for the sitmeap folder selection tree.<p>
065 */
066public class CmsResourceTreeContainer extends HierarchicalContainer {
067
068    /** Property which is used to store the CmsResource. */
069    public static final String PROPERTY_RESOURCE = "RESOURCE";
070
071    /** Property which is used to store the sitemap view caption HTML. */
072    public static final String PROPERTY_SITEMAP_CAPTION = "SITEMAP_CAPTION";
073
074    /** The logger instance for this class. */
075    private static final Log LOG = CmsLog.getLog(CmsResourceTreeContainer.class);
076
077    /** Serial version id. */
078    private static final long serialVersionUID = 1L;
079
080    /** The resource filter. */
081    private CmsResourceFilter m_filter;
082
083    /**
084     * Default constructor.<p>
085     *
086     * @param filter the resource filter to use
087     */
088    public CmsResourceTreeContainer(CmsResourceFilter filter) {
089
090        m_filter = filter;
091        defineProperties();
092    }
093
094    /**
095     * Adds an item to the folder tree.<p>
096     *
097     * @param cms the CMS context
098     * @param resource the folder resource
099     * @param parentId the parent folder id
100     */
101    public void addTreeItem(CmsObject cms, CmsResource resource, CmsUUID parentId) {
102
103        List<Container.Filter> filters = Lists.newArrayList(getContainerFilters());
104
105        // getItem only finds an existing item if it isn't filtered out by the container
106        // filters, so we temporarily remove the filters to access the complete data set
107        removeAllContainerFilters();
108        try {
109            Item resourceItem = getItem(resource.getStructureId());
110            if (resourceItem == null) {
111                resourceItem = addItem(resource.getStructureId());
112            }
113            fillProperties(cms, resourceItem, resource, parentId);
114            if (resource.isFile()) {
115                setChildrenAllowed(resource.getStructureId(), false);
116            }
117            if (parentId != null) {
118                setParent(resource.getStructureId(), parentId);
119            }
120        } finally {
121            for (Container.Filter filter : filters) {
122                addContainerFilter(filter);
123            }
124        }
125    }
126
127    /**
128     * @see com.vaadin.v7.data.util.IndexedContainer#getSortableContainerPropertyIds()
129     */
130    @Override
131    public Collection<?> getSortableContainerPropertyIds() {
132
133        if (getItemSorter() instanceof I_CmsItemSorter) {
134            return ((I_CmsItemSorter)getItemSorter()).getSortableContainerPropertyIds(this);
135        } else {
136            return super.getSortableContainerPropertyIds();
137        }
138    }
139
140    /**
141     * Initializes the root level of the tree.<p>
142     *
143     * @param cms the CMS context
144     * @param root the root folder
145     */
146    public void initRoot(CmsObject cms, CmsResource root) {
147
148        addTreeItem(cms, root, null);
149        readTreeLevel(cms, root.getStructureId());
150    }
151
152    /**
153     * Reads the given tree level.<p>
154     * @param cms the CMS context
155     * @param parentId the parent id
156     */
157    public void readTreeLevel(CmsObject cms, CmsUUID parentId) {
158
159        try {
160            CmsResource parent = cms.readResource(parentId, m_filter);
161            List<CmsResource> children = cms.readResources(parent, m_filter, false);
162
163            // sets the parent to leaf mode, in case no child folders are present
164            setChildrenAllowed(parentId, !children.isEmpty());
165
166            for (CmsResource resource : children) {
167                addTreeItem(cms, resource, parentId);
168            }
169        } catch (CmsException e) {
170            CmsErrorDialog.showErrorDialog(
171                CmsVaadinUtils.getMessageText(Messages.ERR_EXPLORER_CAN_NOT_READ_RESOURCE_1, parentId),
172                e);
173            LOG.error(e.getLocalizedMessage(), e);
174        }
175    }
176
177    /**
178     * Clears the given tree level.<p>
179     *
180     * @param parentId the parent id
181     */
182    public void removeChildren(CmsUUID parentId) {
183
184        // create a new list to avoid concurrent modifications
185        Collection<?> children = getChildren(parentId);
186        // may be null when monkey clicking
187        if (children != null) {
188            List<Object> childIds = new ArrayList<Object>(children);
189            for (Object childId : childIds) {
190                removeItemRecursively(childId);
191            }
192        }
193    }
194
195    /**
196     * Updates the item for the given structure id.<p>
197     *
198     * @param cms the CMS context
199     * @param id the structure id
200     * @param filter the resource filter used for reading the resource
201     *
202     * @throws CmsException if something goes wrong
203     */
204    public void update(CmsObject cms, CmsUUID id, CmsResourceFilter filter) throws CmsException {
205
206        try {
207            CmsResource resource = cms.readResource(id, filter);
208
209            CmsResource parent = cms.readParentFolder(id);
210            CmsUUID parentId = parent.getStructureId();
211            Item resourceItem = getItem(id);
212            if (resourceItem != null) {
213                fillProperties(cms, resourceItem, resource, parentId);
214                if (parentId != null) {
215                    setParent(resource.getStructureId(), parentId);
216                }
217            } else {
218                addTreeItem(cms, resource, parentId);
219            }
220        } catch (CmsVfsResourceNotFoundException e) {
221            removeItemRecursively(id);
222            LOG.debug(e.getLocalizedMessage(), e);
223        }
224    }
225
226    /**
227     * Updates the item order according to the latest sort setting.<p>
228     */
229    public void updateSort() {
230
231        doSort();
232
233        // Post sort updates
234        if (isFiltered()) {
235            filterAll();
236        } else {
237            fireItemSetChange();
238        }
239    }
240
241    /**
242     * Defines the container properties.<p>
243     */
244    protected void defineProperties() {
245
246        addContainerProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME, String.class, null);
247        addContainerProperty(CmsResourceTableProperty.PROPERTY_STATE, CmsResourceState.class, null);
248        addContainerProperty(CmsResourceTableProperty.PROPERTY_TREE_CAPTION, String.class, null);
249        addContainerProperty(CmsResourceTableProperty.PROPERTY_INSIDE_PROJECT, Boolean.class, Boolean.TRUE);
250        addContainerProperty(CmsResourceTableProperty.PROPERTY_IS_FOLDER, Boolean.class, Boolean.TRUE);
251        addContainerProperty(PROPERTY_RESOURCE, CmsResource.class, null);
252
253        addContainerProperty(CmsResourceTableProperty.PROPERTY_IN_NAVIGATION, Boolean.class, Boolean.FALSE);
254        addContainerProperty(
255            CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION,
256            Float.class,
257            Float.valueOf(Float.MAX_VALUE));
258        addContainerProperty(PROPERTY_SITEMAP_CAPTION, String.class, "");
259        addContainerProperty(
260            CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT,
261            CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT.getColumnType(),
262            "");
263    }
264
265    /**
266     * Fills the properties of a tree item.<p>
267     *
268     * @param cms the CMS context
269     * @param resourceItem the empty item
270     * @param resource the resource for which the tree item is being created
271     * @param parentId the parent id
272     */
273    protected void fillProperties(CmsObject cms, Item resourceItem, CmsResource resource, CmsUUID parentId) {
274
275        resourceItem.getItemProperty(PROPERTY_RESOURCE).setValue(resource);
276        // use the root path as name in case of the root item
277        String name = getName(cms, resource, parentId);
278        if (resource.isFolder() && !name.endsWith("/")) {
279            name += "/";
280        }
281        CmsResourceUtil resUtil = new CmsResourceUtil(cms, resource);
282        resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_RESOURCE_NAME).setValue(name);
283        resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_STATE).setValue(resource.getState());
284
285        resourceItem.getItemProperty(PROPERTY_INSIDE_PROJECT).setValue(Boolean.valueOf(resUtil.isInsideProject()));
286        resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_IS_FOLDER).setValue(
287            Boolean.valueOf(resource.isFolder()));
288        try {
289            CmsObject rootCms = OpenCms.initCmsObject(cms);
290            rootCms.getRequestContext().setSiteRoot("");
291            CmsJspNavBuilder builder = new CmsJspNavBuilder(rootCms);
292            CmsJspNavElement nav = builder.getNavigationForResource(resource.getRootPath(), m_filter);
293            boolean inNavigation = (nav != null) && nav.isInNavigation();
294            resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_IN_NAVIGATION).setValue(
295                Boolean.valueOf(inNavigation));
296            String navText = null;
297            if (nav != null) {
298                if (inNavigation) {
299                    resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_NAVIGATION_POSITION).setValue(
300                        Float.valueOf(nav.getNavPosition()));
301                }
302
303                if (nav.getProperties().containsKey(CmsPropertyDefinition.PROPERTY_NAVTEXT)) {
304                    navText = nav.getProperties().get(CmsPropertyDefinition.PROPERTY_NAVTEXT);
305                } else if (nav.getProperties().containsKey(CmsPropertyDefinition.PROPERTY_TITLE)) {
306                    navText = nav.getProperties().get(CmsPropertyDefinition.PROPERTY_TITLE);
307                }
308            }
309            resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_NAVIGATION_TEXT).setValue(navText);
310            String folderCaption;
311            folderCaption = CmsResourceIcon.getTreeCaptionHTML(name, resUtil, null, false);
312            resourceItem.getItemProperty(CmsResourceTableProperty.PROPERTY_TREE_CAPTION).setValue(folderCaption);
313            if (inNavigation) {
314                if (navText == null) {
315                    navText = name;
316                }
317                String sitemapCaption = CmsResourceIcon.getTreeCaptionHTML(navText, resUtil, null, false);
318                resourceItem.getItemProperty(PROPERTY_SITEMAP_CAPTION).setValue(sitemapCaption);
319            }
320
321        } catch (CmsException e) {
322            LOG.error(e.getLocalizedMessage(), e);
323        }
324
325    }
326
327    /**
328     * Gets the name to display for the given resource.<p>
329     *
330     * @param cms the CMS context
331     * @param resource a resource
332     * @param parentId the id of the parent of the resource
333     *
334     * @return the name for the given resoure
335     */
336    protected String getName(CmsObject cms, CmsResource resource, CmsUUID parentId) {
337
338        return parentId == null ? resource.getRootPath() : resource.getName();
339    }
340}