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;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.detailpage.CmsDetailPageInfo;
032import org.opencms.db.CmsResourceState;
033import org.opencms.file.CmsObject;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.types.CmsResourceTypeFolderExtended;
037import org.opencms.file.types.CmsResourceTypeFolderSubSitemap;
038import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
039import org.opencms.file.types.I_CmsResourceType;
040import org.opencms.jsp.CmsJspNavBuilder;
041import org.opencms.main.CmsException;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.security.CmsSecurityException;
045import org.opencms.ui.CmsCssIcon;
046import org.opencms.ui.CmsVaadinUtils;
047import org.opencms.workplace.CmsWorkplace;
048import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
049import org.opencms.workplace.explorer.CmsResourceUtil;
050import org.opencms.workplace.list.Messages;
051import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
052
053import java.util.List;
054
055import org.apache.commons.logging.Log;
056
057import com.google.common.collect.Lists;
058import com.vaadin.server.ExternalResource;
059import com.vaadin.server.FontIcon;
060import com.vaadin.server.Resource;
061import com.vaadin.v7.shared.ui.label.ContentMode;
062import com.vaadin.v7.ui.Label;
063
064/**
065 * Displays the resource icon and state and lock info.<p>
066 * Important: To avoid issues with click event propagation within tables, we are required to extent the Label component.
067 */
068public class CmsResourceIcon extends Label {
069
070    /** Enum used to control icon display style. */
071    public enum IconMode {
072        /** locale compare mode. */
073        localeCompare,
074
075        /** sitemap selection mode. */
076        sitemapSelect;
077    }
078
079    /** The changed icon class. */
080    public static final String ICON_CLASS_CHANGED = "oc-icon-16-overlay-changed";
081
082    /** The other user lock icon class. */
083    public static final String ICON_CLASS_LOCK_OTHER = "oc-icon-16-lock-other";
084
085    /** The own user lock icon class. */
086    public static final String ICON_CLASS_LOCK_OWN = "oc-icon-16-lock-own";
087
088    /** The publish lock icon class. */
089    public static final String ICON_CLASS_LOCK_PUBLISH = "oc-icon-16-lock-publish";
090
091    /** The shared lock icon class. */
092    public static final String ICON_CLASS_LOCK_SHARED = "oc-icon-16-lock-shared";
093
094    /** The sibling icon class. */
095    public static final String ICON_CLASS_SIBLING = "oc-icon-16-overlay-sibling";
096
097    /** The log object for this class. */
098    private static final Log LOG = CmsLog.getLog(CmsResourceIcon.class);
099
100    /** The serial version id. */
101    private static final long serialVersionUID = 5031544534869165777L;
102
103    /**
104     * Constuctor.<p>
105     * To be used in declarative layouts. Make sure to call initContent later on.<p>
106     */
107    public CmsResourceIcon() {
108        setPrimaryStyleName(OpenCmsTheme.RESOURCE_ICON);
109        setContentMode(ContentMode.HTML);
110    }
111
112    /**
113     * Constructor.<p>
114     *
115     * @param resUtil the resource util
116     * @param state the resource state
117     * @param showLocks <code>true</code> to show the resource locks
118     */
119    public CmsResourceIcon(CmsResourceUtil resUtil, CmsResourceState state, boolean showLocks) {
120        this();
121        initContent(resUtil, state, showLocks, true);
122    }
123
124    /**
125     * Returns the default file type or detail type for the given resource.<p>
126     *
127     * @param cms the cms context
128     * @param resource the container page resource
129     *
130     * @return the detail content type
131     */
132    public static String getDefaultFileOrDetailType(CmsObject cms, CmsResource resource) {
133
134        String type = null;
135
136        if (resource.isFolder()
137            && !(OpenCms.getResourceManager().getResourceType(resource) instanceof CmsResourceTypeFolderExtended)
138            && !CmsJspNavBuilder.isNavLevelFolder(cms, resource)) {
139            try {
140                CmsResource defaultFile = cms.readDefaultFile(resource, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
141                if (defaultFile != null) {
142                    type = getDetailType(cms, defaultFile, resource);
143                    if (type == null) {
144                        type = OpenCms.getResourceManager().getResourceType(defaultFile).getTypeName();
145                    }
146
147                }
148            } catch (CmsSecurityException e) {
149                // ignore
150            }
151        } else if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) {
152            type = getDetailType(cms, resource, null);
153
154        }
155        return type;
156
157    }
158
159    /**
160     * Returns the detail content type for container pages that may be detail pages.<p>
161     *
162     * @param cms the cms context
163     * @param detailPage the container page resource
164     * @param parentFolder the parent folder or <code>null</code>
165     *
166     * @return the detail content type
167     */
168    public static String getDetailType(CmsObject cms, CmsResource detailPage, CmsResource parentFolder) {
169
170        String type = null;
171        try {
172            if (OpenCms.getADEManager().isDetailPage(cms, detailPage)) {
173                List<CmsDetailPageInfo> detailPages = OpenCms.getADEManager().getAllDetailPages(cms);
174                if (parentFolder == null) {
175                    parentFolder = cms.readParentFolder(detailPage.getStructureId());
176                }
177                for (CmsDetailPageInfo info : detailPages) {
178                    if (info.getId().equals(detailPage.getStructureId())
179                        || info.getId().equals(parentFolder.getStructureId())) {
180                        type = info.getType();
181                        if (type.startsWith(CmsDetailPageInfo.FUNCTION_PREFIX)) {
182                            type = CmsXmlDynamicFunctionHandler.TYPE_FUNCTION;
183                        }
184                        break;
185                    }
186                }
187            }
188        } catch (CmsException e) {
189            // parent folder can't be read, ignore
190        }
191        return type;
192    }
193
194    /**
195     * Returns the icon HTML.<p>
196     *
197     * @param resUtil the resource util for the resource
198     * @param state the resource state
199     * @param showLocks <code>true</code> to show lock state overlay
200     *
201     * @return the icon HTML
202     */
203    public static String getIconHTML(CmsResourceUtil resUtil, CmsResourceState state, boolean showLocks) {
204
205        return "<span class=\""
206            + OpenCmsTheme.RESOURCE_ICON
207            + "\">"
208            + getIconInnerHTML(resUtil, state, showLocks, true)
209            + "</span>";
210    }
211
212    /**
213     * Gets the resource icon for a resource for use in a CmsResourceInfo widget when used in a sitemap context.<p>
214     *
215     * @param cms the CMS context
216     * @param resource a resource
217     * @param iconMode the icon mode
218     * @return the path for the resource icon
219     */
220    public static Resource getSitemapResourceIcon(CmsObject cms, CmsResource resource, IconMode iconMode) {
221
222        CmsResource defaultFile = null;
223        List<CmsResource> resourcesForType = Lists.newArrayList();
224        resourcesForType.add(resource);
225        boolean skipDefaultFile = (iconMode == IconMode.sitemapSelect)
226            && OpenCms.getResourceManager().matchResourceType(
227                CmsResourceTypeFolderSubSitemap.TYPE_SUBSITEMAP,
228                resource.getTypeId());
229        if (resource.isFolder() && !skipDefaultFile) {
230
231            try {
232                defaultFile = cms.readDefaultFile(resource, CmsResourceFilter.IGNORE_EXPIRATION);
233                if (defaultFile != null) {
234                    resourcesForType.add(0, defaultFile);
235                }
236            } catch (Exception e) {
237                // Shouldn't normally happen - readDefaultFile returns null instead of throwing an exception when it doesn't find a default file
238            }
239        }
240        if (CmsJspNavBuilder.isNavLevelFolder(cms, resource)) {
241            return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_NAV_LEVEL_BIG);
242        }
243        CmsResource maybePage = resourcesForType.get(0);
244        if (CmsResourceTypeXmlContainerPage.isContainerPage(maybePage)) {
245            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(cms, maybePage.getRootPath());
246            for (CmsDetailPageInfo realInfo : config.getAllDetailPages(true)) {
247                if (realInfo.getUri().equals(maybePage.getRootPath())
248                    || realInfo.getUri().equals(CmsResource.getParentFolder(maybePage.getRootPath()))) {
249                    CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
250                        realInfo.getIconType());
251                    if (settings != null) {
252                        return CmsResourceUtil.getBigIconResource(settings, resource.getName());
253                    }
254                }
255            }
256        }
257
258        Resource result = null;
259        for (CmsResource res : resourcesForType) {
260            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(res);
261            CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName());
262            if (settings != null) {
263                result = CmsResourceUtil.getBigIconResource(settings, res.getName());
264                break;
265            }
266        }
267        return result;
268    }
269
270    /**
271     * Returns the tree caption HTML including the resource icon.<p>
272     *
273     * @param resourceName the resource name to display
274     * @param resUtil the resource util for the resource
275     * @param state the resource state
276     * @param showLocks <code>true</code> to show lock state overlay
277     *
278     * @return the icon HTML
279     */
280    public static String getTreeCaptionHTML(
281        String resourceName,
282        CmsResourceUtil resUtil,
283        CmsResourceState state,
284        boolean showLocks) {
285
286        return CmsResourceIcon.getIconHTML(resUtil, null, false)
287            + "<span class=\"o-tree-caption\">"
288            + resourceName
289            + "</span>";
290    }
291
292    /**
293     * Returns the icon inner HTML.<p>
294     *
295     * @param resUtil the resource util for the resource
296     * @param state the resource state
297     * @param showLocks <code>true</code> to show lock state overlay
298     * @param showDetailIcon <code>true</code> to show the detail icon overlay
299     *
300     * @return the icon inner HTML
301     */
302    private static String getIconInnerHTML(
303        CmsResourceUtil resUtil,
304        CmsResourceState state,
305        boolean showLocks,
306        boolean showDetailIcon) {
307
308        Resource iconResource = resUtil.getBigIconResource();
309
310        return getIconInnerHTML(resUtil, iconResource, state, showLocks, showDetailIcon);
311    }
312
313    /**
314     * Returns the icon inner HTML.<p>
315     *
316     * @param resUtil the resource util for the resource
317     * @param iconResource the icon path
318     * @param state the resource state
319     * @param showLocks <code>true</code> to show lock state overlay
320     * @param showDetailIcon <code>true</code> to show the detail icon overlay
321     *
322     * @return the icon inner HTML
323     */
324    private static String getIconInnerHTML(
325        CmsResourceUtil resUtil,
326        Resource iconResource,
327        CmsResourceState state,
328        boolean showLocks,
329        boolean showDetailIcon) {
330
331        String content;
332        if (iconResource instanceof FontIcon) {
333            content = ((FontIcon)iconResource).getHtml();
334        } else if (iconResource instanceof ExternalResource) {
335            content = "<img src=\"" + ((ExternalResource)iconResource).getURL() + "\" />";
336        } else {
337            content = "";
338        }
339
340        boolean isNavLevel = false;
341
342        if (resUtil != null) {
343            if (showDetailIcon && !isNavLevel) {
344
345                if (resUtil.getResource().isFolder()) {
346                    String detailType = getDefaultFileOrDetailType(resUtil.getCms(), resUtil.getResource());
347                    if (detailType != null) {
348                        String smallIconUri = getSmallTypeIconHTML(detailType, false);
349                        if (smallIconUri != null) {
350                            content += smallIconUri;
351                        }
352                    }
353                } else if (CmsResourceTypeXmlContainerPage.isContainerPage(resUtil.getResource())) {
354                    String detailType = getDefaultFileOrDetailType(resUtil.getCms(), resUtil.getResource());
355                    if (detailType != null) {
356                        String smallIconUri = getSmallTypeIconHTML(detailType, true);
357                        if (smallIconUri != null) {
358                            content += smallIconUri;
359                        }
360                    }
361
362                }
363            }
364            if (showLocks) {
365                String lockIcon;
366                String message = null;
367                if (resUtil.getLock().getSystemLock().isPublish()) {
368                    lockIcon = OpenCmsTheme.LOCK_PUBLISH + " " + ICON_CLASS_LOCK_PUBLISH;
369                    message = CmsVaadinUtils.getMessageText(
370                        org.opencms.workplace.explorer.Messages.GUI_PUBLISH_TOOLTIP_0);
371                } else {
372                    switch (resUtil.getLockState()) {
373                        case 1:
374                            lockIcon = OpenCmsTheme.LOCK_OTHER + " " + ICON_CLASS_LOCK_OTHER;
375                            break;
376
377                        case 2:
378                            lockIcon = OpenCmsTheme.LOCK_SHARED + " " + ICON_CLASS_LOCK_SHARED;
379                            break;
380                        case 3:
381                            lockIcon = OpenCmsTheme.LOCK_USER + " " + ICON_CLASS_LOCK_OWN;
382                            break;
383                        default:
384                            lockIcon = null;
385                    }
386                    if (lockIcon != null) {
387                        message = CmsVaadinUtils.getMessageText(
388                            Messages.GUI_EXPLORER_LIST_ACTION_LOCK_NAME_2,
389                            resUtil.getLockedByName(),
390                            resUtil.getLockedInProjectName());
391                    }
392                }
393                if (lockIcon != null) {
394                    content += getOverlaySpan(lockIcon, message);
395                }
396            }
397        }
398        if (state != null) {
399            String title = resUtil != null
400            ? CmsVaadinUtils.getMessageText(org.opencms.workplace.commons.Messages.GUI_LABEL_USER_LAST_MODIFIED_0)
401                + " "
402                + resUtil.getUserLastModified()
403            : null;
404            if (state.isChanged() || state.isDeleted()) {
405                content += getOverlaySpan(OpenCmsTheme.STATE_CHANGED + " " + ICON_CLASS_CHANGED, title);
406            } else if (state.isNew()) {
407                content += getOverlaySpan(OpenCmsTheme.STATE_NEW + " " + ICON_CLASS_CHANGED, title);
408            }
409        }
410        if ((resUtil != null) && (resUtil.getLinkType() == 1)) {
411            content += getOverlaySpan(OpenCmsTheme.SIBLING + " " + ICON_CLASS_SIBLING, null);
412        }
413        return content;
414    }
415
416    /**
417     * Generates an overlay icon span.<p>
418     *
419     * @param title the span title
420     * @param cssClass the CSS class
421     *
422     * @return the span element string
423     */
424    private static String getOverlaySpan(String cssClass, String title) {
425
426        StringBuffer result = new StringBuffer();
427        result.append("<span class=\"").append(cssClass).append("\"");
428        if (title != null) {
429            result.append(" title=\"").append(title).append("\"");
430        }
431        result.append("></span>");
432        return result.toString();
433    }
434
435    /**
436     * Returns the URI of the small resource type icon for the given type.<p>
437     *
438     * @param type the resource type name
439     * @param isPageOverlay <code>true</code> in case this is a page overlay and not a folder overlay
440     *
441     * @return the icon URI
442     */
443    private static String getSmallTypeIconHTML(String type, boolean isPageOverlay) {
444
445        CmsExplorerTypeSettings typeSettings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type);
446        if ((typeSettings == null) && LOG.isWarnEnabled()) {
447            LOG.warn("Could not read explorer type settings for " + type);
448        }
449        String result = null;
450        String overlayClass = isPageOverlay ? "o-page-icon-overlay" : "o-icon-overlay";
451        if (typeSettings != null) {
452            if (typeSettings.getSmallIconStyle() != null) {
453                result = "<span class=\"v-icon "
454                    + overlayClass
455                    + " "
456                    + typeSettings.getSmallIconStyle()
457                    + "\">&nbsp;</span>";
458            } else if (typeSettings.getIcon() != null) {
459                result = "<img src=\""
460                    + CmsWorkplace.getResourceUri(CmsWorkplace.RES_PATH_FILETYPES + typeSettings.getIcon())
461                    + "\" class=\""
462                    + overlayClass
463                    + "\" />";
464            } else {
465                result = "<span class=\"v-icon "
466                    + overlayClass
467                    + " "
468                    + CmsExplorerTypeSettings.ICON_STYLE_DEFAULT_SMALL
469                    + "\">&nbsp;</span>";
470            }
471        }
472        return result;
473    }
474
475    /**
476     * Initializes the content.<p>
477     *
478     * @param resUtil the resource util
479     * @param state the resource state
480     * @param showLocks <code>true</code> to show the resource locks
481     * @param showDetailIcon <code>true</code> to show the detail icon overlay
482     */
483    public void initContent(
484        CmsResourceUtil resUtil,
485        CmsResourceState state,
486        boolean showLocks,
487        boolean showDetailIcon) {
488
489        setValue(getIconInnerHTML(resUtil, state, showLocks, showDetailIcon));
490    }
491
492    /**
493     * Initializes the content.<p>
494     *
495     * @param resUtil the resource util
496     * @param iconResource the icon path
497     * @param state the resource state
498     * @param showLocks <code>true</code> to show the resource locks
499     * @param showDetailIcon <code>true</code> to show the detail icon overlay
500     */
501    public void initContent(
502        CmsResourceUtil resUtil,
503        Resource iconResource,
504        CmsResourceState state,
505        boolean showLocks,
506        boolean showDetailIcon) {
507
508        setValue(getIconInnerHTML(resUtil, iconResource, state, showLocks, showDetailIcon));
509    }
510}