diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index d18e6b46a9..8ec7cf5e5e 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -257,5 +257,19 @@ namespace Umbraco.Core.Models ChangePublishedState(false); } } + + /// + /// Creates a clone of the current entity + /// + /// + public IContent Clone() + { + var clone = (Content)this.MemberwiseClone(); + clone.Key = Guid.Empty; + clone.Version = Guid.NewGuid(); + clone.ResetIdentity(); + + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index e806aa956f..94cc87d121 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -45,7 +45,7 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo UserIdSelector = ExpressionHelper.GetPropertyInfo(x => x.UserId); private static readonly PropertyInfo TrashedSelector = ExpressionHelper.GetPropertyInfo(x => x.Trashed); private static readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes); - private readonly static PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); + private static readonly PropertyInfo PropertyGroupCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); protected void PropertyGroupsChanged(object sender, NotifyCollectionChangedEventArgs e) { @@ -57,7 +57,7 @@ namespace Umbraco.Core.Models /// /// Might not be necessary if handled as a relation? [DataMember] - public int ParentId + public virtual int ParentId { get { return _parentId; } set @@ -71,7 +71,7 @@ namespace Umbraco.Core.Models /// Gets or sets the name of the current entity /// [DataMember] - public string Name + public virtual string Name { get { return _name; } set @@ -85,7 +85,7 @@ namespace Umbraco.Core.Models /// Gets or sets the level of the content entity /// [DataMember] - public int Level //NOTE Is this relevant for a ContentType? + public virtual int Level //NOTE Is this relevant for a ContentType? { get { return _level; } set @@ -99,7 +99,7 @@ namespace Umbraco.Core.Models /// Gets of sets the path /// [DataMember] - public string Path //NOTE Is this relevant for a ContentType? + public virtual string Path //NOTE Is this relevant for a ContentType? { get { return _path; } set @@ -113,7 +113,7 @@ namespace Umbraco.Core.Models /// The Alias of the ContentType /// [DataMember] - public string Alias + public virtual string Alias { get { return _alias; } set @@ -127,7 +127,7 @@ namespace Umbraco.Core.Models /// Description for the ContentType /// [DataMember] - public string Description + public virtual string Description { get { return _description; } set @@ -141,7 +141,7 @@ namespace Umbraco.Core.Models /// Gets or sets the sort order of the content entity /// [DataMember] - public int SortOrder + public virtual int SortOrder { get { return _sortOrder; } set @@ -155,7 +155,7 @@ namespace Umbraco.Core.Models /// Name of the icon (sprite class) used to identify the ContentType /// [DataMember] - public string Icon + public virtual string Icon { get { return _icon; } set @@ -169,7 +169,7 @@ namespace Umbraco.Core.Models /// Name of the thumbnail used to identify the ContentType /// [DataMember] - public string Thumbnail + public virtual string Thumbnail { get { return _thumbnail; } set @@ -183,7 +183,7 @@ namespace Umbraco.Core.Models /// Id of the user who created this Content /// [DataMember] - public int UserId + public virtual int UserId { get { return _userId; } set @@ -198,7 +198,7 @@ namespace Umbraco.Core.Models /// If ContentType is Trashed it will be located in the Recyclebin. /// [DataMember] - public bool Trashed //NOTE Is this relevant for a ContentType? + public virtual bool Trashed //NOTE Is this relevant for a ContentType? { get { return _trashed; } set @@ -212,7 +212,7 @@ namespace Umbraco.Core.Models /// Gets or sets a list of integer Ids for allowed ContentTypes /// [DataMember] - public IEnumerable AllowedContentTypes + public virtual IEnumerable AllowedContentTypes { get { return _allowedContentTypes; } set @@ -227,7 +227,7 @@ namespace Umbraco.Core.Models /// /// A PropertyGroup corresponds to a Tab in the UI [DataMember] - public PropertyGroupCollection PropertyGroups + public virtual PropertyGroupCollection PropertyGroups { get { return _propertyGroups; } set @@ -242,7 +242,7 @@ namespace Umbraco.Core.Models /// This list aggregates PropertyTypes across the PropertyGroups. /// [IgnoreDataMember] - public IEnumerable PropertyTypes + public virtual IEnumerable PropertyTypes { get { return PropertyGroups.SelectMany(x => x.PropertyTypes); } } diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs index b09669c2c6..e44bee5dba 100644 --- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs @@ -5,6 +5,9 @@ using System.Runtime.Serialization; namespace Umbraco.Core.Models { + /// + /// Represents an abstract class for composition specific ContentType properties and methods + /// public abstract class ContentTypeCompositionBase : ContentTypeBase, IContentTypeComposition { private List _contentTypeComposition; @@ -14,7 +17,7 @@ namespace Umbraco.Core.Models _contentTypeComposition = new List(); } - private static readonly PropertyInfo ContentTypeCompositionSelector = ExpressionHelper.GetPropertyInfo>(x => x.ContentTypeComposition); + private static readonly PropertyInfo ContentTypeCompositionSelector = ExpressionHelper.GetPropertyInfo>(x => x.ContentTypeComposition); /// /// List of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs index cca3bc501e..109cf08779 100644 --- a/src/Umbraco.Core/Models/EntityBase/Entity.cs +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -86,6 +86,12 @@ namespace Umbraco.Core.Models.EntityBase } } + protected void ResetIdentity() + { + _hasIdentity = false; + _id = 0; + } + /// /// Method to call on entity saved when first added /// diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs new file mode 100644 index 0000000000..29069dad97 --- /dev/null +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -0,0 +1,522 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers.Entities; + +namespace Umbraco.Tests.Models +{ + [TestFixture] + public class ContentTests + { + [Test] + public void Can_Verify_Mocked_Content() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + + // Assert + Assert.That(content, Is.Not.Null); + } + + [Test] + public void Can_Change_Property_Value() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.Properties["title"].Value = "This is the new title"; + + // Assert + Assert.That(content.Properties.Any(), Is.True); + Assert.That(content.Properties["title"], Is.Not.Null); + Assert.That(content.Properties["title"].Value, Is.EqualTo("This is the new title")); + } + + [Test] + public void Can_Clone_Content() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + content.Id = 10; + content.Key = new Guid("29181B97-CB8F-403F-86DE-5FEB497F4800"); + + // Act + var clone = content.Clone(); + + // Assert + Assert.AreNotSame(clone, content); + Assert.AreNotSame(clone.Id, content.Id); + Assert.AreNotSame(clone.Version, content.Version); + Assert.That(clone.HasIdentity, Is.False); + } + + /*[Test] + public void Cannot_Change_Property_With_Invalid_Value() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + var model = new TestEditorModel + { + TestDateTime = DateTime.UtcNow, + TestDouble = 1.2, + TestInt = 2, + TestReadOnly = "Read-only string", + TestString = "This is a test string" + }; + + // Assert + Assert.Throws(() => content.Properties["title"].Value = model); + }*/ + + [Test] + public void Can_Change_Property_Value_Through_Anonymous_Object() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.PropertyValues(new {title = "This is the new title"}); + + // Assert + Assert.That(content.Properties.Any(), Is.True); + Assert.That(content.Properties["title"], Is.Not.Null); + Assert.That(content.Properties["title"].Alias, Is.EqualTo("title")); + Assert.That(content.Properties["title"].Value, Is.EqualTo("This is the new title")); + Assert.That(content.Properties["metaDescription"].Value, Is.EqualTo("The Lorem Ipsum company")); + } + + [Test] + public void Can_Verify_Dirty_Property_On_Content() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ResetDirtyProperties(); + content.Name = "New Home"; + + // Assert + Assert.That(content.Name, Is.EqualTo("New Home")); + Assert.That(content.IsPropertyDirty("Name"), Is.True); + } + + [Test] + public void Can_Add_PropertyGroup_On_ContentType() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + contentType.PropertyGroups.Add(new PropertyGroup{ Name = "Test Group", SortOrder = 3 }); + + // Assert + Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(3)); + Assert.That(content.PropertyGroups.Count(), Is.EqualTo(3)); + } + + [Test] + public void Can_Remove_PropertyGroup_From_ContentType() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + contentType.ResetDirtyProperties(); + + // Act + contentType.PropertyGroups.Remove("Content"); + + // Assert + Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(1)); + Assert.That(contentType.IsPropertyDirty("PropertyGroups"), Is.True); + } + + [Test] + public void Can_Add_PropertyType_To_Group_On_ContentType() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + contentType.PropertyGroups["Content"].PropertyTypes.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "subtitle", + Name = "Subtitle", + Description = "Optional subtitle", + HelpText = "", + Mandatory = false, + SortOrder = 3, + DataTypeId = -88 + }); + + // Assert + Assert.That(contentType.PropertyGroups["Content"].PropertyTypes.Count, Is.EqualTo(3)); + Assert.That(content.PropertyGroups.First(x => x.Name == "Content").PropertyTypes.Count, Is.EqualTo(3)); + } + + [Test] + public void Can_Add_New_Property_To_New_PropertyType() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "subtitle", Name = "Subtitle", Description = "Optional subtitle", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeId = -88 + }; + contentType.PropertyGroups["Content"].PropertyTypes.Add(propertyType); + content.Properties.Add(new Property(propertyType){Value = "This is a subtitle Test"}); + + // Assert + Assert.That(content.Properties.Contains("subtitle"), Is.True); + Assert.That(content.Properties["subtitle"].Value, Is.EqualTo("This is a subtitle Test")); + } + + [Test] + public void Can_Add_New_Property_To_New_PropertyType_In_New_PropertyGroup() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "subtitle", + Name = "Subtitle", + Description = "Optional subtitle", + HelpText = "", + Mandatory = false, + SortOrder = 3, + DataTypeId = -88 + }; + var propertyGroup = new PropertyGroup {Name = "Test Group", SortOrder = 3}; + propertyGroup.PropertyTypes.Add(propertyType); + contentType.PropertyGroups.Add(propertyGroup); + content.Properties.Add(new Property(propertyType){ Value = "Subtitle Test"}); + + // Assert + Assert.That(content.Properties.Count, Is.EqualTo(5)); + Assert.That(content.PropertyTypes.Count(), Is.EqualTo(5)); + Assert.That(content.PropertyGroups.Count(), Is.EqualTo(3)); + Assert.That(content.Properties["subtitle"].Value, Is.EqualTo("Subtitle Test")); + Assert.That(content.Properties["title"].Value, Is.EqualTo("Welcome to our Home page")); + } + + [Test] + public void Can_Update_PropertyType_Through_Content_Properties() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act - note that the PropertyType's properties like SortOrder is not updated through the Content object + var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "title", Name = "Title", Description = "Title description added", HelpText = "", Mandatory = false, SortOrder = 10, DataTypeId = -88 + }; + content.Properties.Add(new Property(propertyType)); + + // Assert + Assert.That(content.Properties.Count, Is.EqualTo(4)); + Assert.That(contentType.PropertyTypes.First(x => x.Alias == "title").SortOrder, Is.EqualTo(1)); + Assert.That(content.Properties["title"].Value, Is.EqualTo("Welcome to our Home page")); + } + + [Test] + public void Can_Change_ContentType_On_Content() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var simpleContentType = MockedContentTypes.CreateSimpleContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ChangeContentType(simpleContentType); + + // Assert + Assert.That(content.Properties.Contains("author"), Is.True); + Assert.That(content.PropertyGroups.Count(), Is.EqualTo(1)); + Assert.That(content.PropertyTypes.Count(), Is.EqualTo(3)); + //Note: There was 4 properties, after changing ContentType 1 has been added (no properties are deleted) + Assert.That(content.Properties.Count, Is.EqualTo(5)); + } + + [Test] + public void Can_Change_ContentType_On_Content_And_Set_Property_Value() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var simpleContentType = MockedContentTypes.CreateSimpleContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ChangeContentType(simpleContentType); + content.SetValue("author", "John Doe"); + + // Assert + Assert.That(content.Properties.Contains("author"), Is.True); + Assert.That(content.Properties["author"].Value, Is.EqualTo("John Doe")); + } + + [Test] + public void Can_Change_ContentType_On_Content_And_Still_Get_Old_Properties() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var simpleContentType = MockedContentTypes.CreateSimpleContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ChangeContentType(simpleContentType); + + // Assert + Assert.That(content.Properties.Contains("author"), Is.True); + Assert.That(content.Properties.Contains("keywords"), Is.True); + Assert.That(content.Properties.Contains("metaDescription"), Is.True); + Assert.That(content.Properties["keywords"].Value, Is.EqualTo("text,home,page")); + Assert.That(content.Properties["metaDescription"].Value, Is.EqualTo("The Lorem Ipsum company")); + } + + [Test] + public void Can_Change_ContentType_On_Content_And_Clear_Old_PropertyTypes() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var simpleContentType = MockedContentTypes.CreateSimpleContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ChangeContentType(simpleContentType, true); + + // Assert + Assert.That(content.Properties.Contains("author"), Is.True); + Assert.That(content.Properties.Contains("keywords"), Is.False); + Assert.That(content.Properties.Contains("metaDescription"), Is.False); + } + + [Test] + public void Can_Verify_Content_Is_Published() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ResetDirtyProperties(); + content.ChangePublishedState(true); + + // Assert + Assert.That(content.IsPropertyDirty("Published"), Is.True); + Assert.That(content.Published, Is.True); + Assert.That(content.IsPropertyDirty("Name"), Is.False); + } + + [Test] + public void Can_Verify_Content_Is_Trashed() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType); + + // Act + content.ResetDirtyProperties(); + content.ChangeTrashedState(true); + + // Assert + Assert.That(content.IsPropertyDirty("Trashed"), Is.True); + Assert.That(content.Trashed, Is.True); + Assert.That(content.IsPropertyDirty("Name"), Is.False); + } + + [Test] + public void Adding_PropertyGroup_To_ContentType_Results_In_Dirty_Entity() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + contentType.ResetDirtyProperties(); + + // Act + var propertyGroup = new PropertyGroup { Name = "Test Group", SortOrder = 3 }; + contentType.PropertyGroups.Add(propertyGroup); + + // Assert + Assert.That(contentType.IsDirty(), Is.True); + Assert.That(contentType.IsPropertyDirty("PropertyGroups"), Is.True); + } + + [Test] + public void Adding_PropertyType_To_PropertyGroup_On_ContentType_Results_In_Dirty_Entity() + { + // Arrange + var contentType = MockedContentTypes.CreateTextpageContentType(); + contentType.ResetDirtyProperties(); + + // Act + var propertyType = new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "subtitle", + Name = "Subtitle", + Description = "Optional subtitle", + HelpText = "", + Mandatory = false, + SortOrder = 3, + DataTypeId = -88 + }; + contentType.PropertyGroups["Content"].PropertyTypes.Add(propertyType); + + // Assert + Assert.That(contentType.PropertyGroups["Content"].IsDirty(), Is.True); + Assert.That(contentType.PropertyGroups["Content"].IsPropertyDirty("PropertyTypes"), Is.True); + Assert.That(contentType.PropertyGroups.Any(x => x.IsDirty()), Is.True); + } + + [Test] + public void Can_Compose_Composite_ContentType_Collection() + { + // Arrange + var simpleContentType = MockedContentTypes.CreateSimpleContentType(); + var simple2ContentType = MockedContentTypes.CreateSimpleContentType("anotherSimple", "Another Simple Page", + new PropertyTypeCollection( + new List + { + new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "coauthor", + Name = "Co-Author", + Description = "Name of the Co-Author", + HelpText = "", + Mandatory = false, + SortOrder = 4, + DataTypeId = -88 + } + })); + + // Act + var added = simpleContentType.AddContentType(simple2ContentType); + var compositionPropertyGroups = simpleContentType.CompositionPropertyGroups; + var compositionPropertyTypes = simpleContentType.CompositionPropertyTypes; + + // Assert + Assert.That(added, Is.True); + Assert.That(compositionPropertyGroups.Count(), Is.EqualTo(1)); + Assert.That(compositionPropertyTypes.Count(), Is.EqualTo(4)); + } + + [Test] + public void Can_Compose_Nested_Composite_ContentType_Collection() + { + // Arrange + var metaContentType = MockedContentTypes.CreateMetaContentType(); + var simpleContentType = MockedContentTypes.CreateSimpleContentType(); + var simple2ContentType = MockedContentTypes.CreateSimpleContentType("anotherSimple", "Another Simple Page", + new PropertyTypeCollection( + new List + { + new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "coauthor", + Name = "Co-Author", + Description = "Name of the Co-Author", + HelpText = "", + Mandatory = false, + SortOrder = 4, + DataTypeId = -88 + } + })); + + // Act + var addedMeta = simple2ContentType.AddContentType(metaContentType); + var added = simpleContentType.AddContentType(simple2ContentType); + var compositionPropertyGroups = simpleContentType.CompositionPropertyGroups; + var compositionPropertyTypes = simpleContentType.CompositionPropertyTypes; + + // Assert + Assert.That(addedMeta, Is.True); + Assert.That(added, Is.True); + Assert.That(compositionPropertyGroups.Count(), Is.EqualTo(2)); + Assert.That(compositionPropertyTypes.Count(), Is.EqualTo(6)); + Assert.That(simpleContentType.ContentTypeCompositionExists("meta"), Is.True); + } + + [Test] + public void Can_Avoid_Circular_Dependencies_In_Composition() + { + var textPage = MockedContentTypes.CreateTextpageContentType(); + var parent = MockedContentTypes.CreateSimpleContentType("parent", "Parent"); + var meta = MockedContentTypes.CreateMetaContentType(); + var mixin1 = MockedContentTypes.CreateSimpleContentType("mixin1", "Mixin1", new PropertyTypeCollection( + new List + { + new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "coauthor", + Name = "Co-Author", + Description = "Name of the Co-Author", + HelpText = "", + Mandatory = false, + SortOrder = 4, + DataTypeId = -88 + } + })); + var mixin2 = MockedContentTypes.CreateSimpleContentType("mixin2", "Mixin2", new PropertyTypeCollection( + new List + { + new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) + { + Alias = "author", + Name = "Author", + Description = "Name of the Author", + HelpText = "", + Mandatory = false, + SortOrder = 4, + DataTypeId = -88 + } + })); + + // Act + var addedMetaMixin2 = mixin2.AddContentType(meta); + var addedMixin2 = mixin1.AddContentType(mixin2); + var addedMeta = parent.AddContentType(meta); + var addedMixin1 = parent.AddContentType(mixin1); + var addedMixin1Textpage = textPage.AddContentType(mixin1); + var addedTextpageParent = parent.AddContentType(textPage); + + var aliases = textPage.CompositionAliases(); + var propertyTypes = textPage.CompositionPropertyTypes; + var propertyGroups = textPage.CompositionPropertyGroups; + + // Assert + Assert.That(mixin2.ContentTypeCompositionExists("meta"), Is.True); + Assert.That(mixin1.ContentTypeCompositionExists("meta"), Is.True); + Assert.That(parent.ContentTypeCompositionExists("meta"), Is.True); + Assert.That(textPage.ContentTypeCompositionExists("meta"), Is.True); + + Assert.That(aliases.Count(), Is.EqualTo(3)); + Assert.That(propertyTypes.Count(), Is.EqualTo(8)); + Assert.That(propertyGroups.Count(), Is.EqualTo(2)); + + Assert.That(addedMeta, Is.True); + Assert.That(addedMetaMixin2, Is.True); + Assert.That(addedMixin2, Is.True); + Assert.That(addedMixin1, Is.False); + Assert.That(addedMixin1Textpage, Is.True); + Assert.That(addedTextpageParent, Is.False); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs new file mode 100644 index 0000000000..5c0cce4697 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -0,0 +1,24 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedContent + { + public static Content CreateTextpageContent(ContentType contentType) + { + var content = new Content(-1, contentType) {Name = "Home", Language = "en-US", Level = 1, ParentId = -1, SortOrder = 1, Template = "~/masterpages/umbTextPage.master", UserId = 0}; + object obj = + new + { + title = "Welcome to our Home page", + bodyText = "This is the welcome message on the first page", + keywords = "text,home,page", + metaDescription = "The Lorem Ipsum company" + }; + + content.PropertyValues(obj); + + return content; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs new file mode 100644 index 0000000000..e36deae013 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -0,0 +1,131 @@ +using System; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedContentTypes + { + public static ContentType CreateTextpageContentType() + { + var contentType = new ContentType(-1) + { + Alias = "textPage", + Name = "Text Page", + Description = "ContentType used for Text pages", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + UserId = 0, + DefaultTemplate = "~/masterpages/umbTextPage.master", + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); + + var metaCollection = new PropertyTypeCollection(); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "keywords", Name = "Meta Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metaDescription", Name = "Meta Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -89 }); + + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Meta", SortOrder = 2 }); + + return contentType; + } + + public static ContentType CreateMetaContentType() + { + var contentType = new ContentType(-1) + { + Alias = "meta", + Name = "Meta", + Description = "ContentType used for Meta tags", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + UserId = 0, + DefaultTemplate = "", + Trashed = false + }; + + var metaCollection = new PropertyTypeCollection(); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metakeywords", Name = "Meta Keywords", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); + metaCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "metadescription", Name = "Meta Description", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -89 }); + + contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Meta", SortOrder = 2 }); + + return contentType; + } + + public static ContentType CreateSimpleContentType() + { + var contentType = new ContentType(-1) + { + Alias = "simple", + Name = "Simple Page", + Description = "ContentType used for simple text pages", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + UserId = 0, + DefaultTemplate = "~/masterpages/umbSimplePage.master", + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); + + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + + return contentType; + } + + public static ContentType CreateSimpleContentType(string alias, string name) + { + var contentType = new ContentType(-1) + { + Alias = alias, + Name = name, + Description = "ContentType used for simple text pages", + Icon = ".sprTreeDoc3", + Thumbnail = "doc2.png", + SortOrder = 1, + UserId = 0, + DefaultTemplate = "~/masterpages/umbSimplePage.master", + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "title", Name = "Title", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", HelpText = "", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); + + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + + return contentType; + } + + public static ContentType CreateSimpleContentType(string alias, string name, PropertyTypeCollection collection) + { + var contentType = new ContentType(-1) + { + Alias = alias, + Name = name, + Description = "ContentType used for simple text pages", + Icon = ".sprTreeDoc3", + Thumbnail = "doc3.png", + SortOrder = 1, + UserId = 0, + DefaultTemplate = "~/masterpages/umbSimplePage.master", + Trashed = false + }; + + contentType.PropertyGroups.Add(new PropertyGroup(collection) { Name = "Content", SortOrder = 1 }); + + return contentType; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs new file mode 100644 index 0000000000..cbe1509327 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.Serialization; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + [Serializable] + [DataContract(IsReference = true)] + public class MockedEntity : Entity + { + [DataMember] + public string Alias { get; set; } + + [DataMember] + public string Name { get; set; } + + [DataMember] + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index befc241252..0b8cc4e532 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -46,6 +46,7 @@ + @@ -60,6 +61,7 @@ + @@ -91,6 +93,9 @@ + + + diff --git a/src/Umbraco.Web/Publishing/IPublishingStrategy.cs b/src/Umbraco.Web/Publishing/IPublishingStrategy.cs new file mode 100644 index 0000000000..0d61b8602b --- /dev/null +++ b/src/Umbraco.Web/Publishing/IPublishingStrategy.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Publishing +{ + public interface IPublishingStrategy + { + bool Publish(IContent content, int userId); + bool PublishWithChildren(IEnumerable children, int userId); + void PublishWithSubs(IContent content, int userId); + bool UnPublish(IContent content, int userId); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Publishing/PublishingStrategy.cs b/src/Umbraco.Web/Publishing/PublishingStrategy.cs index 17acd13d2b..0f0cb80a7c 100644 --- a/src/Umbraco.Web/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Web/Publishing/PublishingStrategy.cs @@ -1,5 +1,7 @@ -using umbraco.BusinessLogic; -using umbraco.cms.businesslogic; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using umbraco.BusinessLogic; using umbraco.cms.businesslogic.web; namespace Umbraco.Web.Publishing @@ -7,13 +9,13 @@ namespace Umbraco.Web.Publishing /// /// Currently acts as an interconnection between the new public api and the legacy api for publishing /// - internal class PublishingStrategy + internal class PublishingStrategy : IPublishingStrategy { internal PublishingStrategy() { } - - internal bool Publish(int userId, int contentId) + + public bool Publish(IContent content, int userId) { //Fire BeforePublish event /*PublishEventArgs e = new PublishEventArgs(); @@ -34,24 +36,33 @@ namespace Umbraco.Web.Publishing //Updating the cache is not done in the Document-Publish methods, so this part should be added //global::umbraco.library.UpdateDocumentCache(doc.Id); + int contentId = content.Id; + var doc = new Document(contentId, true); var user = new User(userId); return doc.PublishWithResult(user); } - internal bool PublishWithChildren(int userId, int contentId) + public bool PublishWithChildren(IEnumerable children, int userId) { + int contentId = children.Last().Id; var doc = new Document(contentId, true); var user = new User(userId); return doc.PublishWithChildrenWithResult(user); } - internal void PublishWithSubs(int userId, int contentId) + public void PublishWithSubs(IContent content, int userId) { + int contentId = content.Id; var doc = new Document(contentId, true); var user = new User(userId); doc.PublishWithSubs(user); } + + public bool UnPublish(IContent content, int userId) + { + return false; + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Services/ContentService.cs b/src/Umbraco.Web/Services/ContentService.cs index c773d70a53..b6d848907c 100644 --- a/src/Umbraco.Web/Services/ContentService.cs +++ b/src/Umbraco.Web/Services/ContentService.cs @@ -3,120 +3,452 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Web.Publishing; +using Content = Umbraco.Core.Models.Content; namespace Umbraco.Web.Services { public class ContentService : IContentService { - public IContent CreateContent(string contentTypeAlias) + private readonly IUnitOfWorkProvider _provider; + private readonly IPublishingStrategy _publishingStrategy; + + public ContentService() : this(new PetaPocoUnitOfWorkProvider()) { - throw new NotImplementedException(); } + public ContentService(IUnitOfWorkProvider provider) : this(provider, new PublishingStrategy()) + { + + } + + public ContentService(IUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy) + { + _provider = provider; + _publishingStrategy = publishingStrategy; + } + + /// + /// Creates an object using the alias of the + /// that this Content is based on. + /// + /// Id of Parent for content + /// Alias of the + /// + public IContent CreateContent(int parentId, string contentTypeAlias) + { + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + var query = Query.Builder.Where(x => x.Alias == contentTypeAlias); + var contentTypes = repository.GetByQuery(query); + + if (!contentTypes.Any()) + throw new Exception(string.Format("No ContentType matching the passed in Alias: {0} was found", contentTypeAlias)); + + var contentType = contentTypes.First(); + + if (contentType == null) + throw new Exception(string.Format("No ContentType matching the passed in Alias: {0} was found", contentTypeAlias)); + + return new Content(parentId, contentType); + } + + /// + /// Gets an object by Id + /// + /// Id of the Content to retrieve + /// public IContent GetById(int id) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + return repository.Get(id); } + /// + /// Gets a collection of objects by Level + /// + /// The level to retrieve Content from + /// An Enumerable list of objects public IEnumerable GetByLevel(int level) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var query = Query.Builder.Where(x => x.Level == level); + var contents = repository.GetByQuery(query); + + return contents; } + /// + /// Gets a collection of objects by Parent Id + /// + /// Id of the Parent to retrieve Children from + /// An Enumerable list of objects public IEnumerable GetChildren(int id) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var query = Query.Builder.Where(x => x.ParentId == id); + var contents = repository.GetByQuery(query); + + return contents; } + /// + /// Gets a collection of an objects versions by Id + /// + /// + /// An Enumerable list of objects public IEnumerable GetVersions(int id) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + var versions = repository.GetAllVersions(id); + return versions; } + /// + /// Gets a collection of objects, which reside at the first level / root + /// + /// An Enumerable list of objects public IEnumerable GetRootContent() { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var query = Query.Builder.Where(x => x.ParentId == -1); + var contents = repository.GetByQuery(query); + + return contents; } + /// + /// Gets a collection of objects, which has an expiration date greater then today + /// + /// An Enumerable list of objects public IEnumerable GetContentForExpiration() { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var query = Query.Builder.Where(x => x.Published == true && x.ExpireDate != null && x.ExpireDate.Value <= DateTime.Now); + var contents = repository.GetByQuery(query); + + return contents; } + /// + /// Gets a collection of objects, which has a release date greater then today + /// + /// An Enumerable list of objects public IEnumerable GetContentForRelease() { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var query = Query.Builder.Where(x => x.Published == true && x.ReleaseDate != null && x.ReleaseDate.Value <= DateTime.Now); + var contents = repository.GetByQuery(query); + + return contents; } + /// + /// Gets a collection of an objects, which resides in the Recycle Bin + /// + /// An Enumerable list of objects public IEnumerable GetContentInRecycleBin() { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var query = Query.Builder.Where(x => x.ParentId == -20); + var contents = repository.GetByQuery(query); + + return contents; } - public bool RePublishAll() + /// + /// Re-Publishes all Content + /// + /// Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False + public bool RePublishAll(int userId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var list = new List(); + + var rootContent = GetRootContent(); + foreach (var content in rootContent) + { + list.AddRange(GetChildrenDeep(content.Id)); + } + + foreach (var item in list) + { + ((Content)item).ChangePublishedState(true); + repository.AddOrUpdate(item); + } + + unitOfWork.Commit(); + + return _publishingStrategy.PublishWithChildren(list, userId); } + /// + /// Publishes a single object + /// + /// The to publish + /// Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False public bool Publish(IContent content, int userId) { - throw new NotImplementedException(); + return SaveAndPublish(content, userId); } + /// + /// Publishes a object and all its children + /// + /// The to publish along with its children + /// Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False public bool PublishWithChildren(IContent content, int userId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + var list = GetChildrenDeep(content.Id); + list.Add(content); + + foreach (var item in list) + { + ((Content)item).ChangePublishedState(true); + repository.AddOrUpdate(item); + } + + unitOfWork.Commit(); + + return _publishingStrategy.PublishWithChildren(list, userId); } + /// + /// UnPublishes a single object + /// + /// The to publish + /// Id of the User issueing the publishing + /// True if unpublishing succeeded, otherwise False + public bool UnPublish(IContent content, int userId) + { + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + ((Content)content).ChangePublishedState(false); + repository.AddOrUpdate(content); + unitOfWork.Commit(); + + return _publishingStrategy.UnPublish(content, userId); + } + + /// + /// Gets a flat list of decendents of content from parent id + /// + /// + /// + private List GetChildrenDeep(int parentId) + { + var list = new List(); + var children = GetChildren(parentId); + foreach (var child in children) + { + list.Add(child); + list.AddRange(GetChildrenDeep(child.Id)); + } + return list; + } + + /// + /// Saves and Publishes a single object + /// + /// The to save and publish + /// Id of the User issueing the publishing + /// True if publishing succeeded, otherwise False public bool SaveAndPublish(IContent content, int userId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + ((Content)content).ChangePublishedState(true); + repository.AddOrUpdate(content); + unitOfWork.Commit(); + + return _publishingStrategy.Publish(content, userId); } + /// + /// Saves a single object + /// + /// The to save + /// Id of the User saving the Content public void Save(IContent content, int userId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + repository.AddOrUpdate(content); + unitOfWork.Commit(); } + /// + /// Saves a collection of objects + /// + /// Collection of to save + /// Id of the User saving the Content public void Save(IEnumerable contents, int userId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + foreach (var content in contents) + { + repository.AddOrUpdate(content); + } + unitOfWork.Commit(); } + /// + /// Deletes all content of specified type. All children of deleted content is moved to Recycle Bin. + /// + /// This needs extra care and attention as its potentially a dangerous and extensive operation + /// Id of the public void DeleteContentOfType(int contentTypeId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + //NOTE What about content that has the contenttype as part of its composition? + var query = Query.Builder.Where(x => x.ContentTypeId == contentTypeId); + var contents = repository.GetByQuery(query); + + foreach (var content in contents) + { + ((Content)content).ChangeTrashedState(true); + repository.AddOrUpdate(content); + } + + unitOfWork.Commit(); } + /// + /// Permanently deletes an object + /// + /// Please note that this method will completely remove the Content from the database + /// The to delete + /// Id of the User deleting the Content public void Delete(IContent content, int userId) { - throw new NotImplementedException(); + //TODO This method should handle/react to errors when there is a constraint issue with the content being deleted + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + repository.Delete(content); + unitOfWork.Commit(); } + /// + /// Deletes an object by moving it to the Recycle Bin + /// + /// Move an item to the Recycle Bin will result in the item being unpublished + /// The to delete + /// Id of the User deleting the Content public void MoveToRecycleBin(IContent content, int userId) { - throw new NotImplementedException(); + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + ((Content)content).ChangeTrashedState(true); + repository.AddOrUpdate(content); + unitOfWork.Commit(); } + /// + /// Moves an object to a new location + /// + /// The to move + /// Id of the Content's new Parent + /// Id of the User moving the Content public void Move(IContent content, int parentId, int userId) { - throw new NotImplementedException(); + content.ParentId = parentId; + SaveAndPublish(content, userId); } + /// + /// Copies an object by creating a new Content object of the same type and copies all data from the current + /// to the new copy which is returned. + /// + /// The to copy + /// Id of the Content's new Parent + /// Id of the User copying the Content + /// The newly created object public IContent Copy(IContent content, int parentId, int userId) { - throw new NotImplementedException(); + var copy = ((Content) content).Clone(); + copy.ParentId = parentId; + + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + + repository.AddOrUpdate(copy); + unitOfWork.Commit(); + + return copy; } - public void SendToPublication(IContent content, int userId) + /// + /// Sends an to Publication, which executes handlers and events for the 'Send to Publication' action. + /// + /// The to send to publication + /// Id of the User issueing the send to publication + /// True if sending publication was succesfull otherwise false + public bool SendToPublication(IContent content, int userId) { - throw new NotImplementedException(); + //TODO Implement something similar to this + /*SendToPublishEventArgs e = new SendToPublishEventArgs(); + FireBeforeSendToPublish(e); + if (!e.Cancel) + { + global::umbraco.BusinessLogic.Actions.Action.RunActionHandlers(content, ActionToPublish.Instance); + + FireAfterSendToPublish(e); + return true; + } + + return false;*/ + return false; } + /// + /// Rollback an object to a previous version. + /// This will create a new version, which is a copy of all the old data. + /// + /// Id of the being rolled back + /// Id of the version to rollback to + /// Id of the User issueing the rollback of the Content + /// The newly created object public IContent Rollback(int id, Guid versionId, int userId) { - throw new NotImplementedException(); + //TODO Need to test if this actually works + var unitOfWork = _provider.GetUnitOfWork(); + var repository = RepositoryResolver.ResolveByType(unitOfWork); + var content = repository.GetByVersion(id, versionId); + + repository.AddOrUpdate(content); + unitOfWork.Commit(); + + return content; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Services/IContentService.cs b/src/Umbraco.Web/Services/IContentService.cs index 07ace24476..c70c35f0fb 100644 --- a/src/Umbraco.Web/Services/IContentService.cs +++ b/src/Umbraco.Web/Services/IContentService.cs @@ -13,9 +13,10 @@ namespace Umbraco.Web.Services /// Creates an object using the alias of the /// that this Content is based on. /// + /// Id of Parent for content /// Alias of the /// - IContent CreateContent(string contentTypeAlias); + IContent CreateContent(int parentId, string contentTypeAlias); //TODO Add CreateNewVersion method? Its currently used in the Document API when Publishing - latest version is published, //but then a new version is created so latest version is not published. @@ -76,8 +77,9 @@ namespace Umbraco.Web.Services /// /// Re-Publishes all Content /// + /// Id of the User issueing the publishing /// True if publishing succeeded, otherwise False - bool RePublishAll(); + bool RePublishAll(int userId); /// /// Publishes a single object @@ -95,6 +97,14 @@ namespace Umbraco.Web.Services /// True if publishing succeeded, otherwise False bool PublishWithChildren(IContent content, int userId); + /// + /// UnPublishes a single object + /// + /// The to publish + /// Id of the User issueing the publishing + /// True if unpublishing succeeded, otherwise False + bool UnPublish(IContent content, int userId); + /// /// Saves and Publishes a single object /// @@ -163,7 +173,8 @@ namespace Umbraco.Web.Services /// /// The to send to publication /// Id of the User issueing the send to publication - void SendToPublication(IContent content, int userId); + /// True if sending publication was succesfull otherwise false + bool SendToPublication(IContent content, int userId); /// /// Rollback an object to a previous version. diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 5ba769e3d7..0c16cbfd94 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -304,6 +304,7 @@ +