Index: main/plugin-web/i18n/messages_en.xml
===================================================================
--- main/plugin-web/i18n/messages_en.xml	(revision 32913)
+++ main/plugin-web/i18n/messages_en.xml	(working copy)
@@ -176,6 +176,9 @@
 	<message key="PLUGINS_WEB_UITOOL_LINKED_PASTE_LABEL">Paste page (contents will be shared)</message>
 	<message key="PLUGINS_WEB_UITOOL_LINKED_PASTE_DESC">This method copies the subtree without duplicating the contents: the contents will be shared.</message>
 	
+	<message key="PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_NOPAGE_DESC">This button is disabled because the clipboard does not contain a page.</message>
+	<message key="PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_PAGE_DESC">Click here in order to paste the following page: </message>
+	
     <!-- Config -->
     <message key="PLUGINS_WEB_CONFIG_PAGE_ELEMENT_CACHE_DISABLED_LABEL">Page elements cache</message>
     <message key="PLUGINS_WEB_CONFIG_PAGE_ELEMENT_CACHE_DISABLED_DESCRIPTION">Check this option to disable the cache of the rendering of some page elements (contents, services, sitemaps, ...).  It is necessary when developing to display XSL updates concerning the content or services rendering, for example.</message>

Index: main/plugin-web/i18n/messages_fr.xml
===================================================================
--- main/plugin-web/i18n/messages_fr.xml	(revision 32913)
+++ main/plugin-web/i18n/messages_fr.xml	(working copy)
@@ -176,6 +176,11 @@
 	<message key="PLUGINS_WEB_UITOOL_LINKED_PASTE_LABEL">Coller la page (partage des contenus)</message>
 	<message key="PLUGINS_WEB_UITOOL_LINKED_PASTE_DESC">Ce mode copie la sous-arborescence sans dupliquer les contenus : les contenus sont partagés.</message>
 	
+	<message key="PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_NOPAGE_DESC">Ce bouton est désactivé car le presse papiers ne contient pas de page.</message>
+	<message key="PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_PAGE_DESC">Cliquez ici pour copier la page </message>
+	
+	
+	
     <!-- Config -->
     <message key="PLUGINS_WEB_CONFIG_PAGE_ELEMENT_CACHE_DISABLED_LABEL">Eléments des pages</message>
     <message key="PLUGINS_WEB_CONFIG_PAGE_ELEMENT_CACHE_DISABLED_DESCRIPTION">Cochez cette option pour désactiver la mise en cache du rendu de certains éléments de pages (service, contenus, plan du site, ...). Ceci est nécessaire en phase de développement pour visualiser les modifications des XSL de rendu de contenus ou de services par exemple.</message>
Index: main/plugin-web/plugin.xml
===================================================================
--- main/plugin-web/plugin.xml	(revision 32913)
+++ main/plugin-web/plugin.xml	(working copy)
@@ -3209,68 +3252,125 @@
 			</extension> -->
 			
 			<!-- Copier une page -->
-    		<!-- <extension id="org.ametys.web.userinterface.CopyPage"
-					   point="org.ametys.core.ui.RibbonControlsManager" 
-					   class="org.ametys.core.ui.StaticClientSideElement">
-				<action class="org.ametys.ribbon.control.button.ModifiablePageActionButton">
-					<param name="action">org.ametys.web.tool.page.Copy.act</param>
-
-					<param name="label" i18n="true">PLUGINS_WEB_UITOOL_COPY_LABEL</param>
-					<param name="default-description" i18n="true">PLUGINS_WEB_UITOOL_COPY_DESC</param>
-					<param name="footer" i18n="true">PLUGINS_WEB_UITOOL_COPY_FOOTER</param>
-					
-					<param name="no-right-description" i18n="true">PLUGINS_WEB_UITOOL_PAGE_NORIGHT_DESC</param>
-					<param name="no-modifiable-description" i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOMODIFIABLE_DESC</param>
-					<param name="no-selection-description" i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOSELECTION_DESC</param>
-					
-					<param name="icon-small" file="true">img/actions/page_copy_16.png</param>
-					<param name="icon-medium" file="true">img/actions/page_copy_32.png</param>
-					<param name="icon-large" file="true">img/actions/page_copy_48.png</param>
-				</action>
-				<scripts>
-					<file>js/org/ametys/web/tool/page/Actions.js</file>
-					<file>js/org/ametys/ribbon/control/button/ModifiablePageActionButton.js</file>
-				</scripts>
-			</extension> -->
+			 <extension id="org.ametys.web.userinterface.CopyPage"
+                       point="org.ametys.core.ui.RibbonControlsManager"
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.plugins.web.page.controller.ModifiablePageController">
+                    <action>Ametys.plugins.web.page.PageActions.copyPage</action>
+                    
+                    <selection-target-type>^(page)$</selection-target-type>
+                
+                    <label i18n="true">PLUGINS_WEB_UITOOL_COPY_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_UITOOL_COPY_DESC</description>
+                    <footer i18n="true">PLUGINS_WEB_UITOOL_COPY_FOOTER</footer>
+                    
+                    <enabled-on-modifiable-only>true</enabled-on-modifiable-only>
+                   
+                    <modifiable-description-no i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOMODIFIABLE_DESC</modifiable-description-no>
+                    <rights-description-no i18n="true">PLUGINS_WEB_UITOOL_PAGE_NORIGHT_DESC</rights-description-no>
+                    <selection-description-empty i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOSELECTION_DESC</selection-description-empty>
+                    
+                    <icon-small file="true">img/actions/page_copy_16.png</icon-small>
+                    <icon-medium file="true">img/actions/page_copy_32.png</icon-medium>
+                    <icon-large file="true">img/actions/page_copy_48.png</icon-large>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/page/PageActions.js</file>
+                    <file>js/Ametys/plugins/web/page/controller/ModifiablePageController.js</file>
+                </scripts>
+            </extension>
 			
-			<!-- Coller une page -->
-			<!-- <extension id="org.ametys.web.userinterface.PastePage"
-    				   point="org.ametys.core.ui.RibbonControlsManager"
-    				   class="org.ametys.cms.clientsideelement.StaticMenu">
-				<action class="org.ametys.web.tool.page.PasteMenu">
-					<param name="action">org.ametys.web.tool.page.Paste.act</param>
-					
-					<param name="label" i18n="true">PLUGINS_WEB_UITOOL_PASTE_SHORTLABEL</param>
-					<param name="default-description" i18n="true">PLUGINS_WEB_UITOOL_PASTE_DESC</param>
-					<param name="no-right-description" i18n="true">PLUGINS_WEB_UITOOL_PASTE_NO_RIGHT_DESC</param>
-					<param name="no-selection-description" i18n="true">PLUGINS_WEB_UITOOL_PASTE_NO_SELECTION_DESC</param>
-					<param name="icon-small" file="true">img/actions/page_paste_16.png</param>
-					<param name="icon-medium" file="true">img/actions/page_paste_32.png</param>
-					<param name="icon-large" file="true">img/actions/page_paste_48.png</param>
-				</action> 
-				<menu-item id="org.ametys.web.page.Paste">
-					<param name="action">org.ametys.web.tool.page.Paste.act</param>
+			<!-- Menu Coller -->
+			<extension id="org.ametys.web.userinterface.PastePage"
+                       point="org.ametys.core.ui.RibbonControlsManager"
+                       class="org.ametys.core.ui.SimpleMenu">
+                <class name="Ametys.plugins.web.page.controller.PasteModifiablePageController">
+                    <label i18n="true">PLUGINS_WEB_UITOOL_PASTE_SHORTLABEL</label>
+                    <description i18n="true">PLUGINS_WEB_UITOOL_PASTE_DESC</description>
+                    
+                    <selection-description-empty>PLUGINS_WEB_PAGE_LINKPAGE_NOSELECTION_DESCRIPTION</selection-description-empty>
+                    <selection-description-nomatch>PLUGINS_WEB_PAGE_LINKPAGE_NOSELECTION_DESCRIPTION</selection-description-nomatch>
+                    
+                    <icon-small file="true">img/actions/page_paste_16.png</icon-small>
+                    <icon-medium file="true">img/actions/page_paste_32.png</icon-medium>
+                    <icon-large file="true">img/actions/page_paste_48.png</icon-large>
+                </class>
+                <menu-items>
+                    <item ref="org.ametys.web.userinterface.Paste" primaryItem="true"/>
+                    <item ref="org.ametys.web.userinterface.LinkedPaste"/>
+                </menu-items>
+                <scripts>
+                    <file>js/Ametys/plugins/web/page/controller/PasteModifiablePageController.js</file>
+                </scripts>
+            </extension>
+			
+			<!-- Coller une page par duplication -->
+            <extension id="org.ametys.web.userinterface.Paste"
+                       point="org.ametys.core.ui.RibbonControlsManager"
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.plugins.web.page.controller.PasteModifiablePageController">
+                    <action>Ametys.plugins.web.page.PageActions.pastePage</action>
 
-					<param name="label" i18n="true">PLUGINS_WEB_UITOOL_PASTE_LABEL</param>
-					<param name="description" i18n="true">PLUGINS_WEB_UITOOL_PASTE_DESC</param>
-					<param name="iconSmall" file="true">img/actions/page_paste_16.png</param>
-				</menu-item>
-				<menu-item id="org.ametys.web.page.LinkedPaste">
-					<param name="action">org.ametys.web.tool.page.Paste.link</param>
-					<param name="right-id">Web_Rights_Page_AddSharedContent</param>
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
+                    
+                    <label i18n="true">PLUGINS_WEB_UITOOL_PASTE_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_UITOOL_PASTE_DESC</description>
+                    
+                    <enabled-on-modifiable-only>true</enabled-on-modifiable-only>
+                    <keep-references>no</keep-references>
+                   
+                    <modifiable-description-no i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOMODIFIABLE_DESC</modifiable-description-no>
+                    <rights-description-no i18n="true">PLUGINS_WEB_UITOOL_PAGE_NORIGHT_DESC</rights-description-no>
+                    <selection-description-empty i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOSELECTION_DESC</selection-description-empty>
+                    
+                    <copy-page-description i18n="true">PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_PAGE_DESC</copy-page-description>
+                    <no-copied-page-description i18n="true">PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_NOPAGE_DESC</no-copied-page-description>
+                    
+                    <icon-small file="true">img/actions/page_paste_16.png</icon-small>
+                    <icon-medium file="true">img/actions/page_paste_32.png</icon-medium>
+                    <icon-large file="true">img/actions/page_paste_48.png</icon-large>
+                    
+                    <rights>Web_Rights_Page_Create</rights>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/page/PageActions.js</file>
+                    <file>js/Ametys/plugins/web/page/controller/PasteModifiablePageController.js</file>
+                </scripts>
+            </extension>
+            
+            <!-- Copier une page par référence (sans duplication) -->
+            <extension id="org.ametys.web.userinterface.LinkedPaste"
+                       point="org.ametys.core.ui.RibbonControlsManager"
+                       class="org.ametys.core.ui.StaticClientSideElement">
+                <class name="Ametys.plugins.web.page.controller.PasteModifiablePageController">
+                    <action>Ametys.plugins.web.page.PageActions.pastePage</action>
+                    
+                    <selection-target-type>^(page|sitemap)$</selection-target-type>
+                    
+                    <label i18n="true">PLUGINS_WEB_UITOOL_LINKED_PASTE_LABEL</label>
+                    <description i18n="true">PLUGINS_WEB_UITOOL_LINKED_PASTE_DESC</description>
+                    
+                    <enabled-on-modifiable-only>true</enabled-on-modifiable-only>
+                    <keep-references>yes</keep-references>
+                   
+                    <modifiable-description-no i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOMODIFIABLE_DESC</modifiable-description-no>
+                    <rights-description-no i18n="true">PLUGINS_WEB_UITOOL_PAGE_NORIGHT_DESC</rights-description-no>
+                    <selection-description-empty i18n="true">PLUGINS_WEB_UITOOL_PAGE_NOSELECTION_DESC</selection-description-empty>
+                    
+                    <copy-page-description i18n="true">PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_PAGE_DESC</copy-page-description>
+                    <no-copied-page-description i18n="true">PLUGINS_WEB_UITOOL_PASTE_CLIPBOARD_NOPAGE_DESC</no-copied-page-description>
+                    
+                    <icon-small file="true">img/actions/page_linked_paste_16.png</icon-small>
+                    <icon-medium file="true">img/actions/page_linked_paste_32.png</icon-medium>
+                    <icon-large file="true">img/actions/page_linked_paste_48.png</icon-large>
 
-					<param name="label" i18n="true">PLUGINS_WEB_UITOOL_LINKED_PASTE_LABEL</param>
-					<param name="description" i18n="true">PLUGINS_WEB_UITOOL_LINKED_PASTE_DESC</param>
-					<param name="iconSmall" file="true">img/actions/page_linked_paste_16.png</param>
-				</menu-item>
-				<scripts>
-					<file plugin="cms">js/org/ametys/cms/ribbon/button/Menu.js</file>
-					<file>js/org/ametys/web/tool/page/PasteMenu.js</file>
-					<file>js/org/ametys/web/tool/page/Actions.js</file>
-					<file>js/org/ametys/ribbon/control/button/ModifiablePageActionButton.js</file>
-				</scripts>
-				<right>Web_Rights_Page_Create</right>
-    		</extension> -->
+                    <rights>Web_Rights_Page_Create|Web_Rights_Page_AddSharedContent</rights>
+                </class>
+                <scripts>
+                    <file>js/Ametys/plugins/web/page/PageActions.js</file>
+                    <file>js/Ametys/plugins/web/page/controller/PasteModifiablePageController.js</file>
+                </scripts>
+            </extension>
 			
 			<!-- Rafraichir la page -->
 			<!-- <extension id="org.ametys.web.userinterface.ReloadPage"
Index: main/plugin-web/resources/js/Ametys/plugins/web/page/PageActions.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/page/PageActions.js	(revision 32913)
+++ main/plugin-web/resources/js/Ametys/plugins/web/page/PageActions.js	(working copy)
@@ -393,7 +393,71 @@
 			}]
 		});
 		
-	}	
+	},
 	
+	/**
+	 * Copy the selected page and its hierarchy
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller the controller calling this function
+	 */
+	copyPage: function(controller)
+	{
+		var pageTargets = controller.getMatchingTargets();
+		if (pageTargets.length > 0)
+		{
+			// keep title too for the button's tooltip
+			Ametys.clipboard.Clipboard.setData (Ametys.message.MessageTarget.PAGE, pageTargets[0].getParameters().page);
+		}
+	},
 	
+	/**
+	 * Paste the clipboard's page and its hierarchy under the selected page
+	 * @param {Ametys.ribbon.element.ui.ButtonController} controller the controller calling this function
+	 */
+	pastePage: function(controller)
+	{
+		var pageTargets = controller.getMatchingTargets();
+		if (pageTargets.length > 0)
+		{
+			var clipboardData = Ametys.clipboard.Clipboard.getData();
+			if (clipboardData.length > 0 && Ametys.clipboard.Clipboard.getType() == Ametys.message.MessageTarget.PAGE)
+			{
+				var params = [pageTargets[0].getParameters().id, clipboardData[0].getId(), controller.getInitialConfig('keep-references')];
+				Ametys.data.ServerComm.callMethod({
+					role: "org.ametys.web.repository.page.PageDAO",
+					methodName: "pastePage",
+					parameters: params,
+					callback: {
+						scope: this,
+						handler: this._pastePageCb,
+					},
+					errorMessage: {
+						category: this.self.getName(),
+						msg: "<i18n:text i18n:key='PLUGINS_WEB_COPYPAGE_ERROR'/>"
+					},
+					waitMessage: true
+				});
+			}
+		}
+	},	
+
+	/**
+	 * Callback for the page pasting process
+	 * @param {Object} response the server's response
+	 * @param {String} response.id the id of the created page
+	 * @param {Object} args the callback arguments
+	 */
+	_pastePageCb: function(response, args)
+	{
+		Ametys.clipboard.Clipboard.setData (Ametys.message.MessageTarget.PAGE, null);
+		
+		var pageId = response.id;
+		
+		Ext.create('Ametys.message.Message', {
+			type: Ametys.message.Message.CREATED,
+			targets: {
+				type: Ametys.message.MessageTarget.PAGE,
+				parameters: { ids: [pageId] }
+			}
+		});
+	}
 });
\ No newline at end of file
Index: main/plugin-web/resources/js/Ametys/plugins/web/page/controller/ModifiablePageController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/page/controller/ModifiablePageController.js	(revision 32913)
+++ main/plugin-web/resources/js/Ametys/plugins/web/page/controller/ModifiablePageController.js	(working copy)
@@ -40,5 +40,15 @@
 		}
 		
 		return null;
+	},
+	
+	/**
+	 * @protected
+	 * Is the target valid ?
+	 * @return true if the target is valid, false otherwise
+	 */
+	_isValidTarget: function()
+	{
+		return enabledOnModifiableOnly && !target.getParameters().isModifiable;
 	}
 });
Index: main/plugin-web/resources/js/Ametys/plugins/web/page/controller/PasteModifiablePageController.js
===================================================================
--- main/plugin-web/resources/js/Ametys/plugins/web/page/controller/PasteModifiablePageController.js	(revision 0)
+++ main/plugin-web/resources/js/Ametys/plugins/web/page/controller/PasteModifiablePageController.js	(revision 0)
@@ -0,0 +1,73 @@
+/*
+ *  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 controller that is able to test if test target
+ * (which should be a target) is modifiable.
+ * @private
+ */
+Ext.define('Ametys.plugins.web.page.controller.PasteModifiablePageController', {
+	extend: 'Ametys.plugins.web.page.controller.ModifiablePageController',
+
+	/**
+	 * @cfg {Boolean} enabled-on-modifiable-only=true If true the controller will be disabled the selection is not modifiable
+	 */
+	/**
+	 * @cfg {String} no-modifiable-description The description text used for tooltip when the selection is not modifiable
+	 */
+	constructor: function()
+	{
+		this.callParent(arguments);
+		
+		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
+	},
+	
+	/**
+	 * @private
+	 * Listener for modified message
+	 * Will update state of the button if the target if the clipboard and it has a page to copy in its data
+	 * @param {Ametys.message.Message} message 
+	 */
+	_onModified: function (message)
+	{
+		var clipboardTarget = message.getTarget(Ametys.message.MessageTarget.CLIPBOARD);
+		if (clipboardTarget != null)
+		{
+			this.refresh(true);
+		}
+	},
+	
+	updateState: function()
+	{
+		var clipboardData = Ametys.clipboard.Clipboard.getData();
+		if (clipboardData.length > 0 && Ametys.clipboard.Clipboard.getType() == Ametys.message.MessageTarget.PAGE) 
+		{
+			var pageTitle = clipboardData[0].getTitle();
+			this.setAdditionalDescription(this.getInitialConfig('copy-page-description') + "'" + pageTitle + "'");
+			this.enable();
+		}
+		else
+		{
+			this.setAdditionalDescription(this.getInitialConfig('no-copied-page-description'));
+			this.disable();
+		}
+	},
+	
+	_isValidTarget: function()
+	{
+		return this.callParent(arguments) && target.getType() != Ametys.message.MessageTarget.SITEMAP;
+	}
+});
Index: main/plugin-web/src/org/ametys/web/repository/page/PageDAO.java
===================================================================
--- main/plugin-web/src/org/ametys/web/repository/page/PageDAO.java	(revision 32913)
+++ main/plugin-web/src/org/ametys/web/repository/page/PageDAO.java	(working copy)
@@ -26,6 +26,9 @@
 import java.util.Map;
 import java.util.Set;
 
+import javax.jcr.Node;
+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;
@@ -44,14 +47,22 @@
 import org.ametys.cms.tag.TagProviderExtensionPoint;
 import org.ametys.core.observation.Event;
 import org.ametys.core.observation.ObservationManager;
+import org.ametys.core.right.RightsManager;
+import org.ametys.core.right.RightsManager.RightResult;
 import org.ametys.core.ui.Callable;
 import org.ametys.core.user.CurrentUserProvider;
 import org.ametys.core.util.I18nizableText;
 import org.ametys.plugins.repository.AmetysObjectIterable;
 import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.plugins.repository.CopiableAmetysObject;
+import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
 import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.plugins.repository.jcr.JCRAmetysObject;
+import org.ametys.plugins.repository.jcr.SimpleAmetysObject;
 import org.ametys.plugins.repository.metadata.CompositeMetadata;
 import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
+import org.ametys.plugins.repository.version.VersionableAmetysObject;
 import org.ametys.plugins.workflow.Workflow;
 import org.ametys.runtime.parameter.Errors;
 import org.ametys.runtime.parameter.ParameterHelper;
@@ -102,6 +113,8 @@
     private TagProviderExtensionPoint _tagProvider;
     private WebLanguagesManager _webLanguagesManager;
     private ContentDAO _contentDAO;
+    private CopySiteComponent _copySiteComponent;
+    private RightsManager _rightsManager;
     
     @Override
     public void service(ServiceManager smanager) throws ServiceException
@@ -121,6 +134,8 @@
         _tagProvider = (TagProviderExtensionPoint) smanager.lookup(TagProviderExtensionPoint.ROLE);
         _webLanguagesManager = (WebLanguagesManager) smanager.lookup(WebLanguagesManager.ROLE);
         _contentDAO = (ContentDAO) smanager.lookup(ContentDAO.ROLE);
+        _copySiteComponent = (CopySiteComponent) smanager.lookup(CopySiteComponent.ROLE);
+        _rightsManager = (RightsManager) smanager.lookup(RightsManager.ROLE);
     }
     
     /**
@@ -316,6 +331,7 @@
     /**
      * Get available content types for specified page
      * @param pageId The page's id
+     * @param zoneName the name of the zone
      * @return the list of available content types
      */
     @Callable
@@ -348,6 +364,7 @@
     /**
      * Get available services for specified page
      * @param pageId The page's id
+     * @param zoneName the name of the zone
      * @return the list of available services
      */
     @Callable
@@ -378,7 +395,7 @@
     
     /**
      * Get available template for specified pages
-     * @param pageId The id of pages
+     * @param pageIds The id of pages
      * @return the list of available template
      */
     @Callable
@@ -411,8 +428,6 @@
     /**
      * Set pages as blank page
      * @param pageIds the id of pages to modify
-     * @param url the url of redirection
-     * @param urlType the type of redirection
      * @return the id of pages which succeeded or failed. 
      */
     @Callable
@@ -1167,4 +1182,122 @@
         String xpathQuery = "//element(*, ametys:page)[@ametys-internal:type = 'LINK' and @ametys-internal:url= '" + pageId + "']";
         return _resolver.query(xpathQuery);
     }
+    
+    
+    /**
+     * Copy a page under another page
+     * @param targetId the page to copy in
+     * @param sourceId the page to copy
+     * @param keepRefs "yes" to keep references, "no" to duplicate the page
+     * @return a map
+     * @throws RepositoryException if an error occurs
+     */
+    @Callable
+    public Map<String, String> pastePage(String targetId, String sourceId, String keepRefs) throws RepositoryException
+    {
+        Map<String, String> result = new HashMap<String, String> ();
+        Page page = _resolver.resolveById(sourceId);
+        if (!(page instanceof CopiableAmetysObject))
+        {
+            throw new IllegalArgumentException("The page '" + page.getId() + "' is not a copiable ametys object, it can not be copied"); 
+        }
+        
+        if (!(page instanceof JCRAmetysObject))
+        {
+            throw new IllegalArgumentException("The page '" + page.getId() + "' is not a JCR ametys object, it can not be copied"); 
+        }
+        
+        PagesContainer parent = _resolver.resolveById(targetId);
+        
+        // Check rights
+        if (!_currentUserProvider.isSuperUser())
+        {
+            if (_rightsManager.hasRight(_currentUserProvider.getUser(), "Web_Rights_Page_Create", "/pages/" + parent.getSitemapName() + "/" + parent.getPathInSitemap()) != RightResult.RIGHT_OK)
+            {
+                throw new IllegalStateException("You do not have the rights to create a page under '/" + parent.getSitemapName() + "/" + parent.getPathInSitemap() + "'");
+            }
+        }
+        
+        if (parent instanceof ModifiableTraversableAmetysObject)
+        {
+            boolean keepReferences = keepRefs.equals("yes");
+            Page cPage = null;
+            if (!keepReferences)
+            {
+                // Restrict the copy to the page and its current children to avoid infinitive loop
+                List<String> pagesToCopy = new ArrayList<String>();
+                pagesToCopy.add(page.getId());
+                pagesToCopy.addAll(_getChildrenPageIds(page));
+                
+                // Copy and duplicate contents
+                cPage = (Page) ((CopiableAmetysObject) page).copyTo((ModifiableTraversableAmetysObject) parent, null, pagesToCopy);
+                _copySiteComponent.updateReferencesAfterCopy(page, cPage);
+                
+                // Creates the first version on all copied contents
+                _createContentVersion (cPage);
+            }
+            else
+            {
+                
+                // Copy without duplicating contents (keep references)
+                String pageName = page.getName();
+                int index = 2;
+                while (parent.hasChild(pageName))
+                {
+                    pageName = page.getName() + "-" + (index++);
+                }
+                
+                String pagePath = ((SimpleAmetysObject) parent).getNode().getPath() + "/" + pageName;
+                Node node = ((JCRAmetysObject) page).getNode();
+                node.getSession().getWorkspace().copy(node.getPath(), pagePath);
+                
+                cPage = parent.getChild(pageName);
+            }
+            
+            ((ModifiableTraversableAmetysObject) parent).saveChanges();
+            result.put("id", cPage.getId());
+            
+            Map<String, Object> eventParams = new HashMap<>();
+            eventParams.put(ObservationConstants.ARGS_PAGE, cPage);
+            _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_ADDED, _currentUserProvider.getUser(), eventParams));
+        }
+        return result;
+    }
+    
+    private void _createContentVersion (Page createdPage) throws AmetysRepositoryException
+    {
+        for (Zone zone : createdPage.getZones())
+        {
+            for (ZoneItem zoneItem : zone.getZoneItems())
+            {
+                if (zoneItem.getType().equals(ZoneType.CONTENT))
+                {
+                    Content content = zoneItem.getContent();
+                    if (content instanceof VersionableAmetysObject)
+                    {
+                        ((VersionableAmetysObject) content).checkpoint();
+                    }
+                }
+            }
+        }
+        
+        // Browse child pages
+        for (Page childPage : createdPage.getChildrenPages())
+        {
+            _createContentVersion (childPage);
+        }
+    }
+    
+    private List<String> _getChildrenPageIds (Page page)
+    {
+        List<String> childIds = new ArrayList<String>();
+        
+        for (Page childPage : page.getChildrenPages())
+        {
+            childIds.add(childPage.getId());
+            childIds.addAll(_getChildrenPageIds(childPage));
+        }
+        
+        return childIds;
+    }
 }
