Index: main/plugin-web/i18n/messages_en.xml
===================================================================
--- main/plugin-web/i18n/messages_en.xml	(revision 27955)
+++ main/plugin-web/i18n/messages_en.xml	(working copy)
@@ -2037,19 +2037,19 @@
 	
     <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_LABEL">Add granted users</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_DESCRIPTION">Add users to the list of granted users</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_NOSELECTION_DESCRIPTION">This button is disabled because no page or sitemap is selected.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_NORIGHT_DESCRIPTION">This button is disabled because your rights are insufficient.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_ANYCONNECTEDUSER_DESCRIPTION">This button is disabled because the page or sitemap allows all connected users.&lt;br/&gt;Please disable this mode to go back to access limitation to specific users and groups.</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_LABEL">Add granted groups</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_DESCRIPTION">Add groups to the list of granted groups</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_NOSELECTION_DESCRIPTION">This button is disabled because no page or sitemap is selected.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_NORIGHT_DESCRIPTION">This button is disabled because your rights are insufficient.</message>
     <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_LABEL">Allow all connected users</message>
     <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_DESCRIPTION">If this option is enabled, any connected user (but not the anonymous ones) can access the page or sitemap and its subpages.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_NOSELECTION_DESCRIPTION">This button is disabled because no page or sitemap is selected.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_NORIGHT_DESCRIPTION">This button is disabled because your rights are insufficient.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_LABEL">Remove authorization</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_DESCRIPTION">Remove the selected user or group from the granted list</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_LABEL">Remove authorization or limitation</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_DESCRIPTION">Remove the selected user or group from the granted list or from the excluded list</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_LABEL">Exclude users</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_DESCRIPTION">Add users to the list of excluded users</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_LABEL">Exclude groups</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_DESCRIPTION">Add groups to the list of excluded groups</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION">This button is disabled because no page or sitemap is selected.</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION">This button is disabled because your rights are insufficient.</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION">This button is disabled because the page or sitemap allows all connected users.&lt;br/&gt;Please disable this mode to go back to access limitation to specific users and groups.</message>
     
     <message key="PLUGINS_WEB_PAGEACCESS_HELP_1">Access restriction on the page </message>
     <message key="PLUGINS_WEB_PAGEACCESS_HELP_2"> and on its child pages</message>
@@ -2066,7 +2066,10 @@
     <message key="PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER">This page or sitemap is restricted, accessible by any connected user</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER_INHERITED"> (this limitation is inherited from its hierarchy)</message>
     <message key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_USERS">Granted users</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_USERS">Granted users</message>
     <message key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_GROUPS">Granted groups</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_USERS">Excluded users</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_GROUPS">Excluded groups</message>
    	<message key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER">_Unknown user</message>
     <message key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP">_Unknown group</message>
     
@@ -2304,7 +2307,7 @@
     <!-- +
     	 | RIBBON
     	 + -->
-    	 
+        	 
    	<!-- Tab Contextual Resources -->
 	<message key="RIBBON_TABS_TAB_RESOURCES_LABEL">Resources</message>
 	<message key="RIBBON_TABS_TAB_RESOURCES_GROUP_LABEL">Resources</message>
Index: main/plugin-web/i18n/messages_fr.xml
===================================================================
--- main/plugin-web/i18n/messages_fr.xml	(revision 27955)
+++ main/plugin-web/i18n/messages_fr.xml	(working copy)
@@ -2038,19 +2038,19 @@
     
     <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_LABEL">Ajouter des utilisateurs autorisés</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_DESCRIPTION">Ajouter des utilisateurs à la liste des utilisateurs autorisés</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_NOSELECTION_DESCRIPTION">Ce bouton est désactivé car aucune page ou plan du site n'est sélectionnée.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_NORIGHT_DESCRIPTION">Ce bouton est désactivé car vos droits sont insuffisants.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDUSERS_ANYCONNECTEDUSER_DESCRIPTION">Ce bouton est désactivé car la page autorise tous les utilisateurs connectés.&lt;br/&gt;Veuillez désactiver ce mode pour revenir à une limitation à des utilisateurs et groupes spécifiques.</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_LABEL">Ajouter un groupe autorisé</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_DESCRIPTION">Ajouter un groupe à la liste des groupes autorisés</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_NOSELECTION_DESCRIPTION">Ce bouton est désactivé car aucune page ou plan du site n'est sélectionnée.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_ADDGROUP_NORIGHT_DESCRIPTION">Ce bouton est désactivé car vos droits sont insuffisants.</message>
     <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_LABEL">Autoriser tous les utilisateurs connectés</message>
     <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_DESCRIPTION">Activer cette option autorise n'importe quel utilisateur connecté (mais pas les visiteurs anonymes) à accéder à la page et ses sous-pages.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_NOSELECTION_DESCRIPTION">Ce bouton est désactivé car aucune page ou plan du site n'est sélectionnée.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_SETANYUSER_NORIGHT_DESCRIPTION">Ce bouton est désactivé car vos droits sont insuffisants.</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_LABEL">Enlever l'autorisation</message>
-    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_DESCRIPTION">Enlever l'autorisation à l'utilisateur ou au groupe sélectionné</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_LABEL">Enlever l'autorisation ou l'exclusion</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_REMOVE_DESCRIPTION">Enlever l'autorisation ou l'exclusion à l'utilisateur ou au groupe sélectionné</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_LABEL">Exclure des utilisateurs</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_DESCRIPTION">Ajouter des utilisateurs à la liste des utilisateurs exclus</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_LABEL">Exclure des groupes</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_DESCRIPTION">Ajouter des groupes à la liste des groupes exclus</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION">Ce bouton est désactivé car aucune page ou plan du site n'est sélectionnée.</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION">Ce bouton est désactivé car vos droits sont insuffisants.</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION">Ce bouton est désactivé car la page autorise tous les utilisateurs connectés.&lt;br/&gt;Veuillez désactiver ce mode pour revenir à une limitation à des utilisateurs et groupes spécifiques.</message>
     
     <message key="PLUGINS_WEB_PAGEACCESS_HELP_1">Limitation de l'accès à la page </message>
     <message key="PLUGINS_WEB_PAGEACCESS_HELP_2"> et à ses sous-pages</message>
@@ -2068,6 +2068,8 @@
     <message key="PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER_INHERITED"> (cette limitation d'accès est héritée de sa hiérarchie)</message>
     <message key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_USERS">Utilisateurs autorisés</message>
     <message key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_GROUPS">Groupes autorisés</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_USERS">Utilisateurs exclus</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_GROUPS">Groupes exclus</message>
     <message key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER">_Utilisateur inconnu</message>
     <message key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP">_Groupe inconnu</message>
     
Index: main/plugin-web/plugin.xml
===================================================================
--- main/plugin-web/plugin.xml	(revision 27955)
+++ main/plugin-web/plugin.xml	(working copy)
@@ -24,7 +24,7 @@
     <!-- +
              | CORE DATASOURCE
              + -->
-        <!-- JDBC -->
+    <!-- JDBC -->
 	<config>
         <param id="front-office.usersManager.jdbc.driver" type="string">
            	<!-- 
@@ -8279,6 +8279,63 @@
                 </css>
             </extension>
             
+            <extension id="org.ametys.web.pageaccess.ExcludeUsers"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
+                <action class="org.ametys.web.tool.pageaccess.control.PageAccessActionButton">
+                    <param name="action">org.ametys.web.pageaccess.ExcludeUsers.act</param>
+                    <param name="target-type">^(page|sitemap)$</param>
+                    
+                    <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_DESCRIPTION</param>
+                    <param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</param>
+                    <param name="any-connected-user-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</param>
+                    <param name="enable-on-allow-any-user"/>
+                    
+                    <param name="icon-small" file="true" plugin="cms">img/users/exclude_16.png</param>
+                    <param name="icon-medium" file="true" plugin="cms">img/users/exclude_32.png</param>
+                    <param name="icon-large" file="true" plugin="cms">img/users/exclude_50.png</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js</file>
+                    <file>js/org/ametys/web/tool/pageaccess/Actions.i18n.js</file>
+                </scripts>
+                <css>
+                    <file plugin="core">css/users/selectuser.css</file>
+                </css>
+                <right>Web_Rights_Page_SetAccess</right>
+            </extension>
+            
+                        
+            <extension id="org.ametys.web.pageaccess.ExcludeGroups"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
+                <action class="org.ametys.web.tool.pageaccess.control.PageAccessActionButton">
+                    <param name="action">org.ametys.web.pageaccess.ExcludeGroups.act</param>
+                    <param name="target-type">^(page|sitemap)$</param>
+                    
+                    <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_DESCRIPTION</param>
+                    <param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</param>
+                    <param name="any-connected-user-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</param>
+                    <param name="enable-on-allow-any-user"/>
+                    
+                    <param name="icon-small" file="true" plugin="cms">img/groups/exclude_16.png</param>
+                    <param name="icon-medium" file="true" plugin="cms">img/groups/exclude_32.png</param>
+                    <param name="icon-large" file="true" plugin="cms">img/groups/exclude_50.png</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js</file>
+                    <file>js/org/ametys/web/tool/pageaccess/Actions.i18n.js</file>
+                </scripts>
+                <css>
+                    <file plugin="core">css/users/selectuser.css</file>
+                </css>
+                <right>Web_Rights_Page_SetAccess</right>
+            </extension>
+                        
             <extension id="org.ametys.web.pageaccess.AddUsers"
                        point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
                        class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
@@ -8288,9 +8345,9 @@
                     
                     <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_LABEL</param>
                     <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_DESCRIPTION</param>
-					<param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_NOSELECTION_DESCRIPTION</param>
-                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_NORIGHT_DESCRIPTION</param>
-                    <param name="any-connected-user-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_ANYCONNECTEDUSER_DESCRIPTION</param>
+					<param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</param>
+                    <param name="any-connected-user-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</param>
                     
                     <param name="icon-small" file="true" plugin="cms">img/users/add_16.png</param>
                     <param name="icon-medium" file="true" plugin="cms">img/users/add_32.png</param>
@@ -8315,9 +8372,9 @@
 
                     <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDGROUP_LABEL</param>
                     <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDGROUP_DESCRIPTION</param>
-					<param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDGROUP_NOSELECTION_DESCRIPTION</param>
-                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDGROUP_NORIGHT_DESCRIPTION</param>
-                    <param name="any-connected-user-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_ANYCONNECTEDUSER_DESCRIPTION</param>
+					<param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</param>
+                    <param name="any-connected-user-description" i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</param>
                     
                     <param name="icon-small" file="true" plugin="cms">img/groups/add_16.png</param>
                     <param name="icon-medium" file="true" plugin="cms">img/groups/add_32.png</param>
@@ -8342,8 +8399,8 @@
 
                     <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_SETANYUSER_LABEL</param>
                     <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_SETANYUSER_DESCRIPTION</param>
-					<param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_SETANYUSER_NOSELECTION_DESCRIPTION</param>
-                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_SETANYUSER_NORIGHT_DESCRIPTION</param>
+					<param name="no-selection-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</param>
+                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</param>
                     
                     <param name="icon-small" file="true">img/pageaccess/set_any_user_16.png</param>
                     <param name="icon-medium" file="true">img/pageaccess/set_any_user_32.png</param>
@@ -8361,7 +8418,7 @@
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
                 <action class="org.ametys.web.pageaccess.RemoveAccessButton">
                     <param name="target-type">^pageaccess$</param>
-                    <param name="subtarget-type">^(user|group)$</param>
+                    <param name="subtarget-type">^(granted-user|granted-group|excluded-user|excluded-group)$</param>
                     
                     <param name="action">org.ametys.web.pageaccess.RemoveAccess.act</param>
                     
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/Actions.i18n.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/Actions.i18n.js	(revision 27955)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/Actions.i18n.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -17,11 +17,13 @@
 Ext.namespace('org.ametys.web.pageaccess');
 Ext.namespace('org.ametys.web.pageaccess.AddUsers');
 Ext.namespace('org.ametys.web.pageaccess.AddGroups');
+Ext.namespace('org.ametys.web.pageaccess.ExcludeUsers');
+Ext.namespace('org.ametys.web.pageaccess.ExcludeGroups');
 Ext.namespace('org.ametys.web.pageaccess.SetAnyUser');
 Ext.namespace('org.ametys.web.pageaccess.RemoveAccess');
 
 /*************************************/
-/**         AFFECT USERS            **/
+/**         ADD USERS               **/
 /*************************************/
 org.ametys.web.pageaccess.AddUsers._pluginName;
 org.ametys.web.pageaccess.AddUsers.act = function(name, params, pluginName)
@@ -55,7 +57,7 @@
     }
     
     var url = '/' + siteName + '/pageaccess/add';
-    var params = {'pagePath': pagePath, 'users': logins, 'pageId': target.getParameters().id, targetType: target.getType()};
+    var params = {'pagePath': pagePath, 'grantedUsers': logins, 'pageId': target.getParameters().id, targetType: target.getType()};
     
     var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.web.pageaccess.AddUsers._pluginName, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.pageaccess.AddUsers._callBack, this, params);
     org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
@@ -65,7 +67,7 @@
 {
 	var messages = [];
 	
-    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'users': params.users});
+    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'users': params.grantedUsers});
     messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED, null, [target]));
     
     var pageTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget(params.targetType, {'id': params.pageId});
@@ -93,8 +95,62 @@
 }
 
 /*************************************/
-/**         AFFECT GROUPS           **/
+/**         EXCLUDE USERS               **/
 /*************************************/
+org.ametys.web.pageaccess.ExcludeUsers._pluginName;
+org.ametys.web.pageaccess.ExcludeUsers.act = function(name, params, pluginName)
+{
+    org.ametys.web.pageaccess.ExcludeUsers._pluginName = pluginName;
+    RUNTIME_Plugin_Runtime_SelectUser.initialize('core');
+    RUNTIME_Plugin_Runtime_SelectUser.act(org.ametys.web.pageaccess.ExcludeUsers._doAction, null, 'org.ametys.runtime.user.UsersManager.FO');    
+}
+
+org.ametys.web.pageaccess.ExcludeUsers._doAction = function(users)
+{
+    var logins = [];
+    for (var i in users)
+    {
+        logins.push(i);
+    }
+    
+    var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+    var target = org.ametys.web.pageaccess.AddUsers._searchPageTarget(targets);
+    
+    if (target == null)
+    {
+        return;
+    }
+    
+    var siteName = context.parameters.siteName;
+    var pagePath = target.getParameters().lang;
+    if (target.getType() == 'page')
+    {
+        pagePath = pagePath + '/' + target.getParameters().path;
+    }
+    
+    var url = '/' + siteName + '/pageaccess/exclude';
+    var params = {'pagePath': pagePath, 'excludedUsers': logins, 'pageId': target.getParameters().id, targetType: target.getType()};
+    
+    var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.web.pageaccess.ExcludeUsers._pluginName, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.pageaccess.ExcludeUsers._callBack, this, params);
+    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.web.pageaccess.ExcludeUsers._callBack = function(response, params)
+{
+	var messages = [];
+	
+    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'users': params.excludedUsers});
+    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED, null, [target]));
+    
+    var pageTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget(params.targetType, {'id': params.pageId});
+    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_MINORMETADATACHANGED, {}, [pageTarget]));
+    
+    org.ametys.messagebus.MessageBus.getInstance().fireMessages(messages);
+}
+
+/*************************************/
+/**         ADD GROUPS              **/
+/*************************************/
 
 org.ametys.web.pageaccess.AddGroups._pluginName;
 org.ametys.web.pageaccess.AddGroups.act = function(name, params, pluginName) 
@@ -128,7 +184,7 @@
     }
     
     var url = '/' + siteName + '/pageaccess/add';
-    var params = {'pagePath': pagePath, 'groups': ids, 'pageId': target.getParameters().id, targetType: target.getType()};
+    var params = {'pagePath': pagePath, 'grantedGroups': ids, 'pageId': target.getParameters().id, targetType: target.getType()};
     
     var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.web.pageaccess.AddGroups._pluginName, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.pageaccess.AddGroups._callBack, this, params);
     org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
@@ -138,7 +194,61 @@
 {
 	var messages = [];
 	
-    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'groups': params.groups});
+    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'groups': params.grantedGroups});
+    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED, null, [target]));
+    
+    var pageTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget(params.targetType, {'id': params.pageId});
+    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_MINORMETADATACHANGED, {}, [pageTarget]));
+    
+    org.ametys.messagebus.MessageBus.getInstance().fireMessages(messages);
+}
+
+/*************************************/
+/**         EXCLUDE GROUPS          **/
+/*************************************/
+org.ametys.web.pageaccess.ExcludeGroups._pluginName;
+org.ametys.web.pageaccess.ExcludeGroups.act = function(name, params, pluginName) 
+{
+    org.ametys.web.pageaccess.ExcludeGroups._pluginName = pluginName;
+    RUNTIME_Plugin_Runtime_SelectGroup.initialize('core');
+    RUNTIME_Plugin_Runtime_SelectGroup.act(org.ametys.web.pageaccess.ExcludeGroups._doAction, null, 'org.ametys.runtime.group.GroupsManager.FO');  
+}
+
+org.ametys.web.pageaccess.ExcludeGroups._doAction = function(groups)
+{
+    var ids = [];
+    for (var i in groups)
+    {
+        ids.push(i);
+    }
+    
+    var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+    var target = org.ametys.web.pageaccess.AddUsers._searchPageTarget(targets);
+    
+    if (target == null)
+    {
+        return;
+    }
+    
+    var siteName = context.parameters.siteName;
+    var pagePath = target.getParameters().lang;
+    if (target.getType() == 'page')
+    {
+        pagePath = pagePath + '/' + target.getParameters().path;
+    }
+    
+    var url = '/' + siteName + '/pageaccess/exclude';
+    var params = {'pagePath': pagePath, 'excludedGroups': ids, 'pageId': target.getParameters().id, targetType: target.getType()};
+    
+    var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.web.pageaccess.ExcludeGroups._pluginName, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.pageaccess.ExcludeGroups._callBack, this, params);
+    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.web.pageaccess.ExcludeGroups._callBack = function(response, params)
+{
+	var messages = [];
+	
+    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'groups': params.excludedGroups});
     messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED, null, [target]));
     
     var pageTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget(params.targetType, {'id': params.pageId});
@@ -203,7 +313,7 @@
     
     var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
     var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == 'pageaccess' });
-    
+
     var pageTarget = org.ametys.web.pageaccess.AddUsers._searchPageTarget(targets);
     
     if (target == null)
@@ -213,22 +323,36 @@
     
     var pagePath = target.getParameters().pagePath;
     
-    var userTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(target.getSubTargets(), function(target) { return target.getType() == 'user' });
-    var groupTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(target.getSubTargets(), function(target) { return target.getType() == 'group' });
+    var grantedUserTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(target.getSubTargets(), function(target) { return target.getType() == 'granted-user' });
+    var grantedGroupTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(target.getSubTargets(), function(target) { return target.getType() == 'granted-group' });
+    var excludedUserTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(target.getSubTargets(), function(target) { return target.getType() == 'excluded-user' });
+    var excludedGroupTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(target.getSubTargets(), function(target) { return target.getType() == 'excluded-group' });
     
-    var users = [];
-    for (var i=0; i &lt; userTargets.length; i++)
+    var grantedUsers = [];
+    for (var i=0; i &lt; grantedUserTargets.length; i++)
     {
-        users.push(userTargets[i].getParameters().login);
+        grantedUsers.push(grantedUserTargets[i].getParameters().login);
     }
     
-    var groups = [];
-    for (var i=0; i &lt; groupTargets.length; i++)
+    var grantedGroups = [];
+    for (var i=0; i &lt; grantedGroupTargets.length; i++)
     {
-        groups.push(groupTargets[i].getParameters().id);
+        grantedGroups.push(grantedGroupTargets[i].getParameters().id);
     }
     
-    if (users.length == 0 &amp;&amp; groups.length == 0)
+    var excludedUsers = [];
+    for (var i=0; i &lt; excludedUserTargets.length; i++)
+    {
+        excludedUsers.push(excludedUserTargets[i].getParameters().login);
+    }
+    
+    var excludedGroups = [];
+    for (var i=0; i &lt; excludedGroupTargets.length; i++)
+    {
+        excludedGroups.push(excludedGroupTargets[i].getParameters().id);
+    }
+    
+    if (grantedUsers.length == 0 &amp;&amp; grantedGroups.length == 0 &amp;&amp; excludedUsers.length == 0 &amp;&amp; excludedGroups.length == 0)
     {
         return;
     }
@@ -236,7 +360,8 @@
     var siteName = context.parameters.siteName;
     
     var url = '/' + siteName + '/pageaccess/remove';
-    var params = {'pagePath': pagePath, 'users': users, 'groups': groups, 'pageId': pageTarget.getParameters().id, targetType: target.getType()};
+    var params = {'pagePath': pagePath, 'grantedUsers': grantedUsers, 'grantedGroups': grantedGroups, 'excludedUsers': excludedUsers, 'excludedGroups': excludedGroups, 
+    		      'pageId': pageTarget.getParameters().id, targetType: target.getType()};
     
     var serverMessage = new org.ametys.servercomm.ServerMessage(org.ametys.web.pageaccess.RemoveAccess._pluginName, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.pageaccess.RemoveAccess._callBack, this, params);
     org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTool.i18n.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTool.i18n.js	(revision 27955)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTool.i18n.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -211,13 +211,21 @@
 	    target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('sitemap', {'id': this._params.pageId}));
     }
 	
-	if (node.attributes.type == 'user')
+	if (node.attributes.type == 'granted-user')
+	{
+		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('granted-user', {'login': node.attributes.login, 'name': node.attributes.name}));
+	}
+	else if (node.attributes.type == 'granted-group')
+	{
+		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('granted-group', {'id': node.attributes.id, 'name': node.attributes.name}));
+	}
+	else if (node.attributes.type == 'excluded-user')
 	{
-		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('user', {'login': node.attributes.login, 'name': node.attributes.name}));
+		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('excluded-user', {'login': node.attributes.login, 'name': node.attributes.name}));
 	}
-	else if (node.attributes.type == 'group')
+	else if (node.attributes.type == 'excluded-group')
 	{
-		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('group', {'id': node.attributes.id, 'name': node.attributes.name}));
+		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('excluded-group', {'id': node.attributes.id, 'name': node.attributes.name}));
 	}
 	
 	var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, [target]);
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.i18n.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.i18n.js	(revision 27955)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.i18n.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -146,7 +146,7 @@
 			var params = {"siteName": context.parameters.siteName, "language": RegExp.$1};
 			if (RegExp.$2 != null &amp;&amp; RegExp.$2 != '')
 		    {
-			    params.path = RegExp.$2;
+			    params.path = (RegExp.$2).substring(1); // remove "/"
 		    }
 			var serverMessage = new org.ametys.servercomm.ServerMessage('web', 'page/path-to-id.xml', params, org.ametys.servercomm.ServerComm.PRIORITY_SYNCHRONOUS, null, this, null);
 			var response = org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
@@ -253,7 +253,7 @@
 
 org.ametys.web.pageaccess.PageAccessTreePanel.prototype._createTooltip = function(attributes)
 {
-    if ((attributes.type == 'user' || attributes.type == 'group') &amp;&amp; attributes.unknown != 'true')
+    if ((attributes.type == 'granted-user' || attributes.type == 'granted-group' || attributes.type == 'excluded-user' || attributes.type == 'excluded-group') &amp;&amp; attributes.unknown != 'true')
     {
         var html = "<div class='right-assign-tooltip'>";
         html += "&lt;b&gt;" + attributes.name + "&lt;/b&gt;&lt;br/&gt;"
@@ -292,6 +292,6 @@
     var re = new RegExp('.*' + val + '.*', 'i');
     
     this.filter.filterBy( function(node) {
-        return re.test(node.text) || node.attributes.type == 'granted-users' || node.attributes.type == 'granted-groups';
+        return re.test(node.text) || node.attributes.type == 'granted-users' || node.attributes.type == 'granted-groups' || node.attributes.type == 'excluded-users' || node.attributes.type == 'excluded-groups';
     });
 }
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.i18n.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.i18n.js	(revision 27955)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.i18n.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -24,9 +24,13 @@
     this._nodeTags = {};
     this._nodeTags['granted-users'] = '';
     this._nodeTags['granted-groups'] = '';
-    this._nodeTags['user'] = '';
-    this._nodeTags['group'] = '';
-    
+    this._nodeTags['granted-user'] = '';
+    this._nodeTags['granted-group'] = '';
+    this._nodeTags['excluded-users'] = '';
+    this._nodeTags['excluded-groups'] = '';
+    this._nodeTags['excluded-user'] = '';
+    this._nodeTags['excluded-group'] = '';
+	
     org.ametys.web.pageaccess.PageAccessXmlLoader.superclass.processResponse.call(this, response, node, callback);
 }
 
@@ -37,10 +41,12 @@
     var allowAnyConnectedUser = false;
     var allowAnyConnectedUserInherited = false;
     var hasLimitations = false;
+    var hasExcludedUsers = false;
     var childNodes = node.childNodes;
+
     for (var i = 0; i &lt; childNodes.length; i++)
     {
-        if (childNodes[i].tagName == 'allow-any-connected-user' &amp;&amp; childNodes[i][org.ametys.servercomm.ServerComm.xmlTextContent] == 'true')
+    	if (childNodes[i].tagName == 'allow-any-connected-user' &amp;&amp; childNodes[i][org.ametys.servercomm.ServerComm.xmlTextContent] == 'true')
         {
             allowAnyConnectedUser = true;
             allowAnyConnectedUserInherited = childNodes[i].getAttribute('inherited') == 'true';
@@ -49,18 +55,34 @@
         {
             hasLimitations = true;
         }
+        
+        if (childNodes[i].tagName == 'excluded-groups' || childNodes[i].tagName == 'excluded-users')
+        {
+        	hasExcludedUsers = true;
+        }
     }
     
     if (allowAnyConnectedUser)
     {
-        var text = "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER'/>";
+    	var text = "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER'/>";
+    	var textNode = null;
         if (allowAnyConnectedUserInherited)
         {
             text = text + "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER_INHERITED'/>";
+            textNode = new Ext.tree.TreeNode({text: text, cls: 'inherit', icon: getPluginResourcesUrl('web') + '/img/pageaccess/any_user_16.png'});
         }
-        // Limited to any connected user.
-        var textNode = new Ext.tree.TreeNode({text: text, icon: getPluginResourcesUrl('web') + '/img/pageaccess/any_user_16.png'});
+        else
+    	{
+	        // Limited to any connected user.
+	        textNode = new Ext.tree.TreeNode({text: text, icon: getPluginResourcesUrl('web') + '/img/pageaccess/any_user_16.png'});
+    	}
+        
         nodes.push(textNode);
+        
+        if (hasExcludedUsers)
+    	{
+        	nodes.push(org.ametys.web.pageaccess.PageAccessXmlLoader.superclass.parseXml.call(this, node));
+    	}
     }
     else if (hasLimitations)
     {
@@ -99,7 +121,25 @@
         attr.loaded = true;
         attr.expanded = true;
     }
-    else if (attr.tagName == 'user')
+    else if (attr.tagName == 'excluded-users')
+	{
+        attr.text = " &lt;b&gt;<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_USERS"/>&lt;/b&gt;",
+        attr.type = 'excluded-users';
+        attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/excluded_user_list_16.png';
+        attr.leaf = false;
+        attr.loaded = true;
+        attr.expanded = true;
+	}
+    else if (attr.tagName == 'excluded-groups')
+	{
+        attr.text = " &lt;b&gt;<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_GROUPS"/>&lt;/b&gt;",
+        attr.type = 'excluded-groups';
+        attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/excluded_group_list_16.png';
+        attr.leaf = false;
+        attr.loaded = true;
+        attr.expanded = true;
+	}
+    else if (attr.tagName == 'granted-user')
     { 
     	if (attr.unknown == 'true')
     	{
@@ -112,7 +152,7 @@
     		attr.icon = getPluginResourcesUrl('core') + '/img/profiles/user_16.png';
     	}
         
-        attr.type = 'user';
+        attr.type = 'granted-user';
         attr.leafSort = true; // attribute for sorting
         
         if (attr.inherited == 'true')
@@ -122,7 +162,7 @@
         
         attr.leaf = true;
     }
-    else if (attr.tagName == 'group')
+    else if (attr.tagName == 'granted-group')
     { 
     	if (attr.unknown == 'true')
     	{
@@ -135,7 +175,53 @@
     		attr.icon = getPluginResourcesUrl('core') + '/img/profiles/group_16.png';
     	}
     	
-        attr.type = 'group';
+        attr.type = 'granted-group';
+        attr.leafSort = true; // attribute for sorting
+        
+        if (attr.inherited == 'true')
+        {
+            attr.cls = 'inherit';
+        }
+        
+        attr.leaf = true;
+    }
+    else if (attr.tagName == 'excluded-user')
+    { 
+    	if (attr.unknown == 'true')
+    	{
+    		attr.text = ' &lt;i&gt;' + "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER"/>" + ' (' + attr.login + ')&lt;/i&gt;';
+    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/user_unknown_16.png';
+    	}
+    	else
+    	{
+    		attr.text = ' ' + attr.name + ' (' + attr.login + ')';
+    		attr.icon = getPluginResourcesUrl('core') + '/img/profiles/excluded_user_16.png';
+    	}
+        
+        attr.type = 'excluded-user';
+        attr.leafSort = true; // attribute for sorting
+        
+        if (attr.inherited == 'true')
+        {
+            attr.cls = 'inherit';
+        }
+        
+        attr.leaf = true;
+    }
+    else if (attr.tagName == 'excluded-group')
+    { 
+    	if (attr.unknown == 'true')
+    	{
+    		attr.text = ' &lt;i&gt;' + "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP"/>" + ' (' + attr.id + ')&lt;/i&gt;';
+    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/group_unknown_16.png';
+    	}
+    	else
+    	{
+    		attr.text = ' ' + attr.name;
+    		attr.icon = getPluginResourcesUrl('core') + '/img/profiles/excluded_ group_16.png';
+    	}
+    	
+        attr.type = 'excluded-group';
         attr.leafSort = true; // attribute for sorting
         
         if (attr.inherited == 'true')
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js	(revision 27993)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js	(working copy)
@@ -72,7 +72,7 @@
     var allowAnyConnectedUserCount = parseInt(params['allow-any-connected-user-count']);
     var inheritedCount = parseInt(params['inherited-rectriction-count']);
     
-    if (allowAnyConnectedUserCount > 0 && inheritedCount == 0)
+    if (allowAnyConnectedUserCount > 0 && inheritedCount == 0 && this.getConfiguration()['enable-on-allow-any-user'] != '')
     {
         // If any connected user is allowed on the page itself (i.e. if not inherited), disable the button.
         this.getElements().each(function(element) {element.disable();});
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/SetAnyUserButton.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/SetAnyUserButton.js	(revision 27955)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/SetAnyUserButton.js	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2012 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -118,9 +118,10 @@
     
     var allowAnyConnectedUserCount = parseInt(params['allow-any-connected-user-count']);
     var inheritedCount = parseInt(params['inherited-rectriction-count']);
+    var negativeRestrictionPage = parseInt(params['has-negative-restriction-count']);
     
-    var toggledState = allowAnyConnectedUserCount > 0;
-    var disabledState = allowAnyConnectedUserCount > 0 && inheritedCount > 0;
+    var toggledState = allowAnyConnectedUserCount > 0 && inheritedCount == 0;
+    var disabledState = allowAnyConnectedUserCount > 0 && inheritedCount > 0 && negativeRestrictionPage == 0;
     
     this.updateTooltip(this.getConfiguration()["default-description"], null, null);
     
Index: main/plugin-web/ribbon/cms-ribbon-pageaccess.xml
===================================================================
--- main/plugin-web/ribbon/cms-ribbon-pageaccess.xml	(revision 27955)
+++ main/plugin-web/ribbon/cms-ribbon-pageaccess.xml	(working copy)
@@ -25,6 +25,8 @@
                     <medium>
                         <control id="org.ametys.web.pageaccess.AddUsers"/>
                         <control id="org.ametys.web.pageaccess.AddGroups"/>
+                        <control id="org.ametys.web.pageaccess.ExcludeUsers"/>
+                        <control id="org.ametys.web.pageaccess.ExcludeGroups"/>
                         <control id="org.ametys.web.pageaccess.SetAnyUser"/>
                         <control id="org.ametys.web.pageaccess.RemoveAccess"/>
                     </medium>
@@ -32,6 +34,8 @@
                         <layout size="small">
                             <control id="org.ametys.web.pageaccess.AddUsers"/>
                             <control id="org.ametys.web.pageaccess.AddGroups"/>
+                            <control id="org.ametys.web.pageaccess.ExcludeUsers"/>
+                            <control id="org.ametys.web.pageaccess.ExcludeGroups"/>
                             <control id="org.ametys.web.pageaccess.SetAnyUser"/>
                             <control id="org.ametys.web.pageaccess.RemoveAccess"/>
                         </layout>
Index: main/plugin-web/sitemap-back.xmap
===================================================================
--- main/plugin-web/sitemap-back.xmap	(revision 27955)
+++ main/plugin-web/sitemap-back.xmap	(working copy)
@@ -1397,6 +1397,17 @@
                 </map:act>
             </map:match>
             
+            <map:match pattern="*/pageaccess/exclude">
+                <map:act type="set-page-access">
+                    <map:parameter name="mode" value="exclude"/>
+                    <map:parameter name="site" value="{1}"/>
+                    <map:parameter name="page" value="{request-param:pagePath}"/>
+                    
+                    <map:generate type="action-result"/>
+                    <map:serialize type="xml"/>
+                </map:act>
+            </map:match>
+            
             <map:match pattern="*/pageaccess/remove">
                 <map:act type="set-page-access">
                     <map:parameter name="mode" value="remove"/>
Index: main/plugin-web/src/org/ametys/web/clientsideelement/PageAccessClientSideElement.java
===================================================================
--- main/plugin-web/src/org/ametys/web/clientsideelement/PageAccessClientSideElement.java	(revision 27955)
+++ main/plugin-web/src/org/ametys/web/clientsideelement/PageAccessClientSideElement.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -58,6 +58,7 @@
         int publicPageCount = 0;
         int allowAnyConnectedUserCount = 0;
         int inheritedRestrictionCount = 0;
+        int negativeRestrictionPage = 0;
         
         List<String> pagesId = (List<String>) parameters.get("targets");
         for (String pageId : pagesId)
@@ -77,11 +78,18 @@
                         allowAnyConnectedUserCount++;
                         results.put("allow-any-connected-user-page-" + allowAnyConnectedUserCount, new I18nizableText(pageId));
                     }
+                    
                     if (accessInfo.isInherited())
                     {
                         inheritedRestrictionCount++;
                         results.put("inherited-rectriction-page-" + inheritedRestrictionCount, new I18nizableText(pageId));
                     }
+                    
+                    if (accessInfo.hasNegativeRestriction())
+                    {
+                        negativeRestrictionPage++;
+                        results.put("has-negative-restriction-page-" + negativeRestrictionPage, new I18nizableText(pageId));
+                    }
                 }
                 else
                 {
@@ -95,6 +103,7 @@
         results.put("private-page-size", new I18nizableText(Integer.toString(privatePageCount)));
         results.put("allow-any-connected-user-count", new I18nizableText(Integer.toString(allowAnyConnectedUserCount)));
         results.put("inherited-rectriction-count", new I18nizableText(Integer.toString(inheritedRestrictionCount)));
+        results.put("has-negative-restriction-count", new I18nizableText(Integer.toString(negativeRestrictionPage)));
         
         return results;
     }
Index: main/plugin-web/src/org/ametys/web/pageaccess/PageAccessGenerator.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/PageAccessGenerator.java	(revision 27955)
+++ main/plugin-web/src/org/ametys/web/pageaccess/PageAccessGenerator.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 package org.ametys.web.pageaccess;
 
 import java.io.IOException;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -144,7 +145,7 @@
 
     /**
      * Generate the access info on a given pages container (page or sitemap).
-     * @param container the container of which to generate the info.
+     * @param container the container from which the info is generated.
      * @param accessInfo the access information.
      * @throws SAXException if an error occurs.
      */
@@ -152,7 +153,7 @@
     {
         if (accessInfo != null && accessInfo.isRestricted())
         {
-            MetadataAwarePagesContainer restrictedPage = accessInfo.getPage();
+            MetadataAwarePagesContainer restrictedPage = accessInfo.getPageForPositiveRestriction();
             String title = "";
             String type = "";
             String restrictedPath = "";
@@ -169,72 +170,194 @@
                 type = "sitemap";
             }
             
-            if (accessInfo.allowAnyConnectedUser())
+            _saxAllowAnyConnectedUser(accessInfo);
+            
+            _saxGrantedUsers(container, accessInfo, title, type, restrictedPath);
+            _saxGrantedGroups(container, accessInfo, title, type, restrictedPath);
+            _saxExcludedUsers(container, accessInfo);
+            _saxExcludedGroups(container, accessInfo);
+        }
+    }
+    
+    private void _saxAllowAnyConnectedUser(PageAccessInfo accessInfo) throws SAXException
+    {
+        if (accessInfo.allowAnyConnectedUser())
+        {
+            AttributesImpl atts = new AttributesImpl();
+            atts.addCDATAAttribute("inherited", String.valueOf(accessInfo.isInherited()));
+            XMLUtils.createElement(contentHandler, "allow-any-connected-user", atts, "true");
+        }
+    }
+    
+    private void _saxGrantedUsers(PagesContainer container, PageAccessInfo accessInfo, String title, String type, String restrictedPath) throws SAXException
+    {
+        if (!accessInfo.getGrantedUsers().isEmpty())
+        {
+            XMLUtils.startElement(contentHandler, "granted-users");
+            for (String login : accessInfo.getGrantedUsers())
             {
-                AttributesImpl atts = new AttributesImpl();
-                atts.addCDATAAttribute("inherited", String.valueOf(accessInfo.isInherited()));
-                XMLUtils.createElement(contentHandler, "allow-any-connected-user", atts, "true");
+                User user = _usersManager.getUser(login);
+                if (user != null)
+                {
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("login", login);
+                    atts.addCDATAAttribute("name", user.getFullName());
+                    atts.addCDATAAttribute("inherited", String.valueOf(accessInfo.isInherited()));
+                    atts.addCDATAAttribute("page", title);
+                    atts.addCDATAAttribute("path", restrictedPath);
+                    atts.addCDATAAttribute("type", type);
+                    XMLUtils.createElement(contentHandler, "granted-user", atts);
+                }
+                else
+                {
+                    getLogger().warn("The page " + container.getPath() + " references the user " + login + " as granted but it can't be found by the UsersManager.");
+                    
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("login", login);
+                    atts.addCDATAAttribute("unknown", "true");
+                    XMLUtils.createElement(contentHandler, "granted-user", atts);
+                }
             }
-            
-            if (!accessInfo.getGrantedUsers().isEmpty())
+            XMLUtils.endElement(contentHandler, "granted-users");
+        }
+    }
+    
+    private void _saxGrantedGroups(PagesContainer container, PageAccessInfo accessInfo, String title, String type, String restrictedPath) throws SAXException
+    {
+        if (!accessInfo.getGrantedGroups().isEmpty())
+        {
+            XMLUtils.startElement(contentHandler, "granted-groups");
+            for (String groupId : accessInfo.getGrantedGroups())
             {
-                XMLUtils.startElement(contentHandler, "granted-users");
-                for (String login : accessInfo.getGrantedUsers())
+                Group group = _groupsManager.getGroup(groupId);
+                if (group != null)
                 {
-                    User user = _usersManager.getUser(login);
-                    if (user != null)
-                    {
-                        AttributesImpl atts = new AttributesImpl();
-                        atts.addCDATAAttribute("login", login);
-                        atts.addCDATAAttribute("name", user.getFullName());
-                        atts.addCDATAAttribute("inherited", String.valueOf(accessInfo.isInherited()));
-                        atts.addCDATAAttribute("page", title);
-                        atts.addCDATAAttribute("path", restrictedPath);
-                        atts.addCDATAAttribute("type", type);
-                        XMLUtils.createElement(contentHandler, "user", atts);
-                    }
-                    else
-                    {
-                        getLogger().warn("The page " + container.getPath() + " references the user " + login + " as granted but it can't be found by the UsersManager.");
-                        
-                        AttributesImpl atts = new AttributesImpl();
-                        atts.addCDATAAttribute("login", login);
-                        atts.addCDATAAttribute("unknown", "true");
-                        XMLUtils.createElement(contentHandler, "user", atts);
-                    }
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("id", groupId);
+                    atts.addCDATAAttribute("name", group.getLabel());
+                    atts.addCDATAAttribute("inherited", String.valueOf(accessInfo.isInherited()));
+                    atts.addCDATAAttribute("page", title);
+                    atts.addCDATAAttribute("path", restrictedPath);
+                    atts.addCDATAAttribute("type", type);
+                    XMLUtils.createElement(contentHandler, "granted-group", atts);
                 }
-                XMLUtils.endElement(contentHandler, "granted-users");
+                else
+                {
+                    getLogger().warn("The page " + container.getPath() + " references the group " + groupId + " as granted but it can't be found by the GroupsManager.");
+                    
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("id", groupId);
+                    atts.addCDATAAttribute("unknown", "true");
+                    XMLUtils.createElement(contentHandler, "granted-group", atts);
+                }
             }
-            
-            if (!accessInfo.getGrantedGroups().isEmpty())
+            XMLUtils.endElement(contentHandler, "granted-groups");
+        }
+    }
+
+    private void _saxExcludedUsers(PagesContainer container, PageAccessInfo accessInfo) throws SAXException
+    {
+        Map<String, PagesContainer> excludedUsers = accessInfo.getExcludedUsers();
+        if (!excludedUsers.isEmpty())
+        {
+            XMLUtils.startElement(contentHandler, "excluded-users");
+            for (String login : excludedUsers.keySet())
             {
-                XMLUtils.startElement(contentHandler, "granted-groups");
-                for (String groupId : accessInfo.getGrantedGroups())
+                // Get page info from accessInfo
+                PagesContainer restrictedPage = excludedUsers.get(login);
+                String title = "";
+                String type = "";
+                String restrictedPath = "";
+                if (restrictedPage instanceof Page)
                 {
-                    Group group = _groupsManager.getGroup(groupId);
-                    if (group != null)
-                    {
-                        AttributesImpl atts = new AttributesImpl();
-                        atts.addCDATAAttribute("id", groupId);
-                        atts.addCDATAAttribute("name", group.getLabel());
-                        atts.addCDATAAttribute("inherited", String.valueOf(accessInfo.isInherited()));
-                        atts.addCDATAAttribute("page", title);
-                        atts.addCDATAAttribute("path", restrictedPath);
-                        atts.addCDATAAttribute("type", type);
-                        XMLUtils.createElement(contentHandler, "group", atts);
-                    }
-                    else
-                    {
-                        getLogger().warn("The page " + container.getPath() + " references the group " + groupId + " as granted but it can't be found by the GroupsManager.");
-                        
-                        AttributesImpl atts = new AttributesImpl();
-                        atts.addCDATAAttribute("id", groupId);
-                        atts.addCDATAAttribute("unknown", "true");
-                        XMLUtils.createElement(contentHandler, "group", atts);
-                    }
+                    title = ((Page) restrictedPage).getTitle();
+                    restrictedPath = "/" + restrictedPage.getSitemapName() + "/" + restrictedPage.getPathInSitemap();
+                    type = "page";
                 }
-                XMLUtils.endElement(contentHandler, "granted-groups");
+                else if (restrictedPage instanceof Sitemap)
+                {
+                    title = restrictedPage.getSitemapName();
+                    restrictedPath = "/" + restrictedPage.getSitemapName();
+                    type = "sitemap";
+                }
+
+                // SAX it
+                User user = _usersManager.getUser(login);
+                if (user != null)
+                {
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("login", login);
+                    atts.addCDATAAttribute("name", user.getFullName());
+                    atts.addCDATAAttribute("inherited", Boolean.toString(!container.equals(restrictedPage)));
+                    atts.addCDATAAttribute("page", title);
+                    atts.addCDATAAttribute("path", restrictedPath);
+                    atts.addCDATAAttribute("type", type);
+                    XMLUtils.createElement(contentHandler, "excluded-user", atts);
+                }
+                else
+                {
+                    getLogger().warn("The page " + container.getPath() + " references the user " + login + " as excluded but it can't be found by the UsersManager.");
+                    
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("login", login);
+                    atts.addCDATAAttribute("unknown", "true");
+                    XMLUtils.createElement(contentHandler, "excluded-user", atts);
+                }
+            }
+            XMLUtils.endElement(contentHandler, "excluded-users");
+        }
+    }
+    
+    private void _saxExcludedGroups(PagesContainer container, PageAccessInfo accessInfo) throws SAXException
+    {
+        Map<String, PagesContainer> excludedGroups = accessInfo.getExcludedGroups();
+        if (!excludedGroups.isEmpty())
+        {
+            XMLUtils.startElement(contentHandler, "excluded-groups");
+            for (String groupId : excludedGroups.keySet())
+            {
+                // Get page info from accessInfo
+                PagesContainer restrictedPage = excludedGroups.get(groupId);
+                String title = "";
+                String type = "";
+                String restrictedPath = "";
+                if (restrictedPage instanceof Page)
+                {
+                    title = ((Page) restrictedPage).getTitle();
+                    restrictedPath = "/" + restrictedPage.getSitemapName() + "/" + restrictedPage.getPathInSitemap();
+                    type = "page";
+                }
+                else if (restrictedPage instanceof Sitemap)
+                {
+                    title = restrictedPage.getSitemapName();
+                    restrictedPath = "/" + restrictedPage.getSitemapName();
+                    type = "sitemap";
+                }
+               
+                // SAX it
+                Group group = _groupsManager.getGroup(groupId);
+                if (group != null)
+                {
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("id", groupId);
+                    atts.addCDATAAttribute("name", group.getLabel());
+                    atts.addCDATAAttribute("inherited", Boolean.toString(!container.equals(restrictedPage)));
+                    atts.addCDATAAttribute("page", title);
+                    atts.addCDATAAttribute("path", restrictedPath);
+                    atts.addCDATAAttribute("type", type);
+                    XMLUtils.createElement(contentHandler, "excluded-group", atts);
+                }
+                else
+                {
+                    getLogger().warn("The page " + container.getPath() + " references the group " + groupId + " as excluded but it can't be found by the GroupsManager.");
+                    
+                    AttributesImpl atts = new AttributesImpl();
+                    atts.addCDATAAttribute("id", groupId);
+                    atts.addCDATAAttribute("unknown", "true");
+                    XMLUtils.createElement(contentHandler, "excluded-group", atts);
+                }
             }
+            XMLUtils.endElement(contentHandler, "excluded-groups");
         }
     }
 }
Index: main/plugin-web/src/org/ametys/web/pageaccess/PageAccessInfo.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/PageAccessInfo.java	(revision 27955)
+++ main/plugin-web/src/org/ametys/web/pageaccess/PageAccessInfo.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -15,10 +15,13 @@
  */
 package org.ametys.web.pageaccess;
 
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import org.ametys.web.repository.page.MetadataAwarePagesContainer;
+import org.ametys.web.repository.page.PagesContainer;
 
 /**
  * Access information.
@@ -31,6 +34,12 @@
     /** The granted groups IDs. */
     protected Set<String> _grantedGroups;
     
+    /** The excluded users logins mapped with their page container (either a page or a sitemap). */
+    protected Map<String, PagesContainer> _excludedUsers;
+
+    /** The excluded groups IDs mapped with their page container (either a page or a sitemap). */
+    protected Map<String, PagesContainer> _excludedGroups;
+    
     /** True to allow any connected user, false otherwise. */
     protected boolean _allowAnyConnectedUser;
     
@@ -47,6 +56,8 @@
     {
         _grantedUsers = new HashSet<String>();
         _grantedGroups = new HashSet<String>();
+        _excludedUsers = new HashMap<String, PagesContainer>();
+        _excludedGroups = new HashMap<String, PagesContainer>();
         _allowAnyConnectedUser = false;
         _inherited = false;
     }
@@ -86,6 +97,72 @@
     {
         this._grantedGroups = grantedGroups;
     }
+    
+    /**
+     * Get the excluded users ids.
+     * @return the excluded users ids
+     */
+    public Map<String, PagesContainer> getExcludedUsers()
+    {
+        return _excludedUsers;
+    }
+    
+    /**
+     * Set the excluded users
+     * @param excludedUsers the excluded users
+     * @param pageOrSitemap the page or sitemap 
+     * @param inherited true if the exclusion is inherited, false otherwise
+     */
+    public void addExcludedUsers(Set<String> excludedUsers, PagesContainer pageOrSitemap, boolean inherited)
+    {
+        for (String user : excludedUsers)
+        {
+            if (inherited)
+            {
+                if (!this._excludedUsers.containsKey(user))
+                {
+                    this._excludedUsers.put(user, pageOrSitemap);
+                }
+            }
+            else
+            {
+                this._excludedUsers.put(user, pageOrSitemap);
+            }
+        }
+    }
+    
+    /**
+     * Get the excluded groups ids.
+     * @return the excluded groups ids
+     */
+    public Map<String, PagesContainer> getExcludedGroups()
+    {
+        return _excludedGroups;
+    }
+    
+    /**
+     * Set the excluded groups
+     * @param excludedGroups the excluded groups
+     * @param pageOrSitemap the page or sitemap
+     * @param inherited true if the exclusion is inherited, false otherwise
+     */
+    public void addExcludedGroups(Set<String> excludedGroups, PagesContainer pageOrSitemap, boolean inherited)
+    {
+        for (String group : excludedGroups)
+        {
+            if (inherited)
+            {
+                if (!this._excludedGroups.containsKey(group))
+                {
+                    this._excludedGroups.put(group, pageOrSitemap);
+                }
+            }
+            else
+            {
+                this._excludedGroups.put(group, pageOrSitemap);
+            }
+        }
+    }
 
     /**
      * Test if any connected user is allowed.
@@ -109,7 +186,7 @@
      * Get the associated page
      * @return the limited page
      */
-    public MetadataAwarePagesContainer getPage ()
+    public MetadataAwarePagesContainer getPageForPositiveRestriction ()
     {
         return _page;
     }
@@ -118,7 +195,7 @@
      * Set the associated page
      * @param page the page
      */
-    public void setPage (MetadataAwarePagesContainer page)
+    public void setPageForPositiveRestriction (MetadataAwarePagesContainer page)
     {
         this._page = page;
     }
@@ -147,6 +224,33 @@
      */
     public boolean isRestricted()
     {
-        return !_grantedUsers.isEmpty() || !_grantedGroups.isEmpty() || _allowAnyConnectedUser; 
+        return _allowAnyConnectedUser || !_grantedUsers.isEmpty() || !_grantedGroups.isEmpty() || !_excludedUsers.isEmpty() || !_excludedGroups.isEmpty();
+    }
+    
+    /**
+     * Determines if the page access has a positive restriction
+     * @return true if if the page access has a positive restriction
+     */
+    public boolean hasGrantedUsersOrGroups()
+    {
+        return  !_grantedUsers.isEmpty() || !_grantedGroups.isEmpty();
+    }
+    
+    /**
+     * Determines if the page access has a positive restriction
+     * @return true if if the page access has a positive restriction
+     */
+    public boolean hasPositiveRestriction()
+    {
+        return _allowAnyConnectedUser || hasGrantedUsersOrGroups();
+    }
+    
+    /**
+     * Determines if the page access has a negative restriction
+     * @return true if if the page access has a negative restriction
+     */
+    public boolean hasNegativeRestriction ()
+    {
+        return !_excludedUsers.isEmpty() || !_excludedGroups.isEmpty();
     }
 }
Index: main/plugin-web/src/org/ametys/web/pageaccess/PageAccessManager.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/PageAccessManager.java	(revision 27955)
+++ main/plugin-web/src/org/ametys/web/pageaccess/PageAccessManager.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -51,11 +51,19 @@
     /** Granted groups metadata. */
     public static final String GRANTED_GROUPS_METADATA = "granted-groups";
     
+    /** Excluded users metadata. */
+    public static final String EXCLUDED_USERS_METADATA = "excluded-users";
+
+    /** Excluded groups metadata. */
+    public static final String EXCLUDED_GROUPS_METADATA = "excluded-groups";
+    
     /** "Grant any connected user" metadata. */
     public static final String GRANT_ANY_USER_METADATA = "grant-any-user";
-    
+
+    /** The groups manager */
     private GroupsManager _groupsManager;
     
+    /** The Avalon context */
     private Context _context;
     
     @Override
@@ -86,7 +94,9 @@
             request.setAttribute("PageAccessInfo.cache", cache);
         }
         
-        return _getAccessInfo(pageOrSitemap, false, cache);
+        PageAccessInfo accessInfo = _getAccessInfo(pageOrSitemap, false, cache);
+        
+        return accessInfo;
     }
     
     private PageAccessInfo _getAccessInfo(MetadataAwarePagesContainer pageOrSitemap, boolean inherited, Map<String, PageAccessInfo> cache)
@@ -97,14 +107,38 @@
             return cache.get(id);
         }
         
+        PageAccessInfo accessInfo = _getPageAccessInfo(pageOrSitemap, inherited);
+        
+        AmetysObject parentObject = pageOrSitemap.getParent();
+        
+        if (parentObject instanceof MetadataAwarePagesContainer && !accessInfo.hasPositiveRestriction())
+        {
+            PageAccessInfo parentAccessInfo = _getAccessInfo((MetadataAwarePagesContainer) parentObject, true, cache);
+            if (accessInfo.hasNegativeRestriction())
+            {
+                parentAccessInfo.addExcludedUsers(accessInfo.getExcludedUsers().keySet(), pageOrSitemap, inherited);
+                parentAccessInfo.addExcludedGroups(accessInfo.getExcludedGroups().keySet(), pageOrSitemap, inherited);
+            }
+            
+            accessInfo = parentAccessInfo;
+        }
+        
+        cache.put(id, accessInfo);
+        return accessInfo;
+    }
+
+    private PageAccessInfo _getPageAccessInfo(MetadataAwarePagesContainer pageOrSitemap, boolean inherited)
+    {
         PageAccessInfo accessInfo = new PageAccessInfo();
         accessInfo.setInherited(inherited);
-        accessInfo.setPage(pageOrSitemap);
+        accessInfo.setPageForPositiveRestriction(pageOrSitemap);
         
         CompositeMetadata meta = pageOrSitemap.getMetadataHolder();
         
         String[] grantedUsers = meta.getStringArray(PageAccessManager.GRANTED_USERS_METADATA, new String[0]);
         String[] grantedGroups = meta.getStringArray(PageAccessManager.GRANTED_GROUPS_METADATA, new String[0]);
+        String[] excludedUsers = meta.getStringArray(PageAccessManager.EXCLUDED_USERS_METADATA, new String[0]);
+        String[] excludedGroups = meta.getStringArray(PageAccessManager.EXCLUDED_GROUPS_METADATA, new String[0]);
         boolean allowAnyConnectedUser = meta.getBoolean(GRANT_ANY_USER_METADATA, false);
         
         if (grantedUsers != null && grantedUsers.length > 0)
@@ -122,18 +156,24 @@
                 accessInfo.getGrantedGroups().add(grantedGroup);
             }
         }
-        
-        accessInfo.setAllowAnyConnectedUser(allowAnyConnectedUser);
-        
-        AmetysObject parentObject = pageOrSitemap.getParent();
-        
-        if (!accessInfo.isRestricted() && parentObject instanceof MetadataAwarePagesContainer)
+
+        if (excludedUsers != null && excludedUsers.length > 0)
         {
-            accessInfo = _getAccessInfo((MetadataAwarePagesContainer) parentObject, true, cache);
+            for (String excludedUser : excludedUsers)
+            {
+                accessInfo.getExcludedUsers().put(excludedUser, pageOrSitemap);
+            }
         }
         
-        cache.put(id, accessInfo);
+        if (excludedGroups != null && excludedGroups.length > 0)
+        {
+            for (String excludedGroup : excludedGroups)
+            {
+                accessInfo.getExcludedGroups().put(excludedGroup, pageOrSitemap);
+            }
+        }
         
+        accessInfo.setAllowAnyConnectedUser(allowAnyConnectedUser);
         return accessInfo;
     }
     
@@ -158,49 +198,9 @@
      */
     public boolean hasRight(MetadataAwarePagesContainer pageOrSitemap, String login)
     {
-        if (accept(pageOrSitemap))
-        {
-            return true;
-        }
-        
-        PageAccessInfo accessInfo = getAccessInfo(pageOrSitemap);
-        
-        if (!accessInfo.isRestricted())
-        {
-            // Page is not restricted
-            return true;
-        }
-        else if (login == null)
-        {
-            // Page is restricted and the user is not logged in
-            return false;
-        }
-        
-        // First test if any connected user is allowed.
-        if (accessInfo.allowAnyConnectedUser())
-        {
-            return true;
-        }
-        
-        // Then, check with the user assignations.
-        if (accessInfo.getGrantedUsers().contains(login))
-        {
-            return true;
-        }
-        
-        // Finally, check with the groups assignations.
-        Set<String> groups  = _groupsManager.getUserGroups(login);
-        for (String groupId : accessInfo.getGrantedGroups())
-        {
-            if (groups.contains(groupId))
-            {
-                return true;
-            }
-        }
-        
-        return false;
+        return accept(pageOrSitemap) ? true : _hasRight(pageOrSitemap, login);
     }
-    
+
     /**
      * Test if access on this page is to be granted to everybody, whether it's limited or not.
      * Used to grant access on some special pages like account creation, even when the whole site is restricted.
@@ -232,4 +232,117 @@
         return false;
     }
     
+    /**
+     * Determines if a user has the right to see the a given page
+     * @param login the login of the user
+     * @param pageOrSitemap the page to be tested
+     * @return true if the user can see the page, false otherwise
+     */
+    private boolean _hasRight(MetadataAwarePagesContainer pageOrSitemap, String login)
+    {
+        PageAccessInfo accessInfo = getAccessInfo(pageOrSitemap);
+        
+        Set<String> excludedUsers = accessInfo.getExcludedUsers().keySet();
+        Set<String> excludedGroups = accessInfo.getExcludedGroups().keySet();
+        Set<String> grantedUsers = accessInfo.getGrantedUsers();
+        Set<String> grantedGroups = accessInfo.getGrantedGroups();
+        Set<String> groups  = _groupsManager.getUserGroups(login);        
+        AmetysObject parentObject = pageOrSitemap.getParent();
+        
+        if (accessInfo.hasNegativeRestriction())
+        {
+            // No user connected
+            if (login == null)
+            {
+                return false;
+            }
+            
+            // Check the exclusions.
+            if (excludedUsers.contains(login))
+            {
+                return false;
+            }
+            
+            for (String groupId : excludedGroups)
+            {
+                if (groups.contains(groupId))
+                {
+                    return false;
+                }
+            }
+            
+            if (accessInfo.hasGrantedUsersOrGroups())
+            {
+                return _isGrantedAccess(login, grantedUsers, grantedGroups);
+            }
+            
+            // Then test if any connected user is allowed.
+            if (accessInfo.allowAnyConnectedUser())
+            {
+                return true;
+            }
+                
+            if (parentObject instanceof MetadataAwarePagesContainer)
+            {
+                return _hasRight((MetadataAwarePagesContainer) parentObject, login);
+            }
+            else
+            {
+                // no parent pages
+                return true;
+            }
+        }
+        else
+        {
+            if (accessInfo.hasPositiveRestriction())
+            {
+                return _isGrantedAccess(login, grantedUsers, grantedGroups);
+            }
+            
+            // Then test if any connected user is allowed.
+            if (accessInfo.allowAnyConnectedUser())
+            {
+                return login != null ? true : false;
+            }
+            
+            if (parentObject instanceof MetadataAwarePagesContainer)
+            {
+                return _hasRight((MetadataAwarePagesContainer) parentObject, login);
+            }
+            else
+            {
+                // no parent pages
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Checks if the user has access to the page or not
+     * @param login the login of the user
+     * @param grantedUsers the list of granted users
+     * @param grantedGroups the list of granted groups
+     * @return true if the user is granted access, false otherwise
+     */
+    private boolean _isGrantedAccess(String login, Set<String> grantedUsers, Set<String> grantedGroups)
+    {
+        Set<String> groups  = _groupsManager.getUserGroups(login);  
+
+        // Check the authorizations
+        if (grantedUsers.contains(login))
+        {
+            return true;
+        }
+        
+        for (String groupId : grantedGroups)
+        {
+            if (groups.contains(groupId))
+            {
+                return true;
+            }
+        }
+        
+        // The user doesn't belong to the granted users
+        return false;
+    }
 }
Index: main/plugin-web/src/org/ametys/web/pageaccess/SetPageAccessAction.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/SetPageAccessAction.java	(revision 27955)
+++ main/plugin-web/src/org/ametys/web/pageaccess/SetPageAccessAction.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2014 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -29,7 +29,6 @@
 import org.apache.cocoon.ProcessingException;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Redirector;
-import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.SourceResolver;
 import org.apache.commons.lang.StringUtils;
 
@@ -66,33 +65,38 @@
     @Override
     public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
     {
-        Request request = ObjectModelHelper.getRequest(objectModel);
         Map<String, Object> parentContext = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
         
         String mode = parameters.getParameter("mode", "set");
         String siteName = parameters.getParameter("site", "");
         String pagePath = parameters.getParameter("page", "");
         
-        // Get the users.
-        Set<String> users = new HashSet<String>();
-        if (parentContext.get("users") != null)
+        // Get the granted users.
+        Set<String> grantedUsers = new HashSet<String>();
+        if (parentContext.get("grantedUsers") != null)
         {
-            users.addAll((Collection<String>) parentContext.get("users"));
+            grantedUsers.addAll((Collection<String>) parentContext.get("grantedUsers"));
         }
-        else if (request.getParameterValues("user") != null)
+        
+        // Get the granted groups.
+        Set<String> grantedGroups = new HashSet<String>();
+        if (parentContext.get("grantedGroups") != null)
         {
-            users.addAll(Arrays.asList(request.getParameterValues("user")));
+            grantedGroups.addAll((Collection<String>) parentContext.get("grantedGroups"));
         }
         
-        // Get the groups.
-        Set<String> groups = new HashSet<String>();
-        if (parentContext.get("groups") != null)
+        // Get the excluded users.
+        Set<String> excludedUsers = new HashSet<String>();
+        if (parentContext.get("excludedUsers") != null)
         {
-            groups.addAll((Collection<String>) parentContext.get("groups"));
+            excludedUsers.addAll((Collection<String>) parentContext.get("excludedUsers"));
         }
-        else if (request.getParameterValues("group") != null)
+        
+        // Get the excluded groups.
+        Set<String> excludedGroups = new HashSet<String>();
+        if (parentContext.get("excludedGroups") != null)
         {
-            users.addAll(Arrays.asList(request.getParameterValues("group")));
+            excludedGroups.addAll((Collection<String>) parentContext.get("excludedGroups"));
         }
         
         boolean anyConnectedUser = false;
@@ -100,10 +104,6 @@
         {
             anyConnectedUser = parentContext.get("set-any-user").equals("true");
         }
-        else if (request.getParameter("set-any-user") != null)
-        {
-            anyConnectedUser = request.getParameter("set-any-user").equals("true");
-        }
         
         try
         {
@@ -120,7 +120,7 @@
                     ModifiablePage page = sitemap.getChild(path);
                     
                     // Set the limited access.
-                    setLimitedAccess(mode, page.getMetadataHolder(), users, groups, anyConnectedUser);
+                    setLimitedAccess(mode, page.getMetadataHolder(), grantedUsers, grantedGroups, excludedUsers, excludedGroups, anyConnectedUser);
                     
                     if (page.needsSave())
                     {
@@ -132,15 +132,15 @@
                 }
                 else
                 {
-                    // Case of a sitemap: set the limited access.
-                    setLimitedAccess(mode, sitemap.getMetadataHolder(), users, groups, anyConnectedUser);
+                    // Case of a site map: set the limited access.
+                    setLimitedAccess(mode, sitemap.getMetadataHolder(), grantedUsers, grantedGroups, excludedUsers, excludedGroups, anyConnectedUser);
                     
                     if (sitemap.needsSave())
                     {
                         sitemap.saveChanges();
                     }
                     
-                    // Notify observers that sitemap data has been changed.
+                    // Notify observers that site map data has been changed.
                     _observationManager.notify(new Event(_getCurrentUser(), ObservationConstants.SITEMAP_UPDATED, sitemap, null));
                 }
             }
@@ -157,37 +157,49 @@
      * Set limited access metadata on the given object (page or sitemap).
      * @param mode the mode, one of "add", "remove", "set-any-user" and "set".
      * @param meta the modifiable object's metadata.
-     * @param users the users to set.
-     * @param groups the groups to set.
+     * @param grantedUsers the granted users to set.
+     * @param grantedGroups the granted groups to set.
+     * @param excludedUsers the excluded users to set.
+     * @param excludedGroups the excluded groups to set.
      * @param anyConnectedUser a boolean indicating if any connected user is allowed.
      */
-    protected void setLimitedAccess(String mode, ModifiableCompositeMetadata meta, Collection<String> users, Collection<String> groups, boolean anyConnectedUser)
+    protected void setLimitedAccess(String mode, ModifiableCompositeMetadata meta, Collection<String> grantedUsers, Collection<String> grantedGroups, 
+                                    Collection<String> excludedUsers, Collection<String> excludedGroups, boolean anyConnectedUser)
     {
         if ("add".equals(mode))
         {
-            _add(meta, PageAccessManager.GRANTED_USERS_METADATA, users);
-            _add(meta, PageAccessManager.GRANTED_GROUPS_METADATA, groups);
+            _add(meta, PageAccessManager.GRANTED_USERS_METADATA, grantedUsers);
+            _add(meta, PageAccessManager.GRANTED_GROUPS_METADATA, grantedGroups);
+        }
+        else if ("exclude".equals(mode))
+        {
+            _add(meta, PageAccessManager.EXCLUDED_USERS_METADATA, excludedUsers);
+            _add(meta, PageAccessManager.EXCLUDED_GROUPS_METADATA, excludedGroups);
         }
         else if ("remove".equals(mode))
         {
-            _remove(meta, PageAccessManager.GRANTED_USERS_METADATA, users);
-            _remove(meta, PageAccessManager.GRANTED_GROUPS_METADATA, groups);
+            _remove(meta, PageAccessManager.GRANTED_USERS_METADATA, grantedUsers);
+            _remove(meta, PageAccessManager.GRANTED_GROUPS_METADATA, grantedGroups);
+            _remove(meta, PageAccessManager.EXCLUDED_USERS_METADATA, excludedUsers);
+            _remove(meta, PageAccessManager.EXCLUDED_GROUPS_METADATA, excludedGroups);
         }
         else if ("set-any-user".equals(mode))
         {
             meta.setMetadata(PageAccessManager.GRANT_ANY_USER_METADATA, anyConnectedUser);
         }
-        else // "set" mode
+        else // default mode : "set" 
         {
-            _set(meta, PageAccessManager.GRANTED_USERS_METADATA, users);
-            _set(meta, PageAccessManager.GRANTED_GROUPS_METADATA, groups);
+            _set(meta, PageAccessManager.GRANTED_USERS_METADATA, grantedUsers);
+            _set(meta, PageAccessManager.GRANTED_GROUPS_METADATA, grantedGroups);
+            _set(meta, PageAccessManager.EXCLUDED_USERS_METADATA, excludedUsers);
+            _set(meta, PageAccessManager.EXCLUDED_GROUPS_METADATA, excludedGroups);
         }
     }
     
     /**
      * Add the objects.
      * @param meta the composite metadata.
-     * @param metaName the metadata name.
+     * @param metaName the metadata name.remove
      * @param objects the objects to add.
      */
     protected void _add(ModifiableCompositeMetadata meta, String metaName, Collection<String> objects)
@@ -208,7 +220,7 @@
             meta.setMetadata(metaName, newObjects);
         }
     }
-
+    
     /**
      * Remove the objects.
      * @param meta the composite metadata.
