Index: main/plugin-web/i18n/messages_en.xml
===================================================================
--- main/plugin-web/i18n/messages_en.xml	(revision 32549)
+++ main/plugin-web/i18n/messages_en.xml	(working copy)
@@ -2128,13 +2136,16 @@
     
     <message key="PLUGINS_WEB_PAGEACCESS_FREE_ACCESS">This page or sitemap is not restricted.</message>
     <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_ALLOW_ANY_CONNECTED_USER_INHERITED">This page or sitemap is restricted, accessible by any connected user (this limitation is inherited from its hierarchy)</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>
+
+    <message key="PLUGINS_WEB_PAGEACCESS_SERVER_ERROR">An error has occurred while setting the access restrictions.</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_REFRESH_ERROR">An error occurred while refreshing the access restrictions' button.</message>
     
 	<!-- +
 	     | LAST PUBLICATIONS
Index: main/plugin-web/i18n/messages_fr.xml
===================================================================
--- main/plugin-web/i18n/messages_fr.xml	(revision 32549)
+++ main/plugin-web/i18n/messages_fr.xml	(working copy)
@@ -2130,7 +2138,7 @@
     
     <message key="PLUGINS_WEB_PAGEACCESS_FREE_ACCESS">Cette page ou ce plan du site est en accès public.</message>
     <message key="PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER">Cette page ou ce plan du site est limité, accessible par tous les utilisateurs enregistrés</message>
-    <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_ALLOW_ANY_CONNECTED_USER_INHERITED">Cette page ou ce plan du site est limité, accessible par tous les utilisateurs enregistrés (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>
@@ -2138,6 +2146,9 @@
     <message key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER">_Utilisateur inconnu</message>
     <message key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP">_Groupe inconnu</message>
     
+    <message key="PLUGINS_WEB_PAGEACCESS_SERVER_ERROR">Une erreur s'est produit lors de l'affectation des restriction d'accès.</message>
+    <message key="PLUGINS_WEB_PAGEACCESS_REFRESH_ERROR">Une erreur s'est produit lors du raffraîchissement du bouton de restriction d'accès.</message>
+    
 	<!-- +
 	     | LAST PUBLICATIONS
 	     + -->
Index: main/plugin-web/plugin.xml
===================================================================
--- main/plugin-web/plugin.xml	(revision 32603)
+++ main/plugin-web/plugin.xml	(working copy)
@@ -8853,67 +8885,72 @@
     
     <feature name="web.userinterface.pageaccess">
         <extensions>
-            <!-- <extension id="org.ametys.web.pageaccess.SetAccess"
+            <extension id="org.ametys.web.pageaccess.PageAccessTab"
+                       point="org.ametys.core.ui.RibbonTabsManager"
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.tab.TabController">
+                    <tool-role>uitool-pageaccess</tool-role>
+                </class> 
+                <scripts>
+                    <file plugin="cms">js/org/ametys/cms/ribbon/tab/ToolTab.js</file>
+                </scripts>      
+            </extension> 
+            
+            <extension id="org.ametys.web.pageaccess.SetAccess"
                        point="org.ametys.core.ui.RibbonControlsManager" 
                        class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
-                <action class="org.ametys.web.tool.pageaccess.control.PageAccessButton">
-                    <param name="role">uitool-pageaccess</param>
-                   
-                    <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_SET_LABEL</param>
-                    <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_SET_DESC</param>
-                    <param name="footer" i18n="true">PLUGINS_WEB_PAGEACCESS_SET_FOOTER</param>
+                <class name="Ametys.plugins.web.pageaccess.controller.PageAccessController">
+                    <opentool-role>uitool-pageaccess</opentool-role>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
+                    <toggle-enabled>true</toggle-enabled>
                     
-                    <param name="refresh-description" i18n="true">PLUGINS_WEB_PAGEACCESS_REFRESH_DESC</param>
-                   	<param name="multiselection-disabled-description" i18n="true">PLUGINS_WEB_PAGEACCESS_TOOMANYPAGE_DESC</param>
-                   	<param name="private-description" i18n="true">PLUGINS_WEB_PAGEACCESS_PRIVATE_DESC</param>
-                   	<param name="no-modifiable-description" i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOMODIFIABLE_DESC</param>
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_SET_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_SET_DESC</description>
+                    <rights-description-no i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</rights-description-no>
+                    <footer i18n="true">PLUGINS_WEB_PAGEACCESS_SET_FOOTER</footer>
+                    
+                   	<selection-description-multiselectionforbidden i18n="true">PLUGINS_WEB_PAGEACCESS_TOOMANYPAGE_DESC</selection-description-multiselectionforbidden>
+                   	<private-description i18n="true">PLUGINS_WEB_PAGEACCESS_PRIVATE_DESC</private-description>
                    	
-                    <param name="icon-small" file="true">img/pageaccess/limit_access_16.png</param>
-                    <param name="icon-medium" file="true">img/pageaccess/limit_access_32.png</param>
-                    <param name="icon-large" file="true">img/pageaccess/limit_access_50.png</param>
+                    <icon-small file="true">img/pageaccess/limit_access_16.png</icon-small>
+                    <icon-medium file="true">img/pageaccess/limit_access_32.png</icon-medium>
+                    <icon-large file="true">img/pageaccess/limit_access_50.png</icon-large>
                     
-                    <param name="private-icon-small" file="true">img/pageaccess/limited_16.png</param>
-                    <param name="private-icon-medium" file="true">img/pageaccess/limited_32.png</param>
-                    <param name="private-icon-large" file="true">img/pageaccess/limited_50.png</param>
-                </action>
+                    <private-icon-small file="true">img/pageaccess/limited_16.png</private-icon-small>
+                    <private-icon-medium file="true">img/pageaccess/limited_32.png</private-icon-medium>
+                    <private-icon-large file="true">img/pageaccess/limited_50.png</private-icon-large>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessButton.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/PageAccessController.js</file>
                 </scripts>
                 <right>Web_Rights_Page_SetAccess</right>
             </extension>
             
-            <extension id="org.ametys.web.pageaccess.PageAccessTab"
-                       point="org.ametys.core.ui.RibbonTabsManager"
-                       class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="Ametys.ribbon.element.tab.TabController">
-                    <param name="tool-role">uitool-pageaccess</param>
-                </action> 
-                <scripts>
-                    <file plugin="cms">js/org/ametys/cms/ribbon/tab/ToolTab.js</file>
-                </scripts>      
-            </extension> 
-            
-            <extension id="org.ametys.web.pageaccess.SetPageAccessFactory"
+            <extension id="org.ametys.web.pageaccess.SetAccessFactory"
                        point="org.ametys.core.ui.UIToolsFactoriesManager"
                        class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="org.ametys.web.tool.pageaccess.PageAccessToolFactory">
-                    <param name="role">uitool-pageaccess</param>
-                    <param name="title" i18n="true">PLUGINS_WEB_PAGEACCESS_SET_LABEL</param>
-                    <param name="description" i18n="true">PLUGINS_WEB_PAGEACCESS_SET_DESC</param>
-                    <param name="footer" i18n="true">PLUGINS_WEB_PAGEACCESS_SET_FOOTER</param>
-                    <param name="icon-small" file="true">img/pageaccess/limit_access_16.png</param>
-                    <param name="icon-medium" file="true">img/pageaccess/limit_access_32.png</param>
-                    <param name="icon-large" file="true">img/pageaccess/limit_access_50.png</param>
-                    <param name="default-location"></param>
-                </action>
+                <class name="Ametys.tool.factory.UniqueToolFactory">
+                    <role>uitool-pageaccess</role>
+                    <toolClass>Ametys.plugins.web.pageaccess.PageAccessTool</toolClass>
+                    
+                    <title i18n="true">PLUGINS_WEB_PAGEACCESS_SET_LABEL</title>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_SET_DESC</description>
+                    <footer i18n="true">PLUGINS_WEB_PAGEACCESS_SET_FOOTER</footer>
+                    <icon-small file="true">img/pageaccess/limit_access_16.png</icon-small>
+                    <icon-medium file="true">img/pageaccess/limit_access_32.png</icon-medium>
+                    <icon-large file="true">img/pageaccess/limit_access_50.png</icon-large>
+                    
+                    <default-location>c</default-location>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/PageAccessTool.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/PageAccessToolFactory.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessTool.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessTreePanel.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessTool/PageAccessEntry.js</file>
                 </scripts>
                 <css>
-                    Profile assignment CSS
+                    <!-- Profile assignment CSS -->
                     <file plugin="cms">css/assign.css</file>
                 </css>
             </extension>
@@ -8921,56 +8958,61 @@
             <extension id="org.ametys.web.pageaccess.ExcludeUsers"
                        point="org.ametys.core.ui.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>
+                <class name="Ametys.plugins.web.pageaccess.controller.PageAccessActionController">
+                    <action>Ametys.plugins.web.pageaccess.PageAccessActions.excludeUsers</action>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
                     
-                    <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="disable-on-allow-any-user">false</param>
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEUSERS_DESCRIPTION</description>
+                    <selection-description-empty i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</selection-description-empty>
+                    <rights-description-no i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</rights-description-no>
                     
-                    <param name="icon-small" file="true">img/pageaccess/exclude_user_16.png</param>
-                    <param name="icon-medium" file="true">img/pageaccess/exclude_user_32.png</param>
-                    <param name="icon-large" file="true">img/pageaccess/exclude_user_48.png</param>
-                </action>
+                    <any-connected-user-description i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</any-connected-user-description>
+                    <disable-on-allow-any-user>false</disable-on-allow-any-user>
+                    
+                    <icon-small file="true">img/pageaccess/exclude_user_16.png</icon-small>
+                    <icon-medium file="true">img/pageaccess/exclude_user_32.png</icon-medium>
+                    <icon-large file="true">img/pageaccess/exclude_user_48.png</icon-large>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/Actions.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessActions.js</file>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
                 </scripts>
                 <css>
-                    <file plugin="core">css/users/selectuser.css</file>
+                    <file plugin="core-ui">css/users/selectuser.css</file>
                 </css>
                 <right>Web_Rights_Page_SetAccess</right>
             </extension>
-            
                         
             <extension id="org.ametys.web.pageaccess.ExcludeGroups"
                        point="org.ametys.core.ui.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>
+                <class name="Ametys.plugins.web.pageaccess.controller.PageAccessActionController">
+                    <action>Ametys.plugins.web.pageaccess.PageAccessActions.excludeGroups</action>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
                     
-                    <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="disable-on-allow-any-user">false</param>
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_EXCLUDEGROUP_DESCRIPTION</description>
+                    <selection-description-empty i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</selection-description-empty>
+                    <rights-description-no i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</rights-description-no>
                     
-                    <param name="icon-small" file="true">img/pageaccess/exclude_group_16.png</param>
-                    <param name="icon-medium" file="true">img/pageaccess/exclude_group_32.png</param>
-                    <param name="icon-large" file="true">img/pageaccess/exclude_group_48.png</param>
-                </action>
+                    <any-connected-user-description i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</any-connected-user-description>
+                    <disable-on-allow-any-user>false</disable-on-allow-any-user>
+                    
+                    <icon-small file="true">img/pageaccess/exclude_group_16.png</icon-small>
+                    <icon-medium file="true">img/pageaccess/exclude_group_32.png</icon-medium>
+                    <icon-large file="true">img/pageaccess/exclude_group_48.png</icon-large>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/Actions.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessActions.js</file>
                 </scripts>
                 <css>
-                    <file plugin="core">css/users/selectuser.css</file>
+                    <file plugin="core-ui">css/users/selectuser.css</file>
                 </css>
                 <right>Web_Rights_Page_SetAccess</right>
             </extension>
@@ -8978,55 +9020,61 @@
             <extension id="org.ametys.web.pageaccess.AddUsers"
                        point="org.ametys.core.ui.RibbonControlsManager"
                        class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
-                <action class="org.ametys.web.tool.pageaccess.control.PageAccessActionButton">
-                    <param name="action">org.ametys.web.pageaccess.AddUsers.act</param>
-                    <param name="target-type">^(page|sitemap)$</param>
+                <class name="Ametys.plugins.web.pageaccess.controller.PageAccessActionController">
+                    <action>Ametys.plugins.web.pageaccess.PageAccessActions.addUsers</action>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
                     
-                    <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_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="disable-on-allow-any-user">true</param>
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_ADDUSERS_DESCRIPTION</description>
+					<selection-description-empty i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</selection-description-empty>
+                    <rights-description-no i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</rights-description-no>
                     
-                    <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>
-                    <param name="icon-large" file="true" plugin="cms">img/users/add_50.png</param>
-                </action>
+                    <any-connected-user-description i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</any-connected-user-description>
+                    <disable-on-allow-any-user>true</disable-on-allow-any-user>
+                    
+                    <icon-small file="true" plugin="cms">img/users/add_16.png</icon-small>
+                    <icon-medium file="true" plugin="cms">img/users/add_32.png</icon-medium>
+                    <icon-large file="true" plugin="cms">img/users/add_50.png</icon-large>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/Actions.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessActions.js</file>
                 </scripts>
                 <css>
-                    <file plugin="core">css/users/selectuser.css</file>
+                    <file plugin="core-ui">css/users/selectuser.css</file>
                 </css>
                 <right>Web_Rights_Page_SetAccess</right>
             </extension>
-            
+
             <extension id="org.ametys.web.pageaccess.AddGroups"
                        point="org.ametys.core.ui.RibbonControlsManager"
                        class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
-                <action class="org.ametys.web.tool.pageaccess.control.PageAccessActionButton">
-                    <param name="action">org.ametys.web.pageaccess.AddGroups.act</param>
-                    <param name="target-type">^(page|sitemap)$</param>
+                <class name="Ametys.plugins.web.pageaccess.controller.PageAccessActionController">
+                    <action>Ametys.plugins.web.pageaccess.PageAccessActions.addGroups</action>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
 
-                    <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_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="disable-on-allow-any-user">true</param>
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_ADDGROUP_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_ADDGROUP_DESCRIPTION</description>
+					<selection-description-empty i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</selection-description-empty>
+                    <rights-description-no i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</rights-description-no>
                     
-                    <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>
-                    <param name="icon-large" file="true" plugin="cms">img/groups/add_50.png</param>
-                </action>
+                    <any-connected-user-description i18n="true">PLUGINS_WEB_PAGEACCESS_ANYCONNECTEDUSER_DESCRIPTION</any-connected-user-description>
+                    <disable-on-allow-any-user>true</disable-on-allow-any-user>
+                    
+                    <icon-small file="true" plugin="cms">img/groups/add_16.png</icon-small>
+                    <icon-medium file="true" plugin="cms">img/groups/add_32.png</icon-medium>
+                    <icon-large file="true" plugin="cms">img/groups/add_50.png</icon-large>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/Actions.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessActions.js</file>
                 </scripts>
                 <css>
-                    <file plugin="core">css/groups/selectgroup.css</file>
+                    <file plugin="core-ui">css/groups/selectgroup.css</file>
                 </css>
                 <right>Web_Rights_Page_SetAccess</right>
             </extension>
@@ -9034,22 +9082,25 @@
             <extension id="org.ametys.web.pageaccess.SetAnyUser"
                        point="org.ametys.core.ui.RibbonControlsManager"
                        class="org.ametys.web.clientsideelement.PageAccessClientSideElement">
-                <action class="org.ametys.web.tool.pageaccess.control.SetAnyUserButton">
-                    <param name="action">org.ametys.web.pageaccess.SetAnyUser.act</param>
-                    <param name="target-type">^(page|sitemap)$</param>
-
-                    <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_NOSELECTION_DESCRIPTION</param>
-                    <param name="no-right-description" i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</param>
+                <class name="Ametys.plugins.web.pageaccess.controller.SetAnyUserController">
+                    <action>Ametys.plugins.web.pageaccess.PageAccessActions.setAnyUser</action>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
+                    <toggle-enabled>true</toggle-enabled>
+    
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_SETANYUSER_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_SETANYUSER_DESCRIPTION</description>
+					<selection-description-empty i18n="true">PLUGINS_WEB_PAGEACCESS_NOSELECTION_DESCRIPTION</selection-description-empty>
+                    <rights-description-no i18n="true">PLUGINS_WEB_PAGEACCESS_NORIGHT_DESCRIPTION</rights-description-no>
                     
-                    <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>
-                    <param name="icon-large" file="true">img/pageaccess/set_any_user_48.png</param>
-                </action>                      
+                    <icon-small file="true">img/pageaccess/set_any_user_16.png</icon-small>
+                    <icon-medium file="true">img/pageaccess/set_any_user_32.png</icon-medium>
+                    <icon-large file="true">img/pageaccess/set_any_user_48.png</icon-large>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
+                </class>                      
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/control/SetAnyUserButton.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/Actions.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/SetAnyUserController.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessActions.js</file>
                 </scripts>
                 <right>Web_Rights_Page_SetAccess</right>
             </extension>
@@ -9057,24 +9108,26 @@
             <extension id="org.ametys.web.pageaccess.RemoveAccess"
                        point="org.ametys.core.ui.RibbonControlsManager"
                        class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="org.ametys.web.pageaccess.RemoveAccessButton">
-                    <param name="target-type">^pageaccess$</param>
-                    <param name="subtarget-type">^(granted-user|granted-group|excluded-user|excluded-group)$</param>
+                <class name="Ametys.plugins.web.pageaccess.controller.RemoveAccessController">
+                    <selection-target-type>^pageaccess$</selection-target-type>
+                    <selection-subtarget-type>^(granted-user|granted-group|excluded-user|excluded-group)$</selection-subtarget-type>
                     
-                    <param name="action">org.ametys.web.pageaccess.RemoveAccess.act</param>
+                    <action>Ametys.plugins.web.pageaccess.PageAccessActions.removeAccess</action>
                     
-                    <param name="label" i18n="true">PLUGINS_WEB_PAGEACCESS_REMOVE_LABEL</param>
-                    <param name="default-description" i18n="true">PLUGINS_WEB_PAGEACCESS_REMOVE_DESCRIPTION</param>
-                    <param name="icon-small" file="true" plugin="cms">img/rights/remove_16.png</param>
-                    <param name="icon-medium" file="true" plugin="cms">img/rights/remove_32.png</param>
-                    <param name="icon-large" file="true" plugin="cms">img/rights/remove_50.png</param>
-                </action>                      
+                    <label i18n="true">PLUGINS_WEB_PAGEACCESS_REMOVE_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_PAGEACCESS_REMOVE_DESCRIPTION</description>
+                    <icon-small file="true" plugin="cms">img/rights/remove_16.png</icon-small>
+                    <icon-medium file="true" plugin="cms">img/rights/remove_32.png</icon-medium>
+                    <icon-large file="true" plugin="cms">img/rights/remove_50.png</icon-large>
+                    
+                    <rights>Web_Rights_Page_SetAccess</rights>
+                </class>                      
                 <scripts>
-                    <file>js/org/ametys/web/tool/pageaccess/Actions.js</file>
-                    <file>js/org/ametys/web/tool/pageaccess/RemoveAccessButton.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/controller/RemoveAccessController.js</file>
+                    <file>js/Ametys/plugins/web/pageaccess/PageAccessActions.js</file>
                 </scripts>
                 <right>Web_Rights_Page_SetAccess</right>
-            </extension> -->
+            </extension>
         </extensions>
     </feature>
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessActions.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessActions.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessActions.js	(revision 0)
@@ -0,0 +1,438 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.PageAccessActions', {
+		singleton: true,
+		
+		/**
+		 * Grant access to the selected user(s) to the current page
+		 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+		 */
+		addUsers: function(controller)
+		{
+			Ametys.helper.SelectUser.act({
+				callback: Ext.bind(this._addUsers, this, [controller], 1),
+				usersManagerRole: 'org.ametys.core.user.UsersManager.FO'
+			});
+		},
+		
+		/**
+		 * @private
+		 * Actual action when the users have been selected
+		 * @param {String[]} users the list of selected users' ids
+		 */
+		_addUsers: function(users)
+		{
+			var ids = {};
+			var grantedUsers = [];
+		    Ext.Object.each(users, function(login) {
+		    	grantedUsers.push(login);
+		    });
+		    
+		    ids.grantedUsers = grantedUsers;
+		    
+			var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
+			var target = message.getTarget(function(target) { return target.getType() == 'pageaccess'});
+		    if (target == null)
+		    {
+		        return;
+		    }
+		    
+		    var pagePath = target.getParameters().pagePath;
+		    
+		    this._setPageAccess([ids, 'add', pagePath], Ext.bind(this._addOrExcludeUsersCb, this, [pagePath, grantedUsers, target.getType(), target.getParameters().id], false));
+		},
+		
+		/**
+		 * Deny access to the selected group(s) to the current page
+		 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+		 */
+		addGroups: function(controller)
+		{
+			Ametys.helper.SelectGroup.act({
+				callback: Ext.bind(this._addGroups, this, [controller], 1),
+				groupsManagerRole: 'org.ametys.core.group.GroupsManager.FO'
+			});
+		},
+		
+		/**
+		 * @private
+		 * Actual action when the groups have been selected
+		 * @param {String[]} groups the list of selected groups' ids
+		 */
+		_addGroups: function(groups)
+		{
+			var ids = {};
+			var grantedGroups = [];
+		    Ext.Object.each(groups, function(groupId) {
+		    	grantedGroups.push(groupId);
+		    });
+		    
+		    ids.grantedGroups = grantedGroups;
+		    
+			var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
+			var target = message.getTarget(function(target) { return target.getType() == 'pageaccess'});
+		    if (target == null)
+		    {
+		        return;
+		    }
+		    
+		    var pagePath = target.getParameters().pagePath;
+		    
+		    this._setPageAccess([ids, 'add', pagePath], Ext.bind(this._addOrExcludeGroupsCb, this, [pagePath, grantedGroups, target.getType(), target.getParameters().id], false));
+		},
+
+		/**
+		 * Deny access to the selected user(s) to the current page
+		 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+		 */
+		excludeUsers: function(controller)
+		{
+			Ametys.helper.SelectUser.act({
+				callback: Ext.bind(this._excludeUsers, this, [controller], 1),
+				usersManagerRole: 'org.ametys.core.user.UsersManager.FO'
+			});
+		},
+		
+		/**
+		 * @private
+		 * Actual action when the users have been selected
+		 * @param {String[]} users the list of selected users' ids
+		 */
+		_excludeUsers: function(users)
+		{
+			var ids = {};
+			var excludedUsers = [];
+		    Ext.Object.each(users, function(login) {
+		    	excludedUsers.push(login);
+		    });
+		    
+		    ids.excludedUsers = excludedUsers;
+		    
+			var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
+			var target = message.getTarget(function(target) { return target.getType() == 'pageaccess'});
+		    if (target == null)
+		    {
+		        return;
+		    }
+		    
+		    var pagePath = target.getParameters().pagePath;
+		    
+		    this._setPageAccess([ids, 'exclude', pagePath], Ext.bind(this._addOrExcludeUsersCb, this, [pagePath, excludedUsers, target.getType(), target.getParameters().id], false));
+		},
+		
+		
+		/**
+		 * Deny access to the selected group(s) to the current page
+		 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+		 */
+		excludeGroups: function(controller)
+		{
+			Ametys.helper.SelectGroup.act({
+				callback: Ext.bind(this._excludeGroups, this, [controller], 1),
+				groupsManagerRole: 'org.ametys.core.group.GroupsManager.FO'
+			});
+		},
+		
+		/**
+		 * @private
+		 * Actual action when the groups have been selected
+		 * @param {String[]} groups the list of selected groups' ids
+		 */
+		_excludeGroups: function(groups)
+		{
+			var ids = {};
+			var excludedGroups = [];
+		    Ext.Object.each(groups, function(groupId) {
+		    	excludedGroups.push(groupId);
+		    });
+		    
+		    ids.excludedGroups = excludedGroups;
+		    
+			var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
+			var target = message.getTarget(function(target) { return target.getType() == 'pageaccess'});
+		    if (target == null)
+		    {
+		        return;
+		    }
+		    
+		    var pagePath = target.getParameters().pagePath;
+		    
+ 		    this._setPageAccess([ids, 'exclude', pagePath], Ext.bind(this._addOrExcludeGroupsCb, this, [pagePath, excludedGroups, target.getType(), target.getParameters().id], false));
+		},
+		
+		/**
+		 * Authorize/forbid access to all connected users/groups to the selected page
+		 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+		 */
+		setAnyUser: function(controller)
+		{
+			var ids = {};
+			ids['set-any-user'] = controller.isPressed();
+			
+			var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
+			var target = message.getTarget(function(target) { return target.getType() == 'pageaccess'});
+		    if (target == null)
+		    {
+		        return;
+		    }
+		    
+		    var pagePath = target.getParameters().pagePath;
+		    
+		    this._setPageAccess([ids, 'set-any-user', pagePath], Ext.bind(this._setAnyUserCb, this, [pagePath, target.getType(), target.getParameters().id], false));
+		},
+		
+		/**
+		 * Remove the access restrictions (positive or negative) to the selected group/user
+		 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+		 */
+		removeAccess: function(controller)
+		{
+			var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
+			var target = message.getTarget(function(target) { return target.getType() == 'pageaccess'});
+		    if (target == null)
+		    {
+		        return;
+		    }
+		    
+		    var pagePath = target.getParameters().pagePath;
+		    
+		    var ids = {};
+		    
+		    var grantedUserTargets = message.getTargets(function(target) { return target.getType() == 'granted-user' });
+		    var grantedGroupTargets = message.getTargets(function(target) { return target.getType() == 'granted-group' });
+		    var excludedUserTargets = message.getTargets(function(target) { return target.getType() == 'excluded-user' });
+		    var excludedGroupTargets = message.getTargets(function(target) { return target.getType() == 'excluded-group' });
+		    
+		    var grantedUsers = [];
+		    for (var i=0; i < grantedUserTargets.length; i++)
+		    {
+		        grantedUsers.push(grantedUserTargets[i].getParameters().login);
+		    }
+		    
+		    var grantedGroups = [];
+		    for (var i=0; i < grantedGroupTargets.length; i++)
+		    {
+		        grantedGroups.push(grantedGroupTargets[i].getParameters().id);
+		    }
+		    
+		    var excludedUsers = [];
+		    for (var i=0; i < excludedUserTargets.length; i++)
+		    {
+		        excludedUsers.push(excludedUserTargets[i].getParameters().login);
+		    }
+		    
+		    var excludedGroups = [];
+		    for (var i=0; i < excludedGroupTargets.length; i++)
+		    {
+		        excludedGroups.push(excludedGroupTargets[i].getParameters().id);
+		    }
+		    
+		    if (grantedUsers.length == 0 && grantedGroups.length == 0 && excludedUsers.length == 0 && excludedGroups.length == 0)
+		    {
+		        return;
+		    }
+		    
+		    ids.grantedUsers = grantedUsers;
+		    ids.grantedGroups = grantedGroups;
+		    ids.excludedUsers = excludedUsers;
+		    ids.excludedGroups = excludedGroups;
+
+		    this._setPageAccess([ids, 'remove', pagePath] , Ext.bind(this._removeAccessCb, this, [pagePath, target.getType(), target.getParameters().id], false));
+		},
+		
+		/**
+		 * @private
+		 * Actual server call setting the page access restrictions/authorizations
+		 * @param {Array} params the parameters used by the server
+		 * @param {Object} params[0] the ids of the involved groups/users
+		 * @param {String[]} [params[0].grantedUsers] the granted users
+		 * @param {String[]} [params[0].grantedGroups] the granted groups
+		 * @param {String[]} [params[0].excludedUsers] the excluded users
+		 * @param {String[]} [params[0].excludedUsers] the excluded groups
+		 * @param {String} params[1] the mode: 'add', 'exclude', 'remove' or 'set-any-user'
+		 * @param {String} params[2] the path of the page
+		 * @param {Function} callback the function to call upon reception of the server's response
+		 */
+		_setPageAccess: function(params, callback)
+		{
+			Ametys.data.ServerComm.callMethod({
+				role: "org.ametys.web.pageaccess.PageAccessDAO",
+				methodName: 'setPageAccess',
+				parameters: params,
+				callback: {
+					handler: callback,
+					scope: this
+				},
+				errorMessage: {
+					category: this.self.getName(),
+					msg: "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_SERVER_ERROR'/>"
+				}
+			});
+		},
+		
+		/**
+		 * @private
+		 * Callback for granting/denying access to a user on a page
+		 * @param {String} pagePath the path of the page
+		 * @param {String[]} users the users
+		 * @param {String} targetType the type of the target ('page' or 'sitemap')
+		 * @param {String} id the page/sitemap id
+		 */
+		_addOrExcludeUsersCb: function(pagePath, users, targetType, id)
+		{
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.CREATED,
+				targets: [
+		          {
+		        	  type: Ametys.message.MessageTarget.PAGE_ACCESS,
+		        	  parameters: {
+		        		  pagePath: pagePath, 
+	        			  users: users
+		        	  }
+		          }
+	            ]
+			});
+			
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.MODIFIED,
+				parameters: {
+					major: false
+				},
+				targets: [
+		          {
+		        	  type: targetType,
+		        	  parameters: {
+		        		  id: id
+		        	  }
+		          }
+	            ]
+			});
+		},
+		
+		/**
+		 * @private
+		 * Callback for granting/denying access to a group on a page
+		 * @param {String} pagePath the path of the page
+		 * @param {String[]} groups the groups
+		 * @param {String} targetType the type of the target ('page' or 'sitemap')
+		 * @param {String} id the page/sitemap id
+		 */
+		_addOrExcludeGroupsCb: function(pagePath, groups, targetType, id)
+		{
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.CREATED,
+				targets: [
+		          {
+		        	  type: Ametys.message.MessageTarget.PAGE_ACCESS,
+		        	  parameters: {
+		        		  pagePath: pagePath, 
+	        			  groups: groups
+		        	  }
+		          }
+	            ]
+			});
+			
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.MODIFIED,
+				parameters: {
+					major: false
+				},
+				targets: [
+		          {
+		        	  type: targetType,
+		        	  parameters: {
+		        		  id: id
+		        	  }
+		          }
+	            ]
+			});
+		},
+		
+		/**
+		 * @private
+		 * Callback after granting/denying access on a page to every connected user
+		 * @param {String} pagePath the path of the page
+ 		 * @param {String} targetType the type of the target ('page' or 'sitemap')
+		 * @param {String} id the page/sitemap id
+		 */
+		_setAnyUserCb: function(pagePath, targetType, id)
+		{
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.CREATED,
+				targets: [
+		          {
+		        	  type: Ametys.message.MessageTarget.PAGE_ACCESS,
+		        	  parameters: {
+		        		  pagePath: pagePath, 
+		        		  'any-user': 'true'
+		        	  }
+		          }
+	            ]
+			});
+			
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.MODIFIED,
+				parameters: {
+					major: false
+				},
+				targets: [
+		          {
+		        	  type: targetType,
+		        	  parameters: {
+		        		  id: id
+		        	  }
+		          }
+	            ]
+			});
+		},
+		
+		/**
+		 * @private
+		 * Callback after granting/denying access on a page to every connected user
+		 * @param {String} pagePath the path of the page
+ 		 * @param {String} targetType the type of the target ('page' or 'sitemap')
+		 * @param {String} id the page/sitemap id
+		 */
+		_removeAccessCb: function(pagePath, targetType, id)
+		{
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.DELETED,
+				targets: [
+		          {
+		        	  type: Ametys.message.MessageTarget.PAGE_ACCESS,
+		        	  parameters: {
+		        		  pagePath: pagePath
+		        	  }
+		          }
+	            ]
+			});
+			
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.MODIFIED,
+				parameters: {
+					major: false
+				},
+				targets: [
+		          {
+		        	  type: targetType,
+		        	  parameters: {
+		        		  id: id
+		        	  }
+		          }
+	            ]
+			});
+		}
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTool.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTool.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTool.js	(revision 0)
@@ -0,0 +1,295 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.PageAccessTool', {
+	extend: 'Ametys.tool.Tool',
+	
+	/**
+	 * @private
+	 * @property {Ametys.plugins.web.pageaccess.PageAccessTreePanel} _pageAccessTree The page access tree panel
+	 */
+	
+	/**
+	 * @private
+	 * @property {Ext.data.NodeInterface} _currentNode the currently selected node
+	 */
+	
+	/**
+	 * @private
+	 * @property {String} _targetType the current target type
+	 */
+	
+	/**
+	 * @private
+	 * @property {String} _pagePath the current page/sitemap path
+	 */
+	
+	/**
+	 * @private
+	 * @property {String} _pageId the current page/sitemap's id
+	 */
+	
+	
+	constructor: function()
+	{
+		this.callParent(arguments);
+		
+		Ametys.message.MessageBus.on(Ametys.message.Message.SELECTION_CHANGED, this._updatePageFromSelection, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this.refresh, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this.refresh, this);
+	},
+
+	getMBSelectionInteraction: function()
+	{
+		return Ametys.tool.Tool.MB_TYPE_ACTIVE;
+	},
+
+	sendCurrentSelection: function()
+	{
+		var node = this._pageAccessTree.getSelectionModel().getSelection()[0];
+		if (node == null)
+		{
+			return;
+		}
+		
+		if (node != this._currentNode)
+		{
+			this._currentNode = node;
+			this._sendMessage(node);
+			
+			this._pageAccessTree._upToPageButton.setDisabled(node == null || !node.data.inherited);
+		}
+	},
+	
+	createPanel: function()
+	{
+		this._pageAccessTree =	Ext.create('Ametys.plugins.web.pageaccess.PageAccessTreePanel', {
+			rootID: 'root',
+			pagePath: '/',
+			
+			listeners: {
+				'selectionchange': Ext.bind(this.sendCurrentSelection, this)
+			}
+		});
+		
+		return this._pageAccessTree;
+	},
+	
+	refresh: function ()
+	{
+		this.showRefreshing();
+		this._pageAccessTree.getStore().load({node: this._pageAccessTree.getRootNode(), callback: this.showRefreshed, scope: this});
+	},
+	
+	onOpen: function()
+	{
+	    this._updatePageFromSelection(Ametys.message.MessageBus.getCurrentSelectionMessage());
+	    this.callParent(arguments);
+	},
+	
+	/**
+	 * Listener when a page/sitemap is selected
+	 * @param {Ametys.message.Message} message the current message
+	 */
+	_updatePageFromSelection: function(message)
+	{
+		var target = message.getTarget(function(target) { return target.getType() == 'page' || target.getType() == 'sitemap';});
+		if (target != null)
+		{
+			if (target.getType() == 'page')
+	        {
+	            var pageId = target.getParameters().id;
+	            var pagePath = target.getParameters().lang + '/' + target.getParameters().path;
+	            
+	            var text = "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_HELP_1'/>";
+	            text += '<b>' + target.getParameters().title + '</b>';
+	            text += "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_HELP_2'/>";
+	            
+	            this._updatePage(Ametys.message.MessageTarget.PAGE, pagePath, pageId);
+	            this._pageAccessTree.updateContextHelperText(text);
+	        }
+	        else if (target.getType() == 'sitemap')
+	        {
+	            var sitemapId = target.getParameters().id;
+	            var sitemapName = target.getParameters().name;
+	            
+	            var text = "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_SITEMAP'/>";
+	
+	            this._updatePage(Ametys.message.MessageTarget.SITEMAP, sitemapName, sitemapId);
+	            this._pageAccessTree.updateContextHelperText(text);
+	        }
+		}
+	},
+	
+	/**
+	 * Update the page path.
+	 * @param {String} targetType the target type (page or sitemap)
+	 * @param {String} path the new page/sitemap path
+	 * @param {String} id the id of the selected page/sitemap
+	 */
+	_updatePage: function(targetType, path, id)
+	{
+		if (this._pagePath != path)
+		{
+			this.showRefreshing();
+
+			this._targetType = targetType;
+			this._pagePath = path;
+			this._pageId = id;
+			
+			this._pageAccessTree.updatePage(path, Ext.bind(this.showRefreshed, this));
+		}
+	},
+		
+	/**
+	 * @private 
+	 * Send the message
+	 * @param {Ext.tree.Node} node The selected node. Can be null
+	 */
+	_sendMessage: function(node)
+	{
+		if (!this.hasFocus() || node == null)
+		{
+			return;
+		}
+		
+		var pagePath = this._pagePath,
+			targetType = this._targetType,
+			pageId = this._pageId;
+		
+		var subtargets = [];
+		
+		// Page or sitemap ? 
+		if (targetType == Ametys.message.MessageTarget.PAGE)
+		{
+			subtargets.push({
+			    	type: Ametys.message.MessageTarget.PAGE,
+			    	parameters: {
+			    		ids: [pageId]
+			    	}
+		    });
+		}
+		else
+		{
+			subtargets.push({
+		    	type: Ametys.message.MessageTarget.SITEMAP,
+		    	parameters: {
+		    		id: pageId
+		    	}
+			});
+		}
+	    
+		if (node.data.type == 'granted-user')
+		{
+			subtargets.push(
+				{
+			    	type: Ametys.message.MessageTarget.GRANTED_USER,
+			    	parameters: {
+			    		login: node.data.login, 
+			    		name: node.data.name
+			    	}
+			    });
+		}
+		else if (node.data.type == 'granted-group')
+		{
+			subtargets.push(
+			{
+		    	type: Ametys.message.MessageTarget.GRANTED_GROUP,
+		    	parameters: {
+		    		id: node.data.id, 
+		    		name: node.data.name
+		    	}
+		    });
+		}
+		else if (node.data.type == 'excluded-user')
+		{
+			subtargets.push(
+			{
+		    	type: Ametys.message.MessageTarget.EXCLUDED_USER,
+		    	parameters: {
+		    		login: node.data.login, 
+		    		name: node.data.name
+		    	}
+		    });
+		}
+		else if (node.data.type == 'excluded-group')
+		{
+			subtargets.push(
+			{
+		    	type: Ametys.message.MessageTarget.EXCLUDED_GROUP,
+		    	parameters: {
+		    		id: node.data.id, 
+		    		name: node.data.name
+		    	}
+		    });
+		}
+		
+		var target = {
+			type: Ametys.message.MessageTarget.PAGE_ACCESS,
+			parameters: {
+				pagePath: pagePath,
+				targetType: targetType
+			},
+			subtargets: subtargets
+		};
+		
+		Ext.create('Ametys.message.Message', {
+			type: Ametys.message.Message.SELECTION_CHANGED,
+			targets: [target]
+		});
+	}
+});
+	
+Ext.define("Ametys.message.PageAccessMessageTarget",
+{
+	override: "Ametys.message.MessageTarget",
+	statics: 
+	{
+		/**
+		 * @member Ametys.message.MessageTarget
+		 * @readonly
+		 * @property {String} PAGE_ACCESS The target of the message is a page access 
+		 */
+		PAGE_ACCESS: "pageaccess",
+		
+		/**
+		 * @member Ametys.message.MessageTarget
+		 * @readonly
+		 * @property {String} GRANTED_USER The target of the message is a user that was granted access to a page 
+		 */
+		GRANTED_USER: "granted-user",
+			
+		/**
+		 * @member Ametys.message.MessageTarget
+		 * @readonly
+		 * @property {String} GRANTED_GROUP The target of the message is a group that was granted access to a page 
+		 */
+		GRANTED_GROUP: "granted-group",
+		
+		/**
+		 * @member Ametys.message.MessageTarget
+		 * @readonly
+		 * @property {String} EXCLUDED_USER The target of the message is a user that was refused access to a page 
+		 */
+		EXCLUDED_USER: "excluded-user",
+		
+		/**
+		 * @member Ametys.message.MessageTarget
+		 * @readonly
+		 * @property {String} EXCLUDED_GROUP The target of the messages is a group that was refused access to a page 
+		 */
+		EXCLUDED_GROUP: "excluded-group"
+	}
+});
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTool/PageAccessEntry.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTool/PageAccessEntry.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTool/PageAccessEntry.js	(revision 0)
@@ -0,0 +1,122 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @private
+ * The model for the {@link Ametys.plugins.web.pageaccess.PageAccessTool} tree tool. 
+ */
+Ext.define('Ametys.plugins.web.pageaccess.PageAccessTool.PageAccessEntry', { 
+    extend: 'Ext.data.TreeModel', 
+    
+    fields: [ 
+        { name: 'id', type: 'string' }, 
+        { name: 'type', type: 'string', mapping: 'type'},
+        { name: 'anyConnectedUserAllowed', type: 'string', mapping: 'anyConnectedUserAllowed'}, // used for the icon of the 'set-any-user' node
+        { name: 'inherited', type: 'boolean', mapping: 'inherited', defaultValue: false},
+        { name: 'unknown', type: 'boolean', mapping: 'unknown', defaultValue: false},
+        { name: 'page', type: 'string', mapping: 'page' },
+        { name: 'path', type: 'string', mapping: 'path' },
+        { name: 'rawText', type: 'string', mapping: 'rawText' },
+        { 
+        	name: 'text', type: 'string', calculate: function(data) {
+        		if (data.unknown)
+    			{
+        			return '<i>' + data.rawText + '</i>';
+    			}
+        		else if (data.type == 'granted-user' || data.type == 'granted-group' || data.type == 'excluded-user' || data.type == 'excluded-group' || data.type == 'allow-any-connected-user')
+    			{
+        			return data.rawText;
+    			}
+        		else
+    			{
+        			return '<b>' + data.rawText + '</b>';
+    			}
+        	}
+    	},
+        { 
+        	name: 'leaf', type: 'boolean', calculate: function(data) {
+        		if (data.type == 'granted-user' || data.type == 'granted-group' || data.type == 'excluded-user' || data.type == 'excluded-group' || data.type == 'allow-any-connected-user')
+    			{
+        			return true;
+        		}
+        		else
+    			{
+        			return false;
+    			}
+        	} 
+        },
+        { 
+			name: 'cls', type: 'string', calculate: function(data){
+				return (data.inherited ? 'inherit' : '');
+        	}
+        },
+        { 
+        	name: 'icon', type: 'string', calculate: function(data) {
+        		if (data.unknown)
+    			{
+        			if (data.type == 'granted-user' || data.type == 'excluded-user')
+        			{
+        				 return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/user_unknown_16.png';
+        			}	
+        			else
+    				{
+        				return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/group_unknown_16.png';
+    				}
+    			}
+        		else
+    			{
+        			if (data.type == 'granted-user' || data.type == 'excluded-user')
+        			{
+        				 return Ametys.getPluginResourcesPrefix('core') + '/img/profiles/user_16.png';
+        			}	
+        			else if (data.type == 'granted-group' || data.type == 'excluded-group')
+    				{
+        				return Ametys.getPluginResourcesPrefix('core') + '/img/profiles/group_16.png';
+    				}
+        			else if (data.type == 'granted-users')
+    				{
+        				return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/user_list_16.png';
+    				}
+        			else if (data.type == 'granted-groups')
+        			{
+        				return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/group_list_16.png';
+        			}
+        			else if( data.type == 'excluded-users')
+    				{
+        				return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/excluded_user_list_16.png';
+    				}
+        			else if (data.type == 'excluded-groups')
+    				{
+        				return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/excluded_group_list_16.png';	
+    				}
+        			else if (data.type == 'allow-any-connected-user')
+    				{
+        				if (data.anyConnectedUserAllowed == "true")
+    					{
+        					return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/any_user_16.png';
+    					}
+        				else
+        				{
+        					return Ametys.getPluginResourcesPrefix('web') + '/img/pageaccess/no_limited_16.png';
+        				}
+    				}
+    			}
+        		
+        		
+        	}
+        }
+    ]
+}); 
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTreePanel.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTreePanel.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/PageAccessTreePanel.js	(revision 0)
@@ -0,0 +1,357 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.PageAccessTreePanel', {
+		extend: 'Ext.tree.Panel',
+		
+		/**
+		 * @private
+		 * @property {String} _pagePath the path of the selected page
+		 */
+		
+		/**
+		 * @private
+		 * @property {Ext.Template} _tooltipTpl The template used for tooltips
+		 */
+		
+		rootVisible: false,
+		
+		border: false,
+		autoScroll: true,
+		
+		constructor: function(config)
+		{
+			this._pagePath = config.pagePath;
+			
+			// The store
+			config.store = config.store || this._createStore();
+			
+			// The root node
+			config.root = config.root || {
+				id: config.rootID,
+				text: ' <b>' + "<i18n:text i18n:key='PAGEACCESS_TREEPANEL_ROOT_NODE'/>" + '</b>',
+				loaded: true,
+				expanded: true
+			};
+			
+			config.dockedItems = this._getDockedItems();
+			
+	  		this._tooltipTpl = Ext.create ('Ext.Template', [
+    	            "<div class='right-assign-tooltip'>",
+    	            "<b>{name}</b><br/>",
+    	            "<u><i18n:text i18n:key='PAGEACCESS_TREEPANEL_PAGE_TOOLTIP'/></u> : {page}",
+    	            "<br/>{path}.html",
+    	            "</div>"
+    		]);
+			
+			this.callParent(arguments);
+		},
+		
+		/**
+		 * Update the selected page
+		 * @param {String} pagePath the new selected page path
+		 * @param {Function} callback the callback after the store is loaded
+		 */
+		updatePage: function(pagePath, callback)
+		{
+		    this._pagePath = pagePath;
+		    this.getStore().load({node: this.getRootNode(), callback: callback, scope: this})
+		},
+		
+		/**
+		 * Update the text of contained in the hint panel for context
+		 * @param {String} text the new text
+		 */
+		updateContextHelperText: function(text)
+		{
+			var hintPanel = this.getDockedItems('toolbar[dock="top"]')[0].child('#context-helper-text');
+			if (hintPanel)
+			{
+				hintPanel.update(text);
+			}
+		},
+		
+		/**
+		 * @private
+		 * Creates the store the tree should use as its data source.
+		 * @return {Ext.data.TreeStore} The tree store
+		 */
+		_createStore: function ()
+		{
+			return Ext.create('Ext.data.TreeStore', {
+				model: 'Ametys.plugins.web.pageaccess.PageAccessTool.PageAccessEntry',
+				
+				root: {
+					expanded: false
+				},			
+				
+		        proxy: {
+		        	type: 'ametys',
+					plugin: 'web',
+					url: 'repository/pageaccess.json',
+		        	reader: {
+		        		type: 'json',
+						rootProperty: 'children'
+		        	}
+		        },
+		        
+		        listeners: {
+		        	'beforeload': Ext.bind(this._onBeforeLoad, this),
+		        	'load': Ext.bind(this._onLoad, this)
+		        }
+			});
+		},
+		
+		_getDockedItems: function()
+		{
+		    // Helper
+		    this._pageHelperPanel = {
+				xtype: 'component',
+				cls: 'hint',
+				id: 'context-helper-text'
+			};
+		    
+		    // Up to page fieldS
+		    this._upToPageButton = Ext.create('Ext.Button', {
+					tooltip: "<i18n:text i18n:key='PAGEACCESS_TREEPANEL_BTN_UPTOCONTEXT_TOOLTIP'/>",
+					handler: Ext.bind (this._upToPage, this),
+					disabled : true,
+					scope: this,
+					icon: Ametys.getPluginResourcesPrefix('core') + '/img/profiles/actions/up_16.png',
+					cls: 'x-btn-text-icon'
+				});
+			
+			return [{
+				xtype: 'toolbar',
+				dock: 'top',
+				items: [
+					{
+						// Search input field
+						xtype: 'textfield',
+						name: 'textfield',
+						width: 250,
+						emptyText: "<i18n:text i18n:key='PAGEACCESS_TREEPANEL_FILTER_BY_NAME'/>",
+						hideLabel: true,
+						enableKeyEvents: true,
+						listeners: {
+							keyup: Ext.bind (this._onKeyUp, this)
+						}
+					},
+					'-',
+					' ', 
+					this._upToPageButton, 
+					'-', 
+					{
+						// Expand tool
+						tooltip: "<i18n:text i18n:key='PAGEACCESS_TREEPANEL_BTN_EXPAND_TOOTIP'/>",
+						handler: Ext.bind (this._expandAll, this),
+						scope: this,
+						icon: Ametys.getPluginResourcesPrefix('core') + '/img/profiles/actions/expand-all.gif',
+						cls: 'x-btn-text-icon'
+					}, 
+					{
+						// Collapse tool
+						tooltip: "<i18n:text i18n:key='PAGEACCESS_TREEPANEL_BTN_COLLAPSE_TOOTIP'/>",
+						handler: Ext.bind (this._collapseAll, this),
+						scope: this,
+						icon: Ametys.getPluginResourcesPrefix('core') + '/img/profiles/actions/collapse-all.gif',
+						cls: 'x-btn-text-icon'
+					},
+					'-',
+					this._pageHelperPanel
+				]
+			}]
+		},
+		
+		/**
+		 * Select the page corresponding to the parent page from which the current page inherits its access limitations
+		 */
+		_upToPage: function()
+		{
+			var node = this.getSelectionModel().getSelection()[0];
+			if (node != null)
+			{
+				var nodePath = node.data.path;
+				// /[lang]/[path] for a page and /[name] for a sitemap.
+				if (/^\/([a-z]{2})(\/(.+))?$/.test(nodePath))
+				{	
+					var path = null;
+					if (RegExp.$2 != null && RegExp.$2 != '')
+				    {
+					    path = (RegExp.$2).substring(1); // remove leading "/"
+				    }
+					
+					var targetType = path == null ? Ametys.message.MessageTarget.SITEMAP : Ametys.message.MessageTarget.PAGE;
+					
+					Ametys.data.ServerComm.callMethod({
+						role: "org.ametys.web.repository.page.SitemapDAO",
+						methodName: 'convertPathToId',
+						parameters: [Ametys.getAppParameter('siteName'), RegExp.$1, path],
+						callback: {
+							handler: Ext.bind(this._upToPageCb, this, [targetType],1),
+							scope: this
+						},
+						errorMessage: {
+							msg: "<i18n:text i18n:key='PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR'/>",
+							category: this.self.getName()
+						}
+					});
+				}
+			}
+		},
+		
+		/**
+		 * Function called after checking if the page with this path exist in sitemap
+		 * @param {String} id The page/sitemap id
+		 * @param {String} targetType the target's type, can be either "page" or "sitemap"
+		 */
+		_upToPageCb: function(id, targetType)
+		{
+			// FIXME its not selecting the corresponding page whereas it is supposed to
+			if (id != null)
+			{
+				var target = null; 
+				if (targetType == Ametys.message.MessageTarget.PAGE)
+				{
+					target = {
+								type: Ametys.message.MessageTarget.PAGE,
+								parameters: {
+									ids: [id]
+								}
+							 };
+				}
+				else
+				{
+					target = {
+							type: Ametys.message.MessageTarget.SITEMAP,
+							parameters: {
+								id: id
+							}
+						 };
+				}
+					
+				Ext.create("Ametys.message.Message", {
+					type: Ametys.message.Message.SELECTION_CHANGED,
+					
+					targets: [target]
+				});
+			}
+		},
+		
+		/**
+		 * @private
+		 * Listen on 'keyup' event
+		 * Filters the tree nodes by the typed in text.
+		 * @param {Ext.form.field.Text} field This text field
+		 */
+		_onKeyUp: function(field)
+		{
+		    this.filter.clear();
+		    var val = field.getRawValue()
+		    var re = new RegExp('.*' + val + '.*', 'i');
+		    
+		    this.filter.filterBy( function(node) {
+		        return re.test(node.text) || node.data.type == 'granted-users' || node.data.type == 'granted-groups' || node.data.type == 'excluded-users' || node.data.type == 'excluded-groups';
+		    });
+		},
+		
+		/**
+		 * Expand the whole tree
+		 * @private
+		 */
+		_expandAll: function ()
+		{
+			this.getRootNode().expandChildren(true);
+		},
+		
+		/**
+		 * Collapse the whole tree
+		 * @private
+		 */
+		_collapseAll: function ()
+		{
+			this.getRootNode().collapseChildren(true);
+		},
+		
+		/**
+		 * Function called before loading the store
+		 * @param {Ext.data.Store} store The store
+		 * @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
+		 * @param {Object} eOpts Event options
+		 * @private
+		 */
+		_onBeforeLoad: function(store, operation, eOpts)
+		{
+			operation.setParams( operation.getParams() || {} );
+			operation.setParams( Ext.apply(operation.getParams(), {
+				pagePath: this._pagePath
+			}));
+		},
+		
+	    /**
+	     * @private
+	     * Listener when store is loaded
+	     * @param {Ext.data.TreeStore} store the store
+	     * @param {Ext.data.TreeModel[]} records the records
+	     * @param {Boolean} successful true if the operation was successful
+	     * @param {Ext.data.NodeInterface} the loaded node
+	     */
+		_onLoad: function(store, records, successful, operation, node)
+	    {
+			this._setTooltips(node);
+			
+	        // Expand first nodes
+	        this.getRootNode().expandChildren(false, false, this._onRootNodesChangedAndExpanded, this);
+	    },
+	    
+	    /**
+	     * @private
+	     * Recursively set the tooltips on the given node an its children
+	     * @param {Ext.data.NodeInterface} the node
+	     */
+	    _setTooltips: function(node)
+	    {
+	        var childNodes = node.childNodes;
+	        for (var i=0; i < childNodes.length; i++)
+	        {
+	            this._setTooltip(childNodes[i]);
+	            this._setTooltips(childNodes[i]);
+	        }
+	    },
+	    
+	    /**
+	     * @private 
+	     * Set the tooltip on a given node 
+	     * @param {Ext.data.NodeInterface} the node
+	     */
+	    _setTooltip: function(node)
+	    {
+	    	var type = node.data.type;
+	        if ((type == 'granted-user' || type == 'granted-group' || type == 'excluded-user' || type == 'excluded-group') && node.data.unknown != 'true')
+	        {
+	        	node.data.qtip = this._tooltipTpl.applyTemplate (node.data);
+	        }
+	    },
+		
+	    /**
+	     * @private
+	     * When the newly loaded root nodes are expanded
+	     */
+	    _onRootNodesChangedAndExpanded: function()
+	    {
+	        // Select first node
+	        this.getSelectionModel().select(this.getRootNode().firstChild);
+	    }
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/PageAccessActionController.js	(revision 0)
@@ -0,0 +1,96 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.controller.PageAccessActionController', {
+	extend: 'Ametys.ribbon.element.ui.ButtonController',
+	
+	constructor: function(config)
+	{
+		this.callParent(arguments);
+		
+		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
+	},
+	
+	/**
+	 * @private
+	 * Listener handler for modified messages
+	 * @param {Ametys.message.Message} message the message
+	 */
+	_onModified: function(message)
+	{
+		this.refresh();
+	},
+	
+	updateState: function()
+	{
+		this._getStatus(this.getMatchingTargets());
+	},
+	
+	/**
+	 * @private
+	 * Get the status
+	 * @param targets The page targets
+	 */
+	_getStatus: function (targets)
+	{
+		if (targets.length > 0)
+		{
+			var pageIds = [];
+			Ext.Array.each(targets, function(target) {
+				pageIds.push(target.getParameters().id);
+			});
+
+			this.serverCall('getStatus',
+					[pageIds], 
+					Ext.bind(this._getStatusCb, this),
+					{ 
+						errorMessage: { 
+							msg: "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_REFRESH_ERROR'/>",
+							category: this.self.getName() 
+						}
+					},
+					true
+			);
+		}
+	},
+	
+	/**
+	 * @private
+	 * Callback for the button's status computation process
+	 * @param {Object} response the server's response
+	 * @param {String} response['private-pages'] the pages having restrictions
+	 * @param {String} response['public-pages'] the pages without restrictions
+	 * @param {String} response['any-connected-user-pages'] the pages for which any connected user is allowed
+	 * @param {String} response['inherited-restriction-pages'] the pages having inherited access restrictions
+	 * @param {Object} args the callback arguments
+	 */
+	_getStatusCb: function(response, args)
+	{
+	    var allowAnyConnectedUserCount = response['any-connected-user-pages'].length;
+	    var inheritedCount = response['inherited-restriction-pages'].length;
+	    
+	    if (allowAnyConnectedUserCount > 0 && inheritedCount == 0 && this.getInitialConfig()['disable-on-allow-any-user'] == 'true')
+	    {
+	        // If any connected user is allowed on the page itself (i.e. if not inherited), disable the button.
+	    	this.disable();
+	    	this.setDescription(this.getInitialConfig()['any-connected-user-description']);
+	    }
+	    else
+	    {
+	    	this.enable();
+	        this.setDescription(this.getInitialConfig()['description']);
+	    }
+	}
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/PageAccessController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/PageAccessController.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/PageAccessController.js	(revision 0)
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.controller.PageAccessController', {
+		extend: 'Ametys.ribbon.element.ui.button.OpenToolButtonController',
+		
+		constructor: function(config)
+		{
+			this.callParent(arguments);
+			
+			Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
+		},
+		
+		/**
+		 * @private
+		 * Listener handler for modified messages
+		 * @param {Ametys.message.Message} message the message
+		 */
+		_onModified: function(message)
+		{
+			this.refresh();
+		},
+		
+		updateState: function()
+		{
+			this._getStatus(this.getMatchingTargets());
+		},
+		
+		/**
+		 * @private
+		 * Get the status
+		 * @param targets The page targets
+		 */
+		_getStatus: function (targets)
+		{
+			if (targets.length > 0)
+			{
+				var pageIds = [];
+				Ext.Array.each(targets, function(target) {
+					pageIds.push(target.getParameters().id);
+				});
+
+				this.serverCall('getStatus',
+						[pageIds], 
+						Ext.bind(this._getStatusCb, this),
+						{ 
+							errorMessage: { 
+								msg: "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_REFRESH_ERROR'/>",
+								category: this.self.getName() 
+							}
+						},
+						true
+				);
+			}
+		},
+		
+		/**
+		 * @private
+		 * Callback for the button's status computation process
+		 * @param {Object} response the server's response
+		 * @param {String} response['private-pages'] the pages having restrictions
+		 * @param {String} response['public-pages'] the pages without restrictions
+		 * @param {String} response['any-connected-user-pages'] the pages for which any connected user is allowed
+		 * @param {String} response['inherited-restriction-pages'] the pages having inherited access restrictions
+		 * @param {Object} args the callback arguments
+		 */
+		_getStatusCb: function(response, args)
+		{
+			var isAccessRestricted = response['private-pages'].length > 0; 
+			if (isAccessRestricted)
+			{
+				this.setDescription(this.getInitialConfig()["private-description"]);
+				this.setIcons(this.getInitialConfig('private-icon-small'), this.getInitialConfig('private-icon-medium'), this.getInitialConfig('private-icon-large'));
+			}
+			else
+			{
+				this.setDescription(this.getInitialConfig()["description"]);
+				this.setIcons(this.getInitialConfig('icon-small'), this.getInitialConfig('icon-medium'), this.getInitialConfig('icon-large'));
+			}
+			
+			this.toggle(isAccessRestricted);
+		}
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/RemoveAccessController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/RemoveAccessController.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/RemoveAccessController.js	(revision 0)
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.controller.RemoveAccessController', {
+	extend: 'Ametys.ribbon.element.ui.ButtonController',
+	
+	hasRightOnAny: function(targets)
+	{
+		// Get down one level in order to access the target actually containing the rights as a parameter
+		// targets can not be null here
+		var pageOrSitemapTargets = Ametys.message.MessageTargetHelper.findTargets(targets, function(target) { return target.getType() == 'page' || target.getType() == 'sitemap';});
+	
+		return this.callParent([pageOrSitemapTargets]);
+	}
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/SetAnyUserController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/SetAnyUserController.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/pageaccess/controller/SetAnyUserController.js	(revision 0)
@@ -0,0 +1,74 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+Ext.define('Ametys.plugins.web.pageaccess.controller.SetAnyUserController', {
+		extend: 'Ametys.ribbon.element.ui.ButtonController',
+		
+		updateState: function()
+		{
+			this._getStatus(this.getMatchingTargets());
+		},
+		
+		/**
+		 * @private
+		 * Get the status
+		 * @param targets The page targets
+		 */
+		_getStatus: function (targets)
+		{
+			if (targets.length > 0)
+			{
+				var pageIds = [];
+				Ext.Array.each(targets, function(target) {
+					pageIds.push(target.getParameters().id);
+				});
+
+				this.serverCall('getStatus',
+						[pageIds], 
+						Ext.bind(this._getStatusCb, this),
+						{ 
+							errorMessage: { 
+								msg: "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_REFRESH_ERROR'/>",
+								category: this.self.getName() 
+							}
+						},
+						true
+				);
+			}
+		},
+		
+		/**
+		 * @private
+		 * Callback for the button's status computation process
+		 * @param {Object} response the server's response
+		 * @param {String} response['private-pages'] the pages having restrictions
+		 * @param {String} response['public-pages'] the pages without restrictions
+		 * @param {String} response['any-connected-user-pages'] the pages for which any connected user is allowed
+		 * @param {String} response['inherited-restriction-pages'] the pages having inherited access restrictions
+		 * @param {Object} args the callback arguments
+		 */
+		_getStatusCb: function(response, args)
+		{
+			var allowAnyConnectedUserCount = response['any-connected-user-pages'].length;
+			var inheritedCount = response['inherited-restriction-pages'].length;
+			var negativeRestrictionPage = response['private-pages'].length;
+			
+			var toggledState = allowAnyConnectedUserCount > 0 && inheritedCount == 0;
+			var disabledState = allowAnyConnectedUserCount > 0 && inheritedCount > 0 && negativeRestrictionPage == 0;
+			
+		    this.setDisabled(disabledState);
+		    this.toggle(toggledState);
+		}
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/Actions.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/Actions.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/Actions.js	(nonexistent)
@@ -1,381 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-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');
-
-/*************************************/
-/**         ADD USERS               **/
-/*************************************/
-org.ametys.web.pageaccess.AddUsers._pluginName;
-org.ametys.web.pageaccess.AddUsers.act = function(name, params, pluginName)
-{
-    org.ametys.web.pageaccess.AddUsers._pluginName = pluginName;
-    RUNTIME_Plugin_Runtime_SelectUser.initialize('core');
-    RUNTIME_Plugin_Runtime_SelectUser.act(org.ametys.web.pageaccess.AddUsers._doAction, null, 'org.ametys.runtime.user.UsersManager.FO');    
-}
-
-org.ametys.web.pageaccess.AddUsers._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/add';
-    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);
-}
-
-org.ametys.web.pageaccess.AddUsers._callBack = function(response, params)
-{
-	var messages = [];
-	
-    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});
-    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_MINORMETADATACHANGED, {}, [pageTarget]));
-    
-    org.ametys.messagebus.MessageBus.getInstance().fireMessages(messages);
-}
-
-org.ametys.web.pageaccess.AddUsers._searchPageTarget = function(targets)
-{
-    for (var i=0; i < targets.length; i++)
-    {
-        if (targets[i].getType() == 'page' || targets[i].getType() == 'sitemap')
-        {
-            return targets[i];
-        }
-        
-        var subSearch = this._searchPageTarget(targets[i].getSubTargets());
-        if (subSearch != null)
-        {
-            return subSearch;
-        }
-    }
-    return null;
-}
-
-/*************************************/
-/**         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) 
-{
-    org.ametys.web.pageaccess.AddGroups._pluginName = pluginName;
-    RUNTIME_Plugin_Runtime_SelectGroup.initialize('core');
-    RUNTIME_Plugin_Runtime_SelectGroup.act(org.ametys.web.pageaccess.AddGroups._doAction, null, 'org.ametys.runtime.group.GroupsManager.FO');  
-}
-
-org.ametys.web.pageaccess.AddGroups._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/add';
-    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);
-}
-
-org.ametys.web.pageaccess.AddGroups._callBack = function(response, params)
-{
-	var messages = [];
-	
-    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});
-    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_MINORMETADATACHANGED, {}, [pageTarget]));
-    
-    org.ametys.messagebus.MessageBus.getInstance().fireMessages(messages);
-}
-
-/*************************************/
-/**     SET ANY CONNECTED USER      **/
-/*************************************/
-
-org.ametys.web.pageaccess.SetAnyUser._pluginName;
-org.ametys.web.pageaccess.SetAnyUser.act = function(name, params, pluginName, state) 
-{
-    org.ametys.web.pageaccess.SetAnyUser._pluginName = pluginName;
-    
-    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/set-any-user';
-    var params = {'pagePath': pagePath, 'set-any-user': String(state), 'pageId': target.getParameters().id, targetType: target.getType()};
-    
-    var serverMessage = new org.ametys.servercomm.ServerMessage(pluginName, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.pageaccess.SetAnyUser._callback, this, params);
-    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
-}
-
-org.ametys.web.pageaccess.SetAnyUser._callback = function(response, params)
-{
-    var messages = [];
-    
-    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath, 'any-user': 'true'});
-    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);
-}
-
-
-/*************************************/
-/**     REMOVE ASSIGNMENT           **/
-/*************************************/
-
-org.ametys.web.pageaccess.RemoveAccess._pluginName;
-org.ametys.web.pageaccess.RemoveAccess.act = function(name, params, pluginName) 
-{
-    org.ametys.web.pageaccess.RemoveAccess._pluginName = pluginName;
-    
-    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)
-    {
-        return;
-    }
-    
-    var pagePath = target.getParameters().pagePath;
-    
-    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 grantedUsers = [];
-    for (var i=0; i < grantedUserTargets.length; i++)
-    {
-        grantedUsers.push(grantedUserTargets[i].getParameters().login);
-    }
-    
-    var grantedGroups = [];
-    for (var i=0; i < grantedGroupTargets.length; i++)
-    {
-        grantedGroups.push(grantedGroupTargets[i].getParameters().id);
-    }
-    
-    var excludedUsers = [];
-    for (var i=0; i < excludedUserTargets.length; i++)
-    {
-        excludedUsers.push(excludedUserTargets[i].getParameters().login);
-    }
-    
-    var excludedGroups = [];
-    for (var i=0; i < excludedGroupTargets.length; i++)
-    {
-        excludedGroups.push(excludedGroupTargets[i].getParameters().id);
-    }
-    
-    if (grantedUsers.length == 0 && grantedGroups.length == 0 && excludedUsers.length == 0 && excludedGroups.length == 0)
-    {
-        return;
-    }
-    
-    var siteName = context.parameters.siteName;
-    
-    var url = '/' + siteName + '/pageaccess/remove';
-    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);
-}
-
-org.ametys.web.pageaccess.RemoveAccess._callBack = function(response, params)
-{
-	var messages = [];
-	
-    var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': params.pagePath});
-    messages.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED, 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);
-}
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTool.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTool.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTool.js	(nonexistent)
@@ -1,285 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.tool.pageaccess');
-
-org.ametys.web.tool.pageaccess.PageAccessTool = function(id, pluginName) 
-{
-	org.ametys.web.tool.pageaccess.PageAccessTool.superclass.constructor.call(this, id, pluginName);
-};
-
-Ext.extend(org.ametys.web.tool.pageaccess.PageAccessTool, org.ametys.uitool.tool.Tool, {});
-
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.createPanel = function()
-{
-	this._tree = new org.ametys.web.pageaccess.PageAccessTreePanel(this._pluginName, {
-		region: 'center',
-		pagePath: '/',
-		rootID: 'root'
-	});
-	
-	this._tree.getSelectionModel().on('selectionchange', this._onSelectionChange, this);
-	
-	this._mainPanel = new Ext.Panel({
-		layout: 'border',
-		items:[
-            this._tree
-		]
-	});
-	
-	this._mainPanel.addListener('focus', this._onFocus, this);
-	this._mainPanel.addListener('blur', this._onBlur, this);
-	this._mainPanel.addListener('close', this._onClose, this);
-	
-	return this._mainPanel;
-}
-
-
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.setParams = function(params)
-{
-	this._params = params;
-	
-	if (this._params['initialize'] != null)
-	{
-		eval(this._params['initialize'] + "(this, '" + this._params['context'] + "');");
-	}
-	
-	this.refresh();
-}
-
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.refresh = function()
-{
-    this._tree.getRootNode().reload();
-}
-
-/**
- * Update the page path.
- * @param pagePath the new page path.
- * @param init true if the page path is initialized to force refreshing
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.updatePage = function(targetType, pagePath, pageId, init)
-{
-	if (this._params.pagePath != pagePath || init)
-	{
-		this.refreshing();
-		
-		this._title = this._params['btnLabel'];
-		
-		this._params.pageId = pageId;
-		this._params.pagePath = pagePath;
-		this._params.targetType = targetType;
-		
-		this._tree.updatePage(pagePath, {}, init, this._updatePageCb.createDelegate(this));
-	}
-}
-
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype._updatePageCb = function(pagePath, init)
-{
-	this.stopRefreshing();
-}
-
-/**
- * Update the help text to display in north region to explain in which context
- * @param text The HTML text to set
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.updatePageHelperText = function(text)
-{
-	this._tree.updatePageHelperText(text);
-}
-
-/**
- * Call when selection change in the grid
- * @param Ext.grid.AbstractSelectionModel sm The selection model
- * @param node The selected node. Can be null.
- * @private
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype._onSelectionChange = function(sm, node)
-{
-	if (node != this._currentNode)
-	{
-		this._currentNode = node;
-		this._sendMessage(node);
-        this._tree.getTopToolbar().items.get(3).setDisabled (node == null || node.attributes.inherited != 'true');
-	}
-}
-
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.onMessage = function(messages)
-{
-	for (var i=messages.length - 1; i >= 0; i--)
-	{
-		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
-		{
-			var targets = messages[i].getTargets();
-			if (this._params['selection-listener'] != null)
-			{
-				eval(this._params['selection-listener'] + "(this, targets);");
-			}
-			
-			this.updatePageFromSelection(targets);
-	    }
-		else if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_CREATED)
-		{
-			var targets = messages[i].getTargets();
-			var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == 'pageaccess' });
-			if (target != null)
-			{
-				this._tree.getRootNode().reload();
-			}
-		}
-		else if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED)
-		{
-			var targets = messages[i].getTargets();
-            var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == 'pageaccess' });
-            if (target != null)
-            {
-                this._tree.getRootNode().reload();
-            }
-		}
-		
-	}
-}
-
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.updatePageFromSelection = function(targets, init)
-{
-    var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) { return target.getType() == 'page' || target.getType() == 'sitemap';});
-    if (target != null)
-    {
-        if (target.getType() == 'page')
-        {
-            var pageId = target.getParameters().id;
-            var pagePath = target.getParameters().lang + '/' + target.getParameters().path;
-            
-            var text = "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_HELP_1"/>";
-            text += '<b>' + target.getParameters().title + '</b>';
-            text += "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_HELP_2"/>";
-            
-            this.updatePage('page', pagePath, pageId, init);
-            this.updatePageHelperText(text);
-        }
-        else if (target.getType() == 'sitemap')
-        {
-            var sitemapId = target.getParameters().id;
-            var sitemapName = target.getParameters().lang;
-            
-            var text = "<i18n:text i18n:key='PLUGINS_WEB_PAGEACCESS_SITEMAP'/>";
-            
-            this.updatePage('sitemap', sitemapName, sitemapId, init);
-            this.updatePageHelperText(text);
-        }
-    }
-}
-
-/**
- * @private Send the message
- * @param {Ext.tree.Node} node The selected node. Can be null
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype._sendMessage = function(node)
-{
-	if (!this._hasFocus)
-	{
-		return;
-	}
-	
-	if (node == null)
-	{
-		return;
-	}
-	
-	var targets = [];
-	
-	var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('pageaccess', {'pagePath': this._params.pagePath, 'targetType': this._params.targetType});
-	
-	if (this._params.targetType == 'page')
-    {
-	    target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('page', {'id': this._params.pageId}));
-    }
-	else if (this._params.targetType == 'sitemap')
-    {
-	    target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('sitemap', {'id': this._params.pageId}));
-    }
-	
-	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.groupId, 'name': node.attributes.name}));
-	}
-	else if (node.attributes.type == 'excluded-user')
-	{
-		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 == 'excluded-group')
-	{
-		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('excluded-group', {'id': node.attributes.groupId, 'name': node.attributes.name}));
-	}
-	
-	var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, [target]);
-	org.ametys.messagebus.MessageBus.getInstance().fireMessages([message]);	
-}
-
-/**
- * Triggered when the panel is loaded.
- * 
- * @private
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype.open = function()
-{
-    var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
-    this.updatePageFromSelection(targets, true);
-}
-
-/**
- * Listener when the panel receives the focus Change the ribbon title and send a
- * selection message
- * 
- * @private
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype._onFocus = function()
-{
-    this._hasFocus = true;
-
-    if (this._tree != null)
-    {
-        var node = this._tree.getSelectionModel().getSelectedNode();
-        if (node != null)
-        {
-            this._currentNode = node;
-            this._sendMessage(node);
-        }
-    }
-}
-
-/**
- * Listener when the panel looses the focus
- * 
- * @private
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype._onBlur = function()
-{
-    this._hasFocus = false;
-}
-
-/**
- * Listener when the panel is closed. Send a no selection message.
- * 
- * @private
- */
-org.ametys.web.tool.pageaccess.PageAccessTool.prototype._onClose = function()
-{
-	this._sendMessage();
-}
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessToolFactory.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessToolFactory.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessToolFactory.js	(nonexistent)
@@ -1,42 +0,0 @@
-/*
- *  Copyright 2010 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.tool.pageaccess');
-
-org.ametys.web.tool.pageaccess.PageAccessToolFactory = function(id, config, pluginName) 
-{
-	org.ametys.web.tool.pageaccess.PageAccessToolFactory.superclass.constructor.call(this, id, config, pluginName);
-};
-
-Ext.extend(org.ametys.web.tool.pageaccess.PageAccessToolFactory, org.ametys.uitool.tool.impl.UniqueInstanceToolFactory, {});
-
-org.ametys.web.tool.pageaccess.PageAccessToolFactory.prototype.createTool = function(id)
-{
-	var tool = new org.ametys.web.tool.pageaccess.PageAccessTool(id, this.getPluginName());
-	tool.setConfiguration(this._config);
-	return tool;
-}
-
-
-org.ametys.web.tool.pageaccess.PageAccessToolFactory.prototype.openTool = function(params)
-{
-	org.ametys.web.tool.pageaccess.PageAccessToolFactory.superclass.openTool.call(this, params);
-	
-	var tool = org.ametys.uitool.ToolsManager.getInstance().getTool(this._id);
-	tool.onEvent(tool, org.ametys.ribbon.RibbonManager.EVENTTYPE_TOOLSTATECHANGED);
-	
-	tool.open();
-}
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessTreePanel.js	(nonexistent)
@@ -1,297 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.pageaccess');
-
-org.ametys.web.pageaccess.PageAccessTreePanel = function(pluginName, config) 
-{
-    this._pluginName = pluginName;
-    this._pagePath = config.pagePath;
-    
-    var loader = new org.ametys.web.pageaccess.PageAccessXmlLoader({
-        dataUrl: getPluginDirectUrl(this._pluginName) + '/' + context.parameters.siteName + '/pageaccess/rights.xml',
-        syncMode: true // synchronous mode for filters
-    });
-    loader.addListener('beforeload', this._onBeforeLoad, this);
-    loader.addListener('load', this._onLoad, this);
-    
-    config.loader = loader;
-    
-    config.rootVisible = false;
-    config.root = new Ext.tree.AsyncTreeNode({
-        id: config.rootID,
-        text: ' <b>' + "<i18n:text i18n:key="PAGEACCESS_TREEPANEL_ROOT_NODE"/>" + '</b>',
-        icon: getPluginResourcesUrl(this._pluginName) + '/img/profiles/profile_16.png'
-    });
-    
-    // Filter
-    config.filter = new Ext.tree.TreeFilter(this, {
-        clearBlank: true,
-        autoClear: true
-    });
-    
-    this._filterInput = new Ext.form.TextField({
-        id: 'filter',
-        
-        bodyStyle: 'margin-right: 10px;',
-        width: 250,
-        emptyText: "<i18n:text i18n:key="PAGEACCESS_TREEPANEL_FILTER_BY_NAME"/>",
-
-        enableKeyEvents:true
-    });
-    this._filterInput.addListener('keyup', this._filterByName, this);
-    
-    // Helper
-    this._pageHelperPanel = new org.ametys.HtmlContainer({
-        html: '',
-        cls: 'context-helper-text'
-    });
-    
-    config.tbar = [
-        this._filterInput,
-        '-',
-        ' ',
-        new Ext.Button({
-      	   tooltip: "<i18n:text i18n:key="PAGEACCESS_TREEPANEL_BTN_UPTOCONTEXT_TOOLTIP"/>",
-      	   handler: this._upToPage,
-      	   disabled : true,
-      	   scope: this,
-      	   icon: getPluginResourcesUrl('core') + '/img/profiles/actions/up_16.png',
-      	   cls: 'x-btn-text-icon'
-         }),
- 		'-',
-        new Ext.Button({
-            tooltip: "<i18n:text i18n:key="PAGEACCESS_TREEPANEL_BTN_EXPAND_TOOTIP"/>",
-            handler: function() { this.expandAll() },
-            scope: this,
-            icon: getPluginResourcesUrl('core') + '/img/profiles/actions/expand-all.gif',
-            cls: 'x-btn-text-icon'
-        }),
-        new Ext.Button({
-           tooltip: "<i18n:text i18n:key="PAGEACCESS_TREEPANEL_BTN_COLLAPSE_TOOTIP"/>",
-           handler: function() { this.collapseAll() },
-           scope: this,
-           icon: getPluginResourcesUrl('core') + '/img/profiles/actions/collapse-all.gif',
-           cls: 'x-btn-text-icon'
-        }),
-        '-',
-        this._pageHelperPanel
-    ];
-    
-    org.ametys.web.pageaccess.PageAccessTreePanel.superclass.constructor.call(this, config);
-};
-
-Ext.extend(org.ametys.web.pageaccess.PageAccessTreePanel, Ext.tree.TreePanel, {
-    
-    autoScroll: true,
-    animate: true,
-    enableDD: false,
-    containerScroll: true,
-    border: true,
-    
-    /**
-     * @cfg {String} pagePath The initial page path.
-     */
-    pagePath: '',
-    
-    /**
-     * @cfg {String} rootID The resources root id.
-     */
-    rootID: null
-});
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._pluginName;
-/** The current page path. */
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._pagePath;
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._loadParams;
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype.updatePage = function(pagePath, params, init, callback)
-{
-    this._pagePath = pagePath;
-    this._loadParams = params;
-    
-    // Reload tree
-    this.reloadTree.defer(500, this, [callback]);
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype.updatePageHelperText = function(text)
-{
-    if (this._pageHelperPanel)
-    {
-        this._pageHelperPanel.update(text);
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._upToPage = function()
-{
-	var node = this.getSelectionModel().getSelectedNode();
-	if (node != null)
-	{
-		var path = node.attributes.path;
-		// /[lang]/[path] for a page and /[lang] for a sitemap.
-		if (/^\/([a-z]{2})(\/(.+))?$/.test(path))
-		{
-			var params = {"siteName": context.parameters.siteName, "language": RegExp.$1};
-			if (RegExp.$2 != null && 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);
-
-			if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key="PLUGINS_WEB_TOOL_PAGE_PAGETOOL_CONVERTPATHTOID_ERROR"/>", response, "org.ametys.web.pageaccess.PageAccessTreePanel._upToPage"))
-			{
-				return false;
-			}
-			
-			var node = response.selectSingleNode("page|sitemap");
-			var type = node.nodeName;
-			var id = node[org.ametys.servercomm.ServerComm.xmlTextContent];
-			if (id != '')
-			{
-				var pageTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget(type, {'id': id});
-				var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, [pageTarget]);
-				org.ametys.messagebus.MessageBus.getInstance().fireMessages([message]);
-			}
-		}
-	}
-}
-
-/**
- * Reload the whole tree
- * @param callback the callback function
- */
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype.reloadTree = function(callback)
-{
-    var rootNode = this.getRootNode();
-    if (rootNode != null)
-    {
-        this.getRootNode().reload(callback);
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._onBeforeLoad = function(loader, node, cb)
-{
-    if (!this.rendered)
-    {
-        return;
-    }
-    
-    if (node != null && node.id != this.getRootNode().id)
-    {
-        loader.baseParams.profile = node.attributes.id;
-    }
-    else
-    {
-        loader.baseParams.profile = null;
-    }
-    loader.baseParams.pagePath = this._pagePath;
-    
-    if (this._loadParams != null)
-    {
-        for (var i in this._loadParams)
-        {
-            loader.baseParams[i] = this._loadParams[i];
-        }
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._onLoad = function(loader, node, response)
-{
-	this._setTooltips(node);
-    
-    // select first node
-    if (node.childNodes[0])
-    {
-        node.childNodes[0].select();
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._setTooltips = function(node)
-{
-    var childNodes = node.childNodes;
-    for (var i=0; i < childNodes.length; i++)
-    {
-        this._setTooltip(childNodes[i]);
-        this._setTooltips(childNodes[i]);
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._setTooltip = function(node)
-{
-    // Remove if exists
-    var tooltip = Ext.getCmp('tooltip-' + node.id);
-    if (tooltip)
-    {
-        tooltip.destroy();
-    }
-    
-    var html =  this._createTooltip (node.attributes);
-    if (html != null)
-    {
-	    // Create tooltip
-	    new Ext.ToolTip({
-	        id: 'tooltip-' + node.id,
-	        target: node.getUI().getTextEl(),
-	        html: this._createTooltip (node.attributes),
-	        dismissDelay: 0
-	    });
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._createTooltip = function(attributes)
-{
-    if ((attributes.type == 'granted-user' || attributes.type == 'granted-group' || attributes.type == 'excluded-user' || attributes.type == 'excluded-group') && attributes.unknown != 'true')
-    {
-        var html = "<div class='right-assign-tooltip'>";
-        html += "<b>" + attributes.name + "</b><br/>"
-        html += "<u>" + "<i18n:text i18n:key="PAGEACCESS_TREEPANEL_PAGE_TOOLTIP"/>" + "</u> : " + attributes.page;
-        html += "<br/>" + attributes.path + ".html"
-        html += "</div>";
-        
-        return html;
-    }
-    else
-    {
-       return null;
-    }
-}
-
-/**
- * @private Clear the filters applied to the tree nodes
- */
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._initFilters = function()
-{
-    this.filter.clear();
-    this._filterInput.setValue('');
-    
-    if (!this.getTopToolbar().items.get(3).pressed)
-    {
-        this.filter.filterBy( function(node) {
-            return node.attributes.inherit != 'true';
-        });
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessTreePanel.prototype._filterByName = function(field, event)
-{
-    this.filter.clear();
-    var val = field.getRawValue()
-    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' || node.attributes.type == 'excluded-users' || node.attributes.type == 'excluded-groups';
-    });
-}
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/PageAccessXmlLoader.js	(nonexistent)
@@ -1,254 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-// Ametys Namespace
-Ext.namespace('org.ametys.web.pageaccess');
-
-org.ametys.web.pageaccess.PageAccessXmlLoader = Ext.extend(org.ametys.tree.XmlTreeLoader, {});
-
-org.ametys.web.pageaccess.PageAccessXmlLoader.prototype.processResponse = function(response, node, callback) 
-{
-    this._nodeTags = {};
-    this._nodeTags['granted-users'] = '';
-    this._nodeTags['granted-groups'] = '';
-    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);
-}
-
-org.ametys.web.pageaccess.PageAccessXmlLoader.prototype.parseXml = function(node) 
-{
-    var nodes = [];
-    
-    var allowAnyConnectedUser = false;
-    var allowAnyConnectedUserInherited = false;
-    var hasLimitations = false;
-    var childNodes = node.childNodes;
-
-    for (var i = 0; i < childNodes.length; i++)
-    {
-    	if (childNodes[i].tagName == 'allow-any-connected-user' && childNodes[i][org.ametys.servercomm.ServerComm.xmlTextContent] == 'true')
-        {
-            allowAnyConnectedUser = true;
-            allowAnyConnectedUserInherited = childNodes[i].getAttribute('inherited') == 'true';
-        }
-        else
-        {
-        	hasLimitations = true;
-        }
-    }
-    
-    if (allowAnyConnectedUser)
-    {
-    	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'});
-        }
-        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 (hasLimitations)
-    {
-        // Normal XML processing.
-    	nodes.push.apply(nodes, org.ametys.web.pageaccess.PageAccessXmlLoader.superclass.parseXml.call(this, node));
-    }
-    else if (!allowAnyConnectedUser)
-    {
-        // No limitation: public access.
-        var textNode = new Ext.tree.TreeNode({text: "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_FREE_ACCESS"/>", icon: getPluginResourcesUrl('web') + '/img/pageaccess/no_limited_16.png'});
-        nodes.push(textNode);
-    }
-
-    return nodes;
-}
-
-org.ametys.web.pageaccess.PageAccessXmlLoader.prototype.processAttributes = function(attr)
-{
-    org.ametys.web.pageaccess.PageAccessXmlLoader.superclass.processAttributes.call(this, attr);
-    
-    if (attr.tagName == 'granted-users')
-    {
-        attr.text = " <b><i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_USERS"/></b>",
-        attr.type = 'granted-users';
-        attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/user_list_16.png';
-        attr.leaf = false;
-        attr.loaded = true;
-        attr.expanded = true;
-    }
-    else if (attr.tagName == 'granted-groups')
-    {
-        attr.text = " <b><i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_AUTHORIZED_GROUPS"/></b>",
-        attr.type = 'granted-groups';
-        attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/group_list_16.png';
-        attr.leaf = false;
-        attr.loaded = true;
-        attr.expanded = true;
-    }
-    else if (attr.tagName == 'excluded-users')
-	{
-        attr.text = " <b><i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_USERS"/></b>",
-        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 = " <b><i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_EXCLUDED_GROUPS"/></b>",
-        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')
-    	{
-    		attr.text = ' <i>' + "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER"/>" + ' (' + attr.login + ')</i>';
-    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/user_unknown_16.png';
-    	}
-    	else
-    	{
-    		attr.text = ' ' + attr.name + ' (' + attr.login + ')';
-    		attr.icon = getPluginResourcesUrl('core') + '/img/profiles/user_16.png';
-    	}
-        
-        attr.type = 'granted-user';
-        attr.leafSort = true; // attribute for sorting
-        
-        if (attr.inherited == 'true')
-        {
-            attr.cls = 'inherit';
-        }
-        
-        attr.leaf = true;
-    }
-    else if (attr.tagName == 'granted-group')
-    { 
-    	if (attr.unknown == 'true')
-    	{
-    		attr.text = ' <i>' + "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP"/>" + ' (' + attr.id + ')</i>';
-    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/group_unknown_16.png';
-    	}
-    	else
-    	{
-    		attr.text = ' ' + attr.name;
-    		attr.icon = getPluginResourcesUrl('core') + '/img/profiles/group_16.png';
-    	}
-    	
-        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 = ' <i>' + "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER"/>" + ' (' + attr.login + ')</i>';
-    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/user_unknown_16.png';
-    	}
-    	else
-    	{
-    		attr.text = ' ' + attr.name + ' (' + attr.login + ')';
-    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/exclude_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 = ' <i>' + "<i18n:text i18n:key="PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP"/>" + ' (' + attr.id + ')</i>';
-    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/group_unknown_16.png';
-    	}
-    	else
-    	{
-    		attr.text = ' ' + attr.name;
-    		attr.icon = getPluginResourcesUrl('web') + '/img/pageaccess/exclude_group_16.png';
-    	}
-    	
-        attr.type = 'excluded-group';
-        attr.leafSort = true; // attribute for sorting
-        
-        if (attr.inherited == 'true')
-        {
-            attr.cls = 'inherit';
-        }
-        
-        attr.leaf = true;
-    }
-}
-
-org.ametys.web.pageaccess.PageAccessXmlLoader.prototype.createNode = function(node) 
-{
-    var attr = {
-        tagName: node.tagName
-    };
-
-    Ext.each(node.attributes, function(a) {
-        attr[a.nodeName] = a.nodeValue;
-    });
-    
-    this._currentXMLNode = node;
-    this.processAttributes(attr);
-    delete this._currentXMLNode;
-    
-    if (this.baseAttrs)
-    {
-        Ext.applyIf(attr, this.baseAttrs);
-    }
-    if (this.applyLoader !== false)
-    {
-        attr.loader = this;
-    }
-    if (typeof attr.uiProvider == 'string')
-    {
-       attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);
-    }
-    
-    return new Ext.tree.TreeNode(attr);
-}
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/RemoveAccessButton.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/RemoveAccessButton.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/RemoveAccessButton.js	(nonexistent)
@@ -1,38 +0,0 @@
-/*
- *  Copyright 2012 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.pageaccess');
-
-org.ametys.web.pageaccess.RemoveAccessButton = function(id, configuration, pluginName)
-{
-	org.ametys.web.pageaccess.RemoveAccessButton.superclass.constructor.call(this, id, configuration, pluginName);
-}
-
-Ext.extend(org.ametys.web.pageaccess.RemoveAccessButton, org.ametys.ribbon.control.button.RightActionSelectionButton, {});
-
-org.ametys.web.pageaccess.RemoveAccessButton.prototype._hasRight = function(target)
-{
-	var pageTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst([target], function (target) { return target.getType() == 'page' || target.getType() == 'sitemap'; });
-
-	if (pageTarget == null)
-	{
-		return false;
-	}
-	else
-	{
-		return org.ametys.web.pageaccess.RemoveAccessButton.superclass._hasRight.call(this, pageTarget);
-	}
-}
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 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessActionButton.js	(nonexistent)
@@ -1,86 +0,0 @@
-/*
- *  Copyright 2012 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.tool.pageaccess.control');
-
-org.ametys.web.tool.pageaccess.control.PageAccessActionButton = function(id, configuration, pluginName)
-{
-	org.ametys.web.tool.pageaccess.control.PageAccessActionButton.superclass.constructor.call(this, id, configuration, pluginName);
-};
-
-Ext.extend(org.ametys.web.tool.pageaccess.control.PageAccessActionButton, org.ametys.ribbon.control.button.RightActionSelectionButton, {});
-
-org.ametys.web.tool.pageaccess.control.PageAccessActionButton.prototype.onMessage = function(messages)
-{
-    for (var i=0; i < messages.length; i++)
-    {
-        if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
-        {
-            var targets = messages[i].getTargets();
-            var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, this._testTarget.createDelegate(this));
-            
-            if (target != null)
-            {
-                if (!this._hasRight(target))
-                {
-                    var text = this._getTooltip(this.getConfiguration()["no-right-description"]);
-                    this.updateTooltip(text, null, null);
-                    
-                    this.getElements().each(function(element) {element.disable();});
-                }
-                else
-                {
-                    var params = {targets: [target.getParameters().id]};
-                    
-                    // Set the toggled state.
-                    org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, params);
-                }
-            }
-            else
-            {
-                var text = this._getTooltip(this.getConfiguration()["no-selection-description"]);
-                this.updateTooltip(text, null, null);
-                this.getElements().each(function(element) {element.disable();});
-            }
-        }
-    }
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessActionButton.prototype.configurationError = function(response)
-{
-    this.getElements().each(function(element) { element.stopRefreshing(); element.setDisabled(true); });
-    this.updateTooltip(this.getConfiguration()['default-description']);
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessActionButton.prototype.configurationChanged = function(params)
-{
-    this.getElements().each(function(element) { element.stopRefreshing(); });
-    
-    var allowAnyConnectedUserCount = parseInt(params['allow-any-connected-user-count']);
-    var inheritedCount = parseInt(params['inherited-rectriction-count']);
-    
-    if (allowAnyConnectedUserCount > 0 && inheritedCount == 0 && this.getConfiguration()['disable-on-allow-any-user'] == 'true')
-    {
-        // 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();});
-        this.updateTooltip(this.getConfiguration()['any-connected-user-description'], null, null);
-    }
-    else
-    {
-        this.getElements().each(function(element) {element.enable();});
-        this.updateTooltip(this.getConfiguration()['default-description'], null, null);
-    }
-}
Index: main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessButton.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessButton.js	(revision 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/PageAccessButton.js	(nonexistent)
@@ -1,262 +0,0 @@
-/*
- *  Copyright 2010 Anyware Services
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.tool.pageaccess.control');
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton = function(id, configuration, pluginName) 
-{
-	org.ametys.web.tool.pageaccess.control.PageAccessButton.superclass.constructor.call(this, id, configuration, pluginName);
-};
-
-Ext.extend(org.ametys.web.tool.pageaccess.control.PageAccessButton, org.ametys.ribbon.control.button.ToolButton, {});
-
-/**
- * @private
- * @property {Map<String, String>} _lastParams The parameters received on the last refresh
- */
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype._lastParams;
-
-/**
- * @private
- * @property {String[]} _currentPagesId The pages id currently selected or null. 
- */
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype._currentPagesId;
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype._areTheSame = function (tab1, tab2)
-{
-	if (tab1 == tab2)
-	{
-		return true;
-	}
-
-	if (tab1 == null || tab2 == null || tab1.length != tab2.length)
-	{
-		return false;
-	}	
-	
-	for (var i=0; i < tab1.length; i++)
-	{
-		if (tab2.indexOf(tab1[i]) == -1)
-		{
-			return false;
-		}
-	}
-	
-	return true;
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype._doReload = function()
-{
-	if (this._currentPagesId.length == 1)
-	{
-		this.getElements().each(
-			function(element)
-			{
-				element.setDisabled(true);
-				element.refreshing();
-			}
-		);
-		this.updateTooltip(this.getConfiguration()["refresh-description"]);
-		org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, {targets: this._currentPagesId});
-	}
-}
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype.onMessage = function (messages)
-{
-	// SELECTION
-	for (var i=messages.length - 1; i >= 0; i--)
-	{
-		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
-		{
-			var targets = messages[i].getTargets();
-			var allTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, this.checkTarget());
-			var allTargetsId = this._convertToId(allTargets);
-			
-			// No page selected
-			if (allTargets.length == 0)
-			{
-				this._currentPagesId = [];
-				this.getElements().each(function(element) { element.setDisabled(true); });
-				this.updateTooltip(this.getConfiguration()["default-description"]);
-				break;
-			}
-			// A single target selected or multiselection is enabled
-			else if (allTargets.length == 1)
-			{
-				if (!this._areTheSame(this._currentPagesId, allTargetsId))
-				{
-					this._currentPagesId = allTargetsId;
-					
-					// Only test modifiable if the target is a page.
-					if (allTargets[0].getType() == 'page' && allTargets[0].getParameters().isModifiable != 'true')
-					{
-						var iconSmall = context.contextPath + this.getConfiguration()['icon-small'];
-						var iconLarge = context.contextPath + this.getConfiguration()['icon-medium'];
-						
-						this.getElements().each(
-								function (element) 
-								{
-									element.iconSmall = iconSmall;
-									element.iconLarge = iconLarge;
-								}
-							);
-						
-						this.getElements().each(function(element) { element.updateIcon(); });
-						this.updateTooltip(this.getConfiguration()["no-modifiable-description"], iconLarge, null);
-						
-						this.getElements().each(function(element) {element.disable();});
-					}
-					else
-					{
-					    // Case of a modifiable page or a sitemap.
-						this.getElements().each(function(element) { element.setDisabled(true); element.refreshing(); });
-						this.updateTooltip(this.getConfiguration()["refresh-description"]);
-						org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, {targets: this._currentPagesId});
-					}
-					
-					break;
-				}
-			}
-			// Too many pages selected
-			else
-			{
-				this._currentPagesId = [];
-				this.getElements().each(function(element) { element.setDisabled(true); });
-				this.updateTooltip(this.getConfiguration()["multiselection-disabled-description"]);
-				break;
-			}
-		}
-		else if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_MINORMETADATACHANGED)
-		{
-			var targets = messages[i].getTargets();
-			var allTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, this.checkTarget());
-			var allTargetsId = this._convertToId(allTargets);
-			
-			// No page selected
-			if (allTargets.length == 0)
-			{
-				this._currentPagesId = [];
-				this.getElements().each(function(element) { element.setDisabled(true); });
-				this.updateTooltip(this.getConfiguration()["default-description"]);
-				break;
-			}
-			// A single target selected or multiselection is enabled
-			else if (allTargets.length == 1)
-			{
-				this._currentPagesId = allTargetsId;
-				
-				if (allTargets[0].getParameters().isModifiable != 'true')
-				{
-					var iconSmall = context.contextPath + this.getConfiguration()['icon-small'];
-					var iconLarge = context.contextPath + this.getConfiguration()['icon-medium'];
-					
-					this.getElements().each(
-							function (element) 
-							{
-								element.iconSmall = iconSmall;
-								element.iconLarge = iconLarge;
-							}
-						);
-					
-					this.getElements().each(function(element) { element.updateIcon(); });
-					this.updateTooltip(this.getConfiguration()["no-modifiable-description"], iconLarge, null);
-					
-					this.getElements().each(function(element) {element.disable();});
-				}
-				else
-				{
-					this.getElements().each(function(element) { element.setDisabled(true); element.refreshing(); });
-					this.updateTooltip(this.getConfiguration()["refresh-description"]);
-					org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, {targets: this._currentPagesId});
-				}
-				
-				break;
-			}
-			// Too many pages selected
-			else
-			{
-				this._currentPagesId = [];
-				this.getElements().each(function(element) { element.setDisabled(true); });
-				this.updateTooltip(this.getConfiguration()["multiselection-disabled-description"]);
-				break;
-			}
-		}
-	}
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype._convertToId = function (targets)
-{
-	var targetsIds = [];
-	for (var i = 0; i < targets.length; i++)
-	{
-		targetsIds.push(targets[i].getParameters()['id']);
-	}
-	return targetsIds;
-}
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype.checkTarget = function ()
-{
-	return function (target) { return target.getType() == 'page' || target.getType() == 'sitemap'; };
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype.configurationError = function(response)
-{
-	this.getElements().each(function(element) { element.stopRefreshing(); element.setDisabled(true); });
-	this.updateTooltip(this.getConfiguration()["default-description"]);
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype.configurationChanged = function(params)
-{
-	this.getElements().each(function(element) { element.stopRefreshing(); });
-
-	this._getIcon(params);
-	var tooltip = this.getConfiguration()["default-description"];
-	var count = params != null ? parseInt(params['private-page-size']) : 0;
-	if (count > 0)
-	{
-		tooltip = this.getConfiguration()["private-description"];
-	}
-	
-	this.getElements().each(function(element) { element.stopRefreshing(); element.setDisabled(false); });
-	this.getElements().each(function(element) { 
-		element.updateIcon(); 
-	});
-	this.updateTooltip(tooltip);
-	
-	this._lastParams = params;
-}
-
-org.ametys.web.tool.pageaccess.control.PageAccessButton.prototype._getIcon = function(params)
-{
-	var nbPrivate = params != null ? parseInt(params['private-page-size']) : 0;
-	var nbPublic = params != null ? parseInt(params['public-page-size']) : 0;
-	
-	var iconSmall = context.contextPath + this.getConfiguration()['icon-small'];
-	var iconLarge = context.contextPath + this.getConfiguration()['icon-medium'];
-	
-	
-	if (nbPrivate > 0)
-	{
-		iconSmall = context.contextPath + this.getConfiguration()['private-icon-small'];
-		iconLarge = context.contextPath + this.getConfiguration()['private-icon-medium'];
-	}
-
-	this.getElements().each(
-		function (element) 
-		{
-			element.iconSmall = iconSmall;
-			element.iconLarge = iconLarge;
-		}
-	);
-}
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 32509)
+++ main/plugin-web/resources/js/org/ametys/web/tool/pageaccess/control/SetAnyUserButton.js	(nonexistent)
@@ -1,145 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-Ext.namespace('org.ametys.web.tool.pageaccess.control');
-
-org.ametys.web.tool.pageaccess.control.SetAnyUserButton = function(id, configuration, pluginName)
-{
-	org.ametys.web.tool.pageaccess.control.SetAnyUserButton.superclass.constructor.call(this, id, configuration, pluginName);
-};
-
-Ext.extend(org.ametys.web.tool.pageaccess.control.SetAnyUserButton, org.ametys.ribbon.control.button.RightActionSelectionButton, {});
-
-/**
- * Creates the toggle button
- * @param {String} size The size between 'large', 'small' and 'very-small'
- * @param {integer} colspan The configured colspan. Must be transmited to the control.
- * @return {Ext.form.field} The toggle button created. 
- */
-org.ametys.web.tool.pageaccess.control.SetAnyUserButton.prototype.createControl = function(size, colspan)
-{
-    org.ametys.messagebus.MessageBus.getInstance().register(this);
-    
-    var initFn = this.getConfiguration()['initialize'];
-    if (initFn != null)
-    {
-        eval(initFn + "(this.getId(), this.getConfiguration(), this.getPluginName());");
-    }
-    
-    this._toolTipImage = this._getTooltipImage ();
-    this._toolTipText = this.getConfiguration()['default-description'];
-    this._toolTipFooter = this.getConfiguration()['footer'];
-    
-    var element = new org.ametys.fluent.controls.Button({
-        text: this.getConfiguration()['label'], 
-        iconSmall: context.contextPath + this.getConfiguration()['icon-small'],
-        iconLarge: context.contextPath + this.getConfiguration()['icon-medium'],
-
-        size: size,
-        colspan: colspan,
-        
-        tooltip: org.ametys.fluent.Tooltip.create(
-            this.getConfiguration()['label'], 
-            this._toolTipImage, 
-            this._toolTipText, 
-            this._toolTipFooter 
-        ),
-        
-        pressed: this.getConfiguration()['pressed'] | false,
-        enableToggle: true,
-        toggleHandler: this.onToggle,
-        
-        controlId: this.getId()
-    });
-    
-    this.getElements().add(element);
-    
-    return element;
-}
-
-org.ametys.web.tool.pageaccess.control.SetAnyUserButton.prototype.onMessage = function(messages)
-{
-    for (var i=0; i < messages.length; i++)
-    {
-        if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
-        {
-            var targets = messages[i].getTargets();
-            var target = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, this._testTarget.createDelegate(this));
-            
-            if (target != null)
-            {
-                if (!this._hasRight(target))
-                {
-                    var text = this._getTooltip(this.getConfiguration()["no-right-description"]);
-                    this.updateTooltip(text, null, null);
-                    
-                    this.getElements().each(function(element) {element.disable();});
-                }
-                else
-                {
-                    var params = {targets: [target.getParameters().id]};
-                    
-                    // Set the toggled state.
-                    org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, params);
-                }
-            }
-            else
-            {
-                var text = this._getTooltip(this.getConfiguration()["no-selection-description"]);
-                this.updateTooltip(text, null, null);
-                this.getElements().each(function(element) {element.disable();});
-            }
-        }
-    }
-}
-
-org.ametys.web.tool.pageaccess.control.SetAnyUserButton.prototype.configurationError = function(response)
-{
-    this.getElements().each(function(element) { element.stopRefreshing(); element.setDisabled(true); });
-    this.updateTooltip(this.getConfiguration()['default-description']);
-}
-
-org.ametys.web.tool.pageaccess.control.SetAnyUserButton.prototype.configurationChanged = function(params)
-{
-    this.getElements().each(function(element) { element.stopRefreshing(); });
-    
-    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 && inheritedCount == 0;
-    var disabledState = allowAnyConnectedUserCount > 0 && inheritedCount > 0 && negativeRestrictionPage == 0;
-    
-    this.updateTooltip(this.getConfiguration()["default-description"], null, null);
-    
-    this.getElements().each(function(element) {
-        element.setDisabled(disabledState);
-        element.toggle(toggledState, true);
-    });
-}
-
-/**
- * @private
- * This handler is called when the toogle button state change
- */
-org.ametys.web.tool.pageaccess.control.SetAnyUserButton.prototype.onToggle = function(button, state)
-{
-    var control = org.ametys.ribbon.RibbonManager.getInstance().getControls()[this.controlId];
-    if (control.getConfiguration()['action'] != null)
-    {
-        eval(control.getConfiguration()['action'] + "(control.getId(), control.getConfiguration(), control.getPluginName(), state);");
-    }
-}
Index: main/plugin-web/sitemap-back.xmap
===================================================================
--- main/plugin-web/sitemap-back.xmap	(revision 32586)
+++ main/plugin-web/sitemap-back.xmap	(working copy)
@@ -78,8 +75,6 @@
 
             <map:generator name="search-content" src="org.ametys.web.search.SearchGenerator" label="xml" />
 
-            <map:generator name="page-access" src="org.ametys.web.pageaccess.PageAccessGenerator" logger="org.ametys.web.pageaccess.PageAccessGenerator" />
-
             <map:generator name="profiles" src="org.ametys.web.profiles.ProfilesGenerator" label="xml" />
             <map:generator name="rights-profiles-context" src="org.ametys.web.profiles.ProfilesByContextGenerator" label="xml" />
 
@@ -109,9 +104,11 @@
             <map:action name="site-config" src="org.ametys.web.site.SiteConfigurationAction" />
 
             <map:action name="sitemap" src="org.ametys.web.cocoon.SitemapAction" />
-
-
-
+            
+            <map:action name="page-access" src="org.ametys.web.pageaccess.GetPageAccessAction" />   
+            
+            <map:action name="service-params" src="org.ametys.web.service.GetServiceParametersAction"/>
+            
             <map:action name="administrator-is-system-user" src="org.ametys.runtime.plugins.admin.IsAdminAction" logger="org.ametys.runtime.plugins.web.administrator.sitemap.action.isAdmin" />
 
             <map:action name="disallow-robots" src="org.ametys.web.repository.page.actions.DisallowRobotsAction" />
@@ -354,13 +346,20 @@
                     <map:read type="json" />
                 </map:act>
             </map:match>
-
+            
             <map:match pattern="repository/sites.json">
                 <map:act type="sites">
                     <map:parameter name="recursive" value="true" />
                     <map:read type="json" />
                 </map:act>
             </map:match>
+            
+            <!-- + | PAGE ACCESS + -->
+            <map:match pattern="repository/pageaccess.json">
+                <map:act type="page-access">
+                    <map:read type="json" />
+                </map:act>
+            </map:match>
 
             <map:match pattern="repository/languages.xml">
                 <map:generate type="languages" />

@@ -1075,70 +1036,6 @@
                 <map:serialize type="xml" />
             </map:match>
 
-            <!-- + | PAGE ACCESS + -->
-            <map:match pattern="*/pageaccess/rights.xml">
-                <map:generate type="page-access">
-                    <map:parameter name="site" value="{1}" />
-                    <map:parameter name="page" value="{request-param:pagePath}" />
-                </map:generate>
-                <map:serialize type="xml" />
-            </map:match>
-
-            <map:match pattern="*/pageaccess/add">
-                <map:act type="set-page-access">
-                    <map:parameter name="mode" value="add" />
-                    <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/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" />
-                    <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/set">
-                <map:act type="set-page-access">
-                    <map:parameter name="mode" value="set" />
-                    <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/set-any-user">
-                <map:act type="set-page-access">
-                    <map:parameter name="mode" value="set-any-user" />
-                    <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>
-
             <!-- + | LAST PUBLICATION + -->
             <map:match pattern="contents/last-published">
                 <map:generate type="last-publication" />
Index: main/plugin-web/src/org/ametys/web/pageaccess/GetPageAccessAction.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/GetPageAccessAction.java	(revision 0)
+++ main/plugin-web/src/org/ametys/web/pageaccess/GetPageAccessAction.java	(revision 0)
@@ -0,0 +1,415 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.web.pageaccess;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.acting.ServiceableAction;
+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;
+import org.xml.sax.SAXException;
+
+import org.ametys.core.cocoon.JSonReader;
+import org.ametys.core.group.Group;
+import org.ametys.core.group.GroupsManager;
+import org.ametys.core.user.User;
+import org.ametys.core.user.UsersManager;
+import org.ametys.core.util.I18nizableText;
+import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.web.cocoon.I18nUtils;
+import org.ametys.web.repository.page.MetadataAwarePagesContainer;
+import org.ametys.web.repository.page.Page;
+import org.ametys.web.repository.page.PagesContainer;
+import org.ametys.web.repository.site.SiteManager;
+import org.ametys.web.repository.sitemap.Sitemap;
+
+/**
+ * Get the page access tree data
+ */
+public class GetPageAccessAction extends ServiceableAction
+{
+    /** The site manager */
+    private SiteManager _siteManager;
+    /** The users manager */
+    private UsersManager _usersManager;
+    /** The groups manager */ 
+    private GroupsManager _groupsManager;
+    /** The page access manager */
+    private PageAccessManager _accessManager;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _usersManager = (UsersManager) serviceManager.lookup(UsersManager.ROLE + ".FO");
+        _groupsManager = (GroupsManager) serviceManager.lookup(GroupsManager.ROLE + ".FO");
+        _accessManager = (PageAccessManager) serviceManager.lookup(PageAccessManager.ROLE);
+        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        Map<String, Object> result = new HashMap<> ();
+         
+        String siteName = (String) request.getAttribute("siteName");
+        String pagePath = request.getParameter("pagePath");
+        
+        try
+        {
+            if (StringUtils.isNotEmpty(pagePath) && !pagePath.equals("/"))
+            {
+                Matcher matcher = PageAccessManager.__PAGE_PATH.matcher(pagePath);
+                if (matcher.matches())
+                {
+                    String sitemapName = matcher.group(1);
+                    Sitemap sitemap = _siteManager.getSite(siteName).getSitemap(sitemapName);
+                    String path = matcher.group(3);
+                    
+                    if (StringUtils.isNotEmpty(path))
+                    {
+                        // Get the page.
+                        Page page = sitemap.getChild(path);
+                        
+                        // Get the access information.
+                        PageAccessInfo accessInfo = _accessManager.getAccessInfo(page);
+
+                        // Generate the tree nodes
+                        result.put("children", _accessInfo2JSON(page, accessInfo));
+                    }
+                    else
+                    {
+                        // Case of a sitemap.
+                        
+                        // Get the access information.
+                        PageAccessInfo accessInfo = _accessManager.getAccessInfo(sitemap);
+                        
+                        // Generate the tree nodes
+                        result.put("children", _accessInfo2JSON(sitemap, accessInfo));
+                    }
+                }
+            }
+        }
+        catch (UnknownAmetysObjectException e)
+        {
+            throw new ProcessingException("Unknown page path : " + pagePath, e);
+        }
+        
+        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
+        return java.util.Collections.EMPTY_MAP;
+    }
+    
+    /**
+     * Generate the access info on a given pages container (page or sitemap).
+     * @param container the container from which the info is generated.
+     * @param accessInfo the access information.
+     * @throws SAXException if an error occurs.
+     */
+    private List<Map<String, Object>> _accessInfo2JSON(PagesContainer container, PageAccessInfo accessInfo) throws SAXException
+    {
+        List<Map<String, Object>> accessInfoList = new ArrayList<> ();
+      
+        MetadataAwarePagesContainer restrictedPage = accessInfo.getPageForPositiveRestriction();
+        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";
+        }    
+        
+        if (accessInfo.allowAnyConnectedUser() || !accessInfo.isRestricted())
+        {
+            accessInfoList.add(_allowAnyConnectedUser2JSON(accessInfo, restrictedPath));
+        }
+        
+        if (accessInfo.isRestricted())
+        {
+            if (!accessInfo.getGrantedUsers().isEmpty())
+            {
+                accessInfoList.add(_grantedUsers2JSON(container, accessInfo, title, type, restrictedPath));
+            }
+            
+            if (!accessInfo.getGrantedGroups().isEmpty())
+            {
+                accessInfoList.add(_grantedGroups2JSON(container, accessInfo, title, type, restrictedPath));
+            }
+            
+            if (!accessInfo.getExcludedUsers().isEmpty())
+            {
+                accessInfoList.add(_excludedUsers2JSON(container, accessInfo));
+            }
+            
+            if (!accessInfo.getExcludedGroups().isEmpty())
+            {
+                accessInfoList.add(_excludedGroups2JSON(container, accessInfo));
+            }
+        }
+        
+        return accessInfoList;
+    }
+    
+    private Map<String, Object> _allowAnyConnectedUser2JSON(PageAccessInfo accessInfo, String restrictedPath)
+    {
+        Map<String, Object> allowAnyConnectedUser = new HashMap<> ();
+        allowAnyConnectedUser.put("type", "allow-any-connected-user");
+        allowAnyConnectedUser.put("path", restrictedPath);
+        
+        boolean anyConnectedUserAllowed = accessInfo.allowAnyConnectedUser();
+        allowAnyConnectedUser.put("anyConnectedUserAllowed", anyConnectedUserAllowed);
+        
+        I18nizableText text = null;
+        if (anyConnectedUserAllowed)
+        {
+            text = new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER");
+            if (accessInfo.isInherited())
+            {
+                text = new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_ALLOW_ANY_CONNECTED_USER_INHERITED"); 
+                allowAnyConnectedUser.put("inherited", true); // default attribute value is "false"
+            }
+        }
+        else if (!accessInfo.isRestricted())
+        {
+            text = new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_FREE_ACCESS");
+        }
+        
+        allowAnyConnectedUser.put("rawText", text);
+        return  allowAnyConnectedUser;
+    }
+    
+    private Map<String, Object> _grantedUsers2JSON(PagesContainer container, PageAccessInfo accessInfo, String title, String type, String restrictedPath)
+    {
+        Map<String, Object> result = new HashMap<> ();
+        result.put("type", "granted-users");
+        result.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_AUTHORIZED_USERS")); 
+        
+        List<Map<String, Object>> grantedUsers = new ArrayList<> ();
+        for (String login : accessInfo.getGrantedUsers())
+        {
+            Map<String, Object> grantedUser = new HashMap<> ();
+
+            User user = _usersManager.getUser(login);
+            if (user != null)
+            {
+                grantedUser.put("login", login);
+                grantedUser.put("name", user.getFullName());
+                grantedUser.put("rawText", user.getFullName() + "(" + login + ")");
+                grantedUser.put("inherited", accessInfo.isInherited());
+                grantedUser.put("page", title);
+                grantedUser.put("path", restrictedPath);
+                grantedUser.put("sitemap", String.valueOf(type.equals("sitemap")));
+                grantedUser.put("type", "granted-user");
+            }
+            else
+            {
+                getLogger().warn("The page " + container.getPath() + " references the user '" + login + "' as granted but the UsersManager did not find him.");
+                
+                grantedUser.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER") + "(" + login + ")");
+                grantedUser.put("unknown", true);
+                grantedUser.put("inherited", accessInfo.isInherited());
+                grantedUser.put("type", "granted-user");
+            }
+            
+            grantedUsers.add(grantedUser);
+        }
+        
+        result.put("children", grantedUsers);
+        return result;
+    }
+    
+    private Map<String, Object> _grantedGroups2JSON(PagesContainer container, PageAccessInfo accessInfo, String title, String type, String restrictedPath)
+    {
+        Map<String, Object> result = new HashMap<> ();
+        result.put("type", "granted-groups");
+        result.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_AUTHORIZED_GROUPS"));
+        
+        List<Map<String, Object>> grantedGroups = new ArrayList<> ();
+        for (String groupId : accessInfo.getGrantedGroups())
+        {
+            Map<String, Object> grantedGroup = new HashMap<> ();
+
+            Group group = _groupsManager.getGroup(groupId);
+            if (group != null)
+            {
+                grantedGroup.put("name", group.getLabel());
+                grantedGroup.put("id", groupId);
+                grantedGroup.put("rawText", group.getLabel());
+                grantedGroup.put("inherited", accessInfo.isInherited());
+                grantedGroup.put("page", title);
+                grantedGroup.put("path", restrictedPath);
+                grantedGroup.put("sitemap", String.valueOf(type.equals("sitemap")));
+                grantedGroup.put("type", "granted-group");
+            }
+            else
+            {
+                getLogger().warn("The page " + container.getPath() + " references the group '" + groupId + "' as granted but the GroupsManager did not find it.");
+
+                grantedGroup.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP") + "(" + groupId + ")");
+                grantedGroup.put("id", groupId);
+                grantedGroup.put("unknown", true);
+                grantedGroup.put("inherited", accessInfo.isInherited());
+                grantedGroup.put("type", "granted-group");
+            }
+            
+            
+            grantedGroups.add(grantedGroup);
+        }
+        
+        result.put("children", grantedGroups);
+        return result;
+    }
+
+    private Map<String, Object> _excludedUsers2JSON(PagesContainer container, PageAccessInfo accessInfo)
+    {
+        Map<String, Object> result = new HashMap<> ();
+        result.put("type", "excluded-users");
+        result.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_EXCLUDED_USERS"));
+        
+        List<Map<String, Object>> excludedUsersList = new ArrayList<> ();
+        Map<String, PagesContainer> excludedUsers = accessInfo.getExcludedUsers();
+        for (String login : excludedUsers.keySet())
+        {
+            Map<String, Object> excludedUser = new HashMap<> ();
+
+            // Get page info from accessInfo
+            PagesContainer restrictedPage = excludedUsers.get(login);
+            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
+            User user = _usersManager.getUser(login);
+            if (user != null)
+            {
+                excludedUser.put("login", login);
+                excludedUser.put("name", user.getFullName());
+                excludedUser.put("rawText", user.getFullName() + "(" + login + ")");
+                excludedUser.put("inherited", !container.equals(restrictedPage));
+                excludedUser.put("page", title);
+                excludedUser.put("path", restrictedPath);
+                excludedUser.put("sitemap", String.valueOf(type.equals("sitemap")));
+                excludedUser.put("type", "excluded-user");
+            }
+            else
+            {
+                getLogger().warn("The page " + container.getPath() + " references the user '" + login + "' as excluded but the UsersManager did not find him.");
+                
+                excludedUser.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_UNKNOWN_USER") + "(" + login + ")");
+                excludedUser.put("login", login);
+                excludedUser.put("unknown", true);
+                excludedUser.put("inherited", accessInfo.isInherited());
+                excludedUser.put("type", "excluded-user");
+            }
+            
+            excludedUsersList.add(excludedUser);
+        }
+        
+        result.put("children", excludedUsersList);        
+        return result;
+    }
+    
+    private Map<String, Object> _excludedGroups2JSON(PagesContainer container, PageAccessInfo accessInfo)
+    {
+        Map<String, Object> result = new HashMap<> ();
+        result.put("type", "excluded-groups");
+        result.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_EXCLUDED_GROUPS"));
+        
+        List<Map<String, Object>> excludedGroupsList = new ArrayList<> ();
+        
+        Map<String, PagesContainer> excludedGroups = accessInfo.getExcludedGroups();
+        for (String groupId : excludedGroups.keySet())
+        {
+            Map<String, Object> excludedGroup = new HashMap<> ();
+            
+            // 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";
+            }
+
+            Group group = _groupsManager.getGroup(groupId);
+            if (group != null)
+            {
+                excludedGroup.put("rawText", group.getLabel());
+                excludedGroup.put("id", groupId);
+                excludedGroup.put("name", group.getLabel());
+                excludedGroup.put("inherited", !container.equals(restrictedPage));
+                excludedGroup.put("page", title);
+                excludedGroup.put("path", restrictedPath);
+                excludedGroup.put("sitemap", String.valueOf(type.equals("sitemap")));
+                excludedGroup.put("type", "excluded-group");
+            }
+            else
+            {
+                getLogger().warn("The page " + container.getPath() + " references the group " + groupId + " as excluded but it can't be found by the GroupsManager.");
+                
+                excludedGroup.put("rawText", new I18nizableText("plugin.web", "PLUGINS_WEB_PAGEACCESS_UNKNOWN_GROUP") + "(" + groupId + ")");
+                excludedGroup.put("id", groupId);
+                excludedGroup.put("unknown", true);
+                excludedGroup.put("inherited", accessInfo.isInherited());
+                excludedGroup.put("type", "excluded-group");
+            }
+            
+            excludedGroupsList.add(excludedGroup);
+        }
+        
+        result.put("children", excludedGroupsList);
+        return result;
+    }
+}   
Index: main/plugin-web/src/org/ametys/web/pageaccess/PageAccessDAO.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/PageAccessDAO.java	(revision 0)
+++ main/plugin-web/src/org/ametys/web/pageaccess/PageAccessDAO.java	(revision 0)
@@ -0,0 +1,291 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.web.pageaccess;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.components.ContextHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.core.observation.Event;
+import org.ametys.core.observation.ObservationManager;
+import org.ametys.core.ui.Callable;
+import org.ametys.core.user.CurrentUserProvider;
+import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
+import org.ametys.web.ObservationConstants;
+import org.ametys.web.repository.page.ModifiablePage;
+import org.ametys.web.repository.site.SiteManager;
+import org.ametys.web.repository.sitemap.Sitemap;
+
+/**
+ * DAO for page access
+ */
+public class PageAccessDAO extends AbstractLogEnabled implements Component, Contextualizable, Serviceable
+{
+    /** The Avalon context */
+    private Context _context;
+    /** The current user provider */
+    private CurrentUserProvider _currentUserProvider;
+    /** The observation manager */
+    private ObservationManager _observationManager;
+    /** The site manager. */
+    private SiteManager _siteManager;
+    
+    @Override
+    public void contextualize(Context context) throws ContextException
+    {
+        _context = context;
+    }
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+        _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE);
+        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
+    }
+    
+    /**
+     * Set access limitations on a page
+     * @param ids the map of users or groups ids
+     * @param mode the mode
+     * @param pagePath the path of the page to set limitations on
+     * @return an empty map
+     * @throws ProcessingException 
+     */
+    @SuppressWarnings("unchecked")
+    @Callable
+    public Map setPageAccess(Map<String, Object> ids, String mode, String pagePath) throws ProcessingException
+    {
+        Request request = ContextHelper.getRequest(_context);
+        String siteName = (String) request.getAttribute("siteName");
+        
+        // Get the granted users.
+        Set<String> grantedUsers = new HashSet<>();
+        if (ids.get("grantedUsers") != null)
+        {
+            grantedUsers.addAll((List<String>) ids.get("grantedUsers"));
+        }
+        
+        // Get the granted groups.
+        Set<String> grantedGroups = new HashSet<>();
+        if (ids.get("grantedGroups") != null)
+        {
+            grantedGroups.addAll((List<String>) ids.get("grantedGroups"));
+        }
+        
+        // Get the excluded users.
+        Set<String> excludedUsers = new HashSet<>();
+        if (ids.get("excludedUsers") != null)
+        {
+            excludedUsers.addAll((List<String>) ids.get("excludedUsers"));
+        }
+        
+        // Get the excluded groups.
+        Set<String> excludedGroups = new HashSet<>();
+        if (ids.get("excludedGroups") != null)
+        {
+            excludedGroups.addAll((List<String>) ids.get("excludedGroups"));
+        }
+        
+        boolean anyConnectedUser = false;
+        if (ids.get("set-any-user") != null)
+        {
+            anyConnectedUser = (Boolean) ids.get("set-any-user");
+        }
+        
+        try
+        {
+            Matcher matcher = PageAccessManager.__PAGE_PATH.matcher(pagePath);
+            if (matcher.matches())
+            {
+                String sitemapName = matcher.group(1);
+                Sitemap sitemap = _siteManager.getSite(siteName).getSitemap(sitemapName);
+                String path = matcher.group(3);
+                
+                if (StringUtils.isNotEmpty(path))
+                {
+                    // Case of a page.
+                    ModifiablePage page = sitemap.getChild(path);
+                    
+                    // Set the limited access.
+                    _setLimitedAccess(mode, page.getMetadataHolder(), grantedUsers, grantedGroups, excludedUsers, excludedGroups, anyConnectedUser);
+                    
+                    if (page.needsSave())
+                    {
+                        page.saveChanges();
+                    }
+                    
+                    // Notify observers that page data has been changed.
+                    Map<String, Object> eventParams = new HashMap<>();
+                    eventParams.put(ObservationConstants.ARGS_PAGE, page);
+                    _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ACCESS_UPDATED, _currentUserProvider.getUser(), eventParams));
+                }
+                else
+                {
+                    // 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 site map data has been changed.
+                    Map<String, Object> eventParams = new HashMap<>();
+                    eventParams.put(ObservationConstants.ARGS_SITEMAP, sitemap);
+                    _observationManager.notify(new Event(ObservationConstants.EVENT_SITEMAP_ACCESS_UPDATED, _currentUserProvider.getUser(), eventParams));
+                }
+            }
+        }
+        catch (UnknownAmetysObjectException e)
+        {
+            throw new ProcessingException("Unknown page path : " + pagePath, e);
+        }
+        
+        return java.util.Collections.EMPTY_MAP;
+    }
+    
+    /**
+     * 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 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.
+     */
+    private void _setLimitedAccess(String mode, ModifiableCompositeMetadata meta, Set<String> grantedUsers, Set<String> grantedGroups, 
+            Set<String> excludedUsers, Set<String> excludedGroups, boolean anyConnectedUser)
+    {
+        switch(mode)
+        {
+            case "add" :
+                _add(meta, PageAccessManager.GRANTED_USERS_METADATA, grantedUsers);
+                _add(meta, PageAccessManager.GRANTED_GROUPS_METADATA, grantedGroups);
+                break;
+            
+            case "exclude": 
+                _add(meta, PageAccessManager.EXCLUDED_USERS_METADATA, excludedUsers);
+                _add(meta, PageAccessManager.EXCLUDED_GROUPS_METADATA, excludedGroups);
+                break;
+                
+            case "remove":
+                _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);
+                break;
+                
+            case "set-any-user": 
+                meta.setMetadata(PageAccessManager.GRANT_ANY_USER_METADATA, anyConnectedUser);
+                break;
+                
+            default:
+                _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);
+                break;
+        }
+    }
+    
+    /**
+     * Add the objects.
+     * @param meta the composite metadata.
+     * @param metaName the metadata name.remove
+     * @param objects the objects to add.
+     */
+    private void _add(ModifiableCompositeMetadata meta, String metaName, Set<String> objects)
+    {
+        if (CollectionUtils.isNotEmpty(objects))
+        {
+            String[] oldObjects = meta.getStringArray(metaName, new String[0]);
+            
+            Set<String> objectSet = new HashSet<>(Arrays.asList(oldObjects));
+            objectSet.addAll(objects);
+            
+            String[] newObjects = objectSet.toArray(new String[objectSet.size()]);
+            if (meta.hasMetadata(metaName))
+            {
+                meta.removeMetadata(metaName);
+            }
+            
+            meta.setMetadata(metaName, newObjects);
+        }
+    }
+    
+    /**
+     * Remove the objects.
+     * @param meta the composite metadata.
+     * @param metaName the metadata name.
+     * @param objects the objects to remove.
+     */
+    private void _remove(ModifiableCompositeMetadata meta, String metaName, Set<String> objects)
+    {
+        if (CollectionUtils.isNotEmpty(objects))
+        {
+            String[] oldObjects = meta.getStringArray(metaName, new String[0]);
+            
+            Set<String> objectSet = new HashSet<>(Arrays.asList(oldObjects));
+            objectSet.removeAll(objects);
+            
+            String[] newObjects = objectSet.toArray(new String[objectSet.size()]);
+            if (meta.hasMetadata(metaName))
+            {
+                meta.removeMetadata(metaName);
+            }
+            
+            meta.setMetadata(metaName, newObjects);
+        }
+    }
+    
+    /**
+     * Set the objects.
+     * @param meta the composite metadata.
+     * @param metaName the metadata name.
+     * @param objects the objects to remove.
+     */
+    private void _set(ModifiableCompositeMetadata meta, String metaName, Set<String> objects)
+    {
+        meta.removeMetadata(metaName);
+        if (CollectionUtils.isNotEmpty(objects))
+        {
+            String[] newObjects = objects.toArray(new String[objects.size()]);
+            meta.setMetadata(metaName, newObjects);
+        }
+    }
+}
Index: main/plugin-web/src/org/ametys/web/pageaccess/PageAccessGenerator.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/PageAccessGenerator.java	(revision 32623)
+++ main/plugin-web/src/org/ametys/web/pageaccess/PageAccessGenerator.java	(nonexistent)
@@ -1,363 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.ametys.web.pageaccess;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.avalon.framework.service.ServiceException;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.generation.ServiceableGenerator;
-import org.apache.cocoon.xml.AttributesImpl;
-import org.apache.cocoon.xml.XMLUtils;
-import org.apache.commons.lang.StringUtils;
-import org.xml.sax.SAXException;
-
-import org.ametys.core.group.Group;
-import org.ametys.core.group.GroupsManager;
-import org.ametys.core.user.User;
-import org.ametys.core.user.UsersManager;
-import org.ametys.plugins.repository.UnknownAmetysObjectException;
-import org.ametys.web.repository.page.MetadataAwarePagesContainer;
-import org.ametys.web.repository.page.Page;
-import org.ametys.web.repository.page.PagesContainer;
-import org.ametys.web.repository.site.SiteManager;
-import org.ametys.web.repository.sitemap.Sitemap;
-
-/**
- * Generates the lists of users and groups who have access to this page.
- */
-public class PageAccessGenerator extends ServiceableGenerator
-{
-    private static final Pattern __PAGE_PATH = Pattern.compile("([^/]+)(/(.+))?");
-    
-    private SiteManager _siteManager;
-    private UsersManager _usersManager;
-    private GroupsManager _groupsManager;
-    private PageAccessManager _accessManager;
-    
-    @Override
-    public void service(ServiceManager serviceManager) throws ServiceException
-    {
-        super.service(serviceManager);
-        _usersManager = (UsersManager) serviceManager.lookup(UsersManager.ROLE + ".FO");
-        _groupsManager = (GroupsManager) serviceManager.lookup(GroupsManager.ROLE + ".FO");
-        _accessManager = (PageAccessManager) serviceManager.lookup(PageAccessManager.ROLE);
-        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
-    }
-    
-    @Override
-    public void generate() throws IOException, SAXException, ProcessingException
-    {
-        String siteName = parameters.getParameter("site", "");
-        String pagePath = parameters.getParameter("page", "");
-        
-        try
-        {
-            contentHandler.startDocument();
-            
-            if (StringUtils.isNotEmpty(pagePath) && !pagePath.equals("/"))
-            {
-                Matcher matcher = __PAGE_PATH.matcher(pagePath);
-                if (matcher.matches())
-                {
-                    String sitemapName = matcher.group(1);
-                    Sitemap sitemap = _siteManager.getSite(siteName).getSitemap(sitemapName);
-                    String path = matcher.group(3);
-                    
-                    if (StringUtils.isNotEmpty(path))
-                    {
-                        // Get the page.
-                        Page page = sitemap.getChild(path);
-                        
-                        // Get the access information.
-                        PageAccessInfo accessInfo = _accessManager.getAccessInfo(page);
-                        
-                        AttributesImpl atts = new AttributesImpl();
-                        atts.addCDATAAttribute("type", "page");
-                        atts.addCDATAAttribute("site", siteName);
-                        atts.addCDATAAttribute("path", pagePath);
-                        atts.addCDATAAttribute("title", page.getTitle());
-                        atts.addCDATAAttribute("long-title", page.getLongTitle());
-                        atts.addCDATAAttribute("id", page.getId());
-                        
-                        XMLUtils.startElement(contentHandler, "container", atts);
-                        
-                        // Generate the granted users list.
-                        _saxAccessInfo(page, accessInfo);
-                        
-                        XMLUtils.endElement(contentHandler, "container");
-                    }
-                    else
-                    {
-                        // Case of a sitemap.
-                        
-                        // Get the access information.
-                        PageAccessInfo accessInfo = _accessManager.getAccessInfo(sitemap);
-                        
-                        AttributesImpl atts = new AttributesImpl();
-                        atts.addCDATAAttribute("type", "sitemap");
-                        atts.addCDATAAttribute("site", siteName);
-                        atts.addCDATAAttribute("path", pagePath);
-                        atts.addCDATAAttribute("title", sitemap.getSitemapName());
-                        atts.addCDATAAttribute("id", sitemap.getId());
-                        
-                        XMLUtils.startElement(contentHandler, "container", atts);
-                        
-                        // Generate the granted users list.
-                        _saxAccessInfo(sitemap, accessInfo);
-                        
-                        XMLUtils.endElement(contentHandler, "container");
-                    }
-                }
-                else
-                {
-                    XMLUtils.createElement(contentHandler, "container");
-                }
-            }
-            else
-            {
-                XMLUtils.createElement(contentHandler, "container");
-            }
-            contentHandler.endDocument();
-        }
-        catch (UnknownAmetysObjectException e)
-        {
-            throw new ProcessingException("Unknown page path : " + pagePath, e);
-        }
-    }
-
-    /**
-     * Generate the access info on a given pages container (page or sitemap).
-     * @param container the container from which the info is generated.
-     * @param accessInfo the access information.
-     * @throws SAXException if an error occurs.
-     */
-    protected void _saxAccessInfo(PagesContainer container, PageAccessInfo accessInfo) throws SAXException
-    {
-        if (accessInfo != null && accessInfo.isRestricted())
-        {
-            MetadataAwarePagesContainer restrictedPage = accessInfo.getPageForPositiveRestriction();
-            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";
-            }
-            
-            _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())
-            {
-                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);
-                }
-            }
-            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())
-            {
-                Group group = _groupsManager.getGroup(groupId);
-                if (group != null)
-                {
-                    AttributesImpl atts = new AttributesImpl();
-                    atts.addCDATAAttribute("groupId", 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);
-                }
-                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);
-                }
-            }
-            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())
-            {
-                // Get page info from accessInfo
-                PagesContainer restrictedPage = excludedUsers.get(login);
-                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
-                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("groupId", 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/PageAccessManager.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/PageAccessManager.java	(revision 32623)
+++ main/plugin-web/src/org/ametys/web/pageaccess/PageAccessManager.java	(working copy)
@@ -18,6 +18,7 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 import org.apache.avalon.framework.component.Component;
 import org.apache.avalon.framework.context.Context;
@@ -42,6 +43,9 @@
  */
 public class PageAccessManager extends AbstractLogEnabled implements Serviceable, Component, Contextualizable
 {
+    /** The pattern for a page path */
+    public static final Pattern __PAGE_PATH = Pattern.compile("([^/]+)(/(.+))?");
+    
     /** Avalon Role */
     public static final String ROLE = PageAccessManager.class.getName();
     
@@ -83,6 +87,7 @@
      * @param pageOrSitemap the page to get access info on.
      * @return the access information.
      */
+    @SuppressWarnings("unchecked")
     public PageAccessInfo getAccessInfo(MetadataAwarePagesContainer pageOrSitemap)
     {
         Map<String, PageAccessInfo> cache;
Index: main/plugin-web/src/org/ametys/web/pageaccess/SetPageAccessAction.java
===================================================================
--- main/plugin-web/src/org/ametys/web/pageaccess/SetPageAccessAction.java	(revision 32623)
+++ main/plugin-web/src/org/ametys/web/pageaccess/SetPageAccessAction.java	(nonexistent)
@@ -1,269 +0,0 @@
-/*
- *  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.
- *  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.ametys.web.pageaccess;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.avalon.framework.parameters.Parameters;
-import org.apache.avalon.framework.service.ServiceException;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.environment.ObjectModelHelper;
-import org.apache.cocoon.environment.Redirector;
-import org.apache.cocoon.environment.SourceResolver;
-import org.apache.commons.lang.StringUtils;
-
-import org.ametys.core.observation.AbstractNotifierAction;
-import org.ametys.core.observation.Event;
-import org.ametys.plugins.repository.AmetysObjectResolver;
-import org.ametys.plugins.repository.UnknownAmetysObjectException;
-import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
-import org.ametys.web.ObservationConstants;
-import org.ametys.web.repository.page.ModifiablePage;
-import org.ametys.web.repository.site.SiteManager;
-import org.ametys.web.repository.sitemap.Sitemap;
-
-/**
- * Action which sets the limited access on a given page or sitemap.
- */
-public class SetPageAccessAction extends AbstractNotifierAction
-{
-    private static final Pattern __PAGE_PATH = Pattern.compile("([^/]+)(/(.+))?");
-    
-    /** The ametys object resolver. */
-    protected AmetysObjectResolver _ametysObjectResolver;
-    /** The site manager. */
-    protected SiteManager _siteManager;
-    
-    @Override
-    public void service(ServiceManager serviceManager) throws ServiceException
-    {
-        super.service(serviceManager);
-        _ametysObjectResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
-        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
-    }
-    
-    @Override
-    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
-    {
-        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 granted users.
-        Set<String> grantedUsers = new HashSet<>();
-        if (parentContext.get("grantedUsers") != null)
-        {
-            grantedUsers.addAll((Collection<String>) parentContext.get("grantedUsers"));
-        }
-        
-        // Get the granted groups.
-        Set<String> grantedGroups = new HashSet<>();
-        if (parentContext.get("grantedGroups") != null)
-        {
-            grantedGroups.addAll((Collection<String>) parentContext.get("grantedGroups"));
-        }
-        
-        // Get the excluded users.
-        Set<String> excludedUsers = new HashSet<>();
-        if (parentContext.get("excludedUsers") != null)
-        {
-            excludedUsers.addAll((Collection<String>) parentContext.get("excludedUsers"));
-        }
-        
-        // Get the excluded groups.
-        Set<String> excludedGroups = new HashSet<>();
-        if (parentContext.get("excludedGroups") != null)
-        {
-            excludedGroups.addAll((Collection<String>) parentContext.get("excludedGroups"));
-        }
-        
-        boolean anyConnectedUser = false;
-        if (parentContext.get("set-any-user") != null)
-        {
-            anyConnectedUser = parentContext.get("set-any-user").equals("true");
-        }
-        
-        try
-        {
-            Matcher matcher = __PAGE_PATH.matcher(pagePath);
-            if (matcher.matches())
-            {
-                String sitemapName = matcher.group(1);
-                Sitemap sitemap = _siteManager.getSite(siteName).getSitemap(sitemapName);
-                String path = matcher.group(3);
-                
-                if (StringUtils.isNotEmpty(path))
-                {
-                    // Case of a page.
-                    ModifiablePage page = sitemap.getChild(path);
-                    
-                    // Set the limited access.
-                    setLimitedAccess(mode, page.getMetadataHolder(), grantedUsers, grantedGroups, excludedUsers, excludedGroups, anyConnectedUser);
-                    
-                    if (page.needsSave())
-                    {
-                        page.saveChanges();
-                    }
-                    
-                    // Notify observers that page data has been changed.
-                    Map<String, Object> eventParams = new HashMap<>();
-                    eventParams.put(ObservationConstants.ARGS_PAGE, page);
-                    _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ACCESS_UPDATED, _getCurrentUser(), eventParams));
-                }
-                else
-                {
-                    // 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 site map data has been changed.
-                    Map<String, Object> eventParams = new HashMap<>();
-                    eventParams.put(ObservationConstants.ARGS_SITEMAP, sitemap);
-                    _observationManager.notify(new Event(ObservationConstants.EVENT_SITEMAP_ACCESS_UPDATED, _getCurrentUser(), eventParams));
-                }
-            }
-        }
-        catch (UnknownAmetysObjectException e)
-        {
-            throw new ProcessingException("Unknown page path : " + pagePath, e);
-        }
-        
-        return EMPTY_MAP;
-    }
-    
-    /**
-     * 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 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> grantedUsers, Collection<String> grantedGroups, 
-                                    Collection<String> excludedUsers, Collection<String> excludedGroups, boolean anyConnectedUser)
-    {
-        if ("add".equals(mode))
-        {
-            _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, 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 // default mode : "set" 
-        {
-            _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.remove
-     * @param objects the objects to add.
-     */
-    protected void _add(ModifiableCompositeMetadata meta, String metaName, Collection<String> objects)
-    {
-        if (objects != null && !objects.isEmpty())
-        {
-            String[] oldObjects = meta.getStringArray(metaName, new String[0]);
-            
-            Set<String> objectSet = new HashSet<>(Arrays.asList(oldObjects));
-            objectSet.addAll(objects);
-            
-            String[] newObjects = objectSet.toArray(new String[objectSet.size()]);
-            if (meta.hasMetadata(metaName))
-            {
-                meta.removeMetadata(metaName);
-            }
-            
-            meta.setMetadata(metaName, newObjects);
-        }
-    }
-    
-    /**
-     * Remove the objects.
-     * @param meta the composite metadata.
-     * @param metaName the metadata name.
-     * @param objects the objects to remove.
-     */
-    protected void _remove(ModifiableCompositeMetadata meta, String metaName, Collection<String> objects)
-    {
-        if (objects != null && !objects.isEmpty())
-        {
-            String[] oldObjects = meta.getStringArray(metaName, new String[0]);
-            
-            Set<String> objectSet = new HashSet<>(Arrays.asList(oldObjects));
-            objectSet.removeAll(objects);
-            
-            String[] newObjects = objectSet.toArray(new String[objectSet.size()]);
-            if (meta.hasMetadata(metaName))
-            {
-                meta.removeMetadata(metaName);
-            }
-            
-            meta.setMetadata(metaName, newObjects);
-        }
-    }
-    
-    /**
-     * Set the objects.
-     * @param meta the composite metadata.
-     * @param metaName the metadata name.
-     * @param objects the objects to remove.
-     */
-    protected void _set(ModifiableCompositeMetadata meta, String metaName, Collection<String> objects)
-    {
-        meta.removeMetadata(metaName);
-        if (objects != null && !objects.isEmpty())
-        {
-            String[] newObjects = objects.toArray(new String[objects.size()]);
-            meta.setMetadata(metaName, newObjects);
-        }
-    }
-}
