* Can optionally remove the hidden and index control aspects if the name of a node no longer matches the filter.
     * 
     * @param nodeRef
     * @param both     if true, will check both if the node should not be hidden and remove hidden and index control
     * 				   aspects if they are present, and if the node should be hidden and add hidden and index control
     * 				   aspects if they are not present.
     * @return true if the node is hidden, irrespective of the clientVisibility property value.
     */
    public boolean checkHidden(NodeRef nodeRef, boolean both, boolean checkChildren)
    {
        if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
        {
            Boolean isHiddenFlag = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_HIDDEN_FLAG);
            if(isHiddenFlag != null && isHiddenFlag)
            {
                logger.debug("node has hidden flag set");
                // node has hidden flag - we are not going to change anything.
                return true;
            }
        }
        
        boolean isHidden = false;
    	if(hasHiddenAspect(nodeRef) && isClientControlled(nodeRef))
    	{
    		// node is already hidden and client controlled -> hidden
    		isHidden = true;
    	}
    	else
    	{
            HiddenFileInfo info = isParentHidden(nodeRef);
            if(info != null && info.cascadeHiddenAspect())
            {
                // Parent has hidden aspect and cascade == true, so apply hidden aspect to children
                isHidden = true;
                if(!hasHiddenAspect(nodeRef))
                {
                    addHiddenAspect(nodeRef, info);
                }
                if(!hasIndexControlAspect(nodeRef))
                {
                    addIndexControlAspect(nodeRef);
                }
                applyHidden(nodeRef, info, checkChildren);
            }
	    	else
	        {
	    		// apply the "old" behaviour: try to match the node path against one of the registered hidden file patterns.
	    		info = findMatch(nodeRef);
	            if(info != null)
	            {
	                isHidden = true;
	                if(!hasHiddenAspect(nodeRef))
	                {
	                    addHiddenAspect(nodeRef, info);
	                }
	                else
	                {
	                    nodeService.setProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK, info.getVisibilityMask());
	                    nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_HIDDEN, info.cascadeHiddenAspect());
	                    nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_INDEX_CONTROL, info.cascadeIndexControlAspect());
	                }
	                if(!hasIndexControlAspect(nodeRef))
	                {
	                    addIndexControlAspect(nodeRef);
	                }
	                applyHidden(nodeRef, info, checkChildren);
	            }
	            else if(both)
	            {
	                // the file does not match the pattern, ensure that the hidden and index control aspects are not present
	                if(hasHiddenAspect(nodeRef))
	                {
	                    removeHiddenAspect(nodeRef);
	                }
	
	                if(hasIndexControlAspect(nodeRef))
	                {
	                    removeIndexControlAspect(nodeRef);
	                }
	                removeHidden(nodeRef);
	            }
	        }
        }
        return isHidden;
    }
    /**
     * Gets the visibility constraint for the given client on the given node.
     * 
     * @param client
     * @param nodeRef
     * 
     * @return the visibility constraint for the given client and node
     */
    public Visibility getVisibility(Client client, NodeRef nodeRef)
    {
        Visibility ret = Visibility.Visible;
        if (! AuthenticationUtil.isRunAsUserTheSystemUser())
        {
            if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
            {
                Integer visibilityMask = (Integer)nodeService.getProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK);
                if (visibilityMask != null)
                {
                	if(client != null && client.equals(Client.admin))
                	{
                        ret = Visibility.Visible;
                	}
                	else if(visibilityMask.intValue() == 0)
                    {
                        ret = Visibility.NotVisible;
                    }
                    else if(client == null)
                    {
                        ret = Visibility.NotVisible;
                    }
                    else
                    {
                        ret = getVisibility(visibilityMask.intValue(), client);
                    }
                }
                else
                {
                    // no visibility mask property, so retain backwards compatibility with 3.4 hidden aspect behaviour
                    if(client == Client.cifs)
                    {
                        ret = Visibility.HiddenAttribute;
                    }
                    else if(client == Client.webdav || client == Client.nfs || client == Client.imap)
                    {
                        ret = Visibility.Visible;
                    }
                    else
                    {
                        ret = Visibility.NotVisible;
                    }
                }
            }
        }
        return ret;
    }
    private class HiddenFileInfoImpl implements HiddenFileInfo
    {
        private Pattern filter;
        private Set