Index: main/plugin-core/i18n/messages_en.xml
===================================================================
--- main/plugin-core/i18n/messages_en.xml	(revision 31811)
+++ main/plugin-core/i18n/messages_en.xml	(working copy)

@@ -202,43 +240,90 @@
 	<!-- ADMINITRATOR SYSTEM -->
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_LABEL">Access</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION">System announcement</message>
+	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION_ON">The announcement is active.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION_OFF">The announcement is deactivated.</message>
 	<!-- message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION">System announcement and maintenance mode</message -->
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_TITLE">Users access</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_HELP">Help</message>
@@ -253,8 +338,13 @@
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_MESSAGE">Message</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT">Announcement</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD">Add</message>
+	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD_DESC">Add a message to the list.</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT">Modify</message>
+	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT_DESC">Modify the selected message.</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE">Delete</message>
+	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE_DESC">Delete the selected message.</message>
+	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_TOGGLE_LABEL">Activate/Deactivate</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_TOGGLE_DESC">Activates or deactivates the welcome announcement. If the button is blue, the announce is activated.</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE_CONFIRM">Do you really want to delete this announcement ?</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_CAPTION">Announcement...</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_OK">Ok</message>
@@ -265,7 +355,10 @@
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_LANG">The language always consist always of two letters.\nFor example: 'en' for english or 'fr' for french.</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_MESSAGE">The message box cannot be empty.</message>
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR">Error</message>
-	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE">An error occurred on the server. Modifications have not been saved.</message>
+	<message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE">An error occurred on the server. The modifications on the welcome announcement have not been saved.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_CONFIRM_DIALOG_TITLE">Replace the existing message</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_CONFIRM_DIALOG_TEXT">A message already exists in the selected language, do you want to replace it ?</message>
+
 
 	<!-- ADMINISTRATOR SUPER USER -->
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SUPERUSER_LABEL">Super user</message>

Index: main/plugin-core/i18n/messages_fr.xml
===================================================================
--- main/plugin-core/i18n/messages_fr.xml	(revision 31811)
+++ main/plugin-core/i18n/messages_fr.xml	(working copy)
@@ -200,43 +233,80 @@ 
     <!-- ADMINITRATOR SYSTEM -->
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_LABEL">Accès</message>
-    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION">Annonce utilisateur</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION">Cet outil permet de gérer les annonces d'accueil dans plusieurs langues.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION_ON">L'annonce est activée.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION_OFF">L'annonce est désactivée.</message>
     <!-- message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION">Annonce utilisateur et mode maintenance</message-->
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_TITLE">Accès des utilisateurs</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_HELP">Aide</message>
@@ -251,8 +321,13 @@
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_MESSAGE">Message</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT">Annonce</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD">Ajouter</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD_DESC">Ajouter un message d'accueil à la liste.</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT">Modifier</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT_DESC">Modifier le message d'accueil sélectionné.</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE">Supprimer</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE_DESC">Supprimer le message d'accueil sélectionné.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_TOGGLE_LABEL">Activer/Désactiver</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_TOGGLE_DESC">Active ou désactive l'annonce d'accueil. Si le bouton est bleuté, l'annonce est activée.</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE_CONFIRM">Etes-vous sûr de vouloir supprimer cette annonce ?</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_CAPTION">Annonce...</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_OK">Ok</message>
@@ -263,7 +338,10 @@
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_LANG">Le code de langue se compose toujours de 2 lettres.\nPar exemple : fr pour le français ou en pour l'anglais.</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_MESSAGE">Le message doit être non vide.</message>
     <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR">Erreur</message>
-    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE">Une erreur est survenue sur le serveur. Les modifications n'ont pas été enregistrées.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE">Une erreur est survenue sur le serveur. Les modifications de l'annonce d'accueil n'ont pas été enregistrées.</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_CONFIRM_DIALOG_TITLE">Remplacer le message existant ?</message>
+    <message key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_CONFIRM_DIALOG_TEXT">Un message existe déjà dans la langue sélectionnée, souhaitez-vous le remplacer ? </message>
+
 
 	<!-- ADMINISTRATOR SUPER USER -->
 	<message key="PLUGINS_CORE_ADMINISTRATOR_SUPERUSER_LABEL">Super utilisateur</message>

     <!-- +
          | Workspace
          + -->
Index: main/plugin-core/pages/administrator/system/view.xsl
===================================================================
--- main/plugin-core/pages/administrator/system/view.xsl	(revision 31811)
+++ main/plugin-core/pages/administrator/system/view.xsl	(nonexistent)
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   Copyright 2012 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:i18n="http://apache.org/cocoon/i18n/2.1" 
-                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-    
-    <xsl:param name="contextPath"/>
-    <xsl:param name="pluginName"/>
-    <xsl:param name="workspaceName"/>
-    <xsl:param name="workspaceURI"/>
-    
-    <xsl:variable name="workspaceContext"><xsl:value-of select="$contextPath"/><xsl:value-of select="$workspaceURI"/></xsl:variable>
-    
-    <xsl:variable name="resourcesPath"><xsl:value-of select="$contextPath"/>/plugins/<xsl:value-of select="$pluginName"/>/resources</xsl:variable>
-    
-    <xsl:template match="/System">
-        <html>
-            <head>
-                <title><i18n:text i18n:key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_TITLE"/></title>
-                <link rel="stylesheet" href="{$resourcesPath}/css/administrator/system.css" type="text/css"/>                  
-            </head>
-            
-	        <script>
-	        	<script type="text/javascript" src="{$contextPath}/plugins/{$pluginName}/resources/js/Ametys/plugins/core/administration/System.js"></script>
-	        	     
-				<script type="text/javascript">
-		                   
-               		Ametys.plugins.core.administration.System.initialize ("<xsl:value-of select="$pluginName"/>");
-                   
-                   	var mainPanel = Ametys.plugins.core.administration.System.createPanel ();
-                   
-                   	<xsl:if test="announcements/@state = 'on'">
-                   		Ametys.plugins.core.administration.System._fieldSet.expand();
-                   	</xsl:if>
-		                   
-					<xsl:choose>
-	                	<xsl:when test="/System/announcements">
-	                    	<xsl:for-each select="/System/announcements/announcement">
-	                        	Ametys.plugins.core.administration.System._listView.getStore().addSorted(Ext.create('Ametys.plugins.core.administrator.System.Announce', {
-	                            	lang : "<xsl:choose><xsl:when test="@lang"><xsl:value-of select="@lang"/></xsl:when><xsl:otherwise>*</xsl:otherwise></xsl:choose>",
-	                                message : Ametys.convertHtmlToTextarea("<xsl:value-of select="."/>")})
-	                            );
-							</xsl:for-each>
-						</xsl:when>
-	                    <xsl:otherwise>
-	                    	Ametys.plugins.core.administration.System._listView.getStore().addSorted(Ext.create('Ametys.plugins.core.administrator.System.Announce', { lang : "*", message : "<i18n:text i18n:key="PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DEFAULTMESSAGE"/>"}));
-						</xsl:otherwise>
-					</xsl:choose>
-                    
-					createPanel = function () 
-					{
-						return mainPanel;
-					}
-				</script>
-	        </script>
-	            
-	            	
-            <body></body>
-        </html>
-    </xsl:template>    
-    
-</xsl:stylesheet>
\ No newline at end of file
Index: main/plugin-core/plugin.xml
===================================================================
--- main/plugin-core/plugin.xml	(revision 31811)
+++ main/plugin-core/plugin.xml	(working copy)
 
@@ -1307,174 +1306,1276 @@

+    
+    <feature name="admin.system">
+        <components>
+            <component role="org.ametys.runtime.plugins.core.administrator.system.SystemHelper"
+                       class="org.ametys.runtime.plugins.core.administrator.system.SystemHelper"/>
+        </components>
+        
+        <extensions>
+
+            <!-- +
+                 |  SYSTEM
+                 + -->           
+            <!-- The system contextual tab -->
+            <extension id="org.ametys.runtime.plugins.core.SystemTab"
+                       point="org.ametys.runtime.ribbon.RibbonTabsManager"
+                       class="org.ametys.runtime.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.tab.TabController">
+                    <tool-role>^(uitool-admin-system)$</tool-role>
+                </class>
+            </extension>
+            
+            <!-- The system tool and its factory -->
+            <extension id="org.ametys.runtime.plugins.core.System"  
+                       point="org.ametys.runtime.ribbon.RibbonControlsManager" 
+                       class="org.ametys.runtime.plugins.core.administrator.system.SystemAnnouncementClientSideElement">
+                <!-- 
+                    This interaction allows the administrator to handle the system welcoming message
+                 -->
+                 <class name="Ametys.plugins.core.administration.controller.SystemAnnouncementController">
+                    <opentool-role>uitool-admin-system</opentool-role>
+                    
+                    <label i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_LABEL</label>
+                    <description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION</description>
+                    
+                    <announcement-on-description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION_ON</announcement-on-description>
+                    <announcement-off-description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION_OFF</announcement-off-description>
+                    
+                    <icon-small file="true">img/administrator/system/off_16.png</icon-small>
+                    <icon-medium file="true">img/administrator/system/off_32.png</icon-medium>
+                    <icon-large file="true">img/administrator/system/off_48.png</icon-large>
+
+                    <announcement-on-icon-small file="true">img/administrator/system/on_16.png</announcement-on-icon-small>
+                    <announcement-on-icon-medium file="true">img/administrator/system/on_32.png</announcement-on-icon-medium>
+                    <announcement-on-icon-large file="true">img/administrator/system/on_48.png</announcement-on-icon-large>
+                    
+                    <announcement-off-icon-small file="true">img/administrator/system/off_16.png</announcement-off-icon-small>
+                    <announcement-off-icon-medium file="true">img/administrator/system/off_32.png</announcement-off-icon-medium>
+                    <announcement-off-icon-large file="true">img/administrator/system/off_48.png</announcement-off-icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/core/administration/tool/SystemTool.js</file>
+                    <file>js/Ametys/plugins/core/administration/controller/SystemAnnouncementController.js</file>
+                </scripts>
+            </extension> 
+            
+            <extension id="org.ametys.runtime.plugins.core.system.SystemFactory"
+                       point="org.ametys.runtime.ui.UIToolsFactoriesManager"
+                       class="org.ametys.runtime.ui.StaticClientSideElement">
+                <class name="Ametys.tool.factory.UniqueToolFactory">
+                    <role>uitool-admin-system</role>
+                    <toolClass>Ametys.plugins.core.administration.tool.SystemTool</toolClass>
+                    
+                    <title i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_LABEL</title>
+                    <description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_DESCRIPTION</description>
+                    
+                    <icon-small file="true">img/administrator/system/icon_16x16.png</icon-small>
+                    <icon-medium file="true">img/administrator/system/icon_32x32.png</icon-medium>
+                    <icon-large file="true">img/administrator/system/icon_48x48.png</icon-large>
+                    
+                    <default-location>c</default-location>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/core/administration/tool/LogsTool.js</file>
+                </scripts>
+            </extension>
+            
+            <!-- Actions -->
+            <extension id="org.ametys.runtime.plugins.core.system.Add"
+                       point="org.ametys.runtime.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.ui.ButtonController">
+                    <action>Ametys.plugins.core.administration.actions.SystemActions.addMessage</action>
+                
+                    <label i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD</label>
+                    <description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD_DESC</description>
+
+                    <icon-small file="true">img/administrator/system/add_16.png</icon-small>
+                    <icon-medium file="true">img/administrator/system/add_32.png</icon-medium>
+                    <icon-large file="true">img/administrator/system/add_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/core/administration/actions/SystemActions.js</file>
+                </scripts>
+            </extension>
+            
+            <extension id="org.ametys.runtime.plugins.core.system.Edit"
+                       point="org.ametys.runtime.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.ui.ButtonController">
+                    <action>Ametys.plugins.core.administration.actions.SystemActions.editMessage</action>
+                
+                    <selection-target-type>^system-announcement-message</selection-target-type>
+                
+                    <label i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT</label>
+                    <description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT_DESC</description>
+
+                    <icon-small file="true">img/administrator/system/edit_16.png</icon-small>
+                    <icon-medium file="true">img/administrator/system/edit_32.png</icon-medium>
+                    <icon-large file="true">img/administrator/system/edit_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/core/administration/actions/SystemActions.js</file>
+                </scripts>
+            </extension>
+            
+            <extension id="org.ametys.runtime.plugins.core.system.Delete"
+                       point="org.ametys.runtime.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.ui.ButtonController">
+                    <action>Ametys.plugins.core.administration.actions.SystemActions.deleteMessage</action>
+                
+                    <selection-target-type>^system-announcement-message$</selection-target-type>
+                    <selection-target-parameter>
+                        <name>^language$</name>
+                        <value>!\*</value>
+                    </selection-target-parameter> 
+                
+                    <label i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE</label>
+                    <description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE_DESC</description>
+
+                    <icon-small file="true">img/administrator/system/delete_16.png</icon-small>
+                    <icon-medium file="true">img/administrator/system/delete_32.png</icon-medium>
+                    <icon-large file="true">img/administrator/system/delete_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/core/administration/actions/SystemActions.js</file>
+                </scripts>
+            </extension>
+            
+            <extension id="org.ametys.runtime.plugins.core.system.SetAnnouncement"
+                       point="org.ametys.runtime.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.plugins.core.administrator.system.SystemAnnouncementClientSideElement">
+                <class name="Ametys.plugins.core.administration.controller.SystemAnnouncementController">
+                    <!--initialize>Ametys.plugins.core.administration.actions.SystemActions.initializeToggleState</initialize-->
+                    <action>Ametys.plugins.core.administration.actions.SystemActions.setAnnouncementAvailable</action>
+
+                    <toggle-enabled>true</toggle-enabled>
+                
+                    <label i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_TOGGLE_LABEL</label>
+                    <description i18n="true">PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_TOGGLE_DESC</description>
+
+                    <icon-small file="true">img/administrator/system/toggle_16.png</icon-small>
+                    <icon-medium file="true">img/administrator/system/toggle_32.png</icon-medium>
+                    <icon-large file="true">img/administrator/system/toggle_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/core/administration/actions/SystemActions.js</file>
+                     <file>js/Ametys/plugins/core/administration/controller/SystemAnnouncementController.js</file>
+                </scripts>
+            </extension>
+        </extensions>
+    </feature>
+    
+    
Index: main/plugin-core/resources/js/Ametys/plugins/core/administration/System.js
===================================================================
--- main/plugin-core/resources/js/Ametys/plugins/core/administration/System.js	(revision 31811)
+++ main/plugin-core/resources/js/Ametys/plugins/core/administration/System.js	(nonexistent)
@@ -1,556 +0,0 @@
-/*
- *  Copyright 2012 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.
- */
-
-/**
- * Class to rule the system screen (the one with user announce).
- * See {@link #initialize} and {@link #createPanel}.
- * @private
- */
-Ext.define('Ametys.plugins.core.administration.System', {
-	singleton: true,
-	
-	/**
-	 * @property {String} pluginName The plugin declaring this class
-	 * @private
-	 */
-	/**
-	 * @private
-	 * @property {Ext.Container} _contextualPanel The right panel
-	 */
-	/**
-	 * @private
-	 * @property {Ext.grid.Panel} _listView The list of announces
-	 */
-	/**
-	 * @private
-	 * @property {Ext.form.FieldSet} _fieldSet The main field set around the _listView
-	 */
-	/**
-	 * @private
-	 * @property {Ametys.admin.rightpanel.ActionPanel} _globalActions The global action panel
-	 */
-	/**
-	 * @private
-	 * @property {Ametys.admin.rightpanel.ActionPanel} _actions The announce action panel
-	 */
-	/**
-	 * @private
-	 * @property {String} _mode The current mode when opening the edition dialog box {@link #box}. Can be 'new' or 'edit'.
-	 */
-	/**
-	 * @private
-	 * @property {Boolean} _initialized Determine if the dialog box {@link #box} for editing announce is initialized
-	 */
-	/**
-	 * @private
-	 * @property {Ext.form.Panel} _form The form panel in the dialog box {@link #box} for editing
-	 */
-	/**
-	 * @private
-	 * @property {Ametys.window.DialogBox} box The dialog box for editing
-	 */
-	
-	/**
-	 * Initialize
-	 * @param {String} pluginName The plugin declaring this class
-	 */
-	initialize: function (pluginName)
-	{
-		this.pluginName = pluginName;
-	},
-	
-	/**
-	 * Creates the panel that rules the screen
-	 * @returns {Ext.Panel} The screen
-	 */
-	createPanel: function ()
-	{
-		this._contextualPanel = new Ext.Container({
-			region:'east',
-			
-			border: false,
-			width: 277,
-			
-			cls: 'admin-right-panel',
-		    
-			items: [this._drawGlobalActionsPanel (), 
-			        this._drawActionsPanel (),
-			        this._drawHelpPanel ()
-			]
-		});
-		
-		Ext.define('Ametys.plugins.core.administrator.System.Announce', {
-		    extend: 'Ext.data.Model',
-		    fields: [
-		       {name: 'lang'},
-		       {name: 'message', type: 'string', sortType: Ext.data.SortTypes.asNonAccentedUCString}
-		    ]
-		});
-		
-		this._listView = new Ext.grid.Panel({
-			region: 'center',
-			
-			id: 'list-view-announce',
-			
-			height: '100%',
-			
-		    store : Ext.create('Ext.data.Store', {
-				model: 'Ametys.plugins.core.administrator.System.Announce',
-		        data: { announces: []},
-		        
-		        sortOnLoad: true,
-		        sorters: [ { property: 'lang', direction: "ASC" } ],
-		        
-		        proxy: {
-		        	type: 'memory',
-		        	reader: {
-		        		type: 'json',
-		        		root: 'announces', // FIXME to remove in extjs 6
-		        		rootProperty: 'announces'
-		        	}
-		        }
-			}), 
-		    	
-		    columns: [
-		        {header: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_LANG'/>", width : 80, flex: 0, menuDisabled : true, sortable: true, dataIndex: 'lang', defaultSortable : true},
-		        {header: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_MESSAGE'/>", width : 400, flex: 1, menuDisabled : true, sortable: true, dataIndex: 'message'}
-		    ],
-			
-		    listeners: {'select': Ext.bind(this._selectAnnouncement, this)}
-		});		
-		
-		this._fieldSet = new Ext.form.FieldSet({
-			region:'center',
-			layout: 'border',
-			cls: 'system',
-			
-			collapsed: true,
-			height: 'auto',
-			
-			title : "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_CHECK'/>",
-			checkboxToggle: true,
-			
-			items : [ this._listView ],
-			
-			listeners: { 'collapse': Ext.bind(this._onCollapse, this),
-						 'expand': Ext.bind(this._onExpand, this),
-						 'boxready': Ext.bind(this._onShow, this)}
-
-		});
-		
-		return new Ext.Panel({
-			region: 'center',
-			layout: 'border',
-			
-			cls: 'transparent-panel',
-			border: false,
-			
-			items: [this._fieldSet, this._contextualPanel]
-		});
-	},
-
-	/**
-	 * @private
-	 * Listener on expanding the announce panel
-	 */
-	_onExpand: function()
-	{
-		this._actions.show();
-		this._fieldSet.setHeight('auto');
-	},
-	
-	/**
-	 * @private
-	 * Listener on showing the announce panel
-	 */
-	_onShow: function()
-	{
-		this._fieldSet.setHeight('auto');
-		
-		// OnExpand and OnCollapse is not fired anymore if the component is not rendered
-		if (this._fieldSet.collapsed)
-		{
-			this._actions.hide();
-		}
-	},
-
-	/**
-	 * @private
-	 * Listener on collapsing the announce panel
-	 */
-	_onCollapse: function()
-	{
-		this._actions.hide();
-	},
-
-
-	/**
-	 * Draw the glabal action panel.
-	 * @return {Ametys.admin.rightpanel.ActionPanel} The action panel
-	 * @private
-	 */
-	_drawGlobalActionsPanel: function ()
-	{
-		this._globalActions = new Ametys.admin.rightpanel.ActionPanel({title: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_HANDLE'/>"});
-		
-		// Save
-		this._globalActions.addAction("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_HANDLE_SAVE'/>",
-						 null,
-					     Ametys.getPluginResourcesPrefix(this.pluginName) + '/img/administrator/system/save.png',
-					     Ext.bind(this.save, this));
-		
-		// Quit
-		this._globalActions.addAction("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_STATUS_HANDLE_QUIT'/>", 
-						 null,
-					     Ametys.getPluginResourcesPrefix(this.pluginName) + '/img/administrator/system/quit.png',
-					     Ext.bind(this.goBack, this));
-		
-		return this._globalActions;
-	},
-
-	/**
-	 * Draw the actions panel.
-	 * @return {Ametys.admin.rightpanel.ActionPanel} The action panel
-	 * @private
-	 */
-	_drawActionsPanel: function ()
-	{
-		this._actions = new Ametys.admin.rightpanel.ActionPanel({title: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT'/>"});
-		
-		// Add
-		this._actions.addAction("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_ADD'/>", 
-			    null,
-				Ametys.getPluginResourcesPrefix(this.pluginName) + '/img/administrator/system/add.png', 
-				Ext.bind(this.add, this));
-		
-		// Edit
-		this._actions.addAction("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_EDIT'/>", 
-			    null,
-				Ametys.getPluginResourcesPrefix(this.pluginName) + '/img/administrator/system/edit.png', 
-				Ext.bind(this.edit, this));
-		
-		// Delete
-		this._actions.addAction("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE'/>", 
-			    null,
-				Ametys.getPluginResourcesPrefix(this.pluginName) + '/img/administrator/system/delete.png', 
-				Ext.bind(this.remove, this));
-			
-		this._actions.hideElt(1);
-		this._actions.hideElt(2);
-		
-		return this._actions;
-	},
-
-	/**
-	 * Draw the help panel.
-	 * @return {Ametys.admin.rightpanel.TextPanel} The help panel
-	 * @private
-	 */
-	_drawHelpPanel: function ()
-	{
-		var helpPanel = new Ametys.admin.rightpanel.TextPanel({title: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_HELP'/>"});
-		helpPanel.addText("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_HELP_TEXT'/>");
-		
-		return helpPanel;
-	},
-
-	/**
-	 * @private
-	 * Listener when an announce is selected
-	 */
-	_selectAnnouncement: function ()
-	{
-		var element = this._listView.getSelectionModel().getSelection()[0];
-		if (element == null)
-		{
-			this._actions.hideElt(1);
-			this._actions.hideElt(2);		  
-		}
-		else
-		{
-			this._actions.showElt(1);
-			if (element.get('lang') == '*')
-			{
-				this._actions.hideElt(2);		  
-			}
-			else
-			{
-				this._actions.showElt(2);		      
-			}     
-		}
-	},
-
-	/**
-	 * Quit
-	 */
-	goBack: function ()
-	{
-	    document.location.href = Ametys.WORKSPACE_URI;
-	},
-
-	/**
-	 * Save
-	 */
-	save: function ()
-	{
-		var args = {};
-		args.lang = [];
-		
-		args.announcement = this._fieldSet.checkboxCmp.getValue() ? "true" : "false";
-
-		var elmts = this._listView.getStore().data.items;
-	    for (var i = 0; i < elmts.length; i++)
-	    {
-	        var element = elmts[i];
-	        var lang = element.get('lang');
-	        args.lang.push(element.get('lang'));
-	        args['message_' + element.get('lang')] = Ametys.convertTextareaToHtml(element.get('message'));
-	    }
-	    
-		var result = Ametys.data.ServerComm.send({
-			plugin: this.pluginName, 
-			url: "/administrator/system/update", 
-			parameters: args, 
-			priority: Ametys.data.ServerComm.PRIORITY_SYNCHRONOUS, 
-			callback: null, 
-			responseType: null
-		});
-	    if (Ametys.data.ServerComm.handleBadResponse("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE'/>", result, "Ametys.plugin.core.administration.System.save"))
-	    {
-	       return;
-	    }
-
-	    this.goBack ();
-	},
-
-	/**
-	 * Add a new announcement
-	 */
-	add: function ()
-	{
-		this._mode = 'new';
-		this.act();
-	},
-
-	/**
-	 * Edit an announcement
-	 */
-	edit: function ()
-	{
-		this._mode = 'edit';
-		this.act();
-	},
-
-	/**
-	 * Delete the selected announcement
-	 */
-	remove: function ()
-	{
-		var element = this._listView.getSelectionModel().getSelection()[0];
-		if (element.get('lang') == '*')
-		{
-			return;
-		}
-		
-		Ametys.Msg.confirm ("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE'/>", 
-				         "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DELETE_CONFIRM'/>", 
-				         Ext.bind(this.doRemove, this));
-	},
-	
-	/**
-	 * @private
-	 * Do actually the removal
-	 * @param {String} answer If answer is 'yes' the removal is done
-	 */
-	doRemove: function (answer)
-	{
-		if (answer == 'yes')
-		{
-			var element = this._listView.getSelectionModel().getSelection()[0];
-			this._listView.getStore().remove(element);
-		}
-	},
-
-	/**
-	 * Open the dialog box to edit a selected announce or a new announce
-	 */
-	act: function ()
-	{
-		if (!this.delayedInitialize())
-		{
-			return;
-		}
-		
-		this.box.show();
-		this._initForm();
-	},
-
-	/**
-	 * @private
-	 * Initialize the dialog box
-	 */
-	delayedInitialize: function ()
-	{
-		if (this._initialized)
-		{
-			return true;
-		}
-		
-		this._form = new Ext.form.Panel({
-			id : 'form-announcement',
-			
-			width: 'auto',
-
-			border: false,
-			bodyStyle :'padding:10px 10px 0',
-			
-			items : [ new Ext.form.field.Text ({
-						labelWidth: 70,
-						fieldLabel: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_LANG'/>",
-						
-						ametysDescription: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_LANG_HELP'/>",
-						name: 'lang',
-						
-						msgTarget: 'side',
-						anchor:'90%'
-					}),
-					new Ext.form.field.TextArea ({
-						labelWidth: 70,
-						fieldLabel :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_MESSAGE'/>",
-						
-						ametysDescription: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_MESSAGE_HELP'/>",
-						name: 'message',
-						anchor:'90%',
-						
-						msgTarget: 'side',
-				        height: 80
-					})
-			]
-		});
-		
-		this.box = new Ametys.window.DialogBox({
-			title :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_CAPTION'/>",
-			
-			layout :'fit',
-			width: 450,
-			height: 205,
-			
-			icon: Ametys.getPluginResourcesPrefix(this.pluginName) + '/img/administrator/system/announce_16.png',
-			
-			items : [ this._form ],
-			
-			defaultButton: this._form.getForm().findField('message'),
-			closeAction: 'hide',
-			
-			buttons : [{
-				text :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_OK'/>",
-				handler : Ext.bind(this.ok, this)
-			}, {
-				text :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_CANCEL'/>",
-				handler : Ext.bind(this.cancel, this)
-			}]
-		});
-		
-		this._initialized = true;
-		
-		return true;
-	},
-
-	/**
-	 * Initialize the form value (depending on new or edit mode)
-	 * @private
-	 */
-	_initForm: function ()
-	{
-		if (this._mode == 'new')
-		{
-		    var lang = this._form.getForm().findField("lang");
-		    var message = this._form.getForm().findField("message");
-		    
-		    lang.setDisabled(false);
-		    lang.setValue("");
-		    message.setValue("");
-		    try {
-		    	lang.focus();
-		    } catch(e) {}
-		}
-		else
-		{
-			var element = this._listView.getSelectionModel().getSelection()[0];
-			
-			var lang = this._form.getForm().findField("lang");
-			var message = this._form.getForm().findField("message");
-
-			lang.setDisabled(element.get('lang') == "*");
-			lang.setValue(element.get('lang'));
-			message.setValue(Ametys.convertHtmlToTextarea(element.get('message')));
-			try {
-				message.focus();
-				message.select();
-			} catch(e) {}
-		}
-	},
-
-	/**
-	 * Listener when clicking on the ok button
-	 * @private 
-	 */
-	ok: function ()
-	{
-		var lang = this._form.getForm().findField("lang");
-	    var message = this._form.getForm().findField("message");
-
-	    if (lang.disabled != true)
-	    {
-	        if (!/[a-z][a-z]/i.test(lang.getValue()))
-	        {
-	        	lang.markInvalid("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_LANG'/>");
-	            return;
-	        }
-	    }
-	    
-	    if (message.getValue() == '')
-	    {
-	    	message.markInvalid("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_MESSAGE'/>");
-	        return;
-	    }
-
-		if (this._mode == 'new')
-		{
-			this._listView.getStore().addSorted(Ext.create('Ametys.plugins.core.administrator.System.Announce', {
-            	lang : lang.getValue(),
-                message : message.getValue().replace(/\r/g, "")
-			}));
-		}
-		else
-		{
-			var element = this._listView.getSelectionModel().getSelection()[0];  
-			element.set('lang', lang.getValue());        
-			element.set('message', message.getValue().replace(/\r/g, ""));      
-		}
-	    
-		this.box.hide();
-	},
-
-	/**
-	 * @private
-	 * Listener when closing the dialog box
-	 */
-	cancel: function ()
-	{
-		this.box.hide();
-	}
-});
Index: main/plugin-core/resources/js/Ametys/plugins/core/administration/actions/SystemActions.js
===================================================================
--- main/plugin-core/resources/js/Ametys/plugins/core/administration/actions/SystemActions.js	(revision 0)
+++ main/plugin-core/resources/js/Ametys/plugins/core/administration/actions/SystemActions.js	(revision 0)
@@ -0,0 +1,401 @@
+/*
+ *  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.
+ */
+
+/**
+ * Singleton class defining the actions related to the welcoming message of the application 
+ * @private
+ */
+Ext.define('Ametys.plugins.core.administration.actions.SystemActions', {
+	singleton: true,
+	
+	/**
+	 * @private
+	 * @property {String} _mode The current mode when opening the edition dialog box {@link #box}. Can be 'new' or 'edit'.
+	 */
+	/**
+	 * @private
+	 * @property {Boolean} _initialized Determine if the dialog box {@link #box} for editing announce is initialized
+	 */
+	/**
+	 * @private
+	 * @property {Ext.form.Panel} _form The form panel in the dialog box {@link #box} for editing
+	 */
+	/**
+	 * @private
+	 * @property {Ametys.window.DialogBox} _box The dialog box for editing
+	 */
+	
+	/**
+	 * Called for the initialization of the controller
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	/*initializeToggleState: function(controller)
+	{
+		this._toggle("init", Ext.bind(this._initOrToggleCb, this, [controller.id], 1));
+	},*/
+	
+	/**
+	 * Activate/Deactivate the welcome announcement
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	setAnnouncementAvailable: function(controller)
+	{
+		var enable = controller.isPressed();
+		
+		Ametys.data.ServerComm.callMethod({
+			role: "org.ametys.runtime.plugins.core.administrator.system.SystemHelper",
+			methodName: "setAnnouncementAvailable",
+			parameters: [enable],
+			callback: {
+				scope: this,
+				handler: this._enableAnnoucementCB,
+				arguments: {
+					state: enable
+				}
+			},
+			errorMessage: {
+				category: this.self.getName(),
+				msg: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE'/>"
+			}
+		});
+	},
+	
+	/**
+	 * @private
+	 * Callback for the toggle button enable/disable system announcement
+	 * @param {Object} response the server's response
+	 * @param {Object} args the callback arguments
+	 */
+	_enableAnnoucementCB: function(response, args)
+	{
+		Ext.create('Ametys.message.Message', {
+			type: Ametys.message.Message.MODIFIED,
+			targets: [{
+				type: 'system-announcement',
+				parameters: {
+					state: args.state
+				}
+			}]
+		});
+	},
+	
+	/**
+	 * Add a message to the list
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	addMessage: function(controller)
+	{
+		this._mode = 'new';
+		if (!this._delayedInitialize())
+		{
+			return;
+		}
+		
+		this._box.show();
+		this._initForm();
+	},
+	
+	/**
+	 * Edit the selected message
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	editMessage: function(controller)
+	{
+		var target = controller.getMatchingTargets()[0];
+		if (target != null)
+		{
+			var targetParameters = target.getParameters();
+			this._mode = 'edit';
+			
+			if (!this._delayedInitialize())
+			{
+				return;
+			}
+			
+			this._box.show();
+			this._initForm(targetParameters.language, targetParameters.message);
+		}
+	},
+	
+	/**
+	 * Delete the selected message
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	deleteMessage: function(controller)
+	{
+		var target = controller.getMatchingTargets()[0];
+		if (target != null)
+		{
+			var lang = target.getParameters().language;
+			
+			Ametys.data.ServerComm.callMethod({
+				role: "org.ametys.runtime.plugins.core.administrator.system.SystemHelper",
+				methodName: "deleteAnnouncement",
+				parameters: [lang],
+				callback: {
+					scope: this,
+					handler: this._deleteCb,
+					arguments: {language: lang}
+				},
+				errorMessage: {
+					category: this.self.getName(),
+					msg: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE'/>"
+				}
+			});
+		}
+	},
+	
+	/**
+	 * @private
+	 * Callback for the deletion process
+	 * @param {Object} response the server's response
+	 * @param {Object} args the callback arguments
+	 * @param {String} args.language the deleted language
+	 */
+	_deleteCb: function(response, args)
+	{
+		Ext.create('Ametys.message.Message', {
+			type: Ametys.message.Message.DELETED,
+			targets: [{
+				type: 'system-announcement-message',
+				parameters: {
+					language: args.language
+				}
+			}]
+		});
+	},
+	
+	/**
+	 * @private
+	 * Initialize the dialog box
+	 */
+	_delayedInitialize: function ()
+	{
+		if (this._initialized)
+		{
+			return true;
+		}
+		
+		this._form = Ext.create('Ext.form.Panel', {
+			width: 'auto',
+
+			border: false,
+			bodyStyle :'padding:10px 10px 0',
+			
+			defaults : {
+				labelWidth: 70,
+				msgTarget: 'side',
+				anchor:'90%',
+				xtype: 'textfield'
+			},
+			
+			items : [ {
+							fieldLabel: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_LANG'/>",
+							ametysDescription: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_LANG_HELP'/>",
+							name: 'language',
+						},
+						{
+							xtype: 'textarea',
+							fieldLabel :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_MESSAGE'/>",
+							ametysDescription: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_MESSAGE_HELP'/>",
+							name: 'message',
+							height: 80
+						}
+			]
+		});
+		
+		this._box = Ext.create('Ametys.window.DialogBox', {
+			title :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_CAPTION'/>",
+			
+			layout :'fit',
+			width: 450,
+			height: 205,
+			
+			icon: Ametys.getPluginResourcesPrefix('core') + '/img/administrator/system/announce_16.png',
+			
+			items : [ this._form ],
+			
+			defaultButton: this._form.getForm().findField('message'),
+			closeAction: 'hide',
+			
+			buttons : [{
+				text :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_OK'/>",
+				handler : Ext.bind(this._ok, this)
+			}, {
+				text :"<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_CANCEL'/>",
+				handler : Ext.bind(this._cancel, this)
+			}]
+		});
+		
+		this._initialized = true;
+		
+		return true;
+	},
+	
+	/**
+	 * @private
+	 * Initialize the form value according to the selected action
+	 * @param {String} language the language of the selected record (used in edition mode only)
+	 * @param {String} message the message of the selected record (used in edition mode only)
+	 */
+	_initForm: function (language, message)
+	{
+		var form = this._form.getForm();
+		var langFd = form.findField("language");
+		
+		if (this._mode == 'new')
+		{
+			form.findField("message").setValue("");
+			
+			langFd.setDisabled(false);
+			langFd.setValue("");
+		}
+		else
+		{
+			langFd.setDisabled(true);
+			langFd.setValue(language);
+			form.findField("message").setValue(Ametys.convertHtmlToTextarea(message));
+		}
+		
+		langFd.focus(true, 10);
+	},
+	
+	/**
+	 * @private 
+	 * Listener when clicking on the ok button
+	 */
+	_ok: function ()
+	{
+		var languageField = this._form.getForm().findField("language");
+	    var messageField = this._form.getForm().findField("message");
+	    
+	    if (!languageField.disabled)
+	    {
+	        if (!(new RegExp('^[a-z]{2}$')).test(languageField.getValue()))
+	        {
+	        	lang.markInvalid("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_LANG'/>");
+	            return;
+	        }
+	    }
+	    
+	    if (messageField.getValue() == '')
+	    {
+	    	messageField.markInvalid("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ANNOUNCEMENT_DIALOG_ERROR_MESSAGE'/>");
+	        return;
+	    }
+	    
+	    // First click on "Ok" => We check if the language exists on both addition and edition modes
+	    this._editAnnouncement(languageField.getValue(), Ametys.convertTextareaToHtml(messageField.getValue()), this._mode == 'edit');
+	},
+
+	/**
+	 * @private
+	 * Listener when closing the dialog box
+	 */
+	_cancel: function ()
+	{
+		this._box.hide();
+	},
+	
+	/**
+	 * @private
+	 * Server call to add/edit the existing annoucements
+	 * @param {String} language the language
+	 * @param {String} message the message
+	 * @param {Boolean} [override=false] true to override message if already exist
+	 */
+	_editAnnouncement: function(language, message, override)
+	{
+		Ametys.data.ServerComm.callMethod({
+			role: "org.ametys.runtime.plugins.core.administrator.system.SystemHelper",
+			methodName: "editAnnouncement",
+			parameters: [language, message, override],
+			callback: {
+				scope: this,
+				handler: this._editAnnouncementCb,
+				arguments: {language: language, message: message} // if the user tries to replace an existing message by clicking on "Add"
+			},
+			errorMessage: {
+				category: this.self.getName(),
+				msg: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_ERROR_SAVE'/>"
+			}
+		});
+	},
+	
+	/**
+	 * @private
+	 * Callback for the addition/edition process
+	 * @param {Object} response the server's response
+	 * @param {String} response['already-exists'] 'yes' if the language already exists, undefined otherwise
+	 * @param {Object} args the callback arguments
+	 * @param {String} args.language the language typed in by the user
+	 * @param {String} args.message the message typed in by the user 
+	 * @param {Boolean} args.announcementActive true if the announcement is active, false otherwise
+	 */
+	_editAnnouncementCb: function(response, args)
+	{
+		if (response['already-exists'])
+		{
+			// Ask the user if he wants to replace the existing welcoming language
+			Ametys.Msg.confirm ("<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_CONFIRM_DIALOG_TITLE'/>", 
+							    "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_CONFIRM_DIALOG_TEXT'/>", 
+							    Ext.bind(this._editOrCancel, this, [args.language, args.message], 1));
+		}
+		else
+		{
+			this._box.hide();
+			
+			var target = Ext.create('Ametys.message.MessageTarget', {
+					type: 'system-announcement-message',
+					parameters: {
+						language: args.language,
+						message: args.message
+					}
+			});
+			
+			if (this._mode == "new")
+			{
+				Ext.create('Ametys.message.Message', {
+					type: Ametys.message.Message.CREATED,
+					targets: [target]
+				});
+			}
+			else
+			{
+				Ext.create('Ametys.message.Message', {
+					type: Ametys.message.Message.MODIFIED,
+					targets: [target]
+				});
+			}
+		}
+	},
+	
+	/**
+	 * @private
+	 * Function invoked after the user's chose to replace/not replace the existing message for the selected language key
+	 * @param {String} answer 'yes' if the user clicked "Ok"
+	 * @param {String} language the language key of the message
+	 * @param {String} message the message itself
+	 */
+	_editOrCancel: function(answer, language, message)
+	{
+		if (answer == 'yes')
+		{
+			this._editAnnouncement(language, message, true);
+		}
+	}
+});
+
Index: main/plugin-core/resources/js/Ametys/plugins/core/administration/controller/SystemAnnouncementController.js
===================================================================
--- main/plugin-core/resources/js/Ametys/plugins/core/administration/controller/SystemAnnouncementController.js	(revision 0)
+++ main/plugin-core/resources/js/Ametys/plugins/core/administration/controller/SystemAnnouncementController.js	(revision 0)
@@ -0,0 +1,122 @@
+/*
+ *  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.
+ */
+
+/**
+ * This class controls a ribbon button reflecting the state of the system announcement message
+ * @private
+ */
+Ext.define('Ametys.plugins.core.administration.controller.SystemAnnouncementController', {
+	extend: 'Ametys.ribbon.element.ui.button.OpenToolButtonController',
+	
+
+	/**
+	 * @cfg {String} announcement-on-icon-small The path to the icon of the button in size 16x16 pixels when the announcement is on
+	 */
+	/**
+	 * @property {String} _onIconSmall See #cfg-announcement-on-icon-small
+	 * @private
+	 */
+	/**
+	 * @cfg {String} announcement-on-icon-medium The path to the icon of the button in size 32x32 pixels when the announcement is on
+	 */
+	/**
+	 * @property {String} _onIconMedium See #cfg-announcement-on-icon-medium
+	 * @private
+	 */
+	/**
+	 * @cfg {String} announcement-on-icon-small The path to the icon of the button in size 48x48 pixels when the announcement is on
+	 */
+	/**
+	 * @property {String} _onIconLarge See #cfg-announcement-on-icon-large
+	 * @private
+	 */
+	
+	/**
+	 * @cfg {String} announcement-off-icon-small The path to the icon of the button in size 16x16 pixels when the announcement is off
+	 */
+	/**
+	 * @property {String} _offIcoffSmall See #cfg-announcement-off-icon-small
+	 * @private
+	 */
+	/**
+	 * @cfg {String} announcement-off-icon-medium The path to the icon of the button in size 32x32 pixels when the announcement is off
+	 */
+	/**
+	 * @property {String} _offIconMedium See #cfg-announcement-off-icon-medium
+	 * @private
+	 */
+	/**
+	 * @cfg {String} announcement-off-icon-small The path to the icon of the button in size 48x48 pixels when the announcement is off
+	 */
+	/**
+	 * @property {String} _offIcoffLarge See #cfg-announcement-off-icon-large
+	 * @private
+	 */
+	
+	constructor: function(config)
+	{
+		this.callParent(arguments);
+		
+		this._offIconSmall = this.getInitialConfig("announcement-off-icon-small") || this.getInitialConfig("icon-small");
+		this._offIconMedium = this.getInitialConfig("announcement-off-icon-medium") || this.getInitialConfig("icon-medium");
+		this._offIconLarge = this.getInitialConfig("announcement-off-icon-large") || this.getInitialConfig("icon-large");
+		
+		this._onIconSmall = this.getInitialConfig("announcement-on-icon-small") || this.getInitialConfig("icon-small");
+		this._onIconMedium = this.getInitialConfig("announcement-on-icon-medium") || this.getInitialConfig("icon-medium");
+		this._onIconLarge = this.getInitialConfig("announcement-on-icon-large") || this.getInitialConfig("icon-large");
+		
+		if (config.available)
+		{
+			this._iconSmall = this._onIconSmall;
+			this._iconMedium = this._onIconMedium;
+			this._iconLarge = this._onIconLarge;
+		}
+		else
+		{
+			this._iconSmall = this._offIconSmall;
+			this._iconMedium = this._offIconMedium;
+			this._iconLarge = this._offIconLarge;
+		}
+
+		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
+	},
+
+	/**
+	 * Listener when the toggle state of the button has been initialized/modified
+	 * Will update the state of the button
+	 * @param {Ametys.message.Message} message The modified message.
+	 * @protected
+	 */
+	_onModified: function (message)
+	{
+		var target = message.getTarget('system-announcement');
+		
+		if (target != null)
+		{
+			var state = target.getParameters().state;
+			if (state)
+			{
+				this.setDescription (this.getInitialConfig("announcement-on-description") || this.getInitialConfig("description"));
+				this.setIcons (this._onIconSmall, this._onIconMedium, this._onIconLarge);
+			}
+			else
+			{
+				this.setDescription (this.getInitialConfig("announcement-off-description") || this.getInitialConfig("description"));
+				this.setIcons (this._offIconSmall, this._offIconMedium, this._offIconLarge);
+			}
+		}
+	}
+});
Index: main/plugin-core/resources/js/Ametys/plugins/core/administration/tool/SystemTool.js
===================================================================
--- main/plugin-core/resources/js/Ametys/plugins/core/administration/tool/SystemTool.js	(revision 0)
+++ main/plugin-core/resources/js/Ametys/plugins/core/administration/tool/SystemTool.js	(revision 0)
@@ -0,0 +1,185 @@
+/*
+ *  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.
+ */
+
+/**
+ * This tool displays the list of welcoming messages for various languages. It allows the administrator to perform several operations 
+ * such as :
+ * &lt;ul&gt;
+ * 	&lt;li&gt; Add a welcome message in a language of his choice
+ *  &lt;li&gt; Modify existing messages
+ *  &lt;li&gt; Delete messages
+ * &lt;/ul&gt;
+ * Note that only one message by language can be recorded at a time. 
+ */
+Ext.define('Ametys.plugins.core.administration.tool.SystemTool', {
+	extend: 'Ametys.tool.Tool',
+	
+	/**
+	 * @private
+	 * @property {Ext.grid.Panel} _grid The messages grid
+	 */
+	
+	constructor: function(config)
+	{
+		this.callParent(arguments);
+		
+		Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onAnnouncementDeleted, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._onAnnoucementCreatedOrModified, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onAnnoucementCreatedOrModified, this);
+	},
+	
+	getMBSelectionInteraction: function() 
+	{
+		return Ametys.tool.Tool.MB_TYPE_ACTIVE;
+	},
+	
+	createPanel: function ()
+	{
+		this._grid = this._drawMessagesPanel();
+		return this._grid;
+	},
+	
+	setParams: function (params)
+	{
+		this.callParent(arguments);
+		this.refresh();
+	},
+	
+	/**
+	 * Refreshes the tool
+	 */
+	refresh: function ()
+	{
+		this.showRefreshing();
+		this._grid.getStore().load({callback: this.showRefreshed, scope: this});
+	},
+	
+	sendCurrentSelection: function()
+	{
+		this._onSelect();
+	},
+	
+	/**
+	 * @private
+	 * Draw the panel displaying the logs
+	 */
+	_drawMessagesPanel: function()
+	{
+		var store = Ext.create('Ext.data.Store', {
+			model: 'Ametys.plugins.core.administration.tool.SystemTool.Message',
+	        
+			sortOnLoad: true,
+	        sorters: [ { property: 'language', direction: "ASC" } ],
+			
+	        proxy: {
+	        	type: 'ametys',
+				plugin: this._pluginName,
+				url: 'administrator/system-announcements',
+	        	reader: {
+	        		type: 'json',
+					rootProperty: 'announcements'
+	        	}
+	        }
+		});		
+		
+		return Ext.create('Ext.grid.Panel',{
+			region: 'center',
+
+			stateful: true,
+			stateId: this.self.getName() + "$grid",
+
+			store : store,
+			
+		    columns: [
+		        {header: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_LANG'/>", menuDisabled : true, sortable: true, width: 80, flex: 0, dataIndex: 'language' },
+		        {header: "<i18n:text i18n:key='PLUGINS_CORE_ADMINISTRATOR_SYSTEM_COL_MESSAGE'/>", menuDisabled : true, sortable: true, flex: 1, dataIndex: 'message' }
+		    ],
+		    
+			listeners: {
+				'selectionchange': Ext.bind(this._onSelect, this)
+			}
+		})
+	},
+	
+	/**
+	 * @private
+	 * Listener when a record is selected
+	 */
+	_onSelect: function()
+	{
+		var targets = [];
+		
+		var selectedRecords = this._grid.getSelectionModel().getSelection();
+		Ext.Array.forEach(selectedRecords, function(selectedRecord) {
+			
+			target = Ext.create('Ametys.message.MessageTarget', {
+				type: 'system-announcement-message',
+				parameters: {language: selectedRecord.get('language'), message: selectedRecord.get('message')}
+			});
+			
+			targets.push(target);
+		});
+		
+		Ext.create('Ametys.message.Message', {
+			type: Ametys.message.Message.SELECTION_CHANGED,
+			targets: targets
+		});
+	},
+	
+	/**
+	 * @private
+	 * Listener when a record is deleted
+	 * @param {Ametys.message.Message} the deletion message
+	 */
+	_onAnnouncementDeleted: function(message)
+	{
+		var targets = message.getTargets('system-announcement-message');
+		if (targets.length > 0)
+		{
+			var store = this._grid.getStore();
+			Ext.Array.forEach(targets, function(target) {
+				var language = target.getParameters().language;
+				var index = store.find("language", language); 
+				if (index != -1)
+				{
+					store.removeAt(index);
+				}
+			});
+		}
+	},
+	
+	/**
+	 * @private
+	 * Listener when a record is modified
+	 * @param {Ametys.message.Message} message The created/modified message.
+	 */
+	_onAnnoucementCreatedOrModified: function(message)
+	{
+		if (message.getTargets('system-announcement-message').length > 0)
+		{
+			this.showOutOfDate();
+		}
+	}
+});
+
+Ext.define('Ametys.plugins.core.administration.tool.SystemTool.Message', {
+    extend: 'Ext.data.Model',
+    
+    fields: [
+       {name: 'language'},
+       {name: 'message'}
+    ]
+});
Index: main/plugin-core/sitemap.xmap
===================================================================
--- main/plugin-core/sitemap.xmap	(revision 31811)
+++ main/plugin-core/sitemap.xmap	(working copy)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-   Copyright 2012 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.
@@ -19,13 +19,18 @@
             <map:generator name="administrator-plugins-change-runtime" src="org.ametys.runtime.plugins.core.administrator.plugins.ChangeRuntime" logger="org.ametys.runtime.plugins.core.administrator.sitemap.generator.ChangeRuntimePlugins" label="content"/>
-            <map:generator name="administrator-system" src="org.ametys.runtime.plugins.core.administrator.system.SystemGenerator" logger="org.ametys.runtime.plugins.core.administrator.sitemap.generator.system" label="content"/>
             <map:generator name="administrator-system-announcement" src="org.ametys.runtime.plugins.core.administrator.system.SystemAnnouncementGenerator" logger="org.ametys.runtime.plugins.core.administrator.sitemap.generator.systemannouncement" label="content"/>
             <map:generator name="administrator-startuptime" src="org.ametys.runtime.plugins.core.administrator.system.StartupTimeGenerator" logger="org.ametys.runtime.plugins.core.administrator.sitemap.generator.startuptime" label="content"/>
             <map:generator name="administrator-rights-profiles-view" src="org.ametys.runtime.plugins.core.right.profile.generators.ProfilesGenerator" logger="org.ametys.runtime.plugins.core.right.profile.sitemap.generator.view" label="content"/>
@@ -65,13 +70,11 @@
         
 		<map:actions>
             <map:action name="set-header" src="org.apache.cocoon.acting.HttpHeaderAction" logger="org.ametys.runtime.plugins.core.administrator.sitemap.action.httpHeader"/>

+            <map:action name="administrator-system" src="org.ametys.runtime.plugins.core.administrator.system.GetSystemAnnouncements" logger="org.ametys.runtime.plugins.core.administrator.sitemap.generator.system" label="content"/>




@@ -459,27 +457,12 @@
             <!-- +
                  | System announce
                  + -->
-            <map:match pattern="administrator/system/view.html">
-                <map:generate type="administrator-system" label="content"/>
-                <map:transform src="pages/administrator/system/view.xsl">
-                    <map:parameter name="contextPath" value="{request:contextPath}"/>
-                    <map:parameter name="pluginName" value="{request-attr:pluginName}"/>
-                    <map:parameter name="workspaceName" value="{request-attr:workspaceName}"/>
-                    <map:parameter name="workspaceURI" value="{request-attr:workspaceURI}"/>
-                </map:transform>
-                <map:serialize/>
-            </map:match>
-            
-            <map:match pattern="administrator/system/update">
-                <map:act type="administrator-system-update">
-                    <map:generate type="action-result"/>
-                    <map:serialize type="xml"/>
+            <map:match pattern="administrator/system-announcements">
+                <map:act type="administrator-system">
+                    <map:read type="json"/>
                 </map:act>
-
-                <map:generate type="action-result"/>
-                <map:serialize type="xml" status-code="500"/>
             </map:match>
-            
+                 
             <!-- +
                  | Super user creation
                  + -->
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/GetSystemAnnouncements.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/GetSystemAnnouncements.java	(revision 0)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/GetSystemAnnouncements.java	(revision 0)
@@ -0,0 +1,71 @@
+package org.ametys.runtime.plugins.core.administrator.system;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.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.ametys.runtime.cocoon.JSonReader;
+import org.ametys.runtime.plugins.core.administrator.system.SystemHelper.SystemAnnouncement;
+
+/**
+ * Get the system announces from the system.xml file
+ */
+public class GetSystemAnnouncements extends ServiceableAction implements Contextualizable
+{
+    private SystemHelper _systemHelper;
+    
+    private org.apache.cocoon.environment.Context _environmentContext;
+
+    @Override
+    public void contextualize(Context context) throws ContextException
+    {
+        _environmentContext = (org.apache.cocoon.environment.Context) context.get(org.apache.cocoon.Constants.CONTEXT_ENVIRONMENT_CONTEXT);
+    }
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _systemHelper = (SystemHelper) serviceManager.lookup(SystemHelper.ROLE);
+    }
+    
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Map<String, Object> result = new HashMap<> ();
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        
+        List<Map<String, String>> announcements = new ArrayList<> ();
+        
+        String contextPath = _environmentContext.getRealPath("/");
+        SystemAnnouncement systemAnnouncement = _systemHelper.readValues(contextPath);
+        
+        Map<String, String> messages = systemAnnouncement.getMessages();
+        
+        for (String lang : messages.keySet())
+        {
+            Map<String, String> message = new HashMap<> ();
+            message.put("language", lang);
+            message.put("message", messages.get(lang));
+            
+            announcements.add(message);
+        }
+        
+        result.put("announcements", announcements);
+        
+        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
+        return EMPTY_MAP;
+    }
+}
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemAnnouncementClientSideElement.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemAnnouncementClientSideElement.java	(revision 0)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemAnnouncementClientSideElement.java	(revision 0)
@@ -0,0 +1,67 @@
+/*
+ *  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.runtime.plugins.core.administrator.system;
+
+import java.util.Map;
+
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+
+import org.ametys.runtime.ui.StaticClientSideElement;
+
+/**
+ * This client side element toggles controller according system announcement's state
+ *
+ */
+public class SystemAnnouncementClientSideElement extends StaticClientSideElement implements Contextualizable
+{
+    private org.apache.cocoon.environment.Context _environmentContext;
+    private SystemHelper _systemHelper;
+    
+    @Override
+    public void contextualize(Context context) throws ContextException
+    {
+        _environmentContext = (org.apache.cocoon.environment.Context) context.get(org.apache.cocoon.Constants.CONTEXT_ENVIRONMENT_CONTEXT);
+    }
+    
+    @Override
+    public void service(ServiceManager smanager) throws ServiceException
+    {
+        super.service(smanager);
+        _systemHelper = (SystemHelper) smanager.lookup(SystemHelper.ROLE);
+    }
+    
+    @Override
+    public Map<String, Object> getParameters(Map<String, Object> contextualParameters)
+    {
+        Map<String, Object> parameters = super.getParameters(contextualParameters);
+        
+        String contextPath = _environmentContext.getRealPath("/");
+        boolean isAvailable = _systemHelper.isSystemAnnouncementAvailable(contextPath);
+        
+        if ("true".equals(parameters.get("toggle-enabled")))
+        {
+            parameters.put("toggle-state", isAvailable);
+        }
+        
+        parameters.put("available", isAvailable);
+        
+        return parameters;
+    }
+}
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemAnnouncementGenerator.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemAnnouncementGenerator.java	(revision 31873)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemAnnouncementGenerator.java	(working copy)
@@ -21,8 +21,10 @@
 import org.apache.avalon.framework.context.Context;
 import org.apache.avalon.framework.context.ContextException;
 import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.generation.AbstractGenerator;
+import org.apache.cocoon.generation.ServiceableGenerator;
 import org.apache.cocoon.i18n.I18nUtils;
 import org.apache.cocoon.xml.XMLUtils;
 import org.xml.sax.SAXException;
@@ -30,34 +32,41 @@
 /**
  * Generates the system announcement
  */
-public class SystemAnnouncementGenerator extends AbstractGenerator implements Contextualizable
+public class SystemAnnouncementGenerator extends ServiceableGenerator implements Contextualizable
 {
     private org.apache.cocoon.environment.Context _environmentContext;
-
+    private SystemHelper _systemHelper;
+    
+    @Override
     public void contextualize(Context context) throws ContextException
     {
         _environmentContext = (org.apache.cocoon.environment.Context) context.get(org.apache.cocoon.Constants.CONTEXT_ENVIRONMENT_CONTEXT);
     }
     
     @Override
+    public void service(ServiceManager smanager) throws ServiceException
+    {
+        super.service(smanager);
+        _systemHelper = (SystemHelper) smanager.lookup(SystemHelper.ROLE);
+    }
+    
+    @Override
     public void generate() throws IOException, SAXException, ProcessingException
     {
         contentHandler.startDocument();
         XMLUtils.startElement(contentHandler, "SystemAnnounce");
         
         String contextPath = _environmentContext.getRealPath("/");
-        boolean isAvailable = SystemHelper.isSystemAnnouncementAvailable(contextPath);
+        boolean isAvailable = _systemHelper.isSystemAnnouncementAvailable(contextPath);
         
         XMLUtils.createElement(contentHandler, "IsAvailable", String.valueOf(isAvailable));
         if (isAvailable)
         {
             Locale locale = I18nUtils.findLocale(objectModel, "locale", null, Locale.getDefault(), true);
             
-            XMLUtils.createElement(contentHandler, "LastModification", String.valueOf(SystemHelper.getSystemAnnoucementLastModificationDate(contextPath))); 
-            XMLUtils.createElement(contentHandler, "Message", SystemHelper.getSystemAnnouncement(locale.getLanguage(), contextPath));
+            XMLUtils.createElement(contentHandler, "LastModification", String.valueOf(_systemHelper.getSystemAnnoucementLastModificationDate(contextPath))); 
+            XMLUtils.createElement(contentHandler, "Message", _systemHelper.getSystemAnnouncement(locale.getLanguage(), contextPath));
         }
-
-        
         
         XMLUtils.endElement(contentHandler, "SystemAnnounce");
         contentHandler.endDocument();
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemGenerator.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemGenerator.java	(revision 31873)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemGenerator.java	(nonexistent)
@@ -1,70 +0,0 @@
-/*
- *  Copyright 2012 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.runtime.plugins.core.administrator.system;
-
-import java.io.IOException;
-
-import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.components.source.SourceUtil;
-import org.apache.cocoon.generation.ServiceableGenerator;
-import org.apache.cocoon.xml.XMLUtils;
-import org.apache.excalibur.source.Source;
-import org.xml.sax.SAXException;
-
-import org.ametys.runtime.util.IgnoreRootHandler;
-
-
-/**
- * SAXes general information about the application : JVM status, sessions count, system properties, ...
- */
-public class SystemGenerator extends ServiceableGenerator
-{
-    public void generate() throws IOException, SAXException, ProcessingException
-    {
-        contentHandler.startDocument();
-        XMLUtils.startElement(contentHandler, "System");
-        
-//        AttributesImpl maintenanceAttrs = new AttributesImpl();
-//        maintenanceAttrs.addAttribute("", "state", "state", "CDATA", RuntimeServlet.getRunMode() == RuntimeServlet.RunMode.MAINTENANCE ? "on" : "off");
-//        XMLUtils.createElement(contentHandler, "maintenance", maintenanceAttrs);
-
-        // Read the file
-        Source systemSource = null;
-        try
-        {
-            systemSource = resolver.resolveURI("context://" + SystemHelper.ADMINISTRATOR_SYSTEM_FILE);
-            if (systemSource.exists())
-            {
-                SourceUtil.parse(manager, systemSource, new IgnoreRootHandler(contentHandler));
-            }
-        }
-        catch (Exception e)
-        {
-            getLogger().error("Administrator try to read the system properties but an error occured while reading the file '" + SystemHelper.ADMINISTRATOR_SYSTEM_FILE + "'", e);
-            throw new ProcessingException(e);
-        }
-        finally 
-        {
-            if (systemSource != null)
-            {
-                resolver.release(systemSource);
-            }
-        }
-        
-        XMLUtils.endElement(contentHandler, "System");
-        contentHandler.endDocument();
-    }
-}
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemHelper.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemHelper.java	(revision 31873)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/SystemHelper.java	(nonexistent)
@@ -1,140 +0,0 @@
-/*
- *  Copyright 2012 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.runtime.plugins.core.administrator.system;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Document;
-
-/**
- * Helper class for accessing the the System parameters defined in the 'admin' section (system announcements, ...)
- */
-public final class SystemHelper
-{
-    /** The relative path to the file where system information are saved (announcement, maintenance...) */
-    public static final String ADMINISTRATOR_SYSTEM_FILE = "WEB-INF/data/administrator/system.xml";
-
-    private SystemHelper()
-    {
-        // empty constructor
-    }
-    
-    /**
-     * Tests if system announcements are active.
-     * @param contextPath the webapp context path
-     * @return true if system announcements are active.
-     */
-    public static boolean isSystemAnnouncementAvailable(String contextPath)
-    {
-        File systemFile = new File(contextPath, ADMINISTRATOR_SYSTEM_FILE);
-        if (!systemFile.exists() || !systemFile.isFile())
-        {
-            return false;
-        }
-
-        try (InputStream is = new FileInputStream(systemFile))
-        {
-            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
-            XPath xpath = XPathFactory.newInstance().newXPath();
-            String state = xpath.evaluate("/announcements/@state", document);
-            
-            return "on".equals(state);
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException("Unable to get system announcements", e);
-        }
-    }
-    
-    /**
-     * Return the date of the last modification of the annonce
-     * @param contextPath the webapp context path
-     * @return The date of the last modification or 0 if there is no announce file
-     */
-    public static long getSystemAnnoucementLastModificationDate(String contextPath)
-    {
-        try
-        {
-            File systemFile = new File(contextPath, ADMINISTRATOR_SYSTEM_FILE);
-            if (!systemFile.exists() || !systemFile.isFile())
-            {
-                return 0;
-            }
-            
-            return systemFile.lastModified();
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException("Unable to get system announcements", e);
-        }
-    }
-
-    /**
-     * Returns the system announcement for the given language code, or for the default language code if there is no specified announcement for the given language code.<br>
-     * Returns null if the system announcements are not activated.
-     * @param languageCode the desired language code of the system announcement
-     * @param contextPath the webapp context path
-     * @return the system announcement in the specified language code, or in the default language code, or null if announcements are not active.
-     */
-    public static String getSystemAnnouncement(String languageCode, String contextPath)
-    {
-        File systemFile = new File(contextPath, ADMINISTRATOR_SYSTEM_FILE);
-        if (!systemFile.exists() || !systemFile.isFile())
-        {
-            return null;
-        }
-        
-        try (InputStream is = new FileInputStream(systemFile))
-        {
-            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
-            XPath xpath = XPathFactory.newInstance().newXPath();
-            String state = xpath.evaluate("/announcements/@state", document);
-            
-            if (!"on".equals(state))
-            {
-                return null;
-            }
-            
-            String announcement = xpath.evaluate("/announcements/announcement[@lang='" + languageCode + "']", document);
-            
-            if (announcement == null || announcement.length() == 0)
-            {
-                String defaultAnnouncement = xpath.evaluate("/announcements/announcement[not(@lang)]", document);
-                
-                if (defaultAnnouncement == null || defaultAnnouncement.length() == 0)
-                {
-                    throw new IllegalStateException("There must be a default announcement.");
-                }
-                
-                return defaultAnnouncement;
-            }
-            else
-            {
-                return announcement;
-            }
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException("Unable to get system announcements", e);
-        }
-    }
-}
Index: main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/UpdateSystemAction.java
===================================================================
--- main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/UpdateSystemAction.java	(revision 31873)
+++ main/plugin-core/src/org/ametys/runtime/plugins/core/administrator/system/UpdateSystemAction.java	(nonexistent)
@@ -1,169 +0,0 @@
-/*
- *  Copyright 2012 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.runtime.plugins.core.administrator.system;
-
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.regex.Pattern;
-
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-
-import org.apache.avalon.framework.parameters.Parameters;
-import org.apache.avalon.framework.thread.ThreadSafe;
-import org.apache.cocoon.acting.AbstractAction;
-import org.apache.cocoon.environment.ObjectModelHelper;
-import org.apache.cocoon.environment.Redirector;
-import org.apache.cocoon.environment.Request;
-import org.apache.cocoon.environment.SourceResolver;
-import org.apache.cocoon.xml.XMLUtils;
-import org.apache.excalibur.source.ModifiableSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.AttributesImpl;
-
-/**
- * This action update the system info.
- */
-public class UpdateSystemAction extends AbstractAction implements ThreadSafe
-{
-    private Pattern _langPattern = Pattern.compile("[a-zA-Z]{2}");
-    
-    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
-    {
-        Request request = ObjectModelHelper.getRequest(objectModel);
-        
-        // The map to keep the messages
-        Map<String, String> messages = new HashMap<>();
-        
-        // Is maintenance
-        // boolean maintenace = "true".equals(_request.getParameter("maintenance"));
-        
-        // Is announcement
-        boolean announcement = "true".equals(request.getParameter("announcement"));
-        
-        // Get the language codes and announcement messages and check them
-        if (!_getMessages(messages, request))
-        {
-            return null;
-        }
-        
-        // Save the file
-        ModifiableSource saveAnnouncementSource = (ModifiableSource) resolver.resolveURI("context://" + SystemHelper.ADMINISTRATOR_SYSTEM_FILE);
-        try (OutputStream os = saveAnnouncementSource.getOutputStream())
-        {
-            _save(os, announcement, messages);
-            
-            // RuntimeServlet.setRunMode(maintenace ? RuntimeServlet.RunMode.MAINTENANCE : RuntimeServlet.RunMode.NORMAL);
-        }
-        catch (Exception e)
-        {
-            getLogger().error("An error occured while saving administrator's properties to file '" + SystemHelper.ADMINISTRATOR_SYSTEM_FILE + "'", e);
-            return null;
-        }
-        finally 
-        {
-            if (saveAnnouncementSource != null)
-            {
-                resolver.release(saveAnnouncementSource);
-            }
-        }
-        
-        return EMPTY_MAP;
-    }
-    
-    private boolean _getMessages(Map<String, String> messages, Request request)
-    {
-        String[] langCodes = request.getParameterValues("lang");
-        if (langCodes == null)
-        {
-            // All messages have be deleted (this should not be possible)
-            return true;
-        }
-        
-        for (String lang : langCodes)
-        {
-            // Lang code
-            if (lang == null)
-            {
-                getLogger().error("One of the administrator's announcements language code is not specified.");
-                return false;
-            }
-            else if (!lang.equals("*") && !_langPattern.matcher(lang).matches())
-            {
-                getLogger().error("Incorrect language code encountered while saving administrator's announcements : '" + lang + "'.");
-                return false;
-            }
-
-            // message
-            String message = request.getParameter("message_" + lang);
-            if (message == null || message.length() == 0)
-            {
-                getLogger().error("No message specified for language '" + lang + "'. Unable to save administrator's announcements.");
-                return false;
-            }
-            
-            messages.put(lang, message);
-        }
-        
-        return true;
-    }
-
-    private void _save(OutputStream os, boolean announcement, Map<String, String> messages) throws TransformerConfigurationException, TransformerFactoryConfigurationError, SAXException
-    {
-        // create a transformer for saving sax into a file
-        TransformerHandler th = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler();
-        // create the result where to write
-        StreamResult sResult = new StreamResult(os);
-        th.setResult(sResult);
-
-        // create the format of result
-        Properties format = new Properties();
-        format.put(OutputKeys.METHOD, "xml");
-        format.put(OutputKeys.INDENT, "yes");
-        format.put(OutputKeys.ENCODING, "UTF-8");
-        th.getTransformer().setOutputProperties(format);
-
-        // Send SAX events
-        th.startDocument();
-
-        AttributesImpl announcementsAttrs = new AttributesImpl();
-        announcementsAttrs.addAttribute("", "state", "state", "CDATA", announcement ? "on" : "off");
-        XMLUtils.startElement(th, "announcements", announcementsAttrs);
-        
-        for (String id : messages.keySet())
-        {
-            AttributesImpl announcementAttrs = new AttributesImpl();
-            if (!"*".equals(id))
-            {
-                announcementAttrs.addAttribute("", "lang", "lang", "CDATA", id);
-            }
-            
-            XMLUtils.createElement(th, "announcement", announcementAttrs, messages.get(id));
-        }
-        
-        XMLUtils.endElement(th, "announcements");
-        
-        th.endDocument();
-    }
-}
