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