Index: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/JCRContentQuery.java
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/JCRContentQuery.java,v
retrieving revision 1.6
diff -u -r1.6 JCRContentQuery.java
--- plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/JCRContentQuery.java	5 Jun 2008 09:44:27 -0000	1.6
+++ plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/JCRContentQuery.java	27 Oct 2008 14:43:03 -0000
@@ -9,7 +9,6 @@
 import javax.jcr.Session;
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.Query;
-import javax.jcr.query.QueryResult;
 
 import org.ametys.anycontent.repository.Content;
 import org.ametys.anycontent.repository.ContentQuery;
@@ -79,24 +78,6 @@
         }
     }
 
-    private NodeIterator _execute(Query jcrQuery) throws RepositoryException
-    {
-        try
-        {
-            QueryResult result = jcrQuery.execute();
-            
-            NodeIterator nodes = result.getNodes();
-            _size = nodes.getSize();
-            return nodes;
-        }
-        catch (javax.jcr.RepositoryException e)
-        {
-            String errorMessage = "An error occured executing the internal query object" + e.getMessage();
-            _logger.error(errorMessage, e);
-            throw new RepositoryException(errorMessage, e);
-        }
-    }
-
     private Collection<Content> _extractContents(NodeIterator nodeIterator, String siteName, Expression expression) throws RepositoryException
     {
         if (_offset > 0)
Index: plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/AbstractJCRQuery.java
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/AbstractJCRQuery.java,v
retrieving revision 1.4
diff -u -r1.4 AbstractJCRQuery.java
--- plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/AbstractJCRQuery.java	27 May 2008 07:45:15 -0000	1.4
+++ plugins/anycontent/src/org/ametys/anycontent/plugins/anycontent/repository/jcr/AbstractJCRQuery.java	27 Oct 2008 14:43:03 -0000
@@ -6,12 +6,15 @@
 import java.util.Date;
 import java.util.TimeZone;
 
+import javax.jcr.NodeIterator;
 import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
 
 import org.apache.avalon.framework.logger.Logger;
 
 import org.ametys.anycontent.repository.Expression;
 import org.ametys.anycontent.repository.Query;
+import org.ametys.anycontent.repository.RepositoryException;
 import org.ametys.anycontent.repository.SortCriteria;
 import org.ametys.runtime.util.LoggerFactory;
 
@@ -613,4 +616,28 @@
     {
         return _size;
     }
+    
+    /**
+     * Execute the JCR query.
+     * @param jcrQuery the query to execute.
+     * @return an iterator on the result nodes.
+     * @throws RepositoryException
+     */
+    protected NodeIterator _execute(javax.jcr.query.Query jcrQuery) throws RepositoryException
+    {
+        try
+        {
+            QueryResult result = jcrQuery.execute();
+            
+            NodeIterator nodes = result.getNodes();
+            _size = nodes.getSize();
+            return nodes;
+        }
+        catch (javax.jcr.RepositoryException e)
+        {
+            String errorMessage = "An error occured executing the internal query object" + e.getMessage();
+            _logger.error(errorMessage, e);
+            throw new RepositoryException(errorMessage, e);
+        }
+    }
 }
Index: plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRRepository.java
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRRepository.java,v
retrieving revision 1.10
diff -u -r1.10 JCRRepository.java
--- plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRRepository.java	10 Jun 2008 07:54:35 -0000	1.10
+++ plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRRepository.java	27 Oct 2008 14:43:03 -0000
@@ -22,6 +22,7 @@
 import org.ametys.anycontent.module.ServiceParameter;
 import org.ametys.anycontent.plugins.anycontent.repository.jcr.JCRContent;
 import org.ametys.anycontent.plugins.anycontent.repository.jcr.JCRSite;
+import org.ametys.anycontent.plugins.web.repository.ContentOnPageQuery;
 import org.ametys.anycontent.plugins.web.repository.Page;
 import org.ametys.anycontent.plugins.web.repository.PageContent;
 import org.ametys.anycontent.plugins.web.repository.PageQuery;
@@ -488,4 +489,18 @@
             throw new RepositoryException(errorMessage, e);
         }
     }
+
+    public ContentOnPageQuery createContentOnPageQuery() throws RepositoryException
+    {
+        try
+        {
+            return new JCRContentOnPageQuery(this, _getSession(), _sManager);
+        }
+        catch (javax.jcr.RepositoryException e)
+        {
+            String errorMessage = "Repository can not create a query! " + e.getMessage();
+            getLogger().error(errorMessage, e);
+            throw new RepositoryException(errorMessage, e);
+        }
+    }
 }
Index: plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRPageQuery.java
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRPageQuery.java,v
retrieving revision 1.10
diff -u -r1.10 JCRPageQuery.java
--- plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRPageQuery.java	12 Jun 2008 13:26:53 -0000	1.10
+++ plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRPageQuery.java	27 Oct 2008 14:43:03 -0000
@@ -9,7 +9,6 @@
 import javax.jcr.Session;
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.Query;
-import javax.jcr.query.QueryResult;
 
 import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.jackrabbit.util.ISO9075;
@@ -94,24 +93,6 @@
         }
     }
     
-    private NodeIterator _execute(Query jcrQuery) throws RepositoryException
-    {
-        try
-        {
-            QueryResult result = jcrQuery.execute();
-
-            NodeIterator nodes = result.getNodes();
-            _size = nodes.getSize();
-            return nodes;
-        }
-        catch (javax.jcr.RepositoryException e)
-        {
-            String errorMessage = "An error occured executing the internal query object" + e.getMessage();
-            _logger.error(errorMessage, e);
-            throw new RepositoryException(errorMessage, e);
-        }
-    }
-    
     private Collection<Page> _extractPages(NodeIterator nodeIterator, ServiceManager manager, String siteName, String sitemapName, String language, Expression expression) throws RepositoryException
     {
         if (_offset > 0)
Index: plugins/web/src/org/ametys/anycontent/plugins/web/repository/Repository.java
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/web/src/org/ametys/anycontent/plugins/web/repository/Repository.java,v
retrieving revision 1.3
diff -u -r1.3 Repository.java
--- plugins/web/src/org/ametys/anycontent/plugins/web/repository/Repository.java	8 Nov 2007 09:18:25 -0000	1.3
+++ plugins/web/src/org/ametys/anycontent/plugins/web/repository/Repository.java	27 Oct 2008 14:43:03 -0000
@@ -73,4 +73,11 @@
      * @throws RepositoryException If an error occured creating the query object
      */
     public PageQuery createPageQuery() throws RepositoryException;
+
+    /**
+     * Create a query to retrieve contents which are located under a certain page.
+     * @return The implementation content-on-page query
+     * @throws RepositoryException If an error occured creating the query object
+     */
+    public ContentOnPageQuery createContentOnPageQuery() throws RepositoryException;
 }
Index: plugins/optional/site/src/org/ametys/anycontent/workspaces/site/repository/file/FileRepository.java
===================================================================
RCS file: /cvs/produits/Ametys/AnyContent_2.9/plugins/optional/site/src/org/ametys/anycontent/workspaces/site/repository/file/FileRepository.java,v
retrieving revision 1.2
diff -u -r1.2 FileRepository.java
--- plugins/optional/site/src/org/ametys/anycontent/workspaces/site/repository/file/FileRepository.java	31 Jul 2008 10:00:40 -0000	1.2
+++ plugins/optional/site/src/org/ametys/anycontent/workspaces/site/repository/file/FileRepository.java	27 Oct 2008 14:43:03 -0000
@@ -13,6 +13,7 @@
 import org.apache.avalon.framework.thread.ThreadSafe;
 import org.apache.cocoon.Constants;
 
+import org.ametys.anycontent.plugins.web.repository.ContentOnPageQuery;
 import org.ametys.anycontent.plugins.web.repository.Page;
 import org.ametys.anycontent.plugins.web.repository.PageContent;
 import org.ametys.anycontent.plugins.web.repository.PageQuery;
@@ -187,6 +188,11 @@
         throw new UnsupportedOperationException("This implementation of Repository does not allowed to query the repository.");
     }
 
+    public ContentOnPageQuery createContentOnPageQuery() throws RepositoryException
+    {
+        throw new UnsupportedOperationException("This implementation of Repository does not allowed to query the repository.");
+    }
+
     public Site createNewSite(String siteName, boolean changeNameIfNeeded) throws RepositoryException
     {
         throw new UnsupportedOperationException("This implementation of Repository does not allowed to write the repository.");
Index: plugins/web/src/org/ametys/anycontent/plugins/web/repository/ContentOnPageQuery.java
===================================================================
RCS file: plugins/web/src/org/ametys/anycontent/plugins/web/repository/ContentOnPageQuery.java
diff -N plugins/web/src/org/ametys/anycontent/plugins/web/repository/ContentOnPageQuery.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/web/src/org/ametys/anycontent/plugins/web/repository/ContentOnPageQuery.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,29 @@
+package org.ametys.anycontent.plugins.web.repository;
+
+import java.util.Collection;
+
+import org.ametys.anycontent.repository.Content;
+import org.ametys.anycontent.repository.Expression;
+import org.ametys.anycontent.repository.Query;
+import org.ametys.anycontent.repository.RepositoryException;
+import org.ametys.anycontent.repository.SortCriteria;
+
+/**
+ * This class handles a query on the repository.<br/> You will retrieve the implementation corresponding to your repository by using the <code>Repository.createContentOnPageQuery</code> method.
+ */
+public interface ContentOnPageQuery extends Query
+{
+    /**
+     * Execute the content query, limited to contents referenced by pages under the given page.
+     * @param siteName The name of the site or null for the default site (mono site).
+     * @param sitemapName The name of the sitemap or null for the default sitemap (mono sitemap).
+     * @param language The language or null for all languages.
+     * @param path the path of the page. All returned contents are referenced by pages under this one.
+     * @param pageExpression an optional expression on the pages or null.
+     * @param contentExpression The expression of the query to execute or null to match all.
+     * @param sortCriteria The criteria to use for sort purposes
+     * @return A collection of <code>Content</code> that match the query
+     * @throws RepositoryException If an error occured
+     */
+    public Collection<Content> execute(String siteName, String sitemapName, String language, String path, Expression pageExpression, Expression contentExpression, SortCriteria sortCriteria) throws RepositoryException;
+}
Index: plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRContentOnPageQuery.java
===================================================================
RCS file: plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRContentOnPageQuery.java
diff -N plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRContentOnPageQuery.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/web/src/org/ametys/anycontent/plugins/web/repository/jcr/JCRContentOnPageQuery.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,196 @@
+package org.ametys.anycontent.plugins.web.repository.jcr;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Session;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.jackrabbit.util.ISO9075;
+
+import org.ametys.anycontent.plugins.anycontent.repository.jcr.AbstractJCRQuery;
+import org.ametys.anycontent.plugins.anycontent.repository.jcr.JCRContent;
+import org.ametys.anycontent.plugins.anycontent.repository.jcr.JCRRepository;
+import org.ametys.anycontent.plugins.web.repository.ContentOnPageQuery;
+import org.ametys.anycontent.repository.Content;
+import org.ametys.anycontent.repository.Expression;
+import org.ametys.anycontent.repository.RepositoryException;
+import org.ametys.anycontent.repository.SortCriteria;
+
+/**
+ * JCR implementation of ContentOnPageQuery.
+ */
+public class JCRContentOnPageQuery extends AbstractJCRQuery implements ContentOnPageQuery
+{
+    /** The jcr repository */
+    protected JCRRepository _repository;
+    
+    /** The excalibur service manager */
+    protected ServiceManager _manager;
+
+    JCRContentOnPageQuery(JCRRepository repository, Session session, ServiceManager manager) throws javax.jcr.RepositoryException
+    {
+        _queryManager = session.getWorkspace().getQueryManager();
+        _repository = repository;
+        _manager = manager;
+    }
+    
+    public Collection<Content> execute(String siteName, String sitemapName, String language, String path, Expression pageExpression, Expression contentExpression, SortCriteria sortCriteria) throws RepositoryException
+    {
+        if (siteName == null)
+        {
+            return execute("default", sitemapName, language, path, pageExpression, contentExpression, sortCriteria);
+        }
+        
+        if (sitemapName == null)
+        {
+            return execute(siteName, "default", language, path, pageExpression, contentExpression, sortCriteria);
+        }
+        
+        long t0 = System.currentTimeMillis();
+        Query jcrQuery = _createQuery(siteName, sitemapName, language, path, pageExpression, contentExpression, sortCriteria);
+        if (_logger.isInfoEnabled())
+        {
+            _logger.info("JCRQuery '" + jcrQuery.getStatement() + "' created in " + (System.currentTimeMillis() - t0) + " ms");
+        }
+
+        long t1 = System.currentTimeMillis();
+        NodeIterator nodeIterator = _execute(jcrQuery);
+        if (_logger.isInfoEnabled())
+        {
+            _logger.info("JCRQuery '" + jcrQuery.getStatement() + "' executed in " + (System.currentTimeMillis() - t1) + " ms");
+        }
+
+        long t2 = System.currentTimeMillis();
+
+        Collection<Content> contents = _extractContents(nodeIterator, siteName, contentExpression);
+
+        if (_logger.isInfoEnabled())
+        {
+            _logger.info("Page list for JCRQuery '" + jcrQuery.getStatement() + "' completed in " + (System.currentTimeMillis() - t2) + " ms");
+        }
+
+        return contents;
+
+    }
+
+    private Query _createQuery(String siteName, String sitemapName, String language, String path, Expression pageExpression, Expression contentExpression, SortCriteria sortCriteria) throws RepositoryException
+    {
+        try
+        {
+            String xpathQuery = _getXPathQuery(siteName, sitemapName, language, path, pageExpression, contentExpression, sortCriteria);
+            _logger.info("Creating JCRQuery : " + xpathQuery);
+            return _queryManager.createQuery(xpathQuery, Query.XPATH);
+        }
+        catch (InvalidQueryException e)
+        {
+            String errorMessage = "The query is not valid! " + e.getMessage();
+            _logger.error(errorMessage, e);
+            throw new RepositoryException(errorMessage, e);
+        }
+        catch (javax.jcr.RepositoryException e)
+        {
+            String errorMessage = "An error occured creating the internal query object! " + e.getMessage();
+            _logger.error(errorMessage, e);
+            throw new RepositoryException(errorMessage, e);
+        }
+    }
+    
+    private String _getXPathQuery(String siteName, String sitemapName, String language, String path, Expression expression, Expression contentExpression, SortCriteria sortCriteria)
+    {
+        JCRExpression jcrExpression = (expression instanceof JCRExpression) ? (JCRExpression) expression : null;
+        String pageComposite = jcrExpression != null && jcrExpression.isComposite() ? jcrExpression.getCompositeMetadataPath() : "";
+
+        JCRExpression jcrContentExpression = (contentExpression instanceof JCRExpression) ? (JCRExpression) contentExpression : null;
+        String contentComposite = jcrContentExpression != null && jcrContentExpression.isComposite() ? jcrContentExpression.getCompositeMetadataPath() : "";
+
+        String pagePredicates = expression != null ? expression.build() : "";
+        String contentPredicates = contentExpression != null ? contentExpression.build() : "";
+
+        String xpathQuery = "/jcr:root/root/" + _getWildcardXPath(siteName) + "/pages/" + _getWildcardXPath(sitemapName) + "/" + _getWildcardXPath(language) + (path != null ? ("/" + _encode(path)) : "")
+            + "//element(*, anycontent:page)" + ((pagePredicates != null && pagePredicates.length() > 0) ? "[" + pagePredicates + "]" : "") + ((pageComposite != null && pageComposite.length() != 0) ? ("/" + pageComposite) : "")
+            + "/jcr:deref(@" + org.ametys.anycontent.plugins.anycontent.repository.jcr.JCRRepository.NAMESPACE_PREFIX_INTERNAL + "pageContent, '*')[@anycontent:template]/default" // predicat template obligatoire à cause d'un bug jackrabbit
+            + "/jcr:deref(@anycontent:content, '*')"
+            + (contentPredicates != null && contentPredicates.length() > 0 ? "[" + contentPredicates + "]" : "")
+            + ((contentComposite != null && contentComposite.length() != 0) ? ("/" + contentComposite) : "")
+            + ((sortCriteria != null) ? (" " + sortCriteria.build()) : "");
+        return xpathQuery;
+    }
+    
+    private String _encode(String path)
+    {
+        if (path == null || path.length() == 0)
+        {
+            return "";
+        }
+        
+        int pos = path.indexOf("/");
+        if (pos == -1)
+        {
+            return ISO9075.encode(path);
+        }
+        else
+        {
+            return ISO9075.encode(path.substring(0, pos)) + "/" + _encode(path.substring(pos + 1));
+        }
+    }
+    
+    private String _getWildcardXPath(String siteName)
+    {
+        return siteName != null ? siteName : "*";
+    }
+
+    private Collection<Content> _extractContents(NodeIterator nodeIterator, String siteName, Expression expression) throws RepositoryException
+    {
+        if (_offset > 0)
+        {
+            nodeIterator.skip(_offset);
+        }
+
+        boolean iterate = _limit != 0;
+        Map<String, Content> contents = new LinkedHashMap<String, Content>();
+
+        JCRExpression jcrExpression = (expression instanceof JCRExpression) ? (JCRExpression) expression : null;
+        boolean isComposite = jcrExpression != null && jcrExpression.isComposite();
+
+        try
+        {
+            while (iterate && nodeIterator.hasNext())
+            {
+                Node node = nodeIterator.nextNode();
+
+                Node contentNode = isComposite ? _getContentLanguageNode(node) : node;
+
+                Content content = new JCRContent(_repository, siteName, contentNode, null);
+
+                contents.put(contentNode.getUUID(), content);
+
+                iterate = _limit < 0 || nodeIterator.getPosition() < (_offset + _limit);
+            }
+        }
+        catch (javax.jcr.RepositoryException ex)
+        {
+            throw new RepositoryException("Unable to extract content form query results", ex);
+        }
+
+        return contents.values();
+    }
+    
+    private Node _getContentLanguageNode(Node node) throws javax.jcr.RepositoryException
+    {
+        String contentLanguageNodeTypeName = JCRRepository.NAMESPACE_PREFIX + "contentLanguage";
+        Node currentNode = node;
+        
+        while (!contentLanguageNodeTypeName.equals(currentNode.getPrimaryNodeType().getName()))
+        {
+            currentNode = currentNode.getParent();
+        }
+        
+        return currentNode;
+    }
+}
