();
        result.putAll(members);
        
        return result;
    }
    
    /**
     * Gets a user's role on this site.
     * 
     * If the user is not a member of the site then null is returned.
     * 
     * @param authorityName  authority name
     * @return String   user's role or null if not a member
     */
    public String getMembersRole(String authorityName)
    {
        return this.siteService.getMembersRole(getShortName(), authorityName);
    }
    
    /**
     * Indicates whether a user is a member of the site.
     * 
     * @param authorityName  user name
     * @return boolean  true if the user is a member of the site, false otherwise
     */
    public boolean isMember(String authorityName)
    {
        return this.siteService.isMember(getShortName(), authorityName);
    }
    
    /**
     * Sets the membership details for a user.
     * 
     * If the user is not already a member of the site then they are added with the role
     * given.  If the user is already a member of the site then their role is updated to the new role.
     * 
     * Only a site manager can modify memberships and there must be at least one site manager at
     * all times.
     * 
     * @param authorityName  authority name
     * @param role      site role
     */
    public void setMembership(String authorityName, String role)
    {
        this.siteService.setMembership(getShortName(), authorityName, role);
    }
    
    /**
     * Removes a user or group membership from a site.
     * 
     * Only a site manager can remove a user's membership and the last site manager can not be removed.
     * 
     * @param authorityName  authority name
     */
    public void removeMembership(String authorityName)
    {
        this.siteService.removeMembership(getShortName(), authorityName);
    }
    /**
     * Gets (or creates) the "container" folder for the specified component id
     * 
     * @param componentId
     * @return node representing the "container" folder (or null, if for some reason 
     *         the container can not be created - probably due to permissions)
     */
    public ScriptNode getContainer(String componentId)
    {
    	ScriptNode container = null;
    	try
    	{
    	    NodeRef containerNodeRef = this.siteService.getContainer(getShortName(), componentId);
    	    if (containerNodeRef != null)
    	    {
    	        container = new ScriptNode(containerNodeRef, this.serviceRegistry, this.scope);
    	    }
    	}
        catch (AccessDeniedException ade)
        {
            return null;
        }
    	return container;
    }
    
    /**
     * Creates a new site container 
     * 
     * @param componentId   component id
     * @return ScriptNode   the created container
     */
    public ScriptNode createContainer(String componentId)
    {
        return createContainer(componentId, null, null);
    }
    
    /**
     * Creates a new site container
     * 
     * @param componentId   component id
     * @param folderType    folder type to create
     * @return ScriptNode   the created container
     */
    public ScriptNode createContainer(String componentId, String folderType)
    {
        return createContainer(componentId, folderType, null);
    }
    /**
     * Creates a new site container
     * 
     * @param componentId   component id
     * @param folderType    folder type to create
     * @return ScriptNode   the created container
     */
    public ScriptNode createContainer(final String componentId, final String folderType, final Object permissions)
    {
        NodeRef containerNodeRef = AuthenticationUtil.runAs(new RunAsWork()
        {
            public NodeRef doWork() throws Exception
            {
                // Get the container type
                QName folderQName = (folderType == null) ? null : QName.createQName(folderType, serviceRegistry.getNamespaceService());
                
                // Create the container node
                NodeRef containerNode = Site.this.siteService.createContainer(getShortName(), componentId, folderQName, null);
                
                // Set any permissions that might have been provided for the container
                if (permissions != null && permissions instanceof ScriptableObject)
                {
                    ScriptableObject scriptable = (ScriptableObject)permissions;
                    Object[] propIds = scriptable.getIds();
                    for (int i = 0; i < propIds.length; i++)
                    {
                        // work on each key in turn
                        Object propId = propIds[i];
                        
                        // we are only interested in keys that are formed of Strings
                        if (propId instanceof String)
                        {
                            // get the value out for the specified key - it must be String
                            final String key = (String)propId;
                            final Object value = scriptable.get(key, scriptable);
                            if (value instanceof String)
                            {                                   
                                // Set the permission on the container
                                Site.this.serviceRegistry.getPermissionService().setPermission(containerNode, key, (String)value, true);
                            }
                        }
                    }
                }
                
                // Make the "admin" the owner of the node
                serviceRegistry.getOwnableService().setOwner(containerNode, AuthenticationUtil.getAdminUserName());
        
                return containerNode;
            }
        }, AuthenticationUtil.SYSTEM_USER_NAME);
        
        if (Site.this.serviceRegistry.getPermissionService().hasPermission(containerNodeRef, PermissionService.READ_PROPERTIES) == AccessStatus.ALLOWED) 
        {
            return getContainer(componentId); 
        }
        else
        {
            // current user has no access.
            return null;
        }          
    
    }
    
    /**
     * Determine if the "container" folder for the specified component exists
     * 
     * @param componentId
     * @return  true => "container" folder exists
     */
    public boolean hasContainer(String componentId)
    {
    	return this.siteService.hasContainer(getShortName(), componentId);
    }
    
    /**
     * Apply a set of permissions to the node.
     * 
     * @param nodeRef   node reference
     */
    public void setPermissions(final ScriptNode node, final Object permissions)
    {
        final NodeRef nodeRef = node.getNodeRef();
        
        if (permissions != null && permissions instanceof ScriptableObject)
        {
            // Get the permission service
            final PermissionService permissionService = this.serviceRegistry.getPermissionService();
            
            if (!permissionService.getInheritParentPermissions(nodeRef))
            {
                // remove existing permissions
                permissionService.deletePermissions(nodeRef);
            }
            
            // Assign the correct permissions
            ScriptableObject scriptable = (ScriptableObject)permissions;
            Object[] propIds = scriptable.getIds();
            for (int i = 0; i < propIds.length; i++)
            {
                // Work on each key in turn
                Object propId = propIds[i];
                
                // Only interested in keys that are formed of Strings
                if (propId instanceof String)
                {
                    // Get the value out for the specified key - it must be String
                    final String key = (String)propId;
                    final Object value = scriptable.get(key, scriptable);
                    if (value instanceof String)
                    {                                   
                        // Set the permission on the node
                        permissionService.setPermission(nodeRef, key, (String)value, true);
                    }
                }
            }
            
            // always add the site managers group with SiteManager permission
            String managers = this.siteService.getSiteRoleGroup(getShortName(), SiteModel.SITE_MANAGER);
            permissionService.setPermission(nodeRef, managers, SiteModel.SITE_MANAGER, true);
            
            // now turn off inherit to finalize our permission changes
            permissionService.setInheritParentPermissions(nodeRef, false);
        }
        else
        {
        	// No permissions passed-in
        	this.resetAllPermissions(node);
        }
    }
    
    /**
     * Reset any permissions that have been set on the node.  
     * 
     * All permissions will be deleted and the node set to inherit permissions.
     * 
     * @param nodeRef   node reference
     */
    public void resetAllPermissions(ScriptNode node)
    {
        final NodeRef nodeRef = node.getNodeRef();
        
        PermissionService permissionService = serviceRegistry.getPermissionService();
        try
        {
            // Ensure node isn't inheriting permissions from an ancestor before deleting
            if (!permissionService.getInheritParentPermissions(nodeRef))
            {
                permissionService.deletePermissions(nodeRef);
                permissionService.setInheritParentPermissions(nodeRef, true);
            }
        }
        catch (AccessDeniedException e)
        {
            throw new AlfrescoRuntimeException("You do not have the authority to update permissions on this node.", e);
        }
    }  
    
    /**
     * Get the value of a custom property, null if the custom property has not been set or doesn't exist.
     * 
     * @param  name             qname of the property 
     * @return Serializable     value of the property, null if not set
     */
    public CustomProperty getCustomProperty(String name)
    {
        return (CustomProperty)getCustomProperties().get(name);
    }
    
    /**
     * Get a map of the sites custom properties
     * 
     * @return ScriptableQNameMap     map of names and values
     */
    public ScriptableQNameMap getCustomProperties()
    {
        if (this.customProperties == null)
        {
            // create the custom properties map
            ScriptNode siteNode = new ScriptNode(this.siteInfo.getNodeRef(), this.serviceRegistry);
            this.customProperties = new ContentAwareScriptableQNameMap(siteNode, this.serviceRegistry);
            
            Map props = siteInfo.getCustomProperties();
            for (QName qname : props.keySet())
            {
                // get the property value
                Serializable propValue = props.get(qname);
                
                // convert the value
                NodeValueConverter valueConverter = siteNode.new NodeValueConverter();
                Serializable value = valueConverter.convertValueForScript(qname, propValue);
                
                // get the type and label information from the dictionary
                String title = null;
                String type = null;
                PropertyDefinition propDef = this.serviceRegistry.getDictionaryService().getProperty(qname);
                if (propDef != null)
                {
                    type = propDef.getDataType().getName().toString();
                    title = propDef.getTitle();
                }
                
                // create the custom property and add to the map
                CustomProperty customProp = new CustomProperty(qname.toString(), value, type, title);
                this.customProperties.put(qname.toString(), customProp);
            }
        }
        return this.customProperties;
    }
    
    /**
     * Create new moderated invitation to this web site
     * @return the new invitation
     */
    public ScriptInvitation> inviteModerated(String inviteeComments, String inviteeUserName, String inviteeRole)
    {
    	Invitation invitation = invitationService.inviteModerated(inviteeComments, inviteeUserName, Invitation.ResourceType.WEB_SITE, getShortName(), inviteeRole);
    	return scriptInvitationFactory.toScriptInvitation(invitation);
    }
    
    /**
     * Create new nominated invitation to this web site
     * @return the new invitation
     */
    public ScriptInvitation> inviteNominated(String inviteeFirstName, String inviteeLastName, String inviteeEmail, String inviteeRole, String serverPath, String acceptUrl, String rejectUrl)
    {    	
    	Invitation invitation = invitationService.inviteNominated(inviteeFirstName, inviteeLastName, inviteeEmail, Invitation.ResourceType.WEB_SITE, getShortName(), inviteeRole, serverPath, acceptUrl, rejectUrl);
    	return scriptInvitationFactory.toScriptInvitation(invitation);
    }
    
    /**
     * Create new nominated invitation to this web site
     * @return the new invitation
     */
    public ScriptInvitation> inviteNominated(String inviteeUserName, String inviteeRole, String serverPath, String acceptUrl, String rejectUrl)
    {    	
    	Invitation invitation = invitationService.inviteNominated(inviteeUserName, Invitation.ResourceType.WEB_SITE, getShortName(), inviteeRole, serverPath, acceptUrl, rejectUrl);
    	return scriptInvitationFactory.toScriptInvitation(invitation);
    }
    
    /**
     * Get an invitation to this web site
     * @return the invitation or null if it does not exist
     */
    public ScriptInvitation> getInvitation(String invitationId)
    {    	
    	try 
    	{
    		Invitation invitation = invitationService.getInvitation(invitationId);
    		return scriptInvitationFactory.toScriptInvitation(invitation);
    	} 
    	catch (InvitationException e)
    	{
    		return null;
        }
    }
    
    /**
     * list the outstanding invitations for this site
     * 
     * Map of name / invitation
     */
    public ScriptInvitation>[] listInvitations()
    {
    	List invitations = invitationService.listPendingInvitationsForResource(Invitation.ResourceType.WEB_SITE, getShortName());
    	ScriptInvitation>[] ret = new ScriptInvitation[invitations.size()];
        int i = 0;
		for(Invitation item : invitations)
		{
			ret[i++] = scriptInvitationFactory.toScriptInvitation(item);
		}
    	return ret;
    }
    
    /**
     * List the open invitations for this web site.
     * props specifies optional properties to be searched.
     * 
     * @param props inviteeUserName
     *
     * @return the invitations
     */
    public ScriptInvitation>[] listInvitations(Scriptable props)
    {
    	InvitationSearchCriteriaImpl crit = new InvitationSearchCriteriaImpl();
    	crit.setResourceName(getShortName());
    	crit.setResourceType(Invitation.ResourceType.WEB_SITE);
    	
    	if (props.has("inviteeUserName", props))
    	{
    		crit.setInvitee((String)props.get("inviteeUserName", props));
      	}
    	if (props.has("invitationType", props))
    	{
    		String invitationType = (String)props.get("invitationType", props);
    		crit.setInvitationType(InvitationType.valueOf(invitationType));
        }
    	List invitations = invitationService.searchInvitation(crit);
    	ScriptInvitation>[] ret = new ScriptInvitation[invitations.size()];
        int i = 0;
		for(Invitation item : invitations)
		{
			ret[i++] = scriptInvitationFactory.toScriptInvitation(item);
		}
    	return ret;
    }
    
    /**
     * Custom property helper class
     * 
     * @author Roy Wetherall
     */
    public class CustomProperty
    {
        /** Details of the custom property */
        private String name;
        private Serializable value;
        private String type;
        private String title;
        
        /**
         * Constructor
         * 
         * @param name      property name
         * @param value     property value
         * @param type      property type
         * @param title     property title
         */
        public CustomProperty(String name, Serializable value, String type, String title)
        {
            this.name = name;
            this.value = value;
            this.type = type;
            this.title = title;
        }
        
        public String getName()
        {
            return name;
        }
        
        public Serializable getValue()
        {
            return value;
        }
        
        public String getType()
        {
            return type;
        }
        
        public String getTitle()
        {
            return title;
        }
    }
}