Index: main/plugin-explorer/i18n/messages_en.xml
===================================================================
--- main/plugin-explorer/i18n/messages_en.xml	(revision 34075)
+++ main/plugin-explorer/i18n/messages_en.xml	(working copy)
@@ -412,6 +412,7 @@
 	
     <message key="PLUGINS_EXPLORER_MOVE_INCOMPATIBLE">Incompatible move</message>
     <message key="PLUGINS_EXPLORER_FILE_HANDLE_UNPASTE">The following(s) file(s) have not been moved : </message>
+    <message key="PLUGINS_EXPLORER_FILE_HANDLE_UNCOPY">The following(s) file(s) have not been copied : </message>
     <message key="PLUGINS_EXPLORER_FOLDER_HANDLE_UNPASTE">The following folder has not been moved : </message>
     <message key="PLUGINS_EXPLORER_CALENDAR_HANDLE_UNPASTE">The following calendar has not been moved : </message>
     <message key="PLUGINS_EXPLORER_CALENDAR_HANDLE_INCOMPATIBLE">.&lt;br/&gt;A calendar can only have calendars as sub-elements and a calendar or a directory as parent.</message>
Index: main/plugin-explorer/i18n/messages_fr.xml
===================================================================
--- main/plugin-explorer/i18n/messages_fr.xml	(revision 34075)
+++ main/plugin-explorer/i18n/messages_fr.xml	(working copy)
@@ -412,6 +412,7 @@
 	
     <message key="PLUGINS_EXPLORER_MOVE_INCOMPATIBLE">Déplacement incompatible</message>
     <message key="PLUGINS_EXPLORER_FILE_HANDLE_UNPASTE">Le ou les fichier(s) suivant(s) n'ont pu être déplacés : </message>
+    <message key="PLUGINS_EXPLORER_FILE_HANDLE_UNCOPY">Le ou les fichier(s) suivant(s) n'ont pu être copiés : </message>
     <message key="PLUGINS_EXPLORER_FOLDER_HANDLE_UNPASTE">Le dossier suivant n'a pu être déplacé : </message>
     <message key="PLUGINS_EXPLORER_CALENDAR_HANDLE_UNPASTE">Le calendrier suivant n'a pu être déplacé : </message>
     <message key="PLUGINS_EXPLORER_CALENDAR_HANDLE_INCOMPATIBLE">.&lt;br/&gt;Un calendrier ne peut avoir comme sous-élément que des calendriers ; et comme parent qu'un calendrier ou un répertoire.</message>
Index: main/plugin-explorer/resources/js/org/ametys/explorer/applications/resources/ResourcesActions.i18n.js
===================================================================
--- main/plugin-explorer/resources/js/org/ametys/explorer/applications/resources/ResourcesActions.i18n.js	(revision 34075)
+++ main/plugin-explorer/resources/js/org/ametys/explorer/applications/resources/ResourcesActions.i18n.js	(working copy)
@@ -1190,7 +1190,64 @@
 	// Call back
 	if (typeof callback == 'function')
 	{
-		callback (target, ids);
+		callback (target, ids, org.ametys.explorer.applications.resources.File._pasteMode);
+	}
+	
+	return ids;
+}
+
+/**
+ * Duplicate files to a target folder
+ * @param {String} target The folder id to duplicate into
+ * @params {String[]} files The files id to move
+ * @param {function} callback The call back function
+ * @return an Array of files moved 
+ */
+org.ametys.explorer.applications.resources.File.duplicate = function(target, files, callback)
+{
+	var params = {};
+	params.id = files;
+	params.collection = target;
+	params.mode = 'add-rename';
+	
+	var serverMessage = new org.ametys.servercomm.ServerMessage('explorer', '/duplicate-object', params, org.ametys.servercomm.ServerComm.PRIORITY_SYNCHRONOUS, null, this, null);
+	var response = org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+	
+	if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_HANDLE_CUT_PASTE_ERROR"/>", response, "org.ametys.explorer.applications.resources.File.duplicate"))
+	{
+		return [];
+	}
+	
+	var msg = response.selectSingleNode("ActionResult/message") != null ? response.selectSingleNode("ActionResult/message")[org.ametys.servercomm.ServerComm.xmlTextContent] : '';
+	if (msg == 'locked')
+	{
+		Ext.Msg.show({
+			title: "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_HANDLE_RENAME"/>",
+			msg: "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_HANDLE_CUT_PASTE_LOCKED"/>",
+			buttons: Ext.Msg.OK,
+			icon: Ext.MessageBox.ERROR
+		});
+		return [];
+	}
+	
+	var unmovedObjects = response.selectSingleNode("ActionResult/unmoved-objects");
+	if (unmovedObjects != null)
+	{
+		Ext.Msg.show({
+			title: "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_HANDLE_RENAME"/>",
+			msg: "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_HANDLE_UNCOPY"/>" + unmovedObjects[org.ametys.servercomm.ServerComm.xmlTextContent] + "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_HANDLE_UNPASTE_2"/>",
+			buttons: Ext.Msg.OK,
+			icon: Ext.MessageBox.WARN
+		});
+	}
+	
+	var movedObjects = response.selectSingleNode("ActionResult/moved-objects") != null ? response.selectSingleNode("ActionResult/moved-objects")[org.ametys.servercomm.ServerComm.xmlTextContent] : '';
+	var ids = movedObjects != '' ? movedObjects.split(',') : [];
+	
+	// Call back
+	if (typeof callback == 'function')
+	{
+		callback (target, ids, org.ametys.explorer.applications.resources.File._pasteMode);
 	}
 	
 	return ids;
@@ -1223,6 +1280,10 @@
 	{
 		org.ametys.explorer.applications.resources.File.move (target, org.ametys.explorer.applications.resources.File._pasteFiles, callback)
 	}
+	else if (org.ametys.explorer.applications.resources.File._pasteMode == 'copy')
+	{
+		org.ametys.explorer.applications.resources.File.duplicate (target, org.ametys.explorer.applications.resources.File._pasteFiles, callback)
+	}
 }
 
 /**
@@ -1234,7 +1295,10 @@
  */
 org.ametys.explorer.applications.resources.File.copy = function(target, files, callback)
 {
-	alert("Not yet implemented ")
+//	alert("Not yet implemented ")
+	org.ametys.explorer.applications.resources.File._pasteFiles = files;
+	org.ametys.explorer.applications.resources.File._pasteMode = 'copy';
+	org.ametys.explorer.applications.resources.File._pasteBase = target;
 }
 
 /*------------------------------------------------------------------------*/
Index: main/plugin-explorer/resources/js/org/ametys/explorer/applications/resources/ResourcesApplication.i18n.js
===================================================================
--- main/plugin-explorer/resources/js/org/ametys/explorer/applications/resources/ResourcesApplication.i18n.js	(revision 34075)
+++ main/plugin-explorer/resources/js/org/ametys/explorer/applications/resources/ResourcesApplication.i18n.js	(working copy)
@@ -393,6 +393,18 @@
         },
 		items: [
 				{
+					 id: this._appPrefix + 'toolbar-btn-file-copy',
+					 text: "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_COPY_LABEL"/>",
+					 icon: getPluginResourcesUrl ('explorer') + '/img/resources/clipboard/copy_16.png',
+					 tooltip : org.ametys.explorer.utils.Tooltip.createTooltipContent ("<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_COPY_LABEL"/>", "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_COPY_DESCRIPTION"/>", getPluginResourcesUrl ('explorer') + '/img/resources/clipboard/copy_32.png'),
+					 application: this,
+					 handler: function () {
+							org.ametys.explorer.applications.resources.toolbar.File.copy (this.application);
+					 },
+					 rightId: 'Plugin_Explorer_File_Add',
+					 disabled: true
+				},
+				{
 					 id: this._appPrefix + 'toolbar-btn-file-cut',
 					 text: "<i18n:text i18n:key="PLUGINS_EXPLORER_FILE_CUT_LABEL"/>",
 					 icon: getPluginResourcesUrl ('explorer') + '/img/resources/clipboard/cut_16.png',
@@ -623,6 +635,9 @@
 	btn = Ext.getCmp(this._appPrefix + 'toolbar-btn-file-cut');
 	btn.setDisabled (files == null || files.length == 0 || notEditable || !this._hasRight (rights, btn.rightId));
 	
+	btn = Ext.getCmp(this._appPrefix + 'toolbar-btn-file-copy');
+	btn.setDisabled (files == null || files.length == 0 || notEditable || !this._hasRight (rights, btn.rightId));
+	
 	btn = Ext.getCmp(this._appPrefix + 'toolbar-btn-file-paste');
 	btn.setDisabled (folder == null || isRightsPanelActive || notEditable || !this._hasRight (rights, btn.rightId));
 	
Index: main/plugin-explorer/sitemap.xmap
===================================================================
--- main/plugin-explorer/sitemap.xmap	(revision 34075)
+++ main/plugin-explorer/sitemap.xmap	(working copy)
@@ -27,6 +27,7 @@
             <map:action name="delete-calendar" src="org.ametys.plugins.explorer.calendar.actions.DeleteCalendarAction" logger="org.ametys.explorer.actions" />
             <map:action name="delete-event" src="org.ametys.plugins.explorer.calendar.actions.DeleteCalendarEventAction" logger="org.ametys.explorer.actions" />
             <map:action name="move-object" src="org.ametys.plugins.explorer.resources.actions.MoveObjectAction" logger="org.ametys.explorer.actions"/>
+            <map:action name="duplicate-object" src="org.ametys.plugins.explorer.resources.actions.CopyResourceAction" logger="org.ametys.explorer.actions"/>
             <map:action name="rename-object" src="org.ametys.plugins.explorer.resources.actions.RenameObjectAction" logger="org.ametys.explorer.actions"/> 
             <map:action name="rename-resource" src="org.ametys.plugins.explorer.resources.actions.RenameResourceAction" logger="org.ametys.explorer.actions"/> 
             <map:action name="restore-resource" src="org.ametys.plugins.explorer.resources.actions.RestoreResourceAction" logger="org.ametys.explorer.actions"/> 
@@ -296,6 +297,17 @@
 				</map:act>
 			</map:match>
 			
+			<map:match pattern="duplicate-object">
+                <map:act type="duplicate-object">
+                    <map:generate type="action-result">
+                        <map:parameter name="message" value="{message}"/>
+                        <map:parameter name="unmoved-objects" value="{unmoved-objects}"/>
+                        <map:parameter name="moved-objects" value="{moved-objects}"/>
+                    </map:generate>
+                    <map:serialize type="xml" />
+                </map:act>
+            </map:match>
+			
 			<map:match pattern="rename-object">
 				<map:act type="rename-object">
 					<map:generate type="action-result">
Index: main/plugin-explorer/src/org/ametys/plugins/explorer/resources/actions/AddOrUpdateResourceAction.java
===================================================================
--- main/plugin-explorer/src/org/ametys/plugins/explorer/resources/actions/AddOrUpdateResourceAction.java	(revision 34075)
+++ main/plugin-explorer/src/org/ametys/plugins/explorer/resources/actions/AddOrUpdateResourceAction.java	(working copy)
@@ -20,11 +20,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import org.apache.avalon.framework.context.ContextException;
 import org.apache.avalon.framework.context.Contextualizable;
@@ -45,6 +48,7 @@
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.NullOutputStream;
 import org.apache.commons.lang.IllegalClassException;
+import org.apache.commons.lang.StringUtils;
 import org.apache.tika.metadata.Metadata;
 
 import org.ametys.plugins.explorer.observation.ExplorerEvent;
@@ -88,6 +92,59 @@
         _context = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
     }
     
+    /**
+     * Get the number of file
+     * @param request
+     * @return the number of file
+     */
+    protected int getFileCount(Request request)
+    {
+        return 1;
+    }
+    
+    /**
+     * Get the file name
+     * @param request
+     * @param i 
+     * @return the name of the file
+     */
+    protected String getFilename(Request request, int i)
+    {
+        return getFile(request, i).getName();
+    }
+
+    /**
+     * Get the file to update
+     * @param request
+     * @param i 
+     * @return the file
+     */
+    protected File getFile(Request request, int i)
+    {
+        Part part = (Part) request.get("filename");
+        if (part instanceof RejectedPart || part == null)
+        {
+            throw new IllegalArgumentException("Part rejected");
+        }
+
+        PartOnDisk uploadedFilePart = (PartOnDisk) part;
+        File uploadedFile = uploadedFilePart.getFile();
+
+        return uploadedFile;
+    }
+
+    /**
+     * Get the input stream of the file
+     * @param request
+     * @param i 
+     * @return the input stream of the file
+     * @throws IOException
+     */
+    protected InputStream getInputStream(Request request, int i) throws IOException
+    {
+        return new FileInputStream(getFile(request, i));
+    } 
+    
     @Override
     public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
     {
@@ -101,120 +158,174 @@
         boolean unzip = Boolean.valueOf(request.getParameter("unzip"));
         
         Map<String, String> result = new HashMap<String, String>();
-        try
+        
+        AmetysObject object = _resolver.resolveById(parentId);
+        if (!(object instanceof ModifiableResourceCollection))
         {
-            boolean created = false;
-            
-            AmetysObject object = _resolver.resolveById(parentId);
-            if (!(object instanceof ModifiableResourceCollection))
-            {
-                throw new IllegalClassException(ModifiableResourceCollection.class, object.getClass());
-            }
-            
-            ModifiableResourceCollection collection = (ModifiableResourceCollection) object;
-            if (!checkLock(collection))
+            throw new IllegalClassException(ModifiableResourceCollection.class, object.getClass());
+        }
+        
+        ModifiableResourceCollection collection = (ModifiableResourceCollection) object;
+        if (!checkLock(collection))
+        {
+            getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to modify collection '" + object.getName() + "' but it is locked by another user");
+            result.put("success", "false");
+            result.put("message", "locked");
+            return result;
+        }
+        
+        // FIXME CMS-2297
+        // checkUserRight(object, "Plugin_Explorer_File_Add");
+        
+        for (int i = 0; i < getFileCount(request); i++)
+        {
+            try
             {
-                getLogger().warn("User '" + _currentUserProvider.getUser() + "' try to modify collection '" + object.getName() + "' but it is locked by another user");
-                result.put("success", "false");
-                result.put("message", "locked");
-                return result;
-            }
+                boolean created = false;
+                boolean success = true;
+                String fileName = "";
+                try
+                {
+                    fileName = getFilename(request, i);
+                }
+                catch (IllegalArgumentException e)
+                {
+                    success = false;
+                    if (i == 0)
+                    {
+                        result.put("message", "rejected");
+                        result.put("success", "false");
+                    }
+                    else
+                    {
+                        result.put("message" + i, "rejected");
+                        result.put("success" + i, "false");
+                    }
+                } 
+                
+                if (success)
+                {
+                    ModifiableResource resource = null;
+                    
+                    if (fileName.toLowerCase().endsWith(".zip") && unzip)
+                    {
+                        return _unzip(request, collection, getFile(request, i));
+                    }
+                    else if (collection.hasChild(fileName))
+                    {
+                        if ("add-rename".equals(mode))
+                        {
+                            // Find a new name
+                            String[] f = fileName.split("\\.");
+                            int index = 1;
+                            while (collection.hasChild(fileName))
+                            {
+                                fileName = f[0] + "-" + (index++) + '.' + f[1];
+                            }
+                            resource = createResource(request, collection, fileName);
+                            created = true;
+                        }
+                        else if ("update".equals(mode))
+                        {
+                            resource = collection.getChild(fileName);
+                        }
+                        else 
+                        {
+                            success = false;
+                            if (i == 0)
+                            {
+                                result.put("message", "already-exist");
+                                result.put("success", "false");
+                            }
+                            else
+                            {
+                                result.put("message" + i, "already-exist");
+                                result.put("success" + i, "false");
+                            }
+                        }
+                    }
+                    else
+                    {
+                        resource = createResource(request, collection, fileName);
+                        created = true;
+                    }
+                    
+                    if (success)
+                    {
+                        String author = _currentUserProvider.getUser();
+                        
+                        InputStream is = null;
+                        try
+                        {
+                            is = getInputStream(request, i);
             
-            // FIXME CMS-2297
-            // checkUserRight(object, "Plugin_Explorer_File_Add");
+                            String mimeType = _context.getMimeType(fileName.toLowerCase());
+                            mimeType = mimeType == null ? "application/unknown" : mimeType;
             
-            Part part = (Part) request.get("filename");
-            if (part instanceof RejectedPart || part == null)
-            {
-                result.put("success", "false");
-                result.put("message", "rejected");
-                return result;
-            }
+                            resource.setData(is, mimeType, new Date(), author);
+                            resource.setLastModified(new Date());
             
-            PartOnDisk uploadedFilePart = (PartOnDisk) part;
-            File uploadedFile = uploadedFilePart.getFile();
+                            _extractResourceMetadata(resource, mimeType);
             
-            String fileName = uploadedFile.getName();
+                            collection.saveChanges();
             
-            ModifiableResource resource = null;
+                            _checkpoint(resource);
+                        }
+                        finally
+                        {
+                            IOUtils.closeQuietly(is);
+                        }            
             
-            if (fileName.toLowerCase().endsWith(".zip") && unzip)
-            {
-                return _unzip(request, collection, uploadedFile);
+                        if (i == 0)
+                        {
+                            result.put("id", resource.getId());
+                            result.put("parentID", collection.getId());
+                            result.put("success", "true");
+                        }
+                        else
+                        {
+                            result.put("id" + i, resource.getId());
+                            result.put("parentID" + i, collection.getId());
+                            result.put("success" + i, "true");
+                        }
+                        
+                        // Notify listeners
+                        Map<String, Object> eventParams = new HashMap<String, Object>();
+                        eventParams.put("parentID", collection.getId());
+                        eventParams.put("name", resource.getName());
+                        eventParams.put("path", resource.getPath());
+                        _obsManager.notifyEvent(new ExplorerEvent (created ? EventType.RESOURCE_CREATE : EventType.RESOURCE_UPDATE, author, resource.getId(), eventParams));
+                    }
+                }
             }
-            else if (collection.hasChild(fileName))
+            catch (UnknownAmetysObjectException e)
             {
-                if ("add-rename".equals(mode))
+                getLogger().error("Unable to add file : the collection of id '" + parentId + "' does not exist anymore", e);
+                if (i == 0)
                 {
-                    // Find a new name
-                    String[] f = fileName.split("\\.");
-                    int index = 1;
-                    while (collection.hasChild(fileName))
-                    {
-                        fileName = f[0] + "-" + (index++) + '.' + f[1];
-                    }
-                    resource = createResource(request, collection, fileName);
-                    created = true;
+                    result.put("message", "unknown-collection");
+                    result.put("success", "false");
                 }
-                else if ("update".equals(mode))
+                else
                 {
-                    resource = collection.getChild(fileName);
+                    result.put("message" + i, "unknown-collection");
+                    result.put("success" + i, "false");
                 }
-                else 
+            }
+            catch (Exception e)
+            {
+                getLogger().error("Unable to add file to the collection of id '" + parentId + "'", e);
+                if (i == 0)
                 {
+                    result.put("message", "error");
                     result.put("success", "false");
-                    result.put("message", "already-exist");
-                    return result;
                 }
-            }
-            else
-            {
-                resource = createResource(request, collection, fileName);
-                created = true;
+                else
+                {
+                    result.put("message" + i, "error");
+                    result.put("success" + i, "false");
+                }
             }
-            
-            
-            String author = _currentUserProvider.getUser();
-            InputStream is = new FileInputStream(uploadedFile);
-            
-            String mimeType = _context.getMimeType(uploadedFile.getName().toLowerCase());
-            mimeType = mimeType == null ? "application/unknown" : mimeType;
-                
-            resource.setData(is, mimeType, new Date(), author);
-            resource.setLastModified(new Date());
-            
-            _extractResourceMetadata(resource, mimeType);
-            
-            collection.saveChanges();
-            
-            _checkpoint(resource);
-            
-            IOUtils.closeQuietly(is);
-            
-            result.put("id", resource.getId());
-            
-            result.put("parentID", collection.getId());
-            result.put("success", "true");
-            
-            // Notify listeners
-            Map<String, Object> eventParams = new HashMap<String, Object>();
-            eventParams.put("parentID", collection.getId());
-            eventParams.put("name", resource.getName());
-            eventParams.put("path", resource.getPath());
-            _obsManager.notifyEvent(new ExplorerEvent (created ? EventType.RESOURCE_CREATE : EventType.RESOURCE_UPDATE, author, resource.getId(), eventParams));
-            
-        }
-        catch (UnknownAmetysObjectException e)
-        {
-            getLogger().error("Unable to add file : the collection of id '" + parentId + "' does not exist anymore", e);
-            result.put("message", "unknown-collection");
-            result.put("success", "false");
-        }
-        catch (Exception e)
-        {
-            getLogger().error("Unable to add file to the collection of id '" + parentId + "'", e);
-            result.put("message", "error");
-            result.put("success", "false");
         }
         
         return result;
Index: main/plugin-explorer/src/org/ametys/plugins/explorer/resources/actions/CopyResourceAction.java
===================================================================
--- main/plugin-explorer/src/org/ametys/plugins/explorer/resources/actions/CopyResourceAction.java	(revision 34075)
+++ main/plugin-explorer/src/org/ametys/plugins/explorer/resources/actions/CopyResourceAction.java	(working copy)
@@ -15,48 +15,103 @@
  */
 package org.ametys.plugins.explorer.resources.actions;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.avalon.framework.parameters.Parameters;
-import org.apache.avalon.framework.service.ServiceException;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
 import org.apache.cocoon.environment.SourceResolver;
+import org.apache.commons.lang.StringUtils;
 
+import org.ametys.plugins.explorer.resources.ModifiableResource;
 import org.ametys.plugins.repository.AmetysObject;
 
 /**
  * Action for copying an {@link AmetysObject}.
  */
-public class CopyResourceAction extends ServiceableAction
+public class CopyResourceAction extends AddOrUpdateResourceAction
 {
-    //private AmetysObjectResolver _resolver;
+    @Override
+    protected int getFileCount(Request request)
+    {
+        String[] ids = request.getParameterValues("id");
+        return ids.length;
+    }
     
     @Override
-    public void service(ServiceManager sManager) throws ServiceException
+    protected String getFilename(Request request, int i)
     {
-        super.service(sManager);
+        String[] ids = request.getParameterValues("id");
         
-        //_resolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE);
+        ModifiableResource resource = _resolver.resolveById(ids[i]);
+        
+        return resource.getName();
+    }
+    
+    @Override
+    protected File getFile(Request request, int i)
+    {
+        throw new UnsupportedOperationException("Get file method doesn't exist for copy");
+    }
+    
+    @Override
+    protected InputStream getInputStream(Request request, int i) throws IOException
+    {
+        String[] ids = request.getParameterValues("id");
+        
+        ModifiableResource resource = _resolver.resolveById(ids[i]);
+        
+        return resource.getInputStream();
     }
     
     @Override
     public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
     {
-        /*Request request = ObjectModelHelper.getRequest(objectModel);
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        String[] ids = request.getParameterValues("id");
+
+        Map<String, String> result = super.act(redirector, resolver, objectModel, source, parameters);
         
-        String targetId = request.getParameter("target");
-        String[] ids = request.getParameterValues("ids");
+        List<String> uncreatedObjects = new ArrayList<String>();
+        List<String> createdObjectIds = new ArrayList<String>();
         
-        ResourcesCollection targetCollection = (ResourcesCollection) _resolver.resolveById(targetId);
+        int i = 0;
+        String key = "success";
+        String message = "message";
+        while (result.containsKey(key))
+        {
+            String value = result.get(key);
+            if (value.equals("true"))
+            {
+                createdObjectIds.add(ids[i]);
+            }
+            else if (result.get(message).equals("already-exist"))
+            {
+                uncreatedObjects.add(getFilename(request, i));
+            }
+            
+            i++;
+            key = "success" + i;
+            message = "message" + i;
+        }
+        
+        if (uncreatedObjects.size() > 0)
+        {
+            result.put("unmoved-objects", uncreatedObjects.toString());
+        }
         
-        for (int i = 0; i < ids.length; i++)
+        if (createdObjectIds.size() > 0)
         {
-            Resource resource = (Resource) _resolver.resolveById(ids[i]);
-            // TODO
-        }   
-        */
-        return EMPTY_MAP;
+            result.put("moved-objects", StringUtils.join(createdObjectIds, ","));
+        }
+        
+        return result;
     }
 }
