281 lines
11 KiB
C#
281 lines
11 KiB
C#
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 Umbraco.Core.Models;
|
|
using Umbraco.Core.Services;
|
|
using UmbracoExamine;
|
|
using umbraco;
|
|
using umbraco.BusinessLogic;
|
|
using umbraco.cms.businesslogic;
|
|
using umbraco.cms.businesslogic.member;
|
|
using umbraco.interfaces;
|
|
using Content = umbraco.cms.businesslogic.Content;
|
|
using Document = umbraco.cms.businesslogic.web.Document;
|
|
|
|
namespace Umbraco.Web.Search
|
|
{
|
|
/// <summary>
|
|
/// Used to wire up events for Examine
|
|
/// </summary>
|
|
public sealed class ExamineEvents : ApplicationEventHandler
|
|
{
|
|
|
|
/// <summary>
|
|
/// Once the application has started we should bind to all events and initialize the providers.
|
|
/// </summary>
|
|
/// <param name="httpApplication"></param>
|
|
/// <param name="applicationContext"></param>
|
|
/// <remarks>
|
|
/// We need to do this on the Started event as to guarantee that all resolvers are setup properly.
|
|
/// </remarks>
|
|
[SecuritySafeCritical]
|
|
protected override void ApplicationStarted(UmbracoApplicationBase httpApplication, ApplicationContext applicationContext)
|
|
{
|
|
LogHelper.Info<ExamineEvents>("Initializing Examine and binding to business logic events");
|
|
|
|
var registeredProviders = ExamineManager.Instance.IndexProviderCollection
|
|
.OfType<BaseUmbracoIndexer>().Count(x => x.EnableDefaultEventHandler);
|
|
|
|
LogHelper.Info<ExamineEvents>("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;
|
|
|
|
MediaService.Saved += MediaServiceSaved;
|
|
MediaService.Deleted += MediaServiceDeleted;
|
|
MediaService.Moved += MediaServiceMoved;
|
|
MediaService.Trashed += MediaServiceTrashed;
|
|
ContentService.Saved += ContentServiceSaved;
|
|
ContentService.Deleted += ContentServiceDeleted;
|
|
ContentService.Moved += ContentServiceMoved;
|
|
ContentService.Trashed += ContentServiceTrashed;
|
|
|
|
//These should only fire for providers that DONT have SupportUnpublishedContent set to true
|
|
content.AfterUpdateDocumentCache += ContentAfterUpdateDocumentCache;
|
|
content.AfterClearDocumentCache += ContentAfterClearDocumentCache;
|
|
|
|
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]
|
|
static void ContentServiceTrashed(IContentService sender, Core.Events.MoveEventArgs<IContent> e)
|
|
{
|
|
IndexConent(e.Entity);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void MediaServiceTrashed(IMediaService sender, Core.Events.MoveEventArgs<IMedia> e)
|
|
{
|
|
IndexMedia(e.Entity);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void ContentServiceMoved(IContentService sender, Umbraco.Core.Events.MoveEventArgs<IContent> e)
|
|
{
|
|
IndexConent(e.Entity);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void ContentServiceDeleted(IContentService sender, Umbraco.Core.Events.DeleteEventArgs<IContent> e)
|
|
{
|
|
e.DeletedEntities.ForEach(
|
|
content =>
|
|
ExamineManager.Instance.DeleteFromIndex(
|
|
content.Id.ToString(),
|
|
ExamineManager.Instance.IndexProviderCollection.OfType<BaseUmbracoIndexer>().Where(x => x.EnableDefaultEventHandler)));
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void ContentServiceSaved(IContentService sender, Umbraco.Core.Events.SaveEventArgs<IContent> e)
|
|
{
|
|
e.SavedEntities.ForEach(IndexConent);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void MediaServiceMoved(IMediaService sender, Umbraco.Core.Events.MoveEventArgs<IMedia> e)
|
|
{
|
|
IndexMedia(e.Entity);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void MediaServiceDeleted(IMediaService sender, Umbraco.Core.Events.DeleteEventArgs<IMedia> e)
|
|
{
|
|
e.DeletedEntities.ForEach(
|
|
media =>
|
|
ExamineManager.Instance.DeleteFromIndex(
|
|
media.Id.ToString(),
|
|
ExamineManager.Instance.IndexProviderCollection.OfType<BaseUmbracoIndexer>().Where(x => x.EnableDefaultEventHandler)));
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
static void MediaServiceSaved(IMediaService sender, Umbraco.Core.Events.SaveEventArgs<IMedia> e)
|
|
{
|
|
e.SavedEntities.ForEach(IndexMedia);
|
|
}
|
|
|
|
[SecuritySafeCritical]
|
|
private static void MemberAfterSave(Member sender, SaveEventArgs e)
|
|
{
|
|
//ensure that only the providers are flagged to listen execute
|
|
var xml = ExamineXmlExtensions.ToXElement(sender.ToXml(new System.Xml.XmlDocument(), false));
|
|
var providers = ExamineManager.Instance.IndexProviderCollection.OfType<BaseUmbracoIndexer>()
|
|
.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<BaseUmbracoIndexer>()
|
|
.Where(x => x.EnableDefaultEventHandler));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only Update indexes for providers that dont SupportUnpublishedContent
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
[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<BaseUmbracoIndexer>()
|
|
.Where(x => !x.SupportUnpublishedContent
|
|
&& x.EnableDefaultEventHandler));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only update indexes for providers that don't SupportUnpublishedContnet
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
[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<BaseUmbracoIndexer>()
|
|
.Where(x => !x.SupportUnpublishedContent
|
|
&& x.EnableDefaultEventHandler));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
[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
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
private static void IndexMedia(IMedia sender)
|
|
{
|
|
ExamineManager.Instance.ReIndexNode(
|
|
sender.ToXml(), "media",
|
|
ExamineManager.Instance.IndexProviderCollection.OfType<BaseUmbracoIndexer>().Where(x => x.EnableDefaultEventHandler));
|
|
}
|
|
|
|
private static void IndexConent(IContent sender)
|
|
{
|
|
//only index this content if the indexer supports unpublished content. that is because the
|
|
// content.AfterUpdateDocumentCache will handle anything being published and will only index against indexers
|
|
// that only support published content.
|
|
// NOTE: The events for publishing have changed slightly from 6.0 to 6.1 and are streamlined in 6.1. Before
|
|
// this event would fire before publishing, then again after publishing. Now the save event fires once before
|
|
// publishing and that is all.
|
|
|
|
ExamineManager.Instance.ReIndexNode(
|
|
sender.ToXml(), "content",
|
|
ExamineManager.Instance.IndexProviderCollection.OfType<BaseUmbracoIndexer>()
|
|
.Where(x => x.SupportUnpublishedContent && x.EnableDefaultEventHandler));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a content node to XDocument
|
|
/// </summary>
|
|
/// <param name="node"></param>
|
|
/// <param name="cacheOnly">true if data is going to be returned from cache</param>
|
|
/// <returns></returns>
|
|
[SecuritySafeCritical]
|
|
[Obsolete("This method is no longer used and will be removed from the core in future versions, the cacheOnly parameter has no effect. Use the other ToXDocument overload instead")]
|
|
public static XDocument ToXDocument(Content node, bool cacheOnly)
|
|
{
|
|
return ToXDocument(node);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a content node to Xml
|
|
/// </summary>
|
|
/// <param name="node"></param>
|
|
/// <returns></returns>
|
|
[SecuritySafeCritical]
|
|
private static XDocument ToXDocument(Content node)
|
|
{
|
|
if (TypeHelper.IsTypeAssignableFrom<Document>(node))
|
|
{
|
|
return new XDocument(((Document) node).Content.ToXml());
|
|
}
|
|
|
|
if (TypeHelper.IsTypeAssignableFrom<global::umbraco.cms.businesslogic.media.Media>(node))
|
|
{
|
|
return new XDocument(((global::umbraco.cms.businesslogic.media.Media) node).MediaItem.ToXml());
|
|
}
|
|
|
|
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(ExamineXmlExtensions.ToXElement(xNode));
|
|
}
|
|
|
|
}
|
|
} |