Index: main/plugin-odf/plugin.xml
===================================================================
--- main/plugin-odf/plugin.xml	(revision 38757)
+++ main/plugin-odf/plugin.xml	(working copy)
@@ -33,6 +33,11 @@
 						 class="org.ametys.odf.cdmfr.CDMfrExtensionPoint">
 			<!-- CDMfr extension point to SAX specific data into CDM. -->
 		</extension-point>
+        
+        <extension-point id="org.ametys.odf.oai.OaiSetExtensionPoint"
+                         class="org.ametys.odf.oai.OaiSetExtensionPoint">
+             <!-- Extension point for providing OAI-PMH Sets -->
+        </extension-point>
 		
     	<!--+
     	 	 + Import extensions points
Index: main/plugin-odf/src/org/ametys/odf/oai/AbstractOaiSet.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/oai/AbstractOaiSet.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/oai/AbstractOaiSet.java	(revision 0)
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.oai;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+
+import org.ametys.runtime.util.I18nizableText;
+
+/**
+ * Abstract implementation of a OAI-PMH set with a configurable name and description
+ */
+public abstract class AbstractOaiSet implements OaiSet, Configurable, Component
+{
+    private I18nizableText _name;
+    private I18nizableText _description;
+
+    @Override
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        _name = _getI18nizableConfiguration(configuration, "name");
+        _description = _getI18nizableConfiguration(configuration, "description");
+    }
+    
+    private I18nizableText _getI18nizableConfiguration(Configuration configuration, String configurationName)
+    {
+        boolean isValueI18n = configuration.getChild(configurationName).getAttributeAsBoolean("i18n", false);
+        String value =  configuration.getChild(configurationName).getValue(null);
+
+        if (value == null)
+        {
+            return null;
+        }
+        else if (isValueI18n)
+        {
+            String valueI18nPlugin = configuration.getChild(configurationName).getAttribute("plugin", "odf");
+            return new I18nizableText("plugin." + valueI18nPlugin, value);
+        }
+        else
+        {
+            return new I18nizableText(value);
+        }
+    }
+
+    public I18nizableText getName()
+    {
+        return _name;
+    }
+
+    public I18nizableText getDescription()
+    {
+        return _description;
+    }
+}
Index: main/plugin-odf/src/org/ametys/odf/oai/ListIdentifiersGenerator.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/oai/ListIdentifiersGenerator.java	(revision 38757)
+++ main/plugin-odf/src/org/ametys/odf/oai/ListIdentifiersGenerator.java	(working copy)
@@ -57,10 +57,14 @@
     /** The ametys object resolver. */
     private AmetysObjectResolver _resolver;
     
+    /** The OAI-PMH Sets Extension Point*/
+    private OaiSetExtensionPoint _oaiSetEP;
+    
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
     {
         _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
+        _oaiSetEP = (OaiSetExtensionPoint) serviceManager.lookup(OaiSetExtensionPoint.ROLE);
     }
     
     @Override
@@ -80,14 +84,6 @@
     {
         Request request = ObjectModelHelper.getRequest(objectModel);
 
-        String set = request.getParameter("set");
-        
-        if (StringUtils.isNotEmpty(set))
-        {
-            generateError("noSetHierarchy", "This repository does not support sets");
-            return;
-        }
-        
         String token = request.getParameter("resumptionToken");
         
         if (StringUtils.isNotEmpty(token))
@@ -154,6 +150,19 @@
             return;
         }
         
+        OaiSet oaiSet = null;
+        String set = request.getParameter("set");
+        
+        if (StringUtils.isNotEmpty(set))
+        {
+            oaiSet = _oaiSetEP.getExtension(set);
+            if (oaiSet == null)
+            {
+                generateError("noRecordsMatch", "The set specified as an argument does not exists.");
+                return;
+            }
+        }
+        
         expressions.add(new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE));
         expressions.add(new ContentLanguageExpression(Operator.EQ, "fr"));
         
@@ -162,9 +171,18 @@
         SortCriteria sortCriteria = new SortCriteria();
         sortCriteria.addCriterion(DefaultContent.METADATA_TITLE, true, true);
         
-        String query = QueryHelper.getXPathQuery(null, "ametys:content", expression, sortCriteria);
+        AmetysObjectIterable<Program> programs;
         
-        AmetysObjectIterable<Program> programs = _resolver.query(query);
+        if (oaiSet != null)
+        {
+            programs = oaiSet.getRecords(expression, sortCriteria);
+        }
+        else
+        {
+            String query = QueryHelper.getXPathQuery(null, "ametys:content", expression, sortCriteria);
+            
+            programs = _resolver.query(query);
+        }
         
         XMLUtils.startElement(contentHandler, getTagName());
         
Index: main/plugin-odf/src/org/ametys/odf/oai/ListSetsGenerator.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/oai/ListSetsGenerator.java	(revision 38757)
+++ main/plugin-odf/src/org/ametys/odf/oai/ListSetsGenerator.java	(working copy)
@@ -18,15 +18,32 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Set;
 
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
 import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.lang.StringUtils;
 import org.xml.sax.SAXException;
 
+import org.ametys.runtime.util.I18nizableText;
+
 /**
  * <code>ListSets</code> generator. Always send a "noSetHierarchy" error as sets are not implemented.
  */
-public class ListSetsGenerator extends OAIVerbGenerator
+public class ListSetsGenerator extends OAIVerbGenerator implements Serviceable
 {
+    private OaiSetExtensionPoint _oaiSetEP;
+
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _oaiSetEP = (OaiSetExtensionPoint) serviceManager.lookup(OaiSetExtensionPoint.ROLE);
+    }
+    
     @Override
     protected Collection<String> getRequiredParameters()
     {
@@ -42,6 +59,47 @@
     @Override
     protected void generateVerb() throws IOException, SAXException, ProcessingException
     {
-        generateError("noSetHierarchy", "This repository does not support sets");
+        Request request = ObjectModelHelper.getRequest(objectModel);
+
+        String token = request.getParameter("resumptionToken");
+        
+        if (StringUtils.isNotEmpty(token))
+        {
+            generateError("badResumptionToken ", "This repository does not support resumption tokens");
+            return;
+        }
+        
+        Set<String> extensionsIds = _oaiSetEP.getExtensionsIds();
+        
+        XMLUtils.startElement(contentHandler, "ListSets");
+        
+        for (String extensionId : extensionsIds)
+        {
+            OaiSet oaiSet = _oaiSetEP.getExtension(extensionId);
+            
+            XMLUtils.startElement(contentHandler, "set");
+            
+            XMLUtils.createElement(contentHandler, "setSpec", extensionId);
+            I18nizableText name = oaiSet.getName();
+            if (name != null)
+            {
+                name.toSAX(contentHandler, "setName");
+            }
+            else
+            {
+                XMLUtils.createElement(contentHandler, "setName", "");
+            }
+            
+            I18nizableText description = oaiSet.getDescription();
+            if (description != null)
+            {
+                description.toSAX(contentHandler, "setDescription");
+            }
+            
+            XMLUtils.endElement(contentHandler, "set");
+        }
+        
+        XMLUtils.endElement(contentHandler, "ListSets");
     }
+
 }
Index: main/plugin-odf/src/org/ametys/odf/oai/OaiSet.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/oai/OaiSet.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/oai/OaiSet.java	(revision 0)
@@ -0,0 +1,51 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.oai;
+
+import org.ametys.odf.program.Program;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.query.SortCriteria;
+import org.ametys.plugins.repository.query.expression.Expression;
+import org.ametys.runtime.util.I18nizableText;
+
+/**
+ * Interface for OAI-PMH Sets
+ */
+public interface OaiSet
+{
+    /** Avalon Role */
+    public static final String ROLE = OaiSet.class.getName();
+    
+    /**
+     * Return the name of the set
+     * @return The name of the set. Can't be null.
+     */
+    public I18nizableText getName();
+    
+    /**
+     * Return the description of the set
+     * @return The description of the set, or null if there is no description.
+     */
+    public I18nizableText getDescription();
+    
+    /**
+     * Retrieve the list of Records associated with this OAI Set
+     * @param expression An Expression to filter the list of records during the request
+     * @param sortCriteria A sort criteria to sort the result
+     * @return The list of Records
+     */
+    public AmetysObjectIterable<Program> getRecords(Expression expression, SortCriteria sortCriteria);
+}
Index: main/plugin-odf/src/org/ametys/odf/oai/OaiSetExtensionPoint.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/oai/OaiSetExtensionPoint.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/oai/OaiSetExtensionPoint.java	(revision 0)
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.oai;
+
+import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
+
+/**
+ * Extension point holding all {@link OaiSet} classes loaded after all components and extensions.
+ */
+public class OaiSetExtensionPoint extends AbstractThreadSafeComponentExtensionPoint<OaiSet>
+{
+    /** Avalon Role */
+    public static final String ROLE = OaiSetExtensionPoint.class.getName();
+}
Index: main/workspace-odf/sitemap.xmap
===================================================================
--- main/workspace-odf/sitemap.xmap	(revision 38757)
+++ main/workspace-odf/sitemap.xmap	(working copy)
@@ -58,6 +58,9 @@
                     </map:when>
                     <map:when test="ListSets">
                         <map:generate type="oai-listsets"/>
+                        <map:transform type="i18n">
+                            <map:parameter name="locale" value="{locale:locale}"/>
+                        </map:transform>
                         <map:serialize type="xml"/>
                     </map:when>
                     <map:when test="ListIdentifiers">
