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 GmbH & Co. KG, 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.workplace.explorer;
029
030import org.opencms.db.CmsDbEntryNotFoundException;
031import org.opencms.db.CmsResourceState;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsProject;
034import org.opencms.file.CmsPropertyDefinition;
035import org.opencms.file.CmsRequestContext;
036import org.opencms.file.CmsResource;
037import org.opencms.file.CmsResourceFilter;
038import org.opencms.file.history.I_CmsHistoryResource;
039import org.opencms.file.types.CmsResourceTypePlain;
040import org.opencms.file.types.CmsResourceTypeUnknownFile;
041import org.opencms.file.types.CmsResourceTypeUnknownFolder;
042import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
043import org.opencms.file.types.I_CmsResourceType;
044import org.opencms.i18n.CmsMessages;
045import org.opencms.jsp.CmsJspNavBuilder;
046import org.opencms.lock.CmsLock;
047import org.opencms.main.CmsException;
048import org.opencms.main.CmsLog;
049import org.opencms.main.OpenCms;
050import org.opencms.search.galleries.CmsGallerySearch;
051import org.opencms.search.galleries.CmsGallerySearchResult;
052import org.opencms.security.CmsOrganizationalUnit;
053import org.opencms.security.CmsPermissionSet;
054import org.opencms.security.CmsPermissionSetCustom;
055import org.opencms.security.CmsPrincipal;
056import org.opencms.ui.CmsCssIcon;
057import org.opencms.util.A_CmsModeIntEnumeration;
058import org.opencms.util.CmsStringUtil;
059import org.opencms.util.CmsUUID;
060import org.opencms.workplace.CmsWorkplace;
061
062import java.util.ArrayList;
063import java.util.List;
064import java.util.Locale;
065import java.util.Map;
066
067import org.apache.commons.logging.Log;
068
069import com.google.common.collect.Maps;
070import com.vaadin.server.ExternalResource;
071import com.vaadin.server.Resource;
072
073/**
074 * Provides {@link CmsResource} utility functions.<p>
075 *
076 * This class provides in java all resource information used by the explorer view,
077 * mostly generated in javascript (see explorer.js)<p>
078 *
079 * @since 6.0.0
080 */
081public final class CmsResourceUtil {
082
083    /**
084     * Enumeration class for defining the resource project state.<p>
085     */
086    public static class CmsResourceProjectState extends A_CmsModeIntEnumeration {
087
088        /** Constant for the project state unlocked. */
089        protected static final CmsResourceProjectState CLEAN = new CmsResourceProjectState(0);
090
091        /** Constant for the project state locked for publishing. */
092        protected static final CmsResourceProjectState LOCKED_FOR_PUBLISHING = new CmsResourceProjectState(5);
093
094        /** Constant for the project state locked in current project. */
095        protected static final CmsResourceProjectState MODIFIED_IN_CURRENT_PROJECT = new CmsResourceProjectState(1);
096
097        /** Constant for the project state locked in other project. */
098        protected static final CmsResourceProjectState MODIFIED_IN_OTHER_PROJECT = new CmsResourceProjectState(2);
099
100        /** serial version UID. */
101        private static final long serialVersionUID = 4580450220255428716L;
102
103        /**
104         * Default constructor.<p>
105         *
106         * @param mode the mode descriptor
107         */
108        protected CmsResourceProjectState(int mode) {
109
110            super(mode);
111        }
112
113        /**
114         * Checks if this is a {@link #LOCKED_FOR_PUBLISHING} state.<p>
115         *
116         * @return <code>true</code> if this is a {@link #LOCKED_FOR_PUBLISHING} state
117         */
118        public boolean isLockedForPublishing() {
119
120            return (this == LOCKED_FOR_PUBLISHING);
121        }
122
123        /**
124         * Checks if this is a {@link #MODIFIED_IN_CURRENT_PROJECT} state.<p>
125         *
126         * @return <code>true</code> if this is a {@link #MODIFIED_IN_CURRENT_PROJECT} state
127         */
128        public boolean isModifiedInCurrentProject() {
129
130            return (this == MODIFIED_IN_CURRENT_PROJECT);
131        }
132
133        /**
134         * Checks if this is a {@link #MODIFIED_IN_OTHER_PROJECT} state.<p>
135         *
136         * @return <code>true</code> if this is a {@link #MODIFIED_IN_OTHER_PROJECT} state
137         */
138        public boolean isModifiedInOtherProject() {
139
140            return (this == MODIFIED_IN_OTHER_PROJECT);
141        }
142
143        /**
144         * Checks if this is a {@link #CLEAN} state.<p>
145         *
146         * @return <code>true</code> if this is a {@link #CLEAN} state
147         */
148        public boolean isUnlocked() {
149
150            return (this == CLEAN);
151        }
152    }
153
154    /**
155     * Enumeration class for defining the site modes.<p>
156     */
157    private static class CmsResourceUtilSiteMode {
158
159        /**
160         * Default constructor.<p>
161         */
162        protected CmsResourceUtilSiteMode() {
163
164            // noop
165        }
166    }
167
168    /** Layout style for resources after expire date. */
169    public static final int LAYOUTSTYLE_AFTEREXPIRE = 2;
170
171    /** Layout style for resources before release date. */
172    public static final int LAYOUTSTYLE_BEFORERELEASE = 1;
173
174    /** Layout style for resources after release date and before expire date. */
175    public static final int LAYOUTSTYLE_INRANGE = 0;
176
177    /** Constant that signalizes that all path operations will be based on the current site. */
178    public static final CmsResourceUtilSiteMode SITE_MODE_CURRENT = new CmsResourceUtilSiteMode();
179
180    /** Constant that signalizes that all path operations will be based on the best matching site. */
181    public static final CmsResourceUtilSiteMode SITE_MODE_MATCHING = new CmsResourceUtilSiteMode();
182
183    /** Constant that signalizes that all path operations will be based on the root path. */
184    public static final CmsResourceUtilSiteMode SITE_MODE_ROOT = new CmsResourceUtilSiteMode();
185
186    /** Constant for the project state locked for publishing. */
187    public static final CmsResourceProjectState STATE_LOCKED_FOR_PUBLISHING = CmsResourceProjectState.LOCKED_FOR_PUBLISHING;
188
189    /** Constant for the project state locked in current project. */
190    public static final CmsResourceProjectState STATE_MODIFIED_IN_CURRENT_PROJECT = CmsResourceProjectState.MODIFIED_IN_CURRENT_PROJECT;
191
192    /** Constant for the project state locked in other project. */
193    public static final CmsResourceProjectState STATE_MODIFIED_IN_OTHER_PROJECT = CmsResourceProjectState.MODIFIED_IN_OTHER_PROJECT;
194
195    /** The log object for this class. */
196    private static final Log LOG = CmsLog.getLog(CmsResourceUtil.class);
197
198    /** The folder size display string constant. */
199    private static final String SIZE_DIR = "-";
200
201    /** Constant for the project state unlocked. */
202    private static final CmsResourceProjectState STATE_CLEAN = CmsResourceProjectState.CLEAN;
203
204    /** If greater than zero, the path will be formatted to this number of chars. */
205    private int m_abbrevLength;
206
207    /** The current cms context. */
208    private CmsObject m_cms;
209
210    /** The current resource lock. */
211    private CmsLock m_lock;
212
213    /** The message bundle for formatting dates, depends on the request locale. */
214    private CmsMessages m_messages;
215
216    /** Reference project resources cache. */
217    private List<String> m_projectResources;
218
219    /** The project to use to check project state, if <code>null</code> the current project will be used. */
220    private CmsProject m_referenceProject;
221
222    /** The 'relative to' path. */
223    private String m_relativeTo;
224
225    /** The current request context. */
226    private CmsRequestContext m_request;
227
228    /** The current resource. */
229    private CmsResource m_resource;
230
231    /** The current resource type. */
232    private I_CmsResourceType m_resourceType;
233
234    /** The search result map. */
235    private Map<Locale, CmsGallerySearchResult> m_searchResultMap = Maps.newHashMap();
236
237    /** The current site mode. */
238    private CmsResourceUtilSiteMode m_siteMode = SITE_MODE_CURRENT;
239
240    /**
241     * Creates a new {@link CmsResourceUtil} object.<p>
242     *
243     * @param cms the cms context
244     */
245    public CmsResourceUtil(CmsObject cms) {
246
247        setCms(cms);
248    }
249
250    /**
251     * Creates a new {@link CmsResourceUtil} object.<p>
252     *
253     * @param cms the cms context
254     * @param resource the resource
255     */
256    public CmsResourceUtil(CmsObject cms, CmsResource resource) {
257
258        setCms(cms);
259        setResource(resource);
260    }
261
262    /**
263     * Creates a new {@link CmsResourceUtil} object.<p>
264     *
265     * @param resource the resource
266     */
267    public CmsResourceUtil(CmsResource resource) {
268
269        setResource(resource);
270    }
271
272    /**
273     * Returns the big icon resource for the given resource.<p>
274     *
275     * @param explorerType the resource explorer type settings
276     * @param resourceName the resource name
277     *
278     * @return the icon resource
279     */
280    public static Resource getBigIconResource(CmsExplorerTypeSettings explorerType, String resourceName) {
281
282        if (explorerType == null) {
283            explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
284                (resourceName == null) && !CmsResource.isFolder(resourceName)
285                ? CmsResourceTypeUnknownFile.RESOURCE_TYPE_NAME
286                : CmsResourceTypeUnknownFolder.RESOURCE_TYPE_NAME);
287        }
288        if (!explorerType.getIconRules().isEmpty() && (resourceName != null)) {
289            String extension = CmsResource.getExtension(resourceName);
290            if (extension != null) {
291                CmsIconRule rule = explorerType.getIconRules().get(extension);
292                if ((rule != null) && (rule.getBigIconStyle() != null)) {
293                    return new CmsCssIcon(rule.getBigIconStyle());
294                }
295            }
296        }
297        if (explorerType.getBigIconStyle() != null) {
298            return new CmsCssIcon(explorerType.getBigIconStyle());
299        } else if (explorerType.getBigIcon() != null) {
300            return new ExternalResource(
301                CmsWorkplace.getResourceUri(CmsWorkplace.RES_PATH_FILETYPES + explorerType.getBigIcon()));
302        } else {
303            return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_DEFAULT_BIG);
304        }
305    }
306
307    /**
308     * Returns the small icon resource for the given resource.<p>
309     *
310     * @param explorerType the resource explorer type settings
311     * @param resourceName the resource name
312     *
313     * @return the icon resource
314     */
315    public static Resource getSmallIconResource(CmsExplorerTypeSettings explorerType, String resourceName) {
316
317        if (explorerType == null) {
318            explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
319                (resourceName == null) && !CmsResource.isFolder(resourceName)
320                ? CmsResourceTypeUnknownFile.RESOURCE_TYPE_NAME
321                : "unknown_folder");
322        }
323
324        if (!explorerType.getIconRules().isEmpty() && (resourceName != null)) {
325            String extension = CmsResource.getExtension(resourceName);
326            if (extension != null) {
327                CmsIconRule rule = explorerType.getIconRules().get(extension);
328                if ((rule != null) && (rule.getSmallIconStyle() != null)) {
329                    return new CmsCssIcon(rule.getSmallIconStyle());
330                }
331            }
332        }
333        if (explorerType.getSmallIconStyle() != null) {
334            return new CmsCssIcon(explorerType.getSmallIconStyle());
335        } else {
336            return new ExternalResource(
337                CmsWorkplace.getResourceUri(CmsWorkplace.RES_PATH_FILETYPES + explorerType.getOriginalIcon()));
338        }
339    }
340
341    /**
342     * Returns the path abbreviation length.<p>
343     *
344     * If greater than zero, the path will be formatted to this number of chars.<p>
345     *
346     * This only affects the generation of the path for the current resource.<p>
347     *
348     * @return the path abbreviation Length
349     */
350    public int getAbbrevLength() {
351
352        return m_abbrevLength;
353    }
354
355    /**
356     * Returns the big icon resource of the current resource.<p>
357     *
358     * @return the icon resource
359     */
360    public Resource getBigIconResource() {
361
362        if ((m_cms != null) && CmsJspNavBuilder.isNavLevelFolder(m_cms, m_resource)) {
363            return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_NAV_LEVEL_BIG);
364        }
365        if ((m_cms != null) && CmsResourceTypeXmlContainerPage.isModelCopyGroup(m_cms, m_resource)) {
366            return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_MODEL_GROUP_COPY_BIG);
367        }
368        I_CmsResourceType type = getResourceType();
369        CmsExplorerTypeSettings explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName());
370        return getBigIconResource(explorerType, m_resource != null ? m_resource.getName() : null);
371    }
372
373    /**
374     * Returns the cms context.<p>
375     *
376     * @return the cms context
377     */
378    public CmsObject getCms() {
379
380        return m_cms;
381    }
382
383    /**
384     * Returns the formatted date of expiration.<p>
385     *
386     * @return the formatted date of expiration
387     */
388    public String getDateExpired() {
389
390        long release = m_resource.getDateExpired();
391        if (release != CmsResource.DATE_EXPIRED_DEFAULT) {
392            return getMessages().getDateTime(release);
393        } else {
394            return CmsWorkplace.DEFAULT_DATE_STRING;
395        }
396    }
397
398    /**
399     * Returns the formatted date of release.<p>
400     *
401     * @return the formatted date of release
402     */
403    public String getDateReleased() {
404
405        long release = m_resource.getDateReleased();
406        if (release != CmsResource.DATE_RELEASED_DEFAULT) {
407            return getMessages().getDateTime(release);
408        } else {
409            return CmsWorkplace.DEFAULT_DATE_STRING;
410        }
411    }
412
413    /**
414     * Returns the path of the current resource, taking into account just the site mode.<p>
415     *
416     * @return the full path
417     */
418    public String getFullPath() {
419
420        String path = m_resource.getRootPath();
421        if ((m_siteMode != SITE_MODE_ROOT) && (m_cms != null)) {
422            String site = getSite();
423            if (path.startsWith(site)) {
424                path = path.substring(site.length());
425            }
426        }
427        return path;
428    }
429
430    /**
431     * Returns the gallery description of the current resource.<p>
432     *
433     * @param locale the content locale
434     *
435     * @return the gallery description
436     */
437    public String getGalleryDescription(Locale locale) {
438
439        return getSearchResult(locale).getDescription();
440    }
441
442    /**
443     * Returns the gallery title of the current resource.<p>
444     *
445     * @param locale the content locale
446     *
447     * @return the gallery title
448     */
449    public String getGalleryTitle(Locale locale) {
450
451        return getSearchResult(locale).getTitle();
452    }
453
454    /**
455     * Returns the resource icon path displayed in the explorer view for the given resource.<p>
456     *
457     * Relative to <code>/system/workplace/resources/</code>.<p>
458     *
459     * If the resource has no sibling it is the same as {@link #getIconPathResourceType()}.<p>
460     *
461     * @return the resource icon path displayed in the explorer view for the given resource
462     *
463     * @see #getStyleSiblings()
464     */
465    public String getIconPathExplorer() {
466
467        if (m_resource.getSiblingCount() > 1) {
468            // links are present
469            if (m_resource.isLabeled()) {
470                // there is at least one link in a marked site
471                return "explorer/link_labeled.gif";
472            } else {
473                // common links are present
474                return "explorer/link.gif";
475            }
476        } else {
477            return getIconPathResourceType();
478        }
479    }
480
481    /**
482     * Returns the lock icon path for the given resource.<p>
483     *
484     * Relative to <code>/system/workplace/resources/</code>.<p>
485     *
486     * Returns <code>explorer/project_none.gif</code> if request context is <code>null</code>.<p>
487     *
488     * @return the lock icon path for the given resource
489     */
490    public String getIconPathLock() {
491
492        CmsLock lock = getLock();
493        String iconPath = null;
494        if (!lock.isUnlocked() && (m_request != null) && isInsideProject()) {
495            if (getLock().isOwnedBy(m_request.getCurrentUser())
496                && (getLockedInProjectId().equals(getReferenceProject().getUuid()))) {
497                if (lock.isShared()) {
498                    iconPath = "shared";
499                } else {
500                    iconPath = "user";
501                }
502            } else {
503                iconPath = "other";
504            }
505        }
506        if (iconPath == null) {
507            iconPath = "project_none";
508        } else {
509            iconPath = "lock_" + iconPath;
510        }
511        return "explorer/" + iconPath + ".gif";
512    }
513
514    /**
515     * Returns the project state icon path for the given resource.<p>
516     *
517     * Relative to <code>/system/workplace/resources/</code>.<p>
518     *
519     * @return the project state icon path for the given resource
520     */
521    public String getIconPathProjectState() {
522
523        String iconPath;
524        if (getProjectState() == STATE_MODIFIED_IN_CURRENT_PROJECT) {
525            iconPath = "this.png";
526        } else if (getProjectState() == STATE_MODIFIED_IN_OTHER_PROJECT) {
527            iconPath = "other.png";
528        } else if (getProjectState() == STATE_LOCKED_FOR_PUBLISHING) {
529            iconPath = "publish.png";
530        } else {
531            // STATE_UNLOCKED
532            iconPath = "none.gif";
533        }
534        return "explorer/project_" + iconPath;
535    }
536
537    /**
538     * Returns the resource type icon path for the given resource.<p>
539     *
540     * Relative to <code>/system/workplace/resources/</code>.<p>
541     *
542     * @return the resource type icon path for the given resource
543     */
544    public String getIconPathResourceType() {
545
546        if (!isEditable()) {
547            return CmsWorkplace.RES_PATH_FILETYPES
548                + OpenCms.getWorkplaceManager().getExplorerTypeSetting(
549                    CmsResourceTypePlain.getStaticTypeName()).getIcon();
550        }
551        return CmsWorkplace.RES_PATH_FILETYPES
552            + OpenCms.getWorkplaceManager().getExplorerTypeSetting(getResourceTypeName()).getIcon();
553    }
554
555    /**
556     * Returns an integer representation for the link type.<p>
557     *
558     * <ul>
559     *   <li><code>0</code>: No sibling
560     *   <li><code>1</code>: Sibling
561     *   <li><code>2</code>: Labeled sibling
562     * </ul>
563     *
564     * @return an integer representation for the link type
565     */
566    public int getLinkType() {
567
568        if (m_resource.getSiblingCount() > 1) {
569            // links are present
570            if (m_resource.isLabeled()) {
571                // there is at least one link in a marked site
572                return 2;
573            } else {
574                // common links are present
575                return 1;
576            }
577        } else {
578            // no links to the resource are in the VFS
579            return 0;
580        }
581    }
582
583    /**
584     * Returns the the lock for the given resource.<p>
585     *
586     * @return the lock the given resource
587     */
588    public CmsLock getLock() {
589
590        if (m_lock == null) {
591            try {
592                m_lock = getCms().getLock(m_resource);
593            } catch (Throwable e) {
594                m_lock = CmsLock.getNullLock();
595                LOG.error(e.getLocalizedMessage(), e);
596            }
597        }
598        return m_lock;
599    }
600
601    /**
602     * Returns the user name who owns the lock for the given resource.<p>
603     *
604     * @return the user name who owns the lock for the given resource
605     */
606    public String getLockedByName() {
607
608        String lockedBy = "";
609        if (!getLock().isNullLock()) {
610            // user
611            lockedBy = getLock().getUserId().toString();
612            try {
613                lockedBy = getCurrentOuRelativeName(
614                    CmsPrincipal.readPrincipalIncludingHistory(getCms(), getLock().getUserId()).getName());
615            } catch (Throwable e) {
616                lockedBy = e.getMessage();
617            }
618        }
619        return lockedBy;
620    }
621
622    /**
623     * Returns the id of the project in which the given resource is locked.<p>
624     *
625     * @return the id of the project in which the given resource is locked
626     */
627    public CmsUUID getLockedInProjectId() {
628
629        CmsUUID lockedInProject = null;
630        if (getLock().isNullLock() && !getResource().getState().isUnchanged()) {
631            // resource is unlocked and modified
632            lockedInProject = getResource().getProjectLastModified();
633        } else if (!getResource().getState().isUnchanged()) {
634            // resource is locked and modified
635            lockedInProject = getProjectId();
636        } else if (!getLock().isNullLock()) {
637            // resource is locked and unchanged
638            lockedInProject = getLock().getProjectId();
639        }
640        return lockedInProject;
641    }
642
643    /**
644     * Returns the project name that locked the current resource's.<p>
645     *
646     * @return the the project name that locked the current resource's
647     */
648    public String getLockedInProjectName() {
649
650        try {
651            CmsUUID pId = getLockedInProjectId();
652            if ((pId == null) || pId.isNullUUID()) {
653                // the resource is unlocked and unchanged
654                return "";
655            }
656            try {
657                return getCurrentOuRelativeName(getCms().readProject(pId).getName());
658            } catch (CmsDbEntryNotFoundException e) {
659                return getCurrentOuRelativeName(getCms().readHistoryProject(pId).getName());
660            }
661        } catch (Throwable e) {
662            LOG.error(e.getLocalizedMessage(), e);
663            return "";
664        }
665    }
666
667    /**
668     * Returns the lock state of the current resource.<p>
669     *
670     * @return the lock state of the current resource
671     */
672    public int getLockState() {
673
674        if (CmsStringUtil.isEmptyOrWhitespaceOnly(getLockedByName())) {
675            // unlocked
676            return 0;
677        }
678        if (!getLockedByName().equals(getCurrentOuRelativeName(m_request.getCurrentUser().getName()))
679            || !getLockedInProjectId().equals(m_request.getCurrentProject().getUuid())) {
680            // locked by other user and/or project
681            return 1;
682        }
683        if (getLock().getType().isShared()) {
684            // shared lock
685            return 2;
686        }
687        // exclusive lock
688        return 3;
689    }
690
691    /**
692     * Returns the navtext of a resource.<p>
693     *
694     * @return the navtext for that resource
695     */
696    public String getNavText() {
697
698        String navText = "";
699        try {
700            navText = getCms().readPropertyObject(
701                getCms().getSitePath(m_resource),
702                CmsPropertyDefinition.PROPERTY_NAVTEXT,
703                false).getValue();
704        } catch (Throwable e) {
705            String storedSiteRoot = getCms().getRequestContext().getSiteRoot();
706            try {
707                getCms().getRequestContext().setSiteRoot("");
708                navText = getCms().readPropertyObject(
709                    m_resource.getRootPath(),
710                    CmsPropertyDefinition.PROPERTY_NAVTEXT,
711                    false).getValue();
712            } catch (Exception e1) {
713                // should usually never happen
714                if (LOG.isInfoEnabled()) {
715                    LOG.info(e);
716                }
717            } finally {
718                getCms().getRequestContext().setSiteRoot(storedSiteRoot);
719            }
720        }
721        if (navText == null) {
722            navText = "";
723        }
724        return navText;
725    }
726
727    /**
728     * Checks is the current resource can be edited by the current user.<p>
729     *
730     * @param locale the locale to use for the messages
731     *
732     * @return an empty string if editable, or a localized string with the reason
733     *
734     * @throws CmsException if something goes wrong
735     */
736    public String getNoEditReason(Locale locale) throws CmsException {
737
738        return getNoEditReason(locale, false);
739    }
740
741    /**
742     * Checks is the current resource can be edited by the current user.<p>
743     *
744     * @param locale the locale to use for the messages
745     * @param ignoreExpiration <code>true</code> to ignore resource release and expiration date
746     *
747     * @return an empty string if editable, or a localized string with the reason
748     *
749     * @throws CmsException if something goes wrong
750     */
751    public String getNoEditReason(Locale locale, boolean ignoreExpiration) throws CmsException {
752
753        String reason = "";
754        if (m_resource instanceof I_CmsHistoryResource) {
755            reason = Messages.get().getBundle(locale).key(Messages.GUI_NO_EDIT_REASON_HISTORY_0);
756        } else if (!m_cms.hasPermissions(
757            m_resource,
758            CmsPermissionSet.ACCESS_WRITE,
759            false,
760            ignoreExpiration ? CmsResourceFilter.IGNORE_EXPIRATION : CmsResourceFilter.DEFAULT) || !isEditable()) {
761            reason = Messages.get().getBundle(locale).key(Messages.GUI_NO_EDIT_REASON_PERMISSION_0);
762        } else if (!getLock().isLockableBy(m_cms.getRequestContext().getCurrentUser())) {
763            if (getLock().getSystemLock().isPublish()) {
764                reason = Messages.get().getBundle(locale).key(Messages.GUI_PUBLISH_TOOLTIP_0);
765            } else {
766                reason = Messages.get().getBundle(locale).key(Messages.GUI_NO_EDIT_REASON_LOCK_1, getLockedByName());
767            }
768        }
769        return reason;
770    }
771
772    /**
773     * Returns the path of the current resource.<p>
774     *
775     * Taking into account following settings:<br>
776     * <ul>
777     *    <li>site mode
778     *    <li>abbreviation length
779     *    <li>relative to
780     * </ul>
781     *
782     * @return the path
783     */
784    public String getPath() {
785
786        String path = getFullPath();
787        if (m_relativeTo != null) {
788            path = getResource().getRootPath();
789            if (path.startsWith(m_relativeTo)) {
790                path = path.substring(m_relativeTo.length());
791                if (path.length() == 0) {
792                    path = ".";
793                }
794            } else {
795                String site = getSite();
796                if (path.startsWith(site + "/") || path.equals(site)) {
797                    path = path.substring(site.length());
798                }
799            }
800        }
801        if (m_abbrevLength > 0) {
802            boolean absolute = path.startsWith("/");
803            path = CmsStringUtil.formatResourceName(path, m_abbrevLength);
804            if (!absolute && path.startsWith("/")) {
805                // remove leading '/'
806                path = path.substring(1);
807            }
808        }
809        return path;
810    }
811
812    /**
813     * Returns the permission set for the given resource.<p>
814     *
815     * @return the permission set for the given resource
816     */
817    public CmsPermissionSet getPermissionSet() {
818
819        CmsPermissionSetCustom pset = new CmsPermissionSetCustom();
820        CmsResource resource = getResource();
821        try {
822            if (getCms().hasPermissions(resource, CmsPermissionSet.ACCESS_CONTROL, false, CmsResourceFilter.ALL)) {
823                pset.grantPermissions(CmsPermissionSet.PERMISSION_CONTROL);
824            } else {
825                pset.denyPermissions(CmsPermissionSet.PERMISSION_CONTROL);
826            }
827        } catch (CmsException e) {
828            LOG.error(e.getLocalizedMessage());
829        }
830        try {
831            if (getCms().hasPermissions(
832                resource,
833                CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
834                false,
835                CmsResourceFilter.ALL)) {
836                pset.grantPermissions(CmsPermissionSet.PERMISSION_DIRECT_PUBLISH);
837            } else {
838                pset.denyPermissions(CmsPermissionSet.PERMISSION_DIRECT_PUBLISH);
839            }
840        } catch (CmsException e) {
841            LOG.error(e.getLocalizedMessage());
842        }
843        try {
844            if (getCms().hasPermissions(resource, CmsPermissionSet.ACCESS_READ, false, CmsResourceFilter.ALL)) {
845                pset.grantPermissions(CmsPermissionSet.PERMISSION_READ);
846            } else {
847                pset.denyPermissions(CmsPermissionSet.PERMISSION_READ);
848            }
849        } catch (CmsException e) {
850            LOG.error(e.getLocalizedMessage());
851        }
852        try {
853            if (getCms().hasPermissions(resource, CmsPermissionSet.ACCESS_VIEW, false, CmsResourceFilter.ALL)) {
854                pset.grantPermissions(CmsPermissionSet.PERMISSION_VIEW);
855            } else {
856                pset.denyPermissions(CmsPermissionSet.PERMISSION_VIEW);
857            }
858        } catch (CmsException e) {
859            LOG.error(e.getLocalizedMessage());
860        }
861        try {
862            if (getCms().hasPermissions(resource, CmsPermissionSet.ACCESS_WRITE, false, CmsResourceFilter.ALL)) {
863                pset.grantPermissions(CmsPermissionSet.PERMISSION_WRITE);
864            } else {
865                pset.denyPermissions(CmsPermissionSet.PERMISSION_WRITE);
866            }
867        } catch (CmsException e) {
868            LOG.error(e.getLocalizedMessage());
869        }
870
871        return pset;
872    }
873
874    /**
875     * Returns the permissions string for the given resource.<p>
876     *
877     * @return the permissions string for the given resource
878     */
879    public String getPermissionString() {
880
881        return getPermissionSet().getPermissionString();
882    }
883
884    /**
885     * Returns the id of the project which the resource belongs to.<p>
886     *
887     * @return the id of the project which the resource belongs to
888     */
889    public CmsUUID getProjectId() {
890
891        CmsUUID projectId = m_resource.getProjectLastModified();
892        if (!getLock().isUnlocked() && !getLock().isInherited()) {
893            // use lock project ID only if lock is not inherited
894            projectId = getLock().getProjectId();
895        }
896        return projectId;
897    }
898
899    /**
900     * Returns the project state of the given resource.<p>
901     *
902     * <ul>
903     *   <li>null: unchanged.</li>
904     *   <li>true: locked in current project.</li>
905     *   <li>false: not locked in current project.</li>
906     * </ul>
907     *
908     * @return the project state of the given resource
909     */
910    public CmsResourceProjectState getProjectState() {
911
912        if (getResource().getState().isUnchanged()) {
913            return STATE_CLEAN; // STATE_CLEAN
914        } else if (getLock().getSystemLock().isPublish()) {
915            return STATE_LOCKED_FOR_PUBLISHING;
916        } else if (getResource().getProjectLastModified().equals(getReferenceProject().getUuid())) {
917            return STATE_MODIFIED_IN_CURRENT_PROJECT; // STATE_MODIFIED_CURRENT_PROJECT
918        } else {
919            return STATE_MODIFIED_IN_OTHER_PROJECT; // STATE_MODIFIED_OTHER_PROJECT
920        }
921    }
922
923    /**
924     * Returns the project to use to check project state.<p>
925     *
926     * @return the project to use to check project state
927     */
928    public CmsProject getReferenceProject() {
929
930        if (m_referenceProject == null) {
931            if (m_request != null) {
932                m_referenceProject = m_request.getCurrentProject();
933            }
934        }
935        return m_referenceProject;
936    }
937
938    /**
939     * Returns the 'relative to' path.<p>
940     *
941     * This only affects the generation of the path for the current resource.<p>
942     *
943     * @return the 'relative to' path
944     */
945    public String getRelativeTo() {
946
947        return m_relativeTo;
948    }
949
950    /**
951     * Returns the resource.<p>
952     *
953     * @return the resource
954     */
955    public CmsResource getResource() {
956
957        return m_resource;
958    }
959
960    /**
961     * Returns the resource type for the given resource.<p>
962     *
963     * @return the resource type for the given resource
964     */
965    public I_CmsResourceType getResourceType() {
966
967        if (m_resourceType == null) {
968            m_resourceType = OpenCms.getResourceManager().getResourceType(m_resource);
969        }
970        return m_resourceType;
971    }
972
973    /**
974     * Returns the resource type id for the given resource.<p>
975     *
976     * @return the resource type id for the given resource
977     */
978    @SuppressWarnings("deprecation")
979    public int getResourceTypeId() {
980
981        return getResourceType().getTypeId();
982    }
983
984    /**
985     * Returns the resource type name for the given resource.<p>
986     *
987     * @return the resource type name for the given resource
988     */
989    public String getResourceTypeName() {
990
991        return getResourceType().getTypeName();
992    }
993
994    /**
995     * Returns the SOLR search result for the current resource.<p>
996     *
997     * @param locale the content locale
998     *
999     * @return the search result
1000     */
1001    public CmsGallerySearchResult getSearchResult(Locale locale) {
1002
1003        if (!m_searchResultMap.containsKey(locale)) {
1004            CmsGallerySearchResult sResult;
1005            try {
1006                sResult = CmsGallerySearch.searchById(m_cms, m_resource.getStructureId(), locale);
1007                m_searchResultMap.put(locale, sResult);
1008            } catch (CmsException e) {
1009                LOG.error(e.getLocalizedMessage(), e);
1010            }
1011        }
1012        return m_searchResultMap.get(locale);
1013    }
1014
1015    /**
1016     * Returns the site of the current resources,
1017     * taking into account the set site mode.<p>
1018     *
1019     * @return the site path
1020     */
1021    public String getSite() {
1022
1023        String site = null;
1024        if ((m_siteMode == SITE_MODE_MATCHING) || (m_cms == null)) {
1025            site = OpenCms.getSiteManager().getSiteRoot(m_resource.getRootPath());
1026        } else if (m_siteMode == SITE_MODE_CURRENT) {
1027            site = m_cms.getRequestContext().getSiteRoot();
1028        } else if (m_siteMode == SITE_MODE_ROOT) {
1029            site = "";
1030        }
1031        return (site == null ? "" : site);
1032    }
1033
1034    /**
1035     * Returns the site mode.<p>
1036     *
1037     * This only affects the generation of the path for the current resource.<p>
1038     *
1039     * @return the site mode
1040     */
1041    public CmsResourceUtilSiteMode getSiteMode() {
1042
1043        return m_siteMode;
1044    }
1045
1046    /**
1047     * Returns the title of the site.<p>
1048     *
1049     * @return the title of the site
1050     */
1051    public String getSiteTitle() {
1052
1053        String title = getSite();
1054        String rootSite = getCms().getRequestContext().getSiteRoot();
1055        try {
1056            getCms().getRequestContext().setSiteRoot("");
1057            title = getCms().readPropertyObject(title, CmsPropertyDefinition.PROPERTY_TITLE, false).getValue(title);
1058        } catch (CmsException e) {
1059            // ignore
1060        } finally {
1061            getCms().getRequestContext().setSiteRoot(rootSite);
1062        }
1063        return title;
1064    }
1065
1066    /**
1067     * Returns the size of the given resource as a String.<p>
1068     *
1069     * For directories it returns <code>SIZE_DIR</code>.<p>
1070     *
1071     * @return the size of the given resource as a String
1072     */
1073    public String getSizeString() {
1074
1075        return m_resource.getLength() == -1 ? SIZE_DIR : "" + m_resource.getLength();
1076    }
1077
1078    /**
1079     * Returns the small icon resource of the current resource.<p>
1080     *
1081     * @return the icon resource
1082     */
1083    public Resource getSmallIconResource() {
1084
1085        if ((m_cms != null) && CmsJspNavBuilder.isNavLevelFolder(m_cms, m_resource)) {
1086            return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_NAV_LEVEL_BIG);
1087        }
1088        if ((m_cms != null) && CmsResourceTypeXmlContainerPage.isModelCopyGroup(m_cms, m_resource)) {
1089            return new CmsCssIcon(CmsExplorerTypeSettings.ICON_STYLE_MODEL_GROUP_COPY_BIG);
1090        }
1091        I_CmsResourceType type = getResourceType();
1092        CmsExplorerTypeSettings explorerType = OpenCms.getWorkplaceManager().getExplorerTypeSetting(type.getTypeName());
1093        return getSmallIconResource(explorerType, m_resource != null ? m_resource.getName() : null);
1094    }
1095
1096    /**
1097     * Returns resource state abbreviation.<p>
1098     *
1099     * @return resource state abbreviation
1100     */
1101    public char getStateAbbreviation() {
1102
1103        return getResource().getState().getAbbreviation();
1104    }
1105
1106    /**
1107     * Returns the state name for a resource.<p>
1108     *
1109     * Uses default locale if request context is <code>null</code>.<p>
1110     *
1111     * @return the state name for that resource
1112     */
1113    public String getStateName() {
1114
1115        CmsResourceState state = m_resource.getState();
1116        String name;
1117        if (m_request == null) {
1118            name = org.opencms.workplace.explorer.Messages.get().getBundle().key(
1119                org.opencms.workplace.explorer.Messages.getStateKey(state));
1120        } else {
1121            name = org.opencms.workplace.explorer.Messages.get().getBundle(m_request.getLocale()).key(
1122                org.opencms.workplace.explorer.Messages.getStateKey(state));
1123        }
1124        return name;
1125    }
1126
1127    /**
1128     * Returns the style class to use for the given resource.<p>
1129     *
1130     * @return style class name
1131     *
1132     * @see org.opencms.workplace.list.CmsListExplorerColumn#getExplorerStyleDef()
1133     */
1134    public String getStyleClassName() {
1135
1136        if (isInsideProject() && isEditable()) {
1137            if (m_resource.getState().isChanged()) {
1138                return "fc";
1139            } else if (m_resource.getState().isNew()) {
1140                return "fn";
1141            } else if (m_resource.getState().isDeleted()) {
1142                return "fd";
1143            } else {
1144                return "nf";
1145            }
1146        }
1147        return "fp";
1148    }
1149
1150    /**
1151     * Returns additional style sheets for the resource type icon depending on siblings.<p>
1152     *
1153     * That is, depending on {@link CmsResource#getSiblingCount()}
1154     *
1155     * Use it with the {@link #getIconPathExplorer} method.<p>
1156     *
1157     * @return additional style sheets depending on siblings
1158     */
1159    public String getStyleSiblings() {
1160
1161        StringBuffer style = new StringBuffer(128);
1162        if (m_resource.getSiblingCount() > 1) {
1163            style.append("background-image:url(");
1164            style.append(CmsWorkplace.getSkinUri());
1165            style.append(getIconPathResourceType());
1166            style.append("); background-position: 0px 0px; background-repeat: no-repeat; ");
1167        }
1168        return style.toString();
1169    }
1170
1171    /**
1172     * Returns the system lock information tooltip for the explorer view.<p>
1173     *
1174     * @param forExplorer if the tool tip should be generated for the explorer view
1175     *
1176     * @return the system lock information tooltip
1177     */
1178    public String getSystemLockInfo(boolean forExplorer) {
1179
1180        if (getLock().getSystemLock().isPublish()) {
1181            if (!forExplorer) {
1182                return getMessages().key(Messages.GUI_PUBLISH_TOOLTIP_0);
1183            } else {
1184                // see explorer.js(sysLockInfo) and top_js.jsp(publishlock)
1185                return "p"; // should have length == 1
1186            }
1187        }
1188        return "";
1189    }
1190
1191    /**
1192     * Returns additional style sheets depending on publication constraints.<p>
1193     *
1194     * That is, depending on {@link CmsResource#getDateReleased()} and
1195     * {@link CmsResource#getDateExpired()}.<p>
1196     *
1197     * @return additional style sheets depending on publication constraints
1198     *
1199     * @see #getTimeWindowLayoutType()
1200     */
1201    public String getTimeWindowLayoutStyle() {
1202
1203        return getTimeWindowLayoutType() == CmsResourceUtil.LAYOUTSTYLE_INRANGE ? "" : "font-style:italic;";
1204    }
1205
1206    /**
1207     * Returns the layout style for the current time window state.<p>
1208     *
1209     * <ul>
1210     *   <li><code>{@link CmsResourceUtil#LAYOUTSTYLE_INRANGE}</code>: The time window is in range
1211     *   <li><code>{@link CmsResourceUtil#LAYOUTSTYLE_BEFORERELEASE}</code>: The resource is not yet released
1212     *   <li><code>{@link CmsResourceUtil#LAYOUTSTYLE_AFTEREXPIRE}</code>: The resource has already expired
1213     * </ul>
1214     *
1215     * @return the layout style for the current time window state
1216     *
1217     * @see #getTimeWindowLayoutStyle()
1218     */
1219    public int getTimeWindowLayoutType() {
1220
1221        int layoutstyle = CmsResourceUtil.LAYOUTSTYLE_INRANGE;
1222        if (!m_resource.isReleased(getCms().getRequestContext().getRequestTime())) {
1223            layoutstyle = CmsResourceUtil.LAYOUTSTYLE_BEFORERELEASE;
1224        } else if (m_resource.isExpired(getCms().getRequestContext().getRequestTime())) {
1225            layoutstyle = CmsResourceUtil.LAYOUTSTYLE_AFTEREXPIRE;
1226        }
1227        return layoutstyle;
1228    }
1229
1230    /**
1231     * Returns the title of a resource.<p>
1232     *
1233     * @return the title for that resource
1234     */
1235    public String getTitle() {
1236
1237        String title = "";
1238        try {
1239            title = getCms().readPropertyObject(
1240                getCms().getSitePath(m_resource),
1241                CmsPropertyDefinition.PROPERTY_TITLE,
1242                false).getValue();
1243        } catch (Throwable e) {
1244            String storedSiteRoot = getCms().getRequestContext().getSiteRoot();
1245            try {
1246                getCms().getRequestContext().setSiteRoot("");
1247                title = getCms().readPropertyObject(
1248                    m_resource.getRootPath(),
1249                    CmsPropertyDefinition.PROPERTY_TITLE,
1250                    false).getValue();
1251            } catch (Exception e1) {
1252                // should usually never happen
1253                if (LOG.isInfoEnabled()) {
1254                    LOG.info(e);
1255                }
1256            } finally {
1257                getCms().getRequestContext().setSiteRoot(storedSiteRoot);
1258            }
1259        }
1260        if (title == null) {
1261            title = "";
1262        }
1263        return title;
1264    }
1265
1266    /**
1267     * Returns the name of the user who created the given resource.<p>
1268     *
1269     * @return the name of the user who created the given resource
1270     */
1271    public String getUserCreated() {
1272
1273        String user = m_resource.getUserCreated().toString();
1274        try {
1275            user = getCurrentOuRelativeName(
1276                CmsPrincipal.readPrincipalIncludingHistory(getCms(), m_resource.getUserCreated()).getName());
1277        } catch (Throwable e) {
1278            LOG.info(e.getLocalizedMessage());
1279        }
1280        return user;
1281    }
1282
1283    /**
1284     * Returns the name of the user who last modified the given resource.<p>
1285     *
1286     * @return the name of the user who last modified the given resource
1287     */
1288    public String getUserLastModified() {
1289
1290        String user = m_resource.getUserLastModified().toString();
1291        try {
1292            user = getCurrentOuRelativeName(
1293                CmsPrincipal.readPrincipalIncludingHistory(getCms(), m_resource.getUserLastModified()).getName());
1294        } catch (Throwable e) {
1295            LOG.info(e.getLocalizedMessage());
1296        }
1297        return user;
1298    }
1299
1300    /**
1301     * Returns <code>true</code> if the given resource is editable by the current user.<p>
1302     *
1303     * Returns <code>false</code> if no request context is set.<p>
1304     *
1305     * @return <code>true</code> if the given resource is editable by the current user
1306     */
1307    public boolean isEditable() {
1308
1309        if (m_request == null) {
1310            return false;
1311        }
1312        CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(getResourceTypeName());
1313        if (settings != null) {
1314            String rightSite = OpenCms.getSiteManager().getSiteRoot(getResource().getRootPath());
1315            if (rightSite == null) {
1316                rightSite = "";
1317            }
1318            String currentSite = getCms().getRequestContext().getSiteRoot();
1319            try {
1320                getCms().getRequestContext().setSiteRoot(rightSite);
1321                return settings.isEditable(getCms(), getResource());
1322            } finally {
1323                getCms().getRequestContext().setSiteRoot(currentSite);
1324            }
1325        }
1326        return false;
1327    }
1328
1329    /**
1330     * Returns <code>true</code> if the given resource is in the reference project.<p>
1331     *
1332     * Returns <code>false</code> if the request context is <code>null</code>.<p>
1333     *
1334     * @return <code>true</code> if the given resource is in the reference project
1335     *
1336     * @see #getReferenceProject()
1337     */
1338    public boolean isInsideProject() {
1339
1340        return CmsProject.isInsideProject(getProjectResources(), getResource());
1341    }
1342
1343    /**
1344     * Returns <code>true</code> if the stored resource has been released and has not expired.<p>
1345     *
1346     * If no request context is available, the current time is used for the validation check.<p>
1347     *
1348     * @return <code>true</code> if the stored resource has been released and has not expired
1349     *
1350     * @see CmsResource#isReleasedAndNotExpired(long)
1351     */
1352    public boolean isReleasedAndNotExpired() {
1353
1354        long requestTime;
1355        if (m_request == null) {
1356            requestTime = System.currentTimeMillis();
1357        } else {
1358            requestTime = m_request.getRequestTime();
1359        }
1360        return m_resource.isReleasedAndNotExpired(requestTime);
1361    }
1362
1363    /**
1364     * Sets the path abbreviation length.<p>
1365     *
1366     * If greater than zero, the path will be formatted to this number of chars.<p>
1367     *
1368     * This only affects the generation of the path for the current resource.<p>
1369     *
1370     * @param abbrevLength the path abbreviation length to set
1371     */
1372    public void setAbbrevLength(int abbrevLength) {
1373
1374        m_abbrevLength = abbrevLength;
1375    }
1376
1377    /**
1378     * Sets the cms context.<p>
1379     *
1380     * @param cms the cms context to set
1381     */
1382    public void setCms(CmsObject cms) {
1383
1384        m_cms = cms;
1385        m_request = cms.getRequestContext();
1386        m_referenceProject = null;
1387        m_projectResources = null;
1388        m_messages = null;
1389    }
1390
1391    /**
1392     * Sets the project to use to check project state.<p>
1393     *
1394     * @param project the project to set
1395     */
1396    public void setReferenceProject(CmsProject project) {
1397
1398        m_referenceProject = project;
1399        m_projectResources = null;
1400    }
1401
1402    /**
1403     * Sets the 'relative to' path.<p>
1404     *
1405     * This only affects the generation of the path for the current resource.<p>
1406     *
1407     * @param relativeTo the 'relative to' path to set
1408     */
1409    public void setRelativeTo(String relativeTo) {
1410
1411        m_relativeTo = relativeTo;
1412        if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_relativeTo)) {
1413            m_relativeTo = null;
1414        } else {
1415            if (!m_relativeTo.startsWith("/")) {
1416                m_relativeTo = "/" + m_relativeTo;
1417            }
1418            if (!m_relativeTo.endsWith("/")) {
1419                m_relativeTo += "/";
1420            }
1421        }
1422    }
1423
1424    /**
1425     * Sets the resource.<p>
1426     *
1427     * @param resource the resource to set
1428     */
1429    public void setResource(CmsResource resource) {
1430
1431        m_resource = resource;
1432        m_lock = null;
1433        m_resourceType = null;
1434    }
1435
1436    /**
1437     * Sets the site mode.<p>
1438     *
1439     * This only affects the generation of the path for the current resource.<p>
1440     *
1441     * @param siteMode the site mode to set
1442     */
1443    public void setSiteMode(CmsResourceUtilSiteMode siteMode) {
1444
1445        m_siteMode = siteMode;
1446    }
1447
1448    /**
1449     * Returns the simple name if the ou is the same as the current user's ou.<p>
1450     *
1451     * @param name the fully qualified name to check
1452     *
1453     * @return the simple name if the ou is the same as the current user's ou
1454     */
1455    private String getCurrentOuRelativeName(String name) {
1456
1457        if (m_request == null) {
1458            return CmsOrganizationalUnit.SEPARATOR + name;
1459        }
1460        String ou = CmsOrganizationalUnit.getParentFqn(name);
1461        if (ou.equals(m_request.getCurrentUser().getOuFqn())) {
1462            return CmsOrganizationalUnit.getSimpleName(name);
1463        }
1464        return CmsOrganizationalUnit.SEPARATOR + name;
1465    }
1466
1467    /**
1468     * Returns the message bundle for formatting dates, depends on the request locale.<p>
1469     *
1470     * @return the message bundle for formatting dates
1471     */
1472    private CmsMessages getMessages() {
1473
1474        if (m_messages == null) {
1475            if (m_cms != null) {
1476                m_messages = Messages.get().getBundle(OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms));
1477            } else {
1478                m_messages = Messages.get().getBundle();
1479            }
1480        }
1481        return m_messages;
1482    }
1483
1484    /**
1485     * Returns the reference project resources.<p>
1486     *
1487     * @return the reference project resources
1488     */
1489    private List<String> getProjectResources() {
1490
1491        if (m_projectResources == null) {
1492            try {
1493                m_projectResources = getCms().readProjectResources(getReferenceProject());
1494            } catch (Throwable e) {
1495                LOG.error(e.getLocalizedMessage(), e);
1496                // use an empty list (all resources are "outside")
1497                m_projectResources = new ArrayList<String>();
1498            }
1499        }
1500        return m_projectResources;
1501    }
1502}