From 8b49d2d377cf79e760da2c15454e0e53ca542728 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Sat, 5 Jan 2013 22:46:50 +0300 Subject: [PATCH] Removes all MyGet dependencies for UmbracoExamine (since it is in the core now). Converts UmbracoContextService and UmbracoMediaService for UmbracoExamine to use the new data api's to extract latest data. Currently had to hack a bit with a new class called LegacyLibrary because at the moment we'll have a circular dependency with Umbraco.Web which needs to be fixed. Moves all Examine event management to Umbraco.Web (again mainly because of circular dependencies, but makes sense to have it there). Fixes the ExamineEvents to implement IApplicationEventHandler instead of IApplicationStartupHandler to ensure that all resolvers have been initialized before attempting to initialize examine indexes. Updates Examine ILogService to use the LogHelper. Adds some new ContentExtensions for IMedia since currently Examine needs xml structures, but have kept it internal. --- src/Umbraco.Core/Models/ContentExtensions.cs | 110 ++++-- src/Umbraco.Core/Properties/AssemblyInfo.cs | 3 +- src/Umbraco.Tests/packages.config | 1 - src/Umbraco.Web/Search/ExamineEvents.cs | 316 ++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 9 +- src/Umbraco.Web/packages.config | 1 - .../umbraco/Search/ExamineEvents.cs | 40 +-- src/UmbracoExamine/ContentExtensions.cs | 9 +- .../DataServices/ILogService.cs | 4 +- .../DataServices/UmbracoContentService.cs | 68 ++-- .../DataServices/UmbracoLogService.cs | 19 +- .../DataServices/UmbracoMediaService.cs | 71 ++-- src/UmbracoExamine/LegacyLibrary.cs | 58 ++++ src/UmbracoExamine/LoggingLevel.cs | 1 + src/UmbracoExamine/UmbracoEventManager.cs | 197 +---------- src/UmbracoExamine/UmbracoExamine.csproj | 10 +- src/UmbracoExamine/UmbracoMemberIndexer.cs | 9 +- src/umbraco.MacroEngines/packages.config | 1 - .../umbraco.MacroEngines.csproj | 8 +- src/umbraco.sln | 2 +- 20 files changed, 570 insertions(+), 367 deletions(-) create mode 100644 src/Umbraco.Web/Search/ExamineEvents.cs create mode 100644 src/UmbracoExamine/LegacyLibrary.cs diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 240724b507..df4696ad30 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -345,6 +345,18 @@ namespace Umbraco.Core.Models return new Tuple(widthTh, heightTh, newFileName); } + /// + /// Gets the for the Creator of this media item. + /// + public 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. /// @@ -389,43 +401,79 @@ 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 = content.ContentType.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); - var nodeName = content.ContentType.Alias; - var niceUrl = content.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", "")); + //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); + //var nodeName = content.ContentType.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); + var nodeName = content.ContentType.Alias; - foreach (var property in content.Properties) - { - if (property == null) continue; + 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())); - xml.Add(property.ToXml()); + return x; - //Check for umbracoUrlName convention - if (property.Alias == "umbracoUrlName" && property.Value.ToString().Trim() != string.Empty) - xml.SetAttributeValue("urlName", property.Value); - } - - return xml; } + /// + /// 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 = content.ContentType.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); + var nodeName = media.ContentType.Alias; + + var x = media.ToXml(nodeName); + x.Add(new XAttribute("nodeType", media.ContentType.Id)); + x.Add(new XAttribute("creatorName", media.GetCreatorProfile().Name)); + x.Add(new XAttribute("writerID", 0)); + x.Add(new XAttribute("template", 0)); + + 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", 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 contentBase.Properties) + { + if (property == null) continue; + + xml.Add(property.ToXml()); + + //Check for umbracoUrlName convention + if (property.Alias == "umbracoUrlName" && property.Value.ToString().Trim() != string.Empty) + xml.SetAttributeValue("urlName", property.Value); + } + + return xml; + } + /// /// Creates the xml representation for the object /// diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 2c428fab52..e7ef5d3d6b 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -37,4 +37,5 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("Umbraco.Core")] [assembly: InternalsVisibleTo("Umbraco.Web")] -[assembly: InternalsVisibleTo("Umbraco.Web.UI")] \ No newline at end of file +[assembly: InternalsVisibleTo("Umbraco.Web.UI")] +[assembly: InternalsVisibleTo("UmbracoExamine")] \ No newline at end of file diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index af6c498773..100db8bd8c 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -11,5 +11,4 @@ - \ No newline at end of file diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs new file mode 100644 index 0000000000..ef268b21ad --- /dev/null +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -0,0 +1,316 @@ +using System; +using System.Linq; +using System.Security; +using System.Xml; +using System.Xml.Linq; +using Examine; +using Examine.LuceneEngine; +using Lucene.Net.Documents; +using Umbraco.Core; +using Umbraco.Core.Logging; +using UmbracoExamine; +using umbraco; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic; +using umbraco.cms.businesslogic.member; +using umbraco.interfaces; +using Document = umbraco.cms.businesslogic.web.Document; + +namespace Umbraco.Web.Search +{ + /// + /// Used to wire up events for Examine + /// + public class ExamineEvents : IApplicationEventHandler + { + public void OnApplicationInitialized(UmbracoApplication httpApplication, ApplicationContext applicationContext) + { + } + + public void OnApplicationStarting(UmbracoApplication httpApplication, ApplicationContext applicationContext) + { + } + + /// + /// Once the application has started we should bind to all events and initialize the providers. + /// + /// + /// + /// + /// We need to do this on the Started event as to guarantee that all resolvers are setup properly. + /// + [SecuritySafeCritical] + public void OnApplicationStarted(UmbracoApplication httpApplication, ApplicationContext applicationContext) + { + var registeredProviders = ExamineManager.Instance.IndexProviderCollection + .OfType().Count(x => x.EnableDefaultEventHandler); + + LogHelper.Info("Adding examine event handlers for index providers: {0}", () => registeredProviders); + + //don't bind event handlers if we're not suppose to listen + if (registeredProviders == 0) + return; + + global::umbraco.cms.businesslogic.media.Media.AfterSave += MediaAfterSave; + global::umbraco.cms.businesslogic.media.Media.AfterDelete += MediaAfterDelete; + CMSNode.AfterMove += MediaAfterMove; + + //These should only fire for providers that DONT have SupportUnpublishedContent set to true + content.AfterUpdateDocumentCache += ContentAfterUpdateDocumentCache; + content.AfterClearDocumentCache += ContentAfterClearDocumentCache; + + //These should only fire for providers that have SupportUnpublishedContent set to true + Document.AfterSave += DocumentAfterSave; + Document.AfterDelete += DocumentAfterDelete; + + Member.AfterSave += MemberAfterSave; + Member.AfterDelete += MemberAfterDelete; + + var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer; + if (contentIndexer != null) + { + contentIndexer.DocumentWriting += IndexerDocumentWriting; + } + var memberIndexer = ExamineManager.Instance.IndexProviderCollection["InternalMemberIndexer"] as UmbracoMemberIndexer; + if (memberIndexer != null) + { + memberIndexer.DocumentWriting += IndexerDocumentWriting; + } + } + + [SecuritySafeCritical] + private static void MemberAfterSave(Member sender, SaveEventArgs e) + { + //ensure that only the providers are flagged to listen execute + var xml = sender.ToXml(new System.Xml.XmlDocument(), false).ToXElement(); + var providers = ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => x.EnableDefaultEventHandler); + ExamineManager.Instance.ReIndexNode(xml, IndexTypes.Member, providers); + } + + [SecuritySafeCritical] + private static void MemberAfterDelete(Member sender, DeleteEventArgs e) + { + var nodeId = sender.Id.ToString(); + + //ensure that only the providers are flagged to listen execute + ExamineManager.Instance.DeleteFromIndex(nodeId, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => x.EnableDefaultEventHandler)); + } + + /// + /// Only index using providers that SupportUnpublishedContent + /// + /// + /// + [SecuritySafeCritical] + private static void DocumentAfterSave(Document sender, SaveEventArgs e) + { + //ensure that only the providers that have unpublishing support enabled + //that are also flagged to listen + + //there's a bug in 4.0.x that fires the Document saving event handler for media when media is moved, + //therefore, we need to try to figure out if this is media or content. Currently one way to do this + //is by checking the creator ID property as it will be null if it is media. We then need to try to + //create the media object, see if it exists, and pass it to the media save event handler... yeah i know, + //pretty horrible but has to be done. + + try + { + var creator = sender.Creator; + if (creator != null) + { + //it's def a Document + ExamineManager.Instance.ReIndexNode(ToXDocument(sender, false).Root, IndexTypes.Content, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => x.SupportUnpublishedContent + && x.EnableDefaultEventHandler)); + + return; //exit, we've indexed the content + } + } + catch (Exception) + { + //if we get this exception, it means it's most likely media, so well do our check next. + + //TODO: Update this logic for 6.0 as we're not dealing with 4.0x + + //this is most likely media, not sure what kind of exception might get thrown in 4.0.x or 4.1 if accessing a null + //creator, so we catch everything. + } + + + + var m = new global::umbraco.cms.businesslogic.media.Media(sender.Id); + if (string.IsNullOrEmpty(m.Path)) return; + //this is a media item, no exception occurred on access to path and it's not empty which means it was found + MediaAfterSave(m, e); + } + + /// + /// Only remove indexes using providers that SupportUnpublishedContent + /// + /// + /// + [SecuritySafeCritical] + private static void DocumentAfterDelete(Document sender, DeleteEventArgs e) + { + var nodeId = sender.Id.ToString(); + + //ensure that only the providers that have unpublishing support enabled + //that are also flagged to listen + ExamineManager.Instance.DeleteFromIndex(nodeId, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => x.SupportUnpublishedContent + && x.EnableDefaultEventHandler)); + } + + [SecuritySafeCritical] + private static void MediaAfterDelete(global::umbraco.cms.businesslogic.media.Media sender, DeleteEventArgs e) + { + var nodeId = sender.Id.ToString(); + + //ensure that only the providers are flagged to listen execute + ExamineManager.Instance.DeleteFromIndex(nodeId, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => x.EnableDefaultEventHandler)); + } + + [SecuritySafeCritical] + private static void MediaAfterSave(global::umbraco.cms.businesslogic.media.Media sender, SaveEventArgs e) + { + //ensure that only the providers are flagged to listen execute + IndexMedia(sender); + } + + [SecuritySafeCritical] + private static void IndexMedia(global::umbraco.cms.businesslogic.media.Media sender) + { + ExamineManager.Instance.ReIndexNode(ToXDocument(sender, true).Root, IndexTypes.Media, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => x.EnableDefaultEventHandler)); + } + + /// + /// When media is moved, re-index + /// + /// + /// + [SecuritySafeCritical] + private static void MediaAfterMove(object sender, MoveEventArgs e) + { + var media = sender as global::umbraco.cms.businesslogic.media.Media; + if (media == null) return; + global::umbraco.cms.businesslogic.media.Media m = media; + IndexMedia(m); + } + + /// + /// Only Update indexes for providers that dont SupportUnpublishedContent + /// + /// + /// + [SecuritySafeCritical] + private static void ContentAfterUpdateDocumentCache(Document sender, DocumentCacheEventArgs e) + { + //ensure that only the providers that have DONT unpublishing support enabled + //that are also flagged to listen + ExamineManager.Instance.ReIndexNode(ToXDocument(sender, true).Root, IndexTypes.Content, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => !x.SupportUnpublishedContent + && x.EnableDefaultEventHandler)); + } + + /// + /// Only update indexes for providers that don't SupportUnpublishedContnet + /// + /// + /// + [SecuritySafeCritical] + private static void ContentAfterClearDocumentCache(Document sender, DocumentCacheEventArgs e) + { + var nodeId = sender.Id.ToString(); + //ensure that only the providers that DONT have unpublishing support enabled + //that are also flagged to listen + ExamineManager.Instance.DeleteFromIndex(nodeId, + ExamineManager.Instance.IndexProviderCollection.OfType() + .Where(x => !x.SupportUnpublishedContent + && 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 + /// + /// + /// + [SecuritySafeCritical] + private static void IndexerDocumentWriting(object sender, DocumentWritingEventArgs e) + { + if (e.Fields.Keys.Contains("nodeName")) + { + //add the lower cased version + e.Document.Add(new Field("__nodeName", + e.Fields["nodeName"].ToLower(), + Field.Store.YES, + Field.Index.ANALYZED, + Field.TermVector.NO + )); + } + } + + + /// + /// Converts a content node to XDocument + /// + /// + /// true if data is going to be returned from cache + /// + /// + /// If the type of node is not a Document, the cacheOnly has no effect, it will use the API to return + /// the xml. + /// + [SecuritySafeCritical] + public static XDocument ToXDocument(Content node, bool cacheOnly) + { + if (cacheOnly && node.GetType().Equals(typeof(Document))) + { + var umbXml = library.GetXmlNodeById(node.Id.ToString()); + if (umbXml != null) + { + return umbXml.ToXDocument(); + } + } + + //this will also occur if umbraco hasn't cached content yet.... + + //if it's not a using cache and it's not cacheOnly, then retrieve the Xml using the API + return ToXDocument(node); + } + + /// + /// Converts a content node to Xml + /// + /// + /// + [SecuritySafeCritical] + private static XDocument ToXDocument(Content node) + { + var xDoc = new XmlDocument(); + var xNode = xDoc.CreateNode(XmlNodeType.Element, "node", ""); + node.XmlPopulate(xDoc, ref xNode, false); + + if (xNode.Attributes["nodeTypeAlias"] == null) + { + //we'll add the nodeTypeAlias ourselves + XmlAttribute d = xDoc.CreateAttribute("nodeTypeAlias"); + d.Value = node.ContentType.Alias; + xNode.Attributes.Append(d); + } + + return new XDocument(xNode.ToXElement()); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8f828ade1f..9075641fa2 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -89,6 +89,10 @@ AllRules.ruleset + + {07fbc26b-2927-4a22-8d96-d644c667fecc} + UmbracoExamine + ..\packages\ClientDependency.1.5.1.0\lib\ClientDependency.Core.dll @@ -193,10 +197,6 @@ System.XML - - False - ..\packages\UmbracoExamine.0.1.42.2941\lib\UmbracoExamine.dll - {5BA5425F-27A7-4677-865E-82246498AA2E} SqlCE4Umbraco @@ -329,6 +329,7 @@ + diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 7924040d73..3b71cb6d65 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -12,7 +12,6 @@ - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs index 1d30d3eb88..9e55a5d14f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Search/ExamineEvents.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Web; using umbraco.BusinessLogic; using Examine; -using UmbracoExamine; using Lucene.Net.Documents; using umbraco.businesslogic; using umbraco.interfaces; @@ -14,43 +13,8 @@ namespace umbraco.presentation.umbraco.Search /// /// Used to wire up events for Examine /// + [Obsolete("This class has been superceded by Umbraco.Web.Search.ExamineEvents. This class is no longer used and will be removed from the codebase.")] public class ExamineEvents : IApplicationStartupHandler - { - - public ExamineEvents() - : base() - { - var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer; - if (contentIndexer != null) - { - contentIndexer.DocumentWriting += new EventHandler(indexer_DocumentWriting); - } - var memberIndexer = ExamineManager.Instance.IndexProviderCollection["InternalMemberIndexer"] as UmbracoMemberIndexer; - if (memberIndexer != null) - { - memberIndexer.DocumentWriting += new EventHandler(indexer_DocumentWriting); - } - } - - /// - /// 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 - /// - /// - /// - void indexer_DocumentWriting(object sender, Examine.LuceneEngine.DocumentWritingEventArgs e) - { - if (e.Fields.Keys.Contains("nodeName")) - { - //add the lower cased version - e.Document.Add(new Field("__nodeName", - e.Fields["nodeName"].ToLower(), - Field.Store.YES, - Field.Index.ANALYZED, - Field.TermVector.NO - )); - } - } - + { } } \ No newline at end of file diff --git a/src/UmbracoExamine/ContentExtensions.cs b/src/UmbracoExamine/ContentExtensions.cs index b2d82375ed..541831c4cb 100644 --- a/src/UmbracoExamine/ContentExtensions.cs +++ b/src/UmbracoExamine/ContentExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Security; using System.Xml; @@ -28,11 +29,12 @@ namespace UmbracoExamine /// the xml. /// [SecuritySafeCritical] + [Obsolete("This method is no longer used and will be removed in future versions")] public static XDocument ToXDocument(this Content node, bool cacheOnly) { if (cacheOnly && node.GetType().Equals(typeof(Document))) { - var umbXml = library.GetXmlNodeById(node.Id.ToString()); + var umbXml = LegacyLibrary.GetXmlNodeById(node.Id.ToString()); if (umbXml != null) { return umbXml.ToXDocument(); @@ -50,7 +52,8 @@ namespace UmbracoExamine /// /// /// - [SecuritySafeCritical] + [SecuritySafeCritical] + [Obsolete("This method is no longer used and will be removed in future versions")] private static XDocument ToXDocument(this Content node) { var xDoc = new XmlDocument(); diff --git a/src/UmbracoExamine/DataServices/ILogService.cs b/src/UmbracoExamine/DataServices/ILogService.cs index fb0e9dcc5d..83c6a77029 100644 --- a/src/UmbracoExamine/DataServices/ILogService.cs +++ b/src/UmbracoExamine/DataServices/ILogService.cs @@ -7,6 +7,8 @@ namespace UmbracoExamine.DataServices void AddErrorLog(int nodeId, string msg); void AddInfoLog(int nodeId, string msg); void AddVerboseLog(int nodeId, string msg); - LoggingLevel LogLevel { get; set; } + + [Obsolete("This value is no longer used since we support the log levels that are available with LogHelper")] + LoggingLevel LogLevel { get; set; } } } diff --git a/src/UmbracoExamine/DataServices/UmbracoContentService.cs b/src/UmbracoExamine/DataServices/UmbracoContentService.cs index 6ce1b3695e..de84f02047 100644 --- a/src/UmbracoExamine/DataServices/UmbracoContentService.cs +++ b/src/UmbracoExamine/DataServices/UmbracoContentService.cs @@ -3,6 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Security; using System.Text; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Services; using umbraco; using System.Xml.Linq; using System.Xml; @@ -18,9 +22,22 @@ using System.Diagnostics; namespace UmbracoExamine.DataServices { - public class UmbracoContentService : UmbracoExamine.DataServices.IContentService + public class UmbracoContentService : IContentService { + private readonly ServiceContext _services; + + public UmbracoContentService() + : this(ApplicationContext.Current.Services) + { + + } + + public UmbracoContentService(ServiceContext services) + { + _services = services; + } + /// /// removes html markup from a string /// @@ -29,7 +46,7 @@ namespace UmbracoExamine.DataServices [SecuritySafeCritical] public string StripHtml(string value) { - return library.StripHtml(value); + return value.StripHtml(); } /// @@ -40,7 +57,10 @@ namespace UmbracoExamine.DataServices [SecuritySafeCritical] public XDocument GetPublishedContentByXPath(string xpath) { - return library.GetXmlNodeByXPath(xpath).ToXDocument(); + //TODO: Remove the need for this, the best way would be to remove all requirements of examine based on Xml but that + // would take some time. Another way in the in-term would be to add a static delegate to this class which can be set + // on the WebBootManager to set how to get the XmlNodeByXPath but that is still ugly :( + return LegacyLibrary.GetXmlNodeByXPath(xpath).ToXDocument(); } /// @@ -52,25 +72,11 @@ namespace UmbracoExamine.DataServices /// [SecuritySafeCritical] public XDocument GetLatestContentByXPath(string xpath) - { - - var rootContent = Document.GetRootDocuments(); + { var xmlContent = XDocument.Parse(""); - var xDoc = new XmlDocument(); - foreach (var c in rootContent) + foreach (var c in _services.ContentService.GetRootContent()) { - var xNode = xDoc.CreateNode(XmlNodeType.Element, "node", ""); - c.XmlPopulate(xDoc, ref xNode, true); - - if (xNode.Attributes["nodeTypeAlias"] == null) - { - //we'll add the nodeTypeAlias ourselves - XmlAttribute d = xDoc.CreateAttribute("nodeTypeAlias"); - d.Value = c.ContentType.Alias; - xNode.Attributes.Append(d); - } - - xmlContent.Root.Add(xNode.ToXElement()); + xmlContent.Root.Add(c.ToXml()); } var result = ((IEnumerable)xmlContent.XPathEvaluate(xpath)).Cast(); return result.ToXDocument(); @@ -84,9 +90,9 @@ namespace UmbracoExamine.DataServices /// /// [SecuritySafeCritical] - private XmlNode GetPage(int documentId) + private static XmlNode GetPage(int documentId) { - XmlNode x = Access.AccessXml.SelectSingleNode("/access/page [@id=" + documentId.ToString() + "]"); + var x = Access.AccessXml.SelectSingleNode("/access/page [@id=" + documentId.ToString() + "]"); return x; } @@ -100,24 +106,17 @@ namespace UmbracoExamine.DataServices /// public bool IsProtected(int nodeId, string path) { - foreach (string id in path.Split(',')) - { - if (GetPage(int.Parse(id)) != null) - { - return true; - } - } - return false; + return path.Split(',').Any(id => GetPage(int.Parse(id)) != null); } - /// + /// /// Returns a list of all of the user defined property names in Umbraco /// /// [SecuritySafeCritical] public IEnumerable GetAllUserPropertyNames() { - //this is how umb codebase 4.0 does this... booo, should be in the data layer, will fix in 4.1 + //TODO: this is how umb codebase 4.0 does this... convert to new data layer var aliases = new List(); var fieldSql = "select distinct alias from cmsPropertyType order by alias"; @@ -139,12 +138,11 @@ namespace UmbracoExamine.DataServices //we don't want to crash the app because of this so we'll actually swallow this //exception... Unfortunately logging probably won't work in this situation either :( - Debug.WriteLine("EXCEPTION OCCURRED reading GetAllUserPropertyNames: " + ex.Message, "Error"); - Trace.WriteLine("EXCEPTION OCCURRED reading GetAllUserPropertyNames: " + ex.Message, "Error"); + LogHelper.Error("EXCEPTION OCCURRED reading GetAllUserPropertyNames", ex); } else { - throw ex; + throw; } } diff --git a/src/UmbracoExamine/DataServices/UmbracoLogService.cs b/src/UmbracoExamine/DataServices/UmbracoLogService.cs index d63af55fcf..7589e4ec2f 100644 --- a/src/UmbracoExamine/DataServices/UmbracoLogService.cs +++ b/src/UmbracoExamine/DataServices/UmbracoLogService.cs @@ -3,33 +3,38 @@ using System.Collections.Generic; using System.Linq; using System.Security; using System.Text; +using Umbraco.Core.Logging; using umbraco.BusinessLogic; namespace UmbracoExamine.DataServices { - public class UmbracoLogService : UmbracoExamine.DataServices.ILogService + public class UmbracoLogService : ILogService { public string ProviderName { get; set; } [SecuritySafeCritical] public void AddInfoLog(int nodeId, string msg) - { - Log.Add(LogTypes.Custom, nodeId, "[UmbracoExamine] (" + ProviderName + ")" + msg); + { + LogHelper.Info("{0}, Provider={1}, NodeId={2}", () => msg, () => ProviderName, () => nodeId); } [SecuritySafeCritical] public void AddErrorLog(int nodeId, string msg) - { - Log.Add(LogTypes.Error, nodeId, "[UmbracoExamine] (" + ProviderName + ")" + msg); + { + //NOTE: not really the prettiest but since AddErrorLog is legacy code, we cannot change it now to accept a real Exception obj for + // use with the new LogHelper + LogHelper.Error( + string.Format("Provider={0}, NodeId={1}", ProviderName, nodeId), + new Exception(msg)); } [SecuritySafeCritical] public void AddVerboseLog(int nodeId, string msg) { - if (LogLevel == LoggingLevel.Verbose) - Log.Add(LogTypes.Custom, nodeId, "[UmbracoExamine] (" + ProviderName + ")" + msg); + LogHelper.Debug("{0}, Provider={1}, NodeId={2}", () => msg, () => ProviderName, () => nodeId); } + [Obsolete("This value is no longer used since we support the log levels that are available with LogHelper")] public LoggingLevel LogLevel { get; set; } } diff --git a/src/UmbracoExamine/DataServices/UmbracoMediaService.cs b/src/UmbracoExamine/DataServices/UmbracoMediaService.cs index 0daed53207..a324903c41 100644 --- a/src/UmbracoExamine/DataServices/UmbracoMediaService.cs +++ b/src/UmbracoExamine/DataServices/UmbracoMediaService.cs @@ -1,13 +1,13 @@ using System; -using System.Collections.Generic; +using System.Collections; using System.Linq; using System.Security; -using System.Text; -using System.Xml.XPath; using System.Xml.Linq; -using umbraco.cms.businesslogic.media; -using System.Collections; +using System.Xml.XPath; using Examine.LuceneEngine; +using Umbraco.Core; +using Umbraco.Core.Services; +using Umbraco.Core.Models; namespace UmbracoExamine.DataServices { @@ -15,37 +15,38 @@ namespace UmbracoExamine.DataServices /// /// Data service used to query for media /// - public class UmbracoMediaService : UmbracoExamine.DataServices.IMediaService - { + public class UmbracoMediaService : IMediaService + { + private readonly ServiceContext _services; - /// - /// This is quite an intensive operation... - /// get all root media, then get the XML structure for all children, - /// then run xpath against the navigator that's created - /// - /// - /// + public UmbracoMediaService() + : this(ApplicationContext.Current.Services) + { + + } + + public UmbracoMediaService(ServiceContext services) + { + _services = services; + } + + /// + /// This is quite an intensive operation... + /// get all root media, then get the XML structure for all children, + /// then run xpath against the navigator that's created + /// + /// + /// [SecuritySafeCritical] public XDocument GetLatestMediaByXpath(string xpath) - { - - Media[] rootMedia = Media.GetRootMedias(); - var xmlMedia = XDocument.Parse(""); - foreach (Media media in rootMedia) - { - xmlMedia.Root.Add(GetMediaItem(media.Id)); - } - var result = ((IEnumerable)xmlMedia.XPathEvaluate(xpath)).Cast(); - return result.ToXDocument(); - } - - [SecuritySafeCritical] - private XElement GetMediaItem(int nodeId) - { - var nodes = umbraco.library.GetMedia(nodeId, true); - return XElement.Parse(nodes.Current.OuterXml); - } - - - } + { + var xmlMedia = XDocument.Parse(""); + foreach (var m in _services.MediaService.GetRootMedia()) + { + xmlMedia.Root.Add(m.ToXml()); + } + var result = ((IEnumerable)xmlMedia.XPathEvaluate(xpath)).Cast(); + return result.ToXDocument(); + } + } } diff --git a/src/UmbracoExamine/LegacyLibrary.cs b/src/UmbracoExamine/LegacyLibrary.cs new file mode 100644 index 0000000000..651c57b00e --- /dev/null +++ b/src/UmbracoExamine/LegacyLibrary.cs @@ -0,0 +1,58 @@ +using System; +using System.Reflection; +using System.Xml.XPath; + +namespace UmbracoExamine +{ + /// + /// This is only used for backward compatibility to get access to the umbraco.library object but this needs to be done + /// via reflection because of the circular reference we have between Umbraco.Web and UmbracoExamine. + /// + internal static class LegacyLibrary + { + private static volatile Type _libraryType; + private static readonly object Locker = new object(); + private static Type LibraryType + { + get + { + if (_libraryType == null) + { + lock (Locker) + { + if (_libraryType == null) + { + var ass = Assembly.Load("umbraco"); + if (ass == null) + throw new InvalidOperationException("Could not load assembly umbraco.dll, the umbraco.dll needs to be loaded in the current app domain"); + var lib = ass.GetType("umbraco.library"); + if (lib == null) + throw new InvalidOperationException("Could not load type umbraco.library, the umbraco.dll needs to be loaded in the current app domain"); + _libraryType = lib; + } + } + } + return _libraryType; + } + } + + internal static XPathNodeIterator GetXmlNodeById(string id) + { + var meth = LibraryType.GetMethod("GetXmlNodeById", BindingFlags.Public | BindingFlags.Static); + return (XPathNodeIterator)meth.Invoke(null, new object[] { id }); + } + + internal static XPathNodeIterator GetMember(int id) + { + var meth = LibraryType.GetMethod("GetMember", BindingFlags.Public | BindingFlags.Static); + return (XPathNodeIterator)meth.Invoke(null, new object[] { id }); + } + + internal static XPathNodeIterator GetXmlNodeByXPath(string xpathQuery) + { + var meth = LibraryType.GetMethod("GetXmlNodeByXPath", BindingFlags.Public | BindingFlags.Static); + return (XPathNodeIterator)meth.Invoke(null, new object[] { xpathQuery }); + } + + } +} \ No newline at end of file diff --git a/src/UmbracoExamine/LoggingLevel.cs b/src/UmbracoExamine/LoggingLevel.cs index 36ece649af..efa87c194d 100644 --- a/src/UmbracoExamine/LoggingLevel.cs +++ b/src/UmbracoExamine/LoggingLevel.cs @@ -5,6 +5,7 @@ using System.Text; namespace UmbracoExamine { + [Obsolete("This object is no longer used since we support the log levels that are available with LogHelper")] public enum LoggingLevel { Verbose, Normal diff --git a/src/UmbracoExamine/UmbracoEventManager.cs b/src/UmbracoExamine/UmbracoEventManager.cs index cebb837670..7a8936d2ca 100644 --- a/src/UmbracoExamine/UmbracoEventManager.cs +++ b/src/UmbracoExamine/UmbracoEventManager.cs @@ -23,201 +23,8 @@ namespace UmbracoExamine /// /// An instance for wiring up Examine to the Umbraco events system /// + [Obsolete("This class is no longer used and will be removed from the codebase in upcoming versions")] public class UmbracoEventManager : ApplicationBase - { - /// - /// Creates a new instance of the class - /// - [SecuritySafeCritical] - public UmbracoEventManager() - { - var registeredProviders = ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler) - .Count(); - - Log.Add(LogTypes.Custom, -1, "[UmbracoExamine] Adding examine event handlers for index providers: " + registeredProviders.ToString()); - - //don't bind event handlers if we're not suppose to listen - if (registeredProviders == 0) - return; - - Media.AfterSave += Media_AfterSave; - Media.AfterDelete += Media_AfterDelete; - CMSNode.AfterMove += Media_AfterMove; - - //These should only fire for providers that DONT have SupportUnpublishedContent set to true - content.AfterUpdateDocumentCache += content_AfterUpdateDocumentCache; - content.AfterClearDocumentCache += content_AfterClearDocumentCache; - - //These should only fire for providers that have SupportUnpublishedContent set to true - Document.AfterSave += Document_AfterSave; - Document.AfterDelete += Document_AfterDelete; - - Member.AfterSave += Member_AfterSave; - Member.AfterDelete += Member_AfterDelete; - } - - //This does work, however we need to lock down the httphandler and thats an issue... so i've removed this - //if people don't want to rebuild on app startup then they can disable it and reindex manually. - - [SecuritySafeCritical] - private void Member_AfterSave(Member sender, SaveEventArgs e) - { - //ensure that only the providers are flagged to listen execute - var xml = sender.ToXml(new System.Xml.XmlDocument(), false).ToXElement(); - var providers = ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler); - ExamineManager.Instance.ReIndexNode(xml, IndexTypes.Member, providers); - } - - [SecuritySafeCritical] - private void Member_AfterDelete(Member sender, DeleteEventArgs e) - { - var nodeId = sender.Id.ToString(); - - //ensure that only the providers are flagged to listen execute - ExamineManager.Instance.DeleteFromIndex(nodeId, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler)); - } - - /// - /// Only index using providers that SupportUnpublishedContent - /// - /// - /// - [SecuritySafeCritical] - private void Document_AfterSave(Document sender, SaveEventArgs e) - { - //ensure that only the providers that have unpublishing support enabled - //that are also flagged to listen - - //there's a bug in 4.0.x that fires the Document saving event handler for media when media is moved, - //therefore, we need to try to figure out if this is media or content. Currently one way to do this - //is by checking the creator ID property as it will be null if it is media. We then need to try to - //create the media object, see if it exists, and pass it to the media save event handler... yeah i know, - //pretty horrible but has to be done. - - try - { - var creator = sender.Creator; - if (creator != null) - { - //it's def a Document - ExamineManager.Instance.ReIndexNode(sender.ToXDocument(false).Root, IndexTypes.Content, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.SupportUnpublishedContent - && x.EnableDefaultEventHandler)); - - return; //exit, we've indexed the content - } - } - catch (Exception) - { - //if we get this exception, it means it's most likely media, so well do our check next. - } - - //this is most likely media, not sure what kind of exception might get thrown in 4.0.x or 4.1 if accessing a null - //creator, so we catch everything. - - var m = new Media(sender.Id); - if (!string.IsNullOrEmpty(m.Path)) - { - //this is a media item, no exception occurred on access to path and it's not empty which means it was found - Media_AfterSave(m, e); - return; - } - - - } - - /// - /// Only remove indexes using providers that SupportUnpublishedContent - /// - /// - /// - [SecuritySafeCritical] - private void Document_AfterDelete(Document sender, DeleteEventArgs e) - { - var nodeId = sender.Id.ToString(); - - //ensure that only the providers that have unpublishing support enabled - //that are also flagged to listen - ExamineManager.Instance.DeleteFromIndex(nodeId, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.SupportUnpublishedContent - && x.EnableDefaultEventHandler)); - } - - [SecuritySafeCritical] - private void Media_AfterDelete(Media sender, DeleteEventArgs e) - { - var nodeId = sender.Id.ToString(); - - //ensure that only the providers are flagged to listen execute - ExamineManager.Instance.DeleteFromIndex(nodeId, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler)); - } - - private void Media_AfterSave(Media sender, umbraco.cms.businesslogic.SaveEventArgs e) - { - //ensure that only the providers are flagged to listen execute - IndexMedia(sender); - } - - private void IndexMedia(Media sender) - { - ExamineManager.Instance.ReIndexNode(sender.ToXDocument(true).Root, IndexTypes.Media, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => x.EnableDefaultEventHandler)); - } - - /// - /// When media is moved, re-index - /// - /// - /// - private void Media_AfterMove(object sender, MoveEventArgs e) - { - if (sender is Media) - { - Media m = (Media)sender; - IndexMedia(m); - } - } - - /// - /// Only Update indexes for providers that dont SupportUnpublishedContent - /// - /// - /// - private void content_AfterUpdateDocumentCache(Document sender, umbraco.cms.businesslogic.DocumentCacheEventArgs e) - { - //ensure that only the providers that have DONT unpublishing support enabled - //that are also flagged to listen - ExamineManager.Instance.ReIndexNode(sender.ToXDocument(true).Root, IndexTypes.Content, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => !x.SupportUnpublishedContent - && x.EnableDefaultEventHandler)); - } - - /// - /// Only update indexes for providers that don't SupportUnpublishedContnet - /// - /// - /// - [SecuritySafeCritical] - private void content_AfterClearDocumentCache(Document sender, DocumentCacheEventArgs e) - { - var nodeId = sender.Id.ToString(); - //ensure that only the providers that DONT have unpublishing support enabled - //that are also flagged to listen - ExamineManager.Instance.DeleteFromIndex(nodeId, - ExamineManager.Instance.IndexProviderCollection.OfType() - .Where(x => !x.SupportUnpublishedContent - && x.EnableDefaultEventHandler)); - } - + { } } diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index eb0fe327ca..9a2a57e4f6 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -116,6 +116,7 @@ + @@ -123,7 +124,6 @@ - @@ -152,6 +152,10 @@ {ccd75ec3-63db-4184-b49d-51c1dd337230} umbraco.cms + + {31785bc3-256c-4613-b2f5-a1b0bdded8c1} + Umbraco.Core + {c7cb79f0-1c97-4b33-bfa7-00731b579ae2} umbraco.datalayer @@ -160,10 +164,6 @@ {511f6d8d-7717-440a-9a57-a507e9a8b27f} umbraco.interfaces - - {651e1350-91b6-44b7-bd60-7207006d7003} - Umbraco.Web - diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index 94a82b9818..fc5c26486b 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -13,7 +13,7 @@ using Lucene.Net.Analysis; namespace UmbracoExamine { - /// + /// /// /// public class UmbracoMemberIndexer : UmbracoContentIndexer @@ -65,7 +65,7 @@ namespace UmbracoExamine return null; } - protected override System.Collections.Generic.Dictionary GetDataToIndex(XElement node, string type) + protected override Dictionary GetDataToIndex(XElement node, string type) { var data = base.GetDataToIndex(node, type); @@ -78,9 +78,10 @@ namespace UmbracoExamine } [SecuritySafeCritical] - private XElement GetMemberItem(int nodeId) + private static XElement GetMemberItem(int nodeId) { - var nodes = umbraco.library.GetMember(nodeId); + //TODO: Change this so that it is not using the LegacyLibrary, just serialize manually! + var nodes = LegacyLibrary.GetMember(nodeId); return XElement.Parse(nodes.Current.OuterXml); } } diff --git a/src/umbraco.MacroEngines/packages.config b/src/umbraco.MacroEngines/packages.config index af909515d2..d604508ba4 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -8,5 +8,4 @@ - \ No newline at end of file diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index f1932c9c8e..73fd20b690 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -93,10 +93,6 @@ - - False - ..\packages\UmbracoExamine.0.1.42.2941\lib\UmbracoExamine.dll - @@ -190,6 +186,10 @@ {651E1350-91B6-44B7-BD60-7207006D7003} Umbraco.Web + + {07fbc26b-2927-4a22-8d96-d644c667fecc} + UmbracoExamine + diff --git a/src/umbraco.sln b/src/umbraco.sln index 16421e9f43..aff76eec87 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -61,7 +61,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{66AB04 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine", "UmbracoExamine\UmbracoExamine.csproj", "{07FBC26B-2927-4A22-8D96-D644C667FECC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UmbracoExamineLibs", "UmbracoExamineLibs", "{DD32977B-EF54-475B-9A1B-B97A502C6E58}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UmbracoExamine Libraries", "UmbracoExamine Libraries", "{DD32977B-EF54-475B-9A1B-B97A502C6E58}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine.PDF", "UmbracoExamine.PDF\UmbracoExamine.PDF.csproj", "{F30DDDB8-3994-4673-82AE-057123C6E1A8}" EndProject