diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index a41c89da66..38370e7832 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -1826,7 +1826,9 @@
-
+
+ ASPXCodeBehind
+
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/actions/publish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/actions/publish.aspx.cs
index 9b964cb166..928ac680a6 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/actions/publish.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/actions/publish.aspx.cs
@@ -2,12 +2,14 @@ using System;
using System.Data;
using System.Configuration;
using System.Collections;
+using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
+using Umbraco.Core.Publishing;
using umbraco.cms.businesslogic.web;
using Umbraco.Core;
@@ -39,9 +41,45 @@ namespace umbraco.presentation.actions
deleteMessage.Text = ui.Text("editContentPublishedHeader");
confirm.Visible = false;
- d.SaveAndPublish(UmbracoUser);
+
+ var result = d.SaveAndPublishWithResult(UmbracoUser);
+ if (result.Success)
+ {
+ deleted.Text = ui.Text("editContentPublishedHeader") + " ('" + d.Text + "') " + ui.Text("editContentPublishedText") + " " + ui.Text("view") + " " + d.Text + "";
+ }
+ else
+ {
+ deleted.Text = "
" + GetMessageForStatus(result.Result) + "
";
+ }
- deleted.Text = ui.Text("editContentPublishedHeader") + " ('" + d.Text + "') " + ui.Text("editContentPublishedText") + " " + ui.Text("view") + " " + d.Text + "";
+ }
+
+ private string GetMessageForStatus(PublishStatus status)
+ {
+ switch (status.StatusType)
+ {
+ case PublishStatusType.Success:
+ case PublishStatusType.SuccessAlreadyPublished:
+ return ui.Text("speechBubbles", "editContentPublishedText", UmbracoUser);
+ case PublishStatusType.FailedPathNotPublished:
+ return ui.Text("publish", "contentPublishedFailedByParent",
+ string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id),
+ UmbracoUser).Trim();
+ case PublishStatusType.FailedCancelledByEvent:
+ return ui.Text("speechBubbles", "contentPublishedFailedByEvent");
+ case PublishStatusType.FailedHasExpired:
+ case PublishStatusType.FailedAwaitingRelease:
+ case PublishStatusType.FailedIsTrashed:
+ case PublishStatusType.FailedContentInvalid:
+ return ui.Text("publish", "contentPublishedFailedInvalid",
+ new[]
+ {
+ string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id),
+ string.Join(",", status.InvalidProperties.Select(x => x.Alias))
+ }, UmbracoUser);
+ default:
+ throw new IndexOutOfRangeException();
+ }
}
}
}
From 7d6736907f9db29f50d1f66bb9d16761e2e79809 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Wed, 24 Jul 2013 14:59:49 +1000
Subject: [PATCH 83/85] adds correct messaging for when publishing fails even
after validation might fail
---
src/Umbraco.Web/Editors/ContentController.cs | 153 ++++++------------
.../Editors/ContentControllerBase.cs | 99 ++++++++++++
src/Umbraco.Web/Umbraco.Web.csproj | 1 +
3 files changed, 147 insertions(+), 106 deletions(-)
create mode 100644 src/Umbraco.Web/Editors/ContentControllerBase.cs
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 55b3edc9fc..372b0671ae 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -6,10 +6,9 @@ using System.Net.Http;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using Umbraco.Core;
-using Umbraco.Core.Logging;
using Umbraco.Core.Models;
-using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
@@ -22,89 +21,6 @@ using umbraco;
namespace Umbraco.Web.Editors
{
- public abstract class ContentControllerBase : UmbracoAuthorizedJsonController
- {
- ///
- /// Constructor
- ///
- protected ContentControllerBase()
- : this(UmbracoContext.Current)
- {
- }
-
- ///
- /// Constructor
- ///
- ///
- protected ContentControllerBase(UmbracoContext umbracoContext)
- : base(umbracoContext)
- {
- }
-
- protected void HandleContentNotFound(int id)
- {
- ModelState.AddModelError("id", string.Format("content with id: {0} was not found", id));
- var errorResponse = Request.CreateErrorResponse(
- HttpStatusCode.NotFound,
- ModelState);
- throw new HttpResponseException(errorResponse);
- }
-
- protected void UpdateName(ContentItemSave contentItem)
- where TPersisted : IContentBase
- {
- //Don't update the name if it is empty
- if (!contentItem.Name.IsNullOrWhiteSpace())
- {
- contentItem.PersistedContent.Name = contentItem.Name;
- }
- }
-
- protected void MapPropertyValues(ContentItemSave contentItem)
- where TPersisted : IContentBase
- {
- //Map the property values
- foreach (var p in contentItem.ContentDto.Properties)
- {
- //get the dbo property
- var dboProperty = contentItem.PersistedContent.Properties[p.Alias];
-
- //create the property data to send to the property editor
- var d = new Dictionary();
- //add the files if any
- var files = contentItem.UploadedFiles.Where(x => x.PropertyId == p.Id).ToArray();
- if (files.Any())
- {
- d.Add("files", files);
- }
- var data = new ContentPropertyData(p.Value, d);
-
- //get the deserialized value from the property editor
- if (p.PropertyEditor == null)
- {
- LogHelper.Warn("No property editor found for property " + p.Alias);
- }
- else
- {
- dboProperty.Value = p.PropertyEditor.ValueEditor.DeserializeValue(data, dboProperty.Value);
- }
- }
- }
-
- protected void HandleInvalidModelState(ContentItemDisplayBase display)
- where TPersisted : IContentBase
- where T : ContentPropertyBasic
- {
- //lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
- if (!ModelState.IsValid)
- {
- display.Errors = ModelState.ToErrorDictionary();
- throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, display));
- }
- }
-
- }
-
///
/// The API controller used for editing content
///
@@ -226,7 +142,9 @@ namespace Umbraco.Web.Editors
}
}
- bool isPublishSuccess = false;
+ //initialize this to successful
+ var publishStatus = new Attempt(true, null);
+
if (contentItem.Action == ContentSaveAction.Save || contentItem.Action == ContentSaveAction.SaveNew)
{
//save the item
@@ -235,7 +153,7 @@ namespace Umbraco.Web.Editors
else
{
//publish the item and check if it worked, if not we will show a diff msg below
- isPublishSuccess = Services.ContentService.SaveAndPublish(contentItem.PersistedContent);
+ publishStatus = ((ContentService)Services.ContentService).SaveAndPublishInternal(contentItem.PersistedContent);
}
@@ -254,30 +172,53 @@ namespace Umbraco.Web.Editors
break;
case ContentSaveAction.Publish:
case ContentSaveAction.PublishNew:
-
- //If the document is at a level deeper than the root but it's ancestor's path is not published,
- //it means that we cannot actually publish this document because one of it's parent's is not published.
- //So, we still need to save the document but we'll show a different notification.
- if (contentItem.PersistedContent.Level > 1 && !Services.ContentService.IsPublishable(contentItem.PersistedContent))
- {
- display.AddWarningNotification(ui.Text("publish"), ui.Text("speechBubbles", "editContentPublishedFailedByParent"));
- }
- else
- {
- if (isPublishSuccess)
- {
- display.AddSuccessNotification(ui.Text("speechBubbles", "editContentPublishedHeader"), ui.Text("speechBubbles", "editContentPublishedText"));
- }
- else
- {
- display.AddWarningNotification(ui.Text("publish"), ui.Text("speechBubbles", "contentPublishedFailedByEvent"));
- }
- }
+ ShowMessageForStatus(publishStatus.Result, display);
break;
}
return display;
}
+ private void ShowMessageForStatus(PublishStatus status, ContentItemDisplay display)
+ {
+ switch (status.StatusType)
+ {
+ case PublishStatusType.Success:
+ case PublishStatusType.SuccessAlreadyPublished:
+ display.AddSuccessNotification(
+ ui.Text("speechBubbles", "editContentPublishedHeader", UmbracoUser),
+ ui.Text("speechBubbles", "editContentPublishedText", UmbracoUser));
+ break;
+ case PublishStatusType.FailedPathNotPublished:
+ display.AddWarningNotification(
+ ui.Text("publish"),
+ ui.Text("publish", "contentPublishedFailedByParent",
+ string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id),
+ UmbracoUser).Trim());
+ break;
+ case PublishStatusType.FailedCancelledByEvent:
+ display.AddWarningNotification(
+ ui.Text("publish"),
+ ui.Text("speechBubbles", "contentPublishedFailedByEvent"));
+ break;
+ case PublishStatusType.FailedHasExpired:
+ case PublishStatusType.FailedAwaitingRelease:
+ case PublishStatusType.FailedIsTrashed:
+ case PublishStatusType.FailedContentInvalid:
+ display.AddWarningNotification(
+ ui.Text("publish"),
+ ui.Text("publish", "contentPublishedFailedInvalid",
+ new[]
+ {
+ string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id),
+ string.Join(",", status.InvalidProperties.Select(x => x.Alias))
+ },
+ UmbracoUser).Trim());
+ break;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs
new file mode 100644
index 0000000000..12da55b2ee
--- /dev/null
+++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs
@@ -0,0 +1,99 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Web.Http;
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Editors;
+using Umbraco.Web.Models.ContentEditing;
+
+namespace Umbraco.Web.Editors
+{
+ ///
+ /// An abstract base controller used for media/content (and probably members) to try to reduce code replication.
+ ///
+ public abstract class ContentControllerBase : UmbracoAuthorizedJsonController
+ {
+ ///
+ /// Constructor
+ ///
+ protected ContentControllerBase()
+ : this(UmbracoContext.Current)
+ {
+ }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ protected ContentControllerBase(UmbracoContext umbracoContext)
+ : base(umbracoContext)
+ {
+ }
+
+ protected void HandleContentNotFound(int id)
+ {
+ ModelState.AddModelError("id", string.Format("content with id: {0} was not found", id));
+ var errorResponse = Request.CreateErrorResponse(
+ HttpStatusCode.NotFound,
+ ModelState);
+ throw new HttpResponseException(errorResponse);
+ }
+
+ protected void UpdateName(ContentItemSave contentItem)
+ where TPersisted : IContentBase
+ {
+ //Don't update the name if it is empty
+ if (!contentItem.Name.IsNullOrWhiteSpace())
+ {
+ contentItem.PersistedContent.Name = contentItem.Name;
+ }
+ }
+
+ protected void MapPropertyValues(ContentItemSave contentItem)
+ where TPersisted : IContentBase
+ {
+ //Map the property values
+ foreach (var p in contentItem.ContentDto.Properties)
+ {
+ //get the dbo property
+ var dboProperty = contentItem.PersistedContent.Properties[p.Alias];
+
+ //create the property data to send to the property editor
+ var d = new Dictionary();
+ //add the files if any
+ var files = contentItem.UploadedFiles.Where(x => x.PropertyId == p.Id).ToArray();
+ if (files.Any())
+ {
+ d.Add("files", files);
+ }
+ var data = new ContentPropertyData(p.Value, d);
+
+ //get the deserialized value from the property editor
+ if (p.PropertyEditor == null)
+ {
+ LogHelper.Warn("No property editor found for property " + p.Alias);
+ }
+ else
+ {
+ dboProperty.Value = p.PropertyEditor.ValueEditor.DeserializeValue(data, dboProperty.Value);
+ }
+ }
+ }
+
+ protected void HandleInvalidModelState(ContentItemDisplayBase display)
+ where TPersisted : IContentBase
+ where T : ContentPropertyBasic
+ {
+ //lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
+ if (!ModelState.IsValid)
+ {
+ display.Errors = ModelState.ToErrorDictionary();
+ throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, display));
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 1abb5c1ac7..fd44567e6f 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -298,6 +298,7 @@
+
From 4b8fed9f069b02ee427ed7855df4f59237ef887a Mon Sep 17 00:00:00 2001
From: Shannon
Date: Thu, 25 Jul 2013 15:31:26 +1000
Subject: [PATCH 84/85] Started converting content model mapping over to use
AutoMapper to fix the composite tabs/properties. New unit tests for the
mapping as well.
---
src/Umbraco.Core/CoreBootManager.cs | 2 +-
src/Umbraco.Core/Models/ContentExtensions.cs | 7 +-
.../Models/Mapping/IMapperConfiguration.cs | 2 +-
.../Models/Mapping/MapperConfiguration.cs | 2 +-
src/Umbraco.Core/Models/PropertyGroup.cs | 2 +
.../Mapping/ContentWebModelMappingTests.cs | 118 ++++++-
.../TestHelpers/BaseUmbracoApplicationTest.cs | 2 +-
src/Umbraco.Web/Editors/ContentController.cs | 11 +-
.../ContentEditing/ContentPropertyBasic.cs | 7 +
.../Models/Mapping/BaseContentModelMapper.cs | 28 +-
.../Models/Mapping/ContentModelMapper.cs | 85 +++--
.../Models/Mapping/ContentTypeModelMapper.cs | 22 --
.../Models/Mapping/MediaModelMapper.cs | 10 -
.../Models/Mapping/MediaTypeModelMapper.cs | 28 ++
.../Models/Mapping/NewContentMapper.cs | 314 ++++++++++++++++++
.../Models/Mapping/SectionModelMapper.cs | 3 +-
.../Models/Mapping/UserModelMapper.cs | 2 +-
src/Umbraco.Web/Umbraco.Web.csproj | 2 +
.../WebApi/Binders/ContentItemBinder.cs | 11 +-
.../WebApi/Binders/MediaItemBinder.cs | 3 +-
.../ApplicationRegistrar.cs | 2 +-
.../ApplicationTreeRegistrar.cs | 2 +-
22 files changed, 548 insertions(+), 117 deletions(-)
create mode 100644 src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs
create mode 100644 src/Umbraco.Web/Models/Mapping/NewContentMapper.cs
diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index 9f5fb22966..92195c9271 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -126,7 +126,7 @@ namespace Umbraco.Core
{
foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType())
{
- m.ConfigureMappings(configuration);
+ m.ConfigureMappings(configuration, ApplicationContext);
}
});
}
diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 860acdd852..1ca6ff4b7c 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -158,7 +158,9 @@ namespace Umbraco.Core.Models
public static IEnumerable GetNonGroupedProperties(this IContentBase content)
{
var propertyIdsInTabs = content.PropertyGroups.SelectMany(pg => pg.PropertyTypes).Select(pt => pt.Id);
- return content.Properties.Where(property => propertyIdsInTabs.Contains(property.PropertyTypeId) == false);
+ return content.Properties
+ .Where(property => propertyIdsInTabs.Contains(property.PropertyTypeId) == false)
+ .OrderBy(x => x.PropertyType.SortOrder);
}
///
@@ -173,7 +175,8 @@ namespace Umbraco.Core.Models
return content.Properties
.Where(property => propertyGroup.PropertyTypes
.Select(propertyType => propertyType.Id)
- .Contains(property.PropertyTypeId));
+ .Contains(property.PropertyTypeId))
+ .OrderBy(x => x.PropertyType.SortOrder);
}
///
diff --git a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
index 0ca76417de..8ea7c46d64 100644
--- a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
+++ b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
@@ -17,6 +17,6 @@ namespace Umbraco.Core.Models.Mapping
///
internal interface IMapperConfiguration : IApplicationEventHandler
{
- void ConfigureMappings(IConfiguration config);
+ void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext);
}
}
diff --git a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
index 8bde82d13f..fcc3410f46 100644
--- a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
+++ b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
@@ -10,6 +10,6 @@ namespace Umbraco.Core.Models.Mapping
///
internal abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration
{
- public abstract void ConfigureMappings(IConfiguration config);
+ public abstract void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs
index 044ecd164e..94ed57569a 100644
--- a/src/Umbraco.Core/Models/PropertyGroup.cs
+++ b/src/Umbraco.Core/Models/PropertyGroup.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Specialized;
+using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
@@ -12,6 +13,7 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract(IsReference = true)]
+ [DebuggerDisplay("Id: {Id}, Name: {Name}")]
public class PropertyGroup : Entity, IEquatable
{
private string _name;
diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs
index 5c3c25e28b..e9e8121860 100644
--- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs
+++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs
@@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using AutoMapper;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
+using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
namespace Umbraco.Tests.Models.Mapping
@@ -22,6 +24,11 @@ namespace Umbraco.Tests.Models.Mapping
}
+ protected override DatabaseBehavior DatabaseTestBehavior
+ {
+ get { return DatabaseBehavior.NewSchemaPerFixture; }
+ }
+
protected override void FreezeResolution()
{
PropertyEditorResolver.Current = new PropertyEditorResolver(
@@ -30,6 +37,113 @@ namespace Umbraco.Tests.Models.Mapping
base.FreezeResolution();
}
+ [Test]
+ public void To_Media_Item_Simple()
+ {
+ var contentType = MockedContentTypes.CreateImageMediaType();
+ var content = MockedMedia.CreateMediaImage(contentType, -1);
+
+ var result = Mapper.Map>(content);
+
+ AssertBasics(result, content);
+
+ foreach (var p in content.Properties)
+ {
+ AssertBasicProperty(result, p);
+ }
+ }
+
+ [Test]
+ public void To_Content_Item_Simple()
+ {
+ var contentType = MockedContentTypes.CreateSimpleContentType();
+ var content = MockedContent.CreateSimpleContent(contentType);
+
+ var result = Mapper.Map>(content);
+
+ AssertBasics(result, content);
+
+ foreach (var p in content.Properties)
+ {
+ AssertBasicProperty(result, p);
+ }
+ }
+
+ [Test]
+ public void To_Content_Item_Dto()
+ {
+ var contentType = MockedContentTypes.CreateSimpleContentType();
+ var content = MockedContent.CreateSimpleContent(contentType);
+
+ var result = Mapper.Map>(content);
+
+ AssertContentItem(result, content);
+ }
+
+ [Test]
+ public void To_Media_Item_Dto()
+ {
+ var contentType = MockedContentTypes.CreateImageMediaType();
+ var content = MockedMedia.CreateMediaImage(contentType, -1);
+
+ var result = Mapper.Map>(content);
+
+ AssertContentItem(result, content);
+ }
+
+ #region Assertions
+ private void AssertBasics(ContentItemBasic result, TPersisted content)
+ where T : ContentPropertyBasic
+ where TPersisted : IContentBase
+ {
+ Assert.AreEqual(content.Id, result.Id);
+ Assert.AreEqual(0, result.Owner.UserId);
+ Assert.AreEqual("admin", result.Owner.Name);
+ Assert.AreEqual(content.ParentId, result.ParentId);
+ Assert.AreEqual(content.UpdateDate, result.UpdateDate);
+ Assert.AreEqual(content.CreateDate, result.CreateDate);
+ Assert.AreEqual(content.Name, result.Name);
+ Assert.AreEqual(content.Properties.Count(), result.Properties.Count());
+ }
+
+ private void AssertBasicProperty(ContentItemBasic result, Property p)
+ where T : ContentPropertyBasic
+ where TPersisted : IContentBase
+ {
+ var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias);
+ Assert.IsNotNull(pDto);
+ Assert.AreEqual(p.Alias, pDto.Alias);
+ Assert.AreEqual(p.Id, pDto.Id);
+ Assert.AreEqual(p.Value, pDto.Value);
+ }
+
+ private void AssertProperty(ContentItemBasic result, Property p)
+ where TPersisted : IContentBase
+ {
+ AssertBasicProperty(result, p);
+
+ var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias);
+ Assert.IsNotNull(pDto);
+ Assert.AreEqual(p.PropertyType.Mandatory, pDto.IsRequired);
+ Assert.AreEqual(p.PropertyType.ValidationRegExp, pDto.ValidationRegExp);
+ Assert.AreEqual(p.PropertyType.Description, pDto.Description);
+ Assert.AreEqual(p.PropertyType.Name, pDto.Label);
+ Assert.AreEqual(ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(p.PropertyType.DataTypeDefinitionId), pDto.DataType);
+ Assert.AreEqual(PropertyEditorResolver.Current.GetById(p.PropertyType.DataTypeId), pDto.PropertyEditor);
+ }
+
+ private void AssertContentItem(ContentItemBasic result, T content)
+ where T : IContentBase
+ {
+ AssertBasics(result, content);
+
+ foreach (var p in content.Properties)
+ {
+ AssertProperty(result, p);
+ }
+ }
+ #endregion
+
[Test]
public void To_Display_Model()
{
@@ -38,7 +152,7 @@ namespace Umbraco.Tests.Models.Mapping
var mapper = new ContentModelMapper(ApplicationContext, new UserModelMapper());
- var result = mapper.ToContentItemDisplay(content);
+ var result = Mapper.Map(content);
Assert.AreEqual(content.Name, result.Name);
Assert.AreEqual(content.Id, result.Id);
@@ -77,7 +191,7 @@ namespace Umbraco.Tests.Models.Mapping
var mapper = new ContentModelMapper(ApplicationContext, new UserModelMapper());
- var result = mapper.ToContentItemDisplay(content);
+ var result = Mapper.Map(content);
Assert.AreEqual(content.Name, result.Name);
Assert.AreEqual(content.Id, result.Id);
diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
index 60fa93fd85..677a425fa9 100644
--- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
+++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
@@ -56,7 +56,7 @@ namespace Umbraco.Tests.TestHelpers
var mappers = PluginManager.Current.FindAndCreateInstances();
foreach (var mapper in mappers)
{
- mapper.ConfigureMappings(configuration);
+ mapper.ConfigureMappings(configuration, ApplicationContext);
}
});
}
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 372b0671ae..900fab5090 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -5,6 +5,7 @@ using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.ModelBinding;
+using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -52,7 +53,7 @@ namespace Umbraco.Web.Editors
{
var foundContent = ((ContentService) Services.ContentService).GetByIds(ids);
- return foundContent.Select(x => _contentModelMapper.ToContentItemDisplay(x));
+ return foundContent.Select(Mapper.Map);
}
///
@@ -67,7 +68,7 @@ namespace Umbraco.Web.Editors
{
HandleContentNotFound(id);
}
- return _contentModelMapper.ToContentItemDisplay(foundContent);
+ return Mapper.Map(foundContent);
}
///
@@ -85,7 +86,7 @@ namespace Umbraco.Web.Editors
}
var emptyContent = new Content("", parentId, contentType);
- return _contentModelMapper.ToContentItemDisplay(emptyContent);
+ return Mapper.Map(emptyContent);
}
///
@@ -124,7 +125,7 @@ namespace Umbraco.Web.Editors
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the modelstate to the outgoing object and throw a 403
- var forDisplay = _contentModelMapper.ToContentItemDisplay(contentItem.PersistedContent);
+ var forDisplay = Mapper.Map(contentItem.PersistedContent);
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden, forDisplay));
@@ -158,7 +159,7 @@ namespace Umbraco.Web.Editors
//return the updated model
- var display = _contentModelMapper.ToContentItemDisplay(contentItem.PersistedContent);
+ var display = Mapper.Map(contentItem.PersistedContent);
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
HandleInvalidModelState(display);
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs
index 237d75939c..ba5c98d89e 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
+using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.Models.ContentEditing
{
@@ -20,5 +21,11 @@ namespace Umbraco.Web.Models.ContentEditing
[Required(AllowEmptyStrings = false)]
public string Alias { get; set; }
+ ///
+ /// Used internally during model mapping
+ ///
+ [IgnoreDataMember]
+ internal PropertyEditor PropertyEditor { get; set; }
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs
index ed3a13e204..7b938642bb 100644
--- a/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs
@@ -20,20 +20,20 @@ namespace Umbraco.Web.Models.Mapping
UserMapper = userMapper;
}
- protected ContentItemDto ToContentItemDtoBase(IContentBase content)
- where TPersisted : IContentBase
- {
- return CreateContent, ContentPropertyDto, TPersisted>(content, null, (propertyDto, originalProperty, propEditor) =>
- {
- propertyDto.IsRequired = originalProperty.PropertyType.Mandatory;
- propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp;
- propertyDto.Alias = originalProperty.Alias;
- propertyDto.Description = originalProperty.PropertyType.Description;
- propertyDto.Label = originalProperty.PropertyType.Name;
- propertyDto.DataType = ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId);
- propertyDto.PropertyEditor = PropertyEditorResolver.Current.GetById(originalProperty.PropertyType.DataTypeId);
- });
- }
+ //protected ContentItemDto ToContentItemDtoBase(IContentBase content)
+ // where TPersisted : IContentBase
+ //{
+ // return CreateContent, ContentPropertyDto, TPersisted>(content, null, (propertyDto, originalProperty, propEditor) =>
+ // {
+ // propertyDto.IsRequired = originalProperty.PropertyType.Mandatory;
+ // propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp;
+ // propertyDto.Alias = originalProperty.Alias;
+ // propertyDto.Description = originalProperty.PropertyType.Description;
+ // propertyDto.Label = originalProperty.PropertyType.Name;
+ // propertyDto.DataType = ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId);
+ // propertyDto.PropertyEditor = PropertyEditorResolver.Current.GetById(originalProperty.PropertyType.DataTypeId);
+ // });
+ //}
protected ContentItemBasic ToContentItemSimpleBase(IContentBase content)
where TPersisted : IContentBase
diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
index eb3cda0ed2..efd7dd2c22 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
@@ -15,59 +15,50 @@ namespace Umbraco.Web.Models.Mapping
{
}
- public ContentItemDto ToContentItemDto(IContent content)
- {
- var result = base.ToContentItemDtoBase(content);
- //NOTE: we don't need this for the dto and it's an extra lookup
- //result.ContentTypeAlias = content.ContentType.Alias;
- //result.Icon = content.ContentType.Icon;
- //result.Updator = userMapper.ToUserBasic(content.GetWriterProfile());
- return result;
- }
- public ContentItemBasic ToContentItemSimple(IContent content)
- {
- var result = base.ToContentItemSimpleBase(content);
- result.ContentTypeAlias = content.ContentType.Alias;
- result.Icon = content.ContentType.Icon;
- result.Updator = UserMapper.ToUserBasic(content.GetWriterProfile());
- return result;
- }
+ //public ContentItemBasic ToContentItemSimple(IContent content)
+ //{
+ // var result = base.ToContentItemSimpleBase(content);
+ // result.ContentTypeAlias = content.ContentType.Alias;
+ // result.Icon = content.ContentType.Icon;
+ // result.Updator = UserMapper.ToUserBasic(content.GetWriterProfile());
+ // return result;
+ //}
- public ContentItemDisplay ToContentItemDisplay(IContent content)
- {
- //create the list of tabs for properties assigned to tabs.
- var tabs = GetTabs(content);
+ //public ContentItemDisplay ToContentItemDisplay(IContent content)
+ //{
+ // //create the list of tabs for properties assigned to tabs.
+ // var tabs = GetTabs(content);
- var result = CreateContent(content, (display, originalContent) =>
- {
- //fill in the rest
- display.Updator = UserMapper.ToUserBasic(content.GetWriterProfile());
- display.ContentTypeAlias = content.ContentType.Alias;
- display.Icon = content.ContentType.Icon;
+ // var result = CreateContent(content, (display, originalContent) =>
+ // {
+ // //fill in the rest
+ // display.Updator = UserMapper.ToUserBasic(content.GetWriterProfile());
+ // display.ContentTypeAlias = content.ContentType.Alias;
+ // display.Icon = content.ContentType.Icon;
- //set display props after the normal properties are alraedy mapped
- display.Name = originalContent.Name;
- display.Tabs = tabs;
- //look up the published version of this item if it is not published
- if (content.Published)
- {
- display.PublishDate = content.UpdateDate;
- }
- else if (content.HasPublishedVersion())
- {
- var published = ApplicationContext.Services.ContentService.GetPublishedVersion(content.Id);
- display.PublishDate = published.UpdateDate;
- }
- else
- {
- display.PublishDate = null;
- }
+ // //set display props after the normal properties are alraedy mapped
+ // display.Name = originalContent.Name;
+ // display.Tabs = tabs;
+ // //look up the published version of this item if it is not published
+ // if (content.Published)
+ // {
+ // display.PublishDate = content.UpdateDate;
+ // }
+ // else if (content.HasPublishedVersion())
+ // {
+ // var published = ApplicationContext.Services.ContentService.GetPublishedVersion(content.Id);
+ // display.PublishDate = published.UpdateDate;
+ // }
+ // else
+ // {
+ // display.PublishDate = null;
+ // }
- }, null, false);
+ // }, null, false);
- return result;
- }
+ // return result;
+ //}
}
}
diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs
index 30fcb50e32..168217b473 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs
@@ -25,26 +25,4 @@ namespace Umbraco.Web.Models.Mapping
};
}
}
-
- internal class MediaTypeModelMapper
- {
- private readonly ApplicationContext _applicationContext;
-
- public MediaTypeModelMapper(ApplicationContext applicationContext)
- {
- _applicationContext = applicationContext;
- }
-
- public ContentTypeBasic ToMediaTypeBasic(IMediaType contentType)
- {
- return new ContentTypeBasic
- {
- Alias = contentType.Alias,
- Id = contentType.Id,
- Description = contentType.Description,
- Icon = contentType.Icon,
- Name = contentType.Name
- };
- }
- }
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
index 7b079664fd..55926afa6d 100644
--- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
@@ -17,16 +17,6 @@ namespace Umbraco.Web.Models.Mapping
{
}
- public ContentItemDto ToMediaItemDto(IMedia content)
- {
- var result = base.ToContentItemDtoBase(content);
- //NOTE: we don't need this for the dto and it's an extra lookup
- //result.ContentTypeAlias = content.ContentType.Alias;
- //result.Icon = content.ContentType.Icon;
- //result.Updator = userMapper.ToUserBasic(content.GetWriterProfile());
- return result;
- }
-
public ContentItemBasic ToMediaItemSimple(IMedia content)
{
var result = base.ToContentItemSimpleBase(content);
diff --git a/src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs
new file mode 100644
index 0000000000..c54c700adf
--- /dev/null
+++ b/src/Umbraco.Web/Models/Mapping/MediaTypeModelMapper.cs
@@ -0,0 +1,28 @@
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Web.Models.ContentEditing;
+
+namespace Umbraco.Web.Models.Mapping
+{
+ internal class MediaTypeModelMapper
+ {
+ private readonly ApplicationContext _applicationContext;
+
+ public MediaTypeModelMapper(ApplicationContext applicationContext)
+ {
+ _applicationContext = applicationContext;
+ }
+
+ public ContentTypeBasic ToMediaTypeBasic(IMediaType contentType)
+ {
+ return new ContentTypeBasic
+ {
+ Alias = contentType.Alias,
+ Id = contentType.Id,
+ Description = contentType.Description,
+ Icon = contentType.Icon,
+ Name = contentType.Name
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/Mapping/NewContentMapper.cs b/src/Umbraco.Web/Models/Mapping/NewContentMapper.cs
new file mode 100644
index 0000000000..443da7b913
--- /dev/null
+++ b/src/Umbraco.Web/Models/Mapping/NewContentMapper.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using AutoMapper;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Mapping;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Web.Models.ContentEditing;
+using System.Linq;
+
+namespace Umbraco.Web.Models.Mapping
+{
+ internal class NewContentMapper : MapperConfiguration
+ {
+ public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
+ {
+ //FROM Property TO ContentPropertyBasic
+ config.CreateMap>()
+ .ForMember(tab => tab.Label, expression => expression.MapFrom(@group => @group.Name))
+ .ForMember(tab => tab.IsActive, expression => expression.UseValue(true))
+ .ForMember(tab => tab.Properties, expression => expression.Ignore());
+
+ //FROM Property TO ContentPropertyBasic
+ config.CreateMap()
+ .ConvertUsing>();
+
+ //FROM Property TO ContentPropertyDto
+ config.CreateMap()
+ .ConvertUsing(new ContentPropertyDtoConverter(applicationContext));
+
+ //FROM Property TO ContentPropertyDisplay
+ config.CreateMap()
+ .ConvertUsing(new ContentPropertyDisplayConverter(applicationContext));
+
+ //FROM IContent TO ContentItemDisplay
+ config.CreateMap()
+ .ForMember(
+ dto => dto.Owner,
+ expression => expression.ResolveUsing>())
+ .ForMember(
+ dto => dto.Updator,
+ expression => expression.ResolveUsing())
+ .ForMember(
+ dto => dto.Icon,
+ expression => expression.MapFrom(content => content.ContentType.Icon))
+ .ForMember(
+ dto => dto.ContentTypeAlias,
+ expression => expression.MapFrom(content => content.ContentType.Alias))
+ .ForMember(
+ dto => dto.PublishDate,
+ expression => expression.MapFrom(content => GetPublishedDate(content, applicationContext)))
+ .ForMember(display => display.Properties, expression => expression.Ignore())
+ .ForMember(display => display.Tabs, expression => expression.Ignore())
+ .AfterMap((content, display) => MapTabsAndProperties(content, display));
+
+ //FROM IContent TO ContentItemBasic
+ config.CreateMap>()
+ .ForMember(
+ dto => dto.Owner,
+ expression => expression.ResolveUsing>())
+ .ForMember(
+ dto => dto.Updator,
+ expression => expression.ResolveUsing())
+ .ForMember(
+ dto => dto.Icon,
+ expression => expression.MapFrom(content => content.ContentType.Icon))
+ .ForMember(
+ dto => dto.ContentTypeAlias,
+ expression => expression.MapFrom(content => content.ContentType.Alias));
+
+ //FROM IMedia TO ContentItemBasic
+ config.CreateMap>()
+ .ForMember(
+ dto => dto.Owner,
+ expression => expression.ResolveUsing>())
+ .ForMember(
+ dto => dto.Icon,
+ expression => expression.MapFrom(content => content.ContentType.Icon))
+ .ForMember(
+ dto => dto.ContentTypeAlias,
+ expression => expression.MapFrom(content => content.ContentType.Alias));
+
+ //FROM IContent TO ContentItemDto
+ config.CreateMap>()
+ .ForMember(
+ dto => dto.Owner,
+ expression => expression.ResolveUsing>());
+
+ //FROM IMedia TO ContentItemDto
+ config.CreateMap>()
+ .ForMember(
+ dto => dto.Owner,
+ expression => expression.ResolveUsing>());
+ }
+
+ ///
+ /// Gets the published date value for the IContent object
+ ///
+ ///
+ ///
+ ///
+ private static DateTime? GetPublishedDate(IContent content, ApplicationContext applicationContext)
+ {
+ if (content.Published)
+ {
+ return content.UpdateDate;
+ }
+ if (content.HasPublishedVersion())
+ {
+ var published = applicationContext.Services.ContentService.GetPublishedVersion(content.Id);
+ return published.UpdateDate;
+ }
+ return null;
+ }
+
+ private static void MapTabsAndProperties(IContentBase content, TabbedContentItem display)
+ {
+ var aggregateTabs = new List>();
+
+ //now we need to aggregate the tabs and properties since we might have duplicate tabs (based on aliases) because
+ // of how content composition works.
+ foreach (var propertyGroups in content.PropertyGroups.GroupBy(x => x.Name))
+ {
+ var aggregateProperties = new List();
+
+ //there will always be one group with a null parent id (the top-most)
+ //then we'll iterate over all of the groups and ensure the properties are
+ //added in order so that when they render they are rendered with highest leve
+ //parent properties first.
+ int? currentParentId = null;
+ for (var i = 0; i < propertyGroups.Count(); i++)
+ {
+ var current = propertyGroups.Single(x => x.ParentId == currentParentId);
+ aggregateProperties.AddRange(
+ Mapper.Map, IEnumerable>(
+ content.GetPropertiesForGroup(current)));
+ currentParentId = current.Id;
+ }
+
+ //then we'll just use the root group's data to make the composite tab
+ var rootGroup = propertyGroups.Single(x => x.ParentId == null);
+ aggregateTabs.Add(new Tab
+ {
+ Id = rootGroup.Id,
+ Alias = rootGroup.Name,
+ Label = rootGroup.Name,
+ Properties = aggregateProperties,
+ IsActive = false
+ });
+ }
+
+ //now add the generic properties tab for any properties that don't belong to a tab
+ var orphanProperties = content.GetNonGroupedProperties();
+
+ //now add the generic properties tab
+ aggregateTabs.Add(new Tab
+ {
+ Id = 0,
+ Label = "Generic properties",
+ Alias = "Generic properties",
+ Properties = Mapper.Map, IEnumerable>(orphanProperties)
+ });
+
+ //set the first tab to active
+ aggregateTabs.First().IsActive = true;
+
+ display.Tabs = aggregateTabs;
+ }
+
+ }
+
+
+
+ internal class ContentDisplayConverter : TypeConverter
+ {
+ protected override ContentItemDisplay ConvertCore(IContent source)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ///