Index: main/plugin-web/i18n/messages_en.xml
===================================================================
--- main/plugin-web/i18n/messages_en.xml	(revision 32361)
+++ main/plugin-web/i18n/messages_en.xml	(working copy)
@@ -1709,7 +1709,7 @@
 		 | ALIAS
 		 + -->
 	<message key="PLUGINS_WEB_ALIAS_UITOOL_LABEL">Page alias</message>
-	<message key="PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION">This module lets you create aliases to redirect the old url to existing pages or url.</message>
+	<message key="PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION">This tool allows you to manage aliases in order to redirect given urls to existing pages or to other urls.</message>
 	<message key="PLUGINS_WEB_ALIAS_UITOOL_FOOTER"></message>
 	<message key="PLUGINS_WEB_ALIAS_CREATE_LABEL">New alias</message>
 	<message key="PLUGINS_WEB_ALIAS_CREATE_DESCRIPTION">Create a new alias</message>
Index: main/plugin-web/i18n/messages_fr.xml
===================================================================
--- main/plugin-web/i18n/messages_fr.xml	(revision 32361)
+++ main/plugin-web/i18n/messages_fr.xml	(working copy)
@@ -1713,7 +1713,7 @@
 		 | ALIAS
 		 + -->
 	<message key="PLUGINS_WEB_ALIAS_UITOOL_LABEL">Redirections d'URL</message>
-	<message key="PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION">Cet outil permet de créer des alias afin de rediriger \"à la volée\" des anciennes url vers des pages ou url existantes.</message>
+	<message key="PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION">Cet outil permet de créer des alias afin de rediriger "à la volée" des anciennes url vers des pages ou url existantes.</message>
 	<message key="PLUGINS_WEB_ALIAS_UITOOL_FOOTER"></message>
 	<message key="PLUGINS_WEB_ALIAS_CREATE_LABEL">Nouvel alias</message>
 	<message key="PLUGINS_WEB_ALIAS_CREATE_DESCRIPTION">Crée un nouvel alias</message>
Index: main/plugin-web/plugin.xml
===================================================================
--- main/plugin-web/plugin.xml	(revision 32412)
+++ main/plugin-web/plugin.xml	(working copy)
@@ -22,8 +22,8 @@
         runtimeVersion="2.0" version="2.0">
         
     <!-- +
-             | CORE DATASOURCE
-             + -->
+         | CORE DATASOURCE
+         + -->
     <!-- JDBC -->
 	<config>
         <param id="front-office.usersManager.jdbc.driver" type="string">
@@ -5448,222 +5448,219 @@
 		 | TOOL FOR PAGE MAPPING
 		 + -->
 	<feature name="userinterface.alias.tool">
-		<extensions>
-			<!-- <extension id="org.ametys.web.userinterface.Alias"
-					   point="org.ametys.core.ui.RibbonControlsManager" 
-					   class="org.ametys.core.ui.StaticClientSideElement">
-				<action class="Ametys.ribbon.element.ui.button.OpenToolButtonController">
-					<param name="role">uitool-alias</param>
-					
-					<param name="label" i18n="true">PLUGINS_WEB_ALIAS_UITOOL_LABEL</param>
-					<param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION</param>
-					<param name="footer" i18n="true">PLUGINS_WEB_ALIAS_UITOOL_FOOTER</param>
-					
-					<param name="icon-small" file="true">img/alias/icon_small.png</param>
-					<param name="icon-medium" file="true">img/alias/icon_medium.png</param>
-					<param name="icon-large" file="true">img/alias/icon_large.png</param>
-				</action>
-				<right>Web_Rights_Alias_Handle</right>
-			</extension>
-			
-			<extension id="org.ametys.web.userinterface.AliasFactory"
-    				   point="org.ametys.core.ui.UIToolsFactoriesManager"
-    				   class="org.ametys.core.ui.StaticClientSideElement">
-				<action class="org.ametys.web.tool.alias.AliasToolFactory">
-					<param name="role">uitool-alias</param>
-					
-					<param name="title" i18n="true">PLUGINS_WEB_ALIAS_UITOOL_LABEL</param>
-					<param name="description" i18n="true">PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION</param>
-					<param name="footer" i18n="true">PLUGINS_WEB_ALIAS_UITOOL_FOOTER</param>
-					
-					<param name="icon-small" file="true">img/alias/icon_small.png</param>
-					<param name="icon-medium" file="true">img/alias/icon_medium.png</param>
-					<param name="icon-large" file="true">img/alias/icon_large.png</param>
-					
-					<param name="default-location"></param>
-				</action>    				   
-				<scripts>
-					<file>js/org/ametys/web/tool/alias/AliasTool.js</file>
-					<file>js/org/ametys/web/tool/alias/AliasToolFactory.js</file>
-				</scripts>
-    		</extension>
-    		
-    		<extension id="org.ametys.web.AliasTab"
-    				   point="org.ametys.core.ui.RibbonTabsManager"
-    				   class="org.ametys.core.ui.StaticClientSideElement">
-				<action class="Ametys.ribbon.element.tab.TabController">
-					<param name="tool-role">uitool-alias</param>
-				</action> 
-				<scripts>
-					<file plugin="cms">js/org/ametys/cms/ribbon/tab/ToolTab.js</file>
-				</scripts>   	
-    		</extension> 
-    		
-    		<extension id="org.ametys.web.Alias.add" 
-    				   point="org.ametys.core.ui.RibbonControlsManager" 
-    				   class="org.ametys.cms.clientsideelement.StaticMenu">
-				<action class="org.ametys.cms.ribbon.button.Menu">
-					<param name="action">org.ametys.web.alias.Edit.addPage</param>
-				
-					<param name="label" i18n="true">PLUGINS_WEB_ALIAS_CREATE_LABEL</param>
-					<param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_CREATE_DESCRIPTION</param>
-					
-					<param name="icon-small" file="true">img/alias/add_16.png</param>
-					<param name="icon-medium" file="true">img/alias/add_32.png</param>
-					<param name="icon-large" file="true">img/alias/add_48.png</param>
-				</action>
-				<menu-item id="org.ametys.web.alias.Edit.add">
-					<param name="action">org.ametys.web.alias.Edit.addPage</param>
-					
-					<param name="label" i18n="true">PLUGINS_WEB_ALIAS_CREATE_TO_PAGE_LABEL</param>
-					<param name="description" i18n="true">PLUGINS_WEB_ALIAS_CREATE_TO_PAGE_DESCRIPTION</param>
-					<param name="iconSmall" file="true">img/alias/icon_small.png</param>
-				</menu-item>
-				<menu-item id="org.ametys.web.page.LinkPage.web">
-					<param name="action">org.ametys.web.alias.Edit.addUrl</param>
-					
-					<param name="label" i18n="true">PLUGINS_WEB_ALIAS_CREATE_TO_URL_LABEL</param>
-					<param name="description" i18n="true">PLUGINS_WEB_ALIAS_CREATE_TO_URL_DESCRIPTION</param>
-					<param name="iconSmall" file="true">img/alias/icon_small.png</param>
-				</menu-item>
-				<scripts>
-					<file plugin="cms">js/org/ametys/cms/ribbon/button/Menu.js</file>
-					<file>js/org/ametys/web/tool/alias/AliasActions.js</file>
-				</scripts>
-				<right>Web_Rights_Alias_Handle</right>
-    		</extension>
-    	
-			<extension id="org.ametys.web.Alias.edit"
-					   point="org.ametys.core.ui.RibbonControlsManager" 
-					   class="org.ametys.core.ui.StaticClientSideElement">
-				<action class="org.ametys.ribbon.control.button.ActionSelectionButton">
-					<param name="action">org.ametys.web.alias.Edit.edit</param>
-					<param name="target-type">^alias$</param>
-					
-					<param name="label" i18n="true">PLUGINS_WEB_ALIAS_EDIT_LABEL</param>
-					<param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_EDIT_DESCRIPTION</param>
-					
-					<param name="icon-small" file="true">img/alias/edit_16.png</param>
-					<param name="icon-medium" file="true">img/alias/edit_32.png</param>
-					<param name="icon-large" file="true">img/alias/edit_48.png</param>
-				</action>
-				<scripts>
-					<file>js/org/ametys/web/tool/alias/AliasActions.js</file>
-				</scripts>
-				<right>Web_Rights_Alias_Handle</right>
-			</extension>
-			
-			<extension id="org.ametys.web.Alias.delete"
-					   point="org.ametys.core.ui.RibbonControlsManager" 
-					   class="org.ametys.core.ui.StaticClientSideElement">
-				<action class="org.ametys.ribbon.control.button.ActionSelectionButton">
-					<param name="action">org.ametys.web.alias.Delete.act</param>
-					<param name="target-type">^alias$</param>
-					
-					<param name="label" i18n="true">PLUGINS_WEB_ALIAS_DELETE_LABEL</param>
-					<param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_DELETE_DESCRIPTION</param>
-					
-					<param name="icon-small" file="true">img/alias/delete_16.png</param>
-					<param name="icon-medium" file="true">img/alias/delete_32.png</param>
-					<param name="icon-large" file="true">img/alias/delete_48.png</param>
-				</action>
-				<scripts>
-					<file>js/org/ametys/web/tool/alias/AliasActions.js</file>
-				</scripts>
-				<right>Web_Rights_Alias_Handle</right>
-			</extension>
+        
+        <components>
+            <component class="org.ametys.web.alias.AliasDAO"
+                       role="org.ametys.web.alias.AliasDAO"/>
+        </components>
+        
+        <!-- 
+            This feature contains only the interaction for handling aliases
+         -->
+        <extensions>
+            <extension id="org.ametys.web.userinterface.Alias"  
+                       point="org.ametys.core.ui.RibbonControlsManager" 
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <!-- 
+                    This interaction allows the administrator to handle sites
+                 -->
+                 <class name="Ametys.ribbon.element.ui.button.OpenToolButtonController">
+                    <opentool-role>uitool-alias</opentool-role>
+                    
+                    <label i18n="true">PLUGINS_WEB_ALIAS_UITOOL_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION</description>
+                    
+                    <icon-small file="true">img/alias/icon_small.png</icon-small>
+                    <icon-medium file="true">img/alias/icon_medium.png</icon-medium>
+                    <icon-large file="true">img/alias/icon_large.png</icon-large>
+                </class>
+            </extension>
+            
+            <extension id="org.ametys.web.userinterface.AliasFactory"  
+                       point="org.ametys.core.ui.UIToolsFactoriesManager" 
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.tool.factory.UniqueToolFactory">
+                    <role>uitool-alias</role>
+                    <toolClass>Ametys.plugins.web.alias.AliasTool</toolClass>
+                    
+                    <title i18n="true">PLUGINS_WEB_ALIAS_UITOOL_LABEL</title>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_UITOOL_DESCRIPTION</description>
+                    
+                    <icon-small file="true">img/alias/icon_small.png</icon-small>
+                    <icon-medium file="true">img/alias/icon_medium.png</icon-medium>
+                    <icon-large file="true">img/alias/icon_large.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/alias/AliasTool.js</file>
+                    <file>js/Ametys/plugins/web/alias/AliasTool/Alias.js</file>
+                </scripts>
+                <default-location>c</default-location>
+            </extension>
+            
+            <extension id="org.ametys.web.AliasTab"
+                       point="org.ametys.core.ui.RibbonTabsManager"
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.tab.TabController">
+                    <tool-role>^(uitool-alias)$</tool-role>
+                </class>
+            </extension>
+            
+            <extension id="org.ametys.web.alias.Add"
+                       point="org.ametys.core.ui.RibbonControlsManager"
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.ui.ButtonController">
+                    <action>Ametys.plugins.web.alias.AliasActions.add</action>
+                    
+                    <label i18n="true">PLUGINS_WEB_ALIAS_CREATE_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_CREATE_DESCRIPTION</description>
+
+                    <icon-small file="true">img/alias/add_16.png</icon-small>
+                    <icon-medium file="true">img/alias/add_32.png</icon-medium>
+                    <icon-large file="true">img/alias/add_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
+                </scripts>
+                <right>Web_Rights_Alias_Handle</right>
+            </extension>
+            
+            <extension id="org.ametys.web.alias.Edit"
+                       point="org.ametys.core.ui.RibbonControlsManager" 
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.ribbon.element.ui.ButtonController">
+                    <action>Ametys.plugins.web.alias.AliasActions.edit</action>
+                    <selection-target-type>^alias$</selection-target-type>
+                    <selection-enable-multiselection>false</selection-enable-multiselection>
+                    
+                    
+                    <label i18n="true">PLUGINS_WEB_ALIAS_EDIT_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_EDIT_DESCRIPTION</description>
+
+                    <icon-small file="true">img/alias/edit_16.png</icon-small>
+                    <icon-medium file="true">img/alias/edit_32.png</icon-medium>
+                    <icon-large file="true">img/alias/edit_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
+                </scripts>
+                <right>Web_Rights_Alias_Handle</right>
+            </extension>
             
-            <extension id="org.ametys.web.Alias.moveFirst"
+            <extension id="org.ametys.web.alias.Delete"
                        point="org.ametys.core.ui.RibbonControlsManager" 
                        class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="org.ametys.ribbon.control.button.AliasMoveButton">
-                    <param name="action">org.ametys.web.alias.MoveFirst.act</param>
-                    <param name="target-type">^alias$</param>
-                    <param name="enable-multiselection">false</param>
-                    <param name="direction">up</param>
+                <class name="Ametys.ribbon.element.ui.ButtonController">
+                    <action>Ametys.plugins.web.alias.AliasActions.deleteAlias</action>
+                    <selection-target-type>^alias$</selection-target-type>
                     
-                    <param name="label" i18n="true">PLUGINS_WEB_ALIAS_MOVE_FIRST_LABEL</param>
-                    <param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_MOVE_FIRST_DESCRIPTION</param>
+                    <label i18n="true">PLUGINS_WEB_ALIAS_DELETE_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_DELETE_DESCRIPTION</description>
+
+                    <icon-small file="true">img/alias/delete_16.png</icon-small>
+                    <icon-medium file="true">img/alias/delete_32.png</icon-medium>
+                    <icon-large file="true">img/alias/delete_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
+                </scripts>
+                <right>Web_Rights_Alias_Handle</right>
+            </extension>
+
+            <extension id="org.ametys.web.alias.MoveFirst"
+                       point="org.ametys.core.ui.RibbonControlsManager" 
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.plugins.web.alias.MoveAliasController">
+                    <action>Ametys.plugins.web.alias.AliasActions.move</action>
+                    <selection-target-type>^alias$</selection-target-type>
+                    <selection-enable-multiselection>false</selection-enable-multiselection>
                     
-                    <param name="icon-small" file="true">img/alias/move_top_16.png</param>
-                    <param name="icon-medium" file="true">img/alias/move_top_32.png</param>
-                    <param name="icon-large" file="true">img/alias/move_top_48.png</param>
-                </action>
+                    <role>move-first</role>
+                    
+                    <label i18n="true">PLUGINS_WEB_ALIAS_MOVE_FIRST_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_MOVE_FIRST_DESCRIPTION</description>
+
+                    <icon-small file="true">img/alias/move_top_16.png</icon-small>
+                    <icon-medium file="true">img/alias/move_top_32.png</icon-medium>
+                    <icon-large file="true">img/alias/move_top_48.png</icon-large>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/ribbon/control/button/AliasMoveButton.js</file>
-                    <file>js/org/ametys/web/tool/alias/AliasActions.js</file>
+                    <file>js/Ametys/plugins/web/alias/MoveAliasController.js</file>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
                 </scripts>
                 <right>Web_Rights_Alias_Handle</right>
             </extension>
             
-            <extension id="org.ametys.web.Alias.moveUp"
+            <extension id="org.ametys.web.alias.MoveUp"
                        point="org.ametys.core.ui.RibbonControlsManager" 
                        class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="org.ametys.ribbon.control.button.AliasMoveButton">
-                    <param name="action">org.ametys.web.alias.MoveUp.act</param>
-                    <param name="target-type">^alias$</param>
-                    <param name="enable-multiselection">false</param>
-                    <param name="direction">up</param>
+                <class name="Ametys.plugins.web.alias.MoveAliasController">
+                    <action>Ametys.plugins.web.alias.AliasActions.move</action>
+                    <selection-target-type>^alias$</selection-target-type>
+                    <selection-enable-multiselection>false</selection-enable-multiselection>
                     
-                    <param name="label" i18n="true">PLUGINS_WEB_ALIAS_MOVE_UP_LABEL</param>
-                    <param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_MOVE_UP_DESCRIPTION</param>
+                    <role>move-up</role>
                     
-                    <param name="icon-small" file="true">img/alias/move_up_16.png</param>
-                    <param name="icon-medium" file="true">img/alias/move_up_32.png</param>
-                    <param name="icon-large" file="true">img/alias/move_up_48.png</param>
-                </action>
+                    <label i18n="true">PLUGINS_WEB_ALIAS_MOVE_UP_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_MOVE_UP_DESCRIPTION</description>
+    
+                    <icon-small file="true">img/alias/move_up_16.png</icon-small>
+                    <icon-medium file="true">img/alias/move_up_32.png</icon-medium>
+                    <icon-large file="true">img/alias/move_up_48.png</icon-large>    
+                </class>
                 <scripts>
-                    <file>js/org/ametys/ribbon/control/button/AliasMoveButton.js</file>
-                    <file>js/org/ametys/web/tool/alias/AliasActions.js</file>
+                    <file>js/Ametys/plugins/web/alias/MoveAliasController.js</file>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
                 </scripts>
                 <right>Web_Rights_Alias_Handle</right>
             </extension>
             
-            <extension id="org.ametys.web.Alias.moveDown"
+            <extension id="org.ametys.web.alias.MoveDown"
                        point="org.ametys.core.ui.RibbonControlsManager" 
                        class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="org.ametys.ribbon.control.button.AliasMoveButton">
-                    <param name="action">org.ametys.web.alias.MoveDown.act</param>
-                    <param name="target-type">^alias$</param>
-                    <param name="enable-multiselection">false</param>
-                    <param name="direction">down</param>
+                <class name="Ametys.plugins.web.alias.MoveAliasController">
+                    <action>Ametys.plugins.web.alias.AliasActions.move</action>
+                    <selection-target-type>^alias$</selection-target-type>
+                    <selection-enable-multiselection>false</selection-enable-multiselection>
                     
-                    <param name="label" i18n="true">PLUGINS_WEB_ALIAS_MOVE_DOWN_LABEL</param>
-                    <param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_MOVE_DOWN_DESCRIPTION</param>
+                    <role>move-down</role>
                     
-                    <param name="icon-small" file="true">img/alias/move_down_16.png</param>
-                    <param name="icon-medium" file="true">img/alias/move_down_32.png</param>
-                    <param name="icon-large" file="true">img/alias/move_down_48.png</param>
-                </action>
+                    <label i18n="true">PLUGINS_WEB_ALIAS_MOVE_DOWN_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_MOVE_DOWN_DESCRIPTION</description>
+    
+                    <icon-small file="true">img/alias/move_down_16.png</icon-small>
+                    <icon-medium file="true">img/alias/move_down_32.png</icon-medium>
+                    <icon-large file="true">img/alias/move_down_48.png</icon-large>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/ribbon/control/button/AliasMoveButton.js</file>
-                    <file>js/org/ametys/web/tool/alias/AliasActions.js</file>
+                    <file>js/Ametys/plugins/web/alias/MoveAliasController.js</file>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
                 </scripts>
                 <right>Web_Rights_Alias_Handle</right>
             </extension>
             
-            <extension id="org.ametys.web.Alias.moveLast"
+            <extension id="org.ametys.web.alias.MoveLast"
                        point="org.ametys.core.ui.RibbonControlsManager" 
                        class="org.ametys.core.ui.StaticClientSideElement">
-                <action class="org.ametys.ribbon.control.button.AliasMoveButton">
-                    <param name="action">org.ametys.web.alias.MoveLast.act</param>
-                    <param name="target-type">^alias$</param>
-                    <param name="enable-multiselection">false</param>
-                    <param name="direction">down</param>
+                <class name="Ametys.plugins.web.alias.MoveAliasController">
+                    <action>Ametys.plugins.web.alias.AliasActions.move</action>
+                    <selection-target-type>^alias$</selection-target-type>
+                    <selection-enable-multiselection>false</selection-enable-multiselection>
                     
-                    <param name="label" i18n="true">PLUGINS_WEB_ALIAS_MOVE_BOTTOM_LABEL</param>
-                    <param name="default-description" i18n="true">PLUGINS_WEB_ALIAS_MOVE_BOTTOM_DESCRIPTION</param>
+                    <role>move-last</role>
                     
-                    <param name="icon-small" file="true">img/alias/move_bottom_16.png</param>
-                    <param name="icon-medium" file="true">img/alias/move_bottom_32.png</param>
-                    <param name="icon-large" file="true">img/alias/move_bottom_48.png</param>
-                </action>
+                    <label i18n="true">PLUGINS_WEB_ALIAS_MOVE_BOTTOM_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_ALIAS_MOVE_BOTTOM_DESCRIPTION</description>
+    
+                    <icon-small file="true">img/alias/move_bottom_16.png</icon-small>
+                    <icon-medium file="true">img/alias/move_bottom_32.png</icon-medium>
+                    <icon-large file="true">img/alias/move_bottom_48.png</icon-large>
+                </class>
                 <scripts>
-                    <file>js/org/ametys/ribbon/control/button/AliasMoveButton.js</file>
-                    <file>js/org/ametys/web/tool/alias/AliasActions.js</file>
+                    <file>js/Ametys/plugins/web/alias/MoveAliasController.js</file>
+                    <file>js/Ametys/plugins/web/alias/AliasActions.js</file>
                 </scripts>
                 <right>Web_Rights_Alias_Handle</right>
-            </extension> -->
+            </extension>
 		</extensions>
 	</feature>
 	
@@ -5808,7 +5805,7 @@
 	 	  + -->
 	 <feature name="userinterface.attachments.ribbon">
        	<extensions>
-       	
+       	    
        		<!-- <extension id="org.ametys.web.AttachmentTab"
     				   point="org.ametys.core.ui.RibbonTabsManager"
     				   class="org.ametys.core.ui.StaticClientSideElement">
Index: main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasActions.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasActions.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasActions.js	(revision 0)
@@ -0,0 +1,602 @@
+/*
+ *  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 gathering all the actions the user can perform on aliases
+ */
+Ext.define('Ametys.plugins.web.alias.AliasActions', {
+	singleton: true,
+	
+	/**
+	 * @private
+	 * @property {Ametys.window.DialogBox} _box The dialog box for
+	 *           edition/creation of aliases
+	 */
+	
+	/**
+	 * @private
+	 * @property {Ext.form.Panel} _form The form inside the dialog box
+	 */
+	
+	/**
+	 * @private
+	 * @property {Boolean} _initialized true when the box is initialized
+	 */
+	
+	/**
+	 * @private
+	 * @property {String} _mode Can be 'new' or 'edit'
+	 */
+	
+	/**
+	 * @private
+	 * @property {String} _aliasType the type of alias. Can be "URL" or "PAGE"
+	 */
+	
+	/**
+	 * Create an alias
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	add: function(controller)
+	{
+		this._mode = 'new';
+		this._aliasType = "URL"; // FIXME
+        this._delayedInitialize();
+	        
+		this._box.setTitle("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_CREATE'/>");
+		this._box.setConfig('icon', Ametys.getPluginResourcesPrefix('web') + "/img/alias/add_16.png");
+		this._box.show();
+		
+		this._initForm();
+	},
+	
+	/**
+	 * Edit an alias
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	edit: function(controller)
+	{
+		var target = controller.getMatchingTargets()[0];
+		if (target != null)
+		{
+			this._mode = 'edit';
+	        this._delayedInitialize();
+			
+			this._box.setTitle("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_EDIT'/>");
+			this._box.setConfig('icon', Ametys.getPluginResourcesPrefix('web') + "/img/alias/edit_16.png");
+			this._box.show();
+			
+			this._initForm(target);
+		}
+	},
+	
+	/**
+	 * Delete an alias
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	deleteAlias: function(controller)
+	{
+		var targets = controller.getMatchingTargets();
+		if (targets.length > 0)
+		{
+			Ext.Msg.confirm("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_DELETE'/>", 
+					"<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_DELETE_CONFIRM'/>", 
+					Ext.bind(this._doDeleteAlias, this, [targets], 1), 
+					this
+			);
+		}
+	},
+	
+	/**
+	 * Delete an alias
+	 * @param {String} btn which button was pressed
+	 * @param {Object[]} targets The selected aliases
+	 */
+	_doDeleteAlias: function (btn, targets)
+	{
+		if (btn == 'yes')
+		{
+			var ids = [];
+			for (var i=0; i < targets.length; i++)
+			{
+				ids.push(targets[i].getParameters().id);
+			}
+			
+			Ametys.data.ServerComm.callMethod({
+				role: "org.ametys.web.alias.AliasDAO",
+				methodName: 'deleteAlias',
+				parameters: [ids],
+				callback: {
+					handler: this._deleteAliasCb,
+					scope: this,
+					arguments: {
+						ids: ids
+					}
+				},
+				errorMessage: {
+					category: this.self.getName(),
+					msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_DELETE_ERROR'/>"
+				}
+			});
+		}
+	},
+
+	/**
+	 * Callback for alias deletion process
+	 * @param {Object} response the server's response
+	 * @param {Object} args the callback arguments
+	 */
+	_deleteAliasCb: function (response, args)
+	{
+		var message = response.msg;
+		if (message == "unknown-alias")
+		{
+			Ext.Msg.show({
+				   title: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_DELETE'/>",
+				   msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_UNKNOWN'/>",
+				   buttons: Ext.Msg.OK,
+				   icon: Ext.MessageBox.ERROR
+			});
+			return;
+		}
+		
+		var targets = [];
+		var ids = args.ids;
+		for (var i=0; i < ids.length; i++)
+		{
+			var target = Ext.create('Ametys.message.MessageTarget', {
+				type: Ametys.message.MessageTarget.ALIAS,
+				parameters: {id: ids[i]}
+			});
+			
+			targets.push(target)
+		}
+		
+		if (targets.length > 0)
+		{
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.DELETED,
+				targets: targets
+			});
+		}
+	},
+	
+	/**
+	 * Move an alias
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
+	 */
+	move: function(controller)
+	{
+		var targets = controller.getMatchingTargets();
+		if (targets.length != 1)
+		{
+			return;
+		}
+		
+		var target = targets[0];
+		var id = target.getParameters().id;
+	    
+	    Ametys.data.ServerComm.callMethod({
+			role: "org.ametys.web.alias.AliasDAO",
+			methodName: 'moveAlias',
+			parameters: [id, controller.role],
+			callback: {
+				handler: this._moveCb,
+				scope: this,
+				arguments: {
+					id: id
+				}
+			},
+			errorMessage: {
+				category: this.self.getName(),
+				msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_MOVE_ERROR'/>"
+			}
+		});
+	},
+	
+	/**
+	 * Callback for alias moving process
+	 * @param {Object} response the server's response
+	 * @param {Object} args the callback arguments
+	 */
+	_moveCb: function(response, args)
+	{
+		// Trigger the refreshment of the store
+	    Ext.create('Ametys.message.Message', {
+			type: Ametys.message.Message.MOVED,
+			targets: [{
+				type: Ametys.message.MessageTarget.ALIAS,
+				parameters: {}
+			}]
+		});
+	},
+	
+	/**
+	 * Initialization of the dialog box
+	 */
+	_delayedInitialize: function()
+	{
+		if (this._initialized)
+		{
+	        return;
+		}
+		
+		var formPanel = Ext.create('Ext.form.Panel', {
+			labelWidth: 120,
+			bodyStyle: 'padding:10px',
+			
+			defaultType :'textfield',
+			
+			autoScroll: true,
+			items: [ Ext.create('Ext.form.Hidden', {
+		        		name: 'id'
+					 }),
+					 Ext.create('Ext.form.Hidden', {
+			        		name: 'type',
+			        		value: 'PAGE'
+						 }),
+					 Ext.create('Ametys.form.widget.Text', {
+						 name: 'url',
+						 fieldLabel: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_ORIGIN_URL'/>",
+						 desc: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_ORIGIN_URL_DESC'/>",
+					     allowBlank: false,
+					     msgTarget: 'side',
+					     width: 300,
+					     regex: new RegExp('^/.+$'),
+					     // regex: new RegExp('^/[a-z0-9-_?./&amp;=%*]+$',
+							// 'i'),
+					     regexText: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_INVALID_URL'/>"
+					 }),
+					// org.ametys.utils.Widgets.createWidget ('string', 'sitemap', {
+					// name: 'page_target',
+					// fieldLabel: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_TARGET_PAGE'/>",
+					// desc: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_TARGET_PAGE_DESC'/>",
+					// width: 300,
+					// allowBlank: false,
+					// msgTarget: 'side',
+					// multiple: false
+					// }, null),
+					 Ext.create('Ametys.form.widget.Text', {
+						 name: 'url_target',
+						 fieldLabel: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_TARGET_URL'/>",
+						 desc: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_TARGET_URL_DESC'/>",
+					     allowBlank: false,
+					     msgTarget: 'side',
+					     width: 300,
+					     regex: new RegExp ('^(https?://|/)[^?]+$'),
+					     regexText: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_INVALID_TARGET_URL'/>"
+					 }),
+					 Ext.create('Ametys.form.widget.Date', {
+						 name: 'expiration_date',
+						 fieldLabel: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_EXPIRATION_DATE'/>",
+						 desc: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_EXPIRATION_DATE_DESC'/>",
+					     msgTarget: 'side',
+					     value: '2010-04-16T00:00:00.000+02:00',
+					     width: 200
+					 })
+			]
+		});
+		
+		this._form = formPanel.getForm();
+		
+		this._box = Ext.create('Ametys.window.DialogBox', {
+			title :"<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_CREATE'/>",
+			icon : Ametys.getPluginResourcesPrefix('web') + "/img/alias/add_16.png",
+			
+			layout :'fit',
+			width :550,
+			height : 200,
+			
+			items : [ formPanel ],
+			
+			defaultButton: formPanel.getForm().findField('url'),
+			closeAction: 'hide',
+			buttons : [ {
+				text :"<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_BTN_OK'/>",
+				handler : Ext.bind(this._ok, this)
+			}, {
+				text :"<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_BTN_CANCEL'/>",
+				handler : Ext.bind(this._cancel, this)
+			} ]
+		});
+		
+		this._initialized = true;
+	},
+	
+	
+	/**
+	 * @private Initialize the form param {Object} the first matching target
+	 */
+	_initForm: function (target)
+	{
+		if (this._mode == 'new')
+		{
+			var form = this._form;
+			
+			this._setType (this._aliasType);
+			
+		    form.findField('id').setValue('');
+		    form.findField('url').setValue('');
+		    form.findField('url').clearInvalid();
+// form.findField('page_target').setValue('');
+		    form.findField('url_target').setValue('');
+// form.findField('page_target').clearInvalid();
+		    form.findField('url_target').clearInvalid('');
+		    var date = new Date();
+		    form.findField('expiration_date').setValue(date);
+		    form.findField('expiration_date').clearInvalid('');
+		}
+		else
+		{
+			var id = target.getParameters().id;
+			
+			// Get selected alias info
+			Ametys.data.ServerComm.callMethod({
+				role: "org.ametys.web.alias.AliasDAO",
+				methodName: 'getAlias',
+				parameters: [id],
+				callback: {
+					handler: this._initFormCb,
+					scope: this
+				},
+				errorMessage: {
+					category: this.self.getName(),
+					msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_SELECTION_ERROR'/>"
+				}
+			});
+		}
+	},
+	
+	/**
+	 * @private Callback for the form initialization process
+	 * @param {Object} response the server's response
+	 * @param {Object} response.alias the alias
+	 * @param {Object} args the callback arguments
+	 */
+	_initFormCb: function(response, args)
+	{
+		var form = this._form;
+		
+		var id = response.id;
+		form.findField('id').setValue(id);
+		
+		var url = response.url;
+		form.findField('url').setValue(url);
+		
+		var type = response.type;
+		this._setType (type);
+	    
+		var target = response.target;
+//	    if (type == 'PAGE')
+//	    {
+//// form.findField('page_target').setValue(target);
+//	    }
+//	    else
+//	    {
+	    	form.findField('url_target').setValue(target);
+//	    }
+
+    	var expirationDate = response.expirationDate;
+	    var date = Ext.Date.parse(expirationDate, Ext.Date.patterns.ISO8601DateTime);
+	    form.findField('expiration_date').setValue(date);
+	},
+
+	_ok: function ()
+	{
+		if (this._form.isValid())
+		{
+			var relHref = this._form.findField('url').getValue();
+			if (/^\/([a-z]{2})\/(.*)\.html$/.test(relHref))
+			{
+// var params = {"siteName": Ametys.getAppParameter('siteName'), "language":
+// RegExp.$1, "path": RegExp.$2 };
+// var serverMessage = new org.ametys.servercomm.ServerMessage('web',
+// 'page/path-to-id.xml', params,
+// org.ametys.servercomm.ServerComm.PRIORITY_SYNCHRONOUS,
+// this._handleLinkCallBack, this, []);
+// var response =
+// org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+//
+// var id =
+// response.selectSingleNode("page")[org.ametys.servercomm.ServerComm.xmlTextContent];
+				
+// if (id != '')
+// {
+// Ext.Msg.show({
+// title: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_PAGE_EXISTS'/>",
+// msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_PAGE_EXISTS_HINT'/>",
+// buttons: Ext.Msg.YESNO,
+// icon: Ext.MessageBox.WARNING,
+// fn: this._createOrUpdateAlias
+// });
+// return;
+// }
+			}
+			
+			this._createOrUpdateAlias ();
+		}
+	},
+	
+	/**
+	 * Create or update an alias
+	 * @param {String} btn which button was pressed
+	 */
+	_createOrUpdateAlias: function (btn)
+	{
+		if (btn == null || btn == 'yes')
+		{
+			var form = this._form;
+			
+			// Parameters
+			var type = form.findField('type').getValue(),
+			    url = form.findField('url').getValue(),
+				target = form.findField('url_target').getValue(),
+			// params.target = params.type == 'PAGE' ?
+			// form.findField('page_target').getValue() :
+			    siteName = Ametys.getAppParameter('siteName'),
+			    date,
+			    id;
+			
+			if (form.findField('expiration_date').getValue() != '')
+			{
+				var date = form.findField('expiration_date').getValue();
+				date = Ext.Date.format(date, Ext.Date.patterns.ISO8601DateTime);
+			}
+			
+			if (this._mode == 'edit')
+			{
+				id = form.findField('id').getValue();
+			}
+			
+			Ametys.data.ServerComm.callMethod({
+				role: "org.ametys.web.alias.AliasDAO",
+				methodName: this._mode == 'new' ? 'createAlias' : 'updateAlias',
+				parameters: this._mode == 'new' ? [type, url, target, siteName, date] : [id, type, url, target, siteName, date],
+				callback: {
+					handler: this._createOrUpdateAliasCb,
+					scope: this,
+					arguments: {
+						mode: this._mode
+					}
+				},
+				errorMessage: {
+					category: this.self.getName(),
+					msg: this._mode == 'new' ? "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_CREATE_ERROR'/>" : "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_EDIT_ERROR'/>"
+				}
+			});
+		}
+	},
+	
+	/**
+	 * Callback for the creation/update of alias
+	 * @param {Object} response the server's response
+	 * @param {Object} args the callback arguments
+	 * @param {String} args.mode the mode (creation or update)
+	 */
+	_createOrUpdateAliasCb: function (response, args)
+	{
+		var message = response.msg;
+		if (args.mode == 'new')
+		{
+			if (message == 'already-exists')
+			{
+				Ext.Msg.show({
+					   title: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_CREATE'/>",
+					   msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_CREATE_ALREADY_EXISTS'/>",
+					   buttons: Ext.Msg.OK,
+					   icon: Ext.MessageBox.ERROR
+				});
+				return;
+			}
+			else if (message == 'invalid-url')
+			{
+				this._form.findField('url').markInvalid("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_INVALID_URL'/>");
+				return;
+			}
+			else if (message == 'invalid-target-url')
+			{
+				this._form.findField('url_target').markInvalid("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_INVALID_TARGET_URL'/>");
+				return;
+			}
+			
+			this._box.hide();
+			
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.CREATED,
+				targets: [{
+					type: Ametys.message.MessageTarget.ALIAS,
+					parameters: {'id': response.id}
+				}]
+			});
+		}
+		else
+		{
+			if (message == "unknown-alias")
+			{
+				Ext.Msg.show({
+					   title: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_EDIT'/>",
+					   msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_UNKNOWN'/>",
+					   buttons: Ext.Msg.OK,
+					   icon: Ext.MessageBox.ERROR
+				});
+				return;
+			}
+			else if (message == 'already-exists')
+			{
+				Ext.Msg.show({
+					   title: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_EDIT'/>",
+					   msg: "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_CREATE_ALREADY_EXISTS'/>",
+					   buttons: Ext.Msg.OK,
+					   icon: Ext.MessageBox.ERROR
+				});
+				return;
+			}
+			else if (message == 'invalid-url')
+			{
+				this._form.findField('url').markInvalid("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_INVALID_URL'/>");
+				return;
+			}
+			else if (message == 'invalid-target-url')
+			{
+				this._form.findField('url_target').markInvalid("<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_INVALID_TARGET_URL'/>");
+				return;
+			}
+			
+			this._box.hide();
+			
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.MODIFIED,
+				targets: [{
+					type: Ametys.message.MessageTarget.ALIAS,
+					parameters: {'id': response.id}
+				}]
+			});;
+		}
+	},
+	
+	/**
+	 * @private
+	 * Cancel the update/creation process
+	 */
+	_cancel: function ()
+	{
+		this._box.hide();
+	},
+	
+	/**
+	 * @private 
+	 * Set the type of the alias
+	 * @param {String} type the type to set
+	 */
+	_setType: function (type)
+	{
+		var form = this._form;
+		form.findField('type').setValue(type);
+// if (type == 'PAGE')
+// {
+// form.findField("url_target").getEl().up('.x-field').setDisplayed(false);
+// form.findField("url_target").disable();
+// form.findField("page_target").getEl().up('.x-field').setDisplayed(true);
+// form.findField("page_target").enable();
+// }
+// else
+// form.findField("page_target").getEl().up('.x-field').setDisplayed(false);
+// form.findField("page_target").disable();
+			form.findField("url_target").getEl().setVisible(true);
+			form.findField("url_target").enable();
+// }
+	}
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasTool/Alias.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasTool/Alias.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasTool/Alias.js	(revision 0)
@@ -0,0 +1,32 @@
+/*
+ *  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.
+ */
+
+/**
+ * @private
+ * The model used in the {@link Ametys.plugins.web.alias.AliasTool} grid
+ */
+Ext.define('Ametys.plugins.web.alias.AliasTool.Alias', {
+    extend: 'Ext.data.Model',
+    
+    fields: [
+       {name: 'id', mapping: 'id'},
+       {name: 'url', mapping: 'url'},
+       {name: 'target', mapping: 'target'},
+       {name: 'createdAt', mapping: 'createdAt', type: 'date', dateFormat: Ext.Date.patterns.ISO8601DateTime},
+       {name: 'expirationDate', mapping: 'expirationDate', type: 'date', dateFormat: Ext.Date.patterns.ISO8601DateTime},
+       {name: 'expired', mapping: 'expired'}
+    ]
+});
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasTool.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasTool.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/alias/AliasTool.js	(revision 0)
@@ -0,0 +1,255 @@
+/*
+ *  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 aliases and allows to perform several operations such as : creating, updating, deleting, or change the priority of an alias.
+ */
+Ext.define('Ametys.plugins.web.alias.AliasTool', {
+	extend: 'Ametys.tool.Tool',
+	
+	/**
+	 * @private
+	 * @property {Ext.grid.Panel} _aliasGrid The alias grid
+	 */
+	
+	constructor: function(config)
+	{
+		this.callParent(arguments);
+		Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onAliasDeleted, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onAliasCreatedOrModified, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._onAliasCreatedOrModified, this);
+		Ametys.message.MessageBus.on(Ametys.message.Message.MOVED, this._onAliasMoved, this);
+	},
+	
+	getMBSelectionInteraction: function() 
+	{
+		return Ametys.tool.Tool.MB_TYPE_ACTIVE;
+	},
+	
+	createPanel: function ()
+	{
+		this._aliasGrid = this._drawAliasPanel();
+		return this._aliasGrid;
+	},
+	
+	setParams: function (params)
+	{
+		this.callParent(arguments);
+		this.refresh();
+	},
+	
+	sendCurrentSelection: function()
+	{
+		this._selectAlias();
+	},
+	
+	refresh: function ()
+	{
+		this.showRefreshing();
+		this._aliasGrid.getStore().load({callback: this.showRefreshed, scope: this});
+	},
+	
+	/**
+	 * @private
+	 * Draw the grid panel showing the aliases
+	 * @return {Ext.grid.Panel} The grid panel
+	 */
+	_drawAliasPanel: function()
+	{
+		var store = Ext.create('Ext.data.Store', {
+			model: 'Ametys.plugins.web.alias.AliasTool.Alias',
+	        groupField: 'expired',
+	        
+//	        sortOnLoad: true,
+//	        sorters: [ { property: 'date', direction: "DESC" }, { property: 'location', direction: "ASC" }],
+	        
+	        proxy: {
+	        	type: 'ametys',
+				plugin: 'web',
+				url: 'alias/list.json',
+	        	reader: {
+	        		type: 'json',
+					rootProperty: 'alias'
+	        	}
+	        },
+	        
+	        listeners: {
+	        	'beforeload' : Ext.bind(this._onBeforeLoad, this),
+	        	'load': Ext.bind(this._selectAlias, this)
+	        }
+		});		
+		
+		return Ext.create('Ext.grid.Panel', {
+			region: 'center',
+
+			stateful: true,
+			stateId: this.self.getName() + "$grid",
+
+			store : store,
+			
+			selModel : {
+		    	mode: 'MULTI'
+		    },
+		    
+		    listeners: {
+		    	'selectionchange': Ext.bind(this._selectAlias, this)
+		    },
+		    
+		    features: [{
+		        groupHeaderTpl: ['{name}'],
+		        ftype: 'grouping'
+		    }],
+			
+		    columns: [
+		        {stateId: 'grid-origin', header: "<i18n:text i18n:key='PLUGINS_WEB_ALIAS_UITOOL_ORIGIN'/>", width : 270, sortable: true, dataIndex: 'url'},
+		        {stateId: 'grid-target', header: "<i18n:text i18n:key='PLUGINS_WEB_ALIAS_UITOOL_TARGET'/>", width : 270, sortable: true, dataIndex:'targetUrl', renderer: Ext.bind(this._renderTargetUrl, this)},
+		        {stateId: 'grid-expirationDate', header: "<i18n:text i18n:key='PLUGINS_WEB_ALIAS_UITOOL_EXPIRATION_DATE'/>", width : 160, sortable: true, dataIndex: 'expirationDate', align :'right', renderer: Ext.util.Format.dateRenderer('d F Y')},
+		        {stateId: 'grid-status', header: "<i18n:text i18n:key='PLUGINS_WEB_ALIAS_UITOOL_STATUS'/>", width : 270, hidden: true, sortable: true, dataIndex: 'expired', align : 'right', renderer: Ext.bind(this._renderExpired, this)}
+		    ]
+		});
+	},
+	
+	/**
+	 * @private
+	 * Render the target url in the aliases grid
+	 * @param {String} value the value of the target url
+	 */
+	_renderTargetUrl: function (value)
+	{
+		if (value == 'unknown')
+		{
+			return '<span style="color: #666; font-style: italic">' + "<i18n:text i18n:key='PLUGINS_WEB_HANDLE_ALIAS_UNKNOWN_PAGE'/>" + '</span>'
+		}
+		
+		return value;
+	},
+	
+	/**
+	 * @private
+	 * Render the expired column of the grid
+	 * @param {String} value the value of 'expired'
+	 */
+	_renderExpired: function (value)
+	{
+		if (value == "true")
+		{
+			return '<img src="' + Ametys.getPluginResourcesPrefix('web') + "/img/alias/expired_13.png" + '" style="float:left; margin-right: 5px"/>' + "<i18n:text i18n:key='PLUGINS_WEB_ALIAS_UITOOL_STATUS_EXPIRED'/>";
+		}
+		
+		return '<img src="' + Ametys.getPluginResourcesPrefix('web') + "/img/alias/active_13.png" + '" style="float:left; margin-right: 5px"/>' + "<i18n:text i18n:key='PLUGINS_WEB_ALIAS_UITOOL_STATUS_ACTIVE'/>";
+	},
+	
+	/**
+	 * @private
+	 * Function called when the 'beforeload' event is fired
+	 * @param {Ext.data.Store} store the store
+	 * @param {Ext.data.operation.Operation} the object that will be passed to the proxy to load the store
+	 */
+	_onBeforeLoad: function(store, operation)
+	{
+		operation.setParams(Ext.apply(operation.getParams() || {}, {
+			siteName: Ametys.getAppParameter('siteName')
+		}));
+	},
+	
+	/**
+	 * @private
+	 * Sends a {@link Ametys.message.Message#SELECTION_CHANGED} message containing the selected alias on the message bus 
+	 */
+	_selectAlias: function()
+	{
+		var targets = [];
+		var store = this._aliasGrid.getStore();
+		
+		var selectedAliases = this._aliasGrid.getSelectionModel().getSelection();
+		Ext.Array.forEach(selectedAliases, function(selectedAlias) {
+			var id = selectedAlias.id;
+			target = Ext.create('Ametys.message.MessageTarget', {
+				type: Ametys.message.MessageTarget.ALIAS,
+				parameters: {id: selectedAlias.id, index: store.find('id', id), size: store.getTotalCount()}
+			});
+			
+			targets.push(target);
+		});
+		
+		if (targets.length > 0)
+		{
+			Ext.create('Ametys.message.Message', {
+				type: Ametys.message.Message.SELECTION_CHANGED,
+				targets: targets
+			});
+		}
+	},
+	
+	/**
+	 * @private
+	 * Listener upon reception of a creation message 
+	 * @param {Ametys.message.Message} message the creation message
+	 */
+	_onAliasCreatedOrModified: function(message)
+	{
+		var targets = message.getTargets(Ametys.message.MessageTarget.ALIAS);
+		if (targets.length > 0)
+		{
+			this.showOutOfDate();
+		}
+	},
+	
+	/**
+	 * @private
+	 * Listener upon reception of a deletion message
+	 * @param {Ametys.message.Message} message the deletion message
+	 */
+	_onAliasDeleted: function(message)
+	{
+		var targets = message.getTargets(Ametys.message.MessageTarget.ALIAS);
+		if (targets.length > 0)
+		{
+			var store = this._aliasGrid.getStore();
+			Ext.Array.forEach(targets, function(target) {
+				var id = target.getParameters().id;
+				var index = store.find("id", id); 
+				if (index != -1)
+				{
+					store.removeAt(index);
+				}
+			});
+		}
+	},
+	
+	/**
+	 * @private
+	 * Listener upon reception of a moved message
+	 * @param {Ametys.message.Message} message the deletion message
+	 */
+	_onAliasMoved: function(message)
+	{
+		this.refresh();
+	}
+});
+
+Ext.define("Ametys.message.AliasMessageTarget",{
+	override: "Ametys.message.MessageTarget",
+	statics: 
+	{
+		/**
+		 * @member Ametys.message.MessageTarget
+		 * @readonly
+		 * @property {String} ALIAS The target of the message is an alias 
+		 */
+		ALIAS: "alias"
+	}
+});
Index: main/plugin-web/resources/js/Ametys/plugins/web/alias/MoveAliasController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/alias/MoveAliasController.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/alias/MoveAliasController.js	(revision 0)
@@ -0,0 +1,83 @@
+/*
+ *  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 gathering all the actions the user can perform on aliases
+ */
+Ext.define('Ametys.plugins.web.alias.MoveAliasController', {
+	extend: 'Ametys.ribbon.element.ui.ButtonController',
+	
+	/**
+	 * @cfg {String} role the role of the button. Can be "move-first", "move-up", "move-down" or "move-last"
+	 */
+	contructor: function(config)
+	{
+		this.callParent(arguments);
+		
+		this.role = config.role; 
+		Ametys.message.MessageBus.on(Ametys.message.Message.MOVED, this._onMoved, this);
+	},
+	
+	/**
+	 * @private
+	 * Listener when the selection has changed
+	 * Will update the state of the buttons effectively upon the current selection.
+	 * @param {Ametys.message.Message} message The modified message.
+	 */
+	_onMoved: function (message)
+	{
+		this.refresh();
+	},
+	
+	updateState: function()
+	{
+		this._getStatus(this.getMatchingTargets());
+	},
+	
+	areSameTargets: function(target1, target2)
+	{
+		if (target1.getParameters().index != null && target2.getParameters().index != null)
+		{
+			return target1.getParameters().index == target2.getParameters().index;
+		}
+		
+		return this.callParent(arguments);
+	},
+	
+	/**
+	 * @private
+	 * Get the logging level of the selected target
+	 * @param targets The content targets
+	 */
+	_getStatus: function (targets)
+	{
+		targets.length == 1 && this._testAlias(targets[0]) ? this.enable() : this.disable(); 
+	},
+	
+	/**
+	 * @private
+	 * Test alias position
+	 * @return {org.ametys.messagebus.message.MessageTarget} The target to check
+	 */
+	_testAlias: function(target)
+	{
+		var role = this.role,
+	        index = target.getParameters().index,
+	        size= target.getParameters().size;
+	        
+	    return (((role == 'move-up' || role == 'move-first') && index != 0) || ((role == 'move-down' || role == 'move-last') && index != size - 1)) ? true : false;
+	}
+});
\ No newline at end of file
Index: main/plugin-web/sitemap-back.xmap
===================================================================
--- main/plugin-web/sitemap-back.xmap	(revision 32412)
+++ main/plugin-web/sitemap-back.xmap	(working copy)
@@ -59,8 +59,6 @@
 			<map:generator name="service" src="org.ametys.web.service.ServiceGenerator"/>
 			<map:generator name="zoneitem-service" src="org.ametys.web.repository.page.generators.ZoneItemServiceGenerator"/>
 			
-			<map:generator name="alias-list" src="org.ametys.web.alias.AliasListGenerator"/>
-			<map:generator name="alias" src="org.ametys.web.alias.AliasGenerator"/>
 			<map:generator name="tags" src="org.ametys.web.tags.generators.TagsGenerator" label="xml" pool-grow="2" pool-max="16" pool-min="0"/>
 			<map:generator name="tags-list" src="org.ametys.web.tags.generators.TagsListGenerator" label="xml" pool-grow="2" pool-max="16" pool-min="0"/>
 			<map:generator name="jcr-tags" src="org.ametys.web.tags.generators.JCRTagsGenerator" label="xml" pool-grow="2" pool-max="16" pool-min="0"/>
@@ -137,13 +135,10 @@
 		  	<map:action name="delete-tag" src="org.ametys.web.tags.actions.DeleteTagAction"/>
 		  	<map:action name="import-tags" src="org.ametys.web.tags.actions.ImportTagsAction"/>
 
-			<map:action name="create-alias" src="org.ametys.web.alias.CreateAliasAction"/>
-		  	<map:action name="update-alias" src="org.ametys.web.alias.UpdateAliasAction"/>
-		  	<map:action name="delete-alias" src="org.ametys.web.alias.DeleteAliasAction"/>
-            <map:action name="move-alias" src="org.ametys.web.alias.MoveAliasAction" logger="org.ametys.web.alias.MoveAliasAction"/>
-		  	
 		  	<map:action name="generate-site" src="org.ametys.web.cocoon.GenerateSiteAction"/>
 		  	
+            <map:action name="alias-list" src="org.ametys.web.alias.GetAliasListAction"/>
+		  	
 		  	<map:action name="delete-content" src="org.ametys.web.repository.content.actions.DeleteContentAction"/>
 		  	<map:action name="update-content-privacy" src="org.ametys.web.repository.content.actions.UpdateContentPrivacyAction"/>
 		  	<map:action name="web-archive" src="org.ametys.web.workflow.ArchiveWebContentAction"/>
@@ -966,63 +961,16 @@
     		<!-- +
     			 | ALIAS
     			 + -->
-			<map:match pattern="alias/list.xml">
-				<map:generate type="alias-list"/>
-				<map:serialize type="xml"/>	
-			</map:match>
-			
-			<map:match pattern="alias.xml">
-				<map:generate type="alias"/>
-				<map:serialize type="xml"/>	
+			<map:match pattern="alias/list.json">
+				<map:act type="alias-list">
+				    <map:read type="json"/>
+				</map:act>
 			</map:match>
 			
 			<!-- Protected Pipelines -->
 			<map:match pattern="alias/*">
 				<map:act type="has-not-right" src="Web_Rights_Alias_Handle"/>
 			</map:match>
-			
-			<map:match pattern="alias/create">
-				<map:act type="create-alias">
-					<map:generate type="action-result">
-						<map:parameter name="id" value="{id}"/>
-						<map:parameter name="msg" value="{msg}"/>
-					</map:generate>
-					<map:serialize type="xml"/>	
-				</map:act>
-			</map:match>
-			
-			<map:match pattern="alias/update">
-				<map:act type="update-alias">
-					<map:generate type="action-result">
-						<map:parameter name="id" value="{id}"/>
-						<map:parameter name="msg" value="{msg}"/>
-					</map:generate>
-					<map:serialize type="xml"/>	
-				</map:act>
-			</map:match>
-			
-			<map:match pattern="alias/delete">
-				<map:act type="delete-alias">
-					<map:generate type="action-result">
-						<map:parameter name="msg" value="{msg}"/>
-					</map:generate>
-					<map:serialize type="xml"/>	
-				</map:act>
-			</map:match>
-            
-            <map:match pattern="alias/move">
-                <map:act type="move-alias">
-                    <map:parameter name="siteName" value="{parent-context-attr:siteName}"/>
-                    <map:parameter name="id" value="{parent-context-attr:id}"/>
-                    <map:parameter name="previousSiblingId" value="{parent-context-attr:previousSiblingId}"/>
-                    <map:parameter name="nextSiblingId" value="{parent-context-attr:nextSiblingId}"/>
-                    <map:parameter name="destination" value="{parent-context-attr:destination}"/>
-                    <map:generate type="action-result">
-                        <map:parameter name="msg" value="{msg}"/>
-                    </map:generate>
-                    <map:serialize type="xml"/> 
-                </map:act>
-            </map:match>
             
     		<!-- +
     			 | ATTACHMENTS
Index: main/plugin-web/src/org/ametys/web/alias/AliasDAO.java
===================================================================
--- main/plugin-web/src/org/ametys/web/alias/AliasDAO.java	(revision 0)
+++ main/plugin-web/src/org/ametys/web/alias/AliasDAO.java	(revision 0)
@@ -0,0 +1,480 @@
+/*
+ *  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.web.alias;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.commons.lang.StringUtils;
+
+import org.ametys.core.ui.Callable;
+import org.ametys.plugins.repository.AmetysObject;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.ModifiableAmetysObject;
+import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
+import org.ametys.plugins.repository.TraversableAmetysObject;
+import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.runtime.parameter.ParameterHelper;
+import org.ametys.runtime.parameter.ParameterHelper.ParameterType;
+import org.ametys.web.alias.Alias.TargetType;
+import org.ametys.web.repository.site.SiteManager;
+
+/**
+ * Class managing {@link Alias} creation, modification, deletion and moving
+ */
+public class AliasDAO extends AbstractLogEnabled implements Component, Serviceable
+{
+    /** The component's role */
+    public static final String ROLE = AliasDAO.class.getName();
+    
+    /** The PAGE pattern */
+    public static final Pattern PAGE_PATTERN = Pattern.compile("/([^?]+)\\.html");
+    /** The URL pattern */
+    public static final Pattern URL_PATTERN = Pattern.compile("/.+");
+    /** The URL pattern */
+    public static final Pattern TARGET_URL_PATTERN = Pattern.compile("(https?://|/)[^?]+");
+    /** The alias default name */
+    public static final String DEFAULT_ALIAS_NAME = "alias";
+    
+    /** The Ametys object resolver */
+    private AmetysObjectResolver _ametysObjectResolver;
+    /** The site manager */
+    private SiteManager _siteManager;
+    
+    public void service(ServiceManager smanager) throws ServiceException
+    {
+        _ametysObjectResolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
+        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
+    }
+    
+    /**
+     * Get an alias
+     * @param id the id of the alias to get
+     * @return the JSON representation of the alias
+     */
+    @Callable
+    public Map<String, String> getAlias(String id)
+    {
+        DefaultAlias alias = _ametysObjectResolver.resolveById(id);
+        return alias2Json(alias);
+    }
+    
+    /**
+     * Create an alias
+     * @param type the type (page or url)
+     * @param url the origin url
+     * @param target the target url
+     * @param siteName the site's name
+     * @param dateStr the optional expiration date of the alias
+     * @return a map
+     */
+    @Callable
+    public Map<String, String> createAlias(String type, String url, String target, String siteName, String dateStr)
+    {
+        Map<String, String> result = new HashMap<>();
+        TargetType targetType = TargetType.valueOf(type);
+        
+        // Check if the alias does not already exist
+        if (_checkExistence(siteName, url))
+        {
+            result.put("msg", "already-exists");
+            return result;
+        }
+        
+        if (!isValidUrl(url))
+        {
+            result.put("msg", "invalid-url");
+            return result;
+        }
+        
+        if (TargetType.URL.equals(TargetType.valueOf(type)) && !isValidTargetUrl(target))
+        {
+            result.put("msg", "invalid-target-url");
+            return result;
+        }
+        
+        ModifiableTraversableAmetysObject rootNode = AliasHelper.getRootNode(_siteManager.getSite(siteName));
+        
+        String aliasName = AliasHelper.getAliasNextUniqueName(rootNode);
+        
+        DefaultAlias alias = rootNode.createChild(aliasName, "ametys:alias");
+        alias.setUrl(url);
+        alias.setTarget(target);
+        alias.setType(targetType);
+        alias.setCreationDate(new Date());
+        if (StringUtils.isNotEmpty(dateStr))
+        {
+            Date date = (Date) ParameterHelper.castValue(dateStr, ParameterType.DATE);
+            alias.setExpirationDate(date);
+        }
+        
+        rootNode.saveChanges();
+        
+        result.put("id", alias.getId());
+        
+        return result;
+    }
+    
+    /**
+     * Update an alias
+     * @param id the id of the alias to update
+     * @param type the type (page or url)
+     * @param url the origin url
+     * @param target the target url
+     * @param siteName the site's name
+     * @param dateStr the optional expiration date of the alias
+     * @return a map
+     */
+    @Callable
+    public Map<String, String> updateAlias(String id, String type, String url, String target, String siteName, String dateStr)
+    {
+        Map<String, String> result = new HashMap<>();
+        
+        // Check the alias does not already exist
+        if (_checkExistence(id, siteName, url))
+        {
+            result.put("msg", "already-exists");
+            return result;
+        }
+        
+        if (!isValidUrl(url))
+        {
+            result.put("msg", "invalid-url");
+            return result;
+        }
+        
+        try
+        {
+            DefaultAlias alias = _ametysObjectResolver.resolveById(id);
+            
+            if (TargetType.URL.equals(alias.getType()) && !isValidTargetUrl(target))
+            {
+                result.put("msg", "invalid-target-url");
+                return result;
+            }
+            
+            alias.setUrl(url);
+            alias.setTarget(target);
+            if (StringUtils.isNotEmpty(dateStr))
+            {
+                Date date = (Date) ParameterHelper.castValue(dateStr, ParameterType.DATE);
+                alias.setExpirationDate(date);
+            }
+            else
+            {
+                alias.removeExpirationDate();
+            }
+            
+            alias.saveChanges();
+            
+            result.put("id", alias.getId());
+        }
+        catch (UnknownAmetysObjectException e)
+        {
+            result.put("msg", "unknown-alias");
+            getLogger().error("Unable to edit alias. The alias of id '" + id + " doest not exist", e);
+        }
+        
+        return result;
+    }
+    
+    /**
+     * Delete an alias
+     * @param ids the list of ids of aliases to delete
+     * @return a map
+     */
+    @Callable
+    public Map<String, String> deleteAlias(List<String> ids)
+    {
+        Map<String, String> result = new HashMap<>();
+        for (String id : ids)
+        {
+            try
+            {
+                DefaultAlias alias = _ametysObjectResolver.resolveById(id);
+                ModifiableAmetysObject parent = alias.getParent();
+                alias.remove(); 
+                
+                parent.saveChanges();
+            }
+            catch (UnknownAmetysObjectException e)
+            {
+                result.put("msg", "unknown-alias");
+                getLogger().error("Unable to delete alias. The alias of id '" + id + " doest not exist", e);
+            }
+        }
+        
+        return result;
+    }
+    
+    /**
+     * Move an alias
+     * @param id the id of the alias to move
+     * @param role the action to perform
+     * @return an empty map
+     * @throws RepositoryException if an error occurs
+     */
+    @Callable
+    public Map<String, Object> moveAlias(String id, String role) throws RepositoryException
+    {
+        Map<String, Object> result = new HashMap<> ();
+         
+        DefaultAlias alias = _ametysObjectResolver.resolveById(id);
+        
+        if ("move-first".equals(role))
+        {
+            _moveFirst(alias);
+        }
+        else if ("move-up".equals(role))
+        {
+            _moveUp(alias);
+        }
+        else if ("move-down".equals(role))
+        {
+            _moveDown(alias);
+        }
+        else if ("move-last".equals(role))
+        {
+            _moveLast(alias);
+        }
+        
+        return result;
+    }
+    
+    
+    /**
+     * Move first.
+     * @param alias the alias to move 
+     * @throws RepositoryException
+     */
+    private void _moveFirst(DefaultAlias alias) throws RepositoryException
+    {
+        AmetysObjectIterable<AmetysObject>  children = null;
+        try
+        {
+            children = ((TraversableAmetysObject) alias.getParent()).getChildren();
+            // Resolve the link in the same session or the linkRoot.saveChanges() call below won't see the order changes.
+            alias.orderBefore(((TraversableAmetysObject) alias.getParent()).getChildren().next());
+            ((ModifiableAmetysObject) alias.getParent()).saveChanges();
+        }
+        finally
+        {
+            if (children != null)
+            {
+                children.close();
+            }
+        }
+    }
+
+    /**
+     * Move down.
+     * @param alias the alias to move 
+     * @throws RepositoryException
+     */
+    private void _moveDown(DefaultAlias alias) throws RepositoryException
+    {
+        TraversableAmetysObject parentNode = alias.getParent();
+        AmetysObjectIterable<AmetysObject> siblings = parentNode.getChildren();
+        
+        boolean iterate = true;
+        while (siblings.hasNext() && iterate)
+        {
+            DefaultAlias sibling = (DefaultAlias) siblings.next();
+            iterate = !sibling.getName().equals(alias.getName());
+        }
+        
+        // Move the link after his next sibling: move the next sibling before the link to move.
+        DefaultAlias nextLink = (DefaultAlias) siblings.next();
+        nextLink.orderBefore(alias);
+
+        alias.saveChanges();
+    }
+    
+    /**
+     * Move up.
+     * @param alias the alias to move
+     * @throws RepositoryException
+     */
+    private void _moveUp(DefaultAlias alias) throws RepositoryException
+    {
+        TraversableAmetysObject parentNode = alias.getParent();
+        AmetysObjectIterable<AmetysObject> siblings = parentNode.getChildren();
+        DefaultAlias previousLink = null;
+
+        while (siblings.hasNext())
+        {
+            DefaultAlias sibling = (DefaultAlias) siblings.next();
+            if (sibling.getName().equals(alias.getName()))
+            {
+                break;
+            }
+            
+            previousLink = sibling;
+        }
+            
+        // Move the link after his next sibling: move the next sibling before the link to move.
+        alias.orderBefore(previousLink);
+        alias.saveChanges();
+    }
+    
+    /**
+     * Move last.
+     * @param alias the alias to move 
+     * @throws RepositoryException
+     */
+    private void _moveLast(DefaultAlias link) throws RepositoryException
+    {
+        link.moveTo(link.getParent(), false);
+        ((ModifiableAmetysObject) link.getParent()).saveChanges();
+    }
+    
+    
+    /**
+     * Checks the existence of an alias with same URL
+     * @param id the alias
+     * @param siteName The site name
+     * @param url The alias URL
+     * @return true if an alias with the same URL exists
+     */
+    private boolean _checkExistence (String id, String siteName, String url)
+    {
+        try
+        {
+            String xpathQuery = AliasHelper.getXPath(siteName, url);
+            
+            AmetysObjectIterable<DefaultAlias> it = _ametysObjectResolver.query(xpathQuery);
+            while (it.hasNext())
+            {
+                DefaultAlias alias = it.next();
+                if (!id.equals(alias.getId()))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+    
+    /**
+     * Checks the existence of an alias
+     * @param siteName The site name
+     * @param url The alias URL
+     * @return true if an alias with the same URL exists
+     */
+    private boolean _checkExistence (String siteName, String url)
+    {
+        try
+        {
+            String xpathQuery = AliasHelper.getXPath(siteName, url);
+            return _ametysObjectResolver.query(xpathQuery).hasNext();
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+    
+    /**
+     * Validates the url 
+     * @param url the url to check
+     * @return true if the url is valid
+     */
+    private boolean isValidUrl (String url)
+    {
+        Matcher matcher = URL_PATTERN.matcher(url);
+        return matcher.matches();
+    }
+    
+    /**
+     * Validates the url 
+     * @param url the url to check
+     * @return true if the url is valid
+     */
+    private boolean isValidTargetUrl (String url)
+    {
+        Matcher matcher = TARGET_URL_PATTERN.matcher(url);
+        return matcher.matches();
+    }
+    
+    /**
+     * Represent an {@link Alias} in JSON
+     * @param alias The alias 
+     * @return the alias in JSON
+     */
+    public Map<String, String> alias2Json (DefaultAlias alias) 
+    {
+        Map<String, String> aliasJson = new HashMap<>();
+        aliasJson.put("id", alias.getId());
+        aliasJson.put("url", alias.getUrl());
+        aliasJson.put("target", alias.getTarget());
+
+        // FIXME
+        
+        //        if (TargetType.PAGE.equals(alias.getType()))
+        //        {
+        //            try
+        //            {
+        //                Page page = _resolver.resolveById(target);
+        //                XMLUtils.createElement(contentHandler, "targetUrl", "/" + page.getSitemapName() + "/" + page.getPathInSitemap() + ".html");
+        //            }
+        //            catch (UnknownAmetysObjectException e)
+        //            {
+        //                getLogger().warn("Alias '" + alias.getUrl() + "' redirect to a deleted page of id '" + target + "'");
+        //                XMLUtils.createElement(contentHandler, "targetUrl", "unknown");
+        //            }
+        //        }
+        //        else
+        //        {
+        //            XMLUtils.createElement(contentHandler, "targetUrl", target);
+        //        }
+        
+        aliasJson.put("targetUrl", alias.getTarget());
+        aliasJson.put("createAt", ParameterHelper.valueToString(alias.getCreationDate()));
+
+        Date expirationDate = alias.getExpirationDate();
+        aliasJson.put("expirationDate", expirationDate != null ? ParameterHelper.valueToString(expirationDate) : "");
+        
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        
+        boolean expired = expirationDate != null && expirationDate.compareTo(cal.getTime()) < 0;
+        aliasJson.put("expired", String.valueOf(expired));
+        aliasJson.put("type", String.valueOf(alias.getType()));
+        
+        return aliasJson;
+    }
+}
Index: main/plugin-web/src/org/ametys/web/alias/AliasGenerator.java
===================================================================
--- main/plugin-web/src/org/ametys/web/alias/AliasGenerator.java	(revision 32361)
+++ main/plugin-web/src/org/ametys/web/alias/AliasGenerator.java	(nonexistent)
@@ -1,117 +0,0 @@
-/*
- *  Copyright 2010 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.web.alias;
-
-import java.io.IOException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-
-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.ServiceableGenerator;
-import org.apache.cocoon.xml.AttributesImpl;
-import org.apache.cocoon.xml.XMLUtils;
-import org.xml.sax.SAXException;
-
-import org.ametys.plugins.repository.AmetysObjectResolver;
-import org.ametys.plugins.repository.UnknownAmetysObjectException;
-import org.ametys.runtime.parameter.ParameterHelper;
-import org.ametys.web.alias.Alias.TargetType;
-import org.ametys.web.repository.page.Page;
-
-/**
- * SAX events for an alias
- */
-public class AliasGenerator extends ServiceableGenerator
-{
-    /** The Ametys object resolver */
-    protected AmetysObjectResolver _resolver;
-    
-    @Override
-    public void service(ServiceManager smanager) throws ServiceException
-    {
-        super.service(manager);
-        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
-    }
-    
-    @Override
-    public void generate() throws IOException, SAXException, ProcessingException
-    {
-        Request request = ObjectModelHelper.getRequest(objectModel);
-        String id = request.getParameter("id");
-        
-        contentHandler.startDocument();
-        
-        DefaultAlias alias = _resolver.resolveById(id);
-        saxAlias(alias);
-        
-        contentHandler.endDocument();
-    }
-    
-    /**
-     * SAX a alias
-     * @param alias The alias to SAX
-     * @throws SAXException If an error occurs while SAXing
-     */
-    protected void saxAlias (DefaultAlias alias) throws SAXException
-    {
-        AttributesImpl attr = new AttributesImpl();
-        attr.addAttribute("", "id", "id", "CDATA", alias.getId());
-        XMLUtils.startElement(contentHandler, "alias", attr);
-        
-        XMLUtils.createElement(contentHandler, "url", alias.getUrl());
-        String target = alias.getTarget();
-        XMLUtils.createElement(contentHandler, "target", target);
-        
-        if (TargetType.PAGE.equals(alias.getType()))
-        {
-            try
-            {
-                Page page = _resolver.resolveById(target);
-                XMLUtils.createElement(contentHandler, "targetUrl", "/" + page.getSitemapName() + "/" + page.getPathInSitemap() + ".html");
-            }
-            catch (UnknownAmetysObjectException e)
-            {
-                getLogger().warn("Alias '" + alias.getUrl() + "' redirect to a deleted page of id '" + target + "'");
-                XMLUtils.createElement(contentHandler, "targetUrl", "unknown");
-            }
-        }
-        else
-        {
-            XMLUtils.createElement(contentHandler, "targetUrl", target);
-        }
-        XMLUtils.createElement(contentHandler, "createAt", ParameterHelper.valueToString(alias.getCreationDate()));
-        Date expirationDate = alias.getExpirationDate();
-        XMLUtils.createElement(contentHandler, "expirationDate", expirationDate != null ? ParameterHelper.valueToString(expirationDate) : "");
-        
-        Calendar cal = new GregorianCalendar();
-        cal.set(Calendar.HOUR_OF_DAY, 0);
-        cal.set(Calendar.MINUTE, 0);
-        cal.set(Calendar.SECOND, 0);
-        cal.set(Calendar.MILLISECOND, 0);
-        
-        boolean expired = expirationDate != null && expirationDate.compareTo(cal.getTime()) < 0;
-        XMLUtils.createElement(contentHandler, "expired", String.valueOf(expired));
-        
-        XMLUtils.createElement(contentHandler, "type", String.valueOf(alias.getType()));
-        XMLUtils.endElement(contentHandler, "alias");
-    }
-
-}
Index: main/plugin-web/src/org/ametys/web/alias/AliasListGenerator.java
===================================================================
--- main/plugin-web/src/org/ametys/web/alias/AliasListGenerator.java	(revision 32361)
+++ main/plugin-web/src/org/ametys/web/alias/AliasListGenerator.java	(nonexistent)
@@ -1,67 +0,0 @@
-/*
- *  Copyright 2010 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.web.alias;
-
-import java.io.IOException;
-
-import org.apache.avalon.framework.service.ServiceException;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.environment.ObjectModelHelper;
-import org.apache.cocoon.environment.Request;
-import org.apache.cocoon.xml.XMLUtils;
-import org.xml.sax.SAXException;
-
-import org.ametys.plugins.repository.AmetysObjectIterable;
-import org.ametys.plugins.repository.TraversableAmetysObject;
-import org.ametys.web.repository.site.SiteManager;
-
-/**
- * SAX events for existing alias
- */
-public class AliasListGenerator extends AliasGenerator
-{
-    /** The site manager */
-    protected SiteManager _siteManager;
-    
-    @Override
-    public void service(ServiceManager smanager) throws ServiceException
-    {
-        super.service(smanager);
-        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
-    }
-    
-    @Override
-    public void generate() throws IOException, SAXException, ProcessingException
-    {
-        Request request = ObjectModelHelper.getRequest(objectModel);
-        String siteName = request.getParameter("siteName");
-        
-        contentHandler.startDocument();
-        
-        XMLUtils.startElement(contentHandler, "Alias");
-        
-        TraversableAmetysObject aliasNode = AliasHelper.getRootNode(_siteManager.getSite(siteName));
-        AmetysObjectIterable<DefaultAlias> it = aliasNode.getChildren();
-        for (DefaultAlias alias : it)
-        {
-            saxAlias(alias);
-        }
-        
-        XMLUtils.endElement(contentHandler, "Alias");
-        contentHandler.endDocument();
-    }
-}
Index: main/plugin-web/src/org/ametys/web/alias/GetAliasListAction.java
===================================================================
--- main/plugin-web/src/org/ametys/web/alias/GetAliasListAction.java	(revision 0)
+++ main/plugin-web/src/org/ametys/web/alias/GetAliasListAction.java	(revision 0)
@@ -0,0 +1,62 @@
+package org.ametys.web.alias;
+
+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.ametys.core.cocoon.JSonReader;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.TraversableAmetysObject;
+import org.ametys.web.repository.site.SiteManager;
+
+/**
+ * Get all the aliases in JSON
+ */
+public class GetAliasListAction extends ServiceableAction
+{
+    /** The site manager */
+    private SiteManager _siteManager;
+    
+    /** The Alias DAO */
+    private AliasDAO _aliasDAO;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
+        _aliasDAO = (AliasDAO) serviceManager.lookup(AliasDAO.ROLE);
+    }
+    
+    @Override
+    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        String siteName = request.getParameter("siteName");
+
+        Map<String, Object> result = new HashMap<> ();
+        List<Map<String, String>> aliasList = new ArrayList<> ();
+        
+        TraversableAmetysObject aliasNode = AliasHelper.getRootNode(_siteManager.getSite(siteName));
+        AmetysObjectIterable<DefaultAlias> it = aliasNode.getChildren();
+        for (DefaultAlias alias : it)
+        {
+            aliasList.add(_aliasDAO.alias2Json(alias));
+        }
+        
+        result.put("alias", aliasList);
+        
+        request.setAttribute(JSonReader.OBJECT_TO_READ, result);
+        return EMPTY_MAP;
+    }
+}
