diff --git a/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs b/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs index 6c51acbe6e..d53f95e838 100644 --- a/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/PhysicalFileSystemTests.cs @@ -26,7 +26,13 @@ namespace Umbraco.Tests.IO [TearDown] public void TearDown() { - Directory.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FileSysTests")); + var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FileSysTests"); + var files = Directory.GetFiles(path); + foreach (var f in files) + { + File.Delete(f); + } + Directory.Delete(path, true); } protected override string ConstructUrl(string path) diff --git a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs new file mode 100644 index 0000000000..0c4dc28f83 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using System.Linq; +using Lucene.Net.Documents; +using NUnit.Framework; +using Umbraco.Core.Configuration; +using Umbraco.Tests.TestHelpers.ExamineHelpers; +using umbraco.MacroEngines; + +namespace Umbraco.Tests.PublishedContent +{ + public class LegacyExamineBackedMediaTests : PublishedContentTestBase + { + public override void Initialize() + { + base.Initialize(); + UmbracoSettings.ForceSafeAliases = true; + UmbracoSettings.UmbracoLibraryCacheDuration = 1800; + UmbracoSettings.ForceSafeAliases = true; + } + + public override void TearDown() + { + base.TearDown(); + } + + [Test] + public void Ensure_Children_Are_Sorted() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var searcher = indexInit.GetUmbracoSearcher(newIndexFolder); + var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile()); + Assert.IsNotNull(result); + Assert.AreEqual(1, result.TotalItemCount); + + var searchItem = result.First(); + var backedMedia = new ExamineBackedMedia(searchItem, indexer, searcher); + var children = backedMedia.ChildrenAsList.Value; + + var currSort = 0; + for (var i = 0; i < children.Count(); i++) + { + Assert.GreaterOrEqual(children[i].SortOrder, currSort); + currSort = children[i].SortOrder; + } + + } + + [Test] + public void Ensure_Result_Has_All_Values() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var searcher = indexInit.GetUmbracoSearcher(newIndexFolder); + var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile()); + Assert.IsNotNull(result); + Assert.AreEqual(1, result.TotalItemCount); + + var searchItem = result.First(); + var backedMedia = new ExamineBackedMedia(searchItem, indexer, searcher); + + Assert.AreEqual(searchItem.Id, backedMedia.Id); + Assert.AreEqual(searchItem.Fields["sortOrder"], backedMedia.SortOrder.ToString()); + Assert.AreEqual(searchItem.Fields["urlName"], backedMedia.UrlName); + Assert.AreEqual(DateTools.StringToDate(searchItem.Fields["createDate"]), backedMedia.CreateDate); + Assert.AreEqual(DateTools.StringToDate(searchItem.Fields["updateDate"]), backedMedia.UpdateDate); + Assert.AreEqual(Guid.Parse(searchItem.Fields["version"]), backedMedia.Version); + Assert.AreEqual(searchItem.Fields["level"], backedMedia.Level.ToString()); + Assert.AreEqual(searchItem.Fields["writerID"], backedMedia.WriterID.ToString()); + Assert.AreEqual(searchItem.Fields["writerID"], backedMedia.CreatorID.ToString()); //there's only writerId in the xml + Assert.AreEqual(searchItem.Fields["writerName"], backedMedia.CreatorName); + Assert.AreEqual(searchItem.Fields["writerName"], backedMedia.WriterName); //tehre's only writer name in the xml + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 800d6dbb0d..e7848d2815 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -24,10 +24,10 @@ using System.Linq; namespace Umbraco.Tests.PublishedContent { - /// + /// /// Tests the typed extension methods on IPublishedContent using the DefaultPublishedMediaStore /// - [TestFixture] + [TestFixture, RequiresSTA] public class PublishedMediaTests : PublishedContentTestBase { @@ -63,7 +63,34 @@ namespace Umbraco.Tests.PublishedContent { return GetNode(id, GetUmbracoContext("/test", 1234)); } + + [Test] + public void Ensure_Children_Sorted_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), + indexInit.GetUmbracoIndexer(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().ToArray(); + var currSort = 0; + for (var i = 0; i < rootChildren.Count(); i++) + { + Assert.GreaterOrEqual(rootChildren[i].SortOrder, currSort); + currSort = rootChildren[i].SortOrder; + } + + + + } + + [Test] public void Do_Not_Find_In_Recycle_Bin() { @@ -72,7 +99,7 @@ namespace Umbraco.Tests.PublishedContent var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); indexer.RebuildIndex(); var searcher = indexInit.GetUmbracoSearcher(newIndexFolder); - var store = new DefaultPublishedMediaStore(searcher); + var store = new DefaultPublishedMediaStore(searcher, indexer); var ctx = GetUmbracoContext("/test", 1234); //ensure it is found @@ -109,7 +136,9 @@ namespace Umbraco.Tests.PublishedContent var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); indexer.RebuildIndex(); - var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + var store = new DefaultPublishedMediaStore( + indexInit.GetUmbracoSearcher(newIndexFolder), + indexInit.GetUmbracoIndexer(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); @@ -129,7 +158,9 @@ namespace Umbraco.Tests.PublishedContent var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); indexer.RebuildIndex(); - var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + var store = new DefaultPublishedMediaStore( + indexInit.GetUmbracoSearcher(newIndexFolder), + indexInit.GetUmbracoIndexer(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); @@ -149,7 +180,9 @@ namespace Umbraco.Tests.PublishedContent var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); indexer.RebuildIndex(); - var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + var store = new DefaultPublishedMediaStore( + indexInit.GetUmbracoSearcher(newIndexFolder), + indexInit.GetUmbracoIndexer(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); @@ -169,7 +202,9 @@ namespace Umbraco.Tests.PublishedContent var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); indexer.RebuildIndex(); - var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + var store = new DefaultPublishedMediaStore( + indexInit.GetUmbracoSearcher(newIndexFolder), + indexInit.GetUmbracoIndexer(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); @@ -185,7 +220,9 @@ namespace Umbraco.Tests.PublishedContent var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); indexer.RebuildIndex(); - var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + var store = new DefaultPublishedMediaStore( + indexInit.GetUmbracoSearcher(newIndexFolder), + indexInit.GetUmbracoIndexer(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); diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs index bf295d7211..f998f28ddc 100644 --- a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs @@ -48,37 +48,35 @@ namespace Umbraco.Tests.TestHelpers.ExamineHelpers new[] { new TestIndexField { Name = "id", EnableSorting = true, Type = "Number" }, - new TestIndexField { Name = "nodeName", EnableSorting = true }, + new TestIndexField { Name = "version" }, + new TestIndexField { Name = "parentID" }, + new TestIndexField { Name = "level" }, + new TestIndexField { Name = "writerID" }, + new TestIndexField { Name = "creatorID" }, + new TestIndexField { Name = "nodeType" }, + new TestIndexField { Name = "template" }, + new TestIndexField { Name = "sortOrder", EnableSorting = true, Type = "Number"}, + new TestIndexField { Name = "createDate", EnableSorting = true, Type = "DateTime" }, new TestIndexField { Name = "updateDate", EnableSorting = true, Type = "DateTime" }, + new TestIndexField { Name = "nodeName", EnableSorting = true }, + new TestIndexField { Name = "urlName" }, new TestIndexField { Name = "writerName" }, - new TestIndexField { Name = "path" }, + new TestIndexField { Name = "creatorName" }, new TestIndexField { Name = "nodeTypeAlias" }, - new TestIndexField { Name = "parentID" } + new TestIndexField { Name = "path" } }, - 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[] { }, + 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(), + Enumerable.Empty(), -1), d, new TestDataService(), @@ -95,53 +93,12 @@ namespace Umbraco.Tests.TestHelpers.ExamineHelpers { 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)); diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml index dd645c6d71..097fa5e4cd 100644 --- a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml @@ -2,15 +2,15 @@ - + - + 115 268 10726 jpg - + 115 268 @@ -26,21 +26,21 @@ 10726 jpg - + 115 268 10726 jpg - + 115 268 10726 jpg - + 115 268 diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 4ddd2dc0a8..56477c4124 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -48,9 +48,9 @@ 4 - + False - ..\packages\Examine.0.1.42.2941\lib\Examine.dll + ..\packages\Examine.0.1.47.2941\lib\Examine.dll False @@ -68,8 +68,9 @@ True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll + + False + ..\packages\NUnit.2.6.2\lib\nunit.framework.dll ..\packages\RhinoMocks.3.6.1\lib\net\Rhino.Mocks.dll @@ -189,6 +190,7 @@ + diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index af6c498773..663631347b 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -1,13 +1,13 @@  - + - + diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index 64c6d760f0..7155d80d34 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -10,6 +10,7 @@ using Examine.Providers; using Lucene.Net.Documents; using Umbraco.Core; using Umbraco.Core.Dynamics; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Web.Models; using UmbracoExamine; @@ -32,16 +33,19 @@ namespace Umbraco.Web { } - /// - /// Generally used for unit testing to use an explicit examine searcher - /// - /// - internal DefaultPublishedMediaStore(BaseSearchProvider searchProvider) + /// + /// Generally used for unit testing to use an explicit examine searcher + /// + /// + /// + internal DefaultPublishedMediaStore(BaseSearchProvider searchProvider, BaseIndexProvider indexProvider) { - _searchProvider = searchProvider; + _searchProvider = searchProvider; + _indexProvider = indexProvider; } - private readonly BaseSearchProvider _searchProvider; + private readonly BaseSearchProvider _searchProvider; + private readonly BaseIndexProvider _indexProvider; public virtual IPublishedContent GetDocumentById(UmbracoContext umbracoContext, int nodeId) { @@ -75,7 +79,29 @@ namespace Umbraco.Web } } - private BaseSearchProvider GetSearchProviderSafe() + private BaseIndexProvider GetIndexProviderSafe() + { + if (_indexProvider != null) + return _indexProvider; + + var eMgr = GetExamineManagerSafe(); + if (eMgr != null) + { + try + { + //by default use the InternalSearcher + return eMgr.IndexProviderCollection["InternalIndexer"]; + } + catch (Exception ex) + { + LogHelper.Error("Could not retreive the InternalIndexer", ex); + //something didn't work, continue returning null. + } + } + return null; + } + + private BaseSearchProvider GetSearchProviderSafe() { if (_searchProvider != null) return _searchProvider; @@ -314,11 +340,29 @@ namespace Umbraco.Web { //first check in Examine as this is WAY faster var criteria = searchProvider.CreateSearchCriteria("media"); - var filter = criteria.ParentId(parentId); - var results = searchProvider.Search(filter.Compile()); + var filter = criteria.ParentId(parentId); + ISearchResults results; + + //we want to check if the indexer for this searcher has "sortOrder" flagged as sortable. + //if so, we'll use Lucene to do the sorting, if not we'll have to manually sort it (slower). + var indexer = GetIndexProviderSafe(); + var useLuceneSort = indexer != null && indexer.IndexerData.StandardFields.Any(x => x.Name.InvariantEquals("sortOrder") && x.EnableSorting); + if (useLuceneSort) + { + //we have a sortOrder field declared to be sorted, so we'll use Examine + results = searchProvider.Search( + filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); + } + else + { + results = searchProvider.Search(filter.Compile()); + } + if (results.Any()) { - return results.Select(ConvertFromSearchResult); + return useLuceneSort + ? results.Select(ConvertFromSearchResult) //will already be sorted by lucene + : results.Select(ConvertFromSearchResult).OrderBy(x => x.SortOrder); } } catch (FileNotFoundException) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 26709f8319..f74ade1b3a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -96,9 +96,9 @@ False ..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll - + False - ..\packages\Examine.0.1.42.2941\lib\Examine.dll + ..\packages\Examine.0.1.47.2941\lib\Examine.dll False @@ -124,7 +124,7 @@ True ..\packages\CodeSharp.Package.AspNetWebPage.1.0\lib\net40\NuGet.Core.dll - + ..\packages\uGoLive.1.4.0\lib\Our.Umbraco.uGoLive.dll diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 41edbfed34..600c3faabc 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs index 6e05ddc74d..f9ed516e19 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicBackingItem.cs @@ -88,7 +88,7 @@ namespace umbraco.MacroEngines var children = media.ChildrenAsList.Value; if (children != null) { - return children.ToList().ConvertAll(m => new DynamicBackingItem(m)); + return children.ConvertAll(m => new DynamicBackingItem(m)); } } return new List(); diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index e7a03abd26..f5ed186acc 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -168,7 +168,7 @@ namespace umbraco.MacroEngines } else { - _cachedChildren = new DynamicNodeList(n.ChildrenAsList); + _cachedChildren = new DynamicNodeList(children); } } return _cachedChildren; @@ -294,34 +294,17 @@ namespace umbraco.MacroEngines var results = s.Search(criteria); return ExamineSearchUtill.ConvertSearchResultToDynamicNode(results); } - - - + public bool HasProperty(string name) { if (n != null) { - try - { - IProperty prop = n.GetProperty(name); - if (prop == null) - { - // check for nicer support of Pascal Casing EVEN if alias is camelCasing: - if (prop == null && name.Substring(0, 1).ToUpper() == name.Substring(0, 1)) - { - prop = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1))); - } - } - return (prop != null); - } - catch (Exception) - { - return false; - } + return GetProperty(name) != null; } return false; } + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { try @@ -1347,7 +1330,43 @@ namespace umbraco.MacroEngines public IProperty GetProperty(string alias) { if (n == null) return null; - return n.GetProperty(alias); + + object result; + IProperty prop; + //check the cache first! + if (_cachedMemberOutput.TryGetValue(alias, out result)) + { + prop = result as IProperty; + if (prop != null) + return prop; + } + + try + { + prop = n.GetProperty(alias); + if (prop == null) + { + // check for nicer support of Pascal Casing EVEN if alias is camelCasing: + if (alias.Substring(0, 1).ToUpper() == alias.Substring(0, 1)) + { + //change the alias to the other case to check + alias = alias.Substring(0, 1).ToLower() + alias.Substring((1)); + prop = n.GetProperty(alias); + } + } + } + catch (Exception) + { + return null; + } + + if (prop == null) + return null; + + //cache it! + _cachedMemberOutput.TryAdd(alias, prop); + + return prop; } public IProperty GetProperty(string alias, bool recursive) { diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/ExamineBackedMedia.cs b/src/umbraco.MacroEngines/RazorDynamicNode/ExamineBackedMedia.cs index f8799b454f..992b1c0969 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/ExamineBackedMedia.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/ExamineBackedMedia.cs @@ -4,6 +4,9 @@ using System.Linq; using System.Text; using System.Xml.XPath; using Examine; +using Examine.LuceneEngine.SearchCriteria; +using Examine.Providers; +using Umbraco.Core; using UmbracoExamine; using Lucene.Net.Documents; using umbraco.interfaces; @@ -15,6 +18,8 @@ namespace umbraco.MacroEngines public class ExamineBackedMedia { + private readonly BaseIndexProvider _indexer; + private readonly BaseSearchProvider _searcher; //Custom properties won't be available public bool WasLoadedFromExamine; @@ -99,6 +104,20 @@ namespace umbraco.MacroEngines Values = result.Fields; WasLoadedFromExamine = true; } + + /// + /// Internal constructor used for unit tests + /// + /// + /// + /// + internal ExamineBackedMedia(SearchResult result, BaseIndexProvider indexer, BaseSearchProvider searcher) + : this(result) + { + _indexer = indexer; + _searcher = searcher; + } + public IProperty LoadCustomPropertyNotFoundInExamine(string alias, out bool propertyExists) { //custom property, not loaded from examine @@ -137,44 +156,26 @@ namespace umbraco.MacroEngines { get { - int parentId = 0; - string value = null; - if (Values.TryGetValue("parentID", out value)) - { - if (int.TryParse(value, out parentId)) - { - return new Lazy(() => { return ExamineBackedMedia.GetUmbracoMedia(parentId); }); - } - } - return null; + var parentId = GetValueAsInt("parentID"); + return parentId != 0 + ? new Lazy(() => GetUmbracoMedia(parentId)) + : null; } } + public int ParentId { get { - int parentId = 0; - if (int.TryParse(Values["parentID"], out parentId)) - { - return parentId; - } - return 0; + return GetValueAsInt("parentID"); } } + public int Id { get { - int id = 0; - string value = null; - if (Values.TryGetValue("id", out value)) - { - if (int.TryParse(value, out id)) - { - return id; - } - } - return 0; + return GetValueAsInt("id"); } } @@ -182,7 +183,7 @@ namespace umbraco.MacroEngines { get { - throw new NotImplementedException(); + return GetValueAsInt("sortOrder"); } } @@ -190,30 +191,23 @@ namespace umbraco.MacroEngines { get { - string value = null; - if (Values.TryGetValue("umbracoFile", out value)) - { - return value; - } - return null; + return GetValueAsString("umbracoFile"); } } public string UrlName { - get { throw new NotImplementedException(); } + get + { + return GetValueAsString("urlName"); + } } public string NodeTypeAlias { get { - string value = null; - if (Values.TryGetValue("nodeTypeAlias", out value)) - { - return value; - } - return null; + return GetValueAsString("nodeTypeAlias"); } } @@ -221,62 +215,56 @@ namespace umbraco.MacroEngines { get { - string value = null; - if (Values.TryGetValue("writerName", out value)) - { - return value; - } - return null; + return GetValueAsString("writerName"); } } public string CreatorName { - get { throw new NotImplementedException(); } + get + { + return GetValueAsString("writerName"); + } } public int WriterID { - get { throw new NotImplementedException(); } + get + { + return GetValueAsInt("writerID"); + } } public int CreatorID { - get { throw new NotImplementedException(); } + get + { + //there is no creator id in xml, so will have to be the same as writer id :( + return GetValueAsInt("writerID"); + } } public string Path { get { - string value = null; - if (Values.TryGetValue("__Path", out value)) - { - return value; - } - return null; + return GetValueAsString("__Path"); } } public DateTime CreateDate { - get { throw new NotImplementedException(); } + get + { + return GetvalueAsDateTime("createDate"); + } } public DateTime UpdateDate { get { - DateTime dt = DateTime.MinValue; - string value = null; - if (Values.TryGetValue("UpdateDate", out value)) - { - if (DateTime.TryParse(value, out dt)) - { - return dt; - } - } - return DateTime.Now; + return GetvalueAsDateTime("updateDate"); } } @@ -284,7 +272,7 @@ namespace umbraco.MacroEngines { get { - throw new NotImplementedException(); + return GetValueAsGuid("version"); } } @@ -292,12 +280,7 @@ namespace umbraco.MacroEngines { get { - string value = null; - if (Values.TryGetValue("umbracoFile", out value)) - { - return value; - } - return null; + return GetValueAsString("umbracoFile"); } } @@ -305,20 +288,87 @@ namespace umbraco.MacroEngines { get { - string value = null; - if (Values.TryGetValue("__Path", out value)) - { - return value.Split(',').Length; - } - return 0; + var level = GetValueAsInt("level"); + if (level != 0) return level; + + //return it based on the path if level is not there + string value; + return Values.TryGetValue("__Path", out value) ? value.Split(',').Length : 0; } } + private BaseIndexProvider GetIndexer() + { + return _indexer ?? ExamineManager.Instance.IndexProviderCollection["InternalIndexer"]; + } + + private BaseSearchProvider GetSearcher() + { + return _searcher ?? ExamineManager.Instance.SearchProviderCollection["InternalSearcher"]; + } + + private DateTime GetvalueAsDateTime(string key) + { + var dt = DateTime.MinValue; + string value = null; + if (Values.TryGetValue(key, out value)) + { + if (DateTime.TryParse(value, out dt)) + { + return dt; + } + //normally dates in lucene are stored with a specific lucnene date format so we'll try to parse that. + try + { + return DateTools.StringToDate(value); + } + catch (FormatException) + { + //it could not be formatted :( + } + } + return dt; + } + + private Guid GetValueAsGuid(string key) + { + string value; + if (Values.TryGetValue(key, out value)) + { + Guid gId; + if (Guid.TryParse(value, out gId)) + { + return gId; + } + } + return Guid.Empty; + } + + private string GetValueAsString(string key) + { + string value; + return Values.TryGetValue(key, out value) ? value : null; + } + + private int GetValueAsInt(string key) + { + var val = 0; + string value; + if (Values.TryGetValue(key, out value)) + { + if (int.TryParse(value, out val)) + { + return val; + } + } + return val; + } + public List PropertiesAsList { get { - string[] internalProperties = new string[] { + var internalProperties = new[] { "id", "nodeName", "updateDate", "writerName", "path", "nodeTypeAlias", "parentID", "__NodeId", "__IndexType", "__Path", "__NodeTypeAlias", "__nodeName" @@ -336,27 +386,37 @@ namespace umbraco.MacroEngines { get { - return new Lazy>(() => - { - return GetChildrenMedia(this.Id); - }); + return new Lazy>(() => GetChildrenMedia(this.Id)); } } - private static List GetChildrenMedia(int ParentId) + + private List GetChildrenMedia(int parentId) { try { //first check in Examine as this is WAY faster - var criteria = ExamineManager.Instance - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); - var filter = criteria.ParentId(ParentId); - var results = ExamineManager - .Instance.SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); + var searcher = GetSearcher(); + var indexer = GetIndexer(); + var criteria = searcher.CreateSearchCriteria("media"); + var filter = criteria.ParentId(parentId); + ISearchResults results; + var useLuceneSort = indexer != null && indexer.IndexerData.StandardFields.Any(x => x.Name.InvariantEquals("sortOrder") && x.EnableSorting); + if (useLuceneSort) + { + //we have a sortOrder field declared to be sorted, so we'll use Examine + results = searcher.Search( + filter.And().OrderBy(new SortableField("sortOrder", SortType.Int)).Compile()); + } + else + { + results = searcher.Search(filter.Compile()); + } + if (results.Any()) { - return results.ToList().ConvertAll(result => new ExamineBackedMedia(result)); + return useLuceneSort + ? results.Select(result => new ExamineBackedMedia(result)).ToList() //will already be sorted by lucene + : results.Select(result => new ExamineBackedMedia(result)).OrderBy(x => x.SortOrder).ToList(); } } catch (FileNotFoundException) @@ -366,12 +426,12 @@ namespace umbraco.MacroEngines //Catch the exception here for the time being, and just fallback to GetMedia } - var media = umbraco.library.GetMedia(ParentId, true); + var media = umbraco.library.GetMedia(parentId, true); if (media != null && media.Current != null) { media.MoveNext(); var children = media.Current.SelectChildren(XPathNodeType.Element); - List mediaList = new List(); + var mediaList = new List(); while (children != null && children.Current != null) { if (children.MoveNext()) diff --git a/src/umbraco.MacroEngines/packages.config b/src/umbraco.MacroEngines/packages.config index af909515d2..c9042c5d9d 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index 6a48f06cf6..f1f2235757 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -42,9 +42,9 @@ bin\Release\umbraco.MacroEngines.xml - + False - ..\packages\Examine.0.1.42.2941\lib\Examine.dll + ..\packages\Examine.0.1.47.2941\lib\Examine.dll False diff --git a/src/umbraco.editorControls/tinymce/tinyMCEImageHelper.cs b/src/umbraco.editorControls/tinymce/tinyMCEImageHelper.cs index 6072ec936f..977a85ed04 100644 --- a/src/umbraco.editorControls/tinymce/tinyMCEImageHelper.cs +++ b/src/umbraco.editorControls/tinymce/tinyMCEImageHelper.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Linq; using System.Text.RegularExpressions; using System.Web; using Umbraco.Core.IO; @@ -14,7 +15,12 @@ namespace umbraco.editorControls.tinymce { public static string cleanImages(string html) { - var allowedAttributes = UmbracoSettings.ImageAllowedAttributes.ToLower().Split(','); + var allowedAttributes = UmbracoSettings.ImageAllowedAttributes.ToLower().Split(',').ToList(); + + //Always add src as it's essential to output any image at all + if (allowedAttributes.Contains("src") == false) + allowedAttributes.Add("src"); + const string pattern = @"]*>"; var tags = Regex.Matches(html + " ", pattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); foreach (Match tag in tags) @@ -43,7 +49,11 @@ namespace umbraco.editorControls.tinymce { int newWidth; int newHeight; - cleanTag += DoResize(ht, out newWidth, out newHeight); + string newSrc; + + cleanTag += DoResize(ht, out newWidth, out newHeight, out newSrc); + + ht["src"] = newSrc; } catch (Exception err) { @@ -96,7 +106,7 @@ namespace umbraco.editorControls.tinymce return html; } - private static string DoResize(IDictionary attributes, out int finalWidth, out int finalHeight) + private static string DoResize(IDictionary attributes, out int finalWidth, out int finalHeight, out string newSrc) { var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); var orgSrc = HttpContext.Current.Server.HtmlDecode(helper.FindAttribute(attributes, "src").Replace("%20", " ")); @@ -106,7 +116,7 @@ namespace umbraco.editorControls.tinymce var newWidth = int.Parse(helper.FindAttribute(attributes, "width")); var newHeight = int.Parse(helper.FindAttribute(attributes, "height")); - var newSrc = ""; + newSrc = ""; if (orgHeight > 0 && orgWidth > 0 && orgSrc != "") { @@ -131,7 +141,7 @@ namespace umbraco.editorControls.tinymce finalWidth = newWidth; finalHeight = newHeight; - return " src=\"" + newSrc + "\" width=\"" + newWidth + "\" height=\"" + newHeight + "\""; + return " width=\"" + newWidth + "\" height=\"" + newHeight + "\""; } } } \ No newline at end of file