From 3dd411f159c8280c8383d181701d55cebebe3b1e Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 15 Nov 2012 21:46:54 +0500 Subject: [PATCH] Working on #U4-1174, lots of updates/fixes to media in MVC regarding loading data from the examine cache to return media + lots of unit tests. --- .../ContentStores/PublishMediaStoreTests.cs | 6 +- .../PublishedContent/PublishedMediaTests.cs | 107 ++++++ .../ExamineResources.Designer.cs | 99 +++++ .../ExamineHelpers/ExamineResources.resx | 127 ++++++ .../ExamineHelpers/IndexInitializer.cs | 360 ++++++++++++++++++ .../TestHelpers/ExamineHelpers/media.xml | 51 +++ src/Umbraco.Tests/TestHelpers/TestHelper.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 23 +- src/Umbraco.Web/DefaultPublishedMediaStore.cs | 121 ++++-- 9 files changed, 854 insertions(+), 42 deletions(-) create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml diff --git a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs index 0595923589..eaced2de55 100644 --- a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs @@ -247,10 +247,12 @@ namespace Umbraco.Tests.ContentStores a => null, //we're not going to test this so ignore a => new List(), - (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a))), + (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), + false), //callback to get the children d => children, - (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a))); + (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), + false); return dicDoc; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 973655b9c1..c37cd0cf18 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -1,11 +1,24 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis.Standard; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.ExamineHelpers; using Umbraco.Web; +using UmbracoExamine; +using UmbracoExamine.DataServices; using umbraco.BusinessLogic; using System.Linq; @@ -95,6 +108,98 @@ namespace Umbraco.Tests.PublishedContent return GetNode(id, GetUmbracoContext("/test", 1234)); } + [Test] + public void Children_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 1111); + var rootChildren = publishedMedia.Children(); + Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { 2222, 1113, 1114, 1115, 1116 })); + + var publishedChild1 = store.GetDocumentById(GetUmbracoContext("/test", 1234), 2222); + var subChildren = publishedChild1.Children(); + Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { 2112 })); + } + + [Test] + public void Descendants_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 1111); + var rootDescendants = publishedMedia.Descendants(); + Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1113, 1114, 1115, 1116 })); + + var publishedChild1 = store.GetDocumentById(GetUmbracoContext("/test", 1234), 2222); + var subDescendants = publishedChild1.Descendants(); + Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 3113 })); + } + + [Test] + public void DescendantsOrSelf_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 1111); + var rootDescendants = publishedMedia.DescendantsOrSelf(); + Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 1111, 2112, 2222, 1113, 1114, 1115, 1116 })); + + var publishedChild1 = store.GetDocumentById(GetUmbracoContext("/test", 1234), 2222); + var subDescendants = publishedChild1.DescendantsOrSelf(); + Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2222, 2112, 3113 })); + } + + [Test] + public void Ancestors_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 3113); + var ancestors = publishedMedia.Ancestors(); + Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1111 })); + } + + [Test] + public void AncestorsOrSelf_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 3113); + var ancestors = publishedMedia.AncestorsOrSelf(); + Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 3113, 2112, 2222, 1111 })); + } + [Test] public void Children_Without_Examine() { @@ -233,4 +338,6 @@ namespace Umbraco.Tests.PublishedContent new[] { mSubChild1.Id, mChild1.Id, mRoot.Id })); } } + + } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs new file mode 100644 index 0000000000..4d3c1d1ce6 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.17929 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Umbraco.Tests.TestHelpers.ExamineHelpers { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExamineResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExamineResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Tests.TestHelpers.ExamineHelpers.ExamineResources", typeof(ExamineResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<media> + /// <node id="1111" version="902e13f7-5793-482a-9e06-cd94eebd1de0" parentID="-1" level="1" writerID="0" nodeType="1031" template="0" sortOrder="2" createDate="2010-05-19T15:26:08" updateDate="2010-05-19T15:26:09" nodeName="Product Images" urlName="productimages" writerName="Administrator" nodeTypeAlias="Folder" path="-1,1111"> + /// <data alias="contents"></data> + /// <node id="2222" version="902e13f7-5793-482a-9e06-cd94eebd1de0" parentID="-1" level="1" writerID="0" [rest of string was truncated]";. + /// + internal static string media { + get { + return ResourceManager.GetString("media", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<!DOCTYPE root[ + /// <!ELEMENT CWS_Contact ANY> + /// <!ATTLIST CWS_Contact id ID #REQUIRED> + /// <!ELEMENT CWS_EmailAFriend ANY> + /// <!ATTLIST CWS_EmailAFriend id ID #REQUIRED> + /// <!ELEMENT CWS_EventItem ANY> + /// <!ATTLIST CWS_EventItem id ID #REQUIRED> + /// <!ELEMENT CWS_Galleries ANY> + /// <!ATTLIST CWS_Galleries id ID #REQUIRED> + /// <!ELEMENT CWS_Gallery ANY> + /// <!ATTLIST CWS_Gallery id ID #REQUIRED> + /// <!ELEMENT CWS_Home ANY> + /// <!ATTLIST CWS_Home id ID #REQUIRED> + /// <!ELEMENT CWS_NewsEven [rest of string was truncated]";. + /// + internal static string umbraco { + get { + return ResourceManager.GetString("umbraco", resourceCulture); + } + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx new file mode 100644 index 0000000000..4d91faaa26 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + media.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + umbraco.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs new file mode 100644 index 0000000000..bf295d7211 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis.Standard; +using UmbracoExamine; +using UmbracoExamine.DataServices; + +namespace Umbraco.Tests.TestHelpers.ExamineHelpers +{ + /// + /// Used internally by test classes to initialize a new index from the template + /// + internal class IndexInitializer + { + + public IndexInitializer() + { + //ensure the umbraco.config and media.xml files exist at the location where we need to load them + var appData = Path.Combine(TestHelper.CurrentAssemblyDirectory, "App_Data"); + Directory.CreateDirectory(appData); + var umbConfig = Path.Combine(appData, "umbraco.config"); + File.Delete(umbConfig); + using (var s = File.CreateText(umbConfig)) + { + s.Write(ExamineResources.umbraco); + } + + var umbMedia = Path.Combine(appData, "media.xml"); + File.Delete(umbMedia); + using (var s = File.CreateText(umbMedia)) + { + s.Write(ExamineResources.media); + } + } + + public UmbracoContentIndexer GetUmbracoIndexer(DirectoryInfo d) + { + var i = new UmbracoContentIndexer(new IndexCriteria( + new[] + { + new TestIndexField { Name = "id", EnableSorting = true, Type = "Number" }, + new TestIndexField { Name = "nodeName", EnableSorting = true }, + new TestIndexField { Name = "updateDate", EnableSorting = true, Type = "DateTime" }, + new TestIndexField { Name = "writerName" }, + new TestIndexField { Name = "path" }, + new TestIndexField { Name = "nodeTypeAlias" }, + new TestIndexField { Name = "parentID" } + }, + Enumerable.Empty(), + //new[] + // { + // new TestIndexField { Name = "headerText" }, + // new TestIndexField { Name = "bodyText" }, + // new TestIndexField { Name = "metaDescription" }, + // new TestIndexField { Name = "metaKeywords" }, + // new TestIndexField { Name = "bodyTextColOne" }, + // new TestIndexField { Name = "bodyTextColTwo" }, + // new TestIndexField { Name = "xmlStorageTest" } + // }, + Enumerable.Empty(), + //new[] + // { + // "CWS_Home", + // "CWS_Textpage", + // "CWS_TextpageTwoCol", + // "CWS_NewsEventsList", + // "CWS_NewsItem", + // "CWS_Gallery", + // "CWS_EventItem", + // "Image", + // }, + new string[] { }, + -1), + d, + new TestDataService(), + new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), + false); + + //i.IndexSecondsInterval = 1; + + i.IndexingError += IndexingError; + + return i; + } + public UmbracoExamineSearcher GetUmbracoSearcher(DirectoryInfo d) + { + return new UmbracoExamineSearcher(d, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); + } + //public SimpleDataIndexer GetSimpleIndexer(DirectoryInfo d) + //{ + // var i = new SimpleDataIndexer(new IndexCriteria( + // new IIndexField[] { }, + // new[] + // { + // new TestIndexField { Name = "Author" }, + // new TestIndexField { Name = "DateCreated", EnableSorting = true, Type = "DateTime" }, + // new TestIndexField { Name = "Title" }, + // new TestIndexField { Name = "Photographer" }, + // new TestIndexField { Name = "YearCreated", Type = "Date.Year" }, + // new TestIndexField { Name = "MonthCreated", Type = "Date.Month" }, + // new TestIndexField { Name = "DayCreated", Type = "Date.Day" }, + // new TestIndexField { Name = "HourCreated", Type = "Date.Hour" }, + // new TestIndexField { Name = "MinuteCreated", Type = "Date.Minute" }, + // new TestIndexField { Name = "SomeNumber", Type = "Number" }, + // new TestIndexField { Name = "SomeFloat", Type = "Float" }, + // new TestIndexField { Name = "SomeDouble", Type = "Double" }, + // new TestIndexField { Name = "SomeLong", Type = "Long" } + // }, + // new string[] { }, + // new string[] { }, + // -1), + // d, + // new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), + // new TestSimpleDataProvider(), + // new[] { "Documents", "Pictures" }, + // false); + // i.IndexingError += IndexingError; + + // return i; + //} + public LuceneSearcher GetLuceneSearcher(DirectoryInfo d) + { + return new LuceneSearcher(d, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); + } + //public PDFIndexer GetPdfIndexer(DirectoryInfo d) + //{ + // var i = new PDFIndexer(d, + // new TestDataService(), + // new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), + // false); + + // i.IndexingError += IndexingError; + + // return i; + //} + public MultiIndexSearcher GetMultiSearcher(DirectoryInfo pdfDir, DirectoryInfo simpleDir, DirectoryInfo conventionDir, DirectoryInfo cwsDir) + { + var i = new MultiIndexSearcher(new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); + return i; + } + + + internal void IndexingError(object sender, IndexingErrorEventArgs e) + { + throw new ApplicationException(e.Message, e.InnerException); + } + + internal class TestIndexField : IIndexField + { + public string Name { get; set; } + public bool EnableSorting { get; set; } + public string Type { get; set; } + } + + internal class TestDataService : IDataService + { + + public TestDataService() + { + ContentService = new TestContentService(); + LogService = new TestLogService(); + MediaService = new TestMediaService(); + } + + #region IDataService Members + + public IContentService ContentService { get; private set; } + + public ILogService LogService { get; private set; } + + public IMediaService MediaService { get; private set; } + + public string MapPath(string virtualPath) + { + return new DirectoryInfo(TestHelper.CurrentAssemblyDirectory) + "\\" + virtualPath.Replace("/", "\\"); + } + + #endregion + } + + /// + /// A mock data service used to return content from the XML data file created with CWS + /// + internal class TestContentService : IContentService + { + public const int ProtectedNode = 1142; + + public TestContentService() + { + var xmlFile = new DirectoryInfo(TestHelper.CurrentAssemblyDirectory).GetDirectories("App_Data") + .Single() + .GetFiles("umbraco.config") + .Single(); + + _xDoc = XDocument.Load(xmlFile.FullName); + } + + #region IContentService Members + + /// + /// Return the XDocument containing the xml from the umbraco.config xml file + /// + /// + /// + /// + /// This is no different in the test suite as published content + /// + public XDocument GetLatestContentByXPath(string xpath) + { + var xdoc = XDocument.Parse(""); + xdoc.Root.Add(_xDoc.XPathSelectElements(xpath)); + + return xdoc; + } + + /// + /// Return the XDocument containing the xml from the umbraco.config xml file + /// + /// + /// + public XDocument GetPublishedContentByXPath(string xpath) + { + var xdoc = XDocument.Parse(""); + xdoc.Root.Add(_xDoc.XPathSelectElements(xpath)); + + return xdoc; + } + + public string StripHtml(string value) + { + const string pattern = @"<(.|\n)*?>"; + return Regex.Replace(value, pattern, string.Empty); + } + + public bool IsProtected(int nodeId, string path) + { + // single node is marked as protected for test indexer + // hierarchy is not important for this test + return nodeId == ProtectedNode; + } + + public IEnumerable GetAllUserPropertyNames() + { + return GetPublishedContentByXPath("//*[count(@id)>0]") + .Root + .Elements() + .Select(x => x.Name.LocalName) + .ToList(); + } + + public IEnumerable GetAllSystemPropertyNames() + { + return new Dictionary() + { + {"id", FieldIndexTypes.NOT_ANALYZED}, + {"version", FieldIndexTypes.NOT_ANALYZED}, + {"parentID", FieldIndexTypes.NOT_ANALYZED}, + {"level", FieldIndexTypes.NOT_ANALYZED}, + {"writerID", FieldIndexTypes.NOT_ANALYZED}, + {"creatorID", FieldIndexTypes.NOT_ANALYZED}, + {"nodeType", FieldIndexTypes.NOT_ANALYZED}, + {"template", FieldIndexTypes.NOT_ANALYZED}, + {"sortOrder", FieldIndexTypes.NOT_ANALYZED}, + {"createDate", FieldIndexTypes.NOT_ANALYZED}, + {"updateDate", FieldIndexTypes.NOT_ANALYZED}, + {"nodeName", FieldIndexTypes.ANALYZED}, + {"urlName", FieldIndexTypes.NOT_ANALYZED}, + {"writerName", FieldIndexTypes.ANALYZED}, + {"creatorName", FieldIndexTypes.ANALYZED}, + {"nodeTypeAlias", FieldIndexTypes.ANALYZED}, + {"path", FieldIndexTypes.NOT_ANALYZED} + }.Select(x => x.Key); + } + + #endregion + + private readonly XDocument _xDoc; + + + + + + + + } + + internal class TestLogService : ILogService + { + #region ILogService Members + + public string ProviderName { get; set; } + + public void AddErrorLog(int nodeId, string msg) + { + Trace.WriteLine("ERROR: (" + nodeId.ToString() + ") " + msg); + } + + public void AddInfoLog(int nodeId, string msg) + { + Trace.WriteLine("INFO: (" + nodeId.ToString() + ") " + msg); + } + + public void AddVerboseLog(int nodeId, string msg) + { + if (LogLevel == LoggingLevel.Verbose) + Trace.WriteLine("VERBOSE: (" + nodeId.ToString() + ") " + msg); + } + + public LoggingLevel LogLevel + { + get + { + return LoggingLevel.Verbose; + } + set + { + //do nothing + } + } + + #endregion + } + + internal class TestMediaService : IMediaService + { + + public TestMediaService() + { + var xmlFile = new DirectoryInfo(TestHelper.CurrentAssemblyDirectory).GetDirectories("App_Data") + .Single() + .GetFiles("media.xml") + .Single(); + + m_Doc = XDocument.Load(xmlFile.FullName); + } + + #region IMediaService Members + + public System.Xml.Linq.XDocument GetLatestMediaByXpath(string xpath) + { + var xdoc = XDocument.Parse(""); + xdoc.Root.Add(m_Doc.XPathSelectElements(xpath)); + return xdoc; + } + + #endregion + + private XDocument m_Doc; + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml new file mode 100644 index 0000000000..dd645c6d71 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml @@ -0,0 +1,51 @@ + + + + + + + + + 115 + 268 + 10726 + jpg + + + 115 + 268 + 10726 + jpg + + + + + + 115 + 268 + 10726 + jpg + + + + 115 + 268 + 10726 + jpg + + + + 115 + 268 + 10726 + jpg + + + + 115 + 268 + 10726 + jpg + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 204fe819af..49aa724605 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -51,7 +51,7 @@ namespace Umbraco.Tests.TestHelpers { get { - var codeBase = Assembly.GetCallingAssembly().CodeBase; + var codeBase = typeof(TestHelper).Assembly.CodeBase; var uri = new Uri(codeBase); var path = uri.LocalPath; return Path.GetDirectoryName(path); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 72528a7163..6b0607a1f0 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -35,6 +35,10 @@ ..\packages\log4net.2.0.0\lib\net40-full\log4net.dll + + False + ..\Umbraco.Web.UI\bin\Lucene.Net.dll + ..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll @@ -51,6 +55,10 @@ + + False + ..\Umbraco.Web.UI\bin\UmbracoExamine.dll + @@ -97,6 +105,12 @@ + + True + True + ExamineResources.resx + + @@ -128,6 +142,7 @@ + Always @@ -178,9 +193,15 @@ Umbraco.Web - + + + ResXFileCodeGenerator + ExamineResources.Designer.cs + + + diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index b6675e01fa..79eed8ef72 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -5,6 +5,8 @@ using System.IO; using System.Linq; using System.Xml.XPath; using Examine; +using Examine.Providers; +using Lucene.Net.Documents; using Umbraco.Core; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; @@ -21,6 +23,22 @@ namespace Umbraco.Web /// internal class DefaultPublishedMediaStore : IPublishedMediaStore { + + public DefaultPublishedMediaStore() + { + } + + /// + /// Generally used for unit testing to use an explicit examine searcher + /// + /// + internal DefaultPublishedMediaStore(BaseSearchProvider searchProvider) + { + _searchProvider = searchProvider; + } + + private readonly BaseSearchProvider _searchProvider; + public virtual IPublishedContent GetDocumentById(UmbracoContext umbracoContext, int nodeId) { return GetUmbracoMedia(nodeId); @@ -53,21 +71,42 @@ namespace Umbraco.Web } } - private IPublishedContent GetUmbracoMedia(int id) + private BaseSearchProvider GetSearchProviderSafe() { + if (_searchProvider != null) + return _searchProvider; + var eMgr = GetExamineManagerSafe(); if (eMgr != null) + { + try + { + //by default use the InternalSearcher + return eMgr.SearchProviderCollection["InternalSearcher"]; + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + } + } + return null; + } + + private IPublishedContent GetUmbracoMedia(int id) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) { try { //first check in Examine as this is WAY faster - var criteria = eMgr - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); + var criteria = searchProvider.CreateSearchCriteria("media"); var filter = criteria.Id(id); - var results = eMgr - .SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); + var results = searchProvider.Search(filter.Compile()); if (results.Any()) { return ConvertFromSearchResult(results.First()); @@ -86,13 +125,6 @@ namespace Umbraco.Web if (media != null && media.Current != null) { media.MoveNext(); - ////error check - //if (media.Current.MoveToFirstChild() && media.Current.Name.InvariantEquals("error")) - //{ - // return null; - //} - //var current = media.Current; - return ConvertFromXPathNavigator(media.Current); } @@ -129,15 +161,13 @@ namespace Umbraco.Web return new DictionaryPublishedContent(values, - d => d.ParentId != -1 //parent should be null if -1 - ? GetUmbracoMedia(d.ParentId) - : null, - //callback to return the children of the current node - d => GetChildrenMedia(d.ParentId), - GetProperty) - { - LoadedFromExamine = true - }; + d => d.ParentId != -1 //parent should be null if -1 + ? GetUmbracoMedia(d.ParentId) + : null, + //callback to return the children of the current node + d => GetChildrenMedia(d.Id), + GetProperty, + true); } internal IPublishedContent ConvertFromXPathNavigator(XPathNavigator xpath) @@ -193,9 +223,9 @@ namespace Umbraco.Web ? GetUmbracoMedia(d.ParentId) : null, //callback to return the children of the current node based on the xml structure already found - //d => GetChildrenMedia(d.ParentId, xpath), d => GetChildrenMedia(d.Id, xpath), - GetProperty); + GetProperty, + false); } /// @@ -254,20 +284,16 @@ namespace Umbraco.Web //if there is no navigator, try examine first, then re-look it up if (xpath == null) { - var eMgr = GetExamineManagerSafe(); + var searchProvider = GetSearchProviderSafe(); - if (eMgr != null) + if (searchProvider != null) { try { //first check in Examine as this is WAY faster - var criteria = eMgr - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); + var criteria = searchProvider.CreateSearchCriteria("media"); var filter = criteria.ParentId(parentId); - var results = eMgr - .SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); + var results = searchProvider.Search(filter.Compile()); if (results.Any()) { return results.Select(ConvertFromSearchResult); @@ -332,7 +358,8 @@ namespace Umbraco.Web IDictionary valueDictionary, Func getParent, Func> getChildren, - Func getProperty) + Func getProperty, + bool fromExamine) { if (valueDictionary == null) throw new ArgumentNullException("valueDictionary"); if (getParent == null) throw new ArgumentNullException("getParent"); @@ -342,7 +369,7 @@ namespace Umbraco.Web _getChildren = getChildren; _getProperty = getProperty; - LoadedFromExamine = false; //default to false + LoadedFromExamine = fromExamine; ValidateAndSetProperty(valueDictionary, val => Id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! ValidateAndSetProperty(valueDictionary, val => TemplateId = int.Parse(val), "template", "templateId"); @@ -356,8 +383,8 @@ namespace Umbraco.Web 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 = DateTime.Parse(val), "createDate"); - ValidateAndSetProperty(valueDictionary, val => UpdateDate = DateTime.Parse(val), "updateDate"); + 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 => { @@ -381,10 +408,28 @@ namespace Umbraco.Web } } + private DateTime ParseDateTimeValue(string val) + { + if (LoadedFromExamine) + { + try + { + //we might need to parse the date time using Lucene converters + return DateTools.StringToDate(val); + } + catch (FormatException) + { + //swallow exception, its not formatted correctly so revert to just trying to parse + } + } + + return DateTime.Parse(val); + } + /// /// Flag to get/set if this was laoded from examine cache /// - internal bool LoadedFromExamine { get; set; } + internal bool LoadedFromExamine { get; private set; } private readonly Func _getParent; private readonly Func> _getChildren;