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.contextmenu;
029
030import static org.opencms.ui.contextmenu.CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE;
031import static org.opencms.ui.contextmenu.CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
032import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.controlpermission;
033import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.defaultfile;
034import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.deleted;
035import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.file;
036import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.folder;
037import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.haseditor;
038import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.hassourcecodeeditor;
039import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.inproject;
040import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.mylock;
041import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.noinheritedlock;
042import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.nootherlock;
043import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.notdeleted;
044import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.notinproject;
045import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.notnew;
046import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.notonline;
047import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.notpointer;
048import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.notunchangedfile;
049import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.otherlock;
050import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.pagefolder;
051import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.pointer;
052import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.publishpermission;
053import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.replacable;
054import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.roleeditor;
055import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.rolewpuser;
056import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.unlocked;
057import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.writepermisssion;
058import static org.opencms.ui.contextmenu.CmsVisibilityCheckFlag.xmlunmarshal;
059
060import org.opencms.file.CmsObject;
061import org.opencms.file.CmsResource;
062import org.opencms.file.CmsResourceFilter;
063import org.opencms.file.types.CmsResourceTypeImage;
064import org.opencms.file.types.CmsResourceTypePointer;
065import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
066import org.opencms.file.types.CmsResourceTypeXmlContent;
067import org.opencms.file.types.CmsResourceTypeXmlPage;
068import org.opencms.file.types.I_CmsResourceType;
069import org.opencms.loader.CmsDumpLoader;
070import org.opencms.lock.CmsLock;
071import org.opencms.main.CmsException;
072import org.opencms.main.CmsLog;
073import org.opencms.main.OpenCms;
074import org.opencms.security.CmsPermissionSet;
075import org.opencms.security.CmsRole;
076import org.opencms.ui.I_CmsDialogContext;
077import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.BundleType;
078import org.opencms.workplace.explorer.CmsResourceUtil;
079import org.opencms.xml.content.CmsXmlContentFactory;
080
081import java.util.Set;
082
083import org.apache.commons.logging.Log;
084
085import com.google.common.collect.Sets;
086
087/**
088 * Standard visibility check implementation.<p>
089 *
090 * Instances of this class are configured with a set of flags, each of which corresponds to a check to perform which
091 * may cause the context menu item to be hidden or deactivated.<p>
092 */
093public final class CmsStandardVisibilityCheck extends A_CmsSimpleVisibilityCheck {
094
095    /** Default visibility check for 'edit-like' operations on folders. */
096    public static final CmsStandardVisibilityCheck COPY_PAGE = new CmsStandardVisibilityCheck(
097        roleeditor,
098        notonline,
099        notdeleted,
100        pagefolder);
101
102    /** Default visibility check for 'edit-like' operations on resources. */
103    public static final CmsStandardVisibilityCheck DEFAULT = new CmsStandardVisibilityCheck(
104        roleeditor,
105        notonline,
106        notdeleted,
107        writepermisssion,
108        inproject);
109
110    /**
111     * Check for operations which need a default file.<p>
112     */
113    public static final I_CmsHasMenuItemVisibility DEFAULT_DEFAULTFILE = new CmsStandardVisibilityCheck(
114        roleeditor,
115        notonline,
116        notdeleted,
117        writepermisssion,
118        inproject,
119        defaultfile);
120
121    /** Default visibility check for 'edit-like' operations on folders. */
122    public static final CmsStandardVisibilityCheck DEFAULT_FOLDERS = new CmsStandardVisibilityCheck(
123        folder,
124        roleeditor,
125        notonline,
126        notdeleted,
127        writepermisssion,
128        inproject);
129
130    /** Like DEFAULT, but only active for files. */
131    public static final CmsStandardVisibilityCheck EDIT = new CmsStandardVisibilityCheck(
132        file,
133        notpointer,
134        roleeditor,
135        notonline,
136        notdeleted,
137        writepermisssion,
138        inproject,
139        xmlunmarshal,
140        haseditor);
141
142    /** Like DEFAULT, but only active for files. */
143    public static final CmsStandardVisibilityCheck EDIT_CODE = new CmsStandardVisibilityCheck(
144        file,
145        hassourcecodeeditor,
146        roleeditor,
147        notonline,
148        notdeleted,
149        writepermisssion,
150        inproject,
151        haseditor);
152
153    /** Visibility check for editing external links (pointers). */
154    public static final I_CmsHasMenuItemVisibility EDIT_POINTER = new CmsStandardVisibilityCheck(
155        file,
156        roleeditor,
157        notonline,
158        notdeleted,
159        writepermisssion,
160        inproject,
161        pointer);
162
163    /** Check for locking resources. */
164    public static final CmsStandardVisibilityCheck LOCK = new CmsStandardVisibilityCheck(
165        unlocked,
166        roleeditor,
167        notonline,
168        notdeleted,
169        inproject);
170
171    /** Visibility check used for copy to project dialog. */
172    public static final CmsStandardVisibilityCheck OTHER_PROJECT = new CmsStandardVisibilityCheck(
173        roleeditor,
174        notonline,
175        notdeleted,
176        notinproject);
177
178    /** Visibility check for the permissions dialog. */
179    public static final I_CmsHasMenuItemVisibility PERMISSIONS = new CmsStandardVisibilityCheck(
180        roleeditor,
181        notonline,
182        notdeleted,
183        writepermisssion,
184        controlpermission,
185        inproject);
186
187    /** Visibility check for publish option. */
188    public static final CmsStandardVisibilityCheck PUBLISH = new CmsStandardVisibilityCheck(
189        notunchangedfile,
190        publishpermission,
191        notonline,
192        inproject);
193
194    /** Check for the 'replace' operation. */
195    public static final CmsStandardVisibilityCheck REPLACE = new CmsStandardVisibilityCheck(
196        replacable,
197        roleeditor,
198        notonline,
199        notdeleted,
200        writepermisssion,
201        inproject);
202
203    /** Default check for 'locked resources' action. */
204    public static final CmsStandardVisibilityCheck SHOW_LOCKS = new CmsStandardVisibilityCheck(
205        notonline,
206        inproject,
207        folder);
208
209    /** Permission check for stealing locks. */
210    public static final I_CmsHasMenuItemVisibility STEAL_LOCK = new CmsStandardVisibilityCheck(
211        otherlock,
212        noinheritedlock,
213        inproject);
214
215    /** Visibility check for undelete option. */
216    public static final CmsStandardVisibilityCheck UNDELETE = new CmsStandardVisibilityCheck(
217        roleeditor,
218        notonline,
219        deleted,
220        writepermisssion,
221        inproject);
222
223    /** Visibility check for the undo function. */
224    public static final CmsStandardVisibilityCheck UNDO = new CmsStandardVisibilityCheck(
225        notunchangedfile,
226        notnew,
227        roleeditor,
228        notonline,
229        notdeleted,
230        writepermisssion,
231        inproject);
232
233    /** Visibility check for the undo function. */
234    public static final CmsStandardVisibilityCheck UNLOCK = new CmsStandardVisibilityCheck(
235        mylock,
236        noinheritedlock,
237        inproject);
238
239    /** Default visibility check for view operations on resources. */
240    public static final CmsStandardVisibilityCheck VIEW = new CmsStandardVisibilityCheck(roleeditor, notdeleted);
241
242    /** Always active. */
243    public static final I_CmsHasMenuItemVisibility VISIBLE = new CmsStandardVisibilityCheck();
244
245    /** Logger instance for this class. */
246    private static final Log LOG = CmsLog.getLog(CmsStandardVisibilityCheck.class);
247
248    /** The set of flags. */
249    private Set<CmsVisibilityCheckFlag> m_flags = Sets.newHashSet();
250
251    /**
252     * Creates a new instance using the given flags.<p>
253     *
254     * Note that the order of the flags does not matter; the checks corresponding to the flags are performed in a fixed order.
255     *
256     * @param flags the flags indicating which checks to perform
257     */
258    public CmsStandardVisibilityCheck(CmsVisibilityCheckFlag... flags) {
259
260        for (CmsVisibilityCheckFlag flag : flags) {
261            m_flags.add(flag);
262        }
263    }
264
265    /**
266     * Helper method to make checking for a flag very short (character count).<p>
267     *
268     * @param flag the flag to check
269     *
270     * @return true if this instance was configured with the given flag
271     */
272    public boolean flag(CmsVisibilityCheckFlag flag) {
273
274        return m_flags.contains(flag);
275    }
276
277    /**
278     * @see org.opencms.ui.contextmenu.A_CmsSimpleVisibilityCheck#getSingleVisibility(org.opencms.file.CmsObject, org.opencms.file.CmsResource)
279     */
280    @Override
281    public CmsMenuItemVisibilityMode getSingleVisibility(CmsObject cms, CmsResource resource) {
282
283        boolean prioritize = false;
284        String inActiveKey = null;
285        if (resource != null) {
286            if (flag(roleeditor) && !OpenCms.getRoleManager().hasRoleForResource(cms, CmsRole.EDITOR, resource)) {
287                return VISIBILITY_INVISIBLE;
288            }
289            if (flag(rolewpuser)
290                && !OpenCms.getRoleManager().hasRoleForResource(cms, CmsRole.WORKPLACE_USER, resource)) {
291                return VISIBILITY_INVISIBLE;
292            }
293        } else {
294            if (flag(roleeditor) && !OpenCms.getRoleManager().hasRole(cms, CmsRole.EDITOR)) {
295                return VISIBILITY_INVISIBLE;
296            }
297
298            if (flag(rolewpuser) && !OpenCms.getRoleManager().hasRole(cms, CmsRole.WORKPLACE_USER)) {
299                return VISIBILITY_INVISIBLE;
300            }
301        }
302        if (flag(notonline) && cms.getRequestContext().getCurrentProject().isOnlineProject()) {
303            return VISIBILITY_INVISIBLE;
304        }
305
306        if ((resource != null)) {
307            CmsResourceUtil resUtil = new CmsResourceUtil(cms, resource);
308            if (flag(file) && !resource.isFile()) {
309                return VISIBILITY_INVISIBLE;
310            }
311
312            if (flag(defaultfile)) {
313                if (!resource.isFile()) {
314                    return VISIBILITY_INVISIBLE;
315                }
316                try {
317                    CmsResource parentFolder = cms.readParentFolder(resource.getStructureId());
318                    if (parentFolder == null) {
319                        return VISIBILITY_INVISIBLE;
320                    }
321                    CmsResource defaultFile = cms.readDefaultFile(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION);
322                    if ((defaultFile == null) || !(defaultFile.getStructureId().equals(resource.getStructureId()))) {
323                        return VISIBILITY_INVISIBLE;
324                    }
325                } catch (CmsException e) {
326                    LOG.error(e.getLocalizedMessage(), e);
327                    return VISIBILITY_INVISIBLE;
328                }
329            }
330
331            if (flag(folder) && resource.isFile()) {
332                return VISIBILITY_INVISIBLE;
333            }
334
335            if (flag(pagefolder)) {
336                if (!resource.isFolder()) {
337                    return VISIBILITY_INVISIBLE;
338                }
339                try {
340                    CmsResource defaultFile;
341                    defaultFile = cms.readDefaultFile("" + resource.getStructureId());
342                    if ((defaultFile == null) || !CmsResourceTypeXmlContainerPage.isContainerPage(defaultFile)) {
343                        return VISIBILITY_INVISIBLE;
344                    }
345                } catch (CmsException e) {
346                    LOG.warn(e.getLocalizedMessage(), e);
347                    return VISIBILITY_INVISIBLE;
348                }
349            }
350
351            if (flag(pointer)
352                && !OpenCms.getResourceManager().matchResourceType(
353                    CmsResourceTypePointer.getStaticTypeName(),
354                    resource.getTypeId())) {
355                return VISIBILITY_INVISIBLE;
356            }
357
358            if (flag(notpointer)
359                && OpenCms.getResourceManager().matchResourceType(
360                    CmsResourceTypePointer.getStaticTypeName(),
361                    resource.getTypeId())) {
362                return VISIBILITY_INVISIBLE;
363            }
364
365            if (flag(replacable)) {
366                I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource);
367                boolean usesDumpLoader = type.getLoaderId() == CmsDumpLoader.RESOURCE_LOADER_ID;
368                if (!usesDumpLoader && !(type instanceof CmsResourceTypeImage)) {
369                    return VISIBILITY_INVISIBLE;
370                }
371
372            }
373
374            if (flag(hassourcecodeeditor)) {
375                I_CmsResourceType type = resUtil.getResourceType();
376                boolean hasSourcecodeEditor = (type instanceof CmsResourceTypeXmlContent)
377                    || (type instanceof CmsResourceTypeXmlPage)
378                    || (type instanceof CmsResourceTypePointer)
379                    || OpenCms.getResourceManager().matchResourceType(
380                        BundleType.PROPERTY.toString(),
381                        resource.getTypeId());
382                if (!hasSourcecodeEditor) {
383                    return VISIBILITY_INVISIBLE;
384                }
385            }
386
387            if (flag(unlocked)) {
388                CmsLock lock = resUtil.getLock();
389                if (!lock.isUnlocked()) {
390                    return VISIBILITY_INVISIBLE;
391                }
392                prioritize = true;
393            }
394
395            if (flag(otherlock)) {
396                CmsLock lock = resUtil.getLock();
397                if (lock.isUnlocked() || lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
398                    return VISIBILITY_INVISIBLE;
399                }
400                prioritize = true;
401            }
402
403            if (flag(nootherlock)) {
404                CmsLock lock = resUtil.getLock();
405                if (!lock.isUnlocked() && !lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
406                    return VISIBILITY_INVISIBLE;
407                }
408            }
409
410            if (flag(mylock)) {
411                CmsLock lock = resUtil.getLock();
412                if (!lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
413                    return VISIBILITY_INVISIBLE;
414                }
415                prioritize = true;
416            }
417
418            if (flag(noinheritedlock)) {
419                CmsLock lock = resUtil.getLock();
420                if (lock.isInherited()) {
421                    return VISIBILITY_INVISIBLE;
422                }
423            }
424
425            if (flag(notunchangedfile) && resource.isFile() && resUtil.getResource().getState().isUnchanged()) {
426                inActiveKey = Messages.GUI_CONTEXTMENU_TITLE_INACTIVE_UNCHANGED_0;
427            }
428
429            if (flag(notnew) && (inActiveKey == null) && resource.getState().isNew()) {
430                inActiveKey = Messages.GUI_CONTEXTMENU_TITLE_INACTIVE_NEW_UNCHANGED_0;
431            }
432
433            if (flag(xmlunmarshal)) {
434                if (CmsResourceTypeXmlContent.isXmlContent(resource)) {
435                    try {
436                        CmsXmlContentFactory.unmarshal(cms, cms.readFile(resource));
437                    } catch (Exception e) {
438                        return VISIBILITY_INVISIBLE;
439                    }
440                }
441
442            }
443
444            if (flag(haseditor) && (OpenCms.getWorkplaceAppManager().getEditorForResource(resource, false) == null)) {
445                return VISIBILITY_INVISIBLE;
446            }
447
448            if (flag(inproject) && (!resUtil.isInsideProject() || resUtil.getProjectState().isLockedForPublishing())) {
449                return VISIBILITY_INVISIBLE;
450            }
451
452            if (flag(notinproject)
453                && (resUtil.isInsideProject() || resUtil.getProjectState().isLockedForPublishing())) {
454                return VISIBILITY_INVISIBLE;
455            }
456
457            if (flag(publishpermission)) {
458                try {
459                    if (!cms.hasPermissions(
460                        resource,
461                        CmsPermissionSet.ACCESS_DIRECT_PUBLISH,
462                        false,
463                        CmsResourceFilter.ALL)) {
464                        return VISIBILITY_INVISIBLE;
465                    }
466                } catch (CmsException e) {
467                    LOG.error(e.getLocalizedMessage(), e);
468                }
469            }
470
471            if (flag(controlpermission)) {
472                try {
473                    if (!cms.hasPermissions(
474                        resource,
475                        CmsPermissionSet.ACCESS_CONTROL,
476                        false,
477                        CmsResourceFilter.IGNORE_EXPIRATION)) {
478                        return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
479                    }
480                } catch (CmsException e) {
481                    LOG.warn("Error checking context menu entry permissions", e);
482                    return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
483                }
484            }
485
486            if (flag(writepermisssion)) {
487                try {
488                    if (!resUtil.getLock().isLockableBy(cms.getRequestContext().getCurrentUser())) {
489                        // set invisible if not lockable
490                        return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
491                    }
492                    if (!resUtil.isEditable()
493                        || !cms.hasPermissions(
494                            resUtil.getResource(),
495                            CmsPermissionSet.ACCESS_WRITE,
496                            false,
497                            CmsResourceFilter.ALL)) {
498                        inActiveKey = Messages.GUI_CONTEXTMENU_TITLE_INACTIVE_PERM_WRITE_0;
499                    }
500                } catch (CmsException e) {
501                    LOG.debug("Error checking context menu entry permissions.", e);
502                    return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
503                }
504            }
505
506            if (flag(notdeleted) && (inActiveKey == null) && resUtil.getResource().getState().isDeleted()) {
507                inActiveKey = Messages.GUI_CONTEXTMENU_TITLE_INACTIVE_DELETED_0;
508            }
509
510            if (flag(deleted) && !resource.getState().isDeleted()) {
511                return CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE;
512            }
513
514        } else {
515            return VISIBILITY_INVISIBLE;
516        }
517        if (inActiveKey != null) {
518            return CmsMenuItemVisibilityMode.VISIBILITY_INACTIVE.addMessageKey(inActiveKey).prioritize(prioritize);
519        }
520        return VISIBILITY_ACTIVE.prioritize(prioritize);
521    }
522
523    /**
524     * @see org.opencms.ui.contextmenu.I_CmsHasMenuItemVisibility#getVisibility(org.opencms.ui.I_CmsDialogContext)
525     */
526    public CmsMenuItemVisibilityMode getVisibility(I_CmsDialogContext context) {
527
528        return getVisibility(context.getCms(), context.getResources());
529    }
530
531    /**
532     * @see java.lang.Object#toString()
533     */
534    @Override
535    public String toString() {
536
537        return "visibility[" + m_flags + "]";
538    }
539}