From c8807c379868ec0fbd391dffa46baca49e0321ed Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 24 Jan 2013 06:16:44 +0300 Subject: [PATCH] Fixes: #U4-1530. Backports ContentExtension changes from 6.1 required for Examine and media. Added unit tests to support and enhanced old unit test for ToXml for content to test for more values to ensure nothing is broken. Updates ExamineEvents to ensure that no event binding occurs until the application is configured and the database is ready. Updates ExamineEvents to ensure that data is indexed using the new 6.0 APIs, though this is a temporary fix and a better one will be implemented in 6.1 since UmbracoExamine is in the source. --- src/Umbraco.Core/Models/ContentExtensions.cs | 119 +++++++++++++----- src/Umbraco.Tests/Models/ContentXmlTest.cs | 22 ++++ .../Entities/MockedContentTypes.cs | 26 ++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../umbraco/Search/ExamineEvents.cs | 98 ++++++++++++++- 5 files changed, 229 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index e2b81de74e..3e0f95d8a5 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -154,7 +154,7 @@ namespace Umbraco.Core.Models private static void SetFileOnContent(IContentBase content, string propertyTypeAlias, string name, Stream fileStream) { var property = content.Properties.FirstOrDefault(x => x.Alias == propertyTypeAlias); - if(property == null) + if (property == null) return; bool supportsResizing = false; @@ -204,7 +204,7 @@ namespace Umbraco.Core.Models XmlNode uploadFieldConfigNode = UmbracoSettings.ImageAutoFillImageProperties.SelectSingleNode( string.Format("uploadField [@alias = \"{0}\"]", propertyTypeAlias)); - + if (uploadFieldConfigNode != null) { //Only add dimensions to web images @@ -236,7 +236,7 @@ namespace Umbraco.Core.Models content.SetValue(propertyNode.FirstChild.Value, propertyValue); } } - + private static string Resize(MediaFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition) { var fileNameThumb = DoResize(fileSystem, path, extension, GetDimensions(fileSystem, path).Item1, GetDimensions(fileSystem, path).Item2, maxWidthHeight, fileNameAddition); @@ -347,16 +347,28 @@ namespace Umbraco.Core.Models return new Tuple(widthTh, heightTh, newFileName); } + /// + /// Gets the for the Creator of this media item. + /// + internal static IProfile GetCreatorProfile(this IMedia media) + { + using (var repository = RepositoryResolver.Current.Factory.CreateUserRepository( + PetaPocoUnitOfWorkProvider.CreateUnitOfWork())) + { + return repository.GetProfileById(media.CreatorId); + } + } + /// /// Gets the for the Creator of this content. /// public static IProfile GetCreatorProfile(this IContent content) { - using (var repository = RepositoryResolver.Current.Factory.CreateUserRepository( - PetaPocoUnitOfWorkProvider.CreateUnitOfWork())) - { - return repository.GetProfileById(content.CreatorId); - } + using (var repository = RepositoryResolver.Current.Factory.CreateUserRepository( + PetaPocoUnitOfWorkProvider.CreateUnitOfWork())) + { + return repository.GetProfileById(content.CreatorId); + } } /// @@ -364,11 +376,11 @@ namespace Umbraco.Core.Models /// public static IProfile GetWriterProfile(this IContent content) { - using(var repository = RepositoryResolver.Current.Factory.CreateUserRepository( - PetaPocoUnitOfWorkProvider.CreateUnitOfWork())) - { - return repository.GetProfileById(content.WriterId); - } + using (var repository = RepositoryResolver.Current.Factory.CreateUserRepository( + PetaPocoUnitOfWorkProvider.CreateUnitOfWork())) + { + return repository.GetProfileById(content.WriterId); + } } /// @@ -391,30 +403,73 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in public static XElement ToXml(this IContent content) { + //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck(); - var niceUrl = content.Name.FormatUrl().ToLower(); + + var x = content.ToXml(nodeName); + x.Add(new XAttribute("nodeType", content.ContentType.Id)); + x.Add(new XAttribute("creatorName", content.GetCreatorProfile().Name)); + x.Add(new XAttribute("writerName", content.GetWriterProfile().Name)); + x.Add(new XAttribute("writerID", content.WriterId)); + x.Add(new XAttribute("template", content.Template == null ? "0" : content.Template.Id.ToString())); + if (UmbracoSettings.UseLegacyXmlSchema) + { + x.Add(new XAttribute("nodeTypeAlias", content.ContentType.Alias)); + } + + return x; + + } + + /// + /// Creates the xml representation for the object + /// + /// to generate xml for + /// Xml representation of the passed in + internal static XElement ToXml(this IMedia media) + { + //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); + var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : media.ContentType.Alias.ToSafeAliasWithForcingCheck(); + + var x = media.ToXml(nodeName); + x.Add(new XAttribute("nodeType", media.ContentType.Id)); + x.Add(new XAttribute("writerName", media.GetCreatorProfile().Name)); + x.Add(new XAttribute("writerID", media.CreatorId)); + x.Add(new XAttribute("version", media.Version)); + x.Add(new XAttribute("template", 0)); + if (UmbracoSettings.UseLegacyXmlSchema) + { + x.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias)); + } + + return x; + } + + /// + /// Creates the xml representation for the object + /// + /// to generate xml for + /// + /// Xml representation of the passed in + private static XElement ToXml(this IContentBase contentBase, string nodeName) + { + var niceUrl = contentBase.Name.FormatUrl().ToLower(); var xml = new XElement(nodeName, - new XAttribute("id", content.Id), - new XAttribute("parentID", content.Level > 1 ? content.ParentId : -1), - new XAttribute("level", content.Level), - new XAttribute("writerID", content.WriterId), - new XAttribute("creatorID", content.CreatorId), - new XAttribute("nodeType", content.ContentType.Id), - new XAttribute("template", content.Template == null ? "0" : content.Template.Id.ToString()), - new XAttribute("sortOrder", content.SortOrder), - new XAttribute("createDate", content.CreateDate.ToString("s")), - new XAttribute("updateDate", content.UpdateDate.ToString("s")), - new XAttribute("nodeName", content.Name), - new XAttribute("urlName", niceUrl),//Format Url ? - new XAttribute("writerName", content.GetWriterProfile().Name), - new XAttribute("creatorName", content.GetCreatorProfile().Name), - new XAttribute("path", content.Path), - new XAttribute("isDoc", ""), - UmbracoSettings.UseLegacyXmlSchema ? new XAttribute("nodeTypeAlias", content.ContentType.Alias) : null); + new XAttribute("id", contentBase.Id), + new XAttribute("parentID", contentBase.Level > 1 ? contentBase.ParentId : -1), + new XAttribute("level", contentBase.Level), + new XAttribute("creatorID", contentBase.CreatorId), + new XAttribute("sortOrder", contentBase.SortOrder), + new XAttribute("createDate", contentBase.CreateDate.ToString("s")), + new XAttribute("updateDate", contentBase.UpdateDate.ToString("s")), + new XAttribute("nodeName", contentBase.Name), + new XAttribute("urlName", niceUrl),//Format Url ? + new XAttribute("path", contentBase.Path), + new XAttribute("isDoc", "")); - foreach (var property in content.Properties) + foreach (var property in contentBase.Properties) { if (property == null) continue; diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index d080e037b6..f05230d191 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Xml.Linq; using NUnit.Framework; using Umbraco.Core; @@ -61,6 +62,27 @@ namespace Umbraco.Tests.Models // Assert Assert.That(element, Is.Not.Null); Assert.That(element.Name.LocalName, Is.EqualTo(nodeName)); + Assert.AreEqual(content.Id.ToString(), (string)element.Attribute("id")); + Assert.AreEqual(content.ParentId.ToString(), (string)element.Attribute("parentID")); + Assert.AreEqual(content.Level.ToString(), (string)element.Attribute("level")); + Assert.AreEqual(content.CreatorId.ToString(), (string)element.Attribute("creatorID")); + Assert.AreEqual(content.SortOrder.ToString(), (string)element.Attribute("sortOrder")); + Assert.AreEqual(content.CreateDate.ToString("s"), (string)element.Attribute("createDate")); + Assert.AreEqual(content.UpdateDate.ToString("s"), (string)element.Attribute("updateDate")); + Assert.AreEqual(content.Name, (string)element.Attribute("nodeName")); + Assert.AreEqual(content.Name.FormatUrl().ToLower(), (string)element.Attribute("urlName")); + Assert.AreEqual(content.Path, (string)element.Attribute("path")); + Assert.AreEqual("", (string)element.Attribute("isDoc")); + Assert.AreEqual(content.ContentType.Id.ToString(), (string)element.Attribute("nodeType")); + Assert.AreEqual(content.GetCreatorProfile().Name, (string)element.Attribute("creatorName")); + Assert.AreEqual(content.GetWriterProfile().Name, (string)element.Attribute("writerName")); + Assert.AreEqual(content.WriterId.ToString(), (string)element.Attribute("writerID")); + Assert.AreEqual(content.Template == null ? "0" : content.Template.Id.ToString(), (string)element.Attribute("template")); + + Assert.AreEqual(content.Properties["title"].Value.ToString(), element.Elements("title").Single().Value); + Assert.AreEqual(content.Properties["bodyText"].Value.ToString(), element.Elements("bodyText").Single().Value); + Assert.AreEqual(content.Properties["keywords"].Value.ToString(), element.Elements("keywords").Single().Value); + Assert.AreEqual(content.Properties["metaDescription"].Value.ToString(), element.Elements("metaDescription").Single().Value); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index b3a7f257c9..f0bf4c35c4 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -169,5 +169,31 @@ namespace Umbraco.Tests.TestHelpers.Entities return mediaType; } + + public static MediaType CreateImageMediaType() + { + var mediaType = new MediaType(-1) + { + Alias = "image", + Name = "Image", + Description = "ContentType used for images", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Nvarchar) { Alias = "umbracoFile", Name = "File", Description = "", HelpText = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -90 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Integer) { Alias = "umbracoWidth", Name = "Width", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Integer) { Alias = "umbracoHeight", Name = "Height", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Integer) { Alias = "umbracoBytes", Name = "Bytes", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 }); + contentCollection.Add(new PropertyType(new Guid(), DataTypeDatabaseType.Integer) { Alias = "umbracoExtension", Name = "File Extension", Description = "", HelpText = "", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -90 }); + + mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); + + return mediaType; + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bf6540e963..27380cb453 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -175,6 +175,7 @@ + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs index 1d30d3eb88..c0012a9e85 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs @@ -1,37 +1,124 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Web; +using Examine.Providers; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web; using umbraco.BusinessLogic; using Examine; using UmbracoExamine; using Lucene.Net.Documents; using umbraco.businesslogic; +using umbraco.cms.businesslogic.media; using umbraco.interfaces; +using Content = umbraco.cms.businesslogic.Content; namespace umbraco.presentation.umbraco.Search { /// /// Used to wire up events for Examine /// - public class ExamineEvents : IApplicationStartupHandler + public class ExamineEvents : IApplicationEventHandler { - public ExamineEvents() - : base() + public void OnApplicationInitialized(UmbracoApplication httpApplication, ApplicationContext applicationContext) { + } + + public void OnApplicationStarting(UmbracoApplication httpApplication, ApplicationContext applicationContext) + { + } + + /// + /// Once the app has booted, then bind to the events + /// + /// + /// + public void OnApplicationStarted(UmbracoApplication httpApplication, ApplicationContext applicationContext) + { + //do not continue if the app context or database is not ready + if (!applicationContext.IsConfigured || !applicationContext.DatabaseContext.IsDatabaseConfigured) + return; + + //TODO: Remove this in 6.1!!! It will not be needed because we've changed the Examine Events entirely since UmbracoExamine is + // in the core. This is only temporary to get this task completed for 6.0: + // http://issues.umbraco.org/issue/U4-1530 + MediaService.Saved += MediaService_Saved; + MediaService.Deleted += MediaService_Deleted; + MediaService.Moved += MediaService_Moved; + ContentService.Saved += ContentService_Saved; + ContentService.Deleted += ContentService_Deleted; + ContentService.Moved += ContentService_Moved; + + //bind to examine events var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer; if (contentIndexer != null) { - contentIndexer.DocumentWriting += new EventHandler(indexer_DocumentWriting); + contentIndexer.DocumentWriting += indexer_DocumentWriting; } var memberIndexer = ExamineManager.Instance.IndexProviderCollection["InternalMemberIndexer"] as UmbracoMemberIndexer; if (memberIndexer != null) { - memberIndexer.DocumentWriting += new EventHandler(indexer_DocumentWriting); + memberIndexer.DocumentWriting += indexer_DocumentWriting; } } + void ContentService_Moved(IContentService sender, Umbraco.Core.Events.MoveEventArgs e) + { + IndexConent(e.Entity); + } + + void ContentService_Deleted(IContentService sender, Umbraco.Core.Events.DeleteEventArgs e) + { + e.DeletedEntities.ForEach( + content => + ExamineManager.Instance.DeleteFromIndex( + content.Id.ToString(), + ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler))); + } + + void ContentService_Saved(IContentService sender, Umbraco.Core.Events.SaveEventArgs e) + { + e.SavedEntities.ForEach(IndexConent); + } + + void MediaService_Moved(IMediaService sender, Umbraco.Core.Events.MoveEventArgs e) + { + IndexMedia(e.Entity); + } + + void MediaService_Deleted(IMediaService sender, Umbraco.Core.Events.DeleteEventArgs e) + { + e.DeletedEntities.ForEach( + media => + ExamineManager.Instance.DeleteFromIndex( + media.Id.ToString(), + ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler))); + } + + void MediaService_Saved(IMediaService sender, Umbraco.Core.Events.SaveEventArgs e) + { + e.SavedEntities.ForEach(IndexMedia); + } + + private void IndexMedia(IMedia sender) + { + ExamineManager.Instance.ReIndexNode( + sender.ToXml(), "media", + ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)); + } + + private void IndexConent(IContent sender) + { + ExamineManager.Instance.ReIndexNode( + sender.ToXml(), "content", + ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)); + } + /// /// Event handler to create a lower cased version of the node name, this is so we can support case-insensitive searching and still /// use the Whitespace Analyzer @@ -52,5 +139,6 @@ namespace umbraco.presentation.umbraco.Search } } + } } \ No newline at end of file