using System; using System.Collections.Generic; using System.Linq; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { internal class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; private readonly IAppCache _snapshotCache; private readonly IAppCache _elementsCache; #region Constructors public MediaCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache) : base(previewDefault) { _snapshot = snapshot; _snapshotCache = snapshotCache; _elementsCache = elementsCache; } #endregion #region Get, Has public override IPublishedContent GetById(bool preview, int contentId) { // ignore preview, there's only draft for media var n = _snapshot.Get(contentId); return n?.PublishedModel; } public override IPublishedContent GetById(bool preview, Guid contentId) { // ignore preview, there's only draft for media var n = _snapshot.Get(contentId); return n?.PublishedModel; } public override IPublishedContent GetById(bool preview, Udi contentId) { var guidUdi = contentId as GuidUdi; if (guidUdi == null) throw new ArgumentException($"Udi must be of type {typeof(GuidUdi).Name}.", nameof(contentId)); if (guidUdi.EntityType != Constants.UdiEntityType.Media) throw new ArgumentException($"Udi entity type must be \"{Constants.UdiEntityType.Media}\".", nameof(contentId)); // ignore preview, there's only draft for media var n = _snapshot.Get(guidUdi.Guid); return n?.PublishedModel; } public override bool HasById(bool preview, int contentId) { var n = _snapshot.Get(contentId); return n != null; } public override IEnumerable GetAtRoot(bool preview) { if (PublishedSnapshotService.CacheContentCacheRoots == false) return GetAtRootNoCache(); var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache; if (cache == null) return GetAtRootNoCache(); // note: ToArray is important here, we want to cache the result, not the function! return (IEnumerable)cache.Get( CacheKeys.MediaCacheRoots(false), // ignore preview, only 1 key! () => GetAtRootNoCache().ToArray()); } private IEnumerable GetAtRootNoCache() { var c = _snapshot.GetAtRoot(); // ignore preview, there's only draft for media return c.Select(n => n.PublishedModel); } public override bool HasContent(bool preview) { return _snapshot.IsEmpty == false; } #endregion #region XPath public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) { var navigator = CreateNavigator(preview); var iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) { var navigator = CreateNavigator(preview); var iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } private static IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) { if (iterator.MoveNext() == false) return null; var xnav = iterator.Current as NavigableNavigator; if (xnav == null) return null; var xcontent = xnav.UnderlyingObject as NavigableContent; return xcontent == null ? null : xcontent.InnerContent; } public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) { var navigator = CreateNavigator(preview); var iterator = navigator.Select(xpath, vars); return GetByXPath(iterator); } public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) { var navigator = CreateNavigator(preview); var iterator = navigator.Select(xpath, vars); return GetByXPath(iterator); } private static IEnumerable GetByXPath(XPathNodeIterator iterator) { while (iterator.MoveNext()) { var xnav = iterator.Current as NavigableNavigator; if (xnav == null) continue; var xcontent = xnav.UnderlyingObject as NavigableContent; if (xcontent == null) continue; yield return xcontent.InnerContent; } } public override XPathNavigator CreateNavigator(bool preview) { var source = new Source(this, preview); var navigator = new NavigableNavigator(source); return navigator; } public override XPathNavigator CreateNodeNavigator(int id, bool preview) { var source = new Source(this, preview); var navigator = new NavigableNavigator(source); return navigator.CloneWithNewRoot(id, 0); } #endregion #region Content types public override IPublishedContentType GetContentType(int id) { return _snapshot.GetContentType(id); } public override IPublishedContentType GetContentType(string alias) { return _snapshot.GetContentType(alias); } #endregion #region IDisposable public void Dispose() { _snapshot.Dispose(); } #endregion } }