Index: main/plugin-syndication/sitemap.xmap
===================================================================
--- main/plugin-syndication/sitemap.xmap	(revision 28681)
+++ main/plugin-syndication/sitemap.xmap	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2015 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -16,24 +16,56 @@
    -->
 <map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
     <map:components>
+        <map:selectors>
+            <map:selector name="request-parameter" src="org.apache.cocoon.selection.RequestParameterSelector" logger="sitemap.selector.requestparameter" />
+        </map:selectors>
+    
         <map:generators>
             <map:generator name="feed" src="org.ametys.plugins.syndication.FeedGenerator"/>
+            <map:generator name="feeds" src="org.ametys.plugins.syndication.FeedsGenerator"/>
         </map:generators>
+        
+        <map:actions>
+            <map:action name="create-conf" src="org.ametys.plugins.syndication.CreateUserConfAction" />
+        </map:actions>
     </map:components>
 	<map:pipelines>
 		<map:pipeline>
-			<map:match pattern="feed/read.xhtml">
-				<map:generate type="feed" src="{parent-context-attr:url}">
-					<map:parameter name="length" value="{parent-context-attr:length}"/>
-				</map:generate>
-				<map:transform type="i18n">
-					<map:parameter name="locale" value="{locale:locale}"/>
-				</map:transform>
-				<map:transform src="service://@xslt"/>
-				<map:transform type="i18n">
-					<map:parameter name="locale" value="{request-attr:sitemapLanguage}"/>
-					 <map:parameter name="plugin" value="{request-attr:pluginName}"/>
-				</map:transform>
+		   <map:match pattern="feed/read.xhtml">
+                <map:generate type="feeds" />
+                <map:transform type="i18n">
+                    <map:parameter name="locale" value="{locale:locale}"/>
+                </map:transform>
+                <map:transform src="service://@xslt"/>
+                <map:transform type="i18n">
+                    <map:parameter name="locale" value="{request-attr:sitemapLanguage}"/>
+                    <map:parameter name="plugin" value="{request-attr:pluginName}"/>
+                </map:transform>
+                <map:serialize type="xml"/>
+            </map:match>
+		    
+		    <map:match pattern="create-conf">
+                <map:act type="create-conf">
+                    <map:generate type="action-result" label="xml"/>
+                    <map:transform type="i18n">
+                        <map:parameter name="locale" value="{locale:locale}"/>
+                        <map:parameter name="default-catalogue-id" value="plugin.{request-attr:pluginName}"/>
+                    </map:transform>
+                    <map:select type="request-parameter">
+                        <map:parameter name="parameter-name" value="ajax"/>
+                        <map:when test="true">
+                            <map:serialize type="xml"/>
+                        </map:when>
+                        <map:otherwise>
+                            <map:transform src="service://pages/services/feed/xsl/result.xsl"/>
+                            <map:serialize type="xml"/>
+                        </map:otherwise>
+                    </map:select>
+                </map:act>
+            </map:match>
+		
+			<map:match pattern="feed">
+				<map:generate type="feed" />
 				<map:serialize type="xml"/>
 			</map:match>
 			
Index: main/plugin-syndication/pages/services/feed/feed2xhtml.xml
===================================================================
--- main/plugin-syndication/pages/services/feed/feed2xhtml.xml	(revision 28681)
+++ main/plugin-syndication/pages/services/feed/feed2xhtml.xml	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2015 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
    See the License for the specific language governing permissions and
    limitations under the License.
    -->
-<service exclude="true">
+<service>
 	<label i18n="true">SYNDICATION_SERVICE_READ_XSLT_FEED2XHTML_LABEL</label>
 </service>
\ No newline at end of file
Index: main/plugin-syndication/pages/services/feed/feedmulticol2xhtml_1.2.xml
===================================================================
--- main/plugin-syndication/pages/services/feed/feedmulticol2xhtml_1.2.xml	(revision 0)
+++ main/plugin-syndication/pages/services/feed/feedmulticol2xhtml_1.2.xml	(revision 0)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2015 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<service>
+    <label i18n="true">SYNDICATION_SERVICE_READ_XSLT_FEEDMULTICOL2XHTML_LABEL</label>
+</service>
\ No newline at end of file
Index: main/plugin-syndication/pages/services/feed/feed2xhtml_1.2.xml
===================================================================
--- main/plugin-syndication/pages/services/feed/feed2xhtml_1.2.xml	(revision 28681)
+++ main/plugin-syndication/pages/services/feed/feed2xhtml_1.2.xml	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2015 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
Index: main/plugin-syndication/pages/services/feed/feed2xhtml.xsl
===================================================================
--- main/plugin-syndication/pages/services/feed/feed2xhtml.xsl	(revision 28681)
+++ main/plugin-syndication/pages/services/feed/feed2xhtml.xsl	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2015 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -21,38 +21,43 @@
 
     <xsl:param name="header" select="ametys:serviceParameter('header')"/>
 
-	<xsl:template match="/feed">
+	<xsl:template match="/rssFeeds">
 		<html>
 			<head>
 				<!-- CSS -->
 				<link href="{ametys:pluginResourceURL('syndication', 'css/service-feed.css')}" rel="stylesheet" type="text/css" />
-			</head>
+			    <xsl:if test="ametys:renderingContext() = 'back'">
+    			    <link href="{ametys:pluginResourceURL('syndication', 'css/bo-error.css')}" rel="stylesheet" type="text/css" />
+			    </xsl:if>
+            </head>
 			
 			<body>
 				<xsl:if test="$header != ''"><h3><xsl:value-of select="$header"/></h3></xsl:if>
 			
 				<div class="feed-wrapper">
-					
-					<xsl:apply-templates select="image"/>
-					
-					<h1><xsl:value-of select="@title"/></h1>
-					<h2><xsl:value-of select="@description"/></h2>
-					
-					<!-- 
-					<xsl:if test="link">
-						<a class="rss-link" href="{link}" title="{title}"><xsl:value-of select="link"></xsl:value-of></a>
-					</xsl:if>
-					 -->
-					 
-					 <!-- 
-					<xsl:if test="lastBuildDate">
-						<div class="rss-date"><xsl:value-of select="lastBuildDate"/></div>
-					</xsl:if>
-					-->
-					 
-					<div class="feed-entry-list">
-						<xsl:apply-templates select="entry"/>
-					</div>
+					<xsl:for-each select="feed">
+						
+						<xsl:apply-templates select="image"/>
+						
+						<h1><xsl:value-of select="@title"/></h1>
+						<h2><xsl:value-of select="@description"/></h2>
+						
+						<!-- 
+						<xsl:if test="link">
+							<a class="rss-link" href="{link}" title="{title}"><xsl:value-of select="link"></xsl:value-of></a>
+						</xsl:if>
+						 -->
+						 
+						 <!-- 
+						<xsl:if test="lastBuildDate">
+							<div class="rss-date"><xsl:value-of select="lastBuildDate"/></div>
+						</xsl:if>
+						-->
+						 
+						<div class="feed-entry-list">
+							<xsl:apply-templates select="entry"/>
+						</div>
+					</xsl:for-each>
 					
 				</div>
 			</body>
Index: main/plugin-syndication/pages/services/feed/utils/preference.xsl
===================================================================
--- main/plugin-syndication/pages/services/feed/utils/preference.xsl	(revision 0)
+++ main/plugin-syndication/pages/services/feed/utils/preference.xsl	(revision 0)
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2015 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<xsl:stylesheet version="1.0" 
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+                xmlns:ametys="org.ametys.web.transformation.xslt.AmetysXSLTHelper"
+                xmlns:math="http://exslt.org/math" 
+                xmlns:resolver="org.ametys.cms.transformation.xslt.ResolveURIComponent"
+                xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+                extension-element-prefixes="math ametys resolver">
+         
+    <xsl:template name="feed-get-url">
+        <xsl:value-of select="concat(ametys:siteUriPrefix(), '/', ametys:lang(), '/_plugins/syndication/page/create-conf')"/>
+    </xsl:template>            
+
+    <xsl:template name="configure-links-btn-text">
+        <i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG" i18n:catalogue="plugin.syndication"/>
+    </xsl:template>
+
+    <xsl:template name="configure-links-btn">
+	    <p id="feed-config-header-{$uniqueId}"><xsl:call-template name="configure-links-btn-text"/></p>
+	    <script type="text/javascript">
+	        $j("#feed-config-header-<xsl:value-of select="$uniqueId"/>").wrapInner("&lt;a class='config' href='javascript: feed_config_form_popup_<xsl:value-of select="$uniqueId"/>()'&gt;&lt;/a&gt;")
+	    </script>
+    </xsl:template>
+                
+    <xsl:template name="feed-config-form">
+        <xsl:if test="/rssFeeds/@showForm = 'true'">
+	        <xsl:variable name="feed-url"><xsl:call-template name="feed-get-url"/></xsl:variable>
+	        
+	        <div class="feed-config-form">
+	            <xsl:call-template name="configure-links-btn" />
+	            
+	            <form id="feed-config-{$uniqueId}" action="{$feed-url}" method="post">
+	                <div id="feed-config-form-{$uniqueId}" class="feed-config-form">
+	
+			            <p>
+			            <i18n:translate>
+			              <i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO" i18n:catalogue="plugin.syndication"/>
+			              <i18n:param><xsl:if test="/rssFeeds/@nbMax != 0"><xsl:value-of select="/rssFeeds/@nbMax"/></xsl:if></i18n:param>
+			            </i18n:translate>
+			            </p>
+			            <noscript>
+			               <p>
+			                   <i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO_FEED_MAX" i18n:catalogue="plugin.syndication"/><xsl:value-of select="/rssFeeds/@nbMax" />
+			               </p>
+			            </noscript>
+	                    <input type="hidden" name="zone-item-id" id="zone-item-id-{$uniqueId}" value="{/rssFeeds/@zoneItemId}"/>
+	                    <input type="hidden" name="nbMaxCheck" id="nb-max-check-{$uniqueId}" value="{/rssFeeds/@nbMax}"/>
+	                    <input type="hidden" name="nbFeedService" id="nb-feed-service-{$uniqueId}" value="{/rssFeeds/@nbFeedService}"/>
+	                    <input type="hidden" name="nbMaxUser" id="nb-max-user-{$uniqueId}" value="{/rssFeeds/@nbMaxUser}"/>
+	                    <xsl:for-each select="/rssFeeds/feed-conf">
+	                            <xsl:if test="@isCustom = 'false'">
+	                                <xsl:call-template name="feed-config-form-feeds">
+	                                    <xsl:with-param name="position" select="position()"/>
+	                                </xsl:call-template>
+	                            </xsl:if>
+	                    </xsl:for-each>
+	                    <br/>
+	                    <xsl:if test="count(/rssFeeds/feed-conf[@isCustom = 'true']) != 0">
+		                    <p><i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO2" i18n:catalogue="plugin.syndication"/></p>
+		                    <xsl:for-each select="/rssFeeds/feed-conf">
+		                            <xsl:if test="@isCustom = 'true'">
+		                                <xsl:call-template name="feed-config-form-feeds-custom">
+		                                    <xsl:with-param name="position" select="position()"/>
+		                                </xsl:call-template>
+		                            </xsl:if>
+		                    </xsl:for-each>
+	                    </xsl:if>
+	                    <xsl:call-template name="feed-config-form-field-submit"/> 
+	                    <div class="feed-config-form-bottom"></div>
+	                </div>
+	            </form>
+	            
+	            <xsl:call-template name="feed-form-script"/>
+	        </div>
+	   </xsl:if>
+    </xsl:template>
+    
+    <xsl:template name="feed-config-form-feeds">
+        <xsl:param name="position" />
+        <xsl:variable name="name">
+	        <xsl:choose>
+	            <xsl:when test="@feedName != ''">
+	                <xsl:value-of select="@feedName"/>
+	            </xsl:when>
+	            <xsl:otherwise>
+	                <xsl:value-of select="@feedUrl"/>
+	            </xsl:otherwise>
+	        </xsl:choose>
+        </xsl:variable>
+        <div class="field checkbox">
+            <div class="input">
+                <input type="checkbox" name="feed-check-{$position}" id="feed-config-{$uniqueId}-check-{$position}">
+                    <xsl:if test="@isCheck = 'true'"><xsl:attribute name="checked">checked</xsl:attribute></xsl:if>
+                </input>
+            </div>
+            <div class="label">
+                <label for="feed-config-{$uniqueId}-check-{$position}"><xsl:value-of select="$name"/></label>
+            </div>
+            <div class="field-end"></div>
+        </div>
+    </xsl:template>
+    
+    <xsl:template name="feed-config-form-feeds-custom">
+        <xsl:param name="position" />
+        <div class="field checkbox">
+            <div class="input">
+                <input type="checkbox" name="feed-check-{$position}" id="feed-config-{$uniqueId}-check-{$position}">
+                    <xsl:if test="@isCheck = 'true'"><xsl:attribute name="checked">checked</xsl:attribute></xsl:if>
+                </input>
+            </div>
+            <div class="label">
+                <input type="input" onkeyup="check_{$uniqueId}('feed-config-{$uniqueId}-check-{$position}')" name="feed-url-{$position}" id="feed-config-{$uniqueId}-url-{$position}" value="{@feedUrl}"/>
+            </div>
+            <div class="field-end"></div>
+        </div>
+    </xsl:template>
+    
+    <xsl:template name="feed-config-form-field-submit">
+         <!-- Template for submit button -->
+         <div id="feed-config-submit-{$uniqueId}">
+             <xsl:call-template name="common-utils-input-submit">
+                 <xsl:with-param name="i18nkey" select="'plugin.syndication:SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_RECORD'" />                
+                 <xsl:with-param name="onclick">return feed_config_form_submit_<xsl:value-of select="$uniqueId"/>();</xsl:with-param>                
+             </xsl:call-template>
+         </div>
+    </xsl:template>
+    
+    <xsl:template name="feed-form-script">
+        <script type="text/javascript">
+            function check_<xsl:value-of select="$uniqueId"/>(id)
+            {
+                var checkBox = $j("#" + id);
+                if (!checkBox[0].checked)
+                {
+                    checkBox[0].click()
+                }
+            }
+        
+            function feed_config_form_switch_<xsl:value-of select="$uniqueId"/>()
+            {
+                var form = document.getElementById('feed-config-<xsl:value-of select="$uniqueId"/>');
+                var submit = document.getElementById('feed-config-submit-<xsl:value-of select="$uniqueId"/>');
+                form.style.display = (form.style.display == 'none') ? '' : 'none';
+                submit.style.display = (submit.style.display == 'none') ? '' : 'none';
+            }
+            feed_config_form_switch_<xsl:value-of select="$uniqueId"/>();
+            
+            function feed_config_form_popup_<xsl:value-of select="$uniqueId"/>()
+            {
+                $j("#feed-config-form-<xsl:value-of select="$uniqueId"/> script").remove();
+                $j("#feed-config-form-<xsl:value-of select="$uniqueId"/>").dialog({
+                  resizable: false,
+                  height:'410',
+                  width: '380',
+                  modal: true,
+                  title: '<i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_TITLE" i18n:catalogue="plugin.syndication"/>',
+                  buttons: {
+                    <i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_RECORD" i18n:catalogue="plugin.syndication"/>: function() {
+                      feed_config_form_submit_<xsl:value-of select="$uniqueId"/>();
+                    },
+                    <i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CANCEL" i18n:catalogue="plugin.syndication"/>: function() {
+                      $j( this ).dialog( "close" );
+                    }
+                  }
+                });
+            }
+            
+            function feed_config_form_submit_<xsl:value-of select="$uniqueId"/>()
+            {
+                var nbMaxCheck = <xsl:value-of select="/rssFeeds/@nbMax"/>;
+                var nbMaxUser = <xsl:value-of select="/rssFeeds/@nbMaxUser"/>;
+                var nbFeedService = <xsl:value-of select="/rssFeeds/@nbFeedService"/>;
+                if (!checkNbFeed_<xsl:value-of select="$uniqueId"/>(nbMaxCheck) &amp;&amp; nbMaxCheck != 0)
+                {
+                    alert("<i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_ERROR_TO_MANY_CHECK" i18n:catalogue="plugin.syndication"/>");
+                    return false;
+                }
+                else
+                {
+                    var i = 1;
+                    var checkBox = $j("#feed-config-<xsl:value-of select="$uniqueId"/>-check-" + i);
+                    var zoneItemInput = $j("#zone-item-id-<xsl:value-of select="$uniqueId"/>");
+                    
+                    var data = {
+                        "ajax": "true",
+                        "zone-item-id": zoneItemInput.val(),
+                        "nbMaxCheck": nbMaxCheck,
+                        "nbFeedService": nbFeedService,
+                        "nbMaxUser": nbMaxUser
+                    };
+                    
+                    while (checkBox[0] != null)
+                    {
+                        data["feed-check-" + i] = checkBox[0].checked;
+                        var inputUrl = $j("#feed-config-<xsl:value-of select="$uniqueId"/>-url-" + i);
+                        if (inputUrl[0] != null)
+                        {
+                            data["feed-url-" + i] = inputUrl[0].value;
+                        }
+                        i++;
+                        checkBox = $j("#feed-config-<xsl:value-of select="$uniqueId"/>-check-" + i);
+                    }
+                  
+                    // send it in ajax
+                    function result(data)
+                    {
+                        var s = "";
+                        $j('errors error', data).each(function () { s += $j(this).text() + "\n"; });
+                        
+                        if (s == "")
+                        {
+                            var errors = $j('error', data);
+                            if (errors.size() == 0)
+                            {
+                                $j("#feed-config-form-<xsl:value-of select="$uniqueId"/>").dialog( "close" );
+                                alert("<i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_OK" i18n:catalogue="plugin.syndication"/>");
+                                location.reload();
+                            }
+                            else
+                            {
+                                var errorInfo = "";
+                                errors.each(function() {
+                                    errorInfo += $j(this).text();
+                                }); 
+                                alert(errorInfo)
+                            }
+                        }
+                        else
+                        {
+                            alert(s);
+                        }
+                    }
+                    
+                    function handleError(jqXHR, textStatus, errorThrown)
+                    {
+                        alert("<i18n:text i18n:key="PLUGINS_CMS_CONTENT_COMMENTS_FORM_ERROR_AJAX" i18n:catalogue="plugin.cms"/>");
+                    }
+                    
+                    $j.ajax({
+                        url: "<xsl:value-of select="$uri-prefix"/>/plugins/syndication/create-conf",
+                        data: data,
+                        type: 'post',
+                        success: result,
+                        error: handleError
+                    });
+                }
+            }
+            
+            function checkNbFeed_<xsl:value-of select="$uniqueId"/>(nbMaxCheck)
+            {
+               var i = 1;
+               var checkBox = $j("#feed-config-<xsl:value-of select="$uniqueId"/>-check-" + i);
+               
+               var nbCheck = 0;
+               while (checkBox[0] != null)
+               {
+                    if (checkBox[0].checked)
+                    {
+                        nbCheck++;
+                    }
+                    i++;
+                    checkBox = $j("#feed-config-<xsl:value-of select="$uniqueId"/>-check-" + i);
+               }
+               
+               return !(nbCheck > nbMaxCheck);
+            }
+        </script>
+    </xsl:template> 
+                
+</xsl:stylesheet>
\ No newline at end of file
Index: main/plugin-syndication/pages/services/feed/utils/result.xsl
===================================================================
--- main/plugin-syndication/pages/services/feed/utils/result.xsl	(revision 0)
+++ main/plugin-syndication/pages/services/feed/utils/result.xsl	(revision 0)
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2015 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<xsl:stylesheet version="1.0" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:i18n="http://apache.org/cocoon/i18n/2.1">
+	
+	<xsl:template match="/ActionResult">
+		<html>
+			<head/>
+			
+			<body id="default">
+				<xsl:choose>
+					<xsl:when test="error">
+						<div class="form-errors">
+							<h1><i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_ERROR" i18n:catalogue="plugin.syndication"/></h1>
+							<p><xsl:value-of select="error"/></p>
+							<noscript>
+								<p><i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK_ALT" i18n:catalogue="plugin.syndication"/></p>
+							</noscript>
+							<script type="text/javascript">
+								document.write("&lt;p&gt;&lt;a href='javascript: history.back();'&gt;<i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK" i18n:catalogue="plugin.syndication"/>&lt;/a&gt;&lt;/p&gt;")
+							</script>
+						</div>
+					</xsl:when>
+					<xsl:otherwise>
+						<div class="form-message">
+							<h1><i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_SUCCESS" i18n:catalogue="plugin.syndication"/></h1>
+							<noscript>
+								<p><i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK_ALT_SUCCESS" i18n:catalogue="plugin.syndication"/></p>
+							</noscript>
+							<script type="text/javascript">
+								document.write("&lt;p&gt;&lt;a href='javascript: history.back();'&gt;<i18n:text i18n:key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK" i18n:catalogue="plugin.syndication"/>&lt;/a&gt;&lt;/p&gt;")
+							</script>
+						</div>							
+					</xsl:otherwise>
+				</xsl:choose>
+			</body>
+		</html>
+	</xsl:template>
+	
+</xsl:stylesheet>
\ No newline at end of file
Index: main/plugin-syndication/pages/services/feed/feedmulticol2xhtml_1.2.xsl
===================================================================
--- main/plugin-syndication/pages/services/feed/feedmulticol2xhtml_1.2.xsl	(revision 0)
+++ main/plugin-syndication/pages/services/feed/feedmulticol2xhtml_1.2.xsl	(revision 0)
@@ -0,0 +1,314 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Copyright 2015 Anyware Services
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   -->
+<xsl:stylesheet version="1.0" 
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+                xmlns:ametys="org.ametys.web.transformation.xslt.AmetysXSLTHelper"
+                xmlns:resolver="org.ametys.cms.transformation.xslt.ResolveURIComponent"
+                xmlns:math="http://exslt.org/math" 
+                xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+                extension-element-prefixes="math ametys resolver">
+
+    <xsl:import href="service:web://pages/services/common.xsl"/>
+    <xsl:import href="service:syndication://pages/services/feed/utils/preference.xsl"/>
+    
+    <xsl:param name="header" select="ametys:serviceParameter('header')"/>
+
+    <xsl:variable name="uniqueId" select="concat('feed_', substring-after(math:random(), '.'))"/>
+    <xsl:variable name="link" select="ametys:serviceParameter('link')"/>
+    <xsl:variable name="link-title" select="ametys:serviceParameter('link-title')"/>
+    
+    <!-- Used in common.xsl -->
+    <xsl:variable name="common-service-css-class-name">syndication</xsl:variable>
+    <xsl:variable name="is-empty" select="not(/rssFeeds/feed) and not(/rssFeeds/feed-conf) and not(/rssFeeds/feed-error) and not(/rssFeeds/feed-error-custom)"/>
+    <xsl:variable name="common-service-name">service-syndication</xsl:variable>    
+
+
+    <xsl:variable name="raw-is-title-visible">        
+        <xsl:call-template name="common-service-head-title-content">
+            <xsl:with-param name="title" select="$header"/>                    
+        </xsl:call-template>
+    </xsl:variable>    
+    <xsl:variable name="is-title-visible" select="string-length($raw-is-title-visible) &gt; 0"/>
+  
+    <!-- Template used by lists to style items -->   
+    <xsl:template name="common-utils-class">
+        <xsl:if test="position() mod 3 = 1"><xsl:text>first-li </xsl:text></xsl:if>                                
+    </xsl:template>
+  
+    <xsl:template name="feed-get-url">
+        <xsl:value-of select="concat(ametys:siteUriPrefix(), '/', ametys:lang(), '/_plugins/syndication/page/create-conf')"/>
+    </xsl:template>
+    
+    <!-- Used in common.xsl -->
+    <xsl:template name="common-service-head-title">
+        <xsl:call-template name="common-service-head-title-content">
+            <xsl:with-param name="title" select="$header"/>                    
+        </xsl:call-template>
+    </xsl:template> 
+    
+    <!-- Used in common.xsl -->
+    <xsl:template name="common-service-head-css">
+        <xsl:if test="ametys:renderingContext() = 'back'">
+            <link href="{ametys:pluginResourceURL('syndication', 'css/bo-error.css')}" rel="stylesheet" type="text/css" />
+        </xsl:if>    
+    </xsl:template> 
+    
+    <!-- Used in common.xsl -->
+    <xsl:template name="common-service-body-nonempty-content-content">
+        <xsl:call-template name="link-top"/>        
+        <div>
+            
+            <xsl:call-template name="feed-config-form" />
+            <xsl:if test="/rssFeeds/feed-error-custom">
+            <div class="error">
+	            <ul>
+	                <xsl:for-each select="/rssFeeds/feed-error-custom">
+	                    <xsl:call-template name="feed-error-custom"/>
+	                </xsl:for-each>
+	            </ul>
+            </div>
+            </xsl:if>
+            <ul class="feed-list">
+            <xsl:for-each select="/rssFeeds/feed|/rssFeeds/feed-error">
+                <li>
+                    <xsl:attribute name="class"><xsl:call-template name="common-utils-class"/></xsl:attribute>
+                
+                    <xsl:choose>
+	                    <xsl:when test="@url">
+                            <xsl:call-template name="feed-error"/>
+	                    </xsl:when>
+	                    <xsl:otherwise>
+		<!--                    <xsl:apply-templates select="image"/> -->
+		                    <xsl:call-template name="syndication-title"/>
+		<!--                     <xsl:call-template name="syndication-description"/> -->
+		                    <xsl:call-template name="syndication-list"/>
+	                    </xsl:otherwise>
+                    </xsl:choose>
+                </li>
+            </xsl:for-each>
+            </ul>
+          </div>
+          <xsl:call-template name="link-bottom"/>        
+    </xsl:template>
+    
+     <!-- Template for error -->    
+    <xsl:template name="feed-error-custom"> 
+        <li><i18n:text i18n:key="SYNDICATION_SERVICE_XSL_ERROR_FEED_CUSTOM" i18n:catalogue="plugin.syndication"/><xsl:value-of select="@url" /></li>
+    </xsl:template>
+    
+      <!-- Template for error -->    
+    <xsl:template name="feed-error">
+        <div class="ametys-cms-zone-error syndication-feed-error">
+	        <div class="ametys-cms-zone-item-error">
+	        <table cellspacing="0" cellpadding="0">
+				<tbody>
+					<tr>
+					    <td style="vertical-align: top; width: 36px;">
+					    <img alt="error" src="/cms/plugins/web/resources/img/skin/zoneitem_error_32.png" />
+					</td>
+					<td>
+					    <strong><i18n:text i18n:key="SYNDICATION_SERVICE_XSL_ERROR_FEED_LABEL" i18n:catalogue="plugin.syndication"/></strong>
+					    <br/>
+						<em>
+						   <i18n:text i18n:key="SYNDICATION_SERVICE_XSL_ERROR_FEED" i18n:catalogue="plugin.syndication"/>
+						   <br/>
+						   <xsl:if test="@name != ''">
+	    					   <xsl:value-of select="@name" /> - 
+						   </xsl:if>
+						   <xsl:value-of select="@url" /> 
+						</em>
+					</td>
+					</tr>
+				</tbody>
+			</table>
+			<xsl:if test="@messageError != ''">
+			  <div class="exception-message">
+			      <xsl:value-of select="@messageError" />
+			  </div>
+			</xsl:if>
+            </div>
+        </div>
+    </xsl:template>       
+    
+    <!-- Template for the general title -->    
+    <xsl:template name="syndication-title">
+        <p class="rss-title">
+          <strong>
+              <xsl:value-of select="@title"/>
+          </strong>
+        </p>
+    </xsl:template>        
+
+    <!-- Template for the description -->
+    <xsl:template name="syndication-description">
+        <p class="description">
+            <xsl:value-of select="@description"/>
+        </p>
+    </xsl:template>
+    
+    <!-- Template for the list -->
+    <xsl:template name="syndication-list">
+        <ul>
+            <xsl:apply-templates select="entry"/>
+        </ul>    
+    </xsl:template>    
+            
+    <!-- Template for each item -->    
+    <xsl:template match="entry"> 
+        <li>
+            <xsl:attribute name="class"><xsl:call-template name="common-utils-class"/></xsl:attribute>
+            
+            <xsl:call-template name="item-title"/>
+            <xsl:call-template name="item-date"/>
+<!--             <xsl:apply-templates select="image"/> -->
+<!--             <xsl:call-template name="item-description"/> -->
+<!--             <xsl:call-template name="item-attachments"/> -->
+<!--             <xsl:call-template name="item-author"/> -->
+        </li>
+    </xsl:template>
+
+    <!-- Template for the title -->
+    <xsl:template name="item-title">
+        <p>
+           <strong>
+            <xsl:choose>
+                <xsl:when test="@link">
+                    <a href="{@link}"><xsl:value-of select="@title"/></a>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:value-of select="@title"/></xsl:otherwise>
+            </xsl:choose>
+           </strong>
+        </p>
+    </xsl:template>
+    
+    <!-- Template for the date -->
+    <xsl:template name="item-date">    
+        <xsl:if test="@date">
+            <p class="date">
+                <i18n:date src-pattern="yyyy-MM-dd'T'HH:mm" pattern="medium"><xsl:value-of select="@date"/></i18n:date>
+                <xsl:text> </xsl:text>
+                <i18n:time src-pattern="yyyy-MM-dd'T'HH:mm" pattern="short"><xsl:value-of select="@date"/></i18n:time>
+            </p>
+        </xsl:if>
+    </xsl:template>
+
+    <!-- Template for the image -->
+    <xsl:template match="image"> 
+        <p class="image">
+            <a href="{@link}">
+                <img src="{@url}" alt="{@title}">
+                    <xsl:if test="heigth"><xsl:attribute name="height"><xsl:value-of select="height"/></xsl:attribute></xsl:if>
+                    <xsl:if test="width"><xsl:attribute name="width"><xsl:value-of select="width"/></xsl:attribute></xsl:if>
+                </img>
+            </a>
+        </p>
+    </xsl:template>
+
+
+    <!-- Template for the description -->
+    <xsl:template name="item-description">  
+        <xsl:if test="description">
+            <p class="description">
+            <xsl:choose>
+                <xsl:when test="'text/html'">
+                    <xsl:value-of select="description" disable-output-escaping="yes"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:value-of select="description"/>
+                </xsl:otherwise>
+            </xsl:choose>
+            </p>
+        </xsl:if>    
+    </xsl:template>    
+    
+    <!-- Template for the attachment if exist -->    
+    <xsl:template name="item-attachments">
+        <xsl:if test="enclosure">
+            <p class="enclosures">
+                <xsl:for-each select="enclosure[@url]">
+                    <xsl:if test="@url">
+                        <div class="enclosure">
+                            <xsl:choose>
+                                <xsl:when test="starts-with(@type, 'video')"><xsl:attribute name="class">enclosure video</xsl:attribute></xsl:when>
+                                <xsl:when test="starts-with(@type, 'audio')"><xsl:attribute name="class">enclosure audio</xsl:attribute></xsl:when>
+                                <xsl:when test="starts-with(@type, 'image')"><xsl:attribute name="class">enclosure image</xsl:attribute></xsl:when>
+                            </xsl:choose>
+                            <a href="{@url}">
+                                <xsl:choose>
+                                    <xsl:when test="@name"><xsl:value-of select="@name"/></xsl:when>
+                                    <xsl:otherwise><xsl:value-of select="@url"/></xsl:otherwise>
+                                </xsl:choose>
+                            </a>
+                            <xsl:if test="length">
+                                <span class="length"> (<xsl:value-of select="length"/>)</span>
+                            </xsl:if>
+                        </div>
+                    </xsl:if>
+                </xsl:for-each>
+            </p>
+        </xsl:if>    
+    </xsl:template>    
+    
+    <!-- Template for author(s) -->    
+    <xsl:template name="item-author">
+        <!-- Item author(s) -->
+        <xsl:if test="author">
+            <p class="feed-entry-author">
+                <i18n:text i18n:key="FEED_ENTRY_AUTHOR"/>
+                <xsl:for-each select="author">
+                    <xsl:choose>
+                        <xsl:when test="@email">
+                            <a href="mailto:{@email}"><xsl:value-of select="."/></a>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:value-of select="."/>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                    <xsl:if test="position () != last()">, </xsl:if>
+                </xsl:for-each>
+            </p>
+        </xsl:if>    
+    </xsl:template>
+        
+    <!-- Template used to display link "all contents" on the top -->
+    <xsl:template name="link-top">
+        <xsl:call-template name="link"/>
+    </xsl:template>
+
+    <!-- Template used to display link "all contents" on the bottom -->
+    <xsl:template name="link-bottom">
+        <xsl:comment>empty</xsl:comment>
+    </xsl:template>
+    
+    <!-- Template used for link "all contents" -->
+    <xsl:template name="link">
+        <xsl:if test="starts-with($link, 'page://')">
+            <div class="link">
+                <a href="{resolver:resolve('page', $link)}">
+                    <xsl:choose>
+                        <xsl:when test="normalize-space($link-title) != ''">
+                            <xsl:value-of select="$link-title"/>
+                        </xsl:when>
+                        <xsl:otherwise><i18n:text i18n:key="PLUGINS_WEB_SERVICE_FILTERED_CONTENTS_LINK_TITLE_DEFAULT" i18n:catalogue="plugin.web"/></xsl:otherwise>
+                    </xsl:choose>
+                </a>
+            </div>
+        </xsl:if>    
+    </xsl:template>
+    
+</xsl:stylesheet>
Index: main/plugin-syndication/pages/services/feed/feed2xhtml_1.2.xsl
===================================================================
--- main/plugin-syndication/pages/services/feed/feed2xhtml_1.2.xsl	(revision 28681)
+++ main/plugin-syndication/pages/services/feed/feed2xhtml_1.2.xsl	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2015 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -17,15 +17,23 @@
 <xsl:stylesheet version="1.0" 
                 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                 xmlns:ametys="org.ametys.web.transformation.xslt.AmetysXSLTHelper"
-                xmlns:i18n="http://apache.org/cocoon/i18n/2.1">
+                xmlns:math="http://exslt.org/math" 
+                xmlns:resolver="org.ametys.cms.transformation.xslt.ResolveURIComponent"
+                xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+                extension-element-prefixes="math ametys resolver">
 
     <xsl:import href="service:web://pages/services/common.xsl"/>
+    <xsl:import href="service:syndication://pages/services/feed/utils/preference.xsl"/>
     
 	<xsl:param name="header" select="ametys:serviceParameter('header')"/>
 
+    <xsl:variable name="uniqueId" select="concat('feed_', substring-after(math:random(), '.'))"/>
+    <xsl:variable name="link" select="ametys:serviceParameter('link')"/>
+    <xsl:variable name="link-title" select="ametys:serviceParameter('link-title')"/>
+    
     <!-- Used in common.xsl -->
-    <xsl:variable name="common-service-css-class-name">syndication</xsl:variable>
-    <xsl:variable name="is-empty" select="not(/feed)"/>
+    <xsl:variable name="common-service-css-class-name">syndication-by-date</xsl:variable>
+    <xsl:variable name="is-empty" select="not(/rssFeeds/feed) and not(/rssFeeds/feed-conf) and not(/rssFeeds/feed-error) and not(/rssFeeds/feed-error-custom)"/>
     <xsl:variable name="common-service-name">service-syndication</xsl:variable>    
 
 
@@ -35,9 +43,8 @@
         </xsl:call-template>
     </xsl:variable>    
     <xsl:variable name="is-title-visible" select="string-length($raw-is-title-visible) &gt; 0"/>
-  
-
-    <!-- +
+    
+  <!-- +
          | HEAD
          + -->
 
@@ -48,6 +55,12 @@
         </xsl:call-template>
     </xsl:template> 
 
+    <!-- Used in common.xsl -->
+    <xsl:template name="common-service-head-css">
+        <xsl:if test="ametys:renderingContext() = 'back'">
+            <link href="{ametys:pluginResourceURL('syndication', 'css/bo-error.css')}" rel="stylesheet" type="text/css" />
+        </xsl:if>    
+    </xsl:template> 
       
     <!-- +
          | BODY
@@ -62,60 +75,79 @@
     
     <!-- Used in common.xsl -->
     <xsl:template name="common-service-body-nonempty-content-content">
-		<xsl:apply-templates select="image"/>
-		
-        <xsl:call-template name="syndication-title"/>
-        
-        <xsl:call-template name="syndication-description"/>
-        
-        <xsl:call-template name="syndication-list"/>
-        
-        <!-- Comments in the old XSL -->
-		<!-- 
-		<xsl:if test="link">
-			<a class="rss-link" href="{link}" title="{title}"><xsl:value-of select="link"></xsl:value-of></a>
-		</xsl:if>
-		 -->
-		 
-		 <!-- 
-		<xsl:if test="lastBuildDate">
-			<div class="rss-date"><xsl:value-of select="lastBuildDate"/></div>
-		</xsl:if>
-		-->
-    </xsl:template>
-    
-    <!-- Template for the general title -->    
-    <xsl:template name="syndication-title">
-   		<xsl:choose>
-   			<xsl:when test="$is-title-visible">
-   				<p class="rss-title"><xsl:value-of select="/feed/@title"/></p>
-   			</xsl:when>
-   			<xsl:otherwise>
-   				<h1 class="rss-title"><xsl:value-of select="/feed/@title"/></h1>
-   			</xsl:otherwise>
-   		</xsl:choose>
-    </xsl:template>        
-
-    <!-- Template for the description -->
-    <xsl:template name="syndication-description">
-        <p class="description"><xsl:value-of select="/feed/@description"/></p>    
-    </xsl:template>
-    
-    <!-- Template for the list -->
-    <xsl:template name="syndication-list">
-        <ul>
-            <xsl:apply-templates select="/feed/entry"/>
-        </ul>    
+        <xsl:call-template name="link-top"/>        
+        <div>
+            <xsl:call-template name="feed-config-form" />
+            <div class="error">
+	            <ul>
+		            <xsl:for-each select="/rssFeeds/feed-error-custom">
+		               <xsl:call-template name="feed-error-custom" />
+		            </xsl:for-each>
+	            </ul>
+            </div>
+            <div>
+	            <xsl:for-each select="/rssFeeds/feed-error">
+	               <xsl:call-template name="feed-error" />
+	            </xsl:for-each>        
+            </div>
+            <ul class="feed-list">
+		        <xsl:for-each select="/rssFeeds/feed/entry">
+		            <xsl:sort select="@date" order="descending"/>
+			        <xsl:call-template name="entry"/>
+		        </xsl:for-each>
+	        </ul>
+	      </div>
+	    <xsl:call-template name="link-bottom"/>        
     </xsl:template>    
             
+    <!-- Template for error -->    
+    <xsl:template name="feed-error-custom"> 
+        <li>
+            <p><i18n:text i18n:key="SYNDICATION_SERVICE_XSL_ERROR_FEED" i18n:catalogue="plugin.syndication"/><xsl:value-of select="@url" /></p>
+        </li>
+    </xsl:template>        
+     
+    <!-- Template for error -->    
+    <xsl:template name="feed-error"> 
+        <div class="ametys-cms-zone-error syndication-feed-error">
+            <div class="ametys-cms-zone-item-error">
+            <table cellspacing="0" cellpadding="0">
+                <tbody>
+                    <tr>
+                        <td style="vertical-align: top; width: 36px;">
+                        <img alt="error" src="/cms/plugins/web/resources/img/skin/zoneitem_error_32.png" />
+                    </td>
+                    <td>
+                        <strong><i18n:text i18n:key="SYNDICATION_SERVICE_XSL_ERROR_FEED_LABEL" i18n:catalogue="plugin.syndication"/></strong>
+                        <br/>
+                        <em>
+                           <i18n:text i18n:key="SYNDICATION_SERVICE_XSL_ERROR_FEED" i18n:catalogue="plugin.syndication"/>
+                           <br/>
+                           <xsl:if test="@name != ''">
+                               <xsl:value-of select="@name" /> - 
+                           </xsl:if>
+                           <xsl:value-of select="@url" /> 
+                        </em>
+                    </td>
+                    </tr>
+                </tbody>
+            </table>
+            <xsl:if test="@messageError != ''">
+              <div class="exception-message">
+                  <xsl:value-of select="@messageError" />
+              </div>
+            </xsl:if>
+            </div>
+        </div>
+    </xsl:template>   
+            
     <!-- Template for each item -->    
-	<xsl:template match="entry"> 
-		<li>
+	<xsl:template name="entry"> 
+		<li class="error">
             <xsl:attribute name="class"><xsl:call-template name="common-utils-class"/></xsl:attribute>
             
-            <xsl:call-template name="item-title"/>
-            <xsl:call-template name="item-date"/>
             <xsl:apply-templates select="image"/>
+            <xsl:call-template name="item-title"/>
             <xsl:call-template name="item-description"/>
             <xsl:call-template name="item-attachments"/>
             <xsl:call-template name="item-author"/>
@@ -124,28 +156,28 @@
 
     <!-- Template for the title -->
     <xsl:template name="item-title">
-    	<h2>
-            <xsl:choose>
-                <xsl:when test="@link">
-                    <a href="{@link}"><xsl:value-of select="@title"/></a>
-                </xsl:when>
-                <xsl:otherwise>
-                	<xsl:value-of select="@title"/></xsl:otherwise>
-            </xsl:choose>
-        </h2>
-    </xsl:template>
-    
-    <!-- Template for the date -->
-    <xsl:template name="item-date">    
-        <xsl:if test="@date">
-            <p class="date">
+   	   <h2>
+           <xsl:choose>
+               <xsl:when test="@link">
+                   <a href="{@link}"><xsl:value-of select="@title"/></a>
+               </xsl:when>
+               <xsl:otherwise>
+               	<xsl:value-of select="@title"/></xsl:otherwise>
+           </xsl:choose>
+   	   </h2>
+       <p class="rss-title">
+          <strong>
+             <xsl:if test="@date">
                 <i18n:date src-pattern="yyyy-MM-dd'T'HH:mm" pattern="medium"><xsl:value-of select="@date"/></i18n:date>
                 <xsl:text> </xsl:text>
                 <i18n:time src-pattern="yyyy-MM-dd'T'HH:mm" pattern="short"><xsl:value-of select="@date"/></i18n:time>
-            </p>
-        </xsl:if>
+                --
+             </xsl:if>
+             <xsl:value-of select="../@title"/>
+          </strong>
+       </p>
     </xsl:template>
-
+    
     <!-- Template for the image -->
     <xsl:template match="image"> 
         <p class="image">
@@ -163,14 +195,14 @@
     <xsl:template name="item-description">  
         <xsl:if test="description">
             <p class="description">
-                <xsl:choose>
-                    <xsl:when test="'text/html'">
-                        <xsl:value-of select="description" disable-output-escaping="yes"/>
-                    </xsl:when>
-                    <xsl:otherwise>
-                        <xsl:value-of select="description"/>
-                    </xsl:otherwise>
-                </xsl:choose>
+            <xsl:choose>
+                <xsl:when test="'text/html'">
+                    <xsl:value-of select="description" disable-output-escaping="yes"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:value-of select="description"/>
+                </xsl:otherwise>
+            </xsl:choose>
             </p>
         </xsl:if>    
     </xsl:template>    
@@ -222,6 +254,32 @@
                 </xsl:for-each>
             </p>
         </xsl:if>    
-    </xsl:template>    
+    </xsl:template>
+    
+    <!-- Template used to display link "all contents" on the top -->
+    <xsl:template name="link-top">
+        <xsl:call-template name="link"/>
+    </xsl:template>
+
+    <!-- Template used to display link "all contents" on the bottom -->
+    <xsl:template name="link-bottom">
+        <xsl:comment>empty</xsl:comment>
+    </xsl:template>
+    
+    <!-- Template used for link "all contents" -->
+    <xsl:template name="link">
+        <xsl:if test="starts-with($link, 'page://')">
+            <div class="link">
+                <a href="{resolver:resolve('page', $link)}">
+                    <xsl:choose>
+                        <xsl:when test="normalize-space($link-title) != ''">
+                            <xsl:value-of select="$link-title"/>
+                        </xsl:when>
+                        <xsl:otherwise><i18n:text i18n:key="PLUGINS_WEB_SERVICE_FILTERED_CONTENTS_LINK_TITLE_DEFAULT" i18n:catalogue="plugin.web"/></xsl:otherwise>
+                    </xsl:choose>
+                </a>
+            </div>
+        </xsl:if>    
+    </xsl:template>
     
 </xsl:stylesheet>
Index: main/plugin-syndication/i18n/messages_en.xml
===================================================================
--- main/plugin-syndication/i18n/messages_en.xml	(revision 28681)
+++ main/plugin-syndication/i18n/messages_en.xml	(working copy)
@@ -29,18 +29,71 @@
 	<message key="SYNDICATION_SERVICE_READ_RSS_URL">Feed URL</message>
 	<message key="SYNDICATION_SERVICE_READ_RSS_URL_DESC">Feed URL (http://...)</message>
 	
-	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH">Length</message>
-	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DESC">Max number of contents to display.&lt;br/&gt;Leave this field empty to display all contents of the feed.</message>
-	
+	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DEFAULT">Number of article by feed</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DEFAULT_DESC">Default value of the number of article displayed by feed.&lt;br/&gt;Empty to display all articles from the feed.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_LENGTH">Number of article displayed</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DESC">Value of the number of article displayed by feed.&lt;br/&gt;Empty to choose the default value.</message>
+    
 	<message key="SYNDICATION_SERVICE_READ_XSLT">Display</message>
 	<message key="SYNDICATION_SERVICE_READ_XSLT_DESC">Select the display to use when displaying this service</message>
 	<message key="SYNDICATION_SERVICE_READ_XSLT_FEED2XHTML_LABEL">Ametys Syndication 1.1 compatibility mode</message>
 	<message key="SYNDICATION_SERVICE_READ_XSLT_FEED2XHTML12_LABEL">Default</message>
 	
+	<message key="SYNDICATION_SERVICE_READ_RSS_CACHE_DEFAULT">Setting up cache duration</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_DEFAULT_DESC">Default setting up cache duration of the informations.&lt;br/&gt;A big time involve a new information will be visible on your web site more later; however the page show more quickly.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE">Setting up cache duration</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_DESC">Setting up cache duration of the informations.&lt;br/&gt;A big time involve a new information will be visible on your web site more later; however the page show more quickly. Empty to take the default value.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_LONG">Web site with few activity (24h)</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_MEDIUM">Web site with normal activity (3h)</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_SMALL">News web site (30 minutes)</message>
+    
+    <message key="SYNDICATION_SERVICE_READ_RSS_LIMITED">Limited to the connected users</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_LIMITED_DESC">Only connected user can see this feed. If this is checked, criteria below ("Limited to users" et "Limited to groups") will not be considerated. No needs to fill them.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_USER">Limited to users</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_USER_DESC">Limite the access to selected users.&lt;br/&gt;This value has no effect if "Limited to the connected users" is checked.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_GROUP">Limited to groups</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_GROUP_DESC">Limite the access to selected groups.&lt;br/&gt;This value has no effect if "Limited to the connected users" is checked.</message>
+    
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_CUSTOM">Custom feeds</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_CUSTOM_DESC">Number of custom feeds the user can have (the user writes feed's url himselef).&lt;br/&gt;&lt;br/&gt;Warning, autorize custom feeds can slow page performances.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_MAX">Number of feeds max</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_MAX_DESC">Number of feeds max (customs feeds included) displayed to the visitor.&lt;br/&gt;&lt;br/&gt;When the number of displayed feeds for a connected visitor is bigger than this maximum, this visitor will have the posibility to choose feeds he wants to display.&lt;br/&gt;&lt;br/&gt;Warning, limite the number of feeds can slow page performances.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FEED_LABEL">Feeds</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FEED_DESC">Feeds</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_ADD">Add a feed</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_EDIT">Edit</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_DELETE">Delete</message>
+    <message key="SYNDICATION_SERVICE_SERVICE_RSS_LINK_TITLE_DESC">A link to the selected page will be showed on the top of the display feeds.</message>
+    
+	
 	<message key="FEED_ENTRY_AUTHOR">Author : </message>
 	<message key="ENCLOSURE_LENGTH_BYTES">{0} o</message>
 	<message key="ENCLOSURE_LENGTH_KB">{0} Kb</message>
 	<message key="ENCLOSURE_LENGTH_MB">{0} Mb</message>
 	<message key="ENCLOSURE_LENGTH_GB">{0} Gb</message>
 	
+	<!-- Popup config -->
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_TITLE">Feeds</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_RECORD">Record</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CANCEL">Cancel</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG">Configure</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO_FEED_MAX">Number of feeds max : </message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_ERROR_TO_MANY_CHECK">You have checked to many feed.</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_OK">Your configuration has been recorded.</message>
+    
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_SUCCESS">Configuration recorded</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_ERROR">An error occurred</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK">Click here to come back to the previous page</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK_ALT_SUCCESS">Use the button 'Previous' of your browser to come back to the previous page.</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK_ALT">Use the button 'Previous' of your browser to change your configuration form.</message>
+
+    <message key="SYNDICATION_SERVICE_READ_XSLT_FEEDMULTICOL2XHTML_LABEL">Multi column</message>
+
+    <message key="SYNDICATION_SERVICE_XSL_ERROR_FEED_LABEL">Display error</message>
+    <message key="SYNDICATION_SERVICE_XSL_ERROR_FEED">An error occurred. Impossible to display the feed : </message>
+    <message key="SYNDICATION_SERVICE_XSL_ERROR_FEED_CUSTOM">Impossible to display your custom feed : </message>
+
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO">Select {0} feeds you want to display checking the matching checkbox :</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO2">You can display your own feeds writing the url given by the web site :</message>
+	
 </catalogue>
Index: main/plugin-syndication/i18n/messages_fr.xml
===================================================================
--- main/plugin-syndication/i18n/messages_fr.xml	(revision 28681)
+++ main/plugin-syndication/i18n/messages_fr.xml	(working copy)
@@ -29,17 +29,70 @@
 	<message key="SYNDICATION_SERVICE_READ_RSS_URL">URL du flux</message>
 	<message key="SYNDICATION_SERVICE_READ_RSS_URL_DESC">URL du flux utilisé (http://...)</message>
 	
-	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH">Taille</message>
-	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DESC">Nombre maximum de contenus du fils RSS à afficher.&lt;br/&gt;Laissez vide pour afficher tous les contenus</message>
-	
+	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DEFAULT">Nombre d'articles par flux</message>
+	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DEFAULT_DESC">Valeur par défaut du nombre de contenus affichés pour chaque flux RSS.&lt;br/&gt;Laisser vide pour afficher tous les contenus des flux RSS.</message>
+	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH">Nombre d'articles affichés</message>
+	<message key="SYNDICATION_SERVICE_READ_RSS_LENGTH_DESC">Nombre de contenus affichés pour le flux RSS.&lt;br/&gt;Laisser vide pour prendre la valeur par défaut.</message>
+    
 	<message key="SYNDICATION_SERVICE_READ_XSLT">Affichage</message>
     <message key="SYNDICATION_SERVICE_READ_XSLT_DESC">Rendu à utiliser pour l'affichage du service</message>
     <message key="SYNDICATION_SERVICE_READ_XSLT_FEED2XHTML_LABEL">Mode de compatibilité Ametys Syndication 1.1</message>
     <message key="SYNDICATION_SERVICE_READ_XSLT_FEED2XHTML12_LABEL">Par défault</message>
 	
-	<message key="FEED_ENTRY_AUTHOR">Auteur : </message>
-	<message key="ENCLOSURE_LENGTH_BYTES">{0} o</message>
-	<message key="ENCLOSURE_LENGTH_KB">{0} Ko</message>
-	<message key="ENCLOSURE_LENGTH_MB">{0} Mo</message>
-	<message key="ENCLOSURE_LENGTH_GB">{0} Go</message>
+	<message key="SYNDICATION_SERVICE_READ_RSS_CACHE_DEFAULT">Durée de mise en cache</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_DEFAULT_DESC">Valeur par défaut de la durée de mise en cache des informations.&lt;br/&gt;Un temps élevé implique qu'un nouvel article sur le site distant ne sera visible sur votre site qu'avec beaucoup de retard ; en contre-partie la page actuelle s'affichera plus rapidement : il est important de choisir une valeur adaptée à la fréquence de mise à jour du site distant.</message>
+	<message key="SYNDICATION_SERVICE_READ_RSS_CACHE">Durée de mise en cache</message>
+	<message key="SYNDICATION_SERVICE_READ_RSS_CACHE_DESC">Durée de mise en cache des informations.&lt;br/&gt;Un temps élevé implique qu'un nouvel article sur le site distant ne sera visible sur votre site qu'avec beaucoup de retard ; en contre-partie la page actuelle s'affichera plus rapidement : il est important de choisir une valeur adaptée à la fréquence de mise à jour du site distant. Laisser vide pour prendre la valeur par défaut.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_LONG">Site peu actif (24h)</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_MEDIUM">Site actif (3h)</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_CACHE_SMALL">Site d'actualités (30 minutes)</message>
+    
+    <message key="SYNDICATION_SERVICE_READ_RSS_LIMITED">Limité aux utilisateurs connectés</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_LIMITED_DESC">Seul les utilisateurs connectés peuvent voir ce flux rss. Si la case est cochée, les critères ci-dessous ("Limité aux utilisateurs" et "Limité aux groupes") ne seront pas pris en compte. Il n'est donc pas necessaire de les remplir.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_USER">Limité aux utilisateurs</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_USER_DESC">Limite l'accès aux utilisateurs sélectionnés.&lt;br/&gt;Cette valeur est sans effet si "Limité aux utilisateurs connectés" est coché.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_GROUP">Limité aux groupes</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FO_GROUP_DESC">Limite l'accès aux groupes sélectionné.&lt;br/&gt;Cette valeur est sans effet si "Limité aux utilisateurs connectés" est coché.</message>
+    
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_CUSTOM">Flux personnalisés</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_CUSTOM_DESC">Nombre de flux personnalisés par le visiteur (dont l'url est saisie par le visiteur connecté lui-même).&lt;br/&gt;&lt;br/&gt;Attention, autoriser des flux personnalisés peut dégrader les performances de la page.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_MAX">Nombre de flux maximum</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_NB_FLUX_MAX_DESC">Nombre maximum de flux (y compris personnalisés) affichés au visiteur.&lt;br/&gt;&lt;br/&gt;Lorsque vous sélectionnez de nombreux flux RSS mais que certains sont limités à un public donné, cette limitation vous permet de ne pas rendre votre page illisible.&lt;br/&gt;&lt;br/&gt;Lorsque le nombre de flux affichable pour un visiteur connecté est supérieur au maximum choisi, ce visiteur aura la possibilité de sélectionner les flux qu'il désire voir parmi ceux disponibles.&lt;br/&gt;&lt;br/&gt;Attention, limiter le nombre de flux affichés au visiteur peut dégrader les performances de la page.</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FEED_LABEL">Liste des flux</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_FEED_DESC">Liste des differents flux affichés</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_ADD">Ajouter un flux</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_EDIT">Editer</message>
+    <message key="SYNDICATION_SERVICE_READ_RSS_DELETE">Supprimer</message>
+    <message key="SYNDICATION_SERVICE_SERVICE_RSS_LINK_TITLE_DESC">Un lien vers la page sélectionnée sera présenté au dessus des flux RSS affichés.</message>
+    
+    <message key="FEED_ENTRY_AUTHOR">Auteur : </message>
+    <message key="ENCLOSURE_LENGTH_BYTES">{0} o</message>
+    <message key="ENCLOSURE_LENGTH_KB">{0} Ko</message>
+    <message key="ENCLOSURE_LENGTH_MB">{0} Mo</message>
+    <message key="ENCLOSURE_LENGTH_GB">{0} Go</message>
+        
+    <!-- Popup config -->
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_TITLE">Flux RSS</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_RECORD">Enregister</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CANCEL">Annuler</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG">Configurer</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO_FEED_MAX">Nombre de flux maximum : </message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_ERROR_TO_MANY_CHECK">Vous avez coché trop de flux rss.</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_OK">Votre configuration a été bien enregistrée.</message>
+    
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_SUCCESS">Configuration enregistrée</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_ERROR">Une erreur est survenue</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK">Cliquez ici pour revenir à la page précédente</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK_ALT_SUCCESS">Utilisez le bouton 'Précédent' de votre navigateur web pour revenir à la page précédente.</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_BACK_ALT">Utilisez le bouton 'Précédent' de votre navigateur web pour corriger votre formulaire de configuration.</message>
+
+    <message key="SYNDICATION_SERVICE_READ_XSLT_FEEDMULTICOL2XHTML_LABEL">Affichage multi colonne</message>
+
+    <message key="SYNDICATION_SERVICE_XSL_ERROR_FEED_LABEL">Erreur d'affichage</message>
+    <message key="SYNDICATION_SERVICE_XSL_ERROR_FEED">Une erreur est survenue. Impossible d'afficher le flux RSS : </message>
+    <message key="SYNDICATION_SERVICE_XSL_ERROR_FEED_CUSTOM">Impossible d'afficher votre flux RSS personnalisé : </message>
+
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO">Selectionner les {0} flux RSS que vous souhaitez afficher en cochant les cases correspondantes ci-dessous :</message>
+    <message key="SYNDICATION_SERVICE_CONFIG_RSS_FORM_POPUP_CONFIG_INFO2">Vous pouvez afficher vos propres flux RSS en saisissant l'adresse fournie par le site :</message>
+
 </catalogue>
Index: main/plugin-syndication/plugin.xml
===================================================================
--- main/plugin-syndication/plugin.xml	(revision 28681)
+++ main/plugin-syndication/plugin.xml	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!--
-   Copyright 2010 Anyware Services
+   Copyright 2015 Anyware Services
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -21,6 +21,12 @@
         
 	<feature name="module.syndication">
 	
+	    <components>
+            <component role="org.ametys.plugins.syndication.FeedCache"
+                       class="org.ametys.plugins.syndication.FeedCache">
+            </component>
+        </components>
+        
     	<extensions>
             <extension point="org.ametys.runtime.plugins.core.right.RightsExtensionPoint"
                        id="org.ametys.plugins.syndication.service.rss.rights">
@@ -32,7 +38,7 @@
             </extension>
             
     		<extension point="org.ametys.web.service.ServiceExtensionPoint"
-	   				   class="org.ametys.web.service.StaticService"
+	   				   class="org.ametys.plugins.syndication.SyndicationService"
 	   				   id="org.ametys.plugins.syndication.service.RSS">
 
 				<url>feed/read.xhtml</url>
@@ -47,21 +53,111 @@
 					<large>img/feed-icon_32.png</large>
 				</thumbnail>
 				<parameters>
-					<parameter name="header" type="string">
-						<label i18n="true">SYNDICATION_SERVICE_READ_RSS_TITLE</label>
-						<description i18n="true">SYNDICATION_SERVICE_READ_RSS_TITLE_DESC</description>
-					</parameter>
-					<parameter name="url" type="string">
-						<label i18n="true">SYNDICATION_SERVICE_READ_RSS_URL</label>
-						<description i18n="true">SYNDICATION_SERVICE_READ_RSS_URL_DESC</description>
-						<validation>
-	                     	<mandatory/>
-	                   </validation>
-					</parameter>
-					<parameter name="length" type="long">
-						<label i18n="true">SYNDICATION_SERVICE_READ_RSS_LENGTH</label>
-						<description i18n="true">SYNDICATION_SERVICE_READ_RSS_LENGTH_DESC</description>
-					</parameter>
+				    <parameter name="header" type="string">
+                        <label i18n="true">SYNDICATION_SERVICE_READ_RSS_TITLE</label>
+                        <description i18n="true">SYNDICATION_SERVICE_READ_RSS_TITLE_DESC</description>
+                    </parameter>
+                    <parameter name="length" type="long">
+                        <label i18n="true">SYNDICATION_SERVICE_READ_RSS_LENGTH_DEFAULT</label>
+                        <description i18n="true">SYNDICATION_SERVICE_READ_RSS_LENGTH_DEFAULT_DESC</description>
+                    </parameter>
+                    <parameter name="cache" type="string">
+                        <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_DEFAULT</label>
+                        <description i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_DEFAULT_DESC</description>
+                        <default-value>2</default-value>
+                        <enumeration>
+                             <entry>
+                                <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_LONG</label>
+                                <value>1</value>
+                             </entry>
+                             <entry>
+                                <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_MEDIUM</label>
+                                <value>2</value>
+                             </entry>
+                             <entry>
+                                <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_SMALL</label>
+                                <value>3</value>
+                             </entry>
+                        </enumeration>
+                        <validation>
+                            <mandatory/>
+                        </validation>
+                    </parameter>
+				    <repeater name="feeds" initial-size="0" min-size="1">
+                        <label i18n="true">SYNDICATION_SERVICE_READ_RSS_FEED_LABEL</label>
+                        <description i18n="true">SYNDICATION_SERVICE_READ_RSS_FEED_DESC</description>
+                        <add-label i18n="true">SYNDICATION_SERVICE_READ_RSS_ADD</add-label>
+                        <add-icon>img/repeater/add_12.png</add-icon>
+                        <edit-label i18n="true">SYNDICATION_SERVICE_READ_RSS_EDIT</edit-label>
+                        <edit-icon>img/repeater/edit_12.png</edit-icon>
+                        <del-label i18n="true">SYNDICATION_SERVICE_READ_RSS_DELETE</del-label>
+                        <parameter name="title" type="string">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_TITLE</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_TITLE_DESC</description>
+                        </parameter>
+                        <parameter name="url" type="string">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_URL</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_URL_DESC</description>
+                            <validation>
+                                <mandatory/>
+                                <regexp>^https?://[a-zA-Z0-9.\-]+(:[0-9]+)?((/[a-zA-Z0-9\-_.]+)*)(\s*,\s*https?://[a-zA-Z0-9.\-]+(:[0-9]+)?((/[a-zA-Z0-9\-_.]+)*))*$</regexp>
+                           </validation>
+                        </parameter>
+                        <parameter name="length" type="long">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_LENGTH</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_LENGTH_DESC</description>
+                        </parameter>
+                        <parameter name="cache" type="string">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_DESC</description>
+                            <default-value>2</default-value>
+                            <enumeration>
+                                 <entry>
+                                    <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_LONG</label>
+                                    <value>1</value>
+                                 </entry>
+                                 <entry>
+                                    <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_MEDIUM</label>
+                                    <value>2</value>
+                                 </entry>
+                                 <entry>
+                                    <label i18n="true">SYNDICATION_SERVICE_READ_RSS_CACHE_SMALL</label>
+                                    <value>3</value>
+                                 </entry>
+                            </enumeration>
+                        </parameter>
+                        <parameter name="limited" type="boolean">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_LIMITED</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_LIMITED_DESC</description>
+                        </parameter>
+                        <parameter name="fo-user" type="string" multiple="true">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_FO_USER</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_FO_USER_DESC</description>
+                            <widget>fouser</widget>
+                        </parameter>
+                        <parameter name="fo-group" type="string" multiple="true">
+                            <label i18n="true">SYNDICATION_SERVICE_READ_RSS_FO_GROUP</label>
+                            <description i18n="true">SYNDICATION_SERVICE_READ_RSS_FO_GROUP_DESC</description>
+                            <widget>fogroup</widget>
+                        </parameter>
+                    </repeater>
+                    <parameter name="nb-feed-user" type="long">
+                        <label i18n="true">SYNDICATION_SERVICE_READ_RSS_NB_FLUX_CUSTOM</label>
+                        <description i18n="true">SYNDICATION_SERVICE_READ_RSS_NB_FLUX_CUSTOM_DESC</description>
+                    </parameter>
+                    <parameter name="nb-feed-max" type="long">
+                        <label i18n="true">SYNDICATION_SERVICE_READ_RSS_NB_FLUX_MAX</label>
+                        <description i18n="true">SYNDICATION_SERVICE_READ_RSS_NB_FLUX_MAX_DESC</description>
+                    </parameter>
+                    <parameter name="link" type="string">
+                        <label i18n="true">plugin.web:PLUGINS_WEB_SERVICE_FILTERED_CONTENTS_LINK</label>
+                        <description i18n="true">SYNDICATION_SERVICE_SERVICE_RSS_LINK_TITLE_DESC</description>
+                        <widget>current-sitemap</widget>
+                    </parameter>
+                    <parameter name="link-title" type="string">
+                        <label i18n="true">plugin.web:PLUGINS_WEB_SERVICE_FILTERED_CONTENTS_LINK_TITLE</label>
+                        <description i18n="true">plugin.web:PLUGINS_WEB_SERVICE_FILTERED_CONTENTS_LINK_TITLE_DESC</description>
+                    </parameter>
 					<parameter name="xslt" type="string">
 						<label i18n="true">SYNDICATION_SERVICE_READ_XSLT</label>
 						<description i18n="true">SYNDICATION_SERVICE_READ_XSLT_DESC</description>
@@ -74,7 +170,7 @@
 	                   	 	<custom-enumerator class="org.ametys.web.service.ServiceXSLTEnumerator">
 								<path>pages/services/feed</path>
 								<values>
-									<value>pages/services/feed/feed2xhtml.xsl</value>
+									<value>pages/services/feed/feedmulticol2xhtml_1.2.xsl</value>
 								</values>								
 	                  	 	</custom-enumerator>
 	                  	</enumeration>
@@ -97,5 +193,12 @@
             </extension>
         </extensions>
     </feature>
+    
+    <feature name="syndication.service.component">
+        <components>
+            <component class="org.ametys.plugins.syndication.ConfAccessComponent" 
+                       role="org.ametys.plugins.syndication.ConfAccessComponent" />
+        </components>
+    </feature>
     	
 </plugin>
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/SyndicationService.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/SyndicationService.java	(revision 0)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/SyndicationService.java	(revision 0)
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.syndication;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.plugins.repository.metadata.CompositeMetadata;
+import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType;
+import org.ametys.web.repository.page.Page;
+import org.ametys.web.repository.page.ZoneItem;
+import org.ametys.web.service.StaticService;
+
+/**
+ *  Flux RSS Service to redefined isCacheable (Not Cacheable if there are some restricted access or personnal flux)
+ */
+public class SyndicationService extends StaticService
+{
+    /** Conf access component */
+    protected ConfAccessComponent _confAccess;
+    
+    @Override
+    public void service(ServiceManager smanager) throws ServiceException
+    {
+        _manager = smanager;
+        _confAccess = (ConfAccessComponent) smanager.lookup(ConfAccessComponent.ROLE);
+    }
+    
+    @Override
+    public boolean isCacheable(Page currentPage, ZoneItem zoneItem)
+    {
+        CompositeMetadata composite = zoneItem.getServiceParameters();
+        CompositeMetadata repeater = composite.getCompositeMetadata("feeds");
+        String nbFeedPerso = composite.getString("nb-feed-user");
+        int nbMaxFeed = Integer.parseInt(composite.getString("nb-feed-max"));
+        int nbFeeds = repeater.getMetadataNames().length;
+        
+        return _confAccess.isServiceCacheable(repeater, nbFeedPerso, nbFeeds, nbMaxFeed);
+    }
+}
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/FeedsGenerator.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/FeedsGenerator.java	(revision 0)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/FeedsGenerator.java	(revision 0)
@@ -0,0 +1,448 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.syndication;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.excalibur.source.SourceResolver;
+import org.xml.sax.SAXException;
+
+import org.ametys.plugins.repository.metadata.CompositeMetadata;
+import org.ametys.runtime.group.GroupsManager;
+import org.ametys.runtime.plugins.core.userpref.UserPreferencesException;
+import org.ametys.runtime.plugins.core.userpref.UserPreferencesManager;
+import org.ametys.runtime.util.IgnoreRootHandler;
+import org.ametys.web.WebConstants;
+import org.ametys.web.pageaccess.PageAccessManager;
+import org.ametys.web.renderingcontext.RenderingContext;
+import org.ametys.web.renderingcontext.RenderingContextHandler;
+import org.ametys.web.repository.page.Page;
+import org.ametys.web.repository.page.ZoneItem;
+import org.ametys.web.userpref.FOUserPreferencesConstants;
+
+/**
+ * Generator for feeds service
+ */
+public class FeedsGenerator extends ServiceableGenerator
+{
+    /** User pref key for urls */
+    public static final String USER_PREF_RSS_URL_KEY = "user-pref-rss-urls";
+    
+    /** User pref key for positions */
+    public static final String USER_PREF_RSS_POSITION_KEY = "user-pref-rss-positions";
+    
+    private static HashMap<String, Integer> _lifeTimes = new HashMap<String, Integer>();
+    static 
+    {
+        _lifeTimes.put("1", 1440);
+        _lifeTimes.put("2", 180);
+        _lifeTimes.put("3", 30);     
+    }
+    
+    /** Conf access component */
+    protected ConfAccessComponent _confAccess;
+    
+    /** The page access manager */
+    protected PageAccessManager _accessManager;
+    
+    /** The group manager */
+    protected GroupsManager _groupManager;
+    
+    /** The feed cache */
+    protected FeedCache _feedCache;
+    
+    /** The source resolver */
+    protected SourceResolver _resolver;
+    
+    /** The user preferences manager. */
+    protected UserPreferencesManager _userPrefManager;
+    
+    /** The rendering context handler. */
+    protected RenderingContextHandler _renderingContext;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _resolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE);
+        _feedCache = (FeedCache) serviceManager.lookup(FeedCache.ROLE);
+        _accessManager = (PageAccessManager) serviceManager.lookup(PageAccessManager.ROLE);
+        _groupManager = (GroupsManager) serviceManager.lookup(GroupsManager.ROLE + ".FO");
+        _userPrefManager = (UserPreferencesManager) serviceManager.lookup(UserPreferencesManager.ROLE + ".FO");
+        _renderingContext = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE);
+        _confAccess = (ConfAccessComponent) serviceManager.lookup(ConfAccessComponent.ROLE);
+    }
+    
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        
+        ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName());
+        String login = (String) request.getAttribute(WebConstants.FO_LOGIN);
+        
+        Page page = zoneItem.getZone().getPage();
+        String siteName = page.getSiteName();
+        String lang = page.getSitemapName();
+        
+        String storageContext = siteName + "/" + lang + "/" + zoneItem.getId();
+        Map<String, String> contextVars = _getContextVars(siteName, lang);
+        
+        CompositeMetadata serviceParameters = zoneItem.getServiceParameters();
+
+        List<String> urlList = new ArrayList<String>();
+        List<String> positionList = new ArrayList<String>();
+        try
+        {
+            urlList = _getListUrl(login, storageContext, contextVars);
+            positionList = _getListPosition(login, storageContext, contextVars);
+        }
+        catch (UserPreferencesException e1)
+        {
+            getLogger().error("Can't have the user preference from user : '" + login + "'");
+        }
+        
+        contentHandler.startDocument();
+        _saxAllFeed(request, zoneItem, serviceParameters, urlList, positionList);
+        contentHandler.endDocument();
+    }
+    
+    // Sax all the feed depend on the service parameters
+    private void _saxAllFeed(Request request, ZoneItem zoneItem, CompositeMetadata serviceParameters, List<String> urlList, List<String> userSelectedFeedsPositions) throws SAXException
+    {
+        String defaultLengthValueAsString = serviceParameters.getString("length");
+        if (StringUtils.isEmpty(defaultLengthValueAsString))
+        {
+            defaultLengthValueAsString = "-1";
+        }
+        String defaultLifeTime = serviceParameters.getString("cache");
+
+        AttributesImpl atts = new AttributesImpl();
+
+        CompositeMetadata rssFeeds = serviceParameters.getCompositeMetadata("feeds");
+        List<CompositeMetadata> listFeeds = _filterAndPreloadCacheMetadata(request, rssFeeds);
+
+        String nbMaxFeedFullSaxedAsString = serviceParameters.getString("nb-feed-max");
+        if (StringUtils.isEmpty(nbMaxFeedFullSaxedAsString))
+        {
+            nbMaxFeedFullSaxedAsString = "0";
+        }
+        long nbMaxFeedFullSaxed = Long.parseLong(nbMaxFeedFullSaxedAsString);
+        
+        String nbMaxUserAsString = serviceParameters.getString("nb-feed-user");
+        if (StringUtils.isEmpty(nbMaxUserAsString))
+        {
+            nbMaxUserAsString = "0";
+        }
+        long nbMaxUser = Long.parseLong(nbMaxUserAsString);
+        
+        atts.addCDATAAttribute("nbFeedService", String.valueOf(listFeeds.size()));
+        atts.addCDATAAttribute("zoneItemId", String.valueOf(zoneItem.getId()));
+        atts.addCDATAAttribute("nbMaxUser", String.valueOf(nbMaxUser));
+        atts.addCDATAAttribute("nbMax", String.valueOf(nbMaxFeedFullSaxed));
+        
+        Page page = (Page) request.getAttribute(Page.class.getName());
+        atts.addCDATAAttribute("showForm", String.valueOf(_confAccess.showPreferenceForm(page, nbMaxUserAsString, listFeeds.size(), nbMaxFeedFullSaxed)));
+        
+        XMLUtils.startElement(contentHandler, "rssFeeds", atts);
+        
+        long totalFeed = listFeeds.size() + nbMaxUser;
+        
+        _saxFeeds(totalFeed, listFeeds, urlList, nbMaxFeedFullSaxed, defaultLengthValueAsString, defaultLifeTime, serviceParameters, userSelectedFeedsPositions);
+        _saxFeedsConfig(totalFeed, nbMaxUser, listFeeds, urlList, userSelectedFeedsPositions);
+        
+        XMLUtils.endElement(contentHandler, "rssFeeds");
+    }
+    
+    private void _saxFeedsConfig(long totalFeed, long nbMaxUser, List<CompositeMetadata> listFeeds, List<String> urlList, List<String> userSelectedFeedsPositions) throws SAXException
+    {
+        int nbFeedCustom = 0;
+        for (int position = 0; position < totalFeed; position++)
+        {
+            CompositeMetadata rssFeed = null;
+            if (position < listFeeds.size())
+            {
+                rssFeed = listFeeds.get(position);
+            }
+            
+            Boolean isCheck = userSelectedFeedsPositions.contains(String.valueOf(position + 1));
+
+            if (rssFeed != null) // Feed from service parameters
+            {
+                _saxInfoFeed(rssFeed.getString("url"), rssFeed.getString("title"), false, isCheck); // Sax the user preference form
+            }
+            else // Feed from user preference
+            {
+                if (position - listFeeds.size() < urlList.size())
+                {
+                    _saxInfoFeed(urlList.get(position - listFeeds.size()), "", true, isCheck); // Sax the user preference form
+                }
+                else if (nbFeedCustom < nbMaxUser)
+                {
+                    _saxInfoFeed("", "", true, isCheck); // Sax the user preference form (empty input)
+                }
+                nbFeedCustom++;
+            }
+        }
+    }
+    
+    private void _saxFeeds(long totalFeed, List<CompositeMetadata> listFeeds, List<String> urlList, long nbMaxFeedFullSaxed, String defaultLengthValueAsString, String defaultLifeTime, CompositeMetadata serviceParameters,  List<String> userSelectedFeedsPositions)
+    {
+        int nbFeedFullSaxed = 0;
+        for (int position = 0; position < totalFeed; position++)
+        {
+            CompositeMetadata rssFeed = null;
+            if (position < listFeeds.size())
+            {
+                rssFeed = listFeeds.get(position);
+            }
+            
+            // Check if the max number of feed to sax was not reached
+            // and if the feed has been selected by the visitor
+            if ((nbFeedFullSaxed < nbMaxFeedFullSaxed || nbMaxFeedFullSaxed == 0)
+                && (userSelectedFeedsPositions.isEmpty() || userSelectedFeedsPositions.contains(String.valueOf(position + 1))))
+            {
+                if (rssFeed != null) // Feed from service parameters
+                {
+                    Boolean isCheck = userSelectedFeedsPositions.contains(String.valueOf(position + 1));
+
+                    _saxFullInfoFeed(rssFeed, isCheck, defaultLengthValueAsString, defaultLifeTime); // Sax all informations
+                }
+                else // Feed from user preference
+                {
+                    if (position - listFeeds.size() < urlList.size())
+                    {
+                        String urlFeed = urlList.get(position - listFeeds.size());
+                        Boolean isCheck = userSelectedFeedsPositions.contains(String.valueOf(position + 1));
+                        
+                        _saxFullInfoFeedCustom(serviceParameters, urlFeed, defaultLengthValueAsString, isCheck); // Sax all informations
+                    }
+                }
+                nbFeedFullSaxed++;
+            }
+        }
+    }
+    
+    // Sax few info from the feed for the user preference form
+    private void _saxInfoFeed(String url, String name, Boolean isCustom, Boolean isCheck) throws SAXException
+    {
+        AttributesImpl atts = new AttributesImpl();
+
+        atts.addCDATAAttribute("feedUrl", url);
+        atts.addCDATAAttribute("feedName", name);
+        atts.addCDATAAttribute("isCustom", String.valueOf(isCustom));
+        atts.addCDATAAttribute("isCheck", String.valueOf(isCheck));
+
+        XMLUtils.createElement(contentHandler, "feed-conf", atts);
+    }
+    
+    // Sax all info from the feed for the service (Feed from service parameters)
+    private void _saxFullInfoFeed(CompositeMetadata rssFeed, Boolean isCheck, String defaultLengthValueAsString, String defaultLifeTime)
+    {
+        String nbItemsAsString = rssFeed.getString("length");
+        if (StringUtils.isEmpty(nbItemsAsString))
+        {
+            nbItemsAsString = defaultLengthValueAsString;
+        }
+        long nbItems = Long.parseLong(nbItemsAsString);
+        String lifeTime = rssFeed.getString("cache", defaultLifeTime);
+        String url = rssFeed.getString("url");
+        String name = rssFeed.getString("title");
+        
+        _callFeedGenerator(nbItems, url, name, lifeTime, false, isCheck);
+    }
+    
+    // Sax all info from the feed for the service (Feed from user preference)
+    private void _saxFullInfoFeedCustom(CompositeMetadata serviceParameters, String url, String defaultLengthValueAsString, Boolean isCheck)
+    {
+        String lifeTime = serviceParameters.getString("cache");
+        _callFeedGenerator(Long.parseLong(defaultLengthValueAsString), url, "", lifeTime, true, isCheck);
+    }
+    
+    // Call the pipeline to sax all information from a feed
+    private void _callFeedGenerator(long length, String url, String name, String lifeTime, Boolean isCustom, Boolean isCheck)
+    {
+        SitemapSource sitemapSource = null;
+        try
+        {
+            HashMap<String, Object> parameter = new HashMap<String, Object>();
+            parameter.put("length", length);
+            parameter.put("url", url);
+            parameter.put("name", name);
+            parameter.put("cache", _lifeTimes.get(lifeTime));
+            parameter.put("isCustom", isCustom);
+            parameter.put("isCheck", isCheck);
+            
+            sitemapSource = (SitemapSource) _resolver.resolveURI("cocoon:/feed", null, parameter);
+            sitemapSource.toSAX(new IgnoreRootHandler(contentHandler));
+        }
+        catch (Exception e)
+        {
+            getLogger().error("There is an error in the flux : '" + url + "'");
+        }
+        finally
+        {
+            _resolver.release(sitemapSource);
+        }
+    }
+    
+    // Get the list of url from the user preferences
+    private List<String> _getListUrl(String login, String storageContext, Map<String, String> contextVars) throws UserPreferencesException, UnsupportedEncodingException
+    {
+        String urls = _userPrefManager.getUserPreferenceAsString(login, storageContext, contextVars, USER_PREF_RSS_URL_KEY);
+        List<String> urlAsList = new ArrayList<String>();
+        if (StringUtils.isNotEmpty(urls))
+        {
+            String[] urlsAsArray = StringUtils.split(urls, ",");
+            for (String url : urlsAsArray)
+            {
+                urlAsList.add(URLDecoder.decode(url, "utf-8"));
+            }
+        }
+          
+        return urlAsList;
+    }
+    
+    // Get the list of chosen feed (position) from the user preferences
+    private List<String> _getListPosition(String login, String storageContext, Map<String, String> contextVars) throws UserPreferencesException
+    {
+        String positions = _userPrefManager.getUserPreferenceAsString(login, storageContext, contextVars, USER_PREF_RSS_POSITION_KEY);
+        if (StringUtils.isNotEmpty(positions))
+        {
+            String[] positionsAsArray = StringUtils.split(positions, ",");
+            return Arrays.asList(positionsAsArray);
+        }
+        
+        return new ArrayList<String>();
+    }
+    
+    // prefilter before sax the feeds
+    private List<CompositeMetadata> _filterAndPreloadCacheMetadata(Request request, CompositeMetadata rssFeeds)
+    {
+        List<CompositeMetadata> listFeeds = new ArrayList<CompositeMetadata>();
+        
+        String[] metadataNames = rssFeeds.getMetadataNames();
+        Arrays.sort(metadataNames, new Comparator<String>() {
+            public int compare(String o1, String o2)
+            {
+                return Integer.parseInt(o1) - Integer.parseInt(o2);
+            }
+        });
+        
+        for (String name : metadataNames)
+        {
+            CompositeMetadata rssFeed = rssFeeds.getCompositeMetadata(name);
+
+            Boolean access = _checkUserAccessRSS(request, rssFeed);
+            if (access)
+            {
+                listFeeds.add(rssFeed);
+            }
+        }
+        
+        return listFeeds;
+    }
+
+    // Check if the user can access to the feed
+    private boolean _checkUserAccessRSS(Request request, CompositeMetadata rssFeed)
+    {
+        if (_renderingContext.getRenderingContext() == RenderingContext.FRONT)
+        {
+            Page page = (Page) request.getAttribute(Page.class.getName());
+            
+            String login = (String) request.getAttribute(WebConstants.FO_LOGIN);
+            
+            String foGroup = rssFeed.getString("fo-group");
+            String foUser = rssFeed.getString("fo-user");
+            Boolean limited = rssFeed.getBoolean("limited");
+            if (limited)
+            {
+                if (page == null)
+                {
+                    return false;
+                }
+                return _accessManager.getAccessInfo(page).isRestricted();
+            }
+            else 
+            {
+                if (login == null)
+                {
+                    return false;
+                }
+                
+                if (StringUtils.isEmpty(foUser) && StringUtils.isEmpty(foGroup))
+                {
+                    return true;
+                }
+                
+                List<String> userList = new ArrayList<String>();
+                if (StringUtils.isNotEmpty(foUser))
+                {
+                    userList = Arrays.asList(StringUtils.split(foUser, ","));
+                    if (userList.contains(login))
+                    {
+                        return userList.contains(login);
+                    }
+                }
+                
+                if (StringUtils.isNotEmpty(foGroup))
+                {
+                    for (String group : StringUtils.split(foGroup, ","))
+                    {
+                        if (_groupManager.getGroup(group).getUsers().contains(login))
+                        {
+                            return true;
+                        }
+                        
+                    }
+                }
+                
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    private Map<String, String> _getContextVars(String siteName, String language)
+    {
+        Map<String, String> contextVars = new HashMap<String, String>();
+        
+        contextVars.put(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME, siteName);
+        contextVars.put(FOUserPreferencesConstants.CONTEXT_VAR_LANGUAGE, language);
+        
+        return contextVars;
+    }
+}
+
+
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/CreateUserConfAction.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/CreateUserConfAction.java	(revision 0)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/CreateUserConfAction.java	(revision 0)
@@ -0,0 +1,152 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.syndication;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.runtime.plugins.core.userpref.UserPreferencesManager;
+import org.ametys.runtime.util.I18nizableText;
+import org.ametys.runtime.util.URLEncoder;
+import org.ametys.runtime.util.cocoon.ActionResultGenerator;
+import org.ametys.web.WebConstants;
+import org.ametys.web.repository.page.Page;
+import org.ametys.web.repository.page.ZoneItem;
+import org.ametys.web.userpref.FOUserPreferencesConstants;
+
+
+/**
+ * Set user preference
+ */
+public class CreateUserConfAction extends ServiceableAction
+{
+    /** The user preferences manager. */
+    protected UserPreferencesManager _userPrefManager;
+    
+    /** The Ametys resolver */
+    protected AmetysObjectResolver _resolver;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _userPrefManager = (UserPreferencesManager) serviceManager.lookup(UserPreferencesManager.ROLE + ".FO");
+        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        Map<String, Object> result = new HashMap<String, Object>();
+        List<I18nizableText> errors = new ArrayList<I18nizableText>();
+
+        String login = (String) request.getAttribute(WebConstants.FO_LOGIN);
+        
+        String zoneItemId = request.getParameter("zone-item-id");
+        ZoneItem zoneItem = _resolver.resolveById(zoneItemId);
+   
+        Page page = zoneItem.getZone().getPage();
+        String language = page.getSitemapName();
+        String siteName = page.getSiteName();
+        
+        String storageContext = siteName + "/" + language + "/" + zoneItem.getId();
+        Map<String, String> contextVars = _getContextVars(siteName, language);
+        
+        Map<String, String> values = new HashMap<String, String>();
+        String positions = _getPositions(request);
+        
+        int nbMaxCheck = Integer.parseInt(request.getParameter("nbMaxCheck"));
+        if (StringUtils.split(positions, ",").length <= nbMaxCheck || nbMaxCheck == 0)
+        {
+            values.put(FeedsGenerator.USER_PREF_RSS_POSITION_KEY, _getPositions(request));
+            values.put(FeedsGenerator.USER_PREF_RSS_URL_KEY, _getUrls(request));
+            
+            _userPrefManager.setUserPreferences(login, storageContext, contextVars, values);
+        }
+        else
+        {
+            errors.add(new I18nizableText("plugin.syndication", "SYNDICATION_SERVICE_CONFIG_RSS_ERROR_TO_MANY_CHECK"));
+        }
+        
+        result.put("error", errors);
+        
+        request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, result);
+        
+        return EMPTY_MAP;
+    }
+    
+    private String _getUrls(Request request)
+    {
+        int nbFeedUser = Integer.parseInt(request.getParameter("nbMaxUser"));
+
+        int nbFeed = Integer.parseInt(request.getParameter("nbFeedService"));
+        int position = nbFeed + 1;
+        
+        List<String> urls = new ArrayList<String>();
+        for (int i = 0; i < nbFeedUser; i++)
+        {
+            String url = request.getParameter("feed-url-" + (position + i));
+            urls.add(URLEncoder.encode(url));
+        }
+        
+        return StringUtils.join(urls, ",");
+    }
+    
+    private String _getPositions(Request request)
+    {
+        int nbFeed = Integer.parseInt(request.getParameter("nbFeedService"));
+        int nbFeedUser = Integer.parseInt(request.getParameter("nbMaxUser"));
+
+        List<Integer> positions = new ArrayList<Integer>();
+        for (int i = 1; i <= (nbFeed + nbFeedUser); i++)
+        {
+            String checked = request.getParameter("feed-check-" + i);
+            if (!StringUtils.isEmpty(checked))
+            {
+                if (checked.equals("true") || checked.equals("on"))
+                {
+                    positions.add(i);
+                }
+            }
+        }
+        
+        return StringUtils.join(positions, ",");
+    }
+    
+    private Map<String, String> _getContextVars(String siteName, String language)
+    {
+        Map<String, String> contextVars = new HashMap<String, String>();
+        
+        contextVars.put(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME, siteName);
+        contextVars.put(FOUserPreferencesConstants.CONTEXT_VAR_LANGUAGE, language);
+        
+        return contextVars;
+    }
+
+}
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/ConfAccessComponent.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/ConfAccessComponent.java	(revision 0)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/ConfAccessComponent.java	(revision 0)
@@ -0,0 +1,120 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.syndication;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.plugins.repository.metadata.CompositeMetadata;
+import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType;
+import org.ametys.web.pageaccess.PageAccessManager;
+import org.ametys.web.renderingcontext.RenderingContext;
+import org.ametys.web.renderingcontext.RenderingContextHandler;
+import org.ametys.web.repository.page.Page;
+
+
+/**
+ *  Component for access to the user configuration for feeds
+ */
+public class ConfAccessComponent implements Component, Serviceable
+{
+    /** Avalon ROLE. */
+    public static final String ROLE = ConfAccessComponent.class.getName();
+ 
+    /** The page access manager */
+    protected PageAccessManager _accessManager;
+    
+    /** The rendering context handler. */
+    protected RenderingContextHandler _renderingContext;
+    
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _accessManager = (PageAccessManager) manager.lookup(PageAccessManager.ROLE);
+        _renderingContext = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE);
+
+    }
+    
+    /**
+     * Say if the preference forms for RSS Feeds is showed
+     * @param page
+     * @param nbFeedPerso
+     * @param nbFeeds
+     * @param nbMaxFeed
+     * @return true if the preference forms for RSS Feeds is showed
+     */
+    public boolean showPreferenceForm(Page page, String nbFeedPerso, long nbFeeds, long nbMaxFeed)
+    {
+        if (_renderingContext.getRenderingContext() == RenderingContext.FRONT)
+        {
+            if (page == null)
+            {
+                return false;
+            }
+            
+            return _accessManager.getAccessInfo(page).isRestricted() && _hasCustomsFeeds(nbFeedPerso, nbFeeds, nbMaxFeed);
+        }
+        
+        return false;
+    }
+
+    /**
+     * Check if the service for feeds is cacheable
+     * @param repeater
+     * @param nbFeedPerso
+     * @param nbFeeds
+     * @param nbMaxFeed
+     * @return true if the service is Cacheable
+     */
+    public boolean isServiceCacheable(CompositeMetadata repeater, String nbFeedPerso, int nbFeeds, int nbMaxFeed)
+    {
+        for (String name : repeater.getMetadataNames())
+        {
+            if (repeater.getType(name) == MetadataType.COMPOSITE)
+            {
+                CompositeMetadata feed = repeater.getCompositeMetadata(name);
+                Boolean limited = feed.getBoolean("limited");
+                String foUser = feed.getString("fo-user");
+                String foGroup = feed.getString("fo-group");
+                
+                if (limited || StringUtils.isNotEmpty(foUser) || StringUtils.isNotEmpty(foGroup))
+                {
+                    return false;
+                }
+            }
+        }
+        
+        return !_hasCustomsFeeds(nbFeedPerso, nbFeeds, nbMaxFeed);
+    }
+    
+    private boolean _hasCustomsFeeds(String nbFeedPerso, long nbFeeds, long nbMaxFeed)
+    {
+        if (StringUtils.isNotEmpty(nbFeedPerso) && Integer.parseInt(nbFeedPerso) != 0)
+        {
+            return true;
+        }
+        
+        if (nbMaxFeed < nbFeeds)
+        {
+            return true;
+        }
+        
+        return false;
+    }
+}
+
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/FeedGenerator.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/FeedGenerator.java	(revision 28681)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/FeedGenerator.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  Copyright 2015 Anyware Services
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -21,19 +21,26 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.cocoon.ProcessingException;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Request;
-import org.apache.cocoon.generation.AbstractGenerator;
+import org.apache.cocoon.generation.ServiceableGenerator;
 import org.apache.cocoon.xml.AttributesImpl;
 import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.lang.StringUtils;
 import org.xml.sax.SAXException;
 
 import org.ametys.runtime.config.Config;
 import org.ametys.runtime.util.I18nizableText;
+import org.ametys.web.renderingcontext.RenderingContext;
+import org.ametys.web.renderingcontext.RenderingContextHandler;
 import org.ametys.web.repository.page.Page;
 import org.ametys.web.repository.page.ZoneItemException;
 import org.ametys.web.repository.site.Site;
@@ -60,51 +67,91 @@
  * Generates the contents of an external RSS of Atom feed.<br>
  * This implementation is based on the ROME API.
  */
-public class FeedGenerator extends AbstractGenerator
+public class FeedGenerator extends ServiceableGenerator
 {
+    /** The feed cache */
+    protected FeedCache _feedCache;
+    
+    /** The rendering context handler. */
+    protected RenderingContextHandler _renderingContext;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _feedCache = (FeedCache) serviceManager.lookup(FeedCache.ROLE);
+        _renderingContext = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE);
+    }
     
     @Override
     public void generate() throws IOException, SAXException, ProcessingException
     {
-        // Check if feed url is not the page itself
-        if (_checkForInfiniteLoop(source))
-        {
-            throw new ZoneItemException("Infinite loop detected for RSS feed : " + source + ". The RSS feed url can not be the page itself.");
-        }
-        
-        URL feedSource = new URL(source);
-        HttpURLConnection connection = (HttpURLConnection) feedSource.openConnection();
-        connection.setRequestProperty("User-Agent", "Ametys/3 (compatible; MSIE 6.0)");
-        
-        int length = parameters.getParameterAsInteger("length", -1);
-        
-        SyndFeedInput input = new SyndFeedInput();
-        SyndFeed feed = null;
+        Map<String, Object> serviceParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
+        String url = (String) serviceParameters.get("url");
+        String name = (String) serviceParameters.get("name");
+        int lifeTime = (Integer) serviceParameters.get("cache");
+        long length = (Long) serviceParameters.get("length");
+        Boolean isCustom = (Boolean) serviceParameters.get("isCustom");
+        Boolean isCheck = (Boolean) serviceParameters.get("isCheck");
         
-        try
+        contentHandler.startDocument();
+
+        if (_checkForInfiniteLoop(url))
         {
-            int responseCode = connection.getResponseCode();
-            if (responseCode != HttpURLConnection.HTTP_OK)
+            getLogger().error("Infinite loop detected for RSS feed : " + url + ". The RSS feed url can not be the page itself.");
+            if (isCustom && _renderingContext.getRenderingContext() == RenderingContext.FRONT)
             {
-                throw new ZoneItemException("Unable to join the RSS feed : " + source + ". HTTP response code is " + responseCode + ".");
+                _saxErrorFeed("feed-error-custom", url, name, null);
             }
-
-            feed = input.build(new XmlReader(connection));
+            else if (_renderingContext.getRenderingContext() == RenderingContext.BACK)
+            {
+                _saxErrorFeed("feed-error", url, name, "Infinite loop detected for RSS feed : " + url + ". The RSS feed url can not be the page itself.");
+            }
         }
-        catch (FeedException e)
+        else
         {
-            throw new ZoneItemException("Unable to read the RSS feed : " + source, e);
+            FeedResult feedResult = _feedCache.getFeed(url, lifeTime);
+            
+            if (feedResult.getStatus() == FeedResult.STATUS_OK)
+            {
+                SyndFeed feed = feedResult.getSynFeed();
+                _saxFeeds(feed, length, url, isCustom, isCheck);
+            }
+            else if (_renderingContext.getRenderingContext() == RenderingContext.BACK)
+            {
+                _saxErrorFeed("feed-error", url, name, feedResult.getMessageError());
+            }
+            else if (isCustom && _renderingContext.getRenderingContext() == RenderingContext.FRONT)
+            {
+                _saxErrorFeed("feed-error-custom", url, name, null);
+            }
         }
-        catch (IOException e)
+
+        contentHandler.endDocument();
+    }
+    
+    private void _saxErrorFeed (String errorName, String url, String name, String messageError) throws SAXException
+    {
+        AttributesImpl atts = new AttributesImpl();
+        _addAttribute(atts, "url", url);
+        _addAttribute(atts, "name", name);
+        if (StringUtils.isNotEmpty(messageError))
         {
-            throw new ZoneItemException("Unable to read the RSS feed to the url : " + source, e);
+            _addAttribute(atts, "messageError", messageError);
         }
-        
-        contentHandler.startDocument();
+
+        XMLUtils.createElement(contentHandler, errorName, atts);
+    }
+    
+    private void _saxFeeds (SyndFeed feed, long length, String url, Boolean isCustom, Boolean isCheck) throws SAXException
+    {
         AttributesImpl atts = new AttributesImpl();
 
         _addAttribute(atts, "title", feed.getTitle());
         _addAttribute(atts, "description", feed.getDescription());
+        _addAttribute(atts, "feedUrl", url);
+        _addAttribute(atts, "isCustom", String.valueOf(isCustom));
+        _addAttribute(atts, "isCheck", String.valueOf(isCheck));
 
         XMLUtils.startElement(contentHandler, "feed", atts);
         
@@ -174,7 +221,6 @@
         }
         
         XMLUtils.endElement(contentHandler, "feed");
-        contentHandler.endDocument();
     }
     
     private void _saxEnclosures (SyndEntry entry) throws SAXException
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/FeedResult.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/FeedResult.java	(revision 0)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/FeedResult.java	(revision 0)
@@ -0,0 +1,140 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.syndication;
+
+import java.util.Date;
+
+import com.sun.syndication.feed.synd.SyndFeed;
+
+/**
+ * A feed retrieve result.
+ */
+public class FeedResult
+{
+    
+    /** The status is unknown. */
+    public static final int STATUS_UNKNOWN = 0;
+    
+    /** Status indicating that the feed was retrieved successfully. */
+    public static final int STATUS_OK = 1;
+    
+    /** Status indicating that there was an error retrieving the feed. */
+    public static final int STATUS_ERROR = 2;
+    
+    /** The status. */
+    protected int _status;
+    
+    /** The feed. */
+    protected SyndFeed _feed;
+    
+    /** The message error. */
+    protected String _messageError;
+    
+    /** The creation date. */
+    protected Date _creationDate;
+    
+    /**
+     * Build a FeedResult object, status unknown.
+     */
+    public FeedResult()
+    {
+        this(STATUS_UNKNOWN, null);
+    }
+    
+    /**
+     * Build a FeedResult object.
+     * @param status the status.
+     * @param feed the feed.
+     */
+    public FeedResult(int status, SyndFeed feed)
+    {
+        this._status = status;
+        this._feed = feed;
+    }
+
+    /**
+     * Get the status.
+     * @return the status
+     */
+    public int getStatus()
+    {
+        return _status;
+    }
+
+    /**
+     * Set the status.
+     * @param status the status to set
+     */
+    public void setStatus(int status)
+    {
+        this._status = status;
+    }
+
+    /**
+     * Get the feed.
+     * @return the feed
+     */
+    public SyndFeed getSynFeed()
+    {
+        return _feed;
+    }
+    
+    /**
+     * Get the message error.
+     * @return the message error
+     */
+    public String getMessageError()
+    {
+        return _messageError;
+    }
+    
+    /**
+     * Set the message error.
+     * @param messageError the message error to set
+     */
+    public void setMessageError(String messageError)
+    {
+        this._messageError = messageError;
+    }
+
+    /**
+     * Set the feed.
+     * @param feed the feed to set
+     */
+    public void setResponse(SyndFeed feed)
+    {
+        this._feed = feed;
+    }
+    
+    /**
+     * Get the creation date.
+     * @return the creation date
+     */
+    public Date getCreationDate()
+    {
+        return _creationDate;
+    }
+
+    /**
+     * Set the creation Date.
+     * @param creationDate the creation date to set
+     */
+    public void setCreationDate(Date creationDate)
+    {
+        this._creationDate = creationDate;
+    }
+    
+}
Index: main/plugin-syndication/src/org/ametys/plugins/syndication/FeedCache.java
===================================================================
--- main/plugin-syndication/src/org/ametys/plugins/syndication/FeedCache.java	(revision 0)
+++ main/plugin-syndication/src/org/ametys/plugins/syndication/FeedCache.java	(revision 0)
@@ -0,0 +1,349 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.syndication;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceException;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.sun.syndication.feed.synd.SyndFeed;
+import com.sun.syndication.io.FeedException;
+import com.sun.syndication.io.SyndFeedInput;
+import com.sun.syndication.io.XmlReader;
+
+/**
+ * Feed cache, supports preloading multiple feeds in multiple concurrent threads.
+ */
+public class FeedCache extends AbstractLogEnabled implements Component, Initializable
+{
+    /** The component role. */
+    public static final String ROLE = FeedCache.class.getName();
+    
+    /** The user information cache. */
+    protected LoadingCache<String, FeedResult> _cache;
+    
+    @Override
+    public void initialize() throws Exception
+    {
+        FeedCacheLoader loader = new FeedCacheLoader();
+        
+        CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder()
+                .expireAfterWrite(24, TimeUnit.HOURS);
+        
+        _cache = cacheBuilder.build(loader);
+    }
+    
+    /**
+     * Pre-load a collection of feeds, one by thread, to avoid getting timeouts sequentially.
+     * @param feeds the feeds to preload.
+     */
+    public void preload(Collection<String> feeds)
+    {
+        if (getLogger().isDebugEnabled())
+        {
+            getLogger().debug("Preloading " + feeds.size() + " feeds...");
+        }
+        
+        long start = System.currentTimeMillis();
+        
+        // Synchronization helper.
+        CountDownLatch doneSignal = new CountDownLatch(feeds.size());
+        
+        for (String feed : feeds)
+        {
+            // Test if the feed is already in the cache.
+            FeedResult result = _cache.getIfPresent(feed);
+            
+            if (result == null)
+            {
+                // The feed is not in the cache: prepare and launch a thread to retrieve it.
+                FeedLoadWorker worker = new FeedLoadWorker(feed, doneSignal);
+                
+                try
+                {
+                    worker.initialize(getLogger());
+                }
+                catch (Exception e)
+                {
+                    // Ignore
+                }
+                
+                new Thread(worker).start();
+            }
+            else
+            {
+                // The feed is already in the cache: count down.
+                doneSignal.countDown();
+            }
+        }
+        
+        try
+        {
+            // Wait for all the URLs to be loaded (i.e. all the threads to end).
+            doneSignal.await();
+            
+            if (getLogger().isDebugEnabled())
+            {
+                long end = System.currentTimeMillis();
+                getLogger().debug("Feed preloading ended in " + (end - start) + " millis.");
+            }
+        }
+        catch (InterruptedException e)
+        {
+            // Ignore, let the threads finish or die.
+        }
+    }
+    
+    /**
+     * Get a feed not from the cache.
+     * @param feedUrl the feed.
+     * @return the feed result.
+     * @throws IOException 
+     */
+    public FeedResult getFeedNoCache(String feedUrl) throws IOException
+    {
+        return loadFeed(feedUrl);
+    }
+    
+    /**
+     * Get a feed.
+     * @param feedUrl the feed.
+     * @param lifeTime
+     * @return the feed response.
+     */
+    public FeedResult getFeed(String feedUrl, int lifeTime)
+    {
+        try
+        {
+            FeedResult feedResult = _cache.get(feedUrl);
+            Date dateFeed = feedResult.getCreationDate();
+            
+            GregorianCalendar calendar = new GregorianCalendar();
+            calendar.setTime(dateFeed);
+            
+            calendar.add(Calendar.MINUTE, lifeTime);
+            
+            Date dateToday = new Date();
+            if (dateToday.after(calendar.getTime()))
+            {
+                _cache.invalidate(feedUrl);
+                return _cache.get(feedUrl);
+            }
+            else
+            {
+                return feedResult;
+            }
+        }
+        catch (ExecutionException e)
+        {
+            getLogger().error(e.getLocalizedMessage());
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Retrieve a feed's content to store it into the cache.
+     * @param feedUrl the feed to load.
+     * @return the feed content.
+     * @throws IOException 
+     */
+    protected FeedResult loadFeed(String feedUrl) throws IOException
+    {
+        FeedResult result = new FeedResult();
+        
+        Date todayDate = new Date();
+        result.setCreationDate(todayDate);
+        
+        SyndFeedInput input = new SyndFeedInput();
+        SyndFeed synFeed = null;
+        
+        try
+        {
+            URL feedSource = new URL(feedUrl);
+            HttpURLConnection connection = (HttpURLConnection) feedSource.openConnection();
+            connection.setRequestProperty("User-Agent", "Ametys/3 (compatible; MSIE 6.0)");
+
+            int responseCode = connection.getResponseCode();
+            if (responseCode != HttpURLConnection.HTTP_OK)
+            {
+                result.setStatus(FeedResult.STATUS_ERROR);
+                result.setMessageError("Unable to join the RSS feed : " + feedUrl + ". HTTP response code is " + responseCode + ".");
+                getLogger().error("Unable to join the RSS feed : " + feedUrl + ". HTTP response code is " + responseCode + ".");
+                return result;
+            }
+
+            synFeed = input.build(new XmlReader(connection));
+            result.setStatus(FeedResult.STATUS_OK);
+            result.setResponse(synFeed);
+        }
+        catch (FeedException e)
+        {
+            result.setStatus(FeedResult.STATUS_ERROR);
+            result.setMessageError(e.getLocalizedMessage());
+            getLogger().error("Unable to read the RSS feed to the url : " + feedUrl);
+        }
+        catch (MalformedURLException e)
+        {
+            result.setStatus(FeedResult.STATUS_ERROR);
+            result.setMessageError(e.getLocalizedMessage());
+            getLogger().error("Unable to read the RSS feed to the url : " + feedUrl + ". Malformed url");
+        }
+        catch (IOException e)
+        {
+            result.setStatus(FeedResult.STATUS_ERROR);
+            result.setMessageError(e.getLocalizedMessage());
+            getLogger().error("Unable to read the RSS feed to the url : " + feedUrl);
+        }
+        catch (Exception e)
+        {
+            result.setStatus(FeedResult.STATUS_ERROR);
+            result.setMessageError(e.getLocalizedMessage());
+            getLogger().error("Unable to read the RSS feed to the url : " + feedUrl);
+        }
+        
+        return result;
+    }
+    
+    /**
+     * The feed cache loader, delegates the loading to the component's loadFeed method (to be easily overridable).
+     */
+    protected class FeedCacheLoader extends CacheLoader<String, FeedResult>
+    {
+        @Override
+        public FeedResult load(String feedUrl) throws Exception
+        {
+            return loadFeed(feedUrl);
+        }
+    }
+    
+    /**
+     * Runnable loading an URL into the cache.
+     */
+    protected class FeedLoadWorker implements Runnable
+    {
+        
+        /** The logger. */
+        protected Logger _logger;
+        
+        /** Is the engine initialized ? */
+        protected boolean _initialized;
+        
+        private final CountDownLatch _endSignal;
+        
+        private final String _feedUrl;
+        
+        /**
+         * Build a worker loading a specific feed.
+         * @param feedUrl the feed to load.
+         * @param endSignal the signal when done.
+         */
+        FeedLoadWorker(String feedUrl, CountDownLatch endSignal)
+        {
+            this._feedUrl = feedUrl;
+            this._endSignal = endSignal;
+        }
+        
+        /**
+         * Initialize the feed loader.
+         * @param logger the logger.
+         * @throws ContextException
+         * @throws ServiceException
+         */
+        public void initialize(Logger logger) throws ContextException, ServiceException
+        {
+            _logger = logger;
+            _initialized = true;
+        }
+                
+        public void run()
+        {
+            try
+            {
+                if (_logger.isDebugEnabled())
+                {
+                    _logger.debug("Preparing to load the URL '" + _feedUrl + "'");
+                }
+                
+                long start = System.currentTimeMillis();
+                
+                _checkInitialization();
+                
+                // Load the feed and signal that it's done.
+                _cache.get(_feedUrl);
+                
+                if (_logger.isDebugEnabled())
+                {
+                    long end = System.currentTimeMillis();
+                    _logger.debug("URL '" + _feedUrl + "' was loaded successfully in " + (end - start) + " millis.");
+                }
+            }
+            catch (Exception e)
+            {
+                _logger.error("An error occurred loading the URL '" + _feedUrl + "'", e);
+            }
+            finally
+            {
+                // Signal that the worker has finished. 
+                _endSignal.countDown();
+                
+                // Dispose of the resources.
+                _dispose();
+            }
+        }
+        
+        /**
+         * Check the initialization and throw an exception if not initialized.
+         */
+        protected void _checkInitialization()
+        {
+            if (!_initialized)
+            {
+                String message = "Le composant de synchronisation doit être initialisé avant d'être lancé."; //TODO
+                _logger.error(message);
+                throw new IllegalStateException(message);
+            }
+        }
+        
+        /**
+         * Dispose of the resources and looked-up components.
+         */
+        protected void _dispose()
+        {
+            _initialized = false;
+        }
+        
+    }
+
+}
Index: main/plugin-syndication/resources/css/bo-error.css
===================================================================
--- main/plugin-syndication/resources/css/bo-error.css	(revision 0)
+++ main/plugin-syndication/resources/css/bo-error.css	(revision 0)
@@ -0,0 +1,21 @@
+/*
+ *  Copyright 2015 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+ 
+ .syndication-feed-error {
+    border-style: dashed;    
+    border-width: 1px;
+    padding: 2px;
+ }
\ No newline at end of file
Index: ivy.xml
===================================================================
--- ivy.xml	(revision 28681)
+++ ivy.xml	(working copy)
@@ -40,7 +40,7 @@
     <dependency org="org.ametys.tools" name="tools" rev="1.1.8" conf="deliver->default"/>
     <dependency org="atlassian" name="clover" rev="2.6.3" conf="deliver->default"/>
     
-  	<dependency org="org.ametys.web" name="web" branch="3.3.x" rev="latest.integration" conf="default->default;runtime->runtime;resources->resources"/>
+  	<dependency org="org.ametys.web" name="web" branch="3.6.x" rev="latest.integration" conf="default->default;runtime->runtime;resources->resources"/>
   	<dependency org="rome" name="rome" rev="1.0" conf="default->default"/>
     <dependency org="rome" name="rome-modules" rev="1.0" conf="default->default"/>
 
