U4-7641 - enable content type copy
This commit is contained in:
@@ -1212,5 +1212,19 @@ AND umbracoNode.id <> @id",
|
|||||||
{
|
{
|
||||||
return PerformExists(id);
|
return PerformExists(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetUniqueAlias(string alias)
|
||||||
|
{
|
||||||
|
var aliasColumn = SqlSyntax.GetQuotedColumnName("alias");
|
||||||
|
var aliases = Database.Fetch<string>(@"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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IEnumerable<string> GetAllContentTypeAliases(params Guid[] objectTypes);
|
IEnumerable<string> GetAllContentTypeAliases(params Guid[] objectTypes);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Derives a unique alias from an existing alias.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="alias">The original alias.</param>
|
||||||
|
/// <returns>The original alias with a number appended to it, so that it is unique.</returns>
|
||||||
|
string GetUniqueAlias(string alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
//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
|
// 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...
|
// 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.
|
// 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>(MoveOperationStatusType.Success, evtMsgs));
|
new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.Success, evtMsgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Attempt<OperationStatus<MoveOperationStatusType>> 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>(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<MoveOperationStatusType> ex)
|
||||||
|
{
|
||||||
|
return Attempt.Fail(new OperationStatus<MoveOperationStatusType>(ex.Operation, evtMsgs));
|
||||||
|
}
|
||||||
|
uow.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Attempt.Succeed(new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.Success, evtMsgs));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves a single <see cref="IMediaType"/> object
|
/// Saves a single <see cref="IMediaType"/> object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -291,5 +291,6 @@ namespace Umbraco.Core.Services
|
|||||||
|
|
||||||
Attempt<OperationStatus<MoveOperationStatusType>> MoveMediaType(IMediaType toMove, int containerId);
|
Attempt<OperationStatus<MoveOperationStatusType>> MoveMediaType(IMediaType toMove, int containerId);
|
||||||
Attempt<OperationStatus<MoveOperationStatusType>> MoveContentType(IContentType toMove, int containerId);
|
Attempt<OperationStatus<MoveOperationStatusType>> MoveContentType(IContentType toMove, int containerId);
|
||||||
|
Attempt<OperationStatus<MoveOperationStatusType>> CopyContentType(IContentType toCopy, int containerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
|||||||
filterContentTypes: filterContentTypes,
|
filterContentTypes: filterContentTypes,
|
||||||
filterPropertyTypes: filterPropertyTypes
|
filterPropertyTypes: filterPropertyTypes
|
||||||
};
|
};
|
||||||
|
|
||||||
return umbRequestHelper.resourcePromise(
|
return umbRequestHelper.resourcePromise(
|
||||||
$http.post(
|
$http.post(
|
||||||
umbRequestHelper.getApiUrl(
|
umbRequestHelper.getApiUrl(
|
||||||
@@ -201,9 +201,9 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
|||||||
* .then(function() {
|
* .then(function() {
|
||||||
* alert("node was moved");
|
* alert("node was moved");
|
||||||
* }, function(err){
|
* }, function(err){
|
||||||
* alert("node didnt move:" + err.data.Message);
|
* alert("node didnt move:" + err.data.Message);
|
||||||
* });
|
* });
|
||||||
* </pre>
|
* </pre>
|
||||||
* @param {Object} args arguments object
|
* @param {Object} args arguments object
|
||||||
* @param {Int} args.idd the ID of the node to move
|
* @param {Int} args.idd the ID of the node to move
|
||||||
* @param {Int} args.parentId the ID of the parent node to move to
|
* @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');
|
'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) {
|
createContainer: function(parentId, name) {
|
||||||
|
|
||||||
return umbRequestHelper.resourcePromise(
|
return umbRequestHelper.resourcePromise(
|
||||||
@@ -237,7 +257,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
|||||||
'Failed to create a folder under parent id ' + parentId);
|
'Failed to create a folder under parent id ' + parentId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource);
|
angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
51
src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html
Normal file
51
src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.DocumentTypes.CopyController">
|
||||||
|
|
||||||
|
<div class="umb-dialog-body">
|
||||||
|
<div class="umb-pane">
|
||||||
|
|
||||||
|
<p class="abstract" ng-hide="success">
|
||||||
|
<localize key="contentTypeEditor_folderToCopy">Select the folder to copy</localize> <strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_structureBelow">to in the tree structure below</localize>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="umb-loader-wrapper" ng-show="busy">
|
||||||
|
<div class="umb-loader"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="error">
|
||||||
|
<h5 class="text-error">{{error.errorMsg}}</h5>
|
||||||
|
<p class="text-error">{{error.data.message}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="success">
|
||||||
|
<h5 class="text-success">
|
||||||
|
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize> <strong>{{target.name}}</strong></h5>
|
||||||
|
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-hide="success">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<umb-tree section="settings"
|
||||||
|
treealias="documentTypes"
|
||||||
|
customtreeparams="foldersonly=1"
|
||||||
|
hideheader="false"
|
||||||
|
hideoptions="true"
|
||||||
|
isdialog="true"
|
||||||
|
eventhandler="dialogTreeEventHandler"
|
||||||
|
enablecheckboxes="true">
|
||||||
|
</umb-tree>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
|
||||||
|
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
|
||||||
|
<localize key="general_cancel">Cancel</localize>
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-primary" ng-click="copy()" ng-disabled="busy">
|
||||||
|
<localize key="actions_copy">Copy</localize>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1055,7 +1055,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
|||||||
<key alias="yesDelete">Yes, delete</key>
|
<key alias="yesDelete">Yes, delete</key>
|
||||||
|
|
||||||
<key alias="movedUnderneath">was moved underneath</key>
|
<key alias="movedUnderneath">was moved underneath</key>
|
||||||
|
<key alias="copiedUnderneath">was copied underneath</key>
|
||||||
<key alias="folderToMove">Select the folder to move</key>
|
<key alias="folderToMove">Select the folder to move</key>
|
||||||
|
<key alias="folderToCopy">Select the folder to copy</key>
|
||||||
<key alias="structureBelow">to in the tree structure below</key>
|
<key alias="structureBelow">to in the tree structure below</key>
|
||||||
|
|
||||||
<key alias="allDocumentTypes">All Document types</key>
|
<key alias="allDocumentTypes">All Document types</key>
|
||||||
|
|||||||
@@ -1051,7 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
|||||||
<key alias="yesDelete">Yes, delete</key>
|
<key alias="yesDelete">Yes, delete</key>
|
||||||
|
|
||||||
<key alias="movedUnderneath">was moved underneath</key>
|
<key alias="movedUnderneath">was moved underneath</key>
|
||||||
|
<key alias="copiedUnderneath">was copied underneath</key>
|
||||||
<key alias="folderToMove">Select the folder to move</key>
|
<key alias="folderToMove">Select the folder to move</key>
|
||||||
|
<key alias="folderToCopy">Select the folder to copy</key>
|
||||||
<key alias="structureBelow">to in the tree structure below</key>
|
<key alias="structureBelow">to in the tree structure below</key>
|
||||||
|
|
||||||
<key alias="allDocumentTypes">All Document types</key>
|
<key alias="allDocumentTypes">All Document types</key>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Configuration;
|
using System.Configuration;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -22,9 +23,9 @@ using Umbraco.Web.Models;
|
|||||||
|
|
||||||
namespace Umbraco.Web.Editors
|
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
|
// 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.
|
// GetAllowedChildren, GetPropertyTypeScaffold, GetAllPropertyTypeAliases are required for content editing.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -129,7 +130,7 @@ namespace Umbraco.Web.Editors
|
|||||||
}
|
}
|
||||||
|
|
||||||
[UmbracoTreeAuthorize(
|
[UmbracoTreeAuthorize(
|
||||||
Constants.Trees.DocumentTypes, Constants.Trees.Content,
|
Constants.Trees.DocumentTypes, Constants.Trees.Content,
|
||||||
Constants.Trees.MediaTypes, Constants.Trees.Media,
|
Constants.Trees.MediaTypes, Constants.Trees.Media,
|
||||||
Constants.Trees.MemberTypes, Constants.Trees.Members)]
|
Constants.Trees.MemberTypes, Constants.Trees.Members)]
|
||||||
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
|
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
|
||||||
@@ -166,13 +167,13 @@ namespace Umbraco.Web.Editors
|
|||||||
|
|
||||||
return Request.CreateResponse(HttpStatusCode.OK);
|
return Request.CreateResponse(HttpStatusCode.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponseMessage PostCreateContainer(int parentId, string name)
|
public HttpResponseMessage PostCreateContainer(int parentId, string name)
|
||||||
{
|
{
|
||||||
var result = Services.ContentTypeService.CreateContentTypeContainer(parentId, name, Security.CurrentUser.Id);
|
var result = Services.ContentTypeService.CreateContentTypeContainer(parentId, name, Security.CurrentUser.Id);
|
||||||
|
|
||||||
return result
|
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);
|
: 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
|
//make sure the template alias is set on the default and allowed template so we can map it back
|
||||||
ctSave.DefaultTemplate = template.Alias;
|
ctSave.DefaultTemplate = template.Alias;
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -231,7 +232,7 @@ namespace Umbraco.Web.Editors
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
ct = new ContentType(parentId);
|
ct = new ContentType(parentId);
|
||||||
|
|
||||||
ct.Icon = "icon-document";
|
ct.Icon = "icon-document";
|
||||||
|
|
||||||
var dto = Mapper.Map<IContentType, DocumentTypeDisplay>(ct);
|
var dto = Mapper.Map<IContentType, DocumentTypeDisplay>(ct);
|
||||||
@@ -302,18 +303,29 @@ namespace Umbraco.Web.Editors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Move the media type
|
/// Move the content type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="move"></param>
|
/// <param name="move"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public HttpResponseMessage PostMove(MoveOrCopy move)
|
public HttpResponseMessage PostMove(MoveOrCopy move)
|
||||||
{
|
{
|
||||||
return PerformMove(
|
return PerformMoveOrCopy(
|
||||||
move,
|
move,
|
||||||
getContentType: i => Services.ContentTypeService.GetContentType(i),
|
getContentType: i => Services.ContentTypeService.GetContentType(i),
|
||||||
doMove: (type, i) => Services.ContentTypeService.MoveContentType(type, i));
|
doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveContentType(type, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copy the content type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="copy"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public HttpResponseMessage PostCopy(MoveOrCopy copy)
|
||||||
|
{
|
||||||
|
return PerformMoveOrCopy(
|
||||||
|
copy,
|
||||||
|
getContentType: i => Services.ContentTypeService.GetContentType(i),
|
||||||
|
doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ namespace Umbraco.Web.Editors
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Am abstract API controller providing functionality used for dealing with content and media types
|
/// Am abstract API controller providing functionality used for dealing with content and media types
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginController("UmbracoApi")]
|
[PluginController("UmbracoApi")]
|
||||||
[PrefixlessBodyModelValidator]
|
[PrefixlessBodyModelValidator]
|
||||||
public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController
|
public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController
|
||||||
{
|
{
|
||||||
@@ -36,7 +36,7 @@ namespace Umbraco.Web.Editors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected ContentTypeControllerBase()
|
protected ContentTypeControllerBase()
|
||||||
: this(UmbracoContext.Current)
|
: this(UmbracoContext.Current)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -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
|
/// 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.
|
/// be looked up via the db, they need to be passed in.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="contentTypeId"></param>
|
/// <param name="contentTypeId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected IEnumerable<Tuple<EntityBasic, bool>> PerformGetAvailableCompositeContentTypes(int contentTypeId,
|
protected IEnumerable<Tuple<EntityBasic, bool>> PerformGetAvailableCompositeContentTypes(int contentTypeId,
|
||||||
UmbracoObjectTypes type,
|
UmbracoObjectTypes type,
|
||||||
string[] filterContentTypes,
|
string[] filterContentTypes,
|
||||||
string[] filterPropertyTypes)
|
string[] filterPropertyTypes)
|
||||||
{
|
{
|
||||||
IContentTypeComposition source = null;
|
IContentTypeComposition source = null;
|
||||||
|
|
||||||
//below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic
|
//below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic
|
||||||
|
|
||||||
IContentTypeComposition[] allContentTypes;
|
IContentTypeComposition[] allContentTypes;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
@@ -132,7 +132,7 @@ namespace Umbraco.Web.Editors
|
|||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected string TranslateItem(string text)
|
protected string TranslateItem(string text)
|
||||||
{
|
{
|
||||||
@@ -155,7 +155,7 @@ namespace Umbraco.Web.Editors
|
|||||||
Action<TContentTypeSave> beforeCreateNew = null)
|
Action<TContentTypeSave> beforeCreateNew = null)
|
||||||
where TContentType : class, IContentTypeComposition
|
where TContentType : class, IContentTypeComposition
|
||||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||||
where TPropertyType : PropertyTypeBasic
|
where TPropertyType : PropertyTypeBasic
|
||||||
{
|
{
|
||||||
var ctId = Convert.ToInt32(contentTypeSave.Id);
|
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();
|
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctId > 0)
|
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
|
//This mapping will cause a lot of content type validation to occur which we need to deal with
|
||||||
try
|
try
|
||||||
@@ -216,7 +216,7 @@ namespace Umbraco.Web.Editors
|
|||||||
{
|
{
|
||||||
beforeCreateNew(contentTypeSave);
|
beforeCreateNew(contentTypeSave);
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type
|
//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
|
//always filter these 0 types out
|
||||||
var allowItselfAsChild = false;
|
var allowItselfAsChild = false;
|
||||||
@@ -227,12 +227,12 @@ namespace Umbraco.Web.Editors
|
|||||||
}
|
}
|
||||||
|
|
||||||
//save as new
|
//save as new
|
||||||
|
|
||||||
TContentType newCt = null;
|
TContentType newCt = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//This mapping will cause a lot of content type validation to occur which we need to deal with
|
//This mapping will cause a lot of content type validation to occur which we need to deal with
|
||||||
newCt = Mapper.Map<TContentType>(contentTypeSave);
|
newCt = Mapper.Map<TContentType>(contentTypeSave);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -260,18 +260,18 @@ namespace Umbraco.Web.Editors
|
|||||||
return newCt;
|
return newCt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change the sort order for media
|
/// Move
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="move"></param>
|
/// <param name="move"></param>
|
||||||
/// <param name="getContentType"></param>
|
/// <param name="getContentType"></param>
|
||||||
/// <param name="doMove"></param>
|
/// <param name="doMoveOrCopy"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected HttpResponseMessage PerformMove<TContentType>(
|
protected HttpResponseMessage PerformMoveOrCopy<TContentType>(
|
||||||
MoveOrCopy move,
|
MoveOrCopy move,
|
||||||
Func<int, TContentType> getContentType,
|
Func<int, TContentType> getContentType,
|
||||||
Func<TContentType, int, Attempt<OperationStatus<MoveOperationStatusType>>> doMove)
|
Func<TContentType, int, Attempt<OperationStatus<MoveOperationStatusType>>> doMoveOrCopy)
|
||||||
where TContentType : IContentTypeComposition
|
where TContentType : IContentTypeComposition
|
||||||
{
|
{
|
||||||
var toMove = getContentType(move.Id);
|
var toMove = getContentType(move.Id);
|
||||||
@@ -280,7 +280,7 @@ namespace Umbraco.Web.Editors
|
|||||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = doMove(toMove, move.ParentId);
|
var result = doMoveOrCopy(toMove, move.ParentId);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
var response = Request.CreateResponse(HttpStatusCode.OK);
|
var response = Request.CreateResponse(HttpStatusCode.OK);
|
||||||
@@ -293,7 +293,7 @@ namespace Umbraco.Web.Editors
|
|||||||
case MoveOperationStatusType.FailedParentNotFound:
|
case MoveOperationStatusType.FailedParentNotFound:
|
||||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||||
case MoveOperationStatusType.FailedCancelledByEvent:
|
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.
|
// notification messages are added to the response.
|
||||||
return Request.CreateValidationErrorResponse(new SimpleNotificationModel());
|
return Request.CreateValidationErrorResponse(new SimpleNotificationModel());
|
||||||
case MoveOperationStatusType.FailedNotAllowedByPath:
|
case MoveOperationStatusType.FailedNotAllowedByPath:
|
||||||
@@ -312,14 +312,14 @@ namespace Umbraco.Web.Editors
|
|||||||
/// <param name="composition"></param>
|
/// <param name="composition"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private HttpResponseException CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(TContentTypeSave contentTypeSave, IContentTypeComposition composition)
|
private HttpResponseException CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(TContentTypeSave contentTypeSave, IContentTypeComposition composition)
|
||||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||||
where TPropertyType : PropertyTypeBasic
|
where TPropertyType : PropertyTypeBasic
|
||||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||||
{
|
{
|
||||||
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
|
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
|
||||||
if (validateAttempt == false)
|
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
|
// are duplicated
|
||||||
var invalidPropertyAliases = validateAttempt.Result.Distinct();
|
var invalidPropertyAliases = validateAttempt.Result.Distinct();
|
||||||
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidPropertyAliases);
|
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidPropertyAliases);
|
||||||
@@ -340,7 +340,7 @@ namespace Umbraco.Web.Editors
|
|||||||
/// <param name="invalidPropertyAliases"></param>
|
/// <param name="invalidPropertyAliases"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave, IEnumerable<string> invalidPropertyAliases)
|
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave, IEnumerable<string> invalidPropertyAliases)
|
||||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||||
where TPropertyType : PropertyTypeBasic
|
where TPropertyType : PropertyTypeBasic
|
||||||
{
|
{
|
||||||
foreach (var propertyAlias in invalidPropertyAliases)
|
foreach (var propertyAlias in invalidPropertyAliases)
|
||||||
@@ -369,8 +369,8 @@ namespace Umbraco.Web.Editors
|
|||||||
private HttpResponseException CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentType, TContentTypeSave, TPropertyType>(
|
private HttpResponseException CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentType, TContentTypeSave, TPropertyType>(
|
||||||
Exception ex, TContentTypeSave contentTypeSave, TContentType ct, int ctId)
|
Exception ex, TContentTypeSave contentTypeSave, TContentType ct, int ctId)
|
||||||
where TContentType : class, IContentTypeComposition
|
where TContentType : class, IContentTypeComposition
|
||||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||||
where TPropertyType : PropertyTypeBasic
|
where TPropertyType : PropertyTypeBasic
|
||||||
{
|
{
|
||||||
InvalidCompositionException invalidCompositionException = null;
|
InvalidCompositionException invalidCompositionException = null;
|
||||||
@@ -431,7 +431,7 @@ namespace Umbraco.Web.Editors
|
|||||||
(_cultureDictionary = CultureDictionaryFactoryResolver.Current.Factory.CreateDictionary());
|
(_cultureDictionary = CultureDictionaryFactoryResolver.Current.Factory.CreateDictionary());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ using Umbraco.Web.Models;
|
|||||||
|
|
||||||
namespace Umbraco.Web.Editors
|
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
|
// 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.
|
// 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)
|
public MediaTypeController(UmbracoContext umbracoContext)
|
||||||
: base(umbracoContext)
|
: base(umbracoContext)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetCount()
|
public int GetCount()
|
||||||
@@ -112,7 +112,7 @@ namespace Umbraco.Web.Editors
|
|||||||
contentType = x.Item1,
|
contentType = x.Item1,
|
||||||
allowed = x.Item2
|
allowed = x.Item2
|
||||||
});
|
});
|
||||||
return Request.CreateResponse(result);
|
return Request.CreateResponse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaTypeDisplay GetEmpty(int parentId)
|
public MediaTypeDisplay GetEmpty(int parentId)
|
||||||
@@ -129,8 +129,8 @@ namespace Umbraco.Web.Editors
|
|||||||
/// Returns all member types
|
/// Returns all member types
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<ContentTypeBasic> GetAll()
|
public IEnumerable<ContentTypeBasic> GetAll()
|
||||||
{
|
{
|
||||||
|
|
||||||
return Services.ContentTypeService.GetAllMediaTypes()
|
return Services.ContentTypeService.GetAllMediaTypes()
|
||||||
.Select(Mapper.Map<IMediaType, ContentTypeBasic>);
|
.Select(Mapper.Map<IMediaType, ContentTypeBasic>);
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ namespace Umbraco.Web.Editors
|
|||||||
var result = Services.ContentTypeService.CreateMediaTypeContainer(parentId, name, Security.CurrentUser.Id);
|
var result = Services.ContentTypeService.CreateMediaTypeContainer(parentId, name, Security.CurrentUser.Id);
|
||||||
|
|
||||||
return result
|
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);
|
: Request.CreateNotificationValidationErrorResponse(result.Exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,11 +227,10 @@ namespace Umbraco.Web.Editors
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public HttpResponseMessage PostMove(MoveOrCopy move)
|
public HttpResponseMessage PostMove(MoveOrCopy move)
|
||||||
{
|
{
|
||||||
return PerformMove(
|
return PerformMoveOrCopy(
|
||||||
move,
|
move,
|
||||||
getContentType: i => Services.ContentTypeService.GetMediaType(i),
|
getContentType: i => Services.ContentTypeService.GetMediaType(i),
|
||||||
doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i));
|
doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,6 +123,7 @@ namespace Umbraco.Web.Trees
|
|||||||
menu.Items.Add<ActionMove>(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true);
|
menu.Items.Add<ActionMove>(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
menu.Items.Add<ActionCopy>(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias)));
|
||||||
menu.Items.Add<ActionExport>(Services.TextService.Localize(string.Format("actions/{0}", ActionExport.Instance.Alias)), true).ConvertLegacyMenuItem(new UmbracoEntity
|
menu.Items.Add<ActionExport>(Services.TextService.Localize(string.Format("actions/{0}", ActionExport.Instance.Alias)), true).ConvertLegacyMenuItem(new UmbracoEntity
|
||||||
{
|
{
|
||||||
Id = int.Parse(id),
|
Id = int.Parse(id),
|
||||||
|
|||||||
Reference in New Issue
Block a user