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