2018-06-29 19:52:40 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Globalization;
|
2019-01-30 17:50:13 +11:00
|
|
|
|
using System.Linq;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
using System.Xml;
|
|
|
|
|
|
using System.Xml.XPath;
|
2021-02-09 10:22:42 +01:00
|
|
|
|
using Umbraco.Cms.Core;
|
|
|
|
|
|
using Umbraco.Cms.Core.Cache;
|
|
|
|
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
|
|
|
|
using Umbraco.Cms.Core.Models.PublishedContent;
|
|
|
|
|
|
using Umbraco.Cms.Core.PublishedCache;
|
|
|
|
|
|
using Umbraco.Cms.Core.Routing;
|
|
|
|
|
|
using Umbraco.Cms.Core.Xml;
|
2021-02-09 11:26:22 +01:00
|
|
|
|
using Umbraco.Extensions;
|
2019-12-19 10:43:00 +01:00
|
|
|
|
using Umbraco.Tests.TestHelpers;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
|
2019-01-30 17:50:13 +11:00
|
|
|
|
namespace Umbraco.Tests.LegacyXmlPublishedCache
|
2018-06-29 19:52:40 +02:00
|
|
|
|
{
|
|
|
|
|
|
internal class PublishedContentCache : PublishedCacheBase, IPublishedContentCache
|
|
|
|
|
|
{
|
2019-01-18 08:14:08 +01:00
|
|
|
|
private readonly IAppCache _appCache;
|
2020-09-08 13:03:43 +02:00
|
|
|
|
private readonly GlobalSettings _globalSettings;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
private readonly RoutesCache _routesCache;
|
2019-12-19 10:43:00 +01:00
|
|
|
|
private readonly IVariationContextAccessor _variationContextAccessor;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
private readonly IDomainCache _domainCache;
|
|
|
|
|
|
private readonly PublishedContentTypeCache _contentTypeCache;
|
|
|
|
|
|
|
|
|
|
|
|
// initialize a PublishedContentCache instance with
|
|
|
|
|
|
// an XmlStore containing the master xml
|
2019-01-18 08:14:08 +01:00
|
|
|
|
// an IAppCache that should be at request-level
|
2018-06-29 19:52:40 +02:00
|
|
|
|
// a RoutesCache - need to cleanup that one
|
|
|
|
|
|
// a preview token string (or null if not previewing)
|
|
|
|
|
|
public PublishedContentCache(
|
|
|
|
|
|
XmlStore xmlStore, // an XmlStore containing the master xml
|
|
|
|
|
|
IDomainCache domainCache, // an IDomainCache implementation
|
2019-01-18 08:14:08 +01:00
|
|
|
|
IAppCache appCache, // an IAppCache that should be at request-level
|
2020-09-08 13:03:43 +02:00
|
|
|
|
GlobalSettings globalSettings,
|
2018-06-29 19:52:40 +02:00
|
|
|
|
PublishedContentTypeCache contentTypeCache, // a PublishedContentType cache
|
|
|
|
|
|
RoutesCache routesCache, // a RoutesCache
|
2019-12-19 10:43:00 +01:00
|
|
|
|
IVariationContextAccessor variationContextAccessor,
|
2018-06-29 19:52:40 +02:00
|
|
|
|
string previewToken) // a preview token string (or null if not previewing)
|
|
|
|
|
|
: base(previewToken.IsNullOrWhiteSpace() == false)
|
|
|
|
|
|
{
|
2019-01-18 08:14:08 +01:00
|
|
|
|
_appCache = appCache;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
_globalSettings = globalSettings;
|
|
|
|
|
|
_routesCache = routesCache; // may be null for unit-testing
|
2019-12-19 10:43:00 +01:00
|
|
|
|
_variationContextAccessor = variationContextAccessor;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
_contentTypeCache = contentTypeCache;
|
|
|
|
|
|
_domainCache = domainCache;
|
|
|
|
|
|
|
|
|
|
|
|
_xmlStore = xmlStore;
|
|
|
|
|
|
_xml = _xmlStore.Xml; // capture - because the cache has to remain consistent
|
|
|
|
|
|
|
|
|
|
|
|
if (previewToken.IsNullOrWhiteSpace() == false)
|
|
|
|
|
|
_previewContent = new PreviewContent(_xmlStore, previewToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region Unit Tests
|
|
|
|
|
|
|
|
|
|
|
|
// for INTERNAL, UNIT TESTS use ONLY
|
|
|
|
|
|
internal RoutesCache RoutesCache => _routesCache;
|
|
|
|
|
|
|
|
|
|
|
|
// for INTERNAL, UNIT TESTS use ONLY
|
|
|
|
|
|
internal XmlStore XmlStore => _xmlStore;
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Routes
|
|
|
|
|
|
|
|
|
|
|
|
public virtual IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (route == null) throw new ArgumentNullException(nameof(route));
|
|
|
|
|
|
|
|
|
|
|
|
// try to get from cache if not previewing
|
|
|
|
|
|
var contentId = preview || _routesCache == null ? 0 : _routesCache.GetNodeId(route);
|
|
|
|
|
|
|
|
|
|
|
|
// if found id in cache then get corresponding content
|
|
|
|
|
|
// and clear cache if not found - for whatever reason
|
|
|
|
|
|
IPublishedContent content = null;
|
|
|
|
|
|
if (contentId > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
content = GetById(preview, contentId);
|
|
|
|
|
|
if (content == null)
|
|
|
|
|
|
_routesCache?.ClearNode(contentId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// still have nothing? actually determine the id
|
|
|
|
|
|
hideTopLevelNode = hideTopLevelNode ?? _globalSettings.HideTopLevelNodeFromPath; // default = settings
|
|
|
|
|
|
content = content ?? DetermineIdByRoute(preview, route, hideTopLevelNode.Value);
|
|
|
|
|
|
|
|
|
|
|
|
// cache if we have a content and not previewing
|
|
|
|
|
|
if (content != null && preview == false && _routesCache != null)
|
|
|
|
|
|
AddToCacheIfDeepestRoute(content, route);
|
|
|
|
|
|
|
|
|
|
|
|
return content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void AddToCacheIfDeepestRoute(IPublishedContent content, string route)
|
|
|
|
|
|
{
|
|
|
|
|
|
var domainRootNodeId = route.StartsWith("/") ? -1 : int.Parse(route.Substring(0, route.IndexOf('/')));
|
|
|
|
|
|
|
|
|
|
|
|
// so we have a route that maps to a content... say "1234/path/to/content" - however, there could be a
|
|
|
|
|
|
// domain set on "to" and route "4567/content" would also map to the same content - and due to how
|
2020-10-05 20:48:38 +02:00
|
|
|
|
// URLs computing work (by walking the tree up to the first domain we find) it is that second route
|
2018-06-29 19:52:40 +02:00
|
|
|
|
// that would be returned - the "deepest" route - and that is the route we want to cache, *not* the
|
|
|
|
|
|
// longer one - so make sure we don't cache the wrong route
|
|
|
|
|
|
|
2019-04-17 14:41:54 +02:00
|
|
|
|
var deepest = DomainUtilities.ExistsDomainInPath(_domainCache.GetAll(false), content.Path, domainRootNodeId) == false;
|
2018-06-29 19:52:40 +02:00
|
|
|
|
|
|
|
|
|
|
if (deepest)
|
|
|
|
|
|
_routesCache.Store(content.Id, route, true); // trusted route
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return GetByRoute(PreviewDefault, route, hideTopLevelNode);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual string GetRouteById(bool preview, int contentId, string culture = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// try to get from cache if not previewing
|
|
|
|
|
|
var route = preview || _routesCache == null ? null : _routesCache.GetRoute(contentId);
|
|
|
|
|
|
|
|
|
|
|
|
// if found in cache then return
|
|
|
|
|
|
if (route != null)
|
|
|
|
|
|
return route;
|
|
|
|
|
|
|
|
|
|
|
|
// else actually determine the route
|
|
|
|
|
|
route = DetermineRouteById(preview, contentId);
|
|
|
|
|
|
|
|
|
|
|
|
// node not found
|
|
|
|
|
|
if (route == null)
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
// cache the route BUT do NOT trust it as it can be a colliding route
|
|
|
|
|
|
// meaning if we GetRouteById again, we'll get it from cache, but it
|
|
|
|
|
|
// won't be used for inbound routing
|
|
|
|
|
|
if (preview == false)
|
|
|
|
|
|
_routesCache.Store(contentId, route, false);
|
|
|
|
|
|
|
|
|
|
|
|
return route;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string GetRouteById(int contentId, string culture = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return GetRouteById(PreviewDefault, contentId, culture);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IPublishedContent DetermineIdByRoute(bool preview, string route, bool hideTopLevelNode)
|
|
|
|
|
|
{
|
|
|
|
|
|
//the route always needs to be lower case because we only store the urlName attribute in lower case
|
|
|
|
|
|
route = route?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(route));
|
|
|
|
|
|
|
|
|
|
|
|
var pos = route.IndexOf('/');
|
|
|
|
|
|
var path = pos == 0 ? route : route.Substring(pos);
|
|
|
|
|
|
var startNodeId = pos == 0 ? 0 : int.Parse(route.Substring(0, pos));
|
|
|
|
|
|
|
|
|
|
|
|
//check if we can find the node in our xml cache
|
|
|
|
|
|
var id = NavigateRoute(preview, startNodeId, path, hideTopLevelNode);
|
|
|
|
|
|
return id > 0 ? GetById(preview, id) : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static XmlElement GetXmlElementChildWithLowestSortOrder(XmlNode element)
|
|
|
|
|
|
{
|
|
|
|
|
|
XmlElement elt = null;
|
|
|
|
|
|
var min = int.MaxValue;
|
|
|
|
|
|
foreach (var n in element.ChildNodes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var e = n as XmlElement;
|
|
|
|
|
|
if (e == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var sortOrder = int.Parse(e.GetAttribute("sortOrder"));
|
|
|
|
|
|
if (sortOrder >= min) continue;
|
|
|
|
|
|
|
|
|
|
|
|
min = sortOrder;
|
|
|
|
|
|
elt = e;
|
|
|
|
|
|
}
|
|
|
|
|
|
return elt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int NavigateRoute(bool preview, int startNodeId, string path, bool hideTopLevelNode)
|
|
|
|
|
|
{
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
XmlElement elt;
|
|
|
|
|
|
|
|
|
|
|
|
// empty path
|
|
|
|
|
|
if (path == string.Empty || path == "/")
|
|
|
|
|
|
{
|
|
|
|
|
|
if (startNodeId > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
elt = xml.GetElementById(startNodeId.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
|
return elt == null ? -1 : startNodeId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
elt = GetXmlElementChildWithLowestSortOrder(xml.DocumentElement);
|
|
|
|
|
|
return elt == null ? -1 : int.Parse(elt.GetAttribute("id"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// non-empty path
|
|
|
|
|
|
elt = startNodeId <= 0
|
|
|
|
|
|
? xml.DocumentElement
|
|
|
|
|
|
: xml.GetElementById(startNodeId.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
|
if (elt == null) return -1;
|
|
|
|
|
|
|
|
|
|
|
|
var urlParts = path.Split(SlashChar, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
|
|
|
|
|
|
if (hideTopLevelNode && startNodeId <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
//Don't use OfType<T> or Cast<T>, this is critical code, all ChildNodes are XmlElement so explicitly cast
|
|
|
|
|
|
// https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198
|
|
|
|
|
|
foreach (var n in elt.ChildNodes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var e = n as XmlElement;
|
|
|
|
|
|
if (e == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var id = NavigateElementRoute(e, urlParts);
|
|
|
|
|
|
if (id > 0) return id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (urlParts.Length > 1)
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return NavigateElementRoute(elt, urlParts);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static int NavigateElementRoute(XmlElement elt, string[] urlParts)
|
|
|
|
|
|
{
|
|
|
|
|
|
var found = true;
|
|
|
|
|
|
var i = 0;
|
|
|
|
|
|
while (found && i < urlParts.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
found = false;
|
|
|
|
|
|
//Don't use OfType<T> or Cast<T>, this is critical code, all ChildNodes are XmlElement so explicitly cast
|
|
|
|
|
|
// https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198
|
|
|
|
|
|
var sortOrder = -1;
|
|
|
|
|
|
foreach (var o in elt.ChildNodes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var child = o as XmlElement;
|
|
|
|
|
|
if (child == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var noNode = child.GetAttributeNode("isDoc") == null;
|
|
|
|
|
|
if (noNode) continue;
|
|
|
|
|
|
if (child.GetAttribute("urlName") != urlParts[i]) continue;
|
|
|
|
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
|
|
|
|
var so = int.Parse(child.GetAttribute("sortOrder"));
|
|
|
|
|
|
if (sortOrder >= 0 && so >= sortOrder) continue;
|
|
|
|
|
|
|
|
|
|
|
|
sortOrder = so;
|
|
|
|
|
|
elt = child;
|
|
|
|
|
|
}
|
|
|
|
|
|
i++;
|
|
|
|
|
|
}
|
|
|
|
|
|
return found ? int.Parse(elt.GetAttribute("id")) : -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string DetermineRouteById(bool preview, int contentId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var node = GetById(preview, contentId);
|
|
|
|
|
|
if (node == null) return null;
|
|
|
|
|
|
|
|
|
|
|
|
// walk up from that node until we hit a node with a domain,
|
2020-10-05 20:48:38 +02:00
|
|
|
|
// or we reach the content root, collecting URLs in the way
|
2018-06-29 19:52:40 +02:00
|
|
|
|
var pathParts = new List<string>();
|
|
|
|
|
|
var n = node;
|
2019-04-17 14:41:54 +02:00
|
|
|
|
var hasDomains = _domainCache.HasAssigned(n.Id);
|
2018-06-29 19:52:40 +02:00
|
|
|
|
while (hasDomains == false && n != null) // n is null at root
|
|
|
|
|
|
{
|
|
|
|
|
|
// get the url
|
2019-12-19 10:43:00 +01:00
|
|
|
|
var urlName = n.UrlSegment(TestHelper.VariationContextAccessor);
|
2018-06-29 19:52:40 +02:00
|
|
|
|
pathParts.Add(urlName);
|
|
|
|
|
|
|
|
|
|
|
|
// move to parent node
|
2019-06-06 16:54:00 +02:00
|
|
|
|
n = n.Parent;
|
2019-04-17 14:41:54 +02:00
|
|
|
|
hasDomains = n != null && _domainCache.HasAssigned(n.Id);
|
2018-06-29 19:52:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
|
|
|
|
|
if (hasDomains == false && _globalSettings.HideTopLevelNodeFromPath)
|
|
|
|
|
|
{
|
2019-06-06 16:54:00 +02:00
|
|
|
|
if (node.Parent == null)
|
2018-06-29 19:52:40 +02:00
|
|
|
|
{
|
|
|
|
|
|
var rootNode = GetByRoute(preview, "/", true);
|
|
|
|
|
|
if (rootNode == null)
|
|
|
|
|
|
throw new Exception("Failed to get node at /.");
|
|
|
|
|
|
if (rootNode.Id == node.Id) // remove only if we're the default node
|
|
|
|
|
|
pathParts.RemoveAt(pathParts.Count - 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
pathParts.RemoveAt(pathParts.Count - 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// assemble the route
|
|
|
|
|
|
pathParts.Reverse();
|
|
|
|
|
|
var path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc
|
|
|
|
|
|
var route = (n?.Id.ToString(CultureInfo.InvariantCulture) ?? "") + path;
|
|
|
|
|
|
|
|
|
|
|
|
return route;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region XPath Strings
|
|
|
|
|
|
|
|
|
|
|
|
static class XPathStrings
|
|
|
|
|
|
{
|
|
|
|
|
|
public const string Root = "/root";
|
|
|
|
|
|
public const string RootDocuments = "/root/* [@isDoc]";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Converters
|
|
|
|
|
|
|
|
|
|
|
|
private IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing)
|
|
|
|
|
|
{
|
2019-12-19 10:43:00 +01:00
|
|
|
|
return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor);
|
2018-06-29 19:52:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private IEnumerable<IPublishedContent> ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing)
|
|
|
|
|
|
{
|
|
|
|
|
|
return xmlNodes.Cast<XmlNode>()
|
2019-12-19 10:43:00 +01:00
|
|
|
|
.Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor));
|
2018-06-29 19:52:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Getters
|
|
|
|
|
|
|
|
|
|
|
|
public override IPublishedContent GetById(bool preview, int nodeId)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ConvertToDocument(GetXml(preview).GetElementById(nodeId.ToString(CultureInfo.InvariantCulture)), preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override IPublishedContent GetById(bool preview, Guid nodeId)
|
|
|
|
|
|
{
|
|
|
|
|
|
// implement this, but in a more efficient way
|
|
|
|
|
|
//const string xpath = "//* [@isDoc and @key=$guid]";
|
|
|
|
|
|
//return GetSingleByXPath(preview, xpath, new[] { new XPathVariable("guid", nodeId.ToString()) });
|
|
|
|
|
|
|
|
|
|
|
|
var keyMatch = nodeId.ToString();
|
|
|
|
|
|
|
|
|
|
|
|
var nav = GetXml(preview).CreateNavigator();
|
|
|
|
|
|
if (nav.MoveToFirstChild() == false) return null; // from / to /root
|
|
|
|
|
|
if (nav.MoveToFirstChild() == false) return null; // from /root to /root/*
|
|
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
var isDoc = false;
|
|
|
|
|
|
string key = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (nav.HasAttributes)
|
|
|
|
|
|
{
|
|
|
|
|
|
nav.MoveToFirstAttribute();
|
|
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
if (nav.Name == "isDoc") isDoc = true;
|
|
|
|
|
|
if (nav.Name == "key") key = nav.Value;
|
|
|
|
|
|
if (isDoc && key != null) break;
|
|
|
|
|
|
} while (nav.MoveToNextAttribute());
|
|
|
|
|
|
nav.MoveToParent();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isDoc == false || key != keyMatch)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isDoc && nav.MoveToFirstChild())
|
|
|
|
|
|
continue;
|
|
|
|
|
|
while (nav.MoveToNext(XPathNodeType.Element) == false)
|
|
|
|
|
|
if (nav.MoveToParent() == false || nav.NodeType == XPathNodeType.Root) return null;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var elt = nav.UnderlyingObject as XmlNode;
|
|
|
|
|
|
return ConvertToDocument(elt, preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-04-12 16:05:43 +02:00
|
|
|
|
public override IPublishedContent GetById(bool preview, Udi nodeId)
|
|
|
|
|
|
=> throw new NotSupportedException();
|
|
|
|
|
|
|
2018-06-29 19:52:40 +02:00
|
|
|
|
public override bool HasById(bool preview, int contentId)
|
|
|
|
|
|
{
|
|
|
|
|
|
return GetXml(preview).CreateNavigator().MoveToId(contentId.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-07 11:15:58 +02:00
|
|
|
|
public override IEnumerable<IPublishedContent> GetAtRoot(bool preview, string culture = null)
|
2018-06-29 19:52:40 +02:00
|
|
|
|
{
|
|
|
|
|
|
return ConvertToDocuments(GetXml(preview).SelectNodes(XPathStrings.RootDocuments), preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (xpath == null) throw new ArgumentNullException(nameof(xpath));
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(xpath)) return null;
|
|
|
|
|
|
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
var node = vars == null
|
|
|
|
|
|
? xml.SelectSingleNode(xpath)
|
|
|
|
|
|
: xml.SelectSingleNode(xpath, vars);
|
|
|
|
|
|
return ConvertToDocument(node, preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (xpath == null) throw new ArgumentNullException(nameof(xpath));
|
|
|
|
|
|
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
var node = vars == null
|
|
|
|
|
|
? xml.SelectSingleNode(xpath)
|
|
|
|
|
|
: xml.SelectSingleNode(xpath, vars);
|
|
|
|
|
|
return ConvertToDocument(node, preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override IEnumerable<IPublishedContent> GetByXPath(bool preview, string xpath, XPathVariable[] vars)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (xpath == null) throw new ArgumentNullException(nameof(xpath));
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(xpath)) return Enumerable.Empty<IPublishedContent>();
|
|
|
|
|
|
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
var nodes = vars == null
|
|
|
|
|
|
? xml.SelectNodes(xpath)
|
|
|
|
|
|
: xml.SelectNodes(xpath, vars);
|
|
|
|
|
|
return ConvertToDocuments(nodes, preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override IEnumerable<IPublishedContent> GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (xpath == null) throw new ArgumentNullException(nameof(xpath));
|
|
|
|
|
|
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
var nodes = vars == null
|
|
|
|
|
|
? xml.SelectNodes(xpath)
|
|
|
|
|
|
: xml.SelectNodes(xpath, vars);
|
|
|
|
|
|
return ConvertToDocuments(nodes, preview);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override bool HasContent(bool preview)
|
|
|
|
|
|
{
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
var node = xml?.SelectSingleNode(XPathStrings.RootDocuments);
|
|
|
|
|
|
return node != null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override XPathNavigator CreateNavigator(bool preview)
|
|
|
|
|
|
{
|
|
|
|
|
|
var xml = GetXml(preview);
|
|
|
|
|
|
return xml.CreateNavigator();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override XPathNavigator CreateNodeNavigator(int id, bool preview)
|
|
|
|
|
|
{
|
|
|
|
|
|
// hackish - backward compatibility ;-(
|
|
|
|
|
|
|
|
|
|
|
|
XPathNavigator navigator = null;
|
|
|
|
|
|
|
|
|
|
|
|
if (preview)
|
|
|
|
|
|
{
|
|
|
|
|
|
var node = _xmlStore.GetPreviewXmlNode(id);
|
|
|
|
|
|
if (node != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
navigator = node.CreateNavigator();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var node = GetXml(false).GetElementById(id.ToInvariantString());
|
|
|
|
|
|
if (node != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var doc = new XmlDocument();
|
|
|
|
|
|
var clone = doc.ImportNode(node, false);
|
|
|
|
|
|
var props = node.SelectNodes("./* [not(@id)]");
|
|
|
|
|
|
if (props == null) throw new Exception("oops");
|
|
|
|
|
|
foreach (var n in props.Cast<XmlNode>())
|
|
|
|
|
|
clone.AppendChild(doc.ImportNode(n, true));
|
|
|
|
|
|
navigator = node.CreateNavigator();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return navigator;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Legacy Xml
|
|
|
|
|
|
|
|
|
|
|
|
private readonly XmlStore _xmlStore;
|
|
|
|
|
|
private XmlDocument _xml;
|
|
|
|
|
|
private readonly PreviewContent _previewContent;
|
|
|
|
|
|
|
|
|
|
|
|
internal XmlDocument GetXml(bool preview)
|
|
|
|
|
|
{
|
|
|
|
|
|
// not trying to be thread-safe here, that's not the point
|
|
|
|
|
|
|
|
|
|
|
|
if (preview == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// if there's a current enlisted reader/writer, use its xml
|
|
|
|
|
|
var tempXml = _xmlStore.TempXml;
|
|
|
|
|
|
if (tempXml != null) return tempXml;
|
|
|
|
|
|
return _xml;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Xml cache does not support retrieving preview content when not previewing
|
|
|
|
|
|
if (_previewContent == null)
|
|
|
|
|
|
throw new InvalidOperationException("Cannot retrieve preview content when not previewing.");
|
|
|
|
|
|
|
|
|
|
|
|
// PreviewContent tries to load the Xml once and if it fails,
|
|
|
|
|
|
// it invalidates itself and always return null for XmlContent.
|
|
|
|
|
|
var previewXml = _previewContent.XmlContent;
|
|
|
|
|
|
return previewXml ?? _xml;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void Resync(XmlDocument xml)
|
|
|
|
|
|
{
|
|
|
|
|
|
_xml = xml; // re-capture
|
|
|
|
|
|
|
|
|
|
|
|
// note: we're not resyncing "preview" because that would mean re-building the whole
|
|
|
|
|
|
// preview set which is costly, so basically when previewing, there will be no resync.
|
|
|
|
|
|
|
|
|
|
|
|
// clear recursive properties cached by XmlPublishedContent.GetProperty
|
|
|
|
|
|
// assume that nothing else is going to cache IPublishedProperty items (else would need to do ByKeySearch)
|
|
|
|
|
|
// NOTE also clears all the media cache properties, which is OK (see media cache)
|
2019-01-18 08:14:08 +01:00
|
|
|
|
_appCache.ClearOfType<IPublishedProperty>();
|
|
|
|
|
|
//_appCache.ClearCacheByKeySearch("XmlPublishedCache.PublishedContentCache:RecursiveProperty-");
|
2018-06-29 19:52:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region XPathQuery
|
|
|
|
|
|
|
|
|
|
|
|
static readonly char[] SlashChar = { '/' };
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Content types
|
|
|
|
|
|
|
Block Editor List (#8273)
* add style to create-option in itempicker + removing overflow hidden
* style adjustment
* clean up of html
* correct sentence to use the number 7
* correct overlays, so they can use size
* numberrange prevalue editor
* add confirmRemove overlay
* correcting primary btn colors
* move confirmMessage below content of overlay.
* min max validation for numberrange
* remove comment
* improved actions for block list
* use file tree for view picker
* style adjustment to border of creator item in item-picker
* vertical align
* clean up + validation limit range
* rename ElementTypes to Blocks
* implement block list editor
* renaming
* use Chrome Headless for unit tests
* test for blockEditorService
* safer code
* block list editor
* rename view to overlayView
* block editor work
* Revert "rename view to overlayView"
This reverts commit 5b910c178a4f193d190367c4f1da3402aa8c4d0e.
* block editor implementation
* sync models
* block list editor copy paste feature
* order var declarations
* remove unused paste function
* block list editor better naming
* simpler label generation
* clean up
* compile config for test mode
* Chrome Debug for VS code
* promise test working
* space change
* another two tests
* more tests for block list editor
* provide key on blockModel for angularJS performance optimization
* syncronization from infinite editing layers
* use an isolated scope
* more tests
* fix C# test
* remove unused block watcher component
* clean css
* only show on hover or focus for block-actions
* clean up and prepare for implementing settings
* remove console
* Add ability to render block list editor using Grid style rendering extension
* Enable Block List Editor settings editing
* Add Stacked Content to Block List migration
* Block Editor: Clean-up, refactoring, one step closer being ready for Content-Apps
* changes naming to Submit, to avoid misunderstanding.
* use a common variable on the block model for isOpen, to be able to make Actions show when open.
* NotSupported property editor, to be used when an editor is not supported in the given context.
* remove unused controller
* Hide group header if only one group is presented
* rename notsupport property editor css class
* smaller header for property group
* hide description if no description is presented
* css adjustments
* Inline create button styling: Better spacing, darker blue color for Focus Outline, moving the plus icon to mouse position for better visual appearance.
* css correction
* Add references for picked items
* Revert commit 45e892f3505059674779c6e1a43084a367c2862f - Changes api to GetData
* Use the .Data propertry as opposed to GetData in this PartialView
* Fix block list test failures
* Just parsing layout as model for partial views.
* minor adjustments
* Remove DB migrations so that they can be reviewed as a block
* Add migrations for new block editor
* Update default rendering partial view
* Add error handling to default template
* Handle color picker data in stacked content
* BlockList PreValue Editor opens configurations as overlay
* translation for prevalue editor property group headlines
* blockcard corrections
* block list prevalue corrections
* revert agressive clean up
* Block Picker
* MaxPropertyWidth PreValue + Implementation
* Incorporate latest block list editor changes, update migration for changed configuration
* Change declared converter type
* Handle invalid data type references
* Remove code duplicated from PR #7957
* use ElementModel for the ContentModel of an ElementType. So we can use ElementTypeModel for the ModelDefinition aka. the Type.
* do still show itempicker for BlockConfiguration even though there is no ElementTypes to pick. This enables the option to create a new ElementType to be used.
* use the right wrapper, for correct spacing
* parse item
* correct fallback for label
* removed unused callback
* paste feature for block-picker
* localize block-picker tabs
* Slightly change for shadow on block-picker item hover
* Localization of BlockEditor Settings Tab
* localizationService
* only filter when more than 8 items available
* Add multiple blocks if hold down CTRL or SuperKey
* adds notes
* ability to add a scoped stylesheet for block view
* make scoped block draggable + style adjustments
* provide index for custom view
* rename contentModel to data + rename layoutModel to layout
* clean up
* more localization
* openSettings option for block editor
* minor changes for a better developer experience
* paste mistake corrected
* only manipulate content-content-app if its present
* make small overlays slightly bigger
* moved block list entry editor into block list editor folder
* limit labelinterpretator to only runs ones pr. edit. and lets make sure to have a label thought we dont have any properties.
* fixed inline views gulp watcher
* changed vm to a better controller instance name
* make watch for views work again.
* able to re run watch
* make js up to date
* fix white background of image-picker
* media-picker container class
* loading indication
* adjust unit tests to latest interface
* getting started on JS Docs
* converting code to use contentTypeKey instead of contentTypeAlias, still missing a few TODOs.
* revert change
* add todo
* use Guid for Key
* use key
* Updates the caching layer to handle GUID keys for content types while preserving backwards compat, fixes unit tests, removes the strongly typed lists for the block editor value since it's unecessary
* Reverts the nested content changes, fixes up the GetEmptyByKey
* Returns ContentTypeKey from the server, updates js to use this everywhere and fix all js tests.
* Allows key in SimpleContentType
* correct for the new spelling
* appended this. since the method is a non-static class method.
* only add background-image if value isnt null
* simplifyed limits validation
* clean up
* no need to execute a digest.
* define the full model for new configurations
* removed setDataFromBlockModel and general clean up and added documentation
* default size should be medium
* use retriveValuesFrom method to transfer data.
* ability to disable an navigation item
* createElementTypeAndCallback working again for settings.
* still have the ability to retrive a scaffold model by alias, since we are still using alias in clipboard service.
* disable clipboard tab if no available paste options
* ups, should stay as alias
* disable clipboard when empty
* use property alias for content add button
* use a grey that can be seen on top of grey and on top of white.
* some accessibility improvements
* rename entry to block
* appended ' and added space in Element Type
* use background color for hover to corospond with active state
* make nested content unsupported
* Moving BlockEditorModelObject into its own file for Documentation purpose. Same goes for renaming BlockModel to BlockObject. and a lot of documentation written.
* fix links in js docs
* added a blocklistentryeditor for unsupported blocks
* ability to retrive the $scope from UmbVariantContentEditors, needed for Block Editor to get a scope existing across variants and splitview.
* Appending the block objects to layout, to share it across variants and in split-view.
* removed trailing comma
* Unsupported block
* Dont copy unsupported blocks
* use grey color for unsupported block
* text correction
* we dont have this fallback anymore
* sort properties
* Text change
* css correction
* use active to determine if an inline editor is expanded. To enable the inline editor to be expanded on creation.
* using udi instead of key.
* bringing the runtime block key back
* live editing ability
* live editing for settings data
* call formSubmit before property editor are removed from DOM. Both for overlay-editing and inline-editing. Fire an postFormSubmitting event after formSubmitting event to let editors know that data could have been updated.
* make sure settings object exists
* only set active to false if it was false before opening the editor.
* update test with new scope parameter
* move destroy responsibility to blockObject
* rename onDestroy to destroy
* added some JS-Docs
* correction jsDocs
* Update ElementType Resource to not use hardcoded URL but to use the Umbraco.Sys.ServerVariables.umbracoUrls instead
* Remove partially completed ConvertToElement migration, fixed in issue 7939 instead.
* Remove external property editor migration
* corrected naming of umbBlockListScopedBlock and umbBlockListBlock
* correct ngdoc type
* removed vscode specific configuration of karma
* Finished Todo, gets name of documentType if copying all entities in an infinite editor
* changed comment from TODO to something that explains the state.
* stop tracking build output files.
* rename files to match file name conventions
* this should not happen.
* remove dublicated code
* rename requestCopyBlock to copyBlock
* make sure images does not repeat.
* scale thumbnail for block showcase
* renamed blockcard.component to umb-block-card and moved it.
* removed inline style
* correct style location
* corrected filepath
* corrected file path
* keep elementTypes up to date through the EventService.
* mark Umbraco.BlockList as unsupported inside Nested Content
* correct js docs name
* remove comment
* remove comment
* remove unused controller
* rename inline method name
* corrected spelling mistake
* remove not very used vars
* make binding one-way
* split in multiple lines
* corrected default rendering
* removing documentation that is relevant for developers of new block editors. this documentation will be transfered to Our documentation.
* added danish translation
* corrected blog to blok
* Remove invalid using statement
* use native forEach
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Benjamin Carleski <benjamin@proworks.com>
Co-authored-by: Warren Buckley <warren@umbraco.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.com>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Claus <claus@claus.nu>
2020-06-30 19:52:42 +10:00
|
|
|
|
public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Content, id);
|
2018-06-29 19:52:40 +02:00
|
|
|
|
|
Block Editor List (#8273)
* add style to create-option in itempicker + removing overflow hidden
* style adjustment
* clean up of html
* correct sentence to use the number 7
* correct overlays, so they can use size
* numberrange prevalue editor
* add confirmRemove overlay
* correcting primary btn colors
* move confirmMessage below content of overlay.
* min max validation for numberrange
* remove comment
* improved actions for block list
* use file tree for view picker
* style adjustment to border of creator item in item-picker
* vertical align
* clean up + validation limit range
* rename ElementTypes to Blocks
* implement block list editor
* renaming
* use Chrome Headless for unit tests
* test for blockEditorService
* safer code
* block list editor
* rename view to overlayView
* block editor work
* Revert "rename view to overlayView"
This reverts commit 5b910c178a4f193d190367c4f1da3402aa8c4d0e.
* block editor implementation
* sync models
* block list editor copy paste feature
* order var declarations
* remove unused paste function
* block list editor better naming
* simpler label generation
* clean up
* compile config for test mode
* Chrome Debug for VS code
* promise test working
* space change
* another two tests
* more tests for block list editor
* provide key on blockModel for angularJS performance optimization
* syncronization from infinite editing layers
* use an isolated scope
* more tests
* fix C# test
* remove unused block watcher component
* clean css
* only show on hover or focus for block-actions
* clean up and prepare for implementing settings
* remove console
* Add ability to render block list editor using Grid style rendering extension
* Enable Block List Editor settings editing
* Add Stacked Content to Block List migration
* Block Editor: Clean-up, refactoring, one step closer being ready for Content-Apps
* changes naming to Submit, to avoid misunderstanding.
* use a common variable on the block model for isOpen, to be able to make Actions show when open.
* NotSupported property editor, to be used when an editor is not supported in the given context.
* remove unused controller
* Hide group header if only one group is presented
* rename notsupport property editor css class
* smaller header for property group
* hide description if no description is presented
* css adjustments
* Inline create button styling: Better spacing, darker blue color for Focus Outline, moving the plus icon to mouse position for better visual appearance.
* css correction
* Add references for picked items
* Revert commit 45e892f3505059674779c6e1a43084a367c2862f - Changes api to GetData
* Use the .Data propertry as opposed to GetData in this PartialView
* Fix block list test failures
* Just parsing layout as model for partial views.
* minor adjustments
* Remove DB migrations so that they can be reviewed as a block
* Add migrations for new block editor
* Update default rendering partial view
* Add error handling to default template
* Handle color picker data in stacked content
* BlockList PreValue Editor opens configurations as overlay
* translation for prevalue editor property group headlines
* blockcard corrections
* block list prevalue corrections
* revert agressive clean up
* Block Picker
* MaxPropertyWidth PreValue + Implementation
* Incorporate latest block list editor changes, update migration for changed configuration
* Change declared converter type
* Handle invalid data type references
* Remove code duplicated from PR #7957
* use ElementModel for the ContentModel of an ElementType. So we can use ElementTypeModel for the ModelDefinition aka. the Type.
* do still show itempicker for BlockConfiguration even though there is no ElementTypes to pick. This enables the option to create a new ElementType to be used.
* use the right wrapper, for correct spacing
* parse item
* correct fallback for label
* removed unused callback
* paste feature for block-picker
* localize block-picker tabs
* Slightly change for shadow on block-picker item hover
* Localization of BlockEditor Settings Tab
* localizationService
* only filter when more than 8 items available
* Add multiple blocks if hold down CTRL or SuperKey
* adds notes
* ability to add a scoped stylesheet for block view
* make scoped block draggable + style adjustments
* provide index for custom view
* rename contentModel to data + rename layoutModel to layout
* clean up
* more localization
* openSettings option for block editor
* minor changes for a better developer experience
* paste mistake corrected
* only manipulate content-content-app if its present
* make small overlays slightly bigger
* moved block list entry editor into block list editor folder
* limit labelinterpretator to only runs ones pr. edit. and lets make sure to have a label thought we dont have any properties.
* fixed inline views gulp watcher
* changed vm to a better controller instance name
* make watch for views work again.
* able to re run watch
* make js up to date
* fix white background of image-picker
* media-picker container class
* loading indication
* adjust unit tests to latest interface
* getting started on JS Docs
* converting code to use contentTypeKey instead of contentTypeAlias, still missing a few TODOs.
* revert change
* add todo
* use Guid for Key
* use key
* Updates the caching layer to handle GUID keys for content types while preserving backwards compat, fixes unit tests, removes the strongly typed lists for the block editor value since it's unecessary
* Reverts the nested content changes, fixes up the GetEmptyByKey
* Returns ContentTypeKey from the server, updates js to use this everywhere and fix all js tests.
* Allows key in SimpleContentType
* correct for the new spelling
* appended this. since the method is a non-static class method.
* only add background-image if value isnt null
* simplifyed limits validation
* clean up
* no need to execute a digest.
* define the full model for new configurations
* removed setDataFromBlockModel and general clean up and added documentation
* default size should be medium
* use retriveValuesFrom method to transfer data.
* ability to disable an navigation item
* createElementTypeAndCallback working again for settings.
* still have the ability to retrive a scaffold model by alias, since we are still using alias in clipboard service.
* disable clipboard tab if no available paste options
* ups, should stay as alias
* disable clipboard when empty
* use property alias for content add button
* use a grey that can be seen on top of grey and on top of white.
* some accessibility improvements
* rename entry to block
* appended ' and added space in Element Type
* use background color for hover to corospond with active state
* make nested content unsupported
* Moving BlockEditorModelObject into its own file for Documentation purpose. Same goes for renaming BlockModel to BlockObject. and a lot of documentation written.
* fix links in js docs
* added a blocklistentryeditor for unsupported blocks
* ability to retrive the $scope from UmbVariantContentEditors, needed for Block Editor to get a scope existing across variants and splitview.
* Appending the block objects to layout, to share it across variants and in split-view.
* removed trailing comma
* Unsupported block
* Dont copy unsupported blocks
* use grey color for unsupported block
* text correction
* we dont have this fallback anymore
* sort properties
* Text change
* css correction
* use active to determine if an inline editor is expanded. To enable the inline editor to be expanded on creation.
* using udi instead of key.
* bringing the runtime block key back
* live editing ability
* live editing for settings data
* call formSubmit before property editor are removed from DOM. Both for overlay-editing and inline-editing. Fire an postFormSubmitting event after formSubmitting event to let editors know that data could have been updated.
* make sure settings object exists
* only set active to false if it was false before opening the editor.
* update test with new scope parameter
* move destroy responsibility to blockObject
* rename onDestroy to destroy
* added some JS-Docs
* correction jsDocs
* Update ElementType Resource to not use hardcoded URL but to use the Umbraco.Sys.ServerVariables.umbracoUrls instead
* Remove partially completed ConvertToElement migration, fixed in issue 7939 instead.
* Remove external property editor migration
* corrected naming of umbBlockListScopedBlock and umbBlockListBlock
* correct ngdoc type
* removed vscode specific configuration of karma
* Finished Todo, gets name of documentType if copying all entities in an infinite editor
* changed comment from TODO to something that explains the state.
* stop tracking build output files.
* rename files to match file name conventions
* this should not happen.
* remove dublicated code
* rename requestCopyBlock to copyBlock
* make sure images does not repeat.
* scale thumbnail for block showcase
* renamed blockcard.component to umb-block-card and moved it.
* removed inline style
* correct style location
* corrected filepath
* corrected file path
* keep elementTypes up to date through the EventService.
* mark Umbraco.BlockList as unsupported inside Nested Content
* correct js docs name
* remove comment
* remove comment
* remove unused controller
* rename inline method name
* corrected spelling mistake
* remove not very used vars
* make binding one-way
* split in multiple lines
* corrected default rendering
* removing documentation that is relevant for developers of new block editors. this documentation will be transfered to Our documentation.
* added danish translation
* corrected blog to blok
* Remove invalid using statement
* use native forEach
Co-authored-by: Niels Lyngsø <nsl@umbraco.dk>
Co-authored-by: Benjamin Carleski <benjamin@proworks.com>
Co-authored-by: Warren Buckley <warren@umbraco.com>
Co-authored-by: Niels Lyngsø <nsl@umbraco.com>
Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Claus <claus@claus.nu>
2020-06-30 19:52:42 +10:00
|
|
|
|
public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Content, alias);
|
|
|
|
|
|
|
|
|
|
|
|
public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Content, key);
|
2018-06-29 19:52:40 +02:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|