### Eclipse Workspace Patch 1.0
#P Ametys - Plugin ODF
Index: main/plugin-odf/i18n/messages_en.xml
===================================================================
--- main/plugin-odf/i18n/messages_en.xml	(revision 27816)
+++ main/plugin-odf/i18n/messages_en.xml	(working copy)
@@ -15,10 +15,31 @@
    limitations under the License.
    -->
 <catalogue xml:lang="en">
+
+    <!-- +
+         | CATALOG
+         + -->
+    <message key="PLUGINS_ODF_FORMATIONS_CATALOGUE_FOOTER_TEXT">All the information written on that page are indicative and do not have any contractual value</message>
+    <message key="PLUGINS_ODF_FORMATIONS_CATALOGUE">Programs' catalogue</message>
+    <message key="PLUGINS_ODF_TABLE_OF_CONTENTS">Table of contents</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_LABEL">Export the programs to pdf</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_DESCRIPTION">This button generates a summary of the programs in pdf format</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_FOOTER">See help for more details</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_ERROR">Unable to start the generation of the programs' catalog.</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TITLE">Unauthorized operation</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TEXT">The programs' catalog is already being generated. An email will be automatically sent to the system administrator when the file is ready.</message>
+    <message key="PLUGINS_ODF_PDF_EXPORT_MAIL_SUBJECT">Programs' catalog</message>
+    <message key="PLUGINS_ODF_PDF_EXPORT_MAIL_BODY_SUCCESS">The programs' catalog has been generated in the WEB-INF directory of the application. It can be downloaded through the following link: </message>
+    <message key="PLUGINS_ODF_PDF_EXPORT_MAIL_BODY_FAILURE">An error occurred during the generation of the programs' catalog.</message> 
+    <message key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TITLE">Generation of the catalog</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TEXT">The programs' catalog generation has begun. An email will be sent to you when the process is over.</message>
+	
 	<!-- +
 	     | RIGHTS
 	     + -->
 	<message key="PLUGINS_ODF_RIGHTS_ODF_CATEGORY">Training offer</message>
+	<message key="PLUGINS_ODF_RIGHTS_ODF_GENERATE_PDF">Generate the catalog</message>
+	<message key="PLUGINS_ODF_RIGHTS_ODF_GENERATE_PDF_DESC">Allows to generate the programs' catalog.</message>
 	<message key="PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD">Add a program</message>
 	<message key="PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD_DESC">Allows to create a new program</message>
 	<message key="PLUGINS_ODF_RIGHTS_ODF_PROGRAM_EDIT">Edit a program</message>
@@ -268,6 +289,7 @@
     <message key="RIBBON_TABS_TAB_ODF_COURSELIST_LABEL">Courses list</message>
     <message key="RIBBON_TABS_TAB_ODF_PERSON_LABEL">People</message>    
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_PERSONS_LABEL">Peoples</message>
+	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_PDF_EXPORT_LABEL">PDF Export</message>
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_ORGUNIT_LABEL">OrgUnits</message>
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_COURSES_LABEL">Courses</message>
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_COURSELIST_LABEL">Courses lists</message>
Index: main/plugin-odf/i18n/messages_fr.xml
===================================================================
--- main/plugin-odf/i18n/messages_fr.xml	(revision 27816)
+++ main/plugin-odf/i18n/messages_fr.xml	(working copy)
@@ -16,10 +16,30 @@
    -->
 <catalogue xml:lang="fr">
 
+    <!-- +
+         | CATALOGUE
+         + -->
+    <message key="PLUGINS_ODF_FORMATIONS_CATALOGUE_FOOTER_TEXT">Toutes les informations données sur cette page sont indicatives et n'ont pas de valeur contractuelle</message>
+    <message key="PLUGINS_ODF_FORMATIONS_CATALOGUE">Catalogue des formations</message>
+    <message key="PLUGINS_ODF_TABLE_OF_CONTENTS">Table des matières</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_LABEL">Export pdf des formations</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_DESCRIPTION">Ce bouton permet de générer un récapitulatif de toutes les formations au format pdf</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_FOOTER">Voir l'aide pour plus de détails</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_ERROR">Une erreur s'est produite lors du lancement de la génération du catalogue de formations.</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TITLE">Opération non autorisée</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TEXT">La catalogue des formations est en train d'être généré. Un email sera automatiquement envoyé à l'administrateur système quand le fichier sera prêt.</message>
+    <message key="PLUGINS_ODF_PDF_EXPORT_MAIL_SUBJECT">Catalogue des formations</message>
+    <message key="PLUGINS_ODF_PDF_EXPORT_MAIL_BODY_SUCCESS">Le catalogue des formations a été généré dans le dossier WEB-INF du répertoire de l'application. Il peut être téléchargé via le lien : </message>
+    <message key="PLUGINS_ODF_PDF_EXPORT_MAIL_BODY_FAILURE">Une erreur s'est produite lors de la génération du catalogue des formations.</message> 
+    <message key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TITLE">Génération du catalogue</message>
+    <message key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TEXT">La génération du catalogue de formations est déclenchée. Un email vous sera envoyé à la fin du processus.</message>
+        
 	<!-- +
 	     | RIGHTS
 	     + -->
 	<message key="PLUGINS_ODF_RIGHTS_ODF_CATEGORY">Offre de formation</message>
+	<message key="PLUGINS_ODF_RIGHTS_ODF_GENERATE_PDF">Générer le catalogue</message>
+	<message key="PLUGINS_ODF_RIGHTS_ODF_GENERATE_PDF_DESC">Autorise à générer le catalogue de formations.</message>
 	<message key="PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD">Ajouter une formation</message>
 	<message key="PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD_DESC">Autorise à créer une nouvelle formation vierge</message>
 	<message key="PLUGINS_ODF_RIGHTS_ODF_PROGRAM_EDIT">Editer une formation</message>
@@ -268,6 +288,7 @@
     <message key="RIBBON_TABS_TAB_ODF_COURSELIST_LABEL">Liste d\'ELP</message>
     <message key="RIBBON_TABS_TAB_ODF_PERSON_LABEL">Contact</message>    
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_PERSONS_LABEL">Contacts</message>
+	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_PDF_EXPORT_LABEL">Export PDF</message>
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_ORGUNIT_LABEL">Composantes</message>
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_COURSES_LABEL">ELP</message>
 	<message key="RIBBON_TABS_TAB_ODF_GROUPS_GROUP_COURSELIST_LABEL">Liste d\'éléments pédagogiques</message>
Index: main/plugin-odf/plugin.xml
===================================================================
--- main/plugin-odf/plugin.xml	(revision 27816)
+++ main/plugin-odf/plugin.xml	(working copy)
@@ -33,7 +33,12 @@
     	 	 +-->    
           <single-extension-point id="org.ametys.odf.person.ContactManager"
                                class="org.ametys.odf.person.ContactManager"
-                               default-extension-id="org.ametys.odf.person.ldap.LdapContactManager"/>      
+                               default-extension-id="org.ametys.odf.person.ldap.LdapContactManager"/>   
+                               
+          <!-- ODF view extension point -->
+          <single-extension-point id="org.ametys.odf.catalog.source.ODFViewSelector" 
+                                  class="org.ametys.odf.catalog.source.ODFViewSelector" 
+                                  default-extension-id="org.ametys.odf.catalog.source.DefaultODFView"/>  
 	</extension-points> 
 	
 	<feature name="odf.config">
@@ -67,14 +72,63 @@
 		</config>
 	</feature>
 	
+    <feature name="default-view-selector">
+        <extensions>
+            <extension point="org.ametys.odf.catalog.source.ODFViewSelector" 
+                       id="org.ametys.odf.catalog.source.DefaultODFView"
+                       class="org.ametys.odf.catalog.source.DefaultODFView" />
+        </extensions>
+    </feature>
+    
+    <feature name="source.odf-view">
+        <extensions>
+            <extension point="org.apache.excalibur.source.SourceFactorySelector" 
+                       id="odf-view" class="org.ametys.odf.catalog.source.ODFViewSourceFactory">
+                <!-- Source factory for retrieving a file in the skins directory, in the WEB-INF/stylesheets directory or in the catalogue plugin -->
+            </extension>
+        </extensions>
+    </feature>
+    
+    <feature name="pdfexport">
+        <extensions>
+            <extension id="org.ametys.odf.PDFExport"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.ActionButton">
+                    <param name="action">org.ametys.odf.pdfexport.PDFExport.pdfExport</param>
+                    
+                    <param name="label" i18n="true">PLUGINS_ODF_PDFEXPORT_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_ODF_PDFEXPORT_DESCRIPTION</param>
+                    <param name="footer" i18n="true">PLUGINS_ODF_PDFEXPORT_FOOTER</param>
+                    
+                    <param name="icon-small" file="true">img/pdfexport/pdfexport-16.png</param>
+                    <param name="icon-medium" file="true">img/pdfexport/pdfexport-32.png</param>
+                    <param name="icon-large" file="true">img/pdfexport/pdfexport-48.png</param>
+                </action> 
+                <scripts>
+                    <file>js/org/ametys/odf/pdfexport/PDFExport.i18n.js</file>
+                </scripts>
+                <right>ODF_Rights_Generate_PDF</right>
+            </extension>
+        </extensions>
+    </feature>
+        
 	<feature name="odf_rights">
         <extensions>            
             <extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
+                       id="odf.rights.generate.pdf">
+                <right id="ODF_Rights_Generate_PDF">
+                    <label>PLUGINS_ODF_RIGHTS_ODF_GENERATE_PDF</label>
+        			<description>PLUGINS_ODF_RIGHTS_ODF_GENERATE_PDF_DESC</description>
+        			<category>PLUGINS_ODF_RIGHTS_ODF_CATEGORY</category>
+                </right>
+            </extension>
+            <extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
                        id="odf.rights.program.add">
                 <right id="ODF_Rights_Program_Add">
                     <label>PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD</label>
-        			<description>PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD_DESC</description>
-        			<category>PLUGINS_ODF_RIGHTS_ODF_CATEGORY</category>
+                    <description>PLUGINS_ODF_RIGHTS_ODF_PROGRAM_ADD_DESC</description>
+                    <category>PLUGINS_ODF_RIGHTS_ODF_CATEGORY</category>
                 </right>
             </extension>
             <extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
Index: main/plugin-odf/resources/js/org/ametys/odf/pdfexport/PDFExport.i18n.js
===================================================================
--- main/plugin-odf/resources/js/org/ametys/odf/pdfexport/PDFExport.i18n.js	(revision 0)
+++ main/plugin-odf/resources/js/org/ametys/odf/pdfexport/PDFExport.i18n.js	(revision 0)
@@ -0,0 +1,61 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.odf.pdfexport');
+Ext.namespace('org.ametys.odf.pdfexport.PDFExport');
+
+org.ametys.odf.pdfexport.PDFExport = {};
+
+//------------------------------------------------------------------
+org.ametys.odf.pdfexport.PDFExport.pdfExport = function(controlId, controlConfiguration, controlPluginName)
+{
+    var serverMessage = new org.ametys.servercomm.ServerMessage(controlPluginName, '/generate-pdf', {}, 
+    															org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.odf.pdfexport.PDFExport._pdfExportCb, 
+    															this, null);
+    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.odf.pdfexport.PDFExport._pdfExportCb = function(response)
+{
+    if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key='PLUGINS_ODF_PDFEXPORT_ERROR'/>", response, 'org.ametys.odf.pdfexport.PDFExport'))
+    {
+        throw "org.ametys.odf.pdfexport.PDFExport request failed";
+    }
+    
+    var errorNode = response.selectSingleNode('ActionResult/error');
+    if (errorNode != null &amp;&amp; errorNode[org.ametys.servercomm.ServerComm.xmlTextContent] != '')
+    {
+        var error = errorNode[org.ametys.servercomm.ServerComm.xmlTextContent];
+        if (error == 'already-running')
+        {
+            Ext.Msg.show({
+                title: "<i18n:text i18n:key='PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TITLE'/>",
+                msg: "<i18n:text i18n:key='PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TEXT'/>",
+                buttons: Ext.Msg.OK,
+                icon: Ext.Msg.ERROR
+            });
+        }
+    }
+    else
+	{
+	    Ext.Msg.show({
+	        title: "<i18n:text i18n:key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TITLE" i18n:catalogue="plugin.odf"/>",
+	        msg: "<i18n:text i18n:key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TEXT" i18n:catalogue="plugin.odf"/>",
+	        buttons: Ext.Msg.OK,
+	        icon: Ext.Msg.INFO
+	    });
+	}
+}
Index: main/plugin-odf/sitemap.xmap
===================================================================
--- main/plugin-odf/sitemap.xmap	(revision 27816)
+++ main/plugin-odf/sitemap.xmap	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2014 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -55,6 +55,9 @@
             <map:generator name="program-workflow-tasks" src="org.ametys.odf.workflow.ProgramWorkflowTasksGenerator"/>
             <map:generator name="course-tasks" src="org.ametys.odf.workflow.CourseTasksGenerator"/>
             <map:generator name="course-workflow-tasks" src="org.ametys.odf.workflow.CourseWorkflowTasksGenerator"/>
+            
+            <map:generator name="catalog-summary" src="org.ametys.odf.catalog.generators.CatalogSummaryGenerator" />
+            <map:generator name="catalog-programs" src="org.ametys.odf.catalog.generators.CatalogProgramsGenerator" />
 		</map:generators>
 		
 		<map:actions>
@@ -88,13 +91,14 @@
 			<map:action name="get-ctype-info" src="org.ametys.cms.content.GetContentTypeInformationAction"/>
 			<map:action name="get-content" src="org.ametys.cms.content.GetContentAction"/>
 			<map:action name="validate-action" src="org.ametys.odf.workflow.ValidateProgramWorkflowAction"/>
+            <map:action name="generate-pdf" src="org.ametys.odf.catalog.actions.GeneratePDFAction" logger="org.ametys.odf.catalog.actions.GeneratePDFAction"/>
 		</map:actions>
 		
 		<map:transformers>
 			<map:transformer name="docbook-enhancement" src="org.ametys.cms.transformation.docbook.DocbookEnhancementTransformer"/>
 			<map:transformer name="html-enhancement" src="org.ametys.cms.transformation.html.HTMLEnhancementTransformer"/>
 		</map:transformers>
-				
+		
 		<map:serializers>
 			 <map:serializer name="xml-utf8-without-xml-decl" src="org.apache.cocoon.serialization.XMLSerializer" logger="sitemap.serializer.text" mime-type="text/plain; charset=UTF-8" pool-grow="4" pool-max="32" pool-min="16">
                 <encoding>UTF-8</encoding>
@@ -120,6 +124,9 @@
 		<map:view from-label="xml" name="xml">
 			<map:serialize type="xml"/>
 		</map:view>
+        <map:view from-label="xsl" name="xsl">
+            <map:serialize type="xml"/>
+        </map:view>
 	</map:views>
 
 	<map:pipelines>
@@ -318,14 +325,14 @@
 
             <map:match pattern="org.ametys.plugins.odf.Content.program.pdf">
                 <map:act type="get-ctype-info">
-                    <map:aggregate element="view">
+                    <map:aggregate element="view" label="xml">
                         <map:part src="cocoon:/pdf-content-program"/>
                     </map:aggregate>
                     <map:transform type="i18n">
                         <map:parameter name="locale" value="{request-attr:renderingLanguage}"/>
                         <map:parameter name="plugin" value="{request-attr:pluginName}"/>
                     </map:transform>
-                    <map:transform src="content-view://program/view-{request-param:metadataSetName}/pdf.xsl">
+                    <map:transform src="content-view://program/view-{request-param:metadataSetName}/pdf.xsl"  label="xsl">
                         <map:parameter name="cms-context" value="{request:contextPath}"/>
                     </map:transform>
                     <map:transform type="i18n">
@@ -544,7 +551,52 @@
                     <map:redirect-to uri="cocoon://_plugins/{plugin}/{contentType}.{../2}"/>
                 </map:act>
             </map:match>
+            
+            <!-- +
+                 | PDF CATALOG
+                 +  -->
+            <map:match pattern="download/catalog.pdf">
+                <map:read mime-type="application/pdf" src="context://WEB-INF/data/odf/catalog/catalog.pdf"/>
+            </map:match>
+            
+            <map:match pattern="generate-pdf">
+                <map:act type="generate-pdf">
+                    <map:generate type="action-result">
+                        <map:parameter name="error" value="{error}"/>
+                    </map:generate>
+                    <map:serialize type="xml"/>
+                </map:act>
+            </map:match>
+            
+            <map:match pattern="pdfcatalog-summary">
+                <map:generate type="catalog-summary"/>   
+                <map:serialize type="xml" />       
+            </map:match>
+       
+            <map:match pattern="pdfcatalog-programs">
+                <map:generate type="catalog-programs"/> 
+                <map:serialize type="xml"/>             
+            </map:match>
+       
+            <map:match pattern="catalog.pdf">
+                <map:aggregate element="view" label="xml">
+                   <map:part src="cocoon:/pdfcatalog-summary"/>
+                   <map:part src="cocoon:/pdfcatalog-programs"/>
+                </map:aggregate>
+                
+                <map:transform type ="xslt" src="odf-view://stylesheets/catalog/programs2pdf.xsl" label="xsl"/>
+                
+                <map:transform type="i18n">
+                    <map:parameter name="locale" value="{locale:locale}"/>
+                    <map:parameter name="plugin" value="{request-attr:pluginName}"/>
+                </map:transform>
+                
+                <map:serialize type="fo2pdf" />
+            </map:match> 
                  
+            <!-- +
+                 | CHECK AUTHENTICATION
+                 +  -->
             <map:match pattern="**">
                 <map:act type="check-auth"/>
             </map:match>
@@ -901,5 +953,6 @@
             </map:match>
 			
 		</map:pipeline>
+	
 	</map:pipelines>
 </map:sitemap>
Index: main/plugin-odf/src/org/ametys/odf/catalog/actions/GeneratePDFAction.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/actions/GeneratePDFAction.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/actions/GeneratePDFAction.java	(revision 0)
@@ -0,0 +1,131 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.catalog.actions;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Timer;
+
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.SourceResolver;
+
+import org.ametys.odf.catalog.engines.GeneratePDFEngine;
+import org.ametys.runtime.user.CurrentUserProvider;
+import org.ametys.runtime.user.User;
+import org.ametys.runtime.user.UsersManager;
+
+/**
+ * Action starting generation of the program's catalogue in pdf format
+ */
+public class GeneratePDFAction extends ServiceableAction implements Contextualizable, Configurable
+{
+    /** The component configuration. */
+    protected Configuration _configuration;
+    
+    /** The avalon context. */
+    protected Context _context;
+    
+    /** The logger. */
+    protected Logger _logger;
+    
+    /** The timer. */
+    protected Timer _timer;
+    
+    /** The manager */
+    protected ServiceManager _manager;
+ 
+    /** The current user provider */
+    private CurrentUserProvider _currentUserProvider;
+
+    /** The users manager */
+    private UsersManager _usersManager;
+    
+    @Override
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        _configuration = configuration;
+    }
+    
+    @Override
+    public void contextualize(Context context) throws ContextException
+    {
+        _context = context;
+    }
+    
+    @Override
+    public void service(ServiceManager sManager) throws ServiceException
+    {
+        _manager = sManager;
+        _currentUserProvider = (CurrentUserProvider) sManager.lookup(CurrentUserProvider.ROLE);
+        _usersManager = (UsersManager) sManager.lookup(UsersManager.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        if (GeneratePDFEngine.isRunning())
+        {
+            return Collections.singletonMap("error", "already-running");
+        }
+        else
+        {
+            GeneratePDFEngine generatePDFEngine = new GeneratePDFEngine();
+            
+            String login = _currentUserProvider.getUser();
+            User currentUser = _usersManager.getUser(login);
+            
+            generatePDFEngine.setUrl(_generateUrl(source));
+            generatePDFEngine.setMailTo(currentUser.getEmail());
+            
+            try
+            {
+                // Initialize and configure the engine.
+                generatePDFEngine.initialize(_manager, _context);
+                
+                // Create the thread, mark it as daemon and start it.
+                Thread engineThread = new Thread(generatePDFEngine, "GeneratePDFEngine");
+                engineThread.setDaemon(true);
+                engineThread.start();
+                
+                return EMPTY_MAP;
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException("Unable to initialize the generate pdf engine", e);
+            }
+        }
+    }
+    
+    /**
+     * Generates the url that will be resolved by the engine
+     * @param source the request's source
+     * @return the url 
+     */
+    protected String _generateUrl(String source)
+    {
+        return "cocoon://_plugins/odf/catalog.pdf";
+    }
+}
Index: main/plugin-odf/src/org/ametys/odf/catalog/engines/GeneratePDFEngine.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/engines/GeneratePDFEngine.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/engines/GeneratePDFEngine.java	(revision 0)
@@ -0,0 +1,321 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.catalog.engines;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.Constants;
+import org.apache.cocoon.components.source.impl.SitemapSource;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.source.SourceUtil;
+
+import org.ametys.cms.engine.BackgroundEngineHelper;
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.runtime.config.Config;
+import org.ametys.runtime.plugins.core.mail.SendMailHelper;
+import org.ametys.runtime.util.I18nUtils;
+import org.ametys.runtime.util.I18nizableText;
+import org.ametys.runtime.util.LoggerFactory;
+
+/**
+ * Content consistency engine: generate consistency information for all contents.
+ * Sends a report e-mail if there are inconsistencies.
+ */
+public class GeneratePDFEngine implements Runnable
+{
+    /** The logger. */
+    protected static final Logger _LOGGER = LoggerFactory.getLoggerFor(GeneratePDFEngine.class);
+    
+    /** Is the thread running ? */
+    private static boolean _RUNNING;
+    
+    /** The avalon context. */
+    protected Context _context;
+
+    /** The cocoon environment context. */
+    protected org.apache.cocoon.environment.Context _environmentContext;
+    
+    /** The service manager. */
+    protected ServiceManager _manager;
+    
+    /** The avalon source resolver. */
+    protected SourceResolver _sourceResolver;
+    
+    /** The i18n utils. */
+    protected I18nUtils _i18nUtils;
+    
+    /** The catalog directory. */
+    protected File _catalogDirectory;
+    
+    /** The sender of the email */
+    protected String _mailFrom;
+    
+    /** The email of the receiver */
+    private String _mailTo;
+    
+    /** Is the engine initialized ? */
+    protected boolean _initialized;
+    
+    /** The url to call */
+    private String _url;
+    
+    /**
+     * Initialize the alert engine.
+     * @param manager the avalon service manager.
+     * @param context the avalon context.
+     * @throws ContextException if an error occurs retrieving the environment context.
+     * @throws ServiceException if an error occurs retrieving a component.
+     */
+    public void initialize(ServiceManager manager, Context context) throws ContextException, ServiceException
+    {
+        _manager = manager;
+        _context = context;
+        _environmentContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
+        
+        // Lookup the needed components.
+        _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
+        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
+        
+        // initialize the parameters
+        _mailFrom = Config.getInstance().getValueAsString("smtp.mail.from");
+        _catalogDirectory = new File(_environmentContext.getRealPath("WEB-INF/data/odf/catalog"));
+        
+        _initialized = true;
+    }
+    
+    /**
+     * Configure the engine (called by the scheduler).
+     * @param configuration the component configuration.
+     * @throws ConfigurationException
+     */
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        // Ignore
+    }
+    
+    /**
+     * Sets the url to resolve
+     * @param url the url of the pipeline to resolve
+     */
+    public void setUrl(String url)
+    {
+        _url = url;
+    }
+    
+    /**
+     * Sets the email address of the receiver
+     * @param mailTo the email address of the receiver
+     */
+    public void setMailTo(String mailTo)
+    {
+        _mailTo = mailTo;
+    }
+    
+    /**
+     * Check the initialization and throw an exception if not initialized.
+     */
+    protected void _checkInitialization()
+    {
+        if (!_initialized)
+        {
+            String message = "The synchronization component has to be initialized before being run";
+            _LOGGER.error(message);
+            throw new IllegalStateException(message);
+        }
+    }
+    
+    /**
+     * Test if the engine is running at the time.
+     * @return true if the engine is running, false otherwise.
+     */
+    public static boolean isRunning()
+    {
+        return _RUNNING;
+    }
+    
+    private static void setRunning(boolean running)
+    {
+        _RUNNING = running;
+    }
+    
+    @Override
+    public void run()
+    {
+        Map<String, Object> environmentInformation = null;
+        boolean error = false; 
+        
+        try
+        {
+            _LOGGER.info("Preparing to generate the pdf catalog...");
+            
+            _checkInitialization();
+            
+            // Create the environment.
+            environmentInformation = BackgroundEngineHelper.createAndEnterEngineEnvironment(_manager, _environmentContext, _LOGGER);
+            
+            _generateReports();
+        }
+        catch (Exception e)
+        {
+            error = true;
+            _LOGGER.error("An error occurred while generating the pdf catalog.", e);
+        }
+        finally
+        {
+            _sendMail(error);
+            
+            // Leave the environment.
+            if (environmentInformation != null)
+            {
+                BackgroundEngineHelper.leaveEngineEnvironment(environmentInformation);
+            }
+            // Dispose of the resources.
+            _dispose();
+            _LOGGER.info("The pdf catalog has been generated.");
+        }
+    }
+    
+    /**
+     * Dispose of the resources and looked-up components.
+     */
+    protected void _dispose()
+    {
+        _sourceResolver = null;
+        _environmentContext = null;
+        _context = null;
+        _manager = null;
+        
+        _initialized = false;
+    }
+    
+    /**
+     * Send all the alerts. Can be overridden to add alerts.
+     * @throws AmetysRepositoryException if an error occurs.
+     * @throws IOException 
+     */
+    protected void _generateReports() throws AmetysRepositoryException, IOException
+    {
+        if (isRunning())
+        {
+            _LOGGER.error("Cannot start the generation, as the engine is running at the time.");
+        }
+        else
+        {
+            try
+            {
+                setRunning(true);
+                
+                // Generate the report.
+                _generateReport();
+            }
+            finally
+            {
+                setRunning(false);
+            }
+        }
+    }
+    
+    /**
+     * Generate the full consistency report.
+     * @throws IOException if an i/o error occurs.
+     */
+    protected void _generateReport() throws IOException
+    {
+        SitemapSource source = null;
+        File reportTmpFile = null;
+        OutputStream reportTmpOs = null;
+        InputStream sourceIs = null;
+            
+        try
+        {
+            FileUtils.forceMkdir(_catalogDirectory);
+            
+            // Resolve the export to the appropriate pdf pipeline.
+            source = (SitemapSource) _sourceResolver.resolveURI(_url);
+            
+            // Save the pdf into a temporary file.
+            reportTmpFile = new File(_catalogDirectory, "catalog.tmp.pdf");
+            reportTmpOs = new FileOutputStream(reportTmpFile);
+            
+            sourceIs = source.getInputStream();
+            SourceUtil.copy(sourceIs, reportTmpOs);
+            
+            // If all went well until now, rename the temporary file 
+            File reportFile = new File(_catalogDirectory, "catalog.pdf");
+            reportTmpFile.renameTo(reportFile);
+        }
+        finally
+        {
+            if (reportTmpFile != null)
+            {
+                FileUtils.deleteQuietly(reportTmpFile);
+            }
+            
+            IOUtils.closeQuietly(sourceIs);
+            IOUtils.closeQuietly(reportTmpOs);
+            
+            if (source != null)
+            {
+                _sourceResolver.release(source);
+            }
+        }
+    }
+    
+    /**
+     * Sends an email to the user who clicked the PDF export button.
+     * @param error true if the catalog wasn't generated successfully, false otherwise.
+     */
+    private void _sendMail(boolean error)
+    {
+        I18nizableText i18nSubject = new I18nizableText("plugin.odf", "PLUGINS_ODF_PDF_EXPORT_MAIL_SUBJECT");
+        String subject = _i18nUtils.translate(i18nSubject);
+        
+        I18nizableText i18nBody = error ? new I18nizableText("plugin.odf", "PLUGINS_ODF_PDF_EXPORT_MAIL_BODY_FAILURE") 
+                                        : new I18nizableText("plugin.odf", "PLUGINS_ODF_PDF_EXPORT_MAIL_BODY_SUCCESS");
+        String body = _i18nUtils.translate(i18nBody);
+        
+        String downloadLink = Config.getInstance().getValueAsString("cms.url") + "/" + "plugins/odf/download/catalog.pdf";
+        
+        String mailBody = error ? body : body + downloadLink;
+        try
+        {
+            SendMailHelper.sendMail(subject, null, mailBody, _mailTo, _mailFrom);
+        }
+        catch (MessagingException e)
+        {
+            if (_LOGGER.isWarnEnabled())
+            {
+                _LOGGER.warn("Could not send an alert e-mail to " + _mailTo, e); 
+            }
+        }
+    }
+}
Index: main/plugin-odf/src/org/ametys/odf/catalog/generators/CatalogProgramsGenerator.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/generators/CatalogProgramsGenerator.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/generators/CatalogProgramsGenerator.java	(revision 0)
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.catalog.generators;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+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.impl.SitemapSource;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.repository.ContentTypeExpression;
+import org.ametys.odf.program.Program;
+import org.ametys.odf.program.ProgramFactory;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.plugins.repository.query.QueryHelper;
+import org.ametys.plugins.repository.query.expression.Expression;
+import org.ametys.plugins.repository.query.expression.Expression.Operator;
+import org.ametys.runtime.util.IgnoreRootHandler;
+
+/**
+ * Generator producing the SAX events for the catalogue summary 
+ */ 
+public class CatalogProgramsGenerator extends ServiceableGenerator
+{
+    /** The Ametys object resolver */
+    private AmetysObjectResolver _ametysObjectResolver;
+    
+    @Override
+    public void service(ServiceManager sManager) throws ServiceException
+    {
+        super.service(sManager);
+        _ametysObjectResolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "fo");
+        _saxPrograms();
+        XMLUtils.endElement(contentHandler, "fo");
+        contentHandler.endDocument();
+    }
+    
+    private void _saxPrograms() throws MalformedURLException, IOException, SAXException
+    {
+        AmetysObjectIterable<Program> programs = _getPrograms();
+        
+        while (programs.hasNext())
+        {
+            SitemapSource src = null;      
+            Program program = programs.next();
+            
+            try
+            {
+                String uri = "cocoon://_plugins/odf/_content/" + program.getName() + ".fo";
+                
+                src = (SitemapSource) resolver.resolveURI(uri);
+                
+                src.toSAX(new IgnoreRootHandler(contentHandler));
+            }
+            catch (UnknownAmetysObjectException e)
+            {
+                // The content may be archived
+            }
+            finally
+            {
+                resolver.release(src);
+            }
+        }
+    }
+
+    private AmetysObjectIterable<Program> _getPrograms()
+    {
+        Expression programExpression = new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE);
+        String programQuery = QueryHelper.getXPathQuery(null, ProgramFactory.PROGRAM_NODETYPE, programExpression);
+        AmetysObjectIterable<Program> programs = _ametysObjectResolver.query(programQuery);
+        
+        return programs;
+    }
+}
Index: main/plugin-odf/src/org/ametys/odf/catalog/generators/CatalogSummaryGenerator.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/generators/CatalogSummaryGenerator.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/generators/CatalogSummaryGenerator.java	(revision 0)
@@ -0,0 +1,81 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.ametys.odf.catalog.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.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.repository.ContentTypeExpression;
+import org.ametys.odf.program.Program;
+import org.ametys.odf.program.ProgramFactory;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.query.QueryHelper;
+import org.ametys.plugins.repository.query.expression.Expression;
+import org.ametys.plugins.repository.query.expression.Expression.Operator;
+
+/**
+ * Generator producing the SAX events for the catalogue summary 
+ */ 
+public class CatalogSummaryGenerator extends ServiceableGenerator
+{
+    /** The Ametys object resolver */
+    AmetysObjectResolver _resolver;
+    
+    @Override
+    public void service(ServiceManager sManager) throws ServiceException
+    {
+        super.service(sManager);
+        _resolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        AmetysObjectIterable<Program> programs = _getPrograms();
+        
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "programs");
+        while (programs.hasNext())
+        {
+            Program program = programs.next();
+            AttributesImpl attrs = new AttributesImpl();
+            attrs.addCDATAAttribute("title", program.getTitle());
+            
+            XMLUtils.startElement(contentHandler, "program", attrs);
+            
+            XMLUtils.endElement(contentHandler, "program");
+        }
+        XMLUtils.endElement(contentHandler, "programs");
+        contentHandler.endDocument();
+    }
+
+    private AmetysObjectIterable<Program> _getPrograms() 
+    {
+        Expression programExpression = new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE);
+        String programQuery = QueryHelper.getXPathQuery(null, ProgramFactory.PROGRAM_NODETYPE, programExpression);
+        AmetysObjectIterable<Program> programs = _resolver.query(programQuery);
+        
+        return programs;
+    }
+}
Index: main/plugin-odf/src/org/ametys/odf/catalog/source/DefaultODFView.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/source/DefaultODFView.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/source/DefaultODFView.java	(revision 0)
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.ametys.odf.catalog.source;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceResolver;
+
+/**
+ * Default implementation of {@link ODFViewSelector}.
+ * First looks the wanted file in context://WEB-INF/stylesheets/path_to_file
+ * If it's not present, it will look in the catalogue plugin.
+ */
+public class DefaultODFView extends AbstractLogEnabled implements ODFViewSelector, Serviceable
+{
+    /** The source resolver */
+    protected SourceResolver _resolver;
+    
+    @Override
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
+    }
+    
+    @Override
+    public Source getSource(String filePath) throws IOException
+    {
+        for (String location : getLocations(filePath))
+        {
+            try
+            {
+                Source source = _resolver.resolveURI(location);
+                if (!source.exists())
+                {
+                    if (getLogger().isDebugEnabled())
+                    {
+                        getLogger().debug("Failed to find a stylesheet at '" + location + "'.");
+                    }
+                }
+                else
+                {
+                    if (getLogger().isDebugEnabled())
+                    {
+                        getLogger().debug("Using source located at '" + location + "'.");
+                    }
+                    return source;
+                }
+            }
+            catch (IOException e)
+            {
+                if (getLogger().isDebugEnabled())
+                {
+                    getLogger().debug("Resolving protocol failed for resolving '" + location + "'.");
+                }
+            }
+        }
+        
+        // Should never occur because of the default stylesheet.
+        throw new IOException("Can't find a stylesheet for: " + filePath);
+    }
+    
+    /**
+     * Returns the ordered list of URI to be tested to find the corresponding view.
+     * @param filePath the requested location.
+     * @return a list of possible URIs for the resource.
+     */
+    protected List<String> getLocations(String filePath)
+    {
+        ArrayList<String> locations = new ArrayList<String>();
+        
+        // First search in the WEB-INF folder.
+        locations.add("context://WEB-INF/" + filePath);
+        
+        // Then search in the catalog plugin.
+        locations.add("plugin:odf://" + filePath);
+        
+        return locations;
+    }
+}
Index: main/plugin-odf/src/org/ametys/odf/catalog/source/ODFViewSelector.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/source/ODFViewSelector.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/source/ODFViewSelector.java	(revision 0)
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.catalog.source;
+
+import java.io.IOException;
+
+import org.apache.excalibur.source.Source;
+
+/**
+ * Component responsible to lookup a {@link Source} for a catalog view
+ */
+public interface ODFViewSelector
+{
+    /** Avalon Role */
+    public static final String ROLE = ODFViewSelector.class.getName();
+    
+    /**
+     * Get the file source for rendering a content
+     * @param filePath the path of the file to retrieve
+     * @return the file source
+     * @throws IOException if the Source cannot be resolved
+     */
+    public Source getSource(String filePath) throws IOException;
+}
+
Index: main/plugin-odf/src/org/ametys/odf/catalog/source/ODFViewSourceFactory.java
===================================================================
--- main/plugin-odf/src/org/ametys/odf/catalog/source/ODFViewSourceFactory.java	(revision 0)
+++ main/plugin-odf/src/org/ametys/odf/catalog/source/ODFViewSourceFactory.java	(revision 0)
@@ -0,0 +1,62 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.odf.catalog.source;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceFactory;
+
+/**
+ * This factory looks for files in the WEB-INF/stylesheets directory and falls back to the default stylesheet .
+ * Use: odfcatalogue-view:[view]://path_to_file
+ */
+public class ODFViewSourceFactory extends AbstractLogEnabled implements SourceFactory, Serviceable
+{
+    /** The ODF view selector */
+    private ODFViewSelector _odfViewSelector;
+    
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _odfViewSelector = (ODFViewSelector) manager.lookup(ODFViewSelector.ROLE);
+    }
+
+    @Override
+    public Source getSource(String location, Map parameters) throws IOException, MalformedURLException
+    {
+        int separatorPos = location.indexOf("://");
+        if (separatorPos < 0)
+        {
+            throw new MalformedURLException("Invalid URI: " + location + ", it must look like protocol://path_to_file");
+        }
+        
+        String fileLocation = location.substring(separatorPos + 3);
+        
+        return _odfViewSelector.getSource(fileLocation);
+    }
+
+    @Override
+    public void release(Source source)
+    {
+        // empty method
+    }
+}
Index: main/plugin-odf/stylesheets/catalog/programs2pdf.xsl
===================================================================
--- main/plugin-odf/stylesheets/catalog/programs2pdf.xsl	(revision 0)
+++ main/plugin-odf/stylesheets/catalog/programs2pdf.xsl	(revision 0)
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2014 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:docbook="http://docbook.org/ns/docbook"
+                xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:cms="http://ametys.org/cms/3.0"
+                xmlns:date="http://exslt.org/dates-and-times"
+                xmlns:decoder="org.ametys.cms.transformation.xslt.URIDecoder" 
+                exclude-result-prefixes="docbook decoder">
+    
+    <xsl:template match="/">
+        <fo:root>
+            <fo:layout-master-set>
+                <fo:simple-page-master master-name="page"
+                    page-width="210mm" page-height="297mm" margin-left="0cm"
+                    margin-right="0cm" margin-top="2cm" margin-bottom="2cm">
+                    <fo:region-body margin-top="0cm" margin-bottom="1cm">
+                        <xsl:call-template name="region-body-definition"/>
+                    </fo:region-body>
+                    <fo:region-before extent="1cm">
+                        <xsl:call-template name="region-before-definition"/>
+                    </fo:region-before>
+                    <fo:region-after extent="0.5cm">
+                        <xsl:call-template name="region-after-definition"/>
+                    </fo:region-after>
+                </fo:simple-page-master>
+            </fo:layout-master-set>
+            <fo:page-sequence master-reference="page">
+                <xsl:call-template name="xsl-region-before"/>
+                <xsl:call-template name="xsl-region-after"/>
+                <fo:flow flow-name="xsl-region-body">
+                    <xsl:call-template name="front-page"/>
+                    <xsl:call-template name="summary"/>
+                    
+                    <xsl:apply-templates select="/view/fo/fo:root/fo:page-sequence/fo:flow"/>
+                    
+                    <fo:block id="theEnd"/>
+                </fo:flow>
+            </fo:page-sequence>
+        </fo:root>
+    </xsl:template>
+       
+    <!-- The front page templates can be overriden in the skin or in the WEB-INF/stylesheets directory -->   
+    <xsl:template name="front-page">
+            <xsl:call-template name="front-page-header"/>
+            
+            <xsl:call-template name="front-page-image"/>
+        
+            <xsl:call-template name="front-page-footer"/>
+                
+            <fo:block break-after="page" />
+    </xsl:template>
+    
+    <xsl:template name="front-page-header">
+        <fo:block text-align="center" font-size="25px">
+            <fo:block margin-top="0.3cm">
+                <i18n:text i18n:key="PLUGINS_ODF_FORMATIONS_CATALOGUE" i18n:catalogue="plugin.odf"/>
+            </fo:block>
+        </fo:block>
+    </xsl:template>
+    
+    <xsl:template name="front-page-image"/>
+    
+    <xsl:template name="front-page-footer"/>
+       
+    <xsl:template match="fo:flow">
+        <xsl:if test="preceding::fo:flow">
+            <fo:block break-after="page" />
+        </xsl:if>
+    
+        <xsl:variable name="position" select="count(preceding::fo:flow) + 1"/>
+        <fo:block id="{generate-id(/view/programs/program[$position])}"></fo:block>
+        
+        <xsl:copy-of select="fo:block[not(@id = 'theEnd')]"/>
+    </xsl:template>
+
+    <xsl:template name="summary">
+        <fo:block text-align="center" font-size="20px">
+            <i18n:text i18n:key="PLUGINS_ODF_TABLE_OF_CONTENTS" i18n:catalogue="plugin.odf"/>
+        </fo:block>
+      
+        <fo:block margin-right="1.2cm" margin-left="1.2cm" text-align-last="justify">
+            <xsl:for-each select="/view/programs/program">
+                 <fo:block margin-top="0.5cm" font-size="14px">
+                     <xsl:copy-of select="position()"/>. <xsl:value-of select="@title"/> <fo:leader leader-pattern = "dots"/> <fo:page-number-citation ref-id="{generate-id()}" />
+                 </fo:block>
+            </xsl:for-each>
+        </fo:block> 
+      
+        <fo:block break-after="page" />
+    </xsl:template>
+    
+    <xsl:template name="xsl-region-after">
+        <fo:static-content flow-name="xsl-region-after">
+            <fo:block margin-left="1cm" margin-right="1cm">
+                <xsl:call-template name="main-region-after-block"/>
+                
+                <fo:table table-layout="fixed" margin-top="0pt" padding-top="0pt">
+                    <fo:table-column column-width="4.5cm"/>
+                    <fo:table-column column-width="10cm"/>
+                    <fo:table-column column-width="4.5cm"/>
+                    <fo:table-body>
+                        <fo:table-row>
+                            <fo:table-cell>
+                                <fo:block text-align="left">
+                                    <xsl:variable name="now" select="date:date-time()"/>
+                                    <xsl:number format="01" value="date:day-in-month($now)"/>
+                                    <xsl:text>/</xsl:text>
+                                    <xsl:number format="01" value="date:month-in-year($now)"/>
+                                    <xsl:text>/</xsl:text>
+                                    <xsl:value-of select="date:year($now)"/>
+                                </fo:block>
+                            </fo:table-cell>
+                            <fo:table-cell>
+                                <fo:block text-align="center" font-size="5px">
+                                    <i18n:text i18n:key="PLUGINS_ODF_FORMATIONS_CATALOGUE_FOOTER_TEXT" i18n:catalogue="plugin.odf"/>
+                                </fo:block>
+                            </fo:table-cell>
+                            <fo:table-cell>
+                                <fo:block text-align="right">Page <fo:page-number /> / <fo:page-number-citation ref-id="theEnd" /></fo:block>
+                            </fo:table-cell>
+                        </fo:table-row>
+                    </fo:table-body>
+                </fo:table>
+            </fo:block>
+        </fo:static-content> 
+    </xsl:template>
+    
+    <xsl:template name="xsl-region-before">
+        <fo:static-content flow-name="xsl-region-before">
+              <fo:block-container absolute-position="fixed" left="0in" top="0in" width="21cm" height="29.7cm">
+                  <fo:block />
+              </fo:block-container>
+        </fo:static-content>
+    </xsl:template>
+    
+    <xsl:template name="main-region-after-block">
+        <xsl:attribute name="font-size">9px</xsl:attribute>
+    </xsl:template>
+
+    <xsl:template name="region-body-definition"/>
+    
+    <xsl:template name="region-before-definition"/>
+    
+    <xsl:template name="region-after-definition"/>
+
+</xsl:stylesheet>
\ No newline at end of file
Index: main/plugin-odf/stylesheets/content/program/program2pdf-main.xsl
===================================================================
--- main/plugin-odf/stylesheets/content/program/program2pdf-main.xsl	(revision 27816)
+++ main/plugin-odf/stylesheets/content/program/program2pdf-main.xsl	(working copy)
@@ -15,16 +15,16 @@
    limitations under the License.
    -->
 <xsl:stylesheet version="1.0"
-	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-    xmlns:docbook="http://docbook.org/ns/docbook"
-	xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
-	xmlns:fo="http://www.w3.org/1999/XSL/Format"
-    xmlns:cms="http://ametys.org/cms/3.0"
-	xmlns:date="http://exslt.org/dates-and-times"
-    xmlns:resolver="org.ametys.cms.transformation.xslt.ResolveURIComponent"
-	xmlns:stringutils="org.apache.commons.lang.StringUtils"
-    exclude-result-prefixes="docbook"
-    extension-element-prefixes="cms date resolver stringutils">
+				xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+			    xmlns:docbook="http://docbook.org/ns/docbook"
+				xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+				xmlns:fo="http://www.w3.org/1999/XSL/Format"
+			    xmlns:cms="http://ametys.org/cms/3.0"
+				xmlns:date="http://exslt.org/dates-and-times"
+			    xmlns:resolver="org.ametys.cms.transformation.xslt.ResolveURIComponent"
+				xmlns:stringutils="org.apache.commons.lang.StringUtils"
+			    exclude-result-prefixes="docbook"
+			    extension-element-prefixes="cms date resolver stringutils">
 
     <xsl:import href="plugin:cms://stylesheets/helper/common.xsl"/>
     <xsl:import href="view://stylesheets/io/docbook2fo.xsl" />
@@ -107,7 +107,7 @@
                         </fo:table-cell>
                         <fo:table-cell>
                             <fo:block text-align="center" font-size="5px">
-                                <xsl:text>Toutes les informations données sur cette page sont indicatives et n'ont pas de valeur contractuelle</xsl:text>
+                                <i18n:text i18n:key="PLUGINS_ODF_FORMATIONS_CATALOGUE_FOOTER_TEXT" i18n:catalogue="plugin.odf"/>
                             </fo:block>
                         </fo:table-cell>
                         <fo:table-cell>
@@ -657,7 +657,7 @@
 	     + -->
 	<xsl:template name="more-info">
         <xsl:param name="truelevel"/>
-    
+        
     	<xsl:if test="normalize-space(metadata/programWebSiteUrl) != ''">
 			<fo:block xsl:use-attribute-sets="right-block">
 				<fo:block xsl:use-attribute-sets="right-block-header">
#P Ametys - Plugin ODF Web
Index: main/plugin-odf-web/plugin.xml
===================================================================
--- main/plugin-odf-web/plugin.xml	(revision 27816)
+++ main/plugin-odf-web/plugin.xml	(working copy)
@@ -34,6 +34,38 @@
 		</components>
 	</feature>	
 	
+    <feature name="web-view-selector">
+        <extensions>
+            <extension point="org.ametys.odf.catalog.source.ODFViewSelector" 
+                       id="org.ametys.plugins.odfweb.catalog.ODFWebView"
+                       class="org.ametys.plugins.odfweb.catalog.ODFWebView" />
+        </extensions>
+    </feature>
+    
+    <feature name="pdfexport">
+        <extensions>
+            <extension id="org.ametys.odf.web.PDFExport"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.ActionButton">
+                    <param name="action">org.ametys.odf.web.PDFExport.pdfExport</param>
+                    
+                    <param name="label" i18n="true">plugin.odf:PLUGINS_ODF_PDFEXPORT_LABEL</param>
+                    <param name="default-description" i18n="true">plugin.odf:PLUGINS_ODF_PDFEXPORT_DESCRIPTION</param>
+                    <param name="footer" i18n="true">plugin.odf:PLUGINS_ODF_PDFEXPORT_FOOTER</param>
+                    
+                    <param name="icon-small" file="true" plugin="odf">img/pdfexport/pdfexport-16.png</param>
+                    <param name="icon-medium" file="true" plugin="odf">img/pdfexport/pdfexport-32.png</param>
+                    <param name="icon-large" file="true" plugin="odf">img/pdfexport/pdfexport-48.png</param>
+                </action> 
+                <scripts>
+                    <file>js/org/ametys/odf/web/PDFExport.i18n.js</file>
+                </scripts>
+                <right>ODF_Rights_Generate_PDF</right>
+            </extension>
+        </extensions>
+    </feature>
+    
     <feature name="userinterface.odf.root">
    		<extensions>
    			<extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
@@ -43,7 +75,7 @@
         			<description>PLUGINS_ODFWEB_RIGHTS_SET_ROOT_PAGE_DESC</description>
         			<category>plugin.odf:PLUGINS_ODF_RIGHTS_ODF_CATEGORY</category>
                 </right>
-            </extension>
+             </extension>
             
 			<extension id="org.ametys.odf.web.page.Root"
 					   point="org.ametys.cms.workspace.ribbon.RibbonControlsManager" 
@@ -90,7 +122,7 @@
 					<file>js/org/ametys/odf/web/SetODFRootAction.i18n.js</file>
 				</scripts>
 				<right>ODF_Rights_SetRootPage</right>
-			</extension>   	
+			</extension>
    		</extensions>
     </feature>
     
Index: main/plugin-odf-web/resources/js/org/ametys/odf/web/PDFExport.i18n.js
===================================================================
--- main/plugin-odf-web/resources/js/org/ametys/odf/web/PDFExport.i18n.js	(revision 0)
+++ main/plugin-odf-web/resources/js/org/ametys/odf/web/PDFExport.i18n.js	(revision 0)
@@ -0,0 +1,61 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.odf.web');
+Ext.namespace('org.ametys.odf.web.PDFExport');
+
+org.ametys.odf.web.PDFExport = {};
+
+//------------------------------------------------------------------
+org.ametys.odf.web.PDFExport.pdfExport = function(controlId, controlConfiguration, controlPluginName)
+{
+    var serverMessage = new org.ametys.servercomm.ServerMessage(controlPluginName, context.parameters.siteName + '/generate-pdf', {}, 
+    															org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.odf.web.PDFExport._pdfExportCb, 
+    															this, null);
+    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+    
+org.ametys.odf.web.PDFExport._pdfExportCb = function(response)
+{
+    if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key='PLUGINS_ODF_PDFEXPORT_ERROR'/>", response, 'org.ametys.odf.pdfexport.PDFExport'))
+    {
+        throw "org.ametys.odf.pdfexport.PDFExport request failed";
+    }
+    
+    var errorNode = response.selectSingleNode('ActionResult/error');
+    if (errorNode != null &amp;&amp; errorNode[org.ametys.servercomm.ServerComm.xmlTextContent] != '')
+    {
+        var error = errorNode[org.ametys.servercomm.ServerComm.xmlTextContent];
+        if (error == 'already-running')
+        {
+            Ext.Msg.show({
+                title: "<i18n:text i18n:key='PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TITLE'/>",
+                msg: "<i18n:text i18n:key='PLUGINS_ODF_PDFEXPORT_ALREADY_RUNNING_MESSAGE_TEXT'/>",
+                buttons: Ext.Msg.OK,
+                icon: Ext.Msg.ERROR
+            });
+        }
+    }
+    else
+	{
+	    Ext.Msg.show({
+	        title: "<i18n:text i18n:key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TITLE" i18n:catalogue="plugin.odf"/>",
+	        msg: "<i18n:text i18n:key="PLUGINS_ODF_PDFEXPORT_LAUNCH_GENERATION_MESSAGE_TEXT" i18n:catalogue="plugin.odf"/>",
+	        buttons: Ext.Msg.OK,
+	        icon: Ext.Msg.INFO
+	    });
+	}
+}
Index: main/plugin-odf-web/sitemap.xmap
===================================================================
--- main/plugin-odf-web/sitemap.xmap	(revision 27816)
+++ main/plugin-odf-web/sitemap.xmap	(working copy)
@@ -31,6 +31,9 @@
 			<map:generator name="get-odf-root" src="org.ametys.plugins.odfweb.generators.GetODFRootPagesGenerator"/>   
 			<map:generator name="catalogs" src="org.ametys.plugins.odfweb.generators.CatalogsGenerator"/>
 			<map:generator name="eligible-metadata" src="org.ametys.plugins.odfweb.generators.EligibleMetadataForLevelGenerator"/>
+			
+            <map:generator name="catalog-summary" src="org.ametys.odf.catalog.generators.CatalogSummaryGenerator" />
+            <map:generator name="catalog-programs" src="org.ametys.odf.catalog.generators.CatalogProgramsGenerator" />
 		</map:generators>	
 		
 		<map:actions>
@@ -41,6 +44,9 @@
             
 			<map:action name="get-content" src="org.ametys.cms.content.GetContentAction"/>
 			<map:action name="get-site" src="org.ametys.web.content.GetSiteAction"/>
+			
+			<map:action name="set-workspace-live" src="org.ametys.plugins.odfweb.actions.SetWorkspaceLiveAction"/>
+            <map:action name="generate-pdf" src="org.ametys.plugins.odfweb.actions.GeneratePDFAction"/>
 		</map:actions>
         
 	</map:components>
@@ -166,6 +172,38 @@
                 </map:act>
             </map:match>
             
+            
+                <map:match pattern="*/_content/*.*">
+                <map:act type="get-content">
+                    <map:parameter name="path" value="/ametys:odf/ametys:contents/{2}"/>
+                    
+                    <map:act type="get-site" src="{../1}">
+                        <map:redirect-to uri="cocoon://_plugins/{../plugin}/{../contentType}.{../../3}"/>
+                    </map:act>
+                </map:act>
+            </map:match>
+            
+            <!-- +
+                 | PDF CATALOG
+                 +  -->            
+            <map:match pattern="*/generate-pdf">
+                <map:act type="generate-pdf" src="{1}">
+                    <map:generate type="action-result">
+                         <map:parameter name="error" value="{error}"/>
+                     </map:generate>
+                     <map:serialize type="xml"/>
+                </map:act>
+            </map:match>
+            
+            
+            <map:match pattern="*/catalog.pdf" >
+                <map:act type="get-site" src="{1}">
+                    <map:act type="set-workspace-live"> 
+                        <map:redirect-to uri="cocoon://_plugins/odf/catalog.pdf"/>
+                    </map:act>
+                </map:act>
+             </map:match> 
+            
             <map:match pattern="**">
                 <map:act type="check-not-front"/>
             </map:match>
@@ -212,7 +250,5 @@
                 <map:serialize type="xml"/>
             </map:match>
 		</map:pipeline>
-		
-	</map:pipelines>
-
+    </map:pipelines>
 </map:sitemap>
Index: main/plugin-odf-web/src/org/ametys/plugins/odfweb/actions/GeneratePDFAction.java
===================================================================
--- main/plugin-odf-web/src/org/ametys/plugins/odfweb/actions/GeneratePDFAction.java	(revision 0)
+++ main/plugin-odf-web/src/org/ametys/plugins/odfweb/actions/GeneratePDFAction.java	(revision 0)
@@ -0,0 +1,29 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.odfweb.actions;
+
+
+/**
+ * Action starting generation of the program's catalogue in pdf format
+ */
+public class GeneratePDFAction extends org.ametys.odf.catalog.actions.GeneratePDFAction 
+{
+    @Override
+    protected String _generateUrl(String source)
+    {
+        return "cocoon://_plugins/odf-web/" + source + "/catalog.pdf";
+    }
+}
Index: main/plugin-odf-web/src/org/ametys/plugins/odfweb/actions/SetWorkspaceLiveAction.java
===================================================================
--- main/plugin-odf-web/src/org/ametys/plugins/odfweb/actions/SetWorkspaceLiveAction.java	(revision 0)
+++ main/plugin-odf-web/src/org/ametys/plugins/odfweb/actions/SetWorkspaceLiveAction.java	(revision 0)
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.odfweb.actions;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.cocoon.acting.AbstractAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+
+import org.ametys.cms.repository.RequestAttributeWorkspaceSelector;
+import org.ametys.web.WebConstants;
+
+/**
+ * Action that sets the live workspace
+ */
+public class SetWorkspaceLiveAction extends AbstractAction
+{
+    @Override
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Request request = null;
+        String workspaceBackup = null;
+        try
+        {
+            request = ObjectModelHelper.getRequest(objectModel);
+            workspaceBackup = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
+            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE);
+        }
+        catch (Exception e)
+        {
+            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceBackup);
+        }
+        
+        return Collections.EMPTY_MAP;
+    }
+}
Index: main/plugin-odf-web/src/org/ametys/plugins/odfweb/catalog/ODFWebView.java
===================================================================
--- main/plugin-odf-web/src/org/ametys/plugins/odfweb/catalog/ODFWebView.java	(revision 0)
+++ main/plugin-odf-web/src/org/ametys/plugins/odfweb/catalog/ODFWebView.java	(revision 0)
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2014 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.odfweb.catalog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.ametys.odf.catalog.source.DefaultODFView;
+
+/**
+ * This implementation of a {@link ODFWebView} looks first in the directory skin://stylesheets/catalog/ then defaults to {@link DefaultODFView}.
+ */
+public class ODFWebView extends org.ametys.odf.catalog.source.DefaultODFView
+{
+    @Override
+    protected List<String> getLocations(String filePath)
+    {
+        ArrayList<String> result = new ArrayList<String>();
+        
+        result.add("skin://" + filePath);
+        result.addAll(super.getLocations(filePath));
+        
+        return result;
+    }
+}
