diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs index 907f9b62c5..64989f9269 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs @@ -38,5 +38,15 @@ namespace Umbraco.Core.Persistence.Repositories /// An Enumerable list of objects IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, string filter = ""); + + /// + /// Gets paged media descendants as XML by path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of media items + IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 598c9e912d..7a6fa4c34e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -465,6 +465,30 @@ namespace Umbraco.Core.Persistence.Repositories } + /// + /// Gets paged media descendants as XML by path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of media items + public IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords) + { + Sql query; + if (path == "-1") + { + query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", Guid.Parse(Constants.ObjectTypes.Media)).OrderBy("nodeId"); + } + else + { + query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE @0)", path.EnsureEndsWith(",%")).OrderBy("nodeId"); + } + var pagedResult = Database.Page(pageIndex+1, pageSize, query); + totalRecords = pagedResult.TotalItems; + return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml)); + } + private IEnumerable ProcessQuery(Sql sql) { //NOTE: This doesn't allow properties to be part of the query diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6ff8f75402..d25ddf7f58 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Xml.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -57,6 +58,19 @@ namespace Umbraco.Core.Services /// public interface IMediaService : IService { + /// + /// Gets all XML entries found in the cmsContentXml table based on the given path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of media items + /// + /// If -1 is passed, then this will return all media xml entries, otherwise will return all descendents from the path + /// + IEnumerable GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords); + /// /// Rebuilds all xml content in the cmsContentXml table for all media /// diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 29235cc7ab..a1182ce80a 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -1199,6 +1199,27 @@ namespace Umbraco.Core.Services return true; } + /// + /// Gets paged media descendants as XML by path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of media items + public IEnumerable GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords) + { + Mandate.ParameterCondition(pageIndex >= 0, "pageIndex"); + Mandate.ParameterCondition(pageSize > 0, "pageSize"); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMediaRepository(uow)) + { + var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, out totalRecords); + return contents; + } + } + /// /// Rebuilds all xml content in the cmsContentXml table for all media /// diff --git a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs index 8a83bea75a..5bf6a4edc5 100644 --- a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Tests.PublishedContent { + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] public class LegacyExamineBackedMediaTests : ExamineBaseTest { public override void Initialize() diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index 6bd01c7f1c..3e7377f3b6 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -3,11 +3,13 @@ using System.Linq; using Examine; using Lucene.Net.Store; using NUnit.Framework; +using Umbraco.Tests.TestHelpers; using UmbracoExamine; namespace Umbraco.Tests.UmbracoExamine { - [TestFixture] + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + [TestFixture] public class EventsTest : ExamineBaseTest { [Test] diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index b303eed997..89a9df8052 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Xml.Linq; using Examine; using Examine.LuceneEngine.Config; using Examine.LuceneEngine.Providers; @@ -7,10 +9,14 @@ using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; using Lucene.Net.Store; using Moq; +using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; using UmbracoExamine; using UmbracoExamine.Config; @@ -34,7 +40,8 @@ namespace Umbraco.Tests.UmbracoExamine IMediaService mediaService = null, IDataTypeService dataTypeService = null, IMemberService memberService = null, - IUserService userService = null) + IUserService userService = null, + IContentTypeService contentTypeService = null) { if (dataService == null) { @@ -94,7 +101,8 @@ namespace Umbraco.Tests.UmbracoExamine long longTotalRecs; int intTotalRecs; - var allRecs = dataService.MediaService.GetLatestMediaByXpath("//node") + var mediaXml = dataService.MediaService.GetLatestMediaByXpath("//node"); + var allRecs = mediaXml .Root .Elements() .Select(x => Mock.Of( @@ -114,20 +122,29 @@ namespace Umbraco.Tests.UmbracoExamine mt.Id == (int)x.Attribute("nodeType")))) .ToArray(); + // MOCK! + var mediaServiceMock = new Mock(); + + mediaServiceMock + .Setup(x => x.GetPagedDescendants( + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()) + ).Returns(() => allRecs); + + mediaServiceMock + .Setup(x => x.GetPagedDescendants( + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + ).Returns(() => allRecs); + + mediaServiceMock + .Setup(x => x.GetPagedDescendants( + It.IsAny(), It.IsAny(), It.IsAny(), out intTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + ).Returns(() => allRecs); + + mediaServiceMock.Setup(service => service.GetPagedXmlEntries(It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs)) + .Returns(() => allRecs.Select(x => x.ToXml())); + + mediaService = mediaServiceMock.Object; - mediaService = Mock.Of( - x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()) - == - allRecs - && x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) - == - allRecs - && x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out intTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) - == - allRecs); } if (dataTypeService == null) { @@ -139,6 +156,18 @@ namespace Umbraco.Tests.UmbracoExamine memberService = Mock.Of(); } + if (contentTypeService == null) + { + var contentTypeServiceMock = new Mock(); + contentTypeServiceMock.Setup(x => x.GetAllContentTypes()) + .Returns(new List() + { + new ContentType(-1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, + new ContentType(-1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} + }); + contentTypeService = contentTypeServiceMock.Object; + } + if (analyzer == null) { analyzer = new StandardAnalyzer(Version.LUCENE_29); @@ -154,6 +183,7 @@ namespace Umbraco.Tests.UmbracoExamine mediaService, dataTypeService, userService, + contentTypeService, analyzer, false); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 7c36dd2953..3bc635bb23 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -10,15 +10,17 @@ using Lucene.Net.Index; using Lucene.Net.Search; using Lucene.Net.Store; using NUnit.Framework; +using Umbraco.Tests.TestHelpers; using UmbracoExamine; namespace Umbraco.Tests.UmbracoExamine { - /// - /// Tests the standard indexing capabilities - /// - [TestFixture, RequiresSTA] + /// + /// Tests the standard indexing capabilities + /// + //[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + [TestFixture, RequiresSTA] public class IndexTest : ExamineBaseTest { @@ -85,12 +87,11 @@ namespace Umbraco.Tests.UmbracoExamine //RESET the parent id existingCriteria = ((IndexCriteria)_indexer.IndexerData); _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, - null); + null); //now ensure it's deleted var newResults = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile()); Assert.AreEqual(1, newResults.Count()); - } [Test] diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index bcd9922e21..9b8b3d50d7 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -8,9 +8,11 @@ using Examine.LuceneEngine.Providers; using Lucene.Net.Store; using NUnit.Framework; using Examine.LuceneEngine.SearchCriteria; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.UmbracoExamine { + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] [TestFixture] public class SearchTests : ExamineBaseTest { diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 4fcf51deae..ce3583a6be 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -1,20 +1,12 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; -using System.Security; -using System.Text; -using System.Web; using System.Xml.Linq; using Examine; -using Examine.Config; -using Examine.Providers; using Lucene.Net.Documents; -using Lucene.Net.Index; using Umbraco.Core; -using umbraco.cms.businesslogic; using Umbraco.Core.Models; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Services; @@ -22,12 +14,9 @@ using UmbracoExamine.DataServices; using Examine.LuceneEngine; using Examine.LuceneEngine.Config; using UmbracoExamine.Config; -using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; -using umbraco.BasePages; using Umbraco.Core.Persistence.Querying; using IContentService = Umbraco.Core.Services.IContentService; -using UmbracoExamine.LocalStorage; using IMediaService = Umbraco.Core.Services.IMediaService; @@ -42,6 +31,7 @@ namespace UmbracoExamine private readonly IMediaService _mediaService; private readonly IDataTypeService _dataTypeService; private readonly IUserService _userService; + private readonly IContentTypeService _contentTypeService; #region Constructors @@ -55,6 +45,7 @@ namespace UmbracoExamine _mediaService = ApplicationContext.Current.Services.MediaService; _dataTypeService = ApplicationContext.Current.Services.DataTypeService; _userService = ApplicationContext.Current.Services.UserService; + _contentTypeService = ApplicationContext.Current.Services.ContentTypeService; } /// @@ -73,6 +64,7 @@ namespace UmbracoExamine _mediaService = ApplicationContext.Current.Services.MediaService; _dataTypeService = ApplicationContext.Current.Services.DataTypeService; _userService = ApplicationContext.Current.Services.UserService; + _contentTypeService = ApplicationContext.Current.Services.ContentTypeService; } /// @@ -91,6 +83,7 @@ namespace UmbracoExamine _mediaService = ApplicationContext.Current.Services.MediaService; _dataTypeService = ApplicationContext.Current.Services.DataTypeService; _userService = ApplicationContext.Current.Services.UserService; + _contentTypeService = ApplicationContext.Current.Services.ContentTypeService; } /// @@ -105,6 +98,7 @@ namespace UmbracoExamine /// /// /// + [Obsolete("Use the overload that specifies the Umbraco services")] public UmbracoContentIndexer(IIndexCriteria indexerData, Lucene.Net.Store.Directory luceneDirectory, IDataService dataService, IContentService contentService, IMediaService mediaService, @@ -117,13 +111,43 @@ namespace UmbracoExamine _mediaService = mediaService; _dataTypeService = dataTypeService; _userService = userService; + _contentTypeService = ApplicationContext.Current.Services.ContentTypeService; + } + + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public UmbracoContentIndexer(IIndexCriteria indexerData, Lucene.Net.Store.Directory luceneDirectory, IDataService dataService, + IContentService contentService, + IMediaService mediaService, + IDataTypeService dataTypeService, + IUserService userService, + IContentTypeService contentTypeService, + Analyzer analyzer, bool async) + : base(indexerData, luceneDirectory, dataService, analyzer, async) + { + _contentService = contentService; + _mediaService = mediaService; + _dataTypeService = dataTypeService; + _userService = userService; + _contentTypeService = contentTypeService; } #endregion #region Constants & Fields - + /// /// Used to store the path of a content object @@ -206,13 +230,8 @@ namespace UmbracoExamine SupportProtectedContent = supportProtected; else SupportProtectedContent = false; - - + base.Initialize(name, config); - - - - } #endregion @@ -285,10 +304,7 @@ namespace UmbracoExamine #endregion #region Public methods - - - /// /// Overridden for logging /// @@ -308,7 +324,6 @@ namespace UmbracoExamine { DataService.LogService.AddErrorLog(-1, string.Format("ReIndexNode cannot proceed, the format of the XElement is invalid, the xml has no 'id' attribute. {0}", node)); } - } /// @@ -355,8 +370,6 @@ namespace UmbracoExamine switch (type) { case IndexTypes.Content: - - var contentParentId = -1; if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) { @@ -391,69 +404,63 @@ namespace UmbracoExamine { content = descendants.ToArray(); } - AddNodesToIndex(GetSerializedContent(content), type); pageIndex++; - - } while (content.Length == pageSize); break; case IndexTypes.Media: - var mediaParentId = -1; + if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) { mediaParentId = IndexerData.ParentNodeId.Value; } - IMedia[] media; + XElement[] mediaXElements; + + var nodeTypes = _contentTypeService.GetAllContentTypes().ToArray(); + var icons = nodeTypes.ToDictionary(x => x.Id, y => y.Icon); + do { long total; - var descendants = _mediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out total); + if (mediaParentId == -1) + { + mediaXElements = _mediaService.GetPagedXmlEntries("-1", pageIndex, pageSize, out total).ToArray(); + } + else + { + //Get the parent + var parent = _mediaService.GetById(mediaParentId); + if (parent == null) + mediaXElements = new XElement[0]; + else + mediaXElements = _mediaService.GetPagedXmlEntries(parent.Path, pageIndex, pageSize, out total).ToArray(); + } //if specific types are declared we need to post filter them //TODO: Update the service layer to join the cmsContentType table so we can query by content type too if (IndexerData.IncludeNodeTypes.Any()) { - media = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); + var includeNodeTypeIds = nodeTypes.Where(x => IndexerData.IncludeNodeTypes.Contains(x.Alias)).Select(x => x.Id); + mediaXElements = mediaXElements.Where(elm => includeNodeTypeIds.Contains(elm.AttributeValue("nodeType"))).ToArray(); } - else + + // ReSharper disable once ForCanBeConvertedToForeach + for (var i = 0; i < mediaXElements.Length; i++) { - media = descendants.ToArray(); + mediaXElements[i].Add(new XAttribute("icon", icons[mediaXElements[i].AttributeValue("nodeType")])); } - - AddNodesToIndex(GetSerializedMedia(media), type); + + AddNodesToIndex(mediaXElements, type); pageIndex++; - } while (media.Length == pageSize); + } while (mediaXElements.Length == pageSize); break; } } - private IEnumerable GetSerializedMedia(IEnumerable media) - { - var serializer = new EntityXmlSerializer(); - foreach (var m in media) - { - var xml = serializer.Serialize( - _mediaService, - _dataTypeService, - _userService, - m); - - //add a custom 'icon' attribute - if (m.ContentType.Icon.IsNullOrWhiteSpace() == false) - { - xml.Add(new XAttribute("icon", m.ContentType.Icon)); - } - - - yield return xml; - } - } - private IEnumerable GetSerializedContent(IEnumerable content) { var serializer = new EntityXmlSerializer(); @@ -510,7 +517,6 @@ namespace UmbracoExamine protected override void OnGatheringNodeData(IndexingNodeDataEventArgs e) { - //strip html of all users fields if we detect it has HTML in it. //if that is the case, we'll create a duplicate 'raw' copy of it so that we can return //the value of the field 'as-is'. @@ -546,7 +552,6 @@ namespace UmbracoExamine var icon = (string)e.Node.Attribute("icon"); if (!e.Fields.ContainsKey(IconFieldName)) e.Fields.Add(IconFieldName, icon); - } /// @@ -584,7 +589,6 @@ namespace UmbracoExamine } return fields; - } /// @@ -605,7 +609,6 @@ namespace UmbracoExamine { return base.GetIndexerData(indexSet); } - } /// @@ -635,10 +638,9 @@ namespace UmbracoExamine { return false; } - return base.ValidateDocument(node); } #endregion } -} +} \ No newline at end of file