From 610538261e2094da2e378331b64879e915749a55 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 15:26:03 +0100 Subject: [PATCH 1/3] U4-7641 - enable content type copy --- .../Repositories/ContentTypeBaseRepository.cs | 14 +++++ .../Interfaces/IContentTypeRepository.cs | 7 +++ .../Services/ContentTypeService.cs | 44 ++++++++++++- .../Services/IContentTypeService.cs | 1 + .../common/resources/contenttype.resource.js | 28 +++++++-- .../views/documenttypes/copy.controller.js | 63 +++++++++++++++++++ .../src/views/documenttypes/copy.html | 51 +++++++++++++++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 + .../umbraco/config/lang/en_us.xml | 2 + .../Editors/ContentTypeController.cs | 36 +++++++---- .../Editors/ContentTypeControllerBase.cs | 52 +++++++-------- .../Editors/MediaTypeController.cs | 21 +++---- .../Trees/ContentTypeTreeController.cs | 1 + 13 files changed, 268 insertions(+), 54 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 907f66435e..d3e7182783 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1212,5 +1212,19 @@ AND umbracoNode.id <> @id", { return PerformExists(id); } + + public string GetUniqueAlias(string alias) + { + var aliasColumn = SqlSyntax.GetQuotedColumnName("alias"); + var aliases = Database.Fetch(@"SELECT cmsContentType." + aliasColumn + @" FROM cmsContentType +INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id +WHERE cmsContentType." + aliasColumn + @" LIKE @pattern +AND umbracoNode.nodeObjectType = @objectType", + new { pattern = alias + "%", objectType = NodeObjectTypeId }); + var i = 1; + string test; + while (aliases.Contains(test = alias + i)) i++; + return test; + } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index 49520387cd..acb1d50a61 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -32,5 +32,12 @@ namespace Umbraco.Core.Persistence.Repositories /// /// IEnumerable GetAllContentTypeAliases(params Guid[] objectTypes); + + /// + /// Derives a unique alias from an existing alias. + /// + /// The original alias. + /// The original alias with a number appended to it, so that it is unique. + string GetUniqueAlias(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 496e855b60..141f4a5a97 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -794,7 +794,7 @@ namespace Umbraco.Core.Services //TODO: This needs to change, if we are deleting a content type, we should just delete the data, // this method will recursively go lookup every content item, check if any of it's descendants are - // of a different type, move them to the recycle bin, then permanently delete the content items. + // of a different type, move them to the recycle bin, then permanently delete the content items. // The main problem with this is that for every content item being deleted, events are raised... // which we need for many things like keeping caches in sync, but we can surely do this MUCH better. @@ -1067,6 +1067,48 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } + public Attempt> CopyContentType(IContentType toCopy, int containerId) + { + var evtMsgs = EventMessagesFactory.Get(); + + var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid)) + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) + { + try + { + if (containerId > 0) + { + var container = containerRepository.Get(containerId); + if (container == null) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + var alias = repository.GetUniqueAlias(toCopy.Alias); + var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy.Name = copy.Name + " (copy)"; // might not be unique + + // if it has a parent, and the parent is a content type, unplug composition + // all other compositions remain in place in the copied content type + if (copy.ParentId > 0) + { + var parent = repository.Get(copy.ParentId); + if (parent != null) + copy.RemoveContentType(parent.Alias); + } + + copy.ParentId = containerId; + repository.AddOrUpdate(copy); + } + catch (DataOperationException ex) + { + return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + } + uow.Commit(); + } + + return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + } + /// /// Saves a single object /// diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index cd905a5ccc..e55430330c 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -291,5 +291,6 @@ namespace Umbraco.Core.Services Attempt> MoveMediaType(IMediaType toMove, int containerId); Attempt> MoveContentType(IContentType toMove, int containerId); + Attempt> CopyContentType(IContentType toCopy, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index 55cc2974b7..4c7b6f916e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -29,7 +29,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { filterContentTypes: filterContentTypes, filterPropertyTypes: filterPropertyTypes }; - + return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( @@ -201,9 +201,9 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function() { * alert("node was moved"); * }, function(err){ - * alert("node didnt move:" + err.data.Message); + * alert("node didnt move:" + err.data.Message); * }); - * + * * @param {Object} args arguments object * @param {Int} args.idd the ID of the node to move * @param {Int} args.parentId the ID of the parent node to move to @@ -230,6 +230,26 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to move content'); }, + copy: function(args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.parentId) { + throw "args.parentId cannot be null"; + } + if (!args.id) { + throw "args.id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCopy"), + { + parentId: args.parentId, + id: args.id + }), + 'Failed to copy content'); + }, + createContainer: function(parentId, name) { return umbRequestHelper.resourcePromise( @@ -237,7 +257,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to create a folder under parent id ' + parentId); } - + }; } angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js new file mode 100644 index 0000000000..c666a2159c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js @@ -0,0 +1,63 @@ +angular.module("umbraco") +.controller("Umbraco.Editors.DocumentTypes.CopyController", + function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + contentTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html new file mode 100644 index 0000000000..db1a0db640 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html @@ -0,0 +1,51 @@ +
+ +
+
+ +

+ Select the folder to copy {{currentNode.name}} to in the tree structure below +

+ +
+
+
+ +
+
{{error.errorMsg}}
+

{{error.data.message}}

+
+ +
+
+ {{currentNode.name}} was copied underneath {{target.name}}
+ +
+ +
+ +
+ + +
+ +
+
+
+ + +
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 4f6b65dc64..0842755da3 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1055,7 +1055,9 @@ To manage your website, simply open the Umbraco back office and start adding con Yes, delete was moved underneath + was copied underneath Select the folder to move + Select the folder to copy to in the tree structure below All Document types diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 1cc40abd1d..ce25e990c8 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1051,7 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con Yes, delete was moved underneath + was copied underneath Select the folder to move + Select the folder to copy to in the tree structure below All Document types diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index eaa587fc6c..5099db8cac 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net; @@ -22,9 +23,9 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Editors { - //TODO: We'll need to be careful about the security on this controller, when we start implementing + //TODO: We'll need to be careful about the security on this controller, when we start implementing // methods to modify content types we'll need to enforce security on the individual methods, we - // cannot put security on the whole controller because things like + // cannot put security on the whole controller because things like // GetAllowedChildren, GetPropertyTypeScaffold, GetAllPropertyTypeAliases are required for content editing. /// @@ -129,7 +130,7 @@ namespace Umbraco.Web.Editors } [UmbracoTreeAuthorize( - Constants.Trees.DocumentTypes, Constants.Trees.Content, + Constants.Trees.DocumentTypes, Constants.Trees.Content, Constants.Trees.MediaTypes, Constants.Trees.Media, Constants.Trees.MemberTypes, Constants.Trees.Members)] public ContentPropertyDisplay GetPropertyTypeScaffold(int id) @@ -166,13 +167,13 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK); } - + public HttpResponseMessage PostCreateContainer(int parentId, string name) { var result = Services.ContentTypeService.CreateContentTypeContainer(parentId, name, Security.CurrentUser.Id); return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id + ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); } @@ -203,7 +204,7 @@ namespace Umbraco.Web.Editors //make sure the template alias is set on the default and allowed template so we can map it back ctSave.DefaultTemplate = template.Alias; - + } }); @@ -231,7 +232,7 @@ namespace Umbraco.Web.Editors } else ct = new ContentType(parentId); - + ct.Icon = "icon-document"; var dto = Mapper.Map(ct); @@ -302,18 +303,29 @@ namespace Umbraco.Web.Editors } /// - /// Move the media type + /// Move the content type /// /// /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMove( + return PerformMoveOrCopy( move, getContentType: i => Services.ContentTypeService.GetContentType(i), - doMove: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); + doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); } - + /// + /// Copy the content type + /// + /// + /// + public HttpResponseMessage PostCopy(MoveOrCopy copy) + { + return PerformMoveOrCopy( + copy, + getContentType: i => Services.ContentTypeService.GetContentType(i), + doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i)); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index bc49e10f02..361c1133fd 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.Editors /// /// Am abstract API controller providing functionality used for dealing with content and media types /// - [PluginController("UmbracoApi")] + [PluginController("UmbracoApi")] [PrefixlessBodyModelValidator] public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController { @@ -36,7 +36,7 @@ namespace Umbraco.Web.Editors /// protected ContentTypeControllerBase() : this(UmbracoContext.Current) - { + { } /// @@ -61,17 +61,17 @@ namespace Umbraco.Web.Editors /// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot /// be looked up via the db, they need to be passed in. /// - /// + /// /// - protected IEnumerable> PerformGetAvailableCompositeContentTypes(int contentTypeId, - UmbracoObjectTypes type, + protected IEnumerable> PerformGetAvailableCompositeContentTypes(int contentTypeId, + UmbracoObjectTypes type, string[] filterContentTypes, string[] filterPropertyTypes) { IContentTypeComposition source = null; //below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic - + IContentTypeComposition[] allContentTypes; switch (type) @@ -132,7 +132,7 @@ namespace Umbraco.Web.Editors }) .ToList(); } - + protected string TranslateItem(string text) { @@ -155,7 +155,7 @@ namespace Umbraco.Web.Editors Action beforeCreateNew = null) where TContentType : class, IContentTypeComposition where TContentTypeDisplay : ContentTypeCompositionDisplay - where TContentTypeSave : ContentTypeSave + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic { var ctId = Convert.ToInt32(contentTypeSave.Id); @@ -187,10 +187,10 @@ namespace Umbraco.Web.Editors { group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList(); } - + if (ctId > 0) { - //its an update to an existing content type + //its an update to an existing content type //This mapping will cause a lot of content type validation to occur which we need to deal with try @@ -216,7 +216,7 @@ namespace Umbraco.Web.Editors { beforeCreateNew(contentTypeSave); } - + //check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type //always filter these 0 types out var allowItselfAsChild = false; @@ -227,12 +227,12 @@ namespace Umbraco.Web.Editors } //save as new - + TContentType newCt = null; try { //This mapping will cause a lot of content type validation to occur which we need to deal with - newCt = Mapper.Map(contentTypeSave); + newCt = Mapper.Map(contentTypeSave); } catch (Exception ex) { @@ -260,18 +260,18 @@ namespace Umbraco.Web.Editors return newCt; } } - + /// - /// Change the sort order for media + /// Move /// /// /// - /// + /// /// - protected HttpResponseMessage PerformMove( + protected HttpResponseMessage PerformMoveOrCopy( MoveOrCopy move, Func getContentType, - Func>> doMove) + Func>> doMoveOrCopy) where TContentType : IContentTypeComposition { var toMove = getContentType(move.Id); @@ -280,7 +280,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.NotFound); } - var result = doMove(toMove, move.ParentId); + var result = doMoveOrCopy(toMove, move.ParentId); if (result.Success) { var response = Request.CreateResponse(HttpStatusCode.OK); @@ -293,7 +293,7 @@ namespace Umbraco.Web.Editors case MoveOperationStatusType.FailedParentNotFound: return Request.CreateResponse(HttpStatusCode.NotFound); case MoveOperationStatusType.FailedCancelledByEvent: - //returning an object of INotificationModel will ensure that any pending + //returning an object of INotificationModel will ensure that any pending // notification messages are added to the response. return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: @@ -312,14 +312,14 @@ namespace Umbraco.Web.Editors /// /// private HttpResponseException CreateCompositionValidationExceptionIfInvalid(TContentTypeSave contentTypeSave, IContentTypeComposition composition) - where TContentTypeSave : ContentTypeSave + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic where TContentTypeDisplay : ContentTypeCompositionDisplay { var validateAttempt = Services.ContentTypeService.ValidateComposition(composition); if (validateAttempt == false) { - //if it's not successful then we need to return some model state for the property aliases that + //if it's not successful then we need to return some model state for the property aliases that // are duplicated var invalidPropertyAliases = validateAttempt.Result.Distinct(); AddCompositionValidationErrors(contentTypeSave, invalidPropertyAliases); @@ -340,7 +340,7 @@ namespace Umbraco.Web.Editors /// /// private void AddCompositionValidationErrors(TContentTypeSave contentTypeSave, IEnumerable invalidPropertyAliases) - where TContentTypeSave : ContentTypeSave + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic { foreach (var propertyAlias in invalidPropertyAliases) @@ -369,8 +369,8 @@ namespace Umbraco.Web.Editors private HttpResponseException CreateInvalidCompositionResponseException( Exception ex, TContentTypeSave contentTypeSave, TContentType ct, int ctId) where TContentType : class, IContentTypeComposition - where TContentTypeDisplay : ContentTypeCompositionDisplay - where TContentTypeSave : ContentTypeSave + where TContentTypeDisplay : ContentTypeCompositionDisplay + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic { InvalidCompositionException invalidCompositionException = null; @@ -431,7 +431,7 @@ namespace Umbraco.Web.Editors (_cultureDictionary = CultureDictionaryFactoryResolver.Current.Factory.CreateDictionary()); } } - + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index 43071125de..81b92f1ee4 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -22,7 +22,7 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Editors { - //TODO: We'll need to be careful about the security on this controller, when we start implementing + //TODO: We'll need to be careful about the security on this controller, when we start implementing // methods to modify content types we'll need to enforce security on the individual methods, we // cannot put security on the whole controller because things like GetAllowedChildren are required for content editing. @@ -49,7 +49,7 @@ namespace Umbraco.Web.Editors public MediaTypeController(UmbracoContext umbracoContext) : base(umbracoContext) { - + } public int GetCount() @@ -112,7 +112,7 @@ namespace Umbraco.Web.Editors contentType = x.Item1, allowed = x.Item2 }); - return Request.CreateResponse(result); + return Request.CreateResponse(result); } public MediaTypeDisplay GetEmpty(int parentId) @@ -129,8 +129,8 @@ namespace Umbraco.Web.Editors /// Returns all member types /// public IEnumerable GetAll() - { - + { + return Services.ContentTypeService.GetAllMediaTypes() .Select(Mapper.Map); } @@ -154,7 +154,7 @@ namespace Umbraco.Web.Editors var result = Services.ContentTypeService.CreateMediaTypeContainer(parentId, name, Security.CurrentUser.Id); return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id + ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); } @@ -227,11 +227,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMove( - move, - getContentType: i => Services.ContentTypeService.GetMediaType(i), - doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); + return PerformMoveOrCopy( + move, + getContentType: i => Services.ContentTypeService.GetMediaType(i), + doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index c45d5d0c1b..1cda4995fa 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -123,6 +123,7 @@ namespace Umbraco.Web.Trees menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true); } } + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias))); menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionExport.Instance.Alias)), true).ConvertLegacyMenuItem(new UmbracoEntity { Id = int.Parse(id), From dafde545b0547c1e4759e79f92f5417863cca924 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 16:02:04 +0100 Subject: [PATCH 2/3] U4-7641 - bugfix + implement for media types --- src/Umbraco.Core/Models/IMediaType.cs | 8 ++- src/Umbraco.Core/Models/MediaType.cs | 25 ++++++++ .../Repositories/ContentTypeBaseRepository.cs | 4 +- .../Interfaces/IContentTypeRepository.cs | 1 + .../Interfaces/IMediaTypeRepository.cs | 8 +++ .../Services/ContentTypeService.cs | 42 +++++++++++++ .../Services/IContentTypeService.cs | 1 + .../common/resources/mediatype.resource.js | 26 +++++++- .../src/views/mediatypes/copy.controller.js | 63 +++++++++++++++++++ .../src/views/mediatypes/copy.html | 51 +++++++++++++++ .../Editors/MediaTypeController.cs | 13 ++++ .../Trees/MediaTypeTreeController.cs | 1 + 12 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html diff --git a/src/Umbraco.Core/Models/IMediaType.cs b/src/Umbraco.Core/Models/IMediaType.cs index 3934d7a40f..29e4b665ba 100644 --- a/src/Umbraco.Core/Models/IMediaType.cs +++ b/src/Umbraco.Core/Models/IMediaType.cs @@ -7,6 +7,12 @@ namespace Umbraco.Core.Models /// + /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset + /// + /// + /// + IMediaType DeepCloneWithResetIdentities(string newAlias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs index c8e2915afd..8596cce910 100644 --- a/src/Umbraco.Core/Models/MediaType.cs +++ b/src/Umbraco.Core/Models/MediaType.cs @@ -38,5 +38,30 @@ namespace Umbraco.Core.Models : base(parent, alias) { } + + /// + /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset + /// + /// + public IMediaType DeepCloneWithResetIdentities(string alias) + { + var clone = (MediaType)DeepClone(); + clone.Alias = alias; + clone.Key = Guid.Empty; + foreach (var propertyGroup in clone.PropertyGroups) + { + propertyGroup.ResetIdentity(); + propertyGroup.ResetDirtyProperties(false); + } + foreach (var propertyType in clone.PropertyTypes) + { + propertyType.ResetIdentity(); + propertyType.ResetDirtyProperties(false); + } + + clone.ResetIdentity(); + clone.ResetDirtyProperties(false); + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index d3e7182783..9a33d71519 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1215,11 +1215,11 @@ AND umbracoNode.id <> @id", public string GetUniqueAlias(string alias) { + // alias is unique accross ALL content types! var aliasColumn = SqlSyntax.GetQuotedColumnName("alias"); var aliases = Database.Fetch(@"SELECT cmsContentType." + aliasColumn + @" FROM cmsContentType INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id -WHERE cmsContentType." + aliasColumn + @" LIKE @pattern -AND umbracoNode.nodeObjectType = @objectType", +WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", new { pattern = alias + "%", objectType = NodeObjectTypeId }); var i = 1; string test; diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index acb1d50a61..61d83645b3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -38,6 +38,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// The original alias. /// The original alias with a number appended to it, so that it is unique. + /// /// Unique accross all content, media and member types. string GetUniqueAlias(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs index 1cec8005c9..7f2f76e541 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs @@ -15,5 +15,13 @@ namespace Umbraco.Core.Persistence.Repositories IEnumerable GetByQuery(IQuery query); IEnumerable> Move(IMediaType toMove, EntityContainer container); + + /// + /// Derives a unique alias from an existing alias. + /// + /// The original alias. + /// The original alias with a number appended to it, so that it is unique. + /// Unique accross all content, media and member types. + string GetUniqueAlias(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 141f4a5a97..72e73d61ae 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -1067,6 +1067,48 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } + public Attempt> CopyMediaType(IMediaType toCopy, int containerId) + { + var evtMsgs = EventMessagesFactory.Get(); + + var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid)) + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) + { + try + { + if (containerId > 0) + { + var container = containerRepository.Get(containerId); + if (container == null) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + var alias = repository.GetUniqueAlias(toCopy.Alias); + var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy.Name = copy.Name + " (copy)"; // might not be unique + + // if it has a parent, and the parent is a content type, unplug composition + // all other compositions remain in place in the copied content type + if (copy.ParentId > 0) + { + var parent = repository.Get(copy.ParentId); + if (parent != null) + copy.RemoveContentType(parent.Alias); + } + + copy.ParentId = containerId; + repository.AddOrUpdate(copy); + } + catch (DataOperationException ex) + { + return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + } + uow.Commit(); + } + + return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + } + public Attempt> CopyContentType(IContentType toCopy, int containerId) { var evtMsgs = EventMessagesFactory.Get(); diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index e55430330c..cbb8bcddb4 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -291,6 +291,7 @@ namespace Umbraco.Core.Services Attempt> MoveMediaType(IMediaType toMove, int containerId); Attempt> MoveContentType(IContentType toMove, int containerId); + Attempt> CopyMediaType(IMediaType toCopy, int containerId); Attempt> CopyContentType(IContentType toCopy, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js index 516b9eb5d1..117edef77f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js @@ -53,7 +53,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function(array) { * $scope.type = type; * }); - * + * * @param {Int} mediaId id of the media item to retrive allowed child types for * @returns {Promise} resourcePromise object. * @@ -145,9 +145,9 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function() { * alert("node was moved"); * }, function(err){ - * alert("node didnt move:" + err.data.Message); + * alert("node didnt move:" + err.data.Message); * }); - * + * * @param {Object} args arguments object * @param {Int} args.idd the ID of the node to move * @param {Int} args.parentId the ID of the parent node to move to @@ -174,6 +174,26 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to move content'); }, + copy: function (args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.parentId) { + throw "args.parentId cannot be null"; + } + if (!args.id) { + throw "args.id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostCopy"), + { + parentId: args.parentId, + id: args.id + }), + 'Failed to copy content'); + }, + createContainer: function(parentId, name) { return umbRequestHelper.resourcePromise( diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js new file mode 100644 index 0000000000..2a1b2463f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js @@ -0,0 +1,63 @@ +angular.module("umbraco") +.controller("Umbraco.Editors.MediaTypes.CopyController", + function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + mediaTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html new file mode 100644 index 0000000000..319e59c4cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html @@ -0,0 +1,51 @@ +
+ +
+
+ +

+ Select the folder to copy {{currentNode.name}} to in the tree structure below +

+ +
+
+
+ +
+
{{error.errorMsg}}
+

{{error.data.message}}

+
+ +
+
+ {{currentNode.name}} was copied underneath {{target.name}}
+ +
+ +
+ +
+ + +
+ +
+
+
+ + +
diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index 81b92f1ee4..f736c207b8 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -232,5 +232,18 @@ namespace Umbraco.Web.Editors getContentType: i => Services.ContentTypeService.GetMediaType(i), doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); } + + /// + /// Copy the media type + /// + /// + /// + public HttpResponseMessage PostCopy(MoveOrCopy copy) + { + return PerformMoveOrCopy( + copy, + getContentType: i => Services.ContentTypeService.GetMediaType(i), + doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i)); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index a7da33898d..717d53ab96 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -92,6 +92,7 @@ namespace Umbraco.Web.Trees { menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias))); } return menu; From f349a9ae3e9dcfb8f0ec28088ed5419a22a9fea0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 16:26:20 +0100 Subject: [PATCH 3/3] U4-7641 - bugfix --- .../Services/ContentTypeService.cs | 18 ++++--- .../Services/IContentTypeService.cs | 4 +- .../Editors/ContentTypeController.cs | 8 +-- .../Editors/ContentTypeControllerBase.cs | 53 +++++++++++++++++-- .../Editors/MediaTypeController.cs | 8 +-- 5 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 72e73d61ae..34dcdc01a9 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -1067,10 +1067,11 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } - public Attempt> CopyMediaType(IMediaType toCopy, int containerId) + public Attempt> CopyMediaType(IMediaType toCopy, int containerId) { var evtMsgs = EventMessagesFactory.Get(); + IMediaType copy; var uow = UowProvider.GetUnitOfWork(); using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid)) using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) @@ -1084,7 +1085,7 @@ namespace Umbraco.Core.Services throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); } var alias = repository.GetUniqueAlias(toCopy.Alias); - var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy = toCopy.DeepCloneWithResetIdentities(alias); copy.Name = copy.Name + " (copy)"; // might not be unique // if it has a parent, and the parent is a content type, unplug composition @@ -1101,18 +1102,19 @@ namespace Umbraco.Core.Services } catch (DataOperationException ex) { - return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + return Attempt.Fail(new OperationStatus(null, ex.Operation, evtMsgs)); } uow.Commit(); } - return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + return Attempt.Succeed(new OperationStatus(copy, MoveOperationStatusType.Success, evtMsgs)); } - public Attempt> CopyContentType(IContentType toCopy, int containerId) + public Attempt> CopyContentType(IContentType toCopy, int containerId) { var evtMsgs = EventMessagesFactory.Get(); + IContentType copy; var uow = UowProvider.GetUnitOfWork(); using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid)) using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) @@ -1126,7 +1128,7 @@ namespace Umbraco.Core.Services throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); } var alias = repository.GetUniqueAlias(toCopy.Alias); - var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy = toCopy.DeepCloneWithResetIdentities(alias); copy.Name = copy.Name + " (copy)"; // might not be unique // if it has a parent, and the parent is a content type, unplug composition @@ -1143,12 +1145,12 @@ namespace Umbraco.Core.Services } catch (DataOperationException ex) { - return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + return Attempt.Fail(new OperationStatus(null, ex.Operation, evtMsgs)); } uow.Commit(); } - return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + return Attempt.Succeed(new OperationStatus(copy, MoveOperationStatusType.Success, evtMsgs)); } /// diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index cbb8bcddb4..2dcdf01291 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -291,7 +291,7 @@ namespace Umbraco.Core.Services Attempt> MoveMediaType(IMediaType toMove, int containerId); Attempt> MoveContentType(IContentType toMove, int containerId); - Attempt> CopyMediaType(IMediaType toCopy, int containerId); - Attempt> CopyContentType(IContentType toCopy, int containerId); + Attempt> CopyMediaType(IMediaType toCopy, int containerId); + Attempt> CopyContentType(IContentType toCopy, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 5099db8cac..45a2821130 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -309,10 +309,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMoveOrCopy( + return PerformMove( move, getContentType: i => Services.ContentTypeService.GetContentType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); + doMove: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); } /// @@ -322,10 +322,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostCopy(MoveOrCopy copy) { - return PerformMoveOrCopy( + return PerformCopy( copy, getContentType: i => Services.ContentTypeService.GetContentType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i)); + doCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i)); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index 361c1133fd..0e699b591a 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -266,12 +266,12 @@ namespace Umbraco.Web.Editors /// /// /// - /// + /// /// - protected HttpResponseMessage PerformMoveOrCopy( + protected HttpResponseMessage PerformMove( MoveOrCopy move, Func getContentType, - Func>> doMoveOrCopy) + Func>> doMove) where TContentType : IContentTypeComposition { var toMove = getContentType(move.Id); @@ -280,7 +280,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.NotFound); } - var result = doMoveOrCopy(toMove, move.ParentId); + var result = doMove(toMove, move.ParentId); if (result.Success) { var response = Request.CreateResponse(HttpStatusCode.OK); @@ -305,6 +305,51 @@ namespace Umbraco.Web.Editors } } + /// + /// Move + /// + /// + /// + /// + /// + protected HttpResponseMessage PerformCopy( + MoveOrCopy move, + Func getContentType, + Func>> doCopy) + where TContentType : IContentTypeComposition + { + var toMove = getContentType(move.Id); + if (toMove == null) + { + return Request.CreateResponse(HttpStatusCode.NotFound); + } + + var result = doCopy(toMove, move.ParentId); + if (result.Success) + { + var copy = result.Result.Entity; + var response = Request.CreateResponse(HttpStatusCode.OK); + response.Content = new StringContent(copy.Path, Encoding.UTF8, "application/json"); + return response; + } + + switch (result.Result.StatusType) + { + case MoveOperationStatusType.FailedParentNotFound: + return Request.CreateResponse(HttpStatusCode.NotFound); + case MoveOperationStatusType.FailedCancelledByEvent: + //returning an object of INotificationModel will ensure that any pending + // notification messages are added to the response. + return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + case MoveOperationStatusType.FailedNotAllowedByPath: + var notificationModel = new SimpleNotificationModel(); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); + return Request.CreateValidationErrorResponse(notificationModel); + default: + throw new ArgumentOutOfRangeException(); + } + } + /// /// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors /// diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index f736c207b8..31be4509fb 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -227,10 +227,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMoveOrCopy( + return PerformMove( move, getContentType: i => Services.ContentTypeService.GetMediaType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); + doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); } /// @@ -240,10 +240,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostCopy(MoveOrCopy copy) { - return PerformMoveOrCopy( + return PerformCopy( copy, getContentType: i => Services.ContentTypeService.GetMediaType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i)); + doCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i)); } } } \ No newline at end of file