Index: plugins/anycontent/sitemap.xmap
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/sitemap.xmap,v
retrieving revision 1.10
diff -u -r1.10 sitemap.xmap
--- plugins/anycontent/sitemap.xmap	27 Oct 2008 15:37:07 -0000	1.10
+++ plugins/anycontent/sitemap.xmap	16 Mar 2009 09:38:56 -0000
@@ -23,6 +23,8 @@
            	<map:generator name="rights-profiles-list" src="org.ametys.runtime.plugins.core.right.ui.generators.ProfilesListGenerator" logger="org.ametys.runtime.plugins.core.right.sitemap.generator.profiles" label="content"/>
            
 			<map:generator name="content-history" src="org.ametys.anycontent.plugins.anycontent.history.ContentHistoryGenerator"/>
+            
+            <map:generator name="resources-rights-assignment" src="org.ametys.anycontent.plugins.anycontent.resourcesexplorer.generators.ResourcesRightsAssignmentGenerator"/>
 		</map:generators>
 		
 		<map:actions>
@@ -39,6 +41,10 @@
 			<map:action name="create-site" src="org.ametys.anycontent.plugins.anycontent.administrator.sites.action.CreateSiteAction"/>
 			<map:action name="delete-site" src="org.ametys.anycontent.plugins.anycontent.administrator.sites.action.DeleteSiteAction"/>
 			
+			<map:action name="has-not-resources-right" src="org.ametys.anycontent.plugins.anycontent.resourcesexplorer.HasResourcesRightAction" logger="anycontent.plugins.anycontent.actions">
+                <has-right>false</has-right>
+            </map:action>
+            
 			<map:action name="resources-explorer-create" src="org.ametys.anycontent.plugins.anycontent.resourcesexplorer.CreateDir" logger="anycontent.plugins.anycontent.actions"/>
 			<map:action name="resources-explorer-delete" src="org.ametys.anycontent.plugins.anycontent.resourcesexplorer.DeleteFile" logger="anycontent.plugins.anycontent.actions"/>
 			<map:action name="resources-explorer-exists" src="org.ametys.anycontent.plugins.anycontent.resourcesexplorer.ExistsFile" logger="anycontent.plugins.anycontent.actions"/>
@@ -54,6 +60,10 @@
 			<map:action name="is-admin" src="org.ametys.runtime.plugins.core.right.actions.IsAdminAction"/>
 		</map:actions>
 		
+        <map:transformers>
+            <map:transformer name="check-rights-on-resource" src="org.ametys.anycontent.plugins.anycontent.resourcesexplorer.RightOnResourcesTransformer" />
+        </map:transformers>
+        
 	</map:components>
 	
 <!-- =========================== Views =================================== -->
@@ -69,7 +79,7 @@
 
 	<!-- RESOURCES -->
 	<map:resources>
-		<!-- Expplorateur de fichier -->
+		<!-- Explorateur de fichier -->
 		<map:resource name="resources_explorer/generator">
 			<map:generate src="resource-explorer://{config:core.resources-explorer}/{site}" type="traversable">
 				<map:parameter name="depth" value="10000"/>
@@ -79,9 +89,16 @@
 				<map:parameter name="uri" value="{path}"/>
 				<map:parameter name="restrictToURI" value="{restrictToPath}"/>
 			</map:transform>
+            <map:transform type="check-rights-on-resource">
+                <map:parameter name="site" value="{site}"/>
+            </map:transform>
+            <map:transform src="pages/resources-explorer/remove-unauthorized-directories.xsl">
+                <map:parameter name="uri" value="{path}"/>
+                <map:parameter name="restrictToURI" value="{restrictToPath}"/>
+            </map:transform>
 		</map:resource>
 		
-		<!-- Expplorateur de fichier -->
+		<!-- Explorateur de fichier -->
 		<map:resource name="resources_explorer/generatorLazy">
 			<map:generate src="resource-explorer://{config:core.resources-explorer}/{site}/{path}" type="traversable">
 				<map:parameter name="depth" value="2"/>
@@ -93,6 +110,13 @@
 				<map:parameter name="lazy" value="lazy"/>
 				<map:parameter name="restrictToURI" value="{restrictToPath}"/>
 			</map:transform>
+            <map:transform type="check-rights-on-resource">
+                <map:parameter name="site" value="{site}"/>
+            </map:transform>
+            <map:transform src="pages/resources-explorer/remove-unauthorized-directories.xsl">
+                <map:parameter name="uri" value="{path}"/>
+                <map:parameter name="restrictToURI" value="{restrictToPath}"/>
+            </map:transform>
 		</map:resource>
 	</map:resources>
 	
@@ -344,11 +368,22 @@
                 <map:serialize type="xml"/>
             </map:match>
             
+            <map:match pattern="rights/rightcontext-resources.xml">
+                <map:generate type="resources-rights-assignment" />
+                <map:transform type="decoding-xslt" src="pages/rights/context/resources-sitemap.xsl">
+                    <map:parameter name="contextPath" value="{request:contextPath}" />
+                    <map:parameter name="myPlugin" value="anycontent" />
+                    <map:parameter name="page" value="{request-param:page}" />
+                </map:transform>
+                <map:serialize type="xml" />
+            </map:match>
+            
             <map:match pattern="rights/view.html">
                 <map:aggregate element="ViewRights" label="xml">
                 	<map:part src="cocoon:/rights/profiles.xml"/>
                     <map:part src="cocoon:/rights/rightcontext-contributor.xml"/>
                     <map:part src="cocoon:/rights/rightcontext-content.xml"/>
+                    <map:part src="cocoon:/rights/rightcontext-resources.xml"/>
                 </map:aggregate>
                 <map:transform type="xslt" src="pages/rights/view.xsl">
                     <map:parameter name="defaultRightContext" value="Contributor"/>
@@ -383,6 +418,16 @@
 		<!-- ************************* -->
 		<!-- Explorateur de ressources -->
 		<!-- ************************* -->
+        <map:pipeline>
+            <map:match pattern="resources_explorer/generator/*">
+                <map:call resource="resources_explorer/generator">
+                    <map:parameter name="path" value="{request-param:path}"/>
+                    <map:parameter name="site" value="{1}"/>
+                </map:call>
+                <map:serialize type="xml"/>
+            </map:match>
+        </map:pipeline>
+        
 		<map:pipeline>
 			<!-- Explorateur -->
 			<map:match pattern="resources-explorer/explorer.html">
@@ -402,6 +447,12 @@
 			</map:match>
 			
 			<map:match pattern="resources-explorer/list">
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="read"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:path}"/>
+                </map:act>
+                
 				<map:generate src="resource-explorer://{config:core.resources-explorer}/{request-param:sitename}/{request-param:path}" type="traversable">
 					<map:parameter name="depth" value="1"/>
 					<map:parameter name="sort" value="directory"/>
@@ -449,6 +500,11 @@
 
 			<!-- Commandes -->		
 			<map:match pattern="resources-explorer/create">
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="write"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:name}"/>
+                </map:act>
 				<map:act type="resources-explorer-create">
 					<map:parameter name="resourcesDir" value="{config:core.resources-explorer}/{request-param:sitename}"/>
 					<map:parameter name="filename" value="{request-param:name}"/>
@@ -463,6 +519,11 @@
 			</map:match>
 			
 			<map:match pattern="resources-explorer/delete">
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="write"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:name}"/>
+                </map:act>
 				<map:act type="resources-explorer-delete">
 					<map:parameter name="resourcesDir" value="{config:core.resources-explorer}/{request-param:sitename}"/>
 					<map:parameter name="filename" value="{request-param:name}"/>
@@ -477,6 +538,17 @@
 			</map:match>
 			
 			<map:match pattern="resources-explorer/move">
+                <!-- Check first the read right on the source file, then the write right on the destination file.-->
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="read"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:file}"/>
+                </map:act>
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="write"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:newfile}"/>
+                </map:act>
 				<map:act type="resources-explorer-move">
 					<map:parameter name="base" value="{config:core.resources-explorer}/{request-param:sitename}/{request-param:file}"/>
 					<map:parameter name="target" value="{config:core.resources-explorer}/{request-param:sitename}/{request-param:newfile}"/>
@@ -491,6 +563,17 @@
 			</map:match>
 			
 			<map:match pattern="resources-explorer/copy">
+                <!-- Check first the read right on the source file, then the write right on the destination file.-->
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="read"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:file}"/>
+                </map:act>
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="write"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:newfile}"/>
+                </map:act>
 				<map:act type="resources-explorer-copy">
 					<map:parameter name="base" value="{config:core.resources-explorer}/{request-param:sitename}/{request-param:file}"/>
 					<map:parameter name="target" value="{config:core.resources-explorer}/{request-param:sitename}/{request-param:newfile}"/>
@@ -505,6 +588,11 @@
 			</map:match>
 			
 			<map:match pattern="resources-explorer/exists">
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="read"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:name}"/>
+                </map:act>
 				<map:act type="resources-explorer-exists">
 					<map:parameter name="resourcesDir" value="{config:core.resources-explorer}/{request-param:sitename}"/>
 					<map:parameter name="filename" value="{request-param:name}"/>
@@ -519,6 +607,11 @@
 			</map:match>
 			
 			<map:match pattern="resources-explorer/upload.html">
+                <map:act type="has-not-resources-right">
+                    <map:parameter name="right" value="write"/>
+                    <map:parameter name="sitename" value="{request-param:sitename}"/>
+                    <map:parameter name="path" value="{request-param:path}"/>
+                </map:act>
 				<map:act type="resources-explorer-upload">
 					<map:parameter name="resourcesDir" value="{config:core.resources-explorer}/{request-param:sitename}"/>
 					<map:parameter name="filename" value="{request-param:filename}"/>
Index: plugins/anycontent/plugin.xml
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/plugin.xml,v
retrieving revision 1.29
diff -u -r1.29 plugin.xml
--- plugins/anycontent/plugin.xml	13 Mar 2009 16:53:42 -0000	1.29
+++ plugins/anycontent/plugin.xml	16 Mar 2009 09:38:56 -0000
@@ -1868,17 +1868,37 @@
             <extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
                        id="anycontent.use.rights.resourcesexplorer">
                 <!-- 
-                    Define the right for handling users
+                    Define the resources explorer rights : access, read and write.
                  -->
                 <right id="Runtime_Rights_ResourcesExplorer">
                     <label>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_LABEL</label>
                     <description>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_DESCRIPTION</description>
                     <category>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY</category>
                 </right>
+                <right id="Runtime_Rights_ResourcesExplorer_ReadAll">
+                    <label>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READALL_LABEL</label>
+                    <description>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READALL_DESCRIPTION</description>
+                    <category>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY</category>
+                </right>
+                <right id="Runtime_Rights_ResourcesExplorer_WriteAll">
+                    <label>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITEALL_LABEL</label>
+                    <description>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITEALL_DESCRIPTION</description>
+                    <category>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY</category>
+                </right>
+                <right id="Runtime_Rights_ResourcesExplorer_ReadResource">
+                    <label>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READ_RESOURCE_LABEL</label>
+                    <description>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READ_RESOURCE_DESCRIPTION</description>
+                    <category>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY</category>
+                </right>
+                <right id="Runtime_Rights_ResourcesExplorer_WriteResource">
+                    <label>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITE_RESOURCE_LABEL</label>
+                    <description>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITE_RESOURCE_DESCRIPTION</description>
+                    <category>PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY</category>
+                </right>
             </extension>
         </extensions>
     </feature>
-    	
+    
 	<feature name="search">
 		<extensions>
 			<extension  point="org.ametys.runtime.ui.item.UIItemManager" 
Index: plugins/anycontent/i18n/messages_en.xml
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/i18n/messages_en.xml,v
retrieving revision 1.18
diff -u -r1.18 messages_en.xml
--- plugins/anycontent/i18n/messages_en.xml	18 Dec 2008 13:47:45 -0000	1.18
+++ plugins/anycontent/i18n/messages_en.xml	16 Mar 2009 09:38:56 -0000
@@ -24,6 +24,14 @@
     <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY">Resources Explorer</message>
     <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_LABEL">Access</message>
     <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_DESCRIPTION">Access to the resources explorer screen</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READALL_LABEL">Global read</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READALL_DESCRIPTION">Read the whole resources explorer</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITEALL_LABEL">Global write</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITEALL_DESCRIPTION">Write the whole resources explorer</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READ_RESOURCE_LABEL">Read</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READ_RESOURCE_DESCRIPTION">Read a directory in the resources explorer</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITE_RESOURCE_LABEL">Write</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITE_RESOURCE_DESCRIPTION">Write a directory in the resources explorer</message>
 
 	<message key="APPLICATION_RIGHTS_CONTENT_CATEGORY">Contents</message>
 	<message key="APPLICATION_RIGHTS_EDITION_ONLINE_LABEL">Edition on line</message>
@@ -196,6 +204,8 @@
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_RENAMEFILE_ERROR">Unable to rename the file.\nMake sure that the name is not already used.</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_RENAME_ERROR_ACCENTS">The file name must not contain any special characters.</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_ERROR_LISTING">Unable to read the directory content</message>
+	<message key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_UNAUTHORIZED_LISTING">You are not allowed to see the contents of this directory.</message>
+	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED">You are not allowed to modify this directory.</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_ACTION_VIEW_DETAIL">Switch to 'Details' view</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_ACTION_VIEW_THUMB">Switch to 'Miniatures' view</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_ACTION_VIEW_VIEWER">Switch to 'Overview'</message>
Index: plugins/anycontent/i18n/messages_fr.xml
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/i18n/messages_fr.xml,v
retrieving revision 1.13
diff -u -r1.13 messages_fr.xml
--- plugins/anycontent/i18n/messages_fr.xml	13 Mar 2009 16:53:43 -0000	1.13
+++ plugins/anycontent/i18n/messages_fr.xml	16 Mar 2009 09:38:56 -0000
@@ -22,8 +22,18 @@
 	<message key="PLUGINS_ANYCONTENT_RIGHTS_DESCRIPTION">Assignation de profils aux utilisateurs</message>
 	
     <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_CATEGORY">Explorateur de ressources</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_ASSIGN_LABEL">Explorateur de ressources</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_ASSIGN_DESCRIPTION">Affecte des droits sur les répertoires</message>
     <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_LABEL">Accès</message>
     <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_DESCRIPTION">Accéder à l'explorateur de ressources</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READALL_LABEL">Lecture globale</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READALL_DESCRIPTION">Lecture de la totalité de l'explorateur de ressources</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITEALL_LABEL">Modification globale</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITEALL_DESCRIPTION">Modifier la totalité de l'explorateur de ressources</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READ_RESOURCE_LABEL">Lecture</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_READ_RESOURCE_DESCRIPTION">Accès à un répertoire de l'explorateur de ressources</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITE_RESOURCE_LABEL">Modification</message>
+    <message key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_RIGHTS_WRITE_RESOURCE_DESCRIPTION">Modification d'un répertoire de l'explorateur de ressources</message>
     
 	<message key="APPLICATION_RIGHTS_CONTENT_CATEGORY">Contenus</message>
 	<message key="APPLICATION_RIGHTS_EDITION_ONLINE_LABEL">Edition en ligne</message>
@@ -196,6 +206,8 @@
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_RENAMEFILE_ERROR">Impossible de renommer le fichier.\nVérifiez que le nom n'existe pas déjà.</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_RENAME_ERROR_ACCENTS">Le nom du fichier ne doit pas contenir d'accents ou de caractères spéciaux.</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_ERROR_LISTING">Impossible de lire le contenu du répertoire</message>
+	<message key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_UNAUTHORIZED_LISTING">Vous n'avez pas le droit de lire le contenu de ce répertoire.</message>
+	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED">Vous n'avez pas le droit de modifier ce répertoire.</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_ACTION_VIEW_DETAIL">Passer en vue 'Détails'</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_ACTION_VIEW_THUMB">Passer en vue 'Miniatures'</message>
 	<message key="PLUGINS_ANYCONTENT_RESOURCESDIR_ACTION_VIEW_VIEWER">Passer en vue 'Aperçu'</message>
Index: plugins/anycontent/pages/resources-explorer/explorer.xsl
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/pages/resources-explorer/explorer.xsl,v
retrieving revision 1.12
diff -u -r1.12 explorer.xsl
--- plugins/anycontent/pages/resources-explorer/explorer.xsl	18 Dec 2008 16:40:47 -0000	1.12
+++ plugins/anycontent/pages/resources-explorer/explorer.xsl	16 Mar 2009 09:38:56 -0000
@@ -122,7 +122,11 @@
 
 															<div id="tree" style="width: 250px; height: 100%; border: 1px inset threedface;">
 																<img width="16px" src="{$reImgDir}/root.gif"/>
-																<a fullpath="" select=""><i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_ROOTNODE"/></a>
+																<a fullpath="" select="">
+                                                                    <xsl:copy-of select="@has-read-right"/>
+                                                                    <xsl:copy-of select="@has-write-right"/>
+                                                                    <i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_ROOTNODE"/>
+                                                                </a>
 																<xsl:call-template name="child"/>
 															</div>
 
@@ -184,28 +188,35 @@
 						{
 							if (tree.selection == null)
 								return; 
-								
-							var fullpath = tree.selection.getAttribute("fullpath");
-							
-							if (fullpath == "")
-								return;
 							
-                            var index = fullpath.lastIndexOf('/');
-							var path = index == -1 ? "" : fullpath.substring(0, index);
-							var name = index == -1 ? fullpath.substring(index) : fullpath.substring(index + 1);
-														
-							if (confirm("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETE_CONFIRM"/> '" + name + "' ?"))
-							{
-								var code = Tools.postUrlStatusCode(getPluginDirectUrl("<xsl:value-of select="$pluginName"/>") + "/resources-explorer/delete", "name=" + encodeURIComponent(fullpath) + "&amp;sitename=" + context.siteName)
-								if (code != 200)
-									alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETEDIR_ERROR"/>")
-								else
-								{
-									var link = tree.selection;
-									STree.select(link.tree, null, link._document);
-									stree.removeNode(link);
-								}
-							}
+                            if (tree.selection.getAttribute('has-write-right') == 'true')
+                            {
+    							var fullpath = tree.selection.getAttribute("fullpath");
+    							
+    							if (fullpath == "")
+    								return;
+    							
+                                var index = fullpath.lastIndexOf('/');
+    							var path = index == -1 ? "" : fullpath.substring(0, index);
+    							var name = index == -1 ? fullpath.substring(index) : fullpath.substring(index + 1);
+    														
+    							if (confirm("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETE_CONFIRM"/> '" + name + "' ?"))
+    							{
+    								var code = Tools.postUrlStatusCode(getPluginDirectUrl("<xsl:value-of select="$pluginName"/>") + "/resources-explorer/delete", "name=" + encodeURIComponent(fullpath) + "&amp;sitename=" + context.siteName)
+    								if (code != 200)
+    									alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETEDIR_ERROR"/>")
+    								else
+    								{
+    									var link = tree.selection;
+    									STree.select(link.tree, null, link._document);
+    									stree.removeNode(link);
+    								}
+    							}
+                            }
+                            else
+                            {
+                                alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                            }
 						}
 						
 						function delFile()
@@ -214,17 +225,24 @@
 							if (tree.selection == null || selection.length == 0)
 								return; 
 								
-							var element = selection[0];
-							var fullpath = tree.selection.getAttribute("fullpath") + "/" + element.name;
-							
-							if (confirm("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETE_CONFIRM"/> '" + element.name + "' ?"))
-							{
-								var code = Tools.postUrlStatusCode(getPluginDirectUrl("<xsl:value-of select="$pluginName"/>") + "/resources-explorer/delete", "name=" + encodeURIComponent(fullpath)  + "&amp;sitename=" + context.siteName)
-								if (code != 200)
-									alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETEDIR_ERROR"/>")
-								else
-									element.remove();
-							}
+                            if (stree.tree.selection != null &amp;&amp; stree.tree.selection.getAttribute('has-write-right') == 'true')
+                            {
+    							var element = selection[0];
+    							var fullpath = tree.selection.getAttribute("fullpath") + "/" + element.name;
+    							
+    							if (confirm("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETE_CONFIRM"/> '" + element.name + "' ?"))
+    							{
+    								var code = Tools.postUrlStatusCode(getPluginDirectUrl("<xsl:value-of select="$pluginName"/>") + "/resources-explorer/delete", "name=" + encodeURIComponent(fullpath)  + "&amp;sitename=" + context.siteName)
+    								if (code != 200)
+    									alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_DELETEDIR_ERROR"/>")
+    								else
+    									element.remove();
+    							}
+                            }
+                            else
+                            {
+                                alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                            }
 
 						}
 						
@@ -250,7 +268,13 @@
 							{
 								alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_UPLOAD_NOSELECT"/>")
 								return;
-							}							
+							}
+                            
+                            if (tree.selection.getAttribute('has-write-right') != 'true')
+                            {
+                                alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                                return;
+                            }
 
 							if (!newDirectoryDialog)
 							{
@@ -305,6 +329,8 @@
 									var li = stree.insertNode (tree.selection, "<xsl:value-of select="$reImgDir"/>/Folder.gif", name, null);
 									var a = STools.getDirectChildrenByTagName(li, "a")[0];
 									a.setAttribute("fullpath", toCreate);
+									a.setAttribute("has-read-right", 'true');
+									a.setAttribute("has-write-right", 'true');
 								}
 							
 						}
@@ -313,31 +339,38 @@
 						{
 							newDirectoryDialog.close();
 						}
-																
-						function upload()
+						
+						upload = function()
 						{
 							if (tree.selection == null)
 							{
 								alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_UPLOAD_NOSELECT"/>")
 								return;
 							}
-						
-							if (!uploadDialog)
-							{
-								var config = new SDialog.Config()
-								config.innerTableClass = "dialog";
-								uploadDialog = new SDialog("uploadTable", 
-																"<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_UPLOAD_DIALOG_CAPTION"/>",
-																"<xsl:value-of select="$relativeUrl"/>resources/img/resources-explorer/newfile.gif",
-																380, 100, 
-																config,
-																uploadDialogListener
-																);
-								uploadDialog.paint();
-								Tools.loadStyle(uploadDialog.ui.iframe.contentWindow.document, "<xsl:value-of select="$contextPath"/>/kernel/resources/css/dialog.css");
-							}
-							uploadDialog.showModal();
-							document.getElementById('uploadTableNewName').focus();
+                            
+                            if (tree.selection.getAttribute('has-write-right') == 'true')
+                            {
+    							if (!uploadDialog)
+    							{
+    								var config = new SDialog.Config()
+    								config.innerTableClass = "dialog";
+    								uploadDialog = new SDialog("uploadTable", 
+    																"<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_UPLOAD_DIALOG_CAPTION"/>",
+    																"<xsl:value-of select="$relativeUrl"/>resources/img/resources-explorer/newfile.gif",
+    																380, 100, 
+    																config,
+    																uploadDialogListener
+    																);
+    								uploadDialog.paint();
+    								Tools.loadStyle(uploadDialog.ui.iframe.contentWindow.document, "<xsl:value-of select="$contextPath"/>/kernel/resources/css/dialog.css");
+    							}
+    							uploadDialog.showModal();
+    							document.getElementById('uploadTableNewName').focus();
+                            }
+                            else
+                            {
+                                alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                            }
 						}
 						
 						function uploadDialogListener()
@@ -409,7 +442,14 @@
 						{
 							if (stree.tree.selection != null)
 							{
-								STree.edit(stree.tree, stree.tree.selection, stree._document);
+                                if (stree.tree.selection.getAttribute('has-write-right') == 'true')
+                                {
+								    STree.edit(stree.tree, stree.tree.selection, stree._document);
+                                }
+                                else
+                                {
+                                    alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                                }
 							}
 						}
 						
@@ -417,7 +457,14 @@
 						{
 							if (slistview.getSelection().length &gt; 0)
 							{
-								slistview.getSelection()[0].rename();
+                                if (stree.tree.selection != null &amp;&amp; stree.tree.selection.getAttribute('has-write-right') == 'true')
+                                {
+    								slistview.getSelection()[0].rename();
+                                }
+                                else
+                                {
+                                    alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                                }
 							}
 						}
 						
@@ -426,8 +473,15 @@
 						clipboard_cutting = false;
 						function clipboard_cut()
 						{
-							clipboard_copy();
-							clipboard_cutting = true;
+                            if (stree.tree.selection != null &amp;&amp; stree.tree.selection.getAttribute('has-write-right') == 'true')
+                            {
+                                clipboard_copy();
+                                clipboard_cutting = true;
+                            }
+                            else
+                            {
+                                alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                            }
 						}
 						function clipboard_copy()
 						{
@@ -449,7 +503,14 @@
 						function clipboard_paste()
 						{
 							if (stree.tree.selection == null)
-								return
+                            {
+								return;
+                            }
+                            if (stree.tree.selection.getAttribute('has-write-right') != 'true')
+                            {
+                                alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_WRITE_UNAUTHORIZED"/>");
+                                return;
+                            }
 
 							// fais la copie/d?placement...
 							clipboard_target = stree.tree.selection.getAttribute("fullpath");
@@ -663,6 +724,11 @@
 							slistview.paint();
 							element.select();
 						}
+                        listener.onSelect = function(element)
+                        {
+                            var selectable = element.properties.selectable;
+                            return selectable !== false;
+                        }
 				
 				      	var slistview = new SListView ("list-view", null, listener);
 				      	slistview.setView("detail");
@@ -802,32 +868,39 @@
 							}
 							
 							var path = link.getAttribute("fullpath");
+                            var canRead = link.getAttribute("has-read-right") == 'true';
 							adressBar.innerHTML = "/" + path;
-							
-							// R?cup?re les fichiers
-							var result = Tools.postFromUrl(getPluginDirectUrl("<xsl:value-of select="$pluginName"/>") + "/resources-explorer/list", "path=" + encodeURIComponent(path) + "&amp;sitename=" + context.siteName);
-							if (result == null)
-							{
-								alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_ERROR_LISTING"/>")
-								return;
-							}	
-							var files = result.selectNodes("/*/collection:resource");
-							for (var i=0; i &lt; files.length; i++)
-							{
-								var name = files[i].getAttribute("name");
-								var _date = files[i].getAttribute("date");
-								var _size = files[i].getAttribute("size");
-								slistview.addElement (name,
-													"<xsl:value-of select="$reImgDir"/>/extensions/" + getExtensionImage(name),
-													"", 
-													"<xsl:value-of select="$reImgDir"/>/extensions/" + getExtensionImage50(name),
-													{
-														"size" : fillSize(_size),
-														"sizeDisplay": showSize(_size),
-														"date" : _date.substring(6,8) + _date.substring(3,5) + _date.substring(0,2) + _date.substring(9),
-														"dateDisplay": _date
-													});
-							}
+							if (canRead)
+                            {
+    							// R?cup?re les fichiers
+    							var result = Tools.postFromUrl(getPluginDirectUrl("<xsl:value-of select="$pluginName"/>") + "/resources-explorer/list", "path=" + encodeURIComponent(path) + "&amp;sitename=" + context.siteName);
+    							if (result == null)
+    							{
+    								alert("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_ERROR_LISTING"/>")
+    								return;
+    							}	
+    							var files = result.selectNodes("/*/collection:resource");
+    							for (var i=0; i &lt; files.length; i++)
+    							{
+    								var name = files[i].getAttribute("name");
+    								var _date = files[i].getAttribute("date");
+    								var _size = files[i].getAttribute("size");
+    								slistview.addElement (name,
+    													"<xsl:value-of select="$reImgDir"/>/extensions/" + getExtensionImage(name),
+    													"", 
+    													"<xsl:value-of select="$reImgDir"/>/extensions/" + getExtensionImage50(name),
+    													{
+    														"size" : fillSize(_size),
+    														"sizeDisplay": showSize(_size),
+    														"date" : _date.substring(6,8) + _date.substring(3,5) + _date.substring(0,2) + _date.substring(9),
+    														"dateDisplay": _date
+    													});
+    							}
+                            }
+                            else
+                            {
+                                slistview.addElement("<i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESEXPLORER_UNAUTHORIZED_LISTING"/>", "", "", "", {'selectable': false});
+                            }
 							window.setTimeout("slistview.paint();", 50);
 						}
 						
@@ -855,9 +928,10 @@
 	</xsl:template>
 	
 	<xsl:template name="child">
-		<xsl:if test="collection:collection">
+        <!-- Display all the folders (collections) which have at least one child folder readable. -->
+		<xsl:if test="collection:collection[descendant-or-self::collection:collection[@has-read-right = 'true']]">
 			<ul>
-				<xsl:for-each select="collection:collection">
+				<xsl:for-each select="collection:collection[descendant-or-self::collection:collection[@has-read-right = 'true']]">
                     <xsl:sort order="ascending" select="@name"/>
                 
 					<li>
@@ -866,6 +940,7 @@
 							<xsl:if test="@current='true'">
 								<xsl:attribute name="select"/>
 							</xsl:if>
+							<xsl:copy-of select="@has-read-right"/>
 							<xsl:copy-of select="@has-write-right"/>
 							<xsl:value-of select="@name"/>
 						</a>
@@ -875,5 +950,5 @@
 			</ul>
 		</xsl:if>
 	</xsl:template>
-
+    
 </xsl:stylesheet>
\ No newline at end of file
Index: plugins/anycontent/pages/resources-explorer/htmleditorlink.xsl
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/pages/resources-explorer/htmleditorlink.xsl,v
retrieving revision 1.7
diff -u -r1.7 htmleditorlink.xsl
--- plugins/anycontent/pages/resources-explorer/htmleditorlink.xsl	2 Dec 2008 11:10:39 -0000	1.7
+++ plugins/anycontent/pages/resources-explorer/htmleditorlink.xsl	16 Mar 2009 09:38:56 -0000
@@ -130,9 +130,11 @@
             <xsl:apply-templates select="collection:collection" mode="listed">
                 <xsl:sort order="ascending" select="@name"/>
             </xsl:apply-templates>
-			<xsl:apply-templates select="collection:resource" mode="listed">
-                <xsl:sort order="ascending" select="@name"/>
-            </xsl:apply-templates>
+            <xsl:if test="@has-read-right = 'true'">
+    			<xsl:apply-templates select="collection:resource" mode="listed">
+                    <xsl:sort order="ascending" select="@name"/>
+                </xsl:apply-templates>
+            </xsl:if>
 		</table>
 	</xsl:template>
 	
Index: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/generators/ResourcesRightsAssignmentGenerator.java
===================================================================
RCS file: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/generators/ResourcesRightsAssignmentGenerator.java
diff -N plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/generators/ResourcesRightsAssignmentGenerator.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/generators/ResourcesRightsAssignmentGenerator.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,71 @@
+package org.ametys.anycontent.plugins.anycontent.resourcesexplorer.generators;
+
+import java.io.IOException;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.components.source.SourceUtil;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.excalibur.source.Source;
+import org.xml.sax.SAXException;
+
+import org.ametys.anycontent.context.CMSContext;
+import org.ametys.anycontent.context.CMSContextHandler;
+import org.ametys.anycontent.context.CMSContextHelper;
+import org.ametys.runtime.util.IgnoreRootHandler;
+
+/**
+ * Get information to assign rights on a resources explorer
+ */
+public class ResourcesRightsAssignmentGenerator extends ServiceableGenerator
+{
+    /** The CMS context handler. */
+    protected CMSContextHandler _contextHandler;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _contextHandler = (CMSContextHandler) serviceManager.lookup(CMSContextHandler.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        CMSContext cmsContext = _contextHandler.peek();
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        
+        String siteName = cmsContext != null ? CMSContextHelper.getSiteName(cmsContext) : request.getParameter("sitename");
+        
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addCDATAAttribute("i18n", "cms");
+        attrs.addCDATAAttribute("name", "CMS_CONTEXT_RESOURCES");
+        attrs.addCDATAAttribute("icon", "/resources/img/resources.gif");
+        attrs.addCDATAAttribute("context", "/resources/" + siteName);
+        
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "resources", attrs);
+        Source src = null;
+        
+        try
+        {
+            String uri = "cocoon:/resources_explorer/generator/" + siteName;
+            src = resolver.resolveURI(uri);
+            if (src != null)
+            {
+                SourceUtil.toSAX(src, new IgnoreRootHandler(contentHandler));
+            }
+        }
+        finally
+        {
+            resolver.release(src);
+        }
+        
+        XMLUtils.endElement(contentHandler, "resources");
+        contentHandler.endDocument();
+    }
+}
Index: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/RightOnResourcesTransformer.java
===================================================================
RCS file: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/RightOnResourcesTransformer.java
diff -N plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/RightOnResourcesTransformer.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/RightOnResourcesTransformer.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,187 @@
+package org.ametys.anycontent.plugins.anycontent.resourcesexplorer;
+
+import java.util.Stack;
+
+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.transformation.ServiceableTransformer;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.commons.lang.StringUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import org.ametys.anycontent.plugins.anycontent.right.RightsHelper;
+import org.ametys.runtime.right.RightsManager;
+
+/**
+ * A transformer checking user right on resources
+ *
+ */
+public class RightOnResourcesTransformer extends ServiceableTransformer implements Serviceable
+{
+    private static final String __TAG_COLLECTION = "collection:collection";
+    private static final String __TAG_RESOURCE = "collection:resource";
+    
+    private static final String __RESOURCES_BASE_CONTEXT = "/resources";
+    
+    /** The rights manager */
+    protected RightsManager _rightsManager;
+    
+    /** A stack which stores the contexts. */
+    protected Stack<Boolean> _readRightsStack;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _rightsManager = (RightsManager) serviceManager.lookup(RightsManager.ROLE);
+    }
+    
+    @Override
+    public void startDocument() throws SAXException
+    {
+        _readRightsStack = new Stack<Boolean>();
+        super.startDocument();
+    }
+    
+    @Override
+    public void endDocument() throws SAXException
+    {
+        super.endDocument();
+        _readRightsStack = null;
+    }
+    
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
+    {
+        AttributesImpl attr = new AttributesImpl(attributes);
+        
+        if (__TAG_COLLECTION.equals(qName))
+        {
+            String sitename = parameters.getParameter("site", null);
+            
+            if (StringUtils.isEmpty(sitename))
+            {
+                throw new RuntimeException("RightOnResourcesTransformer : a 'site' parameter must be provided.");
+            }
+            
+            // construction du path courant
+            String resourcePath = getResourceContext(attr, sitename);
+            
+            if (hasWriteRight(resourcePath, sitename))
+            {
+                attr.addCDATAAttribute("has-write-right", "true");
+            }
+            if (hasReadRight(resourcePath, sitename))
+            {
+                attr.addCDATAAttribute("has-read-right", "true");
+                _readRightsStack.push(Boolean.TRUE);
+            }
+            else
+            {
+                _readRightsStack.push(Boolean.FALSE);
+            }
+            contentHandler.startElement(uri, localName, qName, attr);
+        }
+        else if (__TAG_RESOURCE.equals(qName))
+        {
+            
+            if (_readRightsStack.peek())
+            {
+                contentHandler.startElement(uri, localName, qName, attributes);
+            }
+        }
+        else
+        {
+            super.startElement(uri, localName, qName, attr);
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String loc, String raw) throws SAXException
+    {
+        if (__TAG_RESOURCE.equals(raw))
+        {
+            if (_readRightsStack.peek())
+            {
+                super.endElement(uri, loc, raw);
+            }
+        }
+        else
+        {
+            super.endElement(uri, loc, raw);
+        }
+        
+        if (__TAG_COLLECTION.equals(raw))
+        {
+            _readRightsStack.pop();
+        }
+    }
+    
+    /**
+     * Get the "/resources" right context corresponding to the current resource.
+     * @param attr the tag attributes.
+     * @param siteName 
+     * @return the resource context.
+     */
+    protected String getResourceContext(Attributes attr, String siteName)
+    {
+        String resourcePath = __RESOURCES_BASE_CONTEXT + "/" + siteName;
+        
+        String attrUri = attr.getValue("uri");
+        String attrPath = attr.getValue("path");
+        
+        if (StringUtils.isNotEmpty(attrUri))
+        {
+            resourcePath = attrUri.replaceFirst("resource-explorer:/", __RESOURCES_BASE_CONTEXT + "/" + siteName);
+        }
+        else if (StringUtils.isNotEmpty(attrPath))
+        {
+            resourcePath = __RESOURCES_BASE_CONTEXT + "/" + siteName + "/" + attrPath;
+        }
+        
+        return resourcePath;
+    }
+
+    /**
+     * Tests if a user has the right to read the resource at the given path.
+     * @param resourcePath
+     * @param sitename 
+     * @return true is the user has the right to read the resource, false otherwise.
+     */
+    protected boolean hasReadRight(String resourcePath, String sitename)
+    {
+        String applicationContext = "/contributor/" + sitename;
+        boolean hasReadRight = false;
+
+        // Test the application context for the global read right.
+        hasReadRight = RightsHelper.hasRight(objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_ReadAll", applicationContext);
+        if (!hasReadRight)
+        {
+            hasReadRight = RightsHelper.hasRight(objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_ReadResource", resourcePath);
+        }
+        
+        return hasReadRight;
+    }
+    
+    /**
+     * Tests if a user has the right to write resources at the given path.
+     * @param resourcePath
+     * @param sitename 
+     * @return true is the user has the right to write resources, false otherwise.
+     */
+    protected boolean hasWriteRight(String resourcePath, String sitename)
+    {
+        String applicationContext = "/contributor/" + sitename;
+        boolean hasWriteRight = false;
+        
+        // Test the application context for the global write right.
+        hasWriteRight = RightsHelper.hasRight(objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_WriteAll", applicationContext);
+        if (!hasWriteRight)
+        {
+            hasWriteRight = RightsHelper.hasRight(objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_WriteResource", resourcePath);
+        }
+        
+        return hasWriteRight;
+    }
+}
Index: plugins/anycontent/pages/rights/context/resources-sitemap.xsl
===================================================================
RCS file: plugins/anycontent/pages/rights/context/resources-sitemap.xsl
diff -N plugins/anycontent/pages/rights/context/resources-sitemap.xsl
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/anycontent/pages/rights/context/resources-sitemap.xsl	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,417 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" 
+    xmlns:i18n="http://apache.org/cocoon/i18n/2.1" 
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:anycontent="http://ametys.org/anycontent/3.0"
+    xmlns:collection="http://apache.org/cocoon/collection/1.0">
+   	
+   	<xsl:import href="../../resources-explorer/extensions-icon.xsl"/>
+   	
+   	<xsl:param name="contextPath"/>
+    <xsl:param name="pluginName">core</xsl:param>
+    <xsl:param name="myPlugin"/>
+    <xsl:param name="page"/>
+
+    <xsl:template match="/resources">
+        <Context id="Resources">
+            <Button>
+                <Icon><xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$myPlugin"/>/resources/img/resources-explorer/contributor.gif</Icon>
+                <Label><i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_ASSIGN_LABEL" i18n:catalogue="plugin.{$myPlugin}"/></Label>
+                <Description><i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCES_EXPLORER_ASSIGN_DESCRIPTION" i18n:catalogue="plugin.{$myPlugin}"/></Description>
+            </Button>
+            <Content>
+                <table id="resources-fulltable">
+                 	<colgroup>
+                        <col width="233px"/>
+                        <col width="133px"/>
+                        <col/>
+                    </colgroup>
+                    <tr>
+                        <td id="affect-resources-panel" style="width: 233px; overflow: auto; border-right: 1px inset #ece9d8;"/>
+                        <td>
+							<table id="resources-innertable1" cellspacing="0" cellpadding="0" border="0">
+                                <colgroup>
+                                    <col width="10px"/>
+                                    <col/>
+                                    <col width="10px"/>
+                                </colgroup>
+                                <tr>
+                                    <td/>
+                                    <td>
+                                        <div id="resources-tree">
+                                        	<ul>
+                                        		<xsl:apply-templates select="collection:collection"/>
+											</ul>
+										</div>
+                                    </td>
+                                    <td/>
+                                </tr>
+                                <tr>
+                                    <td colspan="3" style="height: 5px"></td>
+                                </tr>
+                            </table>
+						</td>
+                        <td>
+                        	<table id="resources-innertable2" cellspacing="0" cellpadding="0" border="0">
+                                <colgroup>
+                                    <col width="10px"/>
+                                    <col/>
+                                    <col width="10px"/>
+                                </colgroup>
+                                <tr>
+                                    <td colspan="3" style="height: 5px">
+                                        <div style="margin-left:10px; font-size: 10px"><i18n:text i18n:key="PLUGINS_CORE_RIGHTS_USERGROUP_LABEL" i18n:catalogue="plugin.{$pluginName}"/></div>                                    
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td/>
+                                    <td>
+                                        <div id="list-view-resources"/>
+                                    </td>
+                                    <td/>
+                                </tr>
+                                <tr>
+                                    <td colspan="3" style="height: 5px"></td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+                    <tr>
+                        <td id="profil-resources-panel" style="height: 155px; width: 233px; overflow: auto;  border-right: 1px inset #ece9d8;"/>
+                        <td style="height: 155px; text-align: center; vertical-align: middle;" colspan="2">
+                            <xsl:call-template name="double-liste">
+                                <xsl:with-param name="profilCategory">profilResourcesCategory</xsl:with-param>
+                            </xsl:call-template>
+                        </td>
+                    </tr>
+                </table>
+                
+               
+               <script src="{$contextPath}/plugins/{$pluginName}/resources/js/users/selectuser.js.i18n"><xsl:comment>empty</xsl:comment></script>
+               <script>
+                        <xsl:if test="AdministratorUI = 'true'">
+                            function resourcesGoBack()
+                            {
+                                document.location.href = context.workspaceContext;
+                            }
+                        </xsl:if>
+                        
+                        onresize.push(function (width, height)
+                        {
+                            var appTable = document.getElementById("resources-fulltable");
+                            appTable.style.width = width;
+                            appTable.style.height = height;
+                            
+                            var appTable = document.getElementById("resources-innertable1");
+                            appTable.style.width = 231;
+                            appTable.style.height = height - 155;
+                            var divTree = document.getElementById("resources-tree");
+                            divTree.style.width = 228;
+                            divTree.style.height = height - 155 - 2;
+                            
+                            var appTable = document.getElementById("resources-innertable2");
+                            appTable.style.width = width - 231 - 231;
+                            appTable.style.height = height - 155;
+                            var list = document.getElementById("list-view-resources");
+                            list.style.width = width - 231 - 231 - 5;
+                            list.style.height = height - 155 - 5;
+                            
+                            //list-view-left-appli
+                            //list-view-right-appli
+                        });
+                        
+                    
+                        var resourcesPlugin = "<xsl:value-of select="$myPlugin"/>";
+                        
+                        RUNTIME_Plugin_Runtime_SelectUser.initialize("<xsl:value-of select="$pluginName"/>");
+                        
+                        function RUNTIME_Plugin_Runtime_SelectUserCallBack_resources (users)
+                        {
+                            RUNTIME_Plugin_Runtime_SelectUserCallBack(users, slistview_resources);
+                        }
+                        function RUNTIME_Plugin_Runtime_SelectGroupCallBack_resources (groups)
+                        {
+                            RUNTIME_Plugin_Runtime_SelectGroupCallBack(groups, slistview_resources);
+                        }
+                        function RUNTIME_Plugin_Runtime_Save_Assignment_For_Resources(force)
+                        {
+                            if (force == false)
+                            {
+                                if ((profilResourcesCategory.table.rows[0+1].style.display == "" &amp;&amp; !confirm("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ACT_VALID_CONFIRM" i18n:catalogue="plugin.{$pluginName}"/>")) || profilResourcesCategory.table.rows[0+1].style.display != "")
+                                {
+                                    profilResourcesCategory.showHideElement(0, false);
+                                    return;                             
+                                }
+                            }
+    						
+                            RUNTIME_Plugin_Runtime_Save_Assignment (slistview_resources, slistview_right_resources, resourcesTree.tree.selection.getAttribute("context"));
+
+                            profilResourcesCategory.showHideElement(0, false);
+                        }
+                        function RUNTIME_Plugin_Runtime_Remove_Assignment_For_Resources()
+                        {
+                            RUNTIME_Plugin_Runtime_Remove_Assignment(slistview_resources, resourcesTree.tree.selection.getAttribute("context"));
+                        }
+                    
+                        var sactionpanelagenda3 = new SContextualPanel ("affect-resources-panel");
+                        var actResourcesCategory = sactionpanelagenda3.addCategory ("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ACT_CATEGORY" i18n:catalogue="plugin.{$pluginName}"/>");
+                            actResourcesCategory.addLink("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ACT_ADDUSER" i18n:catalogue="plugin.{$pluginName}"/>", "<xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources/img/rights/action_add_user.gif", function(){RUNTIME_Plugin_Runtime_SelectUser.act(RUNTIME_Plugin_Runtime_SelectUserCallBack_resources)});
+                            actResourcesCategory.addLink("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ACT_ADDGROUP" i18n:catalogue="plugin.{$pluginName}"/>", "<xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources/img/rights/action_add_group.gif", function(){RUNTIME_Plugin_Runtime_SelectGroup.act(RUNTIME_Plugin_Runtime_SelectGroupCallBack_resources)});
+                            actResourcesCategory.addLink("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ACT_REMOVE" i18n:catalogue="plugin.{$pluginName}"/>", "<xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources/img/rights/action_delete.gif", RUNTIME_Plugin_Runtime_Remove_Assignment_For_Resources);
+                    <xsl:if test="AdministratorUI = 'true'">
+                            actResourcesCategory.addLink("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_HANDLE_QUIT" i18n:catalogue="plugin.{$pluginName}"/>", getPluginResourcesUrl("<xsl:value-of select="$pluginName"/>") + "/img/rights/quit.gif", resourcesGoBack);
+                    </xsl:if>
+                            actResourcesCategory.addElement("&lt;br/&gt;&lt;div style='color: #000000; font-size: 10px; text-align: left;'&gt;<i18n:text i18n:key="CORE_ASSIGN_RIGHT_RESOURCES_HELP_AFFECT" i18n:catalogue="plugin.{$myPlugin}"/>&lt;/div&gt;")
+                    
+                        var sactionpanelagenda4 = new SContextualPanel ("profil-resources-panel");
+                        var profilResourcesCategory = sactionpanelagenda4.addCategory ("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_PROFIL_CATEGORY" i18n:catalogue="plugin.{$pluginName}"/>");
+                            profilResourcesCategory.addLink("<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ACT_VALIDATE" i18n:catalogue="plugin.{$pluginName}"/>", "<xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources/img/rights/action_valid.gif", RUNTIME_Plugin_Runtime_Save_Assignment_For_Resources);
+                            profilResourcesCategory.addElement("&lt;div style='color: #000000; font-size: 10px; text-align: left;'&gt;<i18n:text i18n:key="WEB_ASSIGN_RIGHT_SITEMAP_HELP_PROFIL" i18n:catalogue="plugin.web"/>&lt;/div&gt;")
+                    
+                        sactionpanelagenda3.paint();
+                        sactionpanelagenda4.paint();
+                        
+                        actResourcesCategory.showHide(true);
+                        actResourcesCategory.showHideElement(2, false);
+                        profilResourcesCategory.showHide(false);
+                        
+    
+                        /* ************************************
+                           *  TABULATION A DROITE
+                           ************************************ */
+                        
+                        
+                        var listener_resources = {};
+                        
+                        listener_resources.onSelect = function(element)
+                        {
+                            actResourcesCategory.showHideElement(3, true);
+                            profilResourcesCategory.showHide(true);
+                            profilResourcesCategory.showHideElement(0, false);
+                            
+                            slistview_left_resources.elements = new Array();
+                            slistview_left_resources.selection = new Array();
+                            slistview_right_resources.elements = new Array();
+                            slistview_right_resources.selection = new Array();
+    
+                            // afficher les profils dans la double liste
+                            for (var i in all_profiles)
+                            {
+                                // qd plusieurs éléments sont sélectionnés, on affiche tout à gauche de toute manière
+                                if (element.properties[i] == null || slistview_resources.getSelection().length &gt;= 1) 
+                                {
+                                    slistview_left_resources.addElement(all_profiles[i], "<xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources/img/rights/profiles/list16.gif", "", "", {"id": i});
+                                }
+                                else
+                                {
+                                    slistview_right_resources.addElement(all_profiles[i], "<xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources/img/rights/profiles/list16.gif", "", "", {"id": i});
+                                }
+                            }
+    
+                            slistview_left_resources.paint();
+                            slistview_right_resources.paint();
+    
+                            return true;
+                        }
+                        
+                        listener_resources.onUnselect = function(element)
+                        {
+                            RUNTIME_Plugin_Runtime_Save_Assignment_For_Resources(false);
+                        
+                            profilResourcesCategory.showHide(false);
+                            actResourcesCategory.showHideElement(3, false);
+                            
+                            slistview_left_resources.elements = new Array();
+                            slistview_left_resources.selection = new Array();
+                            slistview_left_resources.paint();
+                            
+                            slistview_right_resources.elements = new Array();
+                            slistview_right_resources.selection = new Array();
+                            slistview_right_resources.paint();
+                        }
+                        
+                        
+                        var slistview_resources = new SListView ("list-view-resources", null, listener_resources);
+                        slistview_resources.setView("icon");
+                        slistview_resources.paint();
+                        
+                        
+                       /* ************************************
+					   *  Arbre des ressources
+					   ************************************ */
+					   
+					   	function resources_tree_listener() {};
+						resources_tree_listener.canRename = false;
+						resources_tree_listener.canDrag = false;
+
+						resources_tree_listener.onSelect = function(link)
+						{
+							var notRecorded = "";
+							while (slistview_resources.getElements().length &gt; 0)
+							{
+								var ok = false;
+								for (var j in slistview_resources.getElements()[0].properties)
+								{
+									if (j != "id" &amp;&amp; j != "type")
+									{
+										ok = true;
+										break;
+									}
+								}
+								if (!ok)
+									notRecorded += ", " + slistview_resources.getElements()[0].name;
+								
+								slistview_resources.getElements()[0].remove();
+							}
+							
+							if (notRecorded != "")
+							{
+								alert("<i18n:text i18n:key="PLUGINS_USERS_RIGHTS_CONTEXTS_WARNING_ON_AUTO_DELETE" i18n:catalogue="plugin.{$myPlugin}"/>\n" + notRecorded.substring(2));
+							}
+							
+							if (link == null)						
+							{
+								actResourcesCategory.showHide(true);
+								actResourcesCategory.showHideElement(0, false);
+								actResourcesCategory.showHideElement(1, false);
+								actResourcesCategory.showHideElement(2, false);
+								actResourcesCategory.showHideElement(3, false);
+								actResourcesCategory.showHideElement(4, true);
+							}
+							else
+							{
+								actResourcesCategory.showHide(true);
+								actResourcesCategory.showHideElement(0, true);
+								actResourcesCategory.showHideElement(1, true);
+								actResourcesCategory.showHideElement(2, true);
+								actResourcesCategory.showHideElement(3, false);
+								
+								//Afficher les groupes et/ou utilisateurs
+								RUNTIME_Plugin_Runtime_Get_UsersAndGroups(link.getAttribute("context"), slistview_resources);
+							}
+							
+							return true;
+						}
+					
+						var resourcesTree = new STree ("resources-tree", null, 1, resources_tree_listener);
+					
+                </script>      
+            </Content>
+        </Context>
+    </xsl:template>
+    
+    <xsl:template name="double-liste">
+        <xsl:param name="profilCategory"/>
+        
+        <script>
+        var slistview_left_resources;
+        var slistview_right_resources;
+        
+        function doubleList_resources (lv1, lv2, mode)
+        {
+            var eltsToTransit;
+            if (mode == 'all')
+                          eltsToTransit = lv1.getElements();
+            else
+                          eltsToTransit = lv1.getSelection();
+
+            var newElements = new Array();
+
+            for (var i=0; i &lt; eltsToTransit.length; i++)
+            {
+                profilResourcesCategory.showHideElement (0, true);
+                var elt = eltsToTransit[i];
+
+                newElements.push (lv2.addElement(elt.name, elt.icon16, elt.icon32, elt.icon50, elt.properties));
+            }
+            for (var i=eltsToTransit.length-1; i &gt;= 0; i--)
+            {
+                eltsToTransit[i].remove();
+            }
+
+            lv2.unselect();
+            lv2.paint();
+
+            for (var i=0; i &lt; newElements.length; i++)
+            {
+                newElements[i].select(true);
+            }
+        }
+        </script>
+           <table cellpadding="0" cellspacing="0" style="height: 100%; margin: 10px; margin-top: 5px; margin-bottom: 5px; table-layout: fixed">
+                <colgroup>
+                    <col width="50%"/>
+                    <col width="50px"/>
+                    <col width="50%"/>
+                </colgroup>
+                <tr>
+                    <td>
+                        <div id="list-view-left-resources" style="width: 100%; height: 100%"/>
+                    </td>
+                    <td style="text-align: center; vertical-align: middle">
+                        <img onclick="doubleList_resources(slistview_right_resources, slistview_left_resources, 'all');" src="{$contextPath}/plugins/{$pluginName}/resources/img/rights/dbl_list_rewind.gif" style="width: 25px; height: 25px; text-align: center; display: block; margin: 1px; margin-top: 0px; padding: 0px;" i18n:attr="title" title="plugin.{$pluginName}:PLUGINS_CORE_RIGHTS_DBL_LIST_REWIND"/>
+                        <img onclick="doubleList_resources(slistview_right_resources, slistview_left_resources, 'selection');" src="{$contextPath}/plugins/{$pluginName}/resources/img/rights/dbl_list_previous.gif" style="width: 25px; height: 25px; text-align: center; display: block; margin: 1px; padding: 0px;" i18n:attr="title" title="plugin.{$pluginName}:PLUGINS_CORE_RIGHTS_DBL_LIST_PREV"/>
+                        <img onclick="doubleList_resources(slistview_left_resources, slistview_right_resources, 'selection');" src="{$contextPath}/plugins/{$pluginName}/resources/img/rights/dbl_list_next.gif" style="width: 25px; height: 25px; text-align: center; display: block; margin: 1px; padding: 0px;" i18n:attr="title" title="plugin.{$pluginName}:PLUGINS_CORE_RIGHTS_DBL_LIST_NEXT"/>
+                        <img onclick="doubleList_resources(slistview_left_resources, slistview_right_resources, 'all');" src="{$contextPath}/plugins/{$pluginName}/resources/img/rights/dbl_list_forward.gif" style="width: 25px; height: 25px; text-align: center; display: block; margin: 1px; padding: 0px;" i18n:attr="title" title="plugin.{$pluginName}:PLUGINS_CORE_RIGHTS_DBL_LIST_FORWARD"/>
+                    </td>
+                    <td>
+                        <div id="list-view-right-resources" style="width: 100%; height: 100%"/>
+                    </td>
+                </tr>
+            </table>
+        <script>
+            slistview_left_resources = new SListView ("list-view-left-resources" , null, null);
+            slistview_right_resources = new SListView ("list-view-right-resources", null, null);
+    
+            slistview_left_resources.addColumn (null, "<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ASSIGN_LEFTCOLUMN" i18n:catalogue="plugin.{$pluginName}"/>", null, "230px", null);
+            slistview_right_resources.addColumn (null, "<i18n:text i18n:key="PLUGINS_CORE_RIGHTS_ASSIGN_RIGHTCOLUMN" i18n:catalogue="plugin.{$pluginName}"/>", null, "230px", null);
+    
+            slistview_left_resources.setView("detail");
+            slistview_right_resources.setView("detail");
+    
+            slistview_left_resources.paint();
+            slistview_right_resources.paint();
+        </script>
+    </xsl:template>
+    
+    
+    <xsl:template match="collection:collection">
+    	<xsl:param name="context"/>
+    	<xsl:variable name="name" select="@name"/>
+    	
+    	<li>
+    		<xsl:choose>
+    			<xsl:when test="../@name='CMS_CONTEXT_RESOURCES'">
+    				<!-- Racine -->
+    				<xsl:variable name="root-context" select="../@context"/>
+					<img src="{$contextPath}/plugins/{$myPlugin}/resources/img/resources-explorer/root.gif" style="margin-bottom: 1px;"/>
+					<a context="{$root-context}"><i18n:text i18n:key="PLUGINS_ANYCONTENT_RESOURCESDIR_ROOTNODE" i18n:catalogue="plugin.{$myPlugin}"/></a>
+					
+					<xsl:if test="collection:collection">
+						<ul>
+							<xsl:apply-templates>
+								<xsl:with-param name="context" select="$root-context"/>
+							</xsl:apply-templates>
+						</ul>
+					</xsl:if>
+    			</xsl:when>
+    			<xsl:otherwise>
+    				<img src="{$contextPath}/plugins/{$myPlugin}/resources/img/resources-explorer/Folder.gif" style="margin-bottom: 1px;"/>
+					<a context="{$context}/{$name}"><xsl:value-of select="$name"/></a>
+					
+					<xsl:if test="collection:collection">
+						<ul>
+							<xsl:apply-templates>
+								<xsl:with-param name="context" select="concat($context, '/', @name)"/>
+							</xsl:apply-templates>
+						</ul>
+					</xsl:if>
+    			</xsl:otherwise>
+    		</xsl:choose>
+    	</li>
+     	
+    </xsl:template>
+    
+    <xsl:template match="collection:resource">
+	</xsl:template>
+</xsl:stylesheet>
Index: plugins/anycontent/pages/resources-explorer/remove-unauthorized-directories.xsl
===================================================================
RCS file: plugins/anycontent/pages/resources-explorer/remove-unauthorized-directories.xsl
diff -N plugins/anycontent/pages/resources-explorer/remove-unauthorized-directories.xsl
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/anycontent/pages/resources-explorer/remove-unauthorized-directories.xsl	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+    xmlns:collection="http://apache.org/cocoon/collection/1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+    
+    <!-- Keep only directories on which the user has read right or which contain
+         at least a directory on which the user has read right. -->
+    
+    <xsl:template match="collection:collection">
+        <collection:collection>
+            <xsl:copy-of select="@*"/>
+            <xsl:apply-templates select="collection:collection[descendant-or-self::node()[@has-read-right = 'true']]|*[name() != 'collection:collection']"/>
+        </collection:collection>
+    </xsl:template>
+    
+    <xsl:template match="*">
+        <xsl:copy>
+            <xsl:copy-of select="@*"/>
+            <xsl:apply-templates/>
+        </xsl:copy>
+    </xsl:template>
+    
+</xsl:stylesheet>
Index: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/HasResourcesRightAction.java
===================================================================
RCS file: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/HasResourcesRightAction.java
diff -N plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/HasResourcesRightAction.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/resourcesexplorer/HasResourcesRightAction.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,192 @@
+package org.ametys.anycontent.plugins.anycontent.resourcesexplorer;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.anycontent.plugins.anycontent.right.RightsHelper;
+import org.ametys.runtime.plugins.core.right.actions.HasRightAction;
+import org.ametys.runtime.right.RightsManager;
+import org.ametys.runtime.user.UserHelper;
+
+/**
+ * Tests the two resources rights.
+ */
+public class HasResourcesRightAction extends HasRightAction
+{
+    /** Resources base context. */
+    public static final String RESOURCES_BASE_CONTEXT = "/resources";
+    
+    /** "Has right" or "has not right" action. */
+    protected boolean _hasRight;
+    
+    /**
+     * If true, an anonymous user trying to access the pipeline will get a 403 error.
+     * If false, an IllegalStateException will be thrown.
+     */
+    protected boolean _acceptAnonymous;
+    
+    @Override
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        _hasRight = "true".equals(configuration.getChild("has-right").getValue("true"));
+        _acceptAnonymous = "true".equals(configuration.getChild("accept-anonymous").getValue("false"));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        _objectModel = objectModel;
+        
+        String sitename = parameters.getParameter("sitename", "");
+        String path = parameters.getParameter("path", "");
+        String right = parameters.getParameter("right", "read");
+        
+        if (StringUtils.isEmpty(sitename))
+        {
+            String message = "Cannot check right because cannot determine current site.";
+            getLogger().error(message);
+            throw new IllegalArgumentException(message);
+        }
+        
+        if (_rightsManager == null)
+        {
+            _rightsManager = (RightsManager) manager.lookup(RightsManager.ROLE);
+        }
+
+        if (UserHelper.isAdministrator(objectModel))
+        {
+            return _hasRight ? EMPTY_MAP : null;
+        }
+
+        boolean hasRight = false;
+        String resourcesContext = "";
+        
+        String userLogin = UserHelper.getCurrentUser(objectModel);
+        if (userLogin == null)
+        {
+            if (!_acceptAnonymous)
+            {
+                getLogger().error("Anonymous user tried to access a privileged feature without convenient right. Should have a resource " + right + " right on context '" + resourcesContext + "'");
+                throw new IllegalStateException("You have no right to access this feature.");
+            }
+        }
+        else
+        {
+            String baseContext = RESOURCES_BASE_CONTEXT + "/" + sitename;
+            resourcesContext = baseContext;
+            
+            if (StringUtils.isNotEmpty(path))
+            {
+                resourcesContext = baseContext + "/" + path;
+            }
+            
+            if ("read".equals(right))
+            {
+                hasRight = hasReadRight(resourcesContext, sitename);
+            }
+            else if ("write".equals(right))
+            {
+                hasRight = hasWriteRight(resourcesContext, sitename);
+            }
+        }
+        
+        if (hasRight)
+        {
+            if (_hasRight)
+            {
+                return EMPTY_MAP;
+            }
+            else
+            {
+                return null;
+            }
+        }
+        else
+        {
+            if (_hasRight)
+            {
+                return null;
+            }
+            else
+            {
+                getLogger().error("User '" + userLogin + "' tried to access a privileged feature without convenient right. Should have a resource " + right + " right on context '" + resourcesContext + "'");
+                throw new IllegalStateException("You have no right to access this feature.");
+            }
+        }
+    }
+    
+    /**
+     * Return the base context when not specified
+     * @param parameters The sitemap parameters
+     * @return the base context when not specified
+     */
+    @Override
+    protected String getBaseContext(Parameters parameters)
+    {
+        String sitename = parameters.getParameter("sitename", "");
+        
+        if (StringUtils.isEmpty(sitename))
+        {
+            String message = "Cannot check right because cannot determine current site.";
+            getLogger().error(message);
+            throw new IllegalArgumentException(message);
+        }
+        
+        return RESOURCES_BASE_CONTEXT + "/" + sitename;
+    }
+    
+    /**
+     * Tests if a user has the right to read the resource at the given path.
+     * @param resourcePath
+     * @param sitename 
+     * @return true is the user has the right to read the resource, false otherwise.
+     */
+    protected boolean hasReadRight(String resourcePath, String sitename)
+    {
+        String applicationContext = "/contributor/" + sitename;
+        boolean hasReadRight = false;
+        
+        // Test the application context for the global read right.
+        hasReadRight = RightsHelper.hasRight(_objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_ReadAll", applicationContext);
+        getLogger().warn("Check for 'Runtime_Rights_ResourcesExplorer_ReadAll' on context '" + applicationContext + "' => " + String.valueOf(hasReadRight));
+        // If the user doesn't have the global read right, test the local read right on the resource context.
+        if (!hasReadRight)
+        {
+            hasReadRight = RightsHelper.hasRight(_objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_ReadResource", resourcePath);
+            getLogger().warn("Check for 'Runtime_Rights_ResourcesExplorer_ReadResource' on context '" + resourcePath + "' => " + String.valueOf(hasReadRight));
+        }
+        
+        return hasReadRight;
+    }
+    
+    /**
+     * Tests if a user has the right to write resources at the given path.
+     * @param resourcePath
+     * @param sitename 
+     * @return true is the user has the right to write resources, false otherwise.
+     */
+    protected boolean hasWriteRight(String resourcePath, String sitename)
+    {
+        String applicationContext = "/contributor/" + sitename;
+        boolean hasWriteRight = false;
+        
+        // Test the application context for the global write right.
+        hasWriteRight = RightsHelper.hasRight(_objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_WriteAll", applicationContext);
+        getLogger().warn("Check for 'Runtime_Rights_ResourcesExplorer_WriteAll' on context '" + applicationContext + "' => " + String.valueOf(hasWriteRight));
+        // If the user doesn't have the global write right, test the local write right on the resource context.
+        if (!hasWriteRight)
+        {
+            hasWriteRight = RightsHelper.hasRight(_objectModel, _rightsManager, "Runtime_Rights_ResourcesExplorer_WriteResource", resourcePath);
+            getLogger().warn("Check for 'Runtime_Rights_ResourcesExplorer_WriteResource' on context '" + resourcePath + "' => " + String.valueOf(hasWriteRight));
+        }
+        
+        return hasWriteRight;
+    }
+}
