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