diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs
index f75e59bee0..23f5bbff8d 100644
--- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs
@@ -6,36 +6,119 @@ namespace Umbraco.Core.Models.PublishedContent
///
///
- /// Represents a cached content.
+ /// Represents a published content item.
///
+ ///
+ /// Can be a published document, media or member.
+ ///
public interface IPublishedContent : IPublishedElement
{
#region Content
- // fixme - all these are colliding with models => ?
- // or could we force them to be 'new' in models?
-
- int Id { get; }
- int TemplateId { get; }
- int SortOrder { get; }
- string Name { get; }
- string UrlName { get; } // fixme rename
- string DocumentTypeAlias { get; } // fixme obsolete
- int DocumentTypeId { get; } // fixme obsolete
- string WriterName { get; }
- string CreatorName { get; }
- int WriterId { get; }
- int CreatorId { get; }
- string Path { get; }
- DateTime CreateDate { get; }
- DateTime UpdateDate { get; }
- int Level { get; }
- string Url { get; }
-
- IReadOnlyDictionary CultureNames { get; }
+ // todo - IPublishedContent properties colliding with models
+ // we need to find a way to remove as much clutter as possible from IPublishedContent,
+ // since this is preventing someone from creating a property named 'Path' and have it
+ // in a model, for instance. we could move them all under one unique property eg
+ // Infos, so we would do .Infos.SortOrder - just an idea - not going to do it in v8
///
- /// Gets a value indicating whether the content is a content (aka a document) or a media.
+ /// Gets the unique identifier of the content item.
+ ///
+ int Id { get; }
+
+ ///
+ /// Gets the name of the content item.
+ ///
+ ///
+ /// The value of this property is contextual. When the content type is multi-lingual,
+ /// this is the name for the 'current' culture.
+ ///
+ /// FIXME culture aware - returns the value for the 'current' culture whatever it is + see ?? for others
+ string Name { get; }
+
+ ///
+ /// Gets the url segment of the content item.
+ ///
+ ///
+ /// The value of this property is contextual. When the content type is multi-lingual,
+ /// this is the name for the 'current' culture.
+ ///
+ /// FIXME rename UrlSegment + culture aware
+ string UrlName { get; } // fixme rename, segment!
+
+ ///
+ /// Gets the sort order of the content item.
+ ///
+ int SortOrder { get; }
+
+ ///
+ /// Gets the tree level of the content item.
+ ///
+ int Level { get; }
+
+ ///
+ /// Gets the tree path of the content item.
+ ///
+ string Path { get; }
+
+ ///
+ /// Gets the identifier of the template to use to render the content item.
+ ///
+ int TemplateId { get; }
+
+ ///
+ /// Gets the identifier of the user who created the content item.
+ ///
+ int CreatorId { get; }
+
+ ///
+ /// Gets the name of the user who created the content item.
+ ///
+ string CreatorName { get; }
+
+ ///
+ /// Gets the date the content item was created.
+ ///
+ DateTime CreateDate { get; }
+
+ ///
+ /// Gets the identifier of the user who last updated the content item.
+ ///
+ int WriterId { get; }
+
+ ///
+ /// Gets the name of the user who last updated the content item.
+ ///
+ string WriterName { get; }
+
+ ///
+ /// Gets the date the content item was last updated.
+ ///
+ ///
+ /// For published content items, this is also the date the item was published.
+ /// This date is global to the content item, see FIXME for per-culture dates
+ ///
+ DateTime UpdateDate { get; }
+
+ ///
+ /// Gets the url of the content item.
+ ///
+ ///
+ /// The value of this property is contextual. It depends on the 'current'
+ /// In addition, when the content type is multi-lingual, this is the url for the
+ /// 'current' culture.
+ ///
+ /// FIXME explain what 'current' means here
+ string Url { get; }
+
+ // fixme document
+ //PublishedCultureInfos Culture(string culture = ".");
+ //string GetName(string culture = "."); // best naming? GetName? CultureName?
+ PublishedCultureInfos GetCulture(string culture = ".");
+ IReadOnlyDictionary Cultures { get; }
+
+ ///
+ /// Gets the type of the content item (document, media...).
///
PublishedItemType ItemType { get; }
@@ -51,13 +134,13 @@ namespace Umbraco.Core.Models.PublishedContent
#region Tree
///
- /// Gets the parent of the content.
+ /// Gets the parent of the content item.
///
/// The parent of root content is null.
IPublishedContent Parent { get; }
///
- /// Gets the children of the content.
+ /// Gets the children of the content item.
///
/// Children are sorted by their sortOrder.
IEnumerable Children { get; }
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs
index 6cd4818587..4b579d824b 100644
--- a/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedElement.cs
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.PublishedContent
#region PublishedElement
///
- /// Gets the unique key of the published snapshot item.
+ /// Gets the unique key of the published element.
///
Guid Key { get; }
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
index 528a545f8f..f8a4e16bd2 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
@@ -39,72 +39,99 @@ namespace Umbraco.Core.Models.PublishedContent
public IPublishedContent Unwrap() => _content;
#region ContentType
-
+
+ ///
public virtual PublishedContentType ContentType => _content.ContentType;
#endregion
+
+ #region PublishedElement
- #region Content
-
- public virtual int Id => _content.Id;
-
+ ///
public Guid Key => _content.Key;
- public virtual int TemplateId => _content.TemplateId;
+ #endregion
- public virtual int SortOrder => _content.SortOrder;
+ #region PublishedContent
- public virtual string Name => _content.Name;
-
- public virtual IReadOnlyDictionary CultureNames => _content.CultureNames;
+ ///
+ public virtual int Id => _content.Id;
+ ///
+ public virtual string Name => _content.Name;
+
+ ///
public virtual string UrlName => _content.UrlName;
- public virtual string DocumentTypeAlias => _content.DocumentTypeAlias;
-
- public virtual int DocumentTypeId => _content.DocumentTypeId;
-
- public virtual string WriterName => _content.WriterName;
-
- public virtual string CreatorName => _content.CreatorName;
-
- public virtual int WriterId => _content.WriterId;
-
- public virtual int CreatorId => _content.CreatorId;
-
- public virtual string Path => _content.Path;
-
- public virtual DateTime CreateDate => _content.CreateDate;
-
- public virtual DateTime UpdateDate => _content.UpdateDate;
+ ///
+ public virtual int SortOrder => _content.SortOrder;
+ ///
public virtual int Level => _content.Level;
+ ///
+ public virtual string Path => _content.Path;
+
+ ///
+ public virtual int TemplateId => _content.TemplateId;
+
+ ///
+ public virtual int CreatorId => _content.CreatorId;
+
+ ///
+ public virtual string CreatorName => _content.CreatorName;
+
+ ///
+ public virtual DateTime CreateDate => _content.CreateDate;
+
+ ///
+ public virtual int WriterId => _content.WriterId;
+
+ ///
+ public virtual string WriterName => _content.WriterName;
+
+ ///
+ public virtual DateTime UpdateDate => _content.UpdateDate;
+
+ ///
public virtual string Url => _content.Url;
+ ///
+ public PublishedCultureInfos GetCulture(string culture = ".") => _content.GetCulture(culture);
+
+ ///
+ public IReadOnlyDictionary Cultures => _content.Cultures;
+
+ ///
public virtual PublishedItemType ItemType => _content.ItemType;
+ ///
public virtual bool IsDraft => _content.IsDraft;
#endregion
#region Tree
+ ///
public virtual IPublishedContent Parent => _content.Parent;
+ ///
public virtual IEnumerable Children => _content.Children;
#endregion
#region Properties
+ ///
public virtual IEnumerable Properties => _content.Properties;
+ ///
public virtual IPublishedProperty GetProperty(string alias)
{
return _content.GetProperty(alias);
}
+ ///
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
{
return _content.GetProperty(alias, recurse);
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs
index 59ac875aa4..898649f2e5 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs
@@ -1,4 +1,5 @@
using System;
+using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent
{
@@ -16,4 +17,54 @@ namespace Umbraco.Core.Models.PublishedContent
public string Name { get; }
public string UrlName { get; }
}
+
+ ///
+ /// Contains culture specific values for .
+ ///
+ public class PublishedCultureInfos
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PublishedCultureInfos(string culture, string name, bool published, DateTime publishedDate)
+ {
+ if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture));
+ if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
+
+ Culture = culture;
+ Name = name;
+ UrlSegment = name.ToUrlSegment(culture);
+ Published = published;
+ PublishedDate = publishedDate;
+ }
+
+ ///
+ /// Gets the culture.
+ ///
+ public string Culture { get; }
+
+ ///
+ /// Gets the name of the item.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the url segment of the item.
+ ///
+ public string UrlSegment { get; }
+
+ ///
+ /// Gets a value indicating whether the culture is published.
+ ///
+ ///
+ /// A published content item will only have published cultures, and therefore this
+ /// value will always be true. On the other hand, fixme drafts?
+ ///
+ public bool Published { get; }
+
+ ///
+ /// Gets the date when fixme?
+ ///
+ public DateTime PublishedDate { get; } // fixme - model? model.UpdateDate - here?
+ }
}
diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs
index afdb4c3646..0a41b3c09e 100644
--- a/src/Umbraco.Core/StringExtensions.cs
+++ b/src/Umbraco.Core/StringExtensions.cs
@@ -1157,7 +1157,16 @@ namespace Umbraco.Core
/// The text to filter.
/// The culture.
/// The safe url segment.
- public static string ToUrlSegment(this string text, CultureInfo culture)
+ public static string ToUrlSegment(this string text, string culture)
+ => text.ToUrlSegment(CultureInfo.GetCultureInfo(culture));
+
+ ///
+ /// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment.
+ ///
+ /// The text to filter.
+ /// The culture.
+ /// The safe url segment.
+ public static string ToUrlSegment(this string text, CultureInfo culture) // fixme obsolete that one, use the string one?
{
return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture);
}
diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs
index 34def77faa..78a75af835 100644
--- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs
+++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs
@@ -16,6 +16,7 @@ using Current = Umbraco.Web.Composing.Current;
using LightInject;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
+using Umbraco.Tests.PublishedContent;
namespace Umbraco.Tests.Cache.PublishedCache
{
@@ -23,6 +24,8 @@ namespace Umbraco.Tests.Cache.PublishedCache
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class PublishMediaCacheTests : BaseWebTest
{
+ private Dictionary _mediaTypes;
+
protected override void Compose()
{
base.Compose();
@@ -32,6 +35,21 @@ namespace Umbraco.Tests.Cache.PublishedCache
.Append();
}
+ protected override void Initialize()
+ {
+ base.Initialize();
+ var type = new AutoPublishedContentType(22, "myType", new PublishedPropertyType[] { });
+ var image = new AutoPublishedContentType(23, "Image", new PublishedPropertyType[] { });
+ var testMediaType = new AutoPublishedContentType(24, "TestMediaType", new PublishedPropertyType[] { });
+ _mediaTypes = new Dictionary
+ {
+ { type.Alias, type },
+ { image.Alias, image },
+ { testMediaType.Alias, testMediaType }
+ };
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => _mediaTypes[alias];
+ }
+
private IMediaType MakeNewMediaType(IUser user, string text, int parentId = -1)
{
var mt = new MediaType(parentId) { Name = text, Alias = text, Thumbnail = "icon-folder", Icon = "icon-folder" };
@@ -69,6 +87,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
{
var user = ServiceContext.UserService.GetUserById(0);
var mType = MakeNewMediaType(user, "TestMediaType");
+ _mediaTypes[mType.Alias] = new PublishedContentType(mType, null);
var mRoot = MakeNewMedia("MediaRoot", mType, user, -1);
var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id);
@@ -82,8 +101,8 @@ namespace Umbraco.Tests.Cache.PublishedCache
Assert.AreEqual(mRoot.CreateDate.ToString("dd/MM/yyyy HH:mm:ss"), publishedMedia.CreateDate.ToString("dd/MM/yyyy HH:mm:ss"));
Assert.AreEqual(mRoot.CreatorId, publishedMedia.CreatorId);
//Assert.AreEqual(mRoot.User.Name, publishedMedia.CreatorName);
- Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.DocumentTypeAlias);
- Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.DocumentTypeId);
+ Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.ContentType.Alias);
+ Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.ContentType.Id);
Assert.AreEqual(mRoot.Level, publishedMedia.Level);
Assert.AreEqual(mRoot.Name, publishedMedia.Name);
Assert.AreEqual(mRoot.Path, publishedMedia.Path);
@@ -186,11 +205,11 @@ namespace Umbraco.Tests.Cache.PublishedCache
};
var result = new SearchResult("1234", 1, 1, () => fields.ToDictionary(x => x.Key, x => new List { x.Value }));
-
+
var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache);
var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result));
- DoAssert(doc, 1234, key, 0, 0, "/media/test.jpg", "Image", 0, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2);
+ DoAssert(doc, 1234, key, 0, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2);
Assert.AreEqual(null, doc.Parent);
}
@@ -206,7 +225,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache);
var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true));
- DoAssert(doc, 2000, key, 0, 2, "image1", "Image", 2044, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1);
+ DoAssert(doc, 2000, key, 0, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1);
Assert.AreEqual(null, doc.Parent);
Assert.AreEqual(2, doc.Children.Count());
Assert.AreEqual(2001, doc.Children.ElementAt(0).Id);
@@ -271,7 +290,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
};
}
- private PublishedMediaCache.DictionaryPublishedContent GetDictionaryDocument(
+ private DictionaryPublishedContent GetDictionaryDocument(
string idKey = "id",
string templateKey = "template",
string nodeNameKey = "nodeName",
@@ -284,11 +303,11 @@ namespace Umbraco.Tests.Cache.PublishedCache
{
if (children == null)
children = new List();
- var dicDoc = new PublishedMediaCache.DictionaryPublishedContent(
+ var dicDoc = new DictionaryPublishedContent(
//the dictionary
GetDictionary(idVal, keyVal, parentIdVal, idKey, templateKey, nodeNameKey, nodeTypeAliasKey, pathKey),
//callback to get the parent
- d => new PublishedMediaCache.DictionaryPublishedContent(
+ d => new DictionaryPublishedContent(
// the dictionary
GetDictionary(parentIdVal, default(Guid), -1, idKey, templateKey, nodeNameKey, nodeTypeAliasKey, pathKey),
// callback to get the parent: there is no parent
@@ -317,7 +336,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
}
private void DoAssert(
- PublishedMediaCache.DictionaryPublishedContent dicDoc,
+ DictionaryPublishedContent dicDoc,
int idVal = 1234,
Guid keyVal = default(Guid),
int templateIdVal = 0,
@@ -375,8 +394,8 @@ namespace Umbraco.Tests.Cache.PublishedCache
Assert.AreEqual(templateIdVal, doc.TemplateId);
Assert.AreEqual(sortOrderVal, doc.SortOrder);
Assert.AreEqual(urlNameVal, doc.UrlName);
- Assert.AreEqual(nodeTypeAliasVal, doc.DocumentTypeAlias);
- Assert.AreEqual(nodeTypeIdVal, doc.DocumentTypeId);
+ Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias);
+ Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id);
Assert.AreEqual(writerNameVal, doc.WriterName);
Assert.AreEqual(creatorNameVal, doc.CreatorName);
Assert.AreEqual(writerIdVal, doc.WriterId);
diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs
index 2ad5f470ef..d86b7f0e6b 100644
--- a/src/Umbraco.Tests/Published/NestedContentTests.cs
+++ b/src/Umbraco.Tests/Published/NestedContentTests.cs
@@ -273,10 +273,9 @@ namespace Umbraco.Tests.Published
public override int TemplateId { get; }
public override int SortOrder { get; }
public override string Name { get; }
- public override IReadOnlyDictionary CultureNames => throw new NotSupportedException();
+ public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException();
+ public override IReadOnlyDictionary Cultures => throw new NotSupportedException();
public override string UrlName { get; }
- public override string DocumentTypeAlias { get; }
- public override int DocumentTypeId { get; }
public override string WriterName { get; }
public override string CreatorName { get; }
public override int WriterId { get; }
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs
index 4668a86c78..56c60446ef 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs
@@ -96,8 +96,9 @@ namespace Umbraco.Tests.PublishedContent
public void To_DataTable_With_Filter()
{
var doc = GetContent(true, 1);
- //change a doc type alias
- ((TestPublishedContent) doc.Children.ElementAt(0)).DocumentTypeAlias = "DontMatch";
+ //change a doc type alias
+ var c = (TestPublishedContent) doc.Children.ElementAt(0);
+ c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral);
var dt = doc.ChildrenAsTable(Current.Services, "Child");
@@ -133,8 +134,9 @@ namespace Umbraco.Tests.PublishedContent
CreateDate = DateTime.Now,
CreatorId = 1,
CreatorName = "Shannon",
- DocumentTypeAlias = contentTypeAlias,
- DocumentTypeId = 2,
+ // fixme what're we gonna do?
+ //DocumentTypeAlias = contentTypeAlias,
+ //DocumentTypeId = 2,
Id = 3,
SortOrder = 4,
TemplateId = 5,
@@ -175,6 +177,8 @@ namespace Umbraco.Tests.PublishedContent
((Collection) d.Properties).Add(
new RawValueProperty(factory.CreatePropertyType("property3", 1), d, "value" + (indexVals + 2)));
}
+
+ d.ContentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral);
return d;
}
@@ -201,10 +205,9 @@ namespace Umbraco.Tests.PublishedContent
public int TemplateId { get; set; }
public int SortOrder { get; set; }
public string Name { get; set; }
- public IReadOnlyDictionary CultureNames => throw new NotSupportedException();
+ public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException();
+ public IReadOnlyDictionary Cultures => throw new NotSupportedException();
public string UrlName { get; set; }
- public string DocumentTypeAlias { get; set; }
- public int DocumentTypeId { get; set; }
public string WriterName { get; set; }
public string CreatorName { get; set; }
public int WriterId { get; set; }
@@ -240,10 +243,7 @@ namespace Umbraco.Tests.PublishedContent
return property;
}
- public PublishedContentType ContentType
- {
- get { throw new NotImplementedException(); }
- }
+ public PublishedContentType ContentType { get; set; }
}
}
}
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs
index ab4b1138c5..d6321af692 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs
@@ -1,6 +1,8 @@
-using NUnit.Framework;
+using System.Collections.Generic;
+using NUnit.Framework;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
using Umbraco.Tests.Testing;
using Umbraco.Web;
@@ -13,6 +15,7 @@ namespace Umbraco.Tests.PublishedContent
private UmbracoContext ctx;
private string xmlContent = "";
private bool createContentTypes = true;
+ private Dictionary _contentTypes;
protected override string GetXmlContent(int templateId)
{
@@ -49,8 +52,8 @@ namespace Umbraco.Tests.PublishedContent
[Test]
public void IsDocumentType_Recursive_BaseType_ReturnsTrue()
{
- ContentTypesCache.GetPublishedContentTypeByAlias = null; // fixme this is not pretty
InitializeInheritedContentTypes();
+ ContentTypesCache.GetPublishedContentTypeByAlias = null; // fixme this is not pretty
var publishedContent = ctx.ContentCache.GetById(1100);
Assert.That(publishedContent.IsDocumentType("base", true));
@@ -76,9 +79,17 @@ namespace Umbraco.Tests.PublishedContent
var inheritedType = new ContentType(baseType, contentTypeAlias) { Alias = contentTypeAlias, Name = "Inherited" };
contentTypeService.Save(baseType);
contentTypeService.Save(inheritedType);
+ _contentTypes = new Dictionary
+ {
+ { baseType.Alias, new PublishedContentType(baseType, null) },
+ { inheritedType.Alias, new PublishedContentType(inheritedType, null) }
+ };
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias];
createContentTypes = false;
}
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias];
+
xmlContent = @"
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs
index e1c2ed9d5a..e4fce2a3be 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentOtherTests.cs
@@ -117,6 +117,9 @@ namespace Umbraco.Tests.PublishedContent
var variationAccessor = new TestPublishedVariationContextAccessor();
+ // invariant is the current default
+ variationAccessor.Context = new PublishedVariationContext();
+
var options = new PublishedSnapshotService.Options { IgnoreLocalDb = true };
var snapshotService = new PublishedSnapshotService(options,
null,
@@ -139,9 +142,6 @@ namespace Umbraco.Tests.PublishedContent
var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null);
var publishedContent = snapshot.Content.GetById(1);
- // invariant is the current default
- variationAccessor.Context = null;
-
Assert.IsNotNull(publishedContent);
Assert.AreEqual("It Works1!", publishedContent.Name);
Assert.AreEqual("val1", publishedContent.Value("prop"));
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
index 04ed54d81c..2cf555efda 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
@@ -364,9 +364,18 @@ namespace Umbraco.Tests.PublishedContent
[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.DocumentTypeAlias).ToArray();
+ 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());
@@ -376,10 +385,19 @@ namespace Umbraco.Tests.PublishedContent
[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.DocumentTypeAlias == "CustomDocument");
- var found2 = doc.Children.Where(x => x.DocumentTypeAlias == "Home");
+ 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());
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs
index cfc0df25b7..66f7871a4b 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs
@@ -2,8 +2,10 @@
using System.Collections;
using System.Collections.ObjectModel;
using System.Globalization;
+using System.Linq;
using Moq;
using NUnit.Framework;
+using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Tests.TestHelpers;
@@ -72,8 +74,6 @@ namespace Umbraco.Tests.PublishedContent
var pc = new Mock();
pc.Setup(content => content.Id).Returns(1);
pc.Setup(content => content.Name).Returns("test");
- pc.Setup(content => content.DocumentTypeId).Returns(2);
- pc.Setup(content => content.DocumentTypeAlias).Returns("testAlias");
pc.Setup(content => content.WriterName).Returns("admin");
pc.Setup(content => content.CreatorName).Returns("admin");
pc.Setup(content => content.CreateDate).Returns(DateTime.Now);
@@ -81,6 +81,7 @@ namespace Umbraco.Tests.PublishedContent
pc.Setup(content => content.Path).Returns("-1,1");
pc.Setup(content => content.Parent).Returns(() => null);
pc.Setup(content => content.Properties).Returns(new Collection());
+ pc.Setup(content => content.ContentType).Returns(new PublishedContentType(22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.InvariantNeutral));
return pc;
}
}
diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
index 2be86640d7..307ef459fb 100644
--- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
+++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
@@ -165,8 +165,6 @@ namespace Umbraco.Tests.PublishedContent
IsDraft = false;
ContentType = contentType;
- DocumentTypeAlias = contentType.Alias;
- DocumentTypeId = contentType.Id;
}
#endregion
@@ -178,10 +176,9 @@ namespace Umbraco.Tests.PublishedContent
public int TemplateId { get; set; }
public int SortOrder { get; set; }
public string Name { get; set; }
- public IReadOnlyDictionary CultureNames => throw new NotSupportedException();
+ public PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException();
+ public IReadOnlyDictionary Cultures => throw new NotSupportedException();
public string UrlName { get; set; }
- public string DocumentTypeAlias { get; private set; }
- public int DocumentTypeId { get; private set; }
public string WriterName { get; set; }
public string CreatorName { get; set; }
public int WriterId { get; set; }
diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
index f722906053..c64c70e65a 100644
--- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
+++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
@@ -17,6 +17,8 @@ using Umbraco.Web.Routing;
using Umbraco.Web.WebApi;
using Umbraco.Core.Strings;
using Umbraco.Core.Composing;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.Testing;
using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web.Runtime;
@@ -141,6 +143,9 @@ namespace Umbraco.Tests.Routing
frequest.PublishedContent = umbracoContext.ContentCache.GetById(1172);
frequest.TemplateModel = template;
+ var type = new AutoPublishedContentType(22, "CustomDocument", new PublishedPropertyType[] { });
+ ContentTypesCache.GetPublishedContentTypeByAlias = alias => type;
+
var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContext, Mock.Of()));
handler.GetHandlerForRoute(umbracoContext.HttpContext.Request.RequestContext, frequest);
diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs
index 7d9bbec855..042794e573 100644
--- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs
+++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.PublishedCache;
@@ -10,18 +7,31 @@ namespace Umbraco.Tests.TestHelpers.Stubs
{
internal class TestPublishedContent : PublishedElement, IPublishedContent
{
- public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultureNames = null)
+ public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultures = null)
: base(contentType, key, values, previewing)
{
Id = id;
- CultureNames = cultureNames;
+ Cultures = cultures;
}
public int Id { get; }
public int TemplateId { get; set; }
public int SortOrder { get; set; }
public string Name { get; set; }
- public IReadOnlyDictionary CultureNames { get; set; }
+ public IPublishedVariationContextAccessor VariationContextAccessor { get; set; }
+ public PublishedCultureInfos GetCulture(string culture = ".")
+ {
+ // handle context culture
+ if (culture == ".")
+ culture = VariationContextAccessor?.Context.Culture;
+
+ // no invariant culture infos
+ if (culture == null || Cultures == null) return null;
+
+ // get
+ return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null;
+ }
+ public IReadOnlyDictionary Cultures { get; set; }
public string UrlName { get; set; }
public string DocumentTypeAlias => ContentType.Alias;
public int DocumentTypeId { get; set; }
diff --git a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs
index 9327b462f1..455e5795a9 100644
--- a/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs
+++ b/src/Umbraco.Tests/Testing/Objects/Accessors/TestPublishedVariationContextAccessor.cs
@@ -8,6 +8,10 @@ namespace Umbraco.Tests.Testing.Objects.Accessors
public class TestPublishedVariationContextAccessor : IPublishedVariationContextAccessor
{
///
- public PublishedVariationContext Context { get; set; }
+ public PublishedVariationContext Context
+ {
+ get;
+ set;
+ }
}
}
diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs
index dc4d42a48c..ba23d4c8dd 100644
--- a/src/Umbraco.Web/Editors/MacroController.cs
+++ b/src/Umbraco.Web/Editors/MacroController.cs
@@ -12,6 +12,7 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.Macros;
using Umbraco.Core;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Editors
{
@@ -26,6 +27,13 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState
{
+ private readonly IPublishedVariationContextAccessor _variationContextAccessor;
+
+ public MacroController(IPublishedVariationContextAccessor variationContextAccessor)
+ {
+ _variationContextAccessor = variationContextAccessor;
+ }
+
///
/// Gets the macro parameters to be filled in for a particular macro
///
@@ -113,7 +121,7 @@ namespace Umbraco.Web.Editors
//the 'easiest' way might be to create an IPublishedContent manually and populate the legacy 'page' object with that
//and then set the legacy parameters.
- var legacyPage = new global::umbraco.page(doc);
+ var legacyPage = new global::umbraco.page(doc, _variationContextAccessor);
UmbracoContext.HttpContext.Items["pageID"] = doc.Id;
UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements;
UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null;
diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs
index d51a00b3a2..26ca518259 100644
--- a/src/Umbraco.Web/Editors/TemplateQueryController.cs
+++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs
@@ -90,7 +90,7 @@ namespace Umbraco.Web.Editors
{
timer.Start();
- pointerNode = pointerNode.FirstChild(x => x.DocumentTypeAlias == contentTypeAlias);
+ pointerNode = pointerNode.FirstChild(x => x.ContentType.Alias == contentTypeAlias);
if (pointerNode == null) break;
@@ -277,7 +277,7 @@ namespace Umbraco.Web.Editors
if (targetNode == null || targetNode.Id == current.Id) return aliases;
if (targetNode.Id != current.Id)
{
- aliases.Add(targetNode.DocumentTypeAlias);
+ aliases.Add(targetNode.ContentType.Alias);
}
diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs
index a89cab2c65..084ca1fee2 100644
--- a/src/Umbraco.Web/Models/PublishedContentBase.cs
+++ b/src/Umbraco.Web/Models/PublishedContentBase.cs
@@ -19,24 +19,75 @@ namespace Umbraco.Web.Models
[DebuggerDisplay("Content Id: {Id}, Name: {Name}")]
public abstract class PublishedContentBase : IPublishedContent
{
- #region Content
-
private string _url;
+
+ #region ContentType
- ///
- /// Gets the url of the content.
- ///
+ public abstract PublishedContentType ContentType { get; }
+
+ #endregion
+
+ #region PublishedElement
+
+ ///
+ public abstract Guid Key { get; }
+
+ #endregion
+
+ #region PublishedContent
+
+ ///
+ public abstract int Id { get; }
+
+ ///
+ public abstract string Name { get; }
+
+ ///
+ public abstract string UrlName { get; }
+
+ ///
+ public abstract int SortOrder { get; }
+
+ ///
+ public abstract int Level { get; }
+
+ ///
+ public abstract string Path { get; }
+
+ ///
+ public abstract int TemplateId { get; }
+
+ ///
+ public abstract int CreatorId { get; }
+
+ ///
+ public abstract string CreatorName { get; }
+
+ ///
+ public abstract DateTime CreateDate { get; }
+
+ ///
+ public abstract int WriterId { get; }
+
+ ///
+ public abstract string WriterName { get; }
+
+ ///
+ public abstract DateTime UpdateDate { get; }
+
+ ///
///
- /// If this content is Content, the url that is returned is the one computed by the NiceUrlProvider, otherwise if
- /// this content is Media, the url returned is the value found in the 'umbracoFile' property.
+ /// The url of documents are computed by the document url providers. The url of medias are, at the moment,
+ /// computed here from the 'umbracoFile' property -- but we should move to media url providers at some point.
///
public virtual string Url
{
+ // fixme contextual!
get
{
// should be thread-safe although it won't prevent url from being resolved more than once
if (_url != null)
- return _url;
+ return _url; // fixme very bad idea with nucache? or?
switch (ItemType)
{
@@ -86,86 +137,45 @@ namespace Umbraco.Web.Models
return _url;
}
- }
+ }
+
+ ///
+ public abstract PublishedCultureInfos GetCulture(string culture = ".");
+ ///
+ public abstract IReadOnlyDictionary Cultures { get; }
+
+ ///
public abstract PublishedItemType ItemType { get; }
- public abstract int Id { get; }
- public abstract Guid Key { get; }
- public abstract int TemplateId { get; }
- public abstract int SortOrder { get; }
- public abstract string Name { get; }
- //TODO: On the base ContentData instance this dictionary contains a CultureVariation, should we expose that model here or a different model?
- public abstract IReadOnlyDictionary CultureNames { get; }
- public abstract string UrlName { get; }
- public abstract string DocumentTypeAlias { get; }
- public abstract int DocumentTypeId { get; }
- public abstract string WriterName { get; }
- public abstract string CreatorName { get; }
- public abstract int WriterId { get; }
- public abstract int CreatorId { get; }
- public abstract string Path { get; }
- public abstract DateTime CreateDate { get; }
- public abstract DateTime UpdateDate { get; }
- public abstract int Level { get; }
+ ///
public abstract bool IsDraft { get; }
#endregion
#region Tree
- ///
- /// Gets the parent of the content.
- ///
+ ///
public abstract IPublishedContent Parent { get; }
- ///
- /// Gets the children of the content.
- ///
- /// Children are sorted by their sortOrder.
+ ///
public abstract IEnumerable Children { get; }
#endregion
- #region ContentType
-
- public abstract PublishedContentType ContentType { get; }
-
- #endregion
-
#region Properties
- ///
- /// Gets the properties of the content.
- ///
+ ///
public abstract IEnumerable Properties { get; }
- ///
- /// Gets a property identified by its alias.
- ///
- /// The property alias.
- /// The property identified by the alias.
- ///
- /// If no property with the specified alias exists, returns null.
- /// The returned property may have no value (ie HasValue is false).
- /// The alias is case-insensitive.
- ///
+ ///
public abstract IPublishedProperty GetProperty(string alias);
- ///
- /// Gets a property identified by its alias.
- ///
- /// The property alias.
- /// A value indicating whether to navigate the tree upwards until a property with a value is found.
- /// The property identified by the alias.
- ///
- /// Navigate the tree upwards and look for a property with that alias and with a value (ie HasValue is true).
- /// If found, return the property. If no property with that alias is found, having a value or not, return null. Otherwise
- /// return the first property that was found with the alias but had no value (ie HasValue is false).
- /// The alias is case-insensitive.
- ///
+ ///
public virtual IPublishedProperty GetProperty(string alias, bool recurse)
- {
+ {
+ // fixme - but can this work with variants?
+
var property = GetProperty(alias);
if (recurse == false) return property;
diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
index 3b0b6b35e2..446827f0a3 100644
--- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
+++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
@@ -300,7 +300,7 @@ namespace Umbraco.Web.Mvc
}
//check if there's a custom controller assigned, base on the document type alias.
- var controllerType = _controllerFactory.GetControllerTypeInternal(requestContext, request.PublishedContent.DocumentTypeAlias);
+ var controllerType = _controllerFactory.GetControllerTypeInternal(requestContext, request.PublishedContent.ContentType.Alias);
//check if that controller exists
if (controllerType != null)
@@ -320,7 +320,7 @@ namespace Umbraco.Web.Mvc
else
{
Current.Logger.Warn(() =>
- $"The current Document Type {request.PublishedContent.DocumentTypeAlias} matches a locally declared controller of type {controllerType.FullName}. Custom Controllers for Umbraco routing must implement '{typeof(IRenderController).FullName}' and inherit from '{typeof(ControllerBase).FullName}'.");
+ $"The current Document Type {request.PublishedContent.ContentType.Alias} matches a locally declared controller of type {controllerType.FullName}. Custom Controllers for Umbraco routing must implement '{typeof(IRenderController).FullName}' and inherit from '{typeof(ControllerBase).FullName}'.");
//we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults
// that have already been set above.
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs
index 5f2df36bf1..4b235e42b9 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs
@@ -104,8 +104,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
// hideTopLevelNode = support legacy stuff, look for /*/path/to/node
// else normal, look for /path/to/node
content = hideTopLevelNode.Value
- ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0])
- : GetAtRoot(preview).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]);
+ ? GetAtRoot(preview).SelectMany(x => x.Children).FirstOrDefault(x => x.GetCulture(culture).UrlSegment == parts[0])
+ : GetAtRoot(preview).FirstOrDefault(x => x.GetCulture(culture).UrlSegment == parts[0]);
content = FollowRoute(content, parts, 1, culture);
}
@@ -114,7 +114,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// have to look for /foo (see note in ApplyHideTopLevelNodeFromPath).
if (content == null && hideTopLevelNode.Value && parts.Length == 1)
{
- content = GetAtRoot(preview).FirstOrDefault(x => x.GetUrlName(_localizationService, culture) == parts[0]);
+ content = GetAtRoot(preview).FirstOrDefault(x => x.GetCulture(culture).UrlSegment == parts[0]);
}
return content;
@@ -147,9 +147,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
var hasDomains = _domainHelper.NodeHasDomains(n.Id);
while (hasDomains == false && n != null) // n is null at root
{
- var urlName = n.GetUrlName(_localizationService, culture);
+ var urlSegment = n.GetCulture(culture).UrlSegment;
- pathParts.Add(urlName);
+ pathParts.Add(urlSegment);
// move to parent node
n = n.Parent;
@@ -178,14 +178,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
var part = parts[i++];
content = content.Children.FirstOrDefault(x =>
{
- // fixme - should use ISystemDefaultCultureAccessor NOT ILocalizationService!
- var urlName = x.GetUrlName(_localizationService, culture);
- return urlName == part;
+ var urlSegment = x.GetCulture(culture).UrlSegment;
+ return urlSegment == part;
});
-
- // fixme - if content has a wildcard domain, switch culture! or, shall we?
- // no - do NOT support wildcard domains, it makes no sense
- // OTOH support '*/en' as a valid domain
}
return content;
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs
index a0ec6e4687..d2a4e2f539 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
// ReSharper disable once InconsistentNaming
internal readonly ContentData _contentData; // internal for ContentNode cloning
- private readonly string _urlName;
+ private readonly string _urlSegment;
private IReadOnlyDictionary _cultureNames;
#region Constructors
@@ -28,9 +28,9 @@ namespace Umbraco.Web.PublishedCache.NuCache
_contentNode = contentNode;
_contentData = contentData;
_publishedSnapshotAccessor = publishedSnapshotAccessor;
- VariationContextAccessor = variationContextAccessor;
+ VariationContextAccessor = variationContextAccessor; // fixme why is this a property? should be be on the base class?
- _urlName = _contentData.Name.ToUrlSegment();
+ _urlSegment = _contentData.Name.ToUrlSegment();
IsPreviewing = _contentData.Published == false;
var properties = new List();
@@ -74,7 +74,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
VariationContextAccessor = origin.VariationContextAccessor;
_contentData = origin._contentData;
- _urlName = origin._urlName;
+ _urlSegment = origin._urlSegment;
IsPreviewing = origin.IsPreviewing;
// here is the main benefit: we do not re-create properties so if anything
@@ -91,7 +91,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_contentNode = origin._contentNode;
_contentData = origin._contentData;
- _urlName = origin._urlName;
+ _urlSegment = origin._urlSegment;
IsPreviewing = true;
// clone properties so _isPreviewing is true
@@ -155,51 +155,131 @@ namespace Umbraco.Web.PublishedCache.NuCache
#endregion
- #region IPublishedContent
+ #region Content Type
- public override int Id => _contentNode.Id;
+ ///
+ public override PublishedContentType ContentType => _contentNode.ContentType;
+
+ #endregion
+
+ #region PublishedElement
+
+ ///
public override Guid Key => _contentNode.Uid;
- public override int DocumentTypeId => _contentNode.ContentType.Id;
- public override string DocumentTypeAlias => _contentNode.ContentType.Alias;
- public override PublishedItemType ItemType => _contentNode.ContentType.ItemType;
- public override string Name => _contentData.Name;
- public override IReadOnlyDictionary CultureNames
+ #endregion
+
+ #region PublishedContent
+
+ ///
+ public override int Id => _contentNode.Id;
+
+ ///
+ public override string Name
{
get
{
- if (!ContentType.Variations.HasFlag(ContentVariation.CultureNeutral))
- return null;
+ if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment?
+ return _contentData.Name;
- if (_cultureNames == null)
- {
- var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
- foreach(var c in _contentData.CultureInfos)
- {
- d[c.Key] = new PublishedCultureName(c.Value.Name, c.Value.Name.ToUrlSegment());
- }
- _cultureNames = d;
- }
- return _cultureNames;
+ var culture = VariationContextAccessor.Context.Culture;
+ if (culture == null)
+ return _contentData.Name;
+
+ return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.Name : null;
}
}
- public override int Level => _contentNode.Level;
- public override string Path => _contentNode.Path;
+
+ ///
+ public override string UrlName
+ {
+ get
+ {
+ if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment?
+ return _urlSegment;
+
+ var culture = VariationContextAccessor.Context.Culture;
+ if (culture == null)
+ return _urlSegment;
+
+ return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos.UrlSegment : null;
+ }
+ }
+
+ ///
public override int SortOrder => _contentNode.SortOrder;
+
+ ///
+ public override int Level => _contentNode.Level;
+
+ ///
+ public override string Path => _contentNode.Path;
+
+ ///
public override int TemplateId => _contentData.TemplateId;
- public override string UrlName => _urlName;
-
- public override DateTime CreateDate => _contentNode.CreateDate;
- public override DateTime UpdateDate => _contentData.VersionDate;
-
+ ///
public override int CreatorId => _contentNode.CreatorId;
+
+ ///
public override string CreatorName => GetProfileNameById(_contentNode.CreatorId);
+
+ ///
+ public override DateTime CreateDate => _contentNode.CreateDate;
+
+ ///
public override int WriterId => _contentData.WriterId;
+
+ ///
public override string WriterName => GetProfileNameById(_contentData.WriterId);
+ ///
+ public override DateTime UpdateDate => _contentData.VersionDate;
+
+ private IReadOnlyDictionary _cultureInfos;
+
+ private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary();
+
+ ///
+ public override PublishedCultureInfos GetCulture(string culture = ".")
+ {
+ // handle context culture
+ if (culture == ".")
+ culture = VariationContextAccessor.Context.Culture;
+
+ // no invariant culture infos
+ if (culture == null) return null;
+
+ // get
+ return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null;
+ }
+
+ ///
+ public override IReadOnlyDictionary Cultures
+ {
+ get
+ {
+ if (!ContentType.Variations.Has(ContentVariation.CultureNeutral)) // fixme CultureSegment?
+ return NoCultureInfos;
+
+ if (_cultureInfos != null) return _cultureInfos;
+
+ return _cultureInfos = _contentData.CultureInfos // fixme can it be null?
+ .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value.Name, false, DateTime.MinValue)); // fixme values!
+ }
+ }
+
+ ///
+ public override PublishedItemType ItemType => _contentNode.ContentType.ItemType;
+
+ ///
public override bool IsDraft => _contentData.Published == false;
+ #endregion
+
+ #region Tree
+
+ ///
public override IPublishedContent Parent
{
get
@@ -218,10 +298,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
}
- private string _childrenCacheKey;
-
- private string ChildrenCacheKey => _childrenCacheKey ?? (_childrenCacheKey = CacheKeys.PublishedContentChildren(Key, IsPreviewing));
-
+ ///
public override IEnumerable Children
{
get
@@ -235,6 +312,10 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
}
+ private string _childrenCacheKey;
+
+ private string ChildrenCacheKey => _childrenCacheKey ?? (_childrenCacheKey = CacheKeys.PublishedContentChildren(Key, IsPreviewing));
+
private IEnumerable GetChildren()
{
IEnumerable c;
@@ -258,8 +339,15 @@ namespace Umbraco.Web.PublishedCache.NuCache
// Q: perfs-wise, is it better than having the store managed an ordered list
}
+ #endregion
+
+ #region Properties
+
+
+ ///
public override IEnumerable Properties => PropertiesArray;
+ ///
public override IPublishedProperty GetProperty(string alias)
{
var index = _contentNode.ContentType.GetPropertyIndex(alias);
@@ -270,6 +358,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
return property;
}
+ ///
public override IPublishedProperty GetProperty(string alias, bool recurse)
{
var property = GetProperty(alias);
@@ -283,8 +372,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
return (Property)cache.GetCacheItem(key, () => base.GetProperty(alias, true));
}
- public override PublishedContentType ContentType => _contentNode.ContentType;
-
#endregion
#region Caching
diff --git a/src/Umbraco.Web/PublishedCache/PublishedMember.cs b/src/Umbraco.Web/PublishedCache/PublishedMember.cs
index bf764b0ff0..084daf6457 100644
--- a/src/Umbraco.Web/PublishedCache/PublishedMember.cs
+++ b/src/Umbraco.Web/PublishedCache/PublishedMember.cs
@@ -144,14 +144,12 @@ namespace Umbraco.Web.PublishedCache
public override string Name => _member.Name;
- public override IReadOnlyDictionary CultureNames => throw new NotSupportedException();
+ public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException();
+
+ public override IReadOnlyDictionary Cultures => throw new NotSupportedException();
public override string UrlName => throw new NotSupportedException();
- public override string DocumentTypeAlias => _member.ContentTypeAlias;
-
- public override int DocumentTypeId => _member.ContentType.Id;
-
//TODO: ARGH! need to fix this - this is not good because it uses ApplicationContext.Current
public override string WriterName => _member.GetCreatorProfile().Name;
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
new file mode 100644
index 0000000000..5ef600ba76
--- /dev/null
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Xml.XPath;
+using Examine.LuceneEngine.Providers;
+using Umbraco.Core;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Composing;
+using Umbraco.Web.Models;
+
+namespace Umbraco.Web.PublishedCache.XmlPublishedCache
+{
+ ///
+ /// An IPublishedContent that is represented all by a dictionary.
+ ///
+ ///
+ /// This is a helper class and definitely not intended for public use, it expects that all of the values required
+ /// to create an IPublishedContent exist in the dictionary by specific aliases.
+ ///
+ internal class DictionaryPublishedContent : PublishedContentBase
+ {
+ // note: I'm not sure this class fully complies with IPublishedContent rules especially
+ // I'm not sure that _properties contains all properties including those without a value,
+ // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk
+
+ // List of properties that will appear in the XML and do not match
+ // anything in the ContentType, so they must be ignored.
+ private static readonly string[] IgnoredKeys = { "version", "isDoc" };
+
+ public DictionaryPublishedContent(
+ IReadOnlyDictionary valueDictionary,
+ Func getParent,
+ Func> getChildren,
+ Func getProperty,
+ ICacheProvider cacheProvider,
+ PublishedContentTypeCache contentTypeCache,
+ XPathNavigator nav,
+ bool fromExamine)
+ {
+ if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary));
+ if (getParent == null) throw new ArgumentNullException(nameof(getParent));
+ if (getProperty == null) throw new ArgumentNullException(nameof(getProperty));
+
+ _getParent = new Lazy(() => getParent(ParentId));
+ _getChildren = new Lazy>(() => getChildren(Id, nav));
+ _getProperty = getProperty;
+ _cacheProvider = cacheProvider;
+
+ LoadedFromExamine = fromExamine;
+
+ ValidateAndSetProperty(valueDictionary, val => _id = Int32.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int!
+ ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key");
+ //ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
+ ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder");
+ ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName");
+ ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName");
+ ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.ItemTypeFieldName);
+ ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType");
+ //ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName");
+ ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132
+ //ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID");
+ ValidateAndSetProperty(valueDictionary, val => _creatorId = Int32.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132
+ ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path");
+ ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate");
+ ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate");
+ ValidateAndSetProperty(valueDictionary, val => _level = Int32.Parse(val), "level");
+ ValidateAndSetProperty(valueDictionary, val =>
+ {
+ int pId;
+ ParentId = -1;
+ if (Int32.TryParse(val, out pId))
+ {
+ ParentId = pId;
+ }
+ }, "parentID");
+
+ _contentType = contentTypeCache.Get(PublishedItemType.Media, _documentTypeAlias);
+ _properties = new Collection();
+
+ //handle content type properties
+ //make sure we create them even if there's no value
+ foreach (var propertyType in _contentType.PropertyTypes)
+ {
+ var alias = propertyType.Alias;
+ _keysAdded.Add(alias);
+ string value;
+ const bool isPreviewing = false; // false :: never preview a media
+ var property = valueDictionary.TryGetValue(alias, out value) == false || value == null
+ ? new XmlPublishedProperty(propertyType, this, isPreviewing)
+ : new XmlPublishedProperty(propertyType, this, isPreviewing, value);
+ _properties.Add(property);
+ }
+
+ //loop through remaining values that haven't been applied
+ foreach (var i in valueDictionary.Where(x =>
+ _keysAdded.Contains(x.Key) == false // not already processed
+ && IgnoredKeys.Contains(x.Key) == false)) // not ignorable
+ {
+ if (i.Key.InvariantStartsWith("__"))
+ {
+ // no type for that one, dunno how to convert, drop it
+ //IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty);
+ //_properties.Add(property);
+ }
+ else
+ {
+ // this is a property that does not correspond to anything, ignore and log
+ Current.Logger.Warn("Dropping property \"" + i.Key + "\" because it does not belong to the content type.");
+ }
+ }
+ }
+
+ private DateTime ParseDateTimeValue(string val)
+ {
+ if (LoadedFromExamine == false)
+ return DateTime.Parse(val);
+
+ //we need to parse the date time using Lucene converters
+ var ticks = Int64.Parse(val);
+ return new DateTime(ticks);
+ }
+
+ ///
+ /// Flag to get/set if this was laoded from examine cache
+ ///
+ internal bool LoadedFromExamine { get; }
+
+ //private readonly Func _getParent;
+ private readonly Lazy _getParent;
+ //private readonly Func> _getChildren;
+ private readonly Lazy> _getChildren;
+ private readonly Func _getProperty;
+ private readonly ICacheProvider _cacheProvider;
+
+ ///
+ /// Returns 'Media' as the item type
+ ///
+ public override PublishedItemType ItemType => PublishedItemType.Media;
+
+ public override IPublishedContent Parent => _getParent.Value;
+
+ public int ParentId { get; private set; }
+
+ public override int Id => _id;
+
+ public override Guid Key => _key;
+
+ public override int TemplateId => 0;
+
+ public override int SortOrder => _sortOrder;
+
+ public override string Name => _name;
+
+ public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException();
+
+ public override IReadOnlyDictionary Cultures => throw new NotSupportedException();
+
+ public override string UrlName => _urlName;
+
+ public override string WriterName => _creatorName;
+
+ public override string CreatorName => _creatorName;
+
+ public override int WriterId => _creatorId;
+
+ public override int CreatorId => _creatorId;
+
+ public override string Path => _path;
+
+ public override DateTime CreateDate => _createDate;
+
+ public override DateTime UpdateDate => _updateDate;
+
+ public override int Level => _level;
+
+ public override bool IsDraft => false;
+
+ public override IEnumerable Properties => _properties;
+
+ public override IEnumerable Children => _getChildren.Value;
+
+ public override IPublishedProperty GetProperty(string alias)
+ {
+ return _getProperty(this, alias);
+ }
+
+ public override PublishedContentType ContentType => _contentType;
+
+ // override to implement cache
+ // cache at context level, ie once for the whole request
+ // but cache is not shared by requests because we wouldn't know how to clear it
+ public override IPublishedProperty GetProperty(string alias, bool recurse)
+ {
+ if (recurse == false) return GetProperty(alias);
+
+ var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}";
+ var cacheProvider = _cacheProvider;
+ return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true));
+ }
+
+ private readonly List _keysAdded = new List();
+ private int _id;
+ private Guid _key;
+ //private int _templateId;
+ private int _sortOrder;
+ private string _name;
+ private string _urlName;
+ private string _documentTypeAlias;
+ private int _documentTypeId;
+ //private string _writerName;
+ private string _creatorName;
+ //private int _writerId;
+ private int _creatorId;
+ private string _path;
+ private DateTime _createDate;
+ private DateTime _updateDate;
+ //private Guid _version;
+ private int _level;
+ private readonly ICollection _properties;
+ private readonly PublishedContentType _contentType;
+
+ private void ValidateAndSetProperty(IReadOnlyDictionary valueDictionary, Action setProperty, params string[] potentialKeys)
+ {
+ var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null);
+ if (key == null)
+ {
+ throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + String.Join(",", potentialKeys) + "' elements");
+ }
+
+ setProperty(valueDictionary[key]);
+ _keysAdded.Add(key);
+ }
+ }
+}
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
index 48a63aa8d3..8f354240e0 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
@@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.XPath;
using Examine;
-using Examine.LuceneEngine.Providers;
using Examine.LuceneEngine.SearchCriteria;
using Examine.Providers;
using Lucene.Net.Store;
@@ -16,7 +14,6 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Xml;
-using Umbraco.Web.Models;
using Umbraco.Examine;
using umbraco;
using Umbraco.Core.Cache;
@@ -607,230 +604,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return mediaList;
}
- ///
- /// An IPublishedContent that is represented all by a dictionary.
- ///
- ///
- /// This is a helper class and definitely not intended for public use, it expects that all of the values required
- /// to create an IPublishedContent exist in the dictionary by specific aliases.
- ///
- internal class DictionaryPublishedContent : PublishedContentBase
- {
- // note: I'm not sure this class fully complies with IPublishedContent rules especially
- // I'm not sure that _properties contains all properties including those without a value,
- // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk
-
- // List of properties that will appear in the XML and do not match
- // anything in the ContentType, so they must be ignored.
- private static readonly string[] IgnoredKeys = { "version", "isDoc" };
-
- public DictionaryPublishedContent(
- IReadOnlyDictionary valueDictionary,
- Func getParent,
- Func> getChildren,
- Func getProperty,
- ICacheProvider cacheProvider,
- PublishedContentTypeCache contentTypeCache,
- XPathNavigator nav,
- bool fromExamine)
- {
- if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary));
- if (getParent == null) throw new ArgumentNullException(nameof(getParent));
- if (getProperty == null) throw new ArgumentNullException(nameof(getProperty));
-
- _getParent = new Lazy(() => getParent(ParentId));
- _getChildren = new Lazy>(() => getChildren(Id, nav));
- _getProperty = getProperty;
- _cacheProvider = cacheProvider;
-
- LoadedFromExamine = fromExamine;
-
- ValidateAndSetProperty(valueDictionary, val => _id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int!
- ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key");
- //ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
- ValidateAndSetProperty(valueDictionary, val => _sortOrder = int.Parse(val), "sortOrder");
- ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName");
- ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName");
- ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.ItemTypeFieldName);
- ValidateAndSetProperty(valueDictionary, val => _documentTypeId = int.Parse(val), "nodeType");
- //ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName");
- ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132
- //ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID");
- ValidateAndSetProperty(valueDictionary, val => _creatorId = int.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132
- ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path");
- ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate");
- ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate");
- ValidateAndSetProperty(valueDictionary, val => _level = int.Parse(val), "level");
- ValidateAndSetProperty(valueDictionary, val =>
- {
- int pId;
- ParentId = -1;
- if (int.TryParse(val, out pId))
- {
- ParentId = pId;
- }
- }, "parentID");
-
- _contentType = contentTypeCache.Get(PublishedItemType.Media, _documentTypeAlias);
- _properties = new Collection();
-
- //handle content type properties
- //make sure we create them even if there's no value
- foreach (var propertyType in _contentType.PropertyTypes)
- {
- var alias = propertyType.Alias;
- _keysAdded.Add(alias);
- string value;
- const bool isPreviewing = false; // false :: never preview a media
- var property = valueDictionary.TryGetValue(alias, out value) == false || value == null
- ? new XmlPublishedProperty(propertyType, this, isPreviewing)
- : new XmlPublishedProperty(propertyType, this, isPreviewing, value);
- _properties.Add(property);
- }
-
- //loop through remaining values that haven't been applied
- foreach (var i in valueDictionary.Where(x =>
- _keysAdded.Contains(x.Key) == false // not already processed
- && IgnoredKeys.Contains(x.Key) == false)) // not ignorable
- {
- if (i.Key.InvariantStartsWith("__"))
- {
- // no type for that one, dunno how to convert, drop it
- //IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty);
- //_properties.Add(property);
- }
- else
- {
- // this is a property that does not correspond to anything, ignore and log
- Current.Logger.Warn("Dropping property \"" + i.Key + "\" because it does not belong to the content type.");
- }
- }
- }
-
- private DateTime ParseDateTimeValue(string val)
- {
- if (LoadedFromExamine == false)
- return DateTime.Parse(val);
-
- //we need to parse the date time using Lucene converters
- var ticks = long.Parse(val);
- return new DateTime(ticks);
- }
-
- ///
- /// Flag to get/set if this was laoded from examine cache
- ///
- internal bool LoadedFromExamine { get; }
-
- //private readonly Func _getParent;
- private readonly Lazy _getParent;
- //private readonly Func> _getChildren;
- private readonly Lazy> _getChildren;
- private readonly Func _getProperty;
- private readonly ICacheProvider _cacheProvider;
-
- ///
- /// Returns 'Media' as the item type
- ///
- public override PublishedItemType ItemType => PublishedItemType.Media;
-
- public override IPublishedContent Parent => _getParent.Value;
-
- public int ParentId { get; private set; }
-
- public override int Id => _id;
-
- public override Guid Key => _key;
-
- public override int TemplateId => 0;
-
- public override int SortOrder => _sortOrder;
-
- public override string Name => _name;
-
- public override IReadOnlyDictionary CultureNames => throw new NotSupportedException();
-
- public override string UrlName => _urlName;
-
- public override string DocumentTypeAlias => _documentTypeAlias;
-
- public override int DocumentTypeId => _documentTypeId;
-
- public override string WriterName => _creatorName;
-
- public override string CreatorName => _creatorName;
-
- public override int WriterId => _creatorId;
-
- public override int CreatorId => _creatorId;
-
- public override string Path => _path;
-
- public override DateTime CreateDate => _createDate;
-
- public override DateTime UpdateDate => _updateDate;
-
- public override int Level => _level;
-
- public override bool IsDraft => false;
-
- public override IEnumerable Properties => _properties;
-
- public override IEnumerable Children => _getChildren.Value;
-
- public override IPublishedProperty GetProperty(string alias)
- {
- return _getProperty(this, alias);
- }
-
- public override PublishedContentType ContentType => _contentType;
-
- // override to implement cache
- // cache at context level, ie once for the whole request
- // but cache is not shared by requests because we wouldn't know how to clear it
- public override IPublishedProperty GetProperty(string alias, bool recurse)
- {
- if (recurse == false) return GetProperty(alias);
-
- var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}";
- var cacheProvider = _cacheProvider;
- return cacheProvider.GetCacheItem(key, () => base.GetProperty(alias, true));
- }
-
- private readonly List _keysAdded = new List();
- private int _id;
- private Guid _key;
- //private int _templateId;
- private int _sortOrder;
- private string _name;
- private string _urlName;
- private string _documentTypeAlias;
- private int _documentTypeId;
- //private string _writerName;
- private string _creatorName;
- //private int _writerId;
- private int _creatorId;
- private string _path;
- private DateTime _createDate;
- private DateTime _updateDate;
- //private Guid _version;
- private int _level;
- private readonly ICollection _properties;
- private readonly PublishedContentType _contentType;
-
- private void ValidateAndSetProperty(IReadOnlyDictionary valueDictionary, Action setProperty, params string[] potentialKeys)
- {
- var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null);
- if (key == null)
- {
- throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + string.Join(",", potentialKeys) + "' elements");
- }
-
- setProperty(valueDictionary[key]);
- _keysAdded.Add(key);
- }
- }
-
internal void Resync()
{
// clear recursive properties cached by XmlPublishedContent.GetProperty
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs
index 6f9746c57d..eba0312a46 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs
@@ -150,25 +150,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}
- public override IReadOnlyDictionary CultureNames => throw new NotSupportedException();
+ public override PublishedCultureInfos GetCulture(string culture = ".") => throw new NotSupportedException();
- public override string DocumentTypeAlias
- {
- get
- {
- if (_nodeInitialized == false) InitializeNode();
- return _docTypeAlias;
- }
- }
-
- public override int DocumentTypeId
- {
- get
- {
- if (_nodeInitialized == false) InitializeNode();
- return _docTypeId;
- }
- }
+ public override IReadOnlyDictionary Cultures => throw new NotSupportedException();
public override string WriterName
{
diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs
index fd2ddc51e5..c6d39aa0b0 100644
--- a/src/Umbraco.Web/PublishedContentExtensions.cs
+++ b/src/Umbraco.Web/PublishedContentExtensions.cs
@@ -122,8 +122,8 @@ namespace Umbraco.Web
#endregion
#region Value
-
- // fixme missing variations, but recurse/variations/fallback = ?
+
+ // fixme missing variations, but recurse/variations/fallback = ?
///
/// Recursively gets the value of a content's property identified by its alias.
@@ -163,7 +163,7 @@ namespace Umbraco.Web
/// The alias is case-insensitive.
///
public static object Value(this IPublishedContent content, string alias, string culture = ".", string segment = ".", object defaultValue = default, bool recurse = false)
- {
+ {
// fixme - variations+recurse not implemented here
var property = content.GetProperty(alias, recurse);
return property == null || property.HasValue(culture, segment) == false ? defaultValue : property.GetValue();
@@ -172,7 +172,7 @@ namespace Umbraco.Web
#endregion
#region Value
-
+
///
/// Recursively gets the value of a content's property identified by its alias, converted to a specified type.
///
@@ -290,7 +290,7 @@ namespace Umbraco.Web
/// True if the content is of the specified content type; otherwise false.
public static bool IsDocumentType(this IPublishedContent content, string docTypeAlias)
{
- return content.DocumentTypeAlias.InvariantEquals(docTypeAlias);
+ return content.ContentType.Alias.InvariantEquals(docTypeAlias);
}
///
@@ -477,7 +477,7 @@ namespace Umbraco.Web
/// Does not consider the content itself. Returns all ancestors, of the specified content type.
public static IEnumerable Ancestors(this IPublishedContent content, string contentTypeAlias)
{
- return content.AncestorsOrSelf(false, n => n.DocumentTypeAlias == contentTypeAlias);
+ return content.AncestorsOrSelf(false, n => n.ContentType.Alias == contentTypeAlias);
}
///
@@ -542,7 +542,7 @@ namespace Umbraco.Web
/// May or may not begin with the content itself, depending on its content type.
public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string contentTypeAlias)
{
- return content.AncestorsOrSelf(true, n => n.DocumentTypeAlias == contentTypeAlias);
+ return content.AncestorsOrSelf(true, n => n.ContentType.Alias == contentTypeAlias);
}
///
@@ -605,7 +605,7 @@ namespace Umbraco.Web
/// Does not consider the content itself. May return null.
public static IPublishedContent Ancestor(this IPublishedContent content, string contentTypeAlias)
{
- return content.EnumerateAncestors(false).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias);
+ return content.EnumerateAncestors(false).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias);
}
///
@@ -668,7 +668,7 @@ namespace Umbraco.Web
/// May or may not return the content itself depending on its content type. May return null.
public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias)
{
- return content.EnumerateAncestors(true).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias);
+ return content.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias);
}
///
@@ -781,7 +781,7 @@ namespace Umbraco.Web
public static IEnumerable Descendants(this IPublishedContent content, string contentTypeAlias)
{
- return content.DescendantsOrSelf(false, p => p.DocumentTypeAlias == contentTypeAlias);
+ return content.DescendantsOrSelf(false, p => p.ContentType.Alias == contentTypeAlias);
}
public static IEnumerable Descendants(this IPublishedContent content)
@@ -808,7 +808,7 @@ namespace Umbraco.Web
public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string contentTypeAlias)
{
- return content.DescendantsOrSelf(true, p => p.DocumentTypeAlias == contentTypeAlias);
+ return content.DescendantsOrSelf(true, p => p.ContentType.Alias == contentTypeAlias);
}
public static IEnumerable DescendantsOrSelf(this IPublishedContent content)
@@ -835,7 +835,7 @@ namespace Umbraco.Web
public static IPublishedContent Descendant(this IPublishedContent content, string contentTypeAlias)
{
- return content.EnumerateDescendants(false).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias);
+ return content.EnumerateDescendants(false).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias);
}
public static T Descendant(this IPublishedContent content)
@@ -862,7 +862,7 @@ namespace Umbraco.Web
public static IPublishedContent DescendantOrSelf(this IPublishedContent content, string contentTypeAlias)
{
- return content.EnumerateDescendants(true).FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAlias);
+ return content.EnumerateDescendants(true).FirstOrDefault(x => x.ContentType.Alias == contentTypeAlias);
}
public static T DescendantOrSelf(this IPublishedContent content)
@@ -1018,7 +1018,7 @@ namespace Umbraco.Web
/// The children of the content, of any of the specified types.
public static IEnumerable Children(this IPublishedContent content, params string[] alias)
{
- return content.Children(x => alias.InvariantContains(x.DocumentTypeAlias));
+ return content.Children(x => alias.InvariantContains(x.ContentType.Alias));
}
///
@@ -1094,14 +1094,14 @@ namespace Umbraco.Web
? content.Children.Any()
? content.Children.ElementAt(0)
: null
- : content.Children.FirstOrDefault(x => x.DocumentTypeAlias == contentTypeAliasFilter);
+ : content.Children.FirstOrDefault(x => x.ContentType.Alias == contentTypeAliasFilter);
if (firstNode == null)
return new DataTable(); //no children found
//use new utility class to create table so that we don't have to maintain code in many places, just one
var dt = Core.DataTableExtensions.GenerateDataTable(
//pass in the alias of the first child node since this is the node type we're rendering headers for
- firstNode.DocumentTypeAlias,
+ firstNode.ContentType.Alias,
//pass in the callback to extract the Dictionary of all defined aliases to their names
alias => GetPropertyAliasesAndNames(services, alias),
//pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place.
@@ -1114,7 +1114,7 @@ namespace Umbraco.Web
{
if (contentTypeAliasFilter.IsNullOrWhiteSpace() == false)
{
- if (n.DocumentTypeAlias != contentTypeAliasFilter)
+ if (n.ContentType.Alias != contentTypeAliasFilter)
continue; //skip this one, it doesn't match the filter
}
@@ -1122,7 +1122,7 @@ namespace Umbraco.Web
{
{ "Id", n.Id },
{ "NodeName", n.Name },
- { "NodeTypeAlias", n.DocumentTypeAlias },
+ { "NodeTypeAlias", n.ContentType.Alias },
{ "CreateDate", n.CreateDate },
{ "UpdateDate", n.UpdateDate },
{ "CreatorName", n.CreatorName },
@@ -1208,33 +1208,5 @@ namespace Umbraco.Web
}
#endregion
-
- #region Culture
-
- ///
- /// Return the URL name for the based on the culture specified or default culture defined
- ///
- ///
- ///
- ///
- ///
- public static string GetUrlName(this IPublishedContent content, ILocalizationService localizationService, string culture = null)
- {
- // fixme publishedContent could get ISystemDefaultCultureAccessor injected!
-
- if (content.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral))
- {
- var cultureCode = culture ?? localizationService.GetDefaultLanguageIsoCode(); // fixme kill.kill.kill
- if (cultureCode != null && content.CultureNames.TryGetValue(cultureCode, out var cultureName))
- {
- return cultureName.UrlName;
- }
- }
-
- //if we get here, the content type is invariant or we don't have access to a usable culture code
- return content.UrlName;
- }
-
- #endregion
}
}
diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs
index 17078826ff..41d4d4883c 100644
--- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs
+++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs
@@ -24,6 +24,7 @@ using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Macros;
+using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Profiling;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
@@ -69,8 +70,10 @@ namespace Umbraco.Web.Runtime
//it still needs to use the install controller so we can't do that
composition.Container.RegisterFrom();
- // register the system culture provider
+ // register accessors for cultures
+ // fixme merge the two accessors?
composition.Container.RegisterSingleton();
+ composition.Container.RegisterSingleton();
var typeLoader = composition.Container.GetInstance();
var logger = composition.Container.GetInstance();
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index e0380c898e..e730c0b29c 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -391,6 +391,7 @@
+
diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs
index 61f263f02c..e49bdff9ec 100644
--- a/src/Umbraco.Web/umbraco.presentation/page.cs
+++ b/src/Umbraco.Web/umbraco.presentation/page.cs
@@ -65,7 +65,7 @@ namespace umbraco
throw new ArgumentException("Document request has no node.", "frequest");
populatePageData(frequest.PublishedContent.Id,
- frequest.PublishedContent.Name, frequest.PublishedContent.DocumentTypeId, frequest.PublishedContent.DocumentTypeAlias,
+ frequest.PublishedContent.Name, frequest.PublishedContent.ContentType.Id, frequest.PublishedContent.ContentType.Alias,
frequest.PublishedContent.WriterName, frequest.PublishedContent.CreatorName, frequest.PublishedContent.CreateDate, frequest.PublishedContent.UpdateDate,
frequest.PublishedContent.Path, frequest.PublishedContent.Parent == null ? -1 : frequest.PublishedContent.Parent.Id);
@@ -89,7 +89,7 @@ namespace umbraco
if (doc == null) throw new ArgumentNullException("doc");
populatePageData(doc.Id,
- doc.Name, doc.DocumentTypeId, doc.DocumentTypeAlias,
+ doc.Name, doc.ContentType.Id, doc.ContentType.Alias,
doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate,
doc.Path, doc.Parent == null ? -1 : doc.Parent.Id);
@@ -108,8 +108,8 @@ namespace umbraco
///
/// The content.
/// This is for usage only.
- internal page(IContent content)
- : this(new PagePublishedContent(content))
+ internal page(IContent content, IPublishedVariationContextAccessor variationContextAccessor)
+ : this(new PagePublishedContent(content, variationContextAccessor))
{ }
#endregion
@@ -408,19 +408,24 @@ namespace umbraco
private readonly PublishedContentType _contentType;
private readonly IPublishedProperty[] _properties;
private readonly IPublishedContent _parent;
- private IReadOnlyDictionary _cultureNames;
+ private IReadOnlyDictionary _cultureNames;
+ private IReadOnlyDictionary _cultureInfos;
+ private readonly IPublishedVariationContextAccessor _variationContextAccessor;
+
+ private static readonly IReadOnlyDictionary NoCultureInfos = new Dictionary();
private PagePublishedContent(int id)
{
_id = id;
}
- public PagePublishedContent(IContent inner)
+ public PagePublishedContent(IContent inner, IPublishedVariationContextAccessor variationContextAccessor)
{
if (inner == null)
throw new NullReferenceException("content");
_inner = inner;
+ _variationContextAccessor = variationContextAccessor;
_id = _inner.Id;
_key = _inner.Key;
@@ -472,23 +477,31 @@ namespace umbraco
get { return _inner.Name; }
}
- public IReadOnlyDictionary CultureNames
+ public PublishedCultureInfos GetCulture(string culture = ".")
{
- get
- {
- if (!_inner.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral))
- return null;
-
- if (_cultureNames == null)
- {
- var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
- foreach (var c in _inner.Names)
- {
- d[c.Key] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment());
- }
- _cultureNames = d;
- }
- return _cultureNames;
+ // handle context culture
+ if (culture == ".")
+ culture = _variationContextAccessor.Context.Culture;
+
+ // no invariant culture infos
+ if (culture == null) return null;
+
+ // get
+ return Cultures.TryGetValue(culture, out var cultureInfos) ? cultureInfos : null;
+ }
+
+ public IReadOnlyDictionary Cultures
+ {
+ get
+ {
+ if (!_inner.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral)) // fixme CultureSegment?
+ return NoCultureInfos;
+
+ if (_cultureInfos != null)
+ return _cultureInfos;
+
+ return _cultureInfos = _inner.Names
+ .ToDictionary(x => x.Key, x => new PublishedCultureInfos(x.Key, x.Value, false, DateTime.MinValue)); // fixme values!
}
}