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