diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index 565d693979..1b7a1c5ae1 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -22,7 +22,7 @@
not want this to happen as the alpha of the next major is, really, the next major already.
-->
-
+
diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
index b469c02a3c..51935e6517 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
@@ -121,6 +121,8 @@ namespace Umbraco.Core.Migrations.Upgrade
To("{648A2D5F-7467-48F8-B309-E99CEEE00E2A}"); // fixed version
To("{C39BF2A7-1454-4047-BBFE-89E40F66ED63}");
To("{64EBCE53-E1F0-463A-B40B-E98EFCCA8AE2}");
+ To("{0009109C-A0B8-4F3F-8FEB-C137BBDDA268}");
+
//FINAL
diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs
new file mode 100644
index 0000000000..1df11a3e99
--- /dev/null
+++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs
@@ -0,0 +1,15 @@
+using Umbraco.Core.Persistence.Dtos;
+
+namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
+{
+ public class AddContentTypeIsElementColumn : MigrationBase
+ {
+ public AddContentTypeIsElementColumn(IMigrationContext context) : base(context)
+ { }
+
+ public override void Migrate()
+ {
+ AddColumn("isElement");
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 88b1179f6d..b6ea9f50a0 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -26,6 +26,7 @@ namespace Umbraco.Core.Models
private string _thumbnail = "folder.png";
private bool _allowedAsRoot; // note: only one that's not 'pure element type'
private bool _isContainer;
+ private bool _isElement;
private PropertyGroupCollection _propertyGroups;
private PropertyTypeCollection _noGroupPropertyTypes;
private IEnumerable _allowedContentTypes;
@@ -90,6 +91,7 @@ namespace Umbraco.Core.Models
public readonly PropertyInfo IconSelector = ExpressionHelper.GetPropertyInfo(x => x.Icon);
public readonly PropertyInfo ThumbnailSelector = ExpressionHelper.GetPropertyInfo(x => x.Thumbnail);
public readonly PropertyInfo AllowedAsRootSelector = ExpressionHelper.GetPropertyInfo(x => x.AllowedAsRoot);
+ public readonly PropertyInfo IsElementSelector = ExpressionHelper.GetPropertyInfo(x => x.IsElement);
public readonly PropertyInfo IsContainerSelector = ExpressionHelper.GetPropertyInfo(x => x.IsContainer);
public readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes);
public readonly PropertyInfo PropertyGroupsSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups);
@@ -180,6 +182,14 @@ namespace Umbraco.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _isContainer, Ps.Value.IsContainerSelector);
}
+ ///
+ [DataMember]
+ public bool IsElement
+ {
+ get => _isElement;
+ set => SetPropertyValueAndDetectChanges(value, ref _isElement, Ps.Value.IsElementSelector);
+ }
+
///
/// Gets or sets a list of integer Ids for allowed ContentTypes
///
diff --git a/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs b/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs
index 8af48bb881..adbc3de54f 100644
--- a/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs
@@ -15,7 +15,8 @@ namespace Umbraco.Core.Models
{
var type = contentType.GetType();
var itemType = PublishedItemType.Unknown;
- if (typeof(IContentType).IsAssignableFrom(type)) itemType = PublishedItemType.Content;
+ if (contentType.IsElement) itemType = PublishedItemType.Element;
+ else if (typeof(IContentType).IsAssignableFrom(type)) itemType = PublishedItemType.Content;
else if (typeof(IMediaType).IsAssignableFrom(type)) itemType = PublishedItemType.Media;
else if (typeof(IMemberType).IsAssignableFrom(type)) itemType = PublishedItemType.Member;
return itemType;
diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs
index a1d4aee02f..787e347b37 100644
--- a/src/Umbraco.Core/Models/IContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/IContentTypeBase.cs
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Models
/// the icon (eg. icon-home) along with an optional CSS class name representing the
/// color (eg. icon-blue). Put together, the value for this scenario would be
/// icon-home color-blue.
- ///
+ ///
/// If a class name for the color isn't specified, the icon color will default to black.
///
string Icon { get; set; }
@@ -48,6 +48,16 @@ namespace Umbraco.Core.Models
///
bool IsContainer { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this content type is for an element.
+ ///
+ ///
+ /// By default a content type is for a true media, member or document, but
+ /// it can also be for an element, ie a subset that can for instance be used in
+ /// nested content.
+ ///
+ bool IsElement { get; set; }
+
///
/// Gets or sets the content variation of the content type.
///
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs
index e55fe66945..42e9c9538d 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs
@@ -4,13 +4,18 @@
/// The type of published element.
///
/// Can be a simple element, or a document, a media, a member.
- public enum PublishedItemType // fixme - need to rename to PublishedElementType but then conflicts?
+ public enum PublishedItemType
{
///
/// Unknown.
///
Unknown = 0,
+ ///
+ /// An element.
+ ///
+ Element,
+
///
/// A document.
///
diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs
index d930abc54c..4f3a67aa91 100644
--- a/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs
+++ b/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs
@@ -41,6 +41,10 @@ namespace Umbraco.Core.Persistence.Dtos
[Constraint(Default = "0")]
public bool IsContainer { get; set; }
+ [Column("isElement")]
+ [Constraint(Default = "0")]
+ public bool IsElement { get; set; }
+
[Column("allowAtRoot")]
[Constraint(Default = "0")]
public bool AllowAtRoot { get; set; }
diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
index 38a1aa2aab..7a04a6d0d9 100644
--- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
@@ -107,6 +107,7 @@ namespace Umbraco.Core.Persistence.Factories
entity.CreatorId = dto.NodeDto.UserId ?? Constants.Security.UnknownUserId;
entity.AllowedAsRoot = dto.AllowAtRoot;
entity.IsContainer = dto.IsContainer;
+ entity.IsElement = dto.IsElement;
entity.Trashed = dto.NodeDto.Trashed;
entity.Variations = (ContentVariation) dto.Variations;
}
@@ -132,6 +133,7 @@ namespace Umbraco.Core.Persistence.Factories
NodeId = entity.Id,
AllowAtRoot = entity.AllowedAsRoot,
IsContainer = entity.IsContainer,
+ IsElement = entity.IsElement,
Variations = (byte) entity.Variations,
NodeDto = BuildNodeDto(entity, nodeObjectType)
};
diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs
index c692a75474..a24963bace 100644
--- a/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Description, dto => dto.Description);
CacheMap(src => src.Icon, dto => dto.Icon);
CacheMap(src => src.IsContainer, dto => dto.IsContainer);
+ CacheMap(src => src.IsElement, dto => dto.IsElement);
CacheMap(src => src.Thumbnail, dto => dto.Thumbnail);
}
}
diff --git a/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs
index 3f5a6e24bc..6cf83bc7aa 100644
--- a/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Description, dto => dto.Description);
CacheMap(src => src.Icon, dto => dto.Icon);
CacheMap(src => src.IsContainer, dto => dto.IsContainer);
+ CacheMap(src => src.IsElement, dto => dto.IsElement);
CacheMap(src => src.Thumbnail, dto => dto.Thumbnail);
}
}
diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs
index 28dc19171f..9a4e4ec040 100644
--- a/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Description, dto => dto.Description);
CacheMap(src => src.Icon, dto => dto.Icon);
CacheMap(src => src.IsContainer, dto => dto.IsContainer);
+ CacheMap(src => src.IsElement, dto => dto.IsElement);
CacheMap(src => src.Thumbnail, dto => dto.Thumbnail);
}
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
index 662254d1ee..683df047f8 100644
--- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs
@@ -1283,7 +1283,7 @@ AND umbracoNode.id <> @id",
if (db == null) throw new ArgumentNullException(nameof(db));
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.variations as ctVariations,
- cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
+ cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.IsElement as ctIsElement, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
@@ -1384,6 +1384,7 @@ AND umbracoNode.id <> @id",
Description = currCt.ctDesc,
Icon = currCt.ctIcon,
IsContainer = currCt.ctIsContainer,
+ IsElement = currCt.ctIsElement,
NodeId = currCt.ctId,
PrimaryKey = currCt.ctPk,
Thumbnail = currCt.ctThumb,
@@ -1422,7 +1423,7 @@ AND umbracoNode.id <> @id",
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.variations as ctVariations,
- cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
+ cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.IsElement as ctIsElement, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
@@ -1559,6 +1560,7 @@ AND umbracoNode.id <> @id",
Description = currCt.ctDesc,
Icon = currCt.ctIcon,
IsContainer = currCt.ctIsContainer,
+ IsElement = currCt.ctIsElement,
NodeId = currCt.ctId,
PrimaryKey = currCt.ctPk,
Thumbnail = currCt.ctThumb,
diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs
index d938e032a8..38d5471bb7 100644
--- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs
+++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs
@@ -337,7 +337,8 @@ namespace Umbraco.Core.Services
new XElement("Thumbnail", contentType.Thumbnail),
new XElement("Description", contentType.Description),
new XElement("AllowAtRoot", contentType.AllowedAsRoot.ToString()),
- new XElement("IsListView", contentType.IsContainer.ToString()));
+ new XElement("IsListView", contentType.IsContainer.ToString()),
+ new XElement("IsElement", contentType.IsElement.ToString()));
var masterContentType = contentType.ContentTypeComposition.FirstOrDefault(x => x.Id == contentType.ParentId);
if(masterContentType != null)
diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs
index 106d2b9f12..8f6c287cf1 100644
--- a/src/Umbraco.Core/Services/Implement/PackagingService.cs
+++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs
@@ -578,6 +578,10 @@ namespace Umbraco.Core.Services.Implement
if (isListView != null)
contentType.IsContainer = isListView.Value.InvariantEquals("true");
+ var isElement = infoElement.Element("IsElement");
+ if (isListView != null)
+ contentType.IsElement = isElement.Value.InvariantEquals("true");
+
//Name of the master corresponds to the parent and we need to ensure that the Parent Id is set
var masterElement = infoElement.Element("Master");
if (masterElement != null)
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 609befd233..7d65a46f49 100755
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -377,6 +377,7 @@
+
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
index 6e652fdc68..ab65ac82b1 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
@@ -1,878 +1,877 @@
-//using System;
-//using System.Collections.Generic;
-//using System.Linq;
-//using System.Web;
-//using NUnit.Framework;
-//using Umbraco.Core;
-//using Umbraco.Core.Models.PublishedContent;
-//using Umbraco.Core.PropertyEditors;
-//using Umbraco.Web;
-//using Umbraco.Web.PublishedCache;
-//using Umbraco.Core.Composing;
-//using Moq;
-//using Umbraco.Core.Cache;
-//using Umbraco.Core.Configuration;
-//using Umbraco.Core.Logging;
-//using Umbraco.Core.Models;
-//using Umbraco.Core.Services;
-//using Umbraco.Tests.TestHelpers;
-//using Umbraco.Tests.Testing;
-//using Umbraco.Web.Models.PublishedContent;
-//using Umbraco.Web.PropertyEditors;
-//
-//namespace Umbraco.Tests.PublishedContent
-//{
-// ///
-// /// Tests the methods on IPublishedContent using the DefaultPublishedContentStore
-// ///
-// [TestFixture]
-// [UmbracoTest(TypeLoader = UmbracoTestOptions.TypeLoader.PerFixture)]
-// public class PublishedContentTests : PublishedContentTestBase
-// {
-// protected override void Compose()
-// {
-// base.Compose();
-//
-// Composition.RegisterUnique(f => new PublishedModelFactory(f.GetInstance().GetTypes()));
-// Composition.RegisterUnique();
-// Composition.RegisterUnique();
-//
-// var logger = Mock.Of();
-// var dataTypeService = new TestObjects.TestDataTypeService(
-// new DataType(new VoidEditor(logger)) { Id = 1 },
-// new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 },
-// new DataType(new RichTextPropertyEditor(logger)) { Id = 1002 },
-// new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 },
-// new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 },
-// new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 });
-// Composition.RegisterUnique(f => dataTypeService);
-// }
-//
-// protected override void Initialize()
-// {
-// base.Initialize();
-//
-// var factory = Factory.GetInstance() as PublishedContentTypeFactory;
-//
-// // need to specify a custom callback for unit tests
-// // AutoPublishedContentTypes generates properties automatically
-// // when they are requested, but we must declare those that we
-// // explicitely want to be here...
-//
-// var propertyTypes = new[]
-// {
-// // AutoPublishedContentType will auto-generate other properties
-// factory.CreatePropertyType("umbracoNaviHide", 1001),
-// factory.CreatePropertyType("selectedNodes", 1),
-// factory.CreatePropertyType("umbracoUrlAlias", 1),
-// factory.CreatePropertyType("content", 1002),
-// factory.CreatePropertyType("testRecursive", 1),
-// };
-// var compositionAliases = new[] { "MyCompositionAlias" };
-// var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes);
-// ContentTypesCache.GetPublishedContentTypeByAlias = alias => type;
-// }
-//
-// protected override TypeLoader CreateTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
-// {
-// var pluginManager = base.CreateTypeLoader(runtimeCache, globalSettings, logger);
-//
-// // this is so the model factory looks into the test assembly
-// pluginManager.AssembliesToScan = pluginManager.AssembliesToScan
-// .Union(new[] { typeof(PublishedContentTests).Assembly })
-// .ToList();
-//
-// return pluginManager;
-// }
-//
-// private readonly Guid _node1173Guid = Guid.NewGuid();
-//
-// protected override string GetXmlContent(int templateId)
-// {
-// return @"
-//
-//
-//
-//
-//]>
-//
-//
-//
-//
-// 1
-//
-//
-// This is some content]]>
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//
-// 1
-//
-//
-//
-//
-//
-//
-//
-//
-//
-//";
-// }
-//
-// internal IPublishedContent GetNode(int id)
-// {
-// var ctx = GetUmbracoContext("/test");
-// var doc = ctx.ContentCache.GetById(id);
-// Assert.IsNotNull(doc);
-// return doc;
-// }
-//
-// [Test]
-// public void GetNodeByIds()
-// {
-// var ctx = GetUmbracoContext("/test");
-// var contentById = ctx.ContentCache.GetById(1173);
-// Assert.IsNotNull(contentById);
-// var contentByGuid = ctx.ContentCache.GetById(_node1173Guid);
-// Assert.IsNotNull(contentByGuid);
-// Assert.AreEqual(contentById.Id, contentByGuid.Id);
-// Assert.AreEqual(contentById.Key, contentByGuid.Key);
-//
-// contentById = ctx.ContentCache.GetById(666);
-// Assert.IsNull(contentById);
-// contentByGuid = ctx.ContentCache.GetById(Guid.NewGuid());
-// Assert.IsNull(contentByGuid);
-// }
-//
-// [Test]
-// public void Is_Last_From_Where_Filter_Dynamic_Linq()
-// {
-// var doc = GetNode(1173);
-//
-// var items = doc.Children.Where(x => x.IsVisible()).ToIndexedArray();
-//
-// foreach (var item in items)
-// {
-// if (item.Content.Id != 1178)
-// {
-// Assert.IsFalse(item.IsLast());
-// }
-// else
-// {
-// Assert.IsTrue(item.IsLast());
-// }
-// }
-// }
-//
-// [Test]
-// public void Is_Last_From_Where_Filter()
-// {
-// var doc = GetNode(1173);
-//
-// var items = doc
-// .Children
-// .Where(x => x.IsVisible())
-// .ToIndexedArray();
-//
-// Assert.AreEqual(4, items.Length);
-//
-// foreach (var d in items)
-// {
-// switch (d.Content.Id)
-// {
-// case 1174:
-// Assert.IsTrue(d.IsFirst());
-// Assert.IsFalse(d.IsLast());
-// break;
-// case 117:
-// Assert.IsFalse(d.IsFirst());
-// Assert.IsFalse(d.IsLast());
-// break;
-// case 1177:
-// Assert.IsFalse(d.IsFirst());
-// Assert.IsFalse(d.IsLast());
-// break;
-// case 1178:
-// Assert.IsFalse(d.IsFirst());
-// Assert.IsTrue(d.IsLast());
-// break;
-// default:
-// Assert.Fail("Invalid id.");
-// break;
-// }
-// }
-// }
-//
-// [PublishedModel("Home")]
-// internal class Home : PublishedContentModel
-// {
-// public Home(IPublishedContent content)
-// : base(content)
-// {}
-// }
-//
-// [PublishedModel("anything")]
-// internal class Anything : PublishedContentModel
-// {
-// public Anything(IPublishedContent content)
-// : base(content)
-// { }
-// }
-//
-// [Test]
-// [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme
-// public void Is_Last_From_Where_Filter2()
-// {
-// var doc = GetNode(1173);
-//
-// var items = doc.Children
-// .Select(x => x.CreateModel()) // linq, returns IEnumerable
-//
-// // only way around this is to make sure every IEnumerable extension
-// // explicitely returns a PublishedContentSet, not an IEnumerable
-//
-// .OfType() // ours, return IEnumerable (actually a PublishedContentSet)
-// .Where(x => x.IsVisible()) // so, here it's linq again :-(
-// .ToIndexedArray(); // so, we need that one for the test to pass
-//
-// Assert.AreEqual(1, items.Length);
-//
-// foreach (var d in items)
-// {
-// switch (d.Content.Id)
-// {
-// case 1174:
-// Assert.IsTrue(d.IsFirst());
-// Assert.IsTrue(d.IsLast());
-// break;
-// default:
-// Assert.Fail("Invalid id.");
-// break;
-// }
-// }
-// }
-//
-// [Test]
-// public void Is_Last_From_Take()
-// {
-// var doc = GetNode(1173);
-//
-// var items = doc.Children.Take(4).ToIndexedArray();
-//
-// foreach (var item in items)
-// {
-// if (item.Content.Id != 1178)
-// {
-// Assert.IsFalse(item.IsLast());
-// }
-// else
-// {
-// Assert.IsTrue(item.IsLast());
-// }
-// }
-// }
-//
-// [Test]
-// public void Is_Last_From_Skip()
-// {
-// var doc = GetNode(1173);
-//
-// foreach (var d in doc.Children.Skip(1).ToIndexedArray())
-// {
-// if (d.Content.Id != 1176)
-// {
-// Assert.IsFalse(d.IsLast());
-// }
-// else
-// {
-// Assert.IsTrue(d.IsLast());
-// }
-// }
-// }
-//
-// [Test]
-// public void Is_Last_From_Concat()
-// {
-// var doc = GetNode(1173);
-//
-// var items = doc.Children
-// .Concat(new[] { GetNode(1175), GetNode(4444) })
-// .ToIndexedArray();
-//
-// foreach (var item in items)
-// {
-// if (item.Content.Id != 4444)
-// {
-// Assert.IsFalse(item.IsLast());
-// }
-// else
-// {
-// Assert.IsTrue(item.IsLast());
-// }
-// }
-// }
-//
-// [Test]
-// public void Descendants_Ordered_Properly()
-// {
-// var doc = GetNode(1046);
-//
-// var expected = new[] { 1046, 1173, 1174, 117, 1177, 1178, 1179, 1176, 1175, 4444, 1172 };
-// var exindex = 0;
-//
-// // must respect the XPath descendants-or-self axis!
-// foreach (var d in doc.DescendantsOrSelf())
-// Assert.AreEqual(expected[exindex++], d.Id);
-// }
-//
-// [Test]
-// public void Get_Property_Value_Recursive()
-// {
-// var doc = GetNode(1174);
-// var rVal = doc.Value("testRecursive", fallback: Fallback.ToAncestors);
-// var nullVal = doc.Value("DoNotFindThis", fallback: Fallback.ToAncestors);
-// Assert.AreEqual("This is the recursive val", rVal);
-// Assert.AreEqual(null, nullVal);
-// }
-//
-// [Test]
-// public void Get_Property_Value_Uses_Converter()
-// {
-// var doc = GetNode(1173);
-//
-// var propVal = doc.Value("content");
-// Assert.IsInstanceOf(typeof(IHtmlString), propVal);
-// Assert.AreEqual("This is some content
", propVal.ToString());
-//
-// var propVal2 = doc.Value("content");
-// Assert.IsInstanceOf(typeof(IHtmlString), propVal2);
-// Assert.AreEqual("This is some content
", propVal2.ToString());
-//
-// var propVal3 = doc.Value("Content");
-// Assert.IsInstanceOf(typeof(IHtmlString), propVal3);
-// Assert.AreEqual("This is some content
", propVal3.ToString());
-// }
-//
-// [Test]
-// public void Complex_Linq()
-// {
-// var doc = GetNode(1173);
-//
-// var result = doc.Ancestors().OrderBy(x => x.Level)
-// .Single()
-// .Descendants()
-// .FirstOrDefault(x => x.Value("selectedNodes", defaultValue: "").Split(',').Contains("1173"));
-//
-// Assert.IsNotNull(result);
-// }
-//
-// [Test]
-// public void Children_GroupBy_DocumentTypeAlias()
-// {
-// var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { });
-// var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { });
-// var contentTypes = new Dictionary
-// {
-// { home.Alias, home },
-// { custom.Alias, custom }
-// };
-// ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias];
-//
-// var doc = GetNode(1046);
-//
-// var found1 = doc.Children.GroupBy(x => x.ContentType.Alias).ToArray();
-//
-// Assert.AreEqual(2, found1.Length);
-// Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count());
-// Assert.AreEqual(1, found1.Single(x => x.Key.ToString() == "CustomDocument").Count());
-// }
-//
-// [Test]
-// public void Children_Where_DocumentTypeAlias()
-// {
-// var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { });
-// var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { });
-// var contentTypes = new Dictionary
-// {
-// { home.Alias, home },
-// { custom.Alias, custom }
-// };
-// ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias];
-//
-// var doc = GetNode(1046);
-//
-// var found1 = doc.Children.Where(x => x.ContentType.Alias == "CustomDocument");
-// var found2 = doc.Children.Where(x => x.ContentType.Alias == "Home");
-//
-// Assert.AreEqual(1, found1.Count());
-// Assert.AreEqual(2, found2.Count());
-// }
-//
-// [Test]
-// public void Children_Order_By_Update_Date()
-// {
-// var doc = GetNode(1173);
-//
-// var ordered = doc.Children.OrderBy(x => x.UpdateDate);
-//
-// var correctOrder = new[] { 1178, 1177, 1174, 1176 };
-// for (var i = 0; i < correctOrder.Length; i++)
-// {
-// Assert.AreEqual(correctOrder[i], ordered.ElementAt(i).Id);
-// }
-//
-// }
-//
-// [Test]
-// public void FirstChild()
-// {
-// var doc = GetNode(1173); // has child nodes
-// Assert.IsNotNull(doc.FirstChild());
-// Assert.IsNotNull(doc.FirstChild(x => true));
-// Assert.IsNotNull(doc.FirstChild());
-//
-// doc = GetNode(1175); // does not have child nodes
-// Assert.IsNull(doc.FirstChild());
-// Assert.IsNull(doc.FirstChild(x => true));
-// Assert.IsNull(doc.FirstChild());
-// }
-//
-// [Test]
-// public void FirstChildAsT()
-// {
-// var doc = GetNode(1046); // has child nodes
-//
-// var model = doc.FirstChild(x => true); // predicate
-//
-// Assert.IsNotNull(model);
-// Assert.IsTrue(model.Id == 1173);
-// Assert.IsInstanceOf(model);
-// Assert.IsInstanceOf(model);
-//
-// doc = GetNode(1175); // does not have child nodes
-// Assert.IsNull(doc.FirstChild());
-// Assert.IsNull(doc.FirstChild(x => true));
-// }
-//
-// [Test]
-// public void IsComposedOf()
-// {
-// var doc = GetNode(1173);
-//
-// var isComposedOf = doc.IsComposedOf("MyCompositionAlias");
-//
-// Assert.IsTrue(isComposedOf);
-// }
-//
-// [Test]
-// public void HasProperty()
-// {
-// var doc = GetNode(1173);
-//
-// var hasProp = doc.HasProperty(Constants.Conventions.Content.UrlAlias);
-//
-// Assert.IsTrue(hasProp);
-// }
-//
-// [Test]
-// public void HasValue()
-// {
-// var doc = GetNode(1173);
-//
-// var hasValue = doc.HasValue(Constants.Conventions.Content.UrlAlias);
-// var noValue = doc.HasValue("blahblahblah");
-//
-// Assert.IsTrue(hasValue);
-// Assert.IsFalse(noValue);
-// }
-//
-// [Test]
-// public void Ancestors_Where_Visible()
-// {
-// var doc = GetNode(1174);
-//
-// var whereVisible = doc.Ancestors().Where(x => x.IsVisible());
-// Assert.AreEqual(1, whereVisible.Count());
-//
-// }
-//
-// [Test]
-// public void Visible()
-// {
-// var hidden = GetNode(1046);
-// var visible = GetNode(1173);
-//
-// Assert.IsFalse(hidden.IsVisible());
-// Assert.IsTrue(visible.IsVisible());
-// }
-//
-// [Test]
-// public void Ancestor_Or_Self()
-// {
-// var doc = GetNode(1173);
-//
-// var result = doc.AncestorOrSelf();
-//
-// Assert.IsNotNull(result);
-//
-// // ancestor-or-self has to be self!
-// Assert.AreEqual(1173, result.Id);
-// }
-//
-// [Test]
-// public void U4_4559()
-// {
-// var doc = GetNode(1174);
-// var result = doc.AncestorOrSelf(1);
-// Assert.IsNotNull(result);
-// Assert.AreEqual(1046, result.Id);
-// }
-//
-// [Test]
-// public void Ancestors_Or_Self()
-// {
-// var doc = GetNode(1174);
-//
-// var result = doc.AncestorsOrSelf().ToArray();
-//
-// Assert.IsNotNull(result);
-//
-// Assert.AreEqual(3, result.Length);
-// Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
-// }
-//
-// [Test]
-// public void Ancestors()
-// {
-// var doc = GetNode(1174);
-//
-// var result = doc.Ancestors().ToArray();
-//
-// Assert.IsNotNull(result);
-//
-// Assert.AreEqual(2, result.Length);
-// Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
-// }
-//
-// [Test]
-// public void IsAncestor()
-// {
-// // Structure:
-// // - Root : 1046 (no parent)
-// // -- Home: 1173 (parent 1046)
-// // -- Custom Doc: 1178 (parent 1173)
-// // --- Custom Doc2: 1179 (parent: 1178)
-// // -- Custom Doc4: 117 (parent 1173)
-// // - Custom Doc3: 1172 (no parent)
-//
-// var home = GetNode(1173);
-// var root = GetNode(1046);
-// var customDoc = GetNode(1178);
-// var customDoc2 = GetNode(1179);
-// var customDoc3 = GetNode(1172);
-// var customDoc4 = GetNode(117);
-//
-// Assert.IsTrue(root.IsAncestor(customDoc4));
-// Assert.IsFalse(root.IsAncestor(customDoc3));
-// Assert.IsTrue(root.IsAncestor(customDoc2));
-// Assert.IsTrue(root.IsAncestor(customDoc));
-// Assert.IsTrue(root.IsAncestor(home));
-// Assert.IsFalse(root.IsAncestor(root));
-//
-// Assert.IsTrue(home.IsAncestor(customDoc4));
-// Assert.IsFalse(home.IsAncestor(customDoc3));
-// Assert.IsTrue(home.IsAncestor(customDoc2));
-// Assert.IsTrue(home.IsAncestor(customDoc));
-// Assert.IsFalse(home.IsAncestor(home));
-// Assert.IsFalse(home.IsAncestor(root));
-//
-// Assert.IsFalse(customDoc.IsAncestor(customDoc4));
-// Assert.IsFalse(customDoc.IsAncestor(customDoc3));
-// Assert.IsTrue(customDoc.IsAncestor(customDoc2));
-// Assert.IsFalse(customDoc.IsAncestor(customDoc));
-// Assert.IsFalse(customDoc.IsAncestor(home));
-// Assert.IsFalse(customDoc.IsAncestor(root));
-//
-// Assert.IsFalse(customDoc2.IsAncestor(customDoc4));
-// Assert.IsFalse(customDoc2.IsAncestor(customDoc3));
-// Assert.IsFalse(customDoc2.IsAncestor(customDoc2));
-// Assert.IsFalse(customDoc2.IsAncestor(customDoc));
-// Assert.IsFalse(customDoc2.IsAncestor(home));
-// Assert.IsFalse(customDoc2.IsAncestor(root));
-//
-// Assert.IsFalse(customDoc3.IsAncestor(customDoc3));
-// }
-//
-// [Test]
-// public void IsAncestorOrSelf()
-// {
-// // Structure:
-// // - Root : 1046 (no parent)
-// // -- Home: 1173 (parent 1046)
-// // -- Custom Doc: 1178 (parent 1173)
-// // --- Custom Doc2: 1179 (parent: 1178)
-// // -- Custom Doc4: 117 (parent 1173)
-// // - Custom Doc3: 1172 (no parent)
-//
-// var home = GetNode(1173);
-// var root = GetNode(1046);
-// var customDoc = GetNode(1178);
-// var customDoc2 = GetNode(1179);
-// var customDoc3 = GetNode(1172);
-// var customDoc4 = GetNode(117);
-//
-// Assert.IsTrue(root.IsAncestorOrSelf(customDoc4));
-// Assert.IsFalse(root.IsAncestorOrSelf(customDoc3));
-// Assert.IsTrue(root.IsAncestorOrSelf(customDoc2));
-// Assert.IsTrue(root.IsAncestorOrSelf(customDoc));
-// Assert.IsTrue(root.IsAncestorOrSelf(home));
-// Assert.IsTrue(root.IsAncestorOrSelf(root));
-//
-// Assert.IsTrue(home.IsAncestorOrSelf(customDoc4));
-// Assert.IsFalse(home.IsAncestorOrSelf(customDoc3));
-// Assert.IsTrue(home.IsAncestorOrSelf(customDoc2));
-// Assert.IsTrue(home.IsAncestorOrSelf(customDoc));
-// Assert.IsTrue(home.IsAncestorOrSelf(home));
-// Assert.IsFalse(home.IsAncestorOrSelf(root));
-//
-// Assert.IsFalse(customDoc.IsAncestorOrSelf(customDoc4));
-// Assert.IsFalse(customDoc.IsAncestorOrSelf(customDoc3));
-// Assert.IsTrue(customDoc.IsAncestorOrSelf(customDoc2));
-// Assert.IsTrue(customDoc.IsAncestorOrSelf(customDoc));
-// Assert.IsFalse(customDoc.IsAncestorOrSelf(home));
-// Assert.IsFalse(customDoc.IsAncestorOrSelf(root));
-//
-// Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc4));
-// Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc3));
-// Assert.IsTrue(customDoc2.IsAncestorOrSelf(customDoc2));
-// Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc));
-// Assert.IsFalse(customDoc2.IsAncestorOrSelf(home));
-// Assert.IsFalse(customDoc2.IsAncestorOrSelf(root));
-//
-// Assert.IsTrue(customDoc4.IsAncestorOrSelf(customDoc4));
-// Assert.IsTrue(customDoc3.IsAncestorOrSelf(customDoc3));
-// }
-//
-//
-// [Test]
-// public void Descendants_Or_Self()
-// {
-// var doc = GetNode(1046);
-//
-// var result = doc.DescendantsOrSelf().ToArray();
-//
-// Assert.IsNotNull(result);
-//
-// Assert.AreEqual(10, result.Count());
-// Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 }));
-// }
-//
-// [Test]
-// public void Descendants()
-// {
-// var doc = GetNode(1046);
-//
-// var result = doc.Descendants().ToArray();
-//
-// Assert.IsNotNull(result);
-//
-// Assert.AreEqual(9, result.Count());
-// Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
-// }
-//
-// [Test]
-// public void IsDescendant()
-// {
-// // Structure:
-// // - Root : 1046 (no parent)
-// // -- Home: 1173 (parent 1046)
-// // -- Custom Doc: 1178 (parent 1173)
-// // --- Custom Doc2: 1179 (parent: 1178)
-// // -- Custom Doc4: 117 (parent 1173)
-// // - Custom Doc3: 1172 (no parent)
-//
-// var home = GetNode(1173);
-// var root = GetNode(1046);
-// var customDoc = GetNode(1178);
-// var customDoc2 = GetNode(1179);
-// var customDoc3 = GetNode(1172);
-// var customDoc4 = GetNode(117);
-//
-// Assert.IsFalse(root.IsDescendant(root));
-// Assert.IsFalse(root.IsDescendant(home));
-// Assert.IsFalse(root.IsDescendant(customDoc));
-// Assert.IsFalse(root.IsDescendant(customDoc2));
-// Assert.IsFalse(root.IsDescendant(customDoc3));
-// Assert.IsFalse(root.IsDescendant(customDoc4));
-//
-// Assert.IsTrue(home.IsDescendant(root));
-// Assert.IsFalse(home.IsDescendant(home));
-// Assert.IsFalse(home.IsDescendant(customDoc));
-// Assert.IsFalse(home.IsDescendant(customDoc2));
-// Assert.IsFalse(home.IsDescendant(customDoc3));
-// Assert.IsFalse(home.IsDescendant(customDoc4));
-//
-// Assert.IsTrue(customDoc.IsDescendant(root));
-// Assert.IsTrue(customDoc.IsDescendant(home));
-// Assert.IsFalse(customDoc.IsDescendant(customDoc));
-// Assert.IsFalse(customDoc.IsDescendant(customDoc2));
-// Assert.IsFalse(customDoc.IsDescendant(customDoc3));
-// Assert.IsFalse(customDoc.IsDescendant(customDoc4));
-//
-// Assert.IsTrue(customDoc2.IsDescendant(root));
-// Assert.IsTrue(customDoc2.IsDescendant(home));
-// Assert.IsTrue(customDoc2.IsDescendant(customDoc));
-// Assert.IsFalse(customDoc2.IsDescendant(customDoc2));
-// Assert.IsFalse(customDoc2.IsDescendant(customDoc3));
-// Assert.IsFalse(customDoc2.IsDescendant(customDoc4));
-//
-// Assert.IsFalse(customDoc3.IsDescendant(customDoc3));
-// }
-//
-// [Test]
-// public void IsDescendantOrSelf()
-// {
-// // Structure:
-// // - Root : 1046 (no parent)
-// // -- Home: 1173 (parent 1046)
-// // -- Custom Doc: 1178 (parent 1173)
-// // --- Custom Doc2: 1179 (parent: 1178)
-// // -- Custom Doc4: 117 (parent 1173)
-// // - Custom Doc3: 1172 (no parent)
-//
-// var home = GetNode(1173);
-// var root = GetNode(1046);
-// var customDoc = GetNode(1178);
-// var customDoc2 = GetNode(1179);
-// var customDoc3 = GetNode(1172);
-// var customDoc4 = GetNode(117);
-//
-// Assert.IsTrue(root.IsDescendantOrSelf(root));
-// Assert.IsFalse(root.IsDescendantOrSelf(home));
-// Assert.IsFalse(root.IsDescendantOrSelf(customDoc));
-// Assert.IsFalse(root.IsDescendantOrSelf(customDoc2));
-// Assert.IsFalse(root.IsDescendantOrSelf(customDoc3));
-// Assert.IsFalse(root.IsDescendantOrSelf(customDoc4));
-//
-// Assert.IsTrue(home.IsDescendantOrSelf(root));
-// Assert.IsTrue(home.IsDescendantOrSelf(home));
-// Assert.IsFalse(home.IsDescendantOrSelf(customDoc));
-// Assert.IsFalse(home.IsDescendantOrSelf(customDoc2));
-// Assert.IsFalse(home.IsDescendantOrSelf(customDoc3));
-// Assert.IsFalse(home.IsDescendantOrSelf(customDoc4));
-//
-// Assert.IsTrue(customDoc.IsDescendantOrSelf(root));
-// Assert.IsTrue(customDoc.IsDescendantOrSelf(home));
-// Assert.IsTrue(customDoc.IsDescendantOrSelf(customDoc));
-// Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc2));
-// Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc3));
-// Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc4));
-//
-// Assert.IsTrue(customDoc2.IsDescendantOrSelf(root));
-// Assert.IsTrue(customDoc2.IsDescendantOrSelf(home));
-// Assert.IsTrue(customDoc2.IsDescendantOrSelf(customDoc));
-// Assert.IsTrue(customDoc2.IsDescendantOrSelf(customDoc2));
-// Assert.IsFalse(customDoc2.IsDescendantOrSelf(customDoc3));
-// Assert.IsFalse(customDoc2.IsDescendantOrSelf(customDoc4));
-//
-// Assert.IsTrue(customDoc3.IsDescendantOrSelf(customDoc3));
-// }
-//
-// [Test]
-// public void Up()
-// {
-// var doc = GetNode(1173);
-//
-// var result = doc.Up();
-//
-// Assert.IsNotNull(result);
-//
-// Assert.AreEqual(1046, result.Id);
-// }
-//
-// [Test]
-// public void Down()
-// {
-// var doc = GetNode(1173);
-//
-// var result = doc.Down();
-//
-// Assert.IsNotNull(result);
-//
-// Assert.AreEqual(1174, result.Id);
-// }
-//
-// [Test]
-// public void FragmentProperty()
-// {
-// var factory = Factory.GetInstance() as PublishedContentTypeFactory;
-//
-// var pt = factory.CreatePropertyType("detached", 1003);
-// var ct = factory.CreateContentType(0, "alias", new[] { pt });
-// var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548);
-// Assert.IsInstanceOf(prop.GetValue());
-// Assert.AreEqual(5548, prop.GetValue());
-// }
-//
-// public void Fragment1()
-// {
-// var type = ContentTypesCache.Get(PublishedItemType.Content, "detachedSomething");
-// var values = new Dictionary();
-// var f = new PublishedElement(type, Guid.NewGuid(), values, false);
-// }
-//
-// [Test]
-// public void Fragment2()
-// {
-// var factory = Factory.GetInstance() as PublishedContentTypeFactory;
-//
-// var pt1 = factory.CreatePropertyType("legend", 1004);
-// var pt2 = factory.CreatePropertyType("image", 1005);
-// var pt3 = factory.CreatePropertyType("size", 1003);
-// const string val1 = "boom bam";
-// const int val2 = 0;
-// const int val3 = 666;
-//
-// var guid = Guid.NewGuid();
-//
-// var ct = factory.CreateContentType(0, "alias", new[] { pt1, pt2, pt3 });
-//
-// var c = new ImageWithLegendModel(ct, guid, new Dictionary
-// {
-// { "legend", val1 },
-// { "image", val2 },
-// { "size", val3 },
-// }, false);
-//
-// Assert.AreEqual(val1, c.Legend);
-// Assert.AreEqual(val3, c.Size);
-// }
-//
-// class ImageWithLegendModel : PublishedElement
-// {
-// public ImageWithLegendModel(PublishedContentType contentType, Guid fragmentKey, Dictionary values, bool previewing)
-// : base(contentType, fragmentKey, values, previewing)
-// { }
-//
-//
-// public string Legend => this.Value("legend");
-//
-// public IPublishedContent Image => this.Value("image");
-//
-// public int Size => this.Value("size");
-// }
-// }
-//}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.PropertyEditors;
+using Umbraco.Web;
+using Umbraco.Web.PublishedCache;
+using Umbraco.Core.Composing;
+using Moq;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Services;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Tests.Testing;
+using Umbraco.Web.Models.PublishedContent;
+using Umbraco.Web.PropertyEditors;
+
+namespace Umbraco.Tests.PublishedContent
+{
+ ///
+ /// Tests the methods on IPublishedContent using the DefaultPublishedContentStore
+ ///
+ [TestFixture]
+ [UmbracoTest(TypeLoader = UmbracoTestOptions.TypeLoader.PerFixture)]
+ public class PublishedContentTests : PublishedContentTestBase
+ {
+ protected override void Compose()
+ {
+ base.Compose();
+
+ Composition.RegisterUnique(f => new PublishedModelFactory(f.GetInstance().GetTypes()));
+ Composition.RegisterUnique();
+ Composition.RegisterUnique();
+
+ var logger = Mock.Of();
+ var dataTypeService = new TestObjects.TestDataTypeService(
+ new DataType(new VoidEditor(logger)) { Id = 1 },
+ new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 },
+ new DataType(new RichTextPropertyEditor(logger)) { Id = 1002 },
+ new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 },
+ new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 },
+ new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 });
+ Composition.RegisterUnique(f => dataTypeService);
+ }
+
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ var factory = Factory.GetInstance() as PublishedContentTypeFactory;
+
+ // need to specify a custom callback for unit tests
+ // AutoPublishedContentTypes generates properties automatically
+ // when they are requested, but we must declare those that we
+ // explicitely want to be here...
+
+ var propertyTypes = new[]
+ {
+ // AutoPublishedContentType will auto-generate other properties
+ factory.CreatePropertyType("umbracoNaviHide", 1001),
+ factory.CreatePropertyType("selectedNodes", 1),
+ factory.CreatePropertyType("umbracoUrlAlias", 1),
+ factory.CreatePropertyType("content", 1002),
+ factory.CreatePropertyType("testRecursive", 1),
+ };
+ var compositionAliases = new[] { "MyCompositionAlias" };
+ var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes);
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => type;
+ }
+
+ protected override TypeLoader CreateTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger)
+ {
+ var pluginManager = base.CreateTypeLoader(runtimeCache, globalSettings, logger);
+
+ // this is so the model factory looks into the test assembly
+ pluginManager.AssembliesToScan = pluginManager.AssembliesToScan
+ .Union(new[] { typeof(PublishedContentTests).Assembly })
+ .ToList();
+
+ return pluginManager;
+ }
+
+ private readonly Guid _node1173Guid = Guid.NewGuid();
+
+ protected override string GetXmlContent(int templateId)
+ {
+ return @"
+
+
+
+
+]>
+
+
+
+
+ 1
+
+
+ This is some content]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+";
+ }
+
+ internal IPublishedContent GetNode(int id)
+ {
+ var ctx = GetUmbracoContext("/test");
+ var doc = ctx.ContentCache.GetById(id);
+ Assert.IsNotNull(doc);
+ return doc;
+ }
+
+ [Test]
+ public void GetNodeByIds()
+ {
+ var ctx = GetUmbracoContext("/test");
+ var contentById = ctx.ContentCache.GetById(1173);
+ Assert.IsNotNull(contentById);
+ var contentByGuid = ctx.ContentCache.GetById(_node1173Guid);
+ Assert.IsNotNull(contentByGuid);
+ Assert.AreEqual(contentById.Id, contentByGuid.Id);
+ Assert.AreEqual(contentById.Key, contentByGuid.Key);
+
+ contentById = ctx.ContentCache.GetById(666);
+ Assert.IsNull(contentById);
+ contentByGuid = ctx.ContentCache.GetById(Guid.NewGuid());
+ Assert.IsNull(contentByGuid);
+ }
+
+ [Test]
+ public void Is_Last_From_Where_Filter_Dynamic_Linq()
+ {
+ var doc = GetNode(1173);
+
+ var items = doc.Children.Where(x => x.IsVisible()).ToIndexedArray();
+
+ foreach (var item in items)
+ {
+ if (item.Content.Id != 1178)
+ {
+ Assert.IsFalse(item.IsLast());
+ }
+ else
+ {
+ Assert.IsTrue(item.IsLast());
+ }
+ }
+ }
+
+ [Test]
+ public void Is_Last_From_Where_Filter()
+ {
+ var doc = GetNode(1173);
+
+ var items = doc
+ .Children
+ .Where(x => x.IsVisible())
+ .ToIndexedArray();
+
+ Assert.AreEqual(4, items.Length);
+
+ foreach (var d in items)
+ {
+ switch (d.Content.Id)
+ {
+ case 1174:
+ Assert.IsTrue(d.IsFirst());
+ Assert.IsFalse(d.IsLast());
+ break;
+ case 117:
+ Assert.IsFalse(d.IsFirst());
+ Assert.IsFalse(d.IsLast());
+ break;
+ case 1177:
+ Assert.IsFalse(d.IsFirst());
+ Assert.IsFalse(d.IsLast());
+ break;
+ case 1178:
+ Assert.IsFalse(d.IsFirst());
+ Assert.IsTrue(d.IsLast());
+ break;
+ default:
+ Assert.Fail("Invalid id.");
+ break;
+ }
+ }
+ }
+
+ [PublishedModel("Home")]
+ internal class Home : PublishedContentModel
+ {
+ public Home(IPublishedContent content)
+ : base(content)
+ {}
+ }
+
+ [PublishedModel("anything")]
+ internal class Anything : PublishedContentModel
+ {
+ public Anything(IPublishedContent content)
+ : base(content)
+ { }
+ }
+
+ [Test]
+ public void Is_Last_From_Where_Filter2()
+ {
+ var doc = GetNode(1173);
+
+ var items = doc.Children
+ .Select(x => x.CreateModel()) // linq, returns IEnumerable
+
+ // only way around this is to make sure every IEnumerable extension
+ // explicitely returns a PublishedContentSet, not an IEnumerable
+
+ .OfType() // ours, return IEnumerable (actually a PublishedContentSet)
+ .Where(x => x.IsVisible()) // so, here it's linq again :-(
+ .ToIndexedArray(); // so, we need that one for the test to pass
+
+ Assert.AreEqual(1, items.Length);
+
+ foreach (var d in items)
+ {
+ switch (d.Content.Id)
+ {
+ case 1174:
+ Assert.IsTrue(d.IsFirst());
+ Assert.IsTrue(d.IsLast());
+ break;
+ default:
+ Assert.Fail("Invalid id.");
+ break;
+ }
+ }
+ }
+
+ [Test]
+ public void Is_Last_From_Take()
+ {
+ var doc = GetNode(1173);
+
+ var items = doc.Children.Take(4).ToIndexedArray();
+
+ foreach (var item in items)
+ {
+ if (item.Content.Id != 1178)
+ {
+ Assert.IsFalse(item.IsLast());
+ }
+ else
+ {
+ Assert.IsTrue(item.IsLast());
+ }
+ }
+ }
+
+ [Test]
+ public void Is_Last_From_Skip()
+ {
+ var doc = GetNode(1173);
+
+ foreach (var d in doc.Children.Skip(1).ToIndexedArray())
+ {
+ if (d.Content.Id != 1176)
+ {
+ Assert.IsFalse(d.IsLast());
+ }
+ else
+ {
+ Assert.IsTrue(d.IsLast());
+ }
+ }
+ }
+
+ [Test]
+ public void Is_Last_From_Concat()
+ {
+ var doc = GetNode(1173);
+
+ var items = doc.Children
+ .Concat(new[] { GetNode(1175), GetNode(4444) })
+ .ToIndexedArray();
+
+ foreach (var item in items)
+ {
+ if (item.Content.Id != 4444)
+ {
+ Assert.IsFalse(item.IsLast());
+ }
+ else
+ {
+ Assert.IsTrue(item.IsLast());
+ }
+ }
+ }
+
+ [Test]
+ public void Descendants_Ordered_Properly()
+ {
+ var doc = GetNode(1046);
+
+ var expected = new[] { 1046, 1173, 1174, 117, 1177, 1178, 1179, 1176, 1175, 4444, 1172 };
+ var exindex = 0;
+
+ // must respect the XPath descendants-or-self axis!
+ foreach (var d in doc.DescendantsOrSelf())
+ Assert.AreEqual(expected[exindex++], d.Id);
+ }
+
+ [Test]
+ public void Get_Property_Value_Recursive()
+ {
+ var doc = GetNode(1174);
+ var rVal = doc.Value("testRecursive", fallback: Fallback.ToAncestors);
+ var nullVal = doc.Value("DoNotFindThis", fallback: Fallback.ToAncestors);
+ Assert.AreEqual("This is the recursive val", rVal);
+ Assert.AreEqual(null, nullVal);
+ }
+
+ [Test]
+ public void Get_Property_Value_Uses_Converter()
+ {
+ var doc = GetNode(1173);
+
+ var propVal = doc.Value("content");
+ Assert.IsInstanceOf(typeof(IHtmlString), propVal);
+ Assert.AreEqual("This is some content
", propVal.ToString());
+
+ var propVal2 = doc.Value("content");
+ Assert.IsInstanceOf(typeof(IHtmlString), propVal2);
+ Assert.AreEqual("This is some content
", propVal2.ToString());
+
+ var propVal3 = doc.Value("Content");
+ Assert.IsInstanceOf(typeof(IHtmlString), propVal3);
+ Assert.AreEqual("This is some content
", propVal3.ToString());
+ }
+
+ [Test]
+ public void Complex_Linq()
+ {
+ var doc = GetNode(1173);
+
+ var result = doc.Ancestors().OrderBy(x => x.Level)
+ .Single()
+ .Descendants()
+ .FirstOrDefault(x => x.Value("selectedNodes", defaultValue: "").Split(',').Contains("1173"));
+
+ Assert.IsNotNull(result);
+ }
+
+ [Test]
+ public void Children_GroupBy_DocumentTypeAlias()
+ {
+ var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { });
+ var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { });
+ var contentTypes = new Dictionary
+ {
+ { home.Alias, home },
+ { custom.Alias, custom }
+ };
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias];
+
+ var doc = GetNode(1046);
+
+ var found1 = doc.Children.GroupBy(x => x.ContentType.Alias).ToArray();
+
+ Assert.AreEqual(2, found1.Length);
+ Assert.AreEqual(2, found1.Single(x => x.Key.ToString() == "Home").Count());
+ Assert.AreEqual(1, found1.Single(x => x.Key.ToString() == "CustomDocument").Count());
+ }
+
+ [Test]
+ public void Children_Where_DocumentTypeAlias()
+ {
+ var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { });
+ var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { });
+ var contentTypes = new Dictionary
+ {
+ { home.Alias, home },
+ { custom.Alias, custom }
+ };
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias];
+
+ var doc = GetNode(1046);
+
+ var found1 = doc.Children.Where(x => x.ContentType.Alias == "CustomDocument");
+ var found2 = doc.Children.Where(x => x.ContentType.Alias == "Home");
+
+ Assert.AreEqual(1, found1.Count());
+ Assert.AreEqual(2, found2.Count());
+ }
+
+ [Test]
+ public void Children_Order_By_Update_Date()
+ {
+ var doc = GetNode(1173);
+
+ var ordered = doc.Children.OrderBy(x => x.UpdateDate);
+
+ var correctOrder = new[] { 1178, 1177, 1174, 1176 };
+ for (var i = 0; i < correctOrder.Length; i++)
+ {
+ Assert.AreEqual(correctOrder[i], ordered.ElementAt(i).Id);
+ }
+
+ }
+
+ [Test]
+ public void FirstChild()
+ {
+ var doc = GetNode(1173); // has child nodes
+ Assert.IsNotNull(doc.FirstChild());
+ Assert.IsNotNull(doc.FirstChild(x => true));
+ Assert.IsNotNull(doc.FirstChild());
+
+ doc = GetNode(1175); // does not have child nodes
+ Assert.IsNull(doc.FirstChild());
+ Assert.IsNull(doc.FirstChild(x => true));
+ Assert.IsNull(doc.FirstChild());
+ }
+
+ [Test]
+ public void FirstChildAsT()
+ {
+ var doc = GetNode(1046); // has child nodes
+
+ var model = doc.FirstChild(x => true); // predicate
+
+ Assert.IsNotNull(model);
+ Assert.IsTrue(model.Id == 1173);
+ Assert.IsInstanceOf(model);
+ Assert.IsInstanceOf(model);
+
+ doc = GetNode(1175); // does not have child nodes
+ Assert.IsNull(doc.FirstChild());
+ Assert.IsNull(doc.FirstChild(x => true));
+ }
+
+ [Test]
+ public void IsComposedOf()
+ {
+ var doc = GetNode(1173);
+
+ var isComposedOf = doc.IsComposedOf("MyCompositionAlias");
+
+ Assert.IsTrue(isComposedOf);
+ }
+
+ [Test]
+ public void HasProperty()
+ {
+ var doc = GetNode(1173);
+
+ var hasProp = doc.HasProperty(Constants.Conventions.Content.UrlAlias);
+
+ Assert.IsTrue(hasProp);
+ }
+
+ [Test]
+ public void HasValue()
+ {
+ var doc = GetNode(1173);
+
+ var hasValue = doc.HasValue(Constants.Conventions.Content.UrlAlias);
+ var noValue = doc.HasValue("blahblahblah");
+
+ Assert.IsTrue(hasValue);
+ Assert.IsFalse(noValue);
+ }
+
+ [Test]
+ public void Ancestors_Where_Visible()
+ {
+ var doc = GetNode(1174);
+
+ var whereVisible = doc.Ancestors().Where(x => x.IsVisible());
+ Assert.AreEqual(1, whereVisible.Count());
+
+ }
+
+ [Test]
+ public void Visible()
+ {
+ var hidden = GetNode(1046);
+ var visible = GetNode(1173);
+
+ Assert.IsFalse(hidden.IsVisible());
+ Assert.IsTrue(visible.IsVisible());
+ }
+
+ [Test]
+ public void Ancestor_Or_Self()
+ {
+ var doc = GetNode(1173);
+
+ var result = doc.AncestorOrSelf();
+
+ Assert.IsNotNull(result);
+
+ // ancestor-or-self has to be self!
+ Assert.AreEqual(1173, result.Id);
+ }
+
+ [Test]
+ public void U4_4559()
+ {
+ var doc = GetNode(1174);
+ var result = doc.AncestorOrSelf(1);
+ Assert.IsNotNull(result);
+ Assert.AreEqual(1046, result.Id);
+ }
+
+ [Test]
+ public void Ancestors_Or_Self()
+ {
+ var doc = GetNode(1174);
+
+ var result = doc.AncestorsOrSelf().ToArray();
+
+ Assert.IsNotNull(result);
+
+ Assert.AreEqual(3, result.Length);
+ Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
+ }
+
+ [Test]
+ public void Ancestors()
+ {
+ var doc = GetNode(1174);
+
+ var result = doc.Ancestors().ToArray();
+
+ Assert.IsNotNull(result);
+
+ Assert.AreEqual(2, result.Length);
+ Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
+ }
+
+ [Test]
+ public void IsAncestor()
+ {
+ // Structure:
+ // - Root : 1046 (no parent)
+ // -- Home: 1173 (parent 1046)
+ // -- Custom Doc: 1178 (parent 1173)
+ // --- Custom Doc2: 1179 (parent: 1178)
+ // -- Custom Doc4: 117 (parent 1173)
+ // - Custom Doc3: 1172 (no parent)
+
+ var home = GetNode(1173);
+ var root = GetNode(1046);
+ var customDoc = GetNode(1178);
+ var customDoc2 = GetNode(1179);
+ var customDoc3 = GetNode(1172);
+ var customDoc4 = GetNode(117);
+
+ Assert.IsTrue(root.IsAncestor(customDoc4));
+ Assert.IsFalse(root.IsAncestor(customDoc3));
+ Assert.IsTrue(root.IsAncestor(customDoc2));
+ Assert.IsTrue(root.IsAncestor(customDoc));
+ Assert.IsTrue(root.IsAncestor(home));
+ Assert.IsFalse(root.IsAncestor(root));
+
+ Assert.IsTrue(home.IsAncestor(customDoc4));
+ Assert.IsFalse(home.IsAncestor(customDoc3));
+ Assert.IsTrue(home.IsAncestor(customDoc2));
+ Assert.IsTrue(home.IsAncestor(customDoc));
+ Assert.IsFalse(home.IsAncestor(home));
+ Assert.IsFalse(home.IsAncestor(root));
+
+ Assert.IsFalse(customDoc.IsAncestor(customDoc4));
+ Assert.IsFalse(customDoc.IsAncestor(customDoc3));
+ Assert.IsTrue(customDoc.IsAncestor(customDoc2));
+ Assert.IsFalse(customDoc.IsAncestor(customDoc));
+ Assert.IsFalse(customDoc.IsAncestor(home));
+ Assert.IsFalse(customDoc.IsAncestor(root));
+
+ Assert.IsFalse(customDoc2.IsAncestor(customDoc4));
+ Assert.IsFalse(customDoc2.IsAncestor(customDoc3));
+ Assert.IsFalse(customDoc2.IsAncestor(customDoc2));
+ Assert.IsFalse(customDoc2.IsAncestor(customDoc));
+ Assert.IsFalse(customDoc2.IsAncestor(home));
+ Assert.IsFalse(customDoc2.IsAncestor(root));
+
+ Assert.IsFalse(customDoc3.IsAncestor(customDoc3));
+ }
+
+ [Test]
+ public void IsAncestorOrSelf()
+ {
+ // Structure:
+ // - Root : 1046 (no parent)
+ // -- Home: 1173 (parent 1046)
+ // -- Custom Doc: 1178 (parent 1173)
+ // --- Custom Doc2: 1179 (parent: 1178)
+ // -- Custom Doc4: 117 (parent 1173)
+ // - Custom Doc3: 1172 (no parent)
+
+ var home = GetNode(1173);
+ var root = GetNode(1046);
+ var customDoc = GetNode(1178);
+ var customDoc2 = GetNode(1179);
+ var customDoc3 = GetNode(1172);
+ var customDoc4 = GetNode(117);
+
+ Assert.IsTrue(root.IsAncestorOrSelf(customDoc4));
+ Assert.IsFalse(root.IsAncestorOrSelf(customDoc3));
+ Assert.IsTrue(root.IsAncestorOrSelf(customDoc2));
+ Assert.IsTrue(root.IsAncestorOrSelf(customDoc));
+ Assert.IsTrue(root.IsAncestorOrSelf(home));
+ Assert.IsTrue(root.IsAncestorOrSelf(root));
+
+ Assert.IsTrue(home.IsAncestorOrSelf(customDoc4));
+ Assert.IsFalse(home.IsAncestorOrSelf(customDoc3));
+ Assert.IsTrue(home.IsAncestorOrSelf(customDoc2));
+ Assert.IsTrue(home.IsAncestorOrSelf(customDoc));
+ Assert.IsTrue(home.IsAncestorOrSelf(home));
+ Assert.IsFalse(home.IsAncestorOrSelf(root));
+
+ Assert.IsFalse(customDoc.IsAncestorOrSelf(customDoc4));
+ Assert.IsFalse(customDoc.IsAncestorOrSelf(customDoc3));
+ Assert.IsTrue(customDoc.IsAncestorOrSelf(customDoc2));
+ Assert.IsTrue(customDoc.IsAncestorOrSelf(customDoc));
+ Assert.IsFalse(customDoc.IsAncestorOrSelf(home));
+ Assert.IsFalse(customDoc.IsAncestorOrSelf(root));
+
+ Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc4));
+ Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc3));
+ Assert.IsTrue(customDoc2.IsAncestorOrSelf(customDoc2));
+ Assert.IsFalse(customDoc2.IsAncestorOrSelf(customDoc));
+ Assert.IsFalse(customDoc2.IsAncestorOrSelf(home));
+ Assert.IsFalse(customDoc2.IsAncestorOrSelf(root));
+
+ Assert.IsTrue(customDoc4.IsAncestorOrSelf(customDoc4));
+ Assert.IsTrue(customDoc3.IsAncestorOrSelf(customDoc3));
+ }
+
+
+ [Test]
+ public void Descendants_Or_Self()
+ {
+ var doc = GetNode(1046);
+
+ var result = doc.DescendantsOrSelf().ToArray();
+
+ Assert.IsNotNull(result);
+
+ Assert.AreEqual(10, result.Count());
+ Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 }));
+ }
+
+ [Test]
+ public void Descendants()
+ {
+ var doc = GetNode(1046);
+
+ var result = doc.Descendants().ToArray();
+
+ Assert.IsNotNull(result);
+
+ Assert.AreEqual(9, result.Count());
+ Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
+ }
+
+ [Test]
+ public void IsDescendant()
+ {
+ // Structure:
+ // - Root : 1046 (no parent)
+ // -- Home: 1173 (parent 1046)
+ // -- Custom Doc: 1178 (parent 1173)
+ // --- Custom Doc2: 1179 (parent: 1178)
+ // -- Custom Doc4: 117 (parent 1173)
+ // - Custom Doc3: 1172 (no parent)
+
+ var home = GetNode(1173);
+ var root = GetNode(1046);
+ var customDoc = GetNode(1178);
+ var customDoc2 = GetNode(1179);
+ var customDoc3 = GetNode(1172);
+ var customDoc4 = GetNode(117);
+
+ Assert.IsFalse(root.IsDescendant(root));
+ Assert.IsFalse(root.IsDescendant(home));
+ Assert.IsFalse(root.IsDescendant(customDoc));
+ Assert.IsFalse(root.IsDescendant(customDoc2));
+ Assert.IsFalse(root.IsDescendant(customDoc3));
+ Assert.IsFalse(root.IsDescendant(customDoc4));
+
+ Assert.IsTrue(home.IsDescendant(root));
+ Assert.IsFalse(home.IsDescendant(home));
+ Assert.IsFalse(home.IsDescendant(customDoc));
+ Assert.IsFalse(home.IsDescendant(customDoc2));
+ Assert.IsFalse(home.IsDescendant(customDoc3));
+ Assert.IsFalse(home.IsDescendant(customDoc4));
+
+ Assert.IsTrue(customDoc.IsDescendant(root));
+ Assert.IsTrue(customDoc.IsDescendant(home));
+ Assert.IsFalse(customDoc.IsDescendant(customDoc));
+ Assert.IsFalse(customDoc.IsDescendant(customDoc2));
+ Assert.IsFalse(customDoc.IsDescendant(customDoc3));
+ Assert.IsFalse(customDoc.IsDescendant(customDoc4));
+
+ Assert.IsTrue(customDoc2.IsDescendant(root));
+ Assert.IsTrue(customDoc2.IsDescendant(home));
+ Assert.IsTrue(customDoc2.IsDescendant(customDoc));
+ Assert.IsFalse(customDoc2.IsDescendant(customDoc2));
+ Assert.IsFalse(customDoc2.IsDescendant(customDoc3));
+ Assert.IsFalse(customDoc2.IsDescendant(customDoc4));
+
+ Assert.IsFalse(customDoc3.IsDescendant(customDoc3));
+ }
+
+ [Test]
+ public void IsDescendantOrSelf()
+ {
+ // Structure:
+ // - Root : 1046 (no parent)
+ // -- Home: 1173 (parent 1046)
+ // -- Custom Doc: 1178 (parent 1173)
+ // --- Custom Doc2: 1179 (parent: 1178)
+ // -- Custom Doc4: 117 (parent 1173)
+ // - Custom Doc3: 1172 (no parent)
+
+ var home = GetNode(1173);
+ var root = GetNode(1046);
+ var customDoc = GetNode(1178);
+ var customDoc2 = GetNode(1179);
+ var customDoc3 = GetNode(1172);
+ var customDoc4 = GetNode(117);
+
+ Assert.IsTrue(root.IsDescendantOrSelf(root));
+ Assert.IsFalse(root.IsDescendantOrSelf(home));
+ Assert.IsFalse(root.IsDescendantOrSelf(customDoc));
+ Assert.IsFalse(root.IsDescendantOrSelf(customDoc2));
+ Assert.IsFalse(root.IsDescendantOrSelf(customDoc3));
+ Assert.IsFalse(root.IsDescendantOrSelf(customDoc4));
+
+ Assert.IsTrue(home.IsDescendantOrSelf(root));
+ Assert.IsTrue(home.IsDescendantOrSelf(home));
+ Assert.IsFalse(home.IsDescendantOrSelf(customDoc));
+ Assert.IsFalse(home.IsDescendantOrSelf(customDoc2));
+ Assert.IsFalse(home.IsDescendantOrSelf(customDoc3));
+ Assert.IsFalse(home.IsDescendantOrSelf(customDoc4));
+
+ Assert.IsTrue(customDoc.IsDescendantOrSelf(root));
+ Assert.IsTrue(customDoc.IsDescendantOrSelf(home));
+ Assert.IsTrue(customDoc.IsDescendantOrSelf(customDoc));
+ Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc2));
+ Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc3));
+ Assert.IsFalse(customDoc.IsDescendantOrSelf(customDoc4));
+
+ Assert.IsTrue(customDoc2.IsDescendantOrSelf(root));
+ Assert.IsTrue(customDoc2.IsDescendantOrSelf(home));
+ Assert.IsTrue(customDoc2.IsDescendantOrSelf(customDoc));
+ Assert.IsTrue(customDoc2.IsDescendantOrSelf(customDoc2));
+ Assert.IsFalse(customDoc2.IsDescendantOrSelf(customDoc3));
+ Assert.IsFalse(customDoc2.IsDescendantOrSelf(customDoc4));
+
+ Assert.IsTrue(customDoc3.IsDescendantOrSelf(customDoc3));
+ }
+
+ [Test]
+ public void Up()
+ {
+ var doc = GetNode(1173);
+
+ var result = doc.Up();
+
+ Assert.IsNotNull(result);
+
+ Assert.AreEqual(1046, result.Id);
+ }
+
+ [Test]
+ public void Down()
+ {
+ var doc = GetNode(1173);
+
+ var result = doc.Down();
+
+ Assert.IsNotNull(result);
+
+ Assert.AreEqual(1174, result.Id);
+ }
+
+ [Test]
+ public void FragmentProperty()
+ {
+ var factory = Factory.GetInstance() as PublishedContentTypeFactory;
+
+ var pt = factory.CreatePropertyType("detached", 1003);
+ var ct = factory.CreateContentType(0, "alias", new[] { pt });
+ var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548);
+ Assert.IsInstanceOf(prop.GetValue());
+ Assert.AreEqual(5548, prop.GetValue());
+ }
+
+ public void Fragment1()
+ {
+ var type = ContentTypesCache.Get(PublishedItemType.Content, "detachedSomething");
+ var values = new Dictionary();
+ var f = new PublishedElement(type, Guid.NewGuid(), values, false);
+ }
+
+ [Test]
+ public void Fragment2()
+ {
+ var factory = Factory.GetInstance() as PublishedContentTypeFactory;
+
+ var pt1 = factory.CreatePropertyType("legend", 1004);
+ var pt2 = factory.CreatePropertyType("image", 1005);
+ var pt3 = factory.CreatePropertyType("size", 1003);
+ const string val1 = "boom bam";
+ const int val2 = 0;
+ const int val3 = 666;
+
+ var guid = Guid.NewGuid();
+
+ var ct = factory.CreateContentType(0, "alias", new[] { pt1, pt2, pt3 });
+
+ var c = new ImageWithLegendModel(ct, guid, new Dictionary
+ {
+ { "legend", val1 },
+ { "image", val2 },
+ { "size", val3 },
+ }, false);
+
+ Assert.AreEqual(val1, c.Legend);
+ Assert.AreEqual(val3, c.Size);
+ }
+
+ class ImageWithLegendModel : PublishedElement
+ {
+ public ImageWithLegendModel(PublishedContentType contentType, Guid fragmentKey, Dictionary values, bool previewing)
+ : base(contentType, fragmentKey, values, previewing)
+ { }
+
+
+ public string Legend => this.Value("legend");
+
+ public IPublishedContent Image => this.Value("image");
+
+ public int Size => this.Value("size");
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
index b1a8fa26a8..8dc8a2b45c 100644
--- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs
@@ -22,6 +22,36 @@ namespace Umbraco.Tests.Services
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)]
public class ContentTypeServiceTests : TestWithSomeContentBase
{
+ [Test]
+ public void CanSaveAndGetIsElement()
+ {
+ //create content type with a property type that varies by culture
+ IContentType contentType = MockedContentTypes.CreateBasicContentType();
+ contentType.Variations = ContentVariation.Nothing;
+ var contentCollection = new PropertyTypeCollection(true);
+ contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
+ {
+ Alias = "title",
+ Name = "Title",
+ Description = "",
+ Mandatory = false,
+ SortOrder = 1,
+ DataTypeId = -88,
+ Variations = ContentVariation.Nothing
+ });
+ contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
+ ServiceContext.ContentTypeService.Save(contentType);
+
+ contentType = ServiceContext.ContentTypeService.Get(contentType.Id);
+ Assert.IsFalse(contentType.IsElement);
+
+ contentType.IsElement = true;
+ ServiceContext.ContentTypeService.Save(contentType);
+
+ contentType = ServiceContext.ContentTypeService.Get(contentType.Id);
+ Assert.IsTrue(contentType.IsElement);
+ }
+
[Test]
public void Change_Content_Type_Variation_Clears_Redirects()
{
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js
index 8bad5ae8fd..3d784c999f 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js
@@ -130,8 +130,7 @@
if (!changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) {
configureViewModel();
- //this is required to re-validate
- vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length);
+ reValidate()
}
}
@@ -182,6 +181,8 @@
else {
vm.onValueChanged({ value: [] });
}
+
+ reValidate();
}
/**
@@ -189,7 +190,7 @@
*/
function validateMandatory() {
return {
- isValid: !vm.validation.mandatory || (vm.viewModel != null && vm.viewModel.length > 0),
+ isValid: !vm.validation.mandatory || (vm.viewModel != null && vm.viewModel.length > 0)|| (vm.value != null && vm.value.length > 0),
errorMsg: "Value cannot be empty",
errorKey: "required"
};
@@ -271,6 +272,10 @@
});
}
+ function reValidate() {
+ //this is required to re-validate
+ vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length);
+ }
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js
index e31742e660..1e6fc5d643 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js
@@ -64,7 +64,7 @@
var saveModel = _.pick(displayModel,
'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes',
'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed',
- 'key', 'parentId', 'alias', 'path', 'allowCultureVariant');
+ 'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'isElement');
//TODO: Map these
saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; });
@@ -262,7 +262,7 @@
saveModel[props[m]] = startId.id;
}
- saveModel.parentId = -1;
+ saveModel.parentId = -1;
return saveModel;
},
@@ -293,7 +293,7 @@
});
saveModel.email = propEmail.value.trim();
saveModel.username = propLogin.value.trim();
-
+
saveModel.password = this.formatChangePasswordModel(propPass.value);
var selectedGroups = [];
@@ -336,7 +336,7 @@
/** formats the display model used to display the media to the model used to save the media */
formatMediaPostData: function (displayModel, action) {
- //NOTE: the display model inherits from the save model so we can in theory just post up the display model but
+ //NOTE: the display model inherits from the save model so we can in theory just post up the display model but
// we don't want to post all of the data as it is unecessary.
var saveModel = {
id: displayModel.id,
@@ -354,7 +354,7 @@
/** formats the display model used to display the content to the model used to save the content */
formatContentPostData: function (displayModel, action) {
- //NOTE: the display model inherits from the save model so we can in theory just post up the display model but
+ //NOTE: the display model inherits from the save model so we can in theory just post up the display model but
// we don't want to post all of the data as it is unecessary.
var saveModel = {
id: displayModel.id,
@@ -379,7 +379,7 @@
var propExpireDate = displayModel.removeDate;
var propReleaseDate = displayModel.releaseDate;
var propTemplate = displayModel.template;
-
+
saveModel.expireDate = propExpireDate ? propExpireDate : null;
saveModel.releaseDate = propReleaseDate ? propReleaseDate : null;
saveModel.templateAlias = propTemplate ? propTemplate : null;
@@ -389,8 +389,8 @@
/**
* This formats the server GET response for a content display item
- * @param {} displayModel
- * @returns {}
+ * @param {} displayModel
+ * @returns {}
*/
formatContentGetData: function(displayModel) {
@@ -418,7 +418,7 @@
}
});
});
-
+
//now assign this same invariant property instance to the same index of the other variants property array
for (var j = 1; j < displayModel.variants.length; j++) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js
index 4a7a870618..317fe094ae 100644
--- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js
@@ -25,6 +25,7 @@
vm.removeChild = removeChild;
vm.toggleAllowAsRoot = toggleAllowAsRoot;
vm.toggleAllowCultureVariants = toggleAllowCultureVariants;
+ vm.toggleIsElement = toggleIsElement;
/* ---------- INIT ---------- */
@@ -84,25 +85,18 @@
$scope.model.allowedContentTypes.splice(selectedChildIndex, 1);
}
- /**
- * Toggle the $scope.model.allowAsRoot value to either true or false
- */
- function toggleAllowAsRoot(){
- if($scope.model.allowAsRoot){
- $scope.model.allowAsRoot = false;
- return;
- }
+ // note: "safe toggling" here ie handling cases where the value is undefined, etc
- $scope.model.allowAsRoot = true;
+ function toggleAllowAsRoot() {
+ $scope.model.allowAsRoot = $scope.model.allowAsRoot ? false : true;
}
function toggleAllowCultureVariants() {
- if ($scope.model.allowCultureVariant) {
- $scope.model.allowCultureVariant = false;
- return;
- }
+ $scope.model.allowCultureVariant = $scope.model.allowCultureVariant ? false : true;
+ }
- $scope.model.allowCultureVariant = true;
+ function toggleIsElement() {
+ $scope.model.isElement = $scope.model.isElement ? false : true;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html
index ec1e528f8c..0d74c655d7 100644
--- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html
+++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html
@@ -53,9 +53,25 @@
hotkey="alt+shift+v">
-
+
-
+
+
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 7a59b15eb3..ca4acd9d66 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -107,7 +107,7 @@
- 8.0.0-alpha.31
+ 8.0.0-alpha.32
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 386d3af518..987203442a 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -1518,6 +1518,8 @@ To manage your website, simply open the Umbraco back office and start adding con
Allow varying by culture
Allow editors to create content of this type in different languages
Allow varying by culture
+ Is an Element type
+ An Element type is meant to be used for instance in Nested Content, and not in the tree
Building models
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index 5de373f571..1860d3afc9 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -1559,6 +1559,8 @@ To manage your website, simply open the Umbraco back office and start adding con
Allow varying by culture
Allow editors to create content of this type in different languages
Allow varying by culture
+ Is an Element type
+ An Element type is meant to be used for instance in Nested Content, and not in the tree
Add language
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs
index 4908025351..80358bfc7a 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs
@@ -86,6 +86,12 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "isContainer")]
public bool IsContainer { get; set; }
+ ///
+ /// Indicates if the content is configured as an element
+ ///
+ [DataMember(Name = "isElement")]
+ public bool IsElement { get; set; }
+
///
/// Property indicating if this item is part of a list view parent
///
@@ -117,7 +123,7 @@ namespace Umbraco.Web.Models.ContentEditing
///
[DataMember(Name = "updateDate")]
public DateTime UpdateDate { get; set; }
-
+
[DataMember(Name = "template")]
public string TemplateAlias { get; set; }
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs
index 7211ddbf61..e5e74c2749 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs
@@ -26,6 +26,10 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "isContainer")]
public bool IsContainer { get; set; }
+ //Element
+ [DataMember(Name = "isElement")]
+ public bool IsElement { get; set; }
+
[DataMember(Name = "listViewEditorName")]
[ReadOnly(true)]
public string ListViewEditorName { get; set; }
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs
index c2ec70d3dc..b1d24c5fd2 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs
@@ -25,6 +25,9 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "isContainer")]
public bool IsContainer { get; set; }
+ [DataMember(Name = "isElement")]
+ public bool IsElement { get; set; }
+
[DataMember(Name = "allowAsRoot")]
public bool AllowAsRoot { get; set; }
diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs
index 6de3bdc02c..1caf81a1eb 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs
@@ -46,6 +46,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
.ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name))
.ForMember(dest => dest.IsContainer, opt => opt.MapFrom(src => src.ContentType.IsContainer))
+ .ForMember(dest => dest.IsElement, opt => opt.MapFrom(src => src.ContentType.IsElement))
.ForMember(dest => dest.IsBlueprint, opt => opt.MapFrom(src => src.Blueprint))
.ForMember(dest => dest.IsChildOfListView, opt => opt.ResolveUsing(childOfListViewResolver))
.ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed))
@@ -59,7 +60,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.AllowedTemplates, opt =>
opt.MapFrom(content => content.ContentType.AllowedTemplates
.Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)
- .ToDictionary(t => t.Alias, t => t.Name)))
+ .ToDictionary(t => t.Alias, t => t.Name)))
.ForMember(dest => dest.AllowedActions, opt => opt.ResolveUsing(src => actionButtonsResolver.Resolve(src)))
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
@@ -140,5 +141,5 @@ namespace Umbraco.Web.Models.Mapping
return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"(({source.Name}))";
}
}
- }
+ }
}