From 0cd115c268850c45285ab839c926125953ad6100 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 03:32:50 +0500 Subject: [PATCH 01/21] Fixees: #U4-1166, automatically adds parsable routes in the route table to the reserved paths so you don't have to explicitly add reserved paths to Umbraco config that you are creating routes for. --- .../Configuration/GlobalSettings.cs | 18 +++- src/Umbraco.Core/RouteParser.cs | 79 +++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/RouteParserTests.cs | 96 +++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 5 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Core/RouteParser.cs create mode 100644 src/Umbraco.Tests/RouteParserTests.cs diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index bc16642c23..0ad32fc4d2 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -4,6 +4,7 @@ using System.Configuration; using System.Linq; using System.Web; using System.Web.Configuration; +using System.Web.Routing; using System.Xml; using System.Xml.Linq; using Umbraco.Core.IO; @@ -57,9 +58,20 @@ namespace Umbraco.Core.Configuration { get { - return ConfigurationManager.AppSettings.ContainsKey("umbracoReservedPaths") - ? ConfigurationManager.AppSettings["umbracoReservedPaths"] - : string.Empty; + //first we need to get the parsed base path VirtualPaths in the RouteTable. + //NOTE: for performance we would normally store this result in a variable, however since this class is static + // we cannot gaurantee that all of the routes will be added to the table before this method is called so + // unfortunately, we are stuck resolving these every single time. I don't think the performance will be bad + // but it could be better. Need to fix once we fix up all the configuration classes. + var parser = new RouteParser(RouteTable.Routes); + var reservedFromRouteTable = string.Join(",", parser.ParsedVirtualUrlsFromRouteTable()); + + var config = ConfigurationManager.AppSettings.ContainsKey("umbracoReservedPaths") + ? ConfigurationManager.AppSettings["umbracoReservedPaths"] + : string.Empty; + + //now add the reserved paths + return config.IsNullOrWhiteSpace() ? reservedFromRouteTable : config + "," + reservedFromRouteTable; } } diff --git a/src/Umbraco.Core/RouteParser.cs b/src/Umbraco.Core/RouteParser.cs new file mode 100644 index 0000000000..78696e66c9 --- /dev/null +++ b/src/Umbraco.Core/RouteParser.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Web.Routing; + +namespace Umbraco.Core +{ + /// + /// A class that is used to parse routes in the route table to determine which route paths to ignore in the GlobalSettings. + /// + /// + /// The parser is not that intelligent, it will not work for very complex URL structures. It will only support simple URL structures that + /// have consecutive tokens. + /// + /// + /// Example routes that will parse: + /// + /// /Test/{controller}/{action}/{id} + /// /Test/MyController/{action}/{id} + /// + /// + internal class RouteParser + { + private readonly RouteCollection _routes; + private readonly Regex _matchAction = new Regex(@"\{action\}", RegexOptions.Compiled); + private readonly Regex _matchController = new Regex(@"\{controller\}", RegexOptions.Compiled); + private readonly Regex _matchId = new Regex(@"\{id\}", RegexOptions.Compiled); + + public RouteParser(RouteCollection routes) + { + _routes = routes; + } + + /// + /// Returned all successfully parsed virtual urls from the route collection + /// + /// + public IEnumerable ParsedVirtualUrlsFromRouteTable() + { + return _routes.OfType() + .Select(TryGetBaseVirtualPath) + .Where(x => x.Success) + .Select(x => x.Result); + } + + internal Attempt TryGetBaseVirtualPath(Route route) + { + var url = route.Url; + var controllers = _matchController.Matches(url); + var actions = _matchAction.Matches(url); + var ids = _matchId.Matches(url); + if (controllers.Count > 1) + return new Attempt(new InvalidOperationException("The URL cannot be parsed, it must contain zero or one {controller} tokens")); + if (actions.Count != 1) + return new Attempt(new InvalidOperationException("The URL cannot be parsed, it must contain one {action} tokens")); + if (ids.Count != 1) + return new Attempt(new InvalidOperationException("The URL cannot be parsed, it must contain one {id} tokens")); + + //now we need to validate that the order + if (controllers.Count == 1) + { + //actions must occur after controllers + if (actions[0].Index < controllers[0].Index) + return new Attempt(new InvalidOperationException("The {action} token must be placed after the {controller} token")); + } + //ids must occur after actions + if (ids[0].Index < actions[0].Index) + return new Attempt(new InvalidOperationException("The {id} token must be placed after the {action} token")); + + //this is all validated, so now we need to return the 'base' virtual path of the route + return new Attempt( + true, + "~/" + url.Substring(0, (controllers.Count == 1 ? controllers[0].Index : actions[0].Index)) + .TrimEnd('/')); + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6357255ce7..1415d981ae 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -127,6 +127,7 @@ + diff --git a/src/Umbraco.Tests/RouteParserTests.cs b/src/Umbraco.Tests/RouteParserTests.cs new file mode 100644 index 0000000000..ba161009a2 --- /dev/null +++ b/src/Umbraco.Tests/RouteParserTests.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using System.Web.Routing; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests +{ + [TestFixture] + public class RouteParserTests + { + /// + /// used for testing + /// + private class MyRoute : RouteBase + { + private readonly string _url; + + public MyRoute(string url) + { + _url = url; + } + + public override RouteData GetRouteData(HttpContextBase httpContext) + { + throw new NotImplementedException(); + } + + public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) + { + throw new NotImplementedException(); + } + } + + [Test] + public void Get_Successfully_Parsed_Urls() + { + var urls = new RouteBase[] + { + new Route("Umbraco/RenderMvc/{action}/{id}", null), + new Route("Another/Test/{controller}/{action}/{id}", null), + new MyRoute("Umbraco/RenderMvc/{action}/{id}"), //wont match because its not a route object + new Route("Another/Test/{id}/{controller}/{action}", null) //invalid + }; + var valid = new string[] + { + "~/Umbraco/RenderMvc", + "~/Another/Test" + }; + + var collection = new RouteCollection(); + foreach (var u in urls) + { + collection.Add(u); + } + var parser = new RouteParser(collection); + + var result = parser.ParsedVirtualUrlsFromRouteTable(); + + Assert.AreEqual(2, result.Count()); + Assert.IsTrue(result.ContainsAll(valid)); + } + + [TestCase("Umbraco/RenderMvc/{action}/{id}", "~/Umbraco/RenderMvc")] + [TestCase("Install/PackageInstaller/{action}/{id}", "~/Install/PackageInstaller")] + [TestCase("Test/{controller}/{action}/{id}", "~/Test")] + [TestCase("Another/Test/{controller}/{action}/{id}", "~/Another/Test")] + public void Successful_Virtual_Path(string url, string result) + { + var route = new Route(url, null); + var parser = new RouteParser(new RouteCollection()); + var attempt = parser.TryGetBaseVirtualPath(route); + if (!attempt.Success && attempt.Error != null) + throw attempt.Error; //throw this so we can see the error in the test output + + Assert.IsTrue(attempt.Success); + Assert.AreEqual(result, attempt.Result); + } + + [TestCase("Umbraco/RenderMvc/{action}/{controller}/{id}")] + [TestCase("Install/PackageInstaller/{id}/{action}")] + [TestCase("Test/{controller}/{id}/{action}")] + [TestCase("Another/Test/{id}/{controller}/{action}")] + public void Non_Parsable_Virtual_Path(string url) + { + var route = new Route(url, null); + var parser = new RouteParser(new RouteCollection()); + var attempt = parser.TryGetBaseVirtualPath(route); + Assert.IsFalse(attempt.Success); + } + + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 97fc6cbd1c..f4c6bd60c1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -77,6 +77,7 @@ + From f82ae4fddacea8e5da668eb23d960f4442922c82 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 09:13:53 +0500 Subject: [PATCH 02/21] Fixes: #U4-1174, this now works for all webforms related templates, however RenderTemplate will currently not work for MVC templates... this will come soon. If you try to it will just throw a NotImplementedException. --- .../Routing/PublishedContentRequestBuilder.cs | 1 + src/Umbraco.Web/Routing/TemplateRenderer.cs | 159 ++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../umbraco.presentation/library.cs | 109 +++--------- 4 files changed, 182 insertions(+), 88 deletions(-) create mode 100644 src/Umbraco.Web/Routing/TemplateRenderer.cs diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs index 140f0d1de5..c8157941b7 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs @@ -23,6 +23,7 @@ namespace Umbraco.Web.Routing public PublishedContentRequestBuilder(PublishedContentRequest publishedContentRequest) { + if (publishedContentRequest == null) throw new ArgumentNullException("publishedContentRequest"); _publishedContentRequest = publishedContentRequest; _umbracoContext = publishedContentRequest.RoutingContext.UmbracoContext; _routingContext = publishedContentRequest.RoutingContext; diff --git a/src/Umbraco.Web/Routing/TemplateRenderer.cs b/src/Umbraco.Web/Routing/TemplateRenderer.cs new file mode 100644 index 0000000000..0d0b4ded0f --- /dev/null +++ b/src/Umbraco.Web/Routing/TemplateRenderer.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Web; +using System.Web.Compilation; +using Umbraco.Core; +using umbraco; +using System.Linq; + +namespace Umbraco.Web.Routing +{ + /// + /// This is used purely for the RenderTemplate functionality in Umbraco + /// + /// + /// Currently the only place where you can use RenderTemplate is from within the library class (which is legacy) + /// but i guess we still need to support it. In order to do that we need to be able to render both an MVC and Webforms + /// template. So we will do a Server.Execute to execute this handler, this will then need to find put the 'id' request through + /// the routing logic again, the execute the correct handler. + /// Its is pretty round about but is required. + /// + internal class TemplateRenderer + { + private readonly UmbracoContext _umbracoContext; + private object _oldPageId; + private object _oldPageElements; + private PublishedContentRequest _oldPublishedContentRequest; + private object _oldAltTemplate; + + public TemplateRenderer(UmbracoContext umbracoContext) + { + _umbracoContext = umbracoContext; + } + + /// + /// Gets/sets the page id for the template to render + /// + public int PageId { get; set; } + + /// + /// Gets/sets the alt template to render if there is one + /// + public int? AltTemplate { get; set; } + + public IDictionary QueryStrings { get; set; } + + public void Render(StringWriter writer) + { + // instanciate a request a process + // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter + // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. + var contentRequest = new PublishedContentRequest(_umbracoContext.CleanedUmbracoUrl, _umbracoContext.RoutingContext); + + var doc = contentRequest.RoutingContext.PublishedContentStore.GetDocumentById( + contentRequest.RoutingContext.UmbracoContext, + PageId); + + if (doc == null) + { + writer.Write("", PageId); + return; + } + + //set the culture to the same as is currently rendering + contentRequest.Culture = _umbracoContext.PublishedContentRequest.Culture; + //set the doc that was found by id + contentRequest.PublishedContent = doc; + //set the template, either based on the AltTemplate found or the standard template of the doc + contentRequest.Template = !AltTemplate.HasValue + ? global::umbraco.cms.businesslogic.template.Template.GetTemplate(doc.TemplateId) + : global::umbraco.cms.businesslogic.template.Template.GetTemplate(AltTemplate.Value); + + //if there is not template then exit + if (!contentRequest.HasTemplate) + { + if (!AltTemplate.HasValue) + { + writer.Write("", doc.TemplateId); + } + else + { + writer.Write("", AltTemplate); + } + return; + } + + //ok, we have a document and a template assigned, now to do some rendering. + var builder = new PublishedContentRequestBuilder(contentRequest); + //determine the rendering engine + builder.DetermineRenderingEngine(); + + //First, save all of the items locally that we know are used in the chain of execution, we'll need to restore these + //after this page has rendered. + SaveExistingItems(); + + //set the new items on context objects for this templates execution + SetNewItemsOnContextObjects(contentRequest); + + //Render the template + ExecuteTemplateRendering(writer, contentRequest); + + //restore items on context objects to continuing rendering the parent template + RestoreItems(); + } + + private void ExecuteTemplateRendering(StringWriter sw, PublishedContentRequest contentRequest) + { + switch (contentRequest.RenderingEngine) + { + case RenderingEngine.Mvc: + throw new NotImplementedException("Currently the TemplateRender does not support rendering MVC templates"); + break; + case RenderingEngine.WebForms: + default: + //var webFormshandler = (global::umbraco.UmbracoDefault)BuildManager + // .CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault)); + var url = ("~/default.aspx?" + QueryStrings.Select(x => x.Key + "=" + x.Value + "&")).TrimEnd("&"); + _umbracoContext.HttpContext.Server.Execute(url, sw, true); + break; + } + } + + private void SetNewItemsOnContextObjects(PublishedContentRequest contentRequest) + { + // handlers like default.aspx will want it and most macros currently need it + contentRequest.UmbracoPage = new page(contentRequest); + //now, set the new ones for this page execution + _umbracoContext.HttpContext.Items["pageID"] = contentRequest.DocumentId; + _umbracoContext.HttpContext.Items["pageElements"] = contentRequest.UmbracoPage.Elements; + _umbracoContext.HttpContext.Items["altTemplate"] = null; + _umbracoContext.PublishedContentRequest = contentRequest; + } + + /// + /// Save all items that we know are used for rendering execution to variables so we can restore after rendering + /// + private void SaveExistingItems() + { + //Many objects require that these legacy items are in the http context items... before we render this template we need to first + //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal. + _oldPageId = _umbracoContext.HttpContext.Items["pageID"]; + _oldPageElements = _umbracoContext.HttpContext.Items["pageElements"]; + _oldPublishedContentRequest = _umbracoContext.PublishedContentRequest; + _oldAltTemplate = _umbracoContext.HttpContext.Items["altTemplate"]; + } + + /// + /// Restores all items back to their context's to continue normal page rendering execution + /// + private void RestoreItems() + { + _umbracoContext.PublishedContentRequest = _oldPublishedContentRequest; + _umbracoContext.HttpContext.Items["pageID"] = _oldPageId; + _umbracoContext.HttpContext.Items["pageElements"] = _oldPageElements; + _umbracoContext.HttpContext.Items["altTemplate"] = _oldAltTemplate; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ef9c4fda47..9c98dee1ef 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -312,6 +312,7 @@ + ASPXCodeBehind diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 701e689ff1..62626104f9 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -11,6 +11,7 @@ using System.Xml; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Web; +using Umbraco.Web.Routing; using umbraco.BusinessLogic; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.media; @@ -30,6 +31,7 @@ using System.Collections.Generic; using umbraco.cms.businesslogic.cache; using umbraco.NodeFactory; using UmbracoContext = umbraco.presentation.UmbracoContext; +using System.Linq; namespace umbraco { @@ -1101,36 +1103,29 @@ namespace umbraco { if (UmbracoSettings.UseAspNetMasterPages) { - if (!umbraco.presentation.UmbracoContext.Current.LiveEditingContext.Enabled) + if (!UmbracoContext.Current.LiveEditingContext.Enabled) { - System.Collections.Generic.Dictionary items = getCurrentContextItems(); - HttpContext.Current.Items["altTemplate"] = null; - - HttpContext Context = HttpContext.Current; - StringBuilder queryString = new StringBuilder(); - const string ONE_QS_PARAM = "&{0}={1}"; - foreach (object key in Context.Request.QueryString.Keys) - { - if (!key.ToString().ToLower().Equals("umbpageid") && !key.ToString().ToLower().Equals("alttemplate")) - queryString.Append(string.Format(ONE_QS_PARAM, key, Context.Request.QueryString[key.ToString()])); - } - StringWriter sw = new StringWriter(); + var context = HttpContext.Current; + var queryString = context.Request.QueryString.AllKeys + .ToDictionary(key => key, key => context.Request.QueryString[key]); + var sw = new StringWriter(); try { - Context.Server.Execute( - string.Format("~/default.aspx?umbPageID={0}&alttemplate={1}{2}", - PageId, new template(TemplateId).TemplateAlias, queryString), sw); - + var altTemplate = TemplateId == -1 ? null : (int?)TemplateId; + var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current) + { + PageId = PageId, + AltTemplate = altTemplate, + QueryStrings = queryString + }; + templateRenderer.Render(sw); } catch (Exception ee) { sw.Write("", ee); } - // update the local page items again - updateLocalContextItems(items, Context); - return sw.ToString(); } @@ -1141,39 +1136,17 @@ namespace umbraco } else { - page p = new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()); + + var p = new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()); p.RenderPage(TemplateId); - Control c = p.PageContentControl; - StringWriter sw = new StringWriter(); - HtmlTextWriter hw = new HtmlTextWriter(sw); + var c = p.PageContentControl; + var sw = new StringWriter(); + var hw = new HtmlTextWriter(sw); c.RenderControl(hw); - return sw.ToString(); } } - private static System.Collections.Generic.Dictionary getCurrentContextItems() - { - IDictionary items = HttpContext.Current.Items; - System.Collections.Generic.Dictionary currentItems = new Dictionary(); - IDictionaryEnumerator ide = items.GetEnumerator(); - while (ide.MoveNext()) - { - currentItems.Add(ide.Key, ide.Value); - } - return currentItems; - } - - private static void updateLocalContextItems(IDictionary items, HttpContext Context) - { - Context.Items.Clear(); - IDictionaryEnumerator ide = items.GetEnumerator(); - while (ide.MoveNext()) - { - Context.Items.Add(ide.Key, ide.Value); - } - } - /// /// Renders the default template for a specific page. /// @@ -1181,47 +1154,7 @@ namespace umbraco /// The rendered template as a string. public static string RenderTemplate(int PageId) { - if (UmbracoSettings.UseAspNetMasterPages) - { - if (!umbraco.presentation.UmbracoContext.Current.LiveEditingContext.Enabled) - { - System.Collections.Generic.Dictionary items = getCurrentContextItems(); - HttpContext.Current.Items["altTemplate"] = null; - - HttpContext Context = HttpContext.Current; - StringBuilder queryString = new StringBuilder(); - const string ONE_QS_PARAM = "&{0}={1}"; - foreach (object key in Context.Request.QueryString.Keys) - { - if (!key.ToString().ToLower().Equals("umbpageid") && !key.ToString().ToLower().Equals("alttemplate")) - queryString.Append(string.Format(ONE_QS_PARAM, key, Context.Request.QueryString[key.ToString()])); - } - StringWriter sw = new StringWriter(); - try - { - Context.Server.Execute(string.Format("/default.aspx?umbPageID={0}{1}", PageId, queryString), sw); - } - catch (Exception ee) - { - sw.Write("", ee); - } - - // update the local page items again - updateLocalContextItems(items, Context); - - return sw.ToString(); - } - else - { - return "RenderTemplate not supported in Canvas"; - } - } - else - { - return - RenderTemplate(PageId, - new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()).Template); - } + return RenderTemplate(PageId, -1); } /// From 3df7d81bde8ea9282ee25947a0ece298c58312c9 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 09:19:39 +0500 Subject: [PATCH 03/21] Fixed up the webforms url gen --- src/Umbraco.Web/Routing/TemplateRenderer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Routing/TemplateRenderer.cs b/src/Umbraco.Web/Routing/TemplateRenderer.cs index 0d0b4ded0f..300ef098e9 100644 --- a/src/Umbraco.Web/Routing/TemplateRenderer.cs +++ b/src/Umbraco.Web/Routing/TemplateRenderer.cs @@ -29,6 +29,7 @@ namespace Umbraco.Web.Routing public TemplateRenderer(UmbracoContext umbracoContext) { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); _umbracoContext = umbracoContext; } @@ -46,6 +47,7 @@ namespace Umbraco.Web.Routing public void Render(StringWriter writer) { + if (writer == null) throw new ArgumentNullException("writer"); // instanciate a request a process // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. @@ -112,9 +114,10 @@ namespace Umbraco.Web.Routing break; case RenderingEngine.WebForms: default: - //var webFormshandler = (global::umbraco.UmbracoDefault)BuildManager - // .CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault)); - var url = ("~/default.aspx?" + QueryStrings.Select(x => x.Key + "=" + x.Value + "&")).TrimEnd("&"); + var url = ("~/default.aspx?" + + string.Join("", QueryStrings.Select(x => x.Key + "=" + x.Value + "&"))) + .TrimEnd("&") + .TrimEnd("?"); _umbracoContext.HttpContext.Server.Execute(url, sw, true); break; } From 8597876c92434f2d5850c6746ad686c812967918 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 09:31:20 +0500 Subject: [PATCH 04/21] Fixed up the library calls to RenderTemplate, no longer a ton of duplicated code and removed the requirement to build up query strings as this is completley not required since we use 'preserveForm' in the Server.Execute. --- src/Umbraco.Web/Routing/TemplateRenderer.cs | 20 ++++++++++++------- .../umbraco.presentation/library.cs | 8 ++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web/Routing/TemplateRenderer.cs b/src/Umbraco.Web/Routing/TemplateRenderer.cs index 300ef098e9..55abfa2924 100644 --- a/src/Umbraco.Web/Routing/TemplateRenderer.cs +++ b/src/Umbraco.Web/Routing/TemplateRenderer.cs @@ -43,8 +43,6 @@ namespace Umbraco.Web.Routing /// public int? AltTemplate { get; set; } - public IDictionary QueryStrings { get; set; } - public void Render(StringWriter writer) { if (writer == null) throw new ArgumentNullException("writer"); @@ -107,6 +105,14 @@ namespace Umbraco.Web.Routing private void ExecuteTemplateRendering(StringWriter sw, PublishedContentRequest contentRequest) { + //NOTE: Before we used to build up the query strings here but this is not necessary because when we do a + // Server.Execute in the TemplateRenderer, we pass in a 'true' to 'preserveForm' which automatically preserves all current + // query strings so there's no need for this. HOWEVER, once we get MVC involved, we might have to do some fun things, + // though this will happen in the TemplateRenderer. + + //var queryString = _umbracoContext.HttpContext.Request.QueryString.AllKeys + // .ToDictionary(key => key, key => context.Request.QueryString[key]); + switch (contentRequest.RenderingEngine) { case RenderingEngine.Mvc: @@ -114,11 +120,11 @@ namespace Umbraco.Web.Routing break; case RenderingEngine.WebForms: default: - var url = ("~/default.aspx?" + - string.Join("", QueryStrings.Select(x => x.Key + "=" + x.Value + "&"))) - .TrimEnd("&") - .TrimEnd("?"); - _umbracoContext.HttpContext.Server.Execute(url, sw, true); + var webFormshandler = (global::umbraco.UmbracoDefault)BuildManager + .CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault)); + //the 'true' parameter will ensure that the current query strings are carried through, we don't have + // to build up the url again, it will just work. + _umbracoContext.HttpContext.Server.Execute(webFormshandler, sw, true); break; } } diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 62626104f9..0d8f413a58 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -1104,10 +1104,7 @@ namespace umbraco if (UmbracoSettings.UseAspNetMasterPages) { if (!UmbracoContext.Current.LiveEditingContext.Enabled) - { - var context = HttpContext.Current; - var queryString = context.Request.QueryString.AllKeys - .ToDictionary(key => key, key => context.Request.QueryString[key]); + { var sw = new StringWriter(); try @@ -1116,8 +1113,7 @@ namespace umbraco var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current) { PageId = PageId, - AltTemplate = altTemplate, - QueryStrings = queryString + AltTemplate = altTemplate }; templateRenderer.Render(sw); } From 9554c6a1c6b1921faec849e22845a007d78fa7ca Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 10:47:40 +0500 Subject: [PATCH 05/21] Fixes #U4-1174 - allows MVC views to render when using RenderTemplate. Added new UmbracoHelper method RenderTemplate. Cleaned up some code and ensuring that the string writers are disposed. Moved TemplateRenderer to a better namespace. --- src/Umbraco.Web/Mvc/ControllerExtensions.cs | 15 ++++-- src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 26 +++++++--- .../TemplateRenderer.cs | 51 +++++++++++++------ src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/Umbraco.Web/UmbracoHelper.cs | 23 +++++++++ .../umbraco.presentation/library.cs | 43 ++++++++-------- 6 files changed, 110 insertions(+), 50 deletions(-) rename src/Umbraco.Web/{Routing => Templates}/TemplateRenderer.cs (75%) diff --git a/src/Umbraco.Web/Mvc/ControllerExtensions.cs b/src/Umbraco.Web/Mvc/ControllerExtensions.cs index 42b60bb410..3fa8495a72 100644 --- a/src/Umbraco.Web/Mvc/ControllerExtensions.cs +++ b/src/Umbraco.Web/Mvc/ControllerExtensions.cs @@ -69,23 +69,28 @@ namespace Umbraco.Web.Mvc return sw.ToString().Trim(); } } - + /// /// Renders the partial view to string. /// /// The controller context. /// Name of the view. /// The model. + /// true if it is a Partial view, otherwise false for a normal view /// - internal static string RenderPartialViewToString(this ControllerBase controller, string viewName, object model) + internal static string RenderViewToString(this ControllerBase controller, string viewName, object model, bool isPartial = false) { + if (controller.ControllerContext == null) + throw new ArgumentException("The controller must have an assigned ControllerContext to execute this method."); controller.ViewData.Model = model; - using (StringWriter sw = new StringWriter()) + using (var sw = new StringWriter()) { - ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName); - ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw); + var viewResult = !isPartial + ? ViewEngines.Engines.FindView(controller.ControllerContext, viewName, null) + : ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName); + var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw); viewResult.View.Render(viewContext, sw); return sw.GetStringBuilder().ToString(); diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 30483cd022..dc65b726b3 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -65,13 +65,11 @@ namespace Umbraco.Web.Mvc { throw new NullReferenceException("There is not current PublishedContentRequest, it must be initialized before the RenderRouteHandler executes"); } - - var renderModel = new RenderModel(docRequest.PublishedContent, docRequest.Culture); - - //put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine - requestContext.RouteData.DataTokens.Add("umbraco", renderModel); //required for the RenderModelBinder - requestContext.RouteData.DataTokens.Add("umbraco-doc-request", docRequest); //required for RenderMvcController - requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext); //required for UmbracoTemplatePage + + SetupRouteDataForRequest( + new RenderModel(docRequest.PublishedContent, docRequest.Culture), + requestContext, + docRequest); return GetHandlerForRoute(requestContext, docRequest); @@ -79,6 +77,20 @@ namespace Umbraco.Web.Mvc #endregion + /// + /// Ensures that all of the correct DataTokens are added to the route values which are all required for rendering front-end umbraco views + /// + /// + /// + /// + internal void SetupRouteDataForRequest(RenderModel renderModel, RequestContext requestContext, PublishedContentRequest docRequest) + { + //put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine + requestContext.RouteData.DataTokens.Add("umbraco", renderModel); //required for the RenderModelBinder and view engine + requestContext.RouteData.DataTokens.Add("umbraco-doc-request", docRequest); //required for RenderMvcController + requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext); //required for UmbracoTemplatePage + } + /// /// Checks the request and query strings to see if it matches the definition of having a Surface controller /// posted value, if so, then we return a PostedDataProxyInfo object with the correct information. diff --git a/src/Umbraco.Web/Routing/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs similarity index 75% rename from src/Umbraco.Web/Routing/TemplateRenderer.cs rename to src/Umbraco.Web/Templates/TemplateRenderer.cs index 55abfa2924..ce2da96730 100644 --- a/src/Umbraco.Web/Routing/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -1,23 +1,21 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Web; using System.Web.Compilation; +using System.Web.Mvc; +using System.Web.Routing; using Umbraco.Core; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using umbraco; -using System.Linq; -namespace Umbraco.Web.Routing +namespace Umbraco.Web.Templates { /// /// This is used purely for the RenderTemplate functionality in Umbraco /// /// - /// Currently the only place where you can use RenderTemplate is from within the library class (which is legacy) - /// but i guess we still need to support it. In order to do that we need to be able to render both an MVC and Webforms - /// template. So we will do a Server.Execute to execute this handler, this will then need to find put the 'id' request through - /// the routing logic again, the execute the correct handler. - /// Its is pretty round about but is required. + /// This allows you to render either an MVC or Webforms template based purely off of a node id and an optional alttemplate id as string output. /// internal class TemplateRenderer { @@ -27,21 +25,23 @@ namespace Umbraco.Web.Routing private PublishedContentRequest _oldPublishedContentRequest; private object _oldAltTemplate; - public TemplateRenderer(UmbracoContext umbracoContext) + public TemplateRenderer(UmbracoContext umbracoContext, int pageId, int? altTemplateId) { if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + PageId = pageId; + AltTemplate = altTemplateId; _umbracoContext = umbracoContext; } /// /// Gets/sets the page id for the template to render /// - public int PageId { get; set; } + public int PageId { get; private set; } /// /// Gets/sets the alt template to render if there is one /// - public int? AltTemplate { get; set; } + public int? AltTemplate { get; private set; } public void Render(StringWriter writer) { @@ -103,7 +103,7 @@ namespace Umbraco.Web.Routing RestoreItems(); } - private void ExecuteTemplateRendering(StringWriter sw, PublishedContentRequest contentRequest) + private void ExecuteTemplateRendering(TextWriter sw, PublishedContentRequest contentRequest) { //NOTE: Before we used to build up the query strings here but this is not necessary because when we do a // Server.Execute in the TemplateRenderer, we pass in a 'true' to 'preserveForm' which automatically preserves all current @@ -112,11 +112,29 @@ namespace Umbraco.Web.Routing //var queryString = _umbracoContext.HttpContext.Request.QueryString.AllKeys // .ToDictionary(key => key, key => context.Request.QueryString[key]); - + switch (contentRequest.RenderingEngine) { case RenderingEngine.Mvc: - throw new NotImplementedException("Currently the TemplateRender does not support rendering MVC templates"); + var requestContext = new RequestContext(_umbracoContext.HttpContext, new RouteData() + { + Route = RouteTable.Routes["Umbraco_default"] + }); + var routeHandler = new RenderRouteHandler(ControllerBuilder.Current.GetControllerFactory(), _umbracoContext); + var routeDef = routeHandler.GetUmbracoRouteDefinition(requestContext, contentRequest); + var renderModel = new RenderModel(contentRequest.PublishedContent, contentRequest.Culture); + //manually add the action/controller, this is required by mvc + requestContext.RouteData.Values.Add("action", routeDef.ActionName); + requestContext.RouteData.Values.Add("controller", routeDef.ControllerName); + //add the rest of the required route data + routeHandler.SetupRouteDataForRequest(renderModel, requestContext, contentRequest); + //create and assign the controller context + routeDef.Controller.ControllerContext = new ControllerContext(requestContext, routeDef.Controller); + //render as string + var stringOutput = routeDef.Controller.RenderViewToString( + routeDef.ActionName, + renderModel); + sw.Write(stringOutput); break; case RenderingEngine.WebForms: default: @@ -126,7 +144,8 @@ namespace Umbraco.Web.Routing // to build up the url again, it will just work. _umbracoContext.HttpContext.Server.Execute(webFormshandler, sw, true); break; - } + } + } private void SetNewItemsOnContextObjects(PublishedContentRequest contentRequest) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9c98dee1ef..5d8846ba5d 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -312,7 +312,7 @@ - + ASPXCodeBehind diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 4be9eba95f..a8a9c69466 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -17,6 +17,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Web.Models; using Umbraco.Web.Mvc; +using Umbraco.Web.Routing; using Umbraco.Web.Templates; using umbraco; using System.Collections.Generic; @@ -63,6 +64,28 @@ namespace Umbraco.Web } } + /// + /// Renders the template for the specified pageId and an optional altTemplateId + /// + /// + /// If not specified, will use the template assigned to the node + /// + public IHtmlString RenderTemplate(int pageId, int? altTemplateId = null) + { + var templateRenderer = new TemplateRenderer(_umbracoContext, pageId, altTemplateId); + using (var sw = new StringWriter()) + { + try + { + templateRenderer.Render(sw); + } + catch(Exception ex) + { + sw.Write("", pageId, ex); + } + return new HtmlString(sw.ToString()); + } + } #region RenderMacro diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 0d8f413a58..8a78f77168 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -12,6 +12,7 @@ using System.Xml.XPath; using Umbraco.Core; using Umbraco.Web; using Umbraco.Web.Routing; +using Umbraco.Web.Templates; using umbraco.BusinessLogic; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.media; @@ -1105,25 +1106,21 @@ namespace umbraco { if (!UmbracoContext.Current.LiveEditingContext.Enabled) { - var sw = new StringWriter(); - - try - { - var altTemplate = TemplateId == -1 ? null : (int?)TemplateId; - var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current) + using (var sw = new StringWriter()) + { + try { - PageId = PageId, - AltTemplate = altTemplate - }; - templateRenderer.Render(sw); - } - catch (Exception ee) - { - sw.Write("", ee); - } - - return sw.ToString(); + var altTemplate = TemplateId == -1 ? null : (int?)TemplateId; + var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current, PageId, altTemplate); + templateRenderer.Render(sw); + } + catch (Exception ee) + { + sw.Write("", PageId, ee); + } + return sw.ToString(); + } } else { @@ -1136,10 +1133,14 @@ namespace umbraco var p = new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()); p.RenderPage(TemplateId); var c = p.PageContentControl; - var sw = new StringWriter(); - var hw = new HtmlTextWriter(sw); - c.RenderControl(hw); - return sw.ToString(); + + using (var sw = new StringWriter()) + using(var hw = new HtmlTextWriter(sw)) + { + c.RenderControl(hw); + return sw.ToString(); + } + } } From 8e7fe5ee55bc49c60dd3e075c6a635596774006c Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 14 Nov 2012 06:40:09 -0100 Subject: [PATCH 06/21] fix f3a3bdaf6329 --- .../umbraco.presentation/umbraco/editContent.aspx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs index 94847e3874..7bcc17e972 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/editContent.aspx.cs @@ -311,7 +311,7 @@ namespace umbraco.cms.presentation { Trace.Warn("before d.publish"); - if (_document.Id != 1061 && _document.PublishWithResult(base.getUser())) + if (_document.PublishWithResult(base.getUser())) { ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editContentPublishedHeader", null), ui.Text("speechBubbles", "editContentPublishedText", null)); From 3fb9ffca5b0c4cec96661db82b9312ff6a3cc52a Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 19:50:39 +0500 Subject: [PATCH 07/21] Update for installer rest service, updates version --- .../Configuration/GlobalSettings.cs | 2 +- .../Install/InstallPackageController.cs | 3 +- .../UmbracoInstallAuthorizeAttribute.cs | 92 +++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index bc16642c23..7bd344a323 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core.Configuration #region Private static fields // CURRENT UMBRACO VERSION ID - private const string CurrentUmbracoVersion = "4.10.0"; + private const string CurrentUmbracoVersion = "4.10.1"; private static string _reservedUrlsCache; private static string _reservedPathsCache; diff --git a/src/Umbraco.Web/Install/InstallPackageController.cs b/src/Umbraco.Web/Install/InstallPackageController.cs index 8e9a5d02ba..212cb3f97e 100644 --- a/src/Umbraco.Web/Install/InstallPackageController.cs +++ b/src/Umbraco.Web/Install/InstallPackageController.cs @@ -16,6 +16,7 @@ namespace Umbraco.Web.Install /// Currently this is used for web services however we should/could eventually migrate the whole installer to MVC as it /// is a bit of a mess currently. /// + [UmbracoInstallAuthorize] public class InstallPackageController : Controller { private readonly ApplicationContext _applicationContext; @@ -173,6 +174,6 @@ namespace Umbraco.Web.Install message = "Starter kit has been installed" }); } - } + } diff --git a/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs b/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs new file mode 100644 index 0000000000..4bfe4dc8d3 --- /dev/null +++ b/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs @@ -0,0 +1,92 @@ +using System; +using System.Web; +using System.Web.Mvc; +using Umbraco.Core; +using umbraco.BasePages; + +namespace Umbraco.Web.Install +{ + /// + /// Ensures authorization occurs for the installer if it has already completed. If install has not yet occured + /// then the authorization is successful + /// + internal class UmbracoInstallAuthorizeAttribute : AuthorizeAttribute + { + + public const string InstallRoleName = "umbraco-install-EF732A6E-AA55-4A93-9F42-6C989D519A4F"; + + public ApplicationContext ApplicationContext { get; set; } + + public UmbracoInstallAuthorizeAttribute(ApplicationContext appContext) + { + if (appContext == null) throw new ArgumentNullException("appContext"); + ApplicationContext = appContext; + } + + public UmbracoInstallAuthorizeAttribute() + : this(ApplicationContext.Current) + { + + } + + /// + /// Ensures that the user must be in the Administrator or the Install role + /// + /// + /// + protected override bool AuthorizeCore(HttpContextBase httpContext) + { + if (httpContext == null) + { + throw new ArgumentNullException("httpContext"); + } + + try + { + //if its not configured then we can continue + if (!ApplicationContext.IsConfigured) + { + return true; + } + + //otherwise we need to ensure that a user is logged in + var isLoggedIn = BasePage.ValidateUserContextID(BasePage.umbracoUserContextID); + if (isLoggedIn) + { + return true; + } + + return false; + } + catch (Exception) + { + return false; + } + } + + public override void OnAuthorization(AuthorizationContext filterContext) + { + Mandate.ParameterNotNull(filterContext, "filterContext"); + if (OutputCacheAttribute.IsChildActionCacheActive(filterContext)) + throw new InvalidOperationException("Cannot use UmbracoInstallAuthorizeAttribute on a child action"); + if (AuthorizeCore(filterContext.HttpContext)) + { + //with a little help from dotPeek... this is what it normally would do + var cache = filterContext.HttpContext.Response.Cache; + cache.SetProxyMaxAge(new TimeSpan(0L)); + cache.AddValidationCallback(CacheValidateHandler, null); + } + else + { + //they aren't authorized but the app has installed + throw new HttpException((int)global::System.Net.HttpStatusCode.Unauthorized, + "You must login to view this resource."); + } + } + + private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) + { + validationStatus = OnCacheAuthorization(new HttpContextWrapper(context)); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ef9c4fda47..56d35f4642 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -251,6 +251,7 @@ + From eac11c1b80adbcf70d806a84ff13003434e1a004 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 21:22:30 +0500 Subject: [PATCH 08/21] cleaned up some of the install auth attribute --- .../Install/UmbracoInstallAuthorizeAttribute.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs b/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs index 4bfe4dc8d3..a990088fdf 100644 --- a/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs +++ b/src/Umbraco.Web/Install/UmbracoInstallAuthorizeAttribute.cs @@ -12,15 +12,12 @@ namespace Umbraco.Web.Install /// internal class UmbracoInstallAuthorizeAttribute : AuthorizeAttribute { - - public const string InstallRoleName = "umbraco-install-EF732A6E-AA55-4A93-9F42-6C989D519A4F"; - - public ApplicationContext ApplicationContext { get; set; } + private readonly ApplicationContext _applicationContext; public UmbracoInstallAuthorizeAttribute(ApplicationContext appContext) { if (appContext == null) throw new ArgumentNullException("appContext"); - ApplicationContext = appContext; + _applicationContext = appContext; } public UmbracoInstallAuthorizeAttribute() @@ -44,7 +41,7 @@ namespace Umbraco.Web.Install try { //if its not configured then we can continue - if (!ApplicationContext.IsConfigured) + if (!_applicationContext.IsConfigured) { return true; } From 10df6e878c9abf445ee989ee99ebe85f89a0ee04 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Nov 2012 22:05:58 +0500 Subject: [PATCH 09/21] updated config --- src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config b/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config index 002a7db03c..2556fe2fd7 100644 --- a/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config +++ b/src/Umbraco.Web.UI/web.Template.ShandemVaio.Debug.config @@ -19,7 +19,7 @@ + value="4.10.1"/> Date: Thu, 15 Nov 2012 02:04:46 +0500 Subject: [PATCH 10/21] renamed namespace in test proj --- .../DynamicDocumentTestsBase.cs | 3 +-- .../DynamicNodeTests.cs | 7 +------ ...amicPublishedContentCustomExtensionMethods.cs | 4 +--- .../DynamicPublishedContentTests.cs | 12 +----------- .../DynamicXmlTests.cs | 4 +--- .../PublishedContentDataTableTests.cs | 2 +- .../PublishedContentTests.cs | 3 +-- .../StronglyTypedQueryTests.cs | 3 +-- src/Umbraco.Tests/Umbraco.Tests.csproj | 16 ++++++++-------- 9 files changed, 16 insertions(+), 38 deletions(-) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/DynamicDocumentTestsBase.cs (96%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/DynamicNodeTests.cs (87%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/DynamicPublishedContentCustomExtensionMethods.cs (86%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/DynamicPublishedContentTests.cs (88%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/DynamicXmlTests.cs (95%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/PublishedContentDataTableTests.cs (96%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/PublishedContentTests.cs (96%) rename src/Umbraco.Tests/{DynamicDocument => PublishedContent}/StronglyTypedQueryTests.cs (96%) diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs similarity index 96% rename from src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs rename to src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs index f5db4adc33..b1b66ad110 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs @@ -6,10 +6,9 @@ using System.Web; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Dynamics; -using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { [TestFixture] public abstract class DynamicDocumentTestsBase : BaseRoutingTest diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs similarity index 87% rename from src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs rename to src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs index 360af99c14..e693c0d8a8 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicNodeTests.cs @@ -5,17 +5,12 @@ using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Tests.TestHelpers; using Umbraco.Web; -using Umbraco.Web.Models; -using umbraco.BusinessLogic; using umbraco.IO; using umbraco.MacroEngines; using umbraco.NodeFactory; -using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.template; using System.Linq; -using umbraco.cms.businesslogic.web; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { [TestFixture] public class DynamicNodeTests : DynamicDocumentTestsBase diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs b/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentCustomExtensionMethods.cs similarity index 86% rename from src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs rename to src/Umbraco.Tests/PublishedContent/DynamicPublishedContentCustomExtensionMethods.cs index 92a47a6260..b2cebf3ed0 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentCustomExtensionMethods.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentCustomExtensionMethods.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; -using Umbraco.Core.Dynamics; -using Umbraco.Core.Models; using Umbraco.Web.Models; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { public static class DynamicPublishedContentCustomExtensionMethods { diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs similarity index 88% rename from src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs rename to src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs index e9c0dfa3b3..3e0d52feb1 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicPublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicPublishedContentTests.cs @@ -1,23 +1,13 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Linq; -using System.Web; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Dynamics; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; -using Umbraco.Tests.Routing; using Umbraco.Web; using Umbraco.Web.Models; -using Umbraco.Web.Routing; -using umbraco.BusinessLogic; -using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.template; -using umbraco.cms.businesslogic.web; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { [TestFixture] public class DynamicPublishedContentTests : DynamicDocumentTestsBase diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicXmlTests.cs b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs similarity index 95% rename from src/Umbraco.Tests/DynamicDocument/DynamicXmlTests.cs rename to src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs index 1d69b1f9cd..479fcc5929 100644 --- a/src/Umbraco.Tests/DynamicDocument/DynamicXmlTests.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicXmlTests.cs @@ -1,12 +1,10 @@ using System; using System.Diagnostics; -using System.Xml; -using System.Xml.Linq; using Microsoft.CSharp.RuntimeBinder; using NUnit.Framework; using Umbraco.Core.Dynamics; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { [TestFixture] public class DynamicXmlTests diff --git a/src/Umbraco.Tests/DynamicDocument/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs similarity index 96% rename from src/Umbraco.Tests/DynamicDocument/PublishedContentDataTableTests.cs rename to src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 2ccd8684f0..6d8b00e5eb 100644 --- a/src/Umbraco.Tests/DynamicDocument/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Web; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { /// /// Unit tests for IPublishedContent and extensions diff --git a/src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs similarity index 96% rename from src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs rename to src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 3c55138a75..d62131c3f2 100644 --- a/src/Umbraco.Tests/DynamicDocument/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -7,9 +7,8 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Web; -using Umbraco.Web.Models; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { /// /// Tests the typed extension methods on IPublishedContent the same way we test the dynamic ones diff --git a/src/Umbraco.Tests/DynamicDocument/StronglyTypedQueryTests.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs similarity index 96% rename from src/Umbraco.Tests/DynamicDocument/StronglyTypedQueryTests.cs rename to src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs index 452086970b..39695065ac 100644 --- a/src/Umbraco.Tests/DynamicDocument/StronglyTypedQueryTests.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -9,7 +8,7 @@ using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Web; -namespace Umbraco.Tests.DynamicDocument +namespace Umbraco.Tests.PublishedContent { [TestFixture] public class StronglyTypedQueryTests : BaseWebTest diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f4c6bd60c1..146dfad43d 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -55,10 +55,10 @@ - - - - + + + + @@ -70,10 +70,10 @@ - - - - + + + + From a7a38478db6e94866a6773340adfa96c8b96dda9 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 15 Nov 2012 04:18:23 +0500 Subject: [PATCH 11/21] Working on #U4-1174 - issue with TypedMedia in MVC, fixes a few things and ensures Children() works as expected, adds unit tests. --- .../Configuration/UmbracoSettings.cs | 29 ++++- .../ContentStores/PublishMediaStoreTests.cs | 42 +++++- .../PublishedContent/PublishedContentTests.cs | 2 +- .../PublishedContent/PublishedMediaTests.cs | 122 ++++++++++++++++++ src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 4 + src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/Umbraco.Tests/UriUtilityTests.cs | 8 ++ src/Umbraco.Web/DefaultPublishedMediaStore.cs | 100 ++++++++------ 8 files changed, 264 insertions(+), 44 deletions(-) create mode 100644 src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs index 9c3bd8bdc7..beab6537b3 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs @@ -25,6 +25,18 @@ namespace Umbraco.Core.Configuration /// internal class UmbracoSettings { + /// + /// Used in unit testing to reset all config items that were set with property setters (i.e. did not come from config) + /// + internal static void ResetSetters() + { + _addTrailingSlash = null; + _forceSafeAliases = null; + _useLegacySchema = null; + _useDomainPrefixes = null; + _umbracoLibraryCacheDuration = null; + } + internal const string TempFriendlyXmlChildContainerNodename = ""; // "children"; /// @@ -601,6 +613,8 @@ namespace Umbraco.Core.Configuration } } + private static bool? _forceSafeAliases; + /// /// Whether to force safe aliases (no spaces, no special characters) at businesslogic level on contenttypes and propertytypes /// @@ -608,6 +622,9 @@ namespace Umbraco.Core.Configuration { get { + if (_forceSafeAliases.HasValue) + return _forceSafeAliases.Value; + string forceSafeAlias = GetKey("/settings/content/ForceSafeAliases"); if (String.IsNullOrEmpty(forceSafeAlias)) return true; @@ -622,7 +639,11 @@ namespace Umbraco.Core.Configuration return true; } } - + } + internal set + { + //used for unit testing + _forceSafeAliases = value; } } @@ -645,6 +666,8 @@ namespace Umbraco.Core.Configuration get { return GetKey("/settings/content/scripteditor/scriptFileTypes"); } } + private static int? _umbracoLibraryCacheDuration; + /// /// Gets the duration in seconds to cache queries to umbraco library member and media methods /// Default is 1800 seconds (30 minutes) @@ -653,6 +676,9 @@ namespace Umbraco.Core.Configuration { get { + if (_umbracoLibraryCacheDuration.HasValue) + return _umbracoLibraryCacheDuration.Value; + string libraryCacheDuration = GetKey("/settings/content/UmbracoLibraryCacheDuration"); if (String.IsNullOrEmpty(libraryCacheDuration)) return 1800; @@ -669,6 +695,7 @@ namespace Umbraco.Core.Configuration } } + internal set { _umbracoLibraryCacheDuration = value; } } /// diff --git a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs index b9cc5c485d..214931138c 100644 --- a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs @@ -6,13 +6,51 @@ using Examine; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Tests.PublishedContent; +using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using umbraco.BusinessLogic; -namespace Umbraco.Tests +namespace Umbraco.Tests.ContentStores { [TestFixture] - public class PublishMediaStoreTests + public class PublishMediaStoreTests : BaseWebTest { + public override void Initialize() + { + base.Initialize(); + //we're going to use the same initialization as the PublishedMediaTests + PublishedMediaTests.DoInitialization(GetUmbracoContext("/test", 1234)); + } + + public override void TearDown() + { + base.TearDown(); + PublishedMediaTests.DoTearDown(); + } + + [Test] + public void Get_Item_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var publishedMedia = PublishedMediaTests.GetNode(mRoot.Id, GetUmbracoContext("/test", 1234)); + + Assert.AreEqual(mRoot.Id, publishedMedia.Id); + Assert.AreEqual(mRoot.CreateDateTime.ToString("dd/MM/yyyy HH:mm:ss"), publishedMedia.CreateDate.ToString("dd/MM/yyyy HH:mm:ss")); + Assert.AreEqual(mRoot.User.Id, publishedMedia.CreatorId); + Assert.AreEqual(mRoot.User.Name, publishedMedia.CreatorName); + Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.DocumentTypeAlias); + Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.DocumentTypeId); + Assert.AreEqual(mRoot.Level, publishedMedia.Level); + Assert.AreEqual(mRoot.Text, publishedMedia.Name); + Assert.AreEqual(mRoot.Path, publishedMedia.Path); + Assert.AreEqual(mRoot.sortOrder, publishedMedia.SortOrder); + Assert.IsNull(publishedMedia.Parent); + } + [TestCase("id")] [TestCase("nodeId")] [TestCase("__NodeId")] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index d62131c3f2..bf3a410e51 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -11,7 +11,7 @@ using Umbraco.Web; namespace Umbraco.Tests.PublishedContent { /// - /// Tests the typed extension methods on IPublishedContent the same way we test the dynamic ones + /// Tests the methods on IPublishedContent using the DefaultPublishedContentStore /// [TestFixture] public class PublishedContentTests : BaseWebTest diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs new file mode 100644 index 0000000000..a5a33db6a2 --- /dev/null +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -0,0 +1,122 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; +using umbraco.BusinessLogic; +using System.Linq; + +namespace Umbraco.Tests.PublishedContent +{ + /// + /// Tests the typed extension methods on IPublishedContent using the DefaultPublishedMediaStore + /// + [TestFixture] + public class PublishedMediaTests : BaseWebTest + { + + public override void Initialize() + { + base.Initialize(); + DoInitialization(GetUmbracoContext("/test", 1234)); + } + + /// + /// Shared with PublishMediaStoreTests + /// + /// + internal static void DoInitialization(UmbracoContext umbContext) + { + PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver( + new[] + { + typeof(DatePickerPropertyEditorValueConverter), + typeof(TinyMcePropertyEditorValueConverter), + typeof(YesNoPropertyEditorValueConverter) + }); + + //need to specify a custom callback for unit tests + PublishedContentHelper.GetDataTypeCallback = (docTypeAlias, propertyAlias) => + { + if (propertyAlias == "content") + { + //return the rte type id + return Guid.Parse("5e9b75ae-face-41c8-b47e-5f4b0fd82f83"); + } + return Guid.Empty; + }; + + UmbracoSettings.ForceSafeAliases = true; + UmbracoSettings.UmbracoLibraryCacheDuration = 1800; + + UmbracoContext.Current = umbContext; + PublishedMediaStoreResolver.Current = new PublishedMediaStoreResolver(new DefaultPublishedMediaStore()); + + UmbracoSettings.ForceSafeAliases = true; + } + + public override void TearDown() + { + base.TearDown(); + + DoTearDown(); + } + + /// + /// Shared with PublishMediaStoreTests + /// + internal static void DoTearDown() + { + PropertyEditorValueConvertersResolver.Reset(); + UmbracoContext.Current = null; + PublishedMediaStoreResolver.Reset(); + } + + /// + /// Shared with PublishMediaStoreTests + /// + /// + /// + /// + internal static IPublishedContent GetNode(int id, UmbracoContext umbracoContext) + { + var ctx = umbracoContext; + var mediaStore = new DefaultPublishedMediaStore(); + var doc = mediaStore.GetDocumentById(ctx, id); + Assert.IsNotNull(doc); + return doc; + } + + private IPublishedContent GetNode(int id) + { + return GetNode(id, GetUmbracoContext("/test", 1234)); + } + + [Test] + public void Children_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id); + var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id); + + var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id); + var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id); + var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id); + + var publishedMedia = GetNode(mRoot.Id); + var rootChildren = publishedMedia.Children(); + Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] {mChild1.Id, mChild2.Id, mChild3.Id})); + + var publishedChild1 = GetNode(mChild1.Id); + var subChildren = publishedChild1.Children(); + Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index 4cb5ad9c11..3a404347f0 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -4,6 +4,7 @@ using System.Web.Routing; using System.Xml; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.ObjectResolution; using Umbraco.Tests.Stubs; @@ -41,6 +42,7 @@ namespace Umbraco.Tests.TestHelpers public virtual void TearDown() { //reset the app context + ApplicationContext.ApplicationCache.ClearAllCache(); ApplicationContext.Current = null; Resolution.IsFrozen = false; if (RequiresDbSetup) @@ -49,6 +51,8 @@ namespace Umbraco.Tests.TestHelpers AppDomain.CurrentDomain.SetData("DataDirectory", null); Cache.ClearAllCache(); + + UmbracoSettings.ResetSetters(); } /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 146dfad43d..72528a7163 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -58,6 +58,7 @@ + diff --git a/src/Umbraco.Tests/UriUtilityTests.cs b/src/Umbraco.Tests/UriUtilityTests.cs index 6328c9ba12..a477d668d8 100644 --- a/src/Umbraco.Tests/UriUtilityTests.cs +++ b/src/Umbraco.Tests/UriUtilityTests.cs @@ -1,6 +1,7 @@ using System; using System.Configuration; using NUnit.Framework; +using Umbraco.Core.Configuration; using Umbraco.Web; namespace Umbraco.Tests @@ -10,6 +11,13 @@ namespace Umbraco.Tests [TestFixture] public class UriUtilityTests { + + [TearDown] + public void TearDown() + { + UmbracoSettings.ResetSetters(); + } + // test normal urls [TestCase("http://LocalHost/", "http://localhost/")] [TestCase("http://LocalHost/?x=y", "http://localhost/?x=y")] diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index dde6248565..562bfd91e5 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -41,37 +41,52 @@ namespace Umbraco.Web return result; } - private IPublishedContent GetUmbracoMedia(int id) + private ExamineManager GetExamineManagerSafe() { - try { - //first check in Examine as this is WAY faster - var criteria = ExamineManager.Instance - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); - var filter = criteria.Id(id); - var results = ExamineManager - .Instance.SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); - if (results.Any()) - { - return ConvertFromSearchResult(results.First()); - } + return ExamineManager.Instance; } - catch (FileNotFoundException) + catch (TypeInitializationException) { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - //TODO: Need to fix examine in LB scenarios! + return null; + } + } + + private IPublishedContent GetUmbracoMedia(int id) + { + var eMgr = GetExamineManagerSafe(); + if (eMgr != null) + { + try + { + //first check in Examine as this is WAY faster + var criteria = eMgr + .SearchProviderCollection["InternalSearcher"] + .CreateSearchCriteria("media"); + var filter = criteria.Id(id); + var results = eMgr + .SearchProviderCollection["InternalSearcher"] + .Search(filter.Compile()); + if (results.Any()) + { + return ConvertFromSearchResult(results.First()); + } + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + } } var media = global::umbraco.library.GetMedia(id, true); if (media != null && media.Current != null) { - if (media.MoveNext()) - { + //if (media.MoveNext()) + //{ var current = media.Current; //error check if (media.Current.MoveToFirstChild() && media.Current.Name.InvariantEquals("error")) @@ -80,7 +95,7 @@ namespace Umbraco.Web } return ConvertFromXPathNavigator(current); - } + //} } return null; @@ -240,27 +255,32 @@ namespace Umbraco.Web //if there is no navigator, try examine first, then re-look it up if (xpath == null) { - try + var eMgr = GetExamineManagerSafe(); + + if (eMgr != null) { - //first check in Examine as this is WAY faster - var criteria = ExamineManager.Instance - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); - var filter = criteria.ParentId(parentId); - var results = ExamineManager - .Instance.SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); - if (results.Any()) + try { - return results.Select(ConvertFromSearchResult); + //first check in Examine as this is WAY faster + var criteria = eMgr + .SearchProviderCollection["InternalSearcher"] + .CreateSearchCriteria("media"); + var filter = criteria.ParentId(parentId); + var results = eMgr + .SearchProviderCollection["InternalSearcher"] + .Search(filter.Compile()); + if (results.Any()) + { + return results.Select(ConvertFromSearchResult); + } } - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + } + } var media = library.GetMedia(parentId, true); if (media != null && media.Current != null) From 278aa3f8208c3ec1a5d5b6e6823c3f1e73a6a005 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 15 Nov 2012 13:41:44 -0100 Subject: [PATCH 12/21] U4-1203 - UmbracoHelper.RenderMacro displays trace output at the wrong place --- src/Umbraco.Web/UmbracoHelper.cs | 43 +++++++++++++++---- .../umbraco.presentation/default.aspx.cs | 6 +-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index a8a9c69466..ba3cac7433 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -119,7 +119,7 @@ namespace Umbraco.Web public IHtmlString RenderMacro(string alias, IDictionary parameters) { if (alias == null) throw new ArgumentNullException("alias"); - var containerPage = new FormlessPage(); + var m = macro.GetMacro(alias); if (_umbracoContext.PageId == null) { @@ -139,16 +139,41 @@ namespace Umbraco.Web var macroControl = m.renderMacro(macroProps, UmbracoContext.Current.PublishedContentRequest.UmbracoPage.Elements, _umbracoContext.PageId.Value); - containerPage.Controls.Add(macroControl); - using (var output = new StringWriter()) - { - _umbracoContext.HttpContext.Server.Execute(containerPage, output, false); - //Now, we need to ensure that local links are parsed - return new HtmlString( - TemplateUtilities.ParseInternalLinks( - output.ToString())); + string html; + if (macroControl is LiteralControl) + { + // no need to execute, we already have text + html = (macroControl as LiteralControl).Text; } + else + { + var containerPage = new FormlessPage(); + containerPage.Controls.Add(macroControl); + + using (var output = new StringWriter()) + { + // .Execute() does a PushTraceContext/PopTraceContext and writes trace output straight into 'output' + // and I do not see how we could wire the trace context to the current context... so it creates dirty + // trace output right in the middle of the page. + // + // The only thing we can do is fully disable trace output while .Execute() runs and restore afterwards + // which means trace output is lost if the macro is a control (.ascx or user control) that is invoked + // from within Razor -- which makes sense anyway because the control can _not_ run correctly from + // within Razor since it will never be inserted into the page pipeline (which may even not exist at all + // if we're running MVC). + // + var traceIsEnabled = containerPage.Trace.IsEnabled; + containerPage.Trace.IsEnabled = false; + _umbracoContext.HttpContext.Server.Execute(containerPage, output, false); + containerPage.Trace.IsEnabled = traceIsEnabled; + + //Now, we need to ensure that local links are parsed + html = TemplateUtilities.ParseInternalLinks(output.ToString()); + } + } + + return new HtmlString(html); } #endregion diff --git a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs index c40b42d54f..1526186145 100644 --- a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs @@ -45,6 +45,9 @@ namespace umbraco { base.OnPreInit(e); + // handle the infamous umbDebugShowTrace, etc + Page.Trace.IsEnabled &= GlobalSettings.DebugMode && !String.IsNullOrWhiteSpace(Request["umbDebugShowTrace"]); + // get the document request and the page _docRequest = UmbracoContext.Current.PublishedContentRequest; _upage = _docRequest.UmbracoPage; @@ -116,9 +119,6 @@ namespace umbraco if (!liveEditing && ValidateRequest) Request.ValidateInput(); - - // handle the infamous umbDebugShowTrace, etc - Page.Trace.IsEnabled &= GlobalSettings.DebugMode && !String.IsNullOrWhiteSpace(Request["umbDebugShowTrace"]); } protected override void Render(HtmlTextWriter writer) From 8b141dc51ac942853b90e938249f43cc2bf700c3 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 15 Nov 2012 20:11:10 +0500 Subject: [PATCH 13/21] Working on #U4-1174, lots of updates/fixes to media in MVC + unit tests. --- .../ContentStores/PublishMediaStoreTests.cs | 18 +++ .../PublishedContent/PublishedMediaTests.cs | 114 ++++++++++++++++++ src/Umbraco.Web/DefaultPublishedMediaStore.cs | 51 ++++---- 3 files changed, 159 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs index 214931138c..0595923589 100644 --- a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs @@ -29,6 +29,24 @@ namespace Umbraco.Tests.ContentStores PublishedMediaTests.DoTearDown(); } + [Test] + public void Get_Root_Docs() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot1", mType, user, -1); + var mRoot2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot2", mType, user, -1); + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot1.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot2.Id); + + var ctx = GetUmbracoContext("/test", 1234); + var mediaStore = new DefaultPublishedMediaStore(); + var roots = mediaStore.GetRootDocuments(ctx); + Assert.AreEqual(2, roots.Count()); + Assert.IsTrue(roots.Select(x => x.Id).ContainsAll(new[] {mRoot1.Id, mRoot2.Id})); + + } + [Test] public void Get_Item_Without_Examine() { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index a5a33db6a2..973655b9c1 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -118,5 +118,119 @@ namespace Umbraco.Tests.PublishedContent var subChildren = publishedChild1.Children(); Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); } + + [Test] + public void Descendants_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id); + var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id); + + var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id); + var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id); + var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id); + + var publishedMedia = GetNode(mRoot.Id); + var rootDescendants = publishedMedia.Descendants(); + Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mChild2.Id, mChild3.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); + + var publishedChild1 = GetNode(mChild1.Id); + var subDescendants = publishedChild1.Descendants(); + Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); + } + + [Test] + public void DescendantsOrSelf_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id); + var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id); + + var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id); + var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id); + var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id); + + var publishedMedia = GetNode(mRoot.Id); + var rootDescendantsOrSelf = publishedMedia.DescendantsOrSelf(); + Assert.IsTrue(rootDescendantsOrSelf.Select(x => x.Id).ContainsAll( + new[] { mRoot.Id, mChild1.Id, mChild2.Id, mChild3.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); + + var publishedChild1 = GetNode(mChild1.Id); + var subDescendantsOrSelf = publishedChild1.DescendantsOrSelf(); + Assert.IsTrue(subDescendantsOrSelf.Select(x => x.Id).ContainsAll( + new[] { mChild1.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); + } + + [Test] + public void Parent_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id); + var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id); + + var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id); + var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id); + var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id); + + var publishedRoot = GetNode(mRoot.Id); + Assert.AreEqual(null, publishedRoot.Parent); + + var publishedChild1 = GetNode(mChild1.Id); + Assert.AreEqual(mRoot.Id, publishedChild1.Parent.Id); + + var publishedSubChild1 = GetNode(mSubChild1.Id); + Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent.Id); + } + + [Test] + public void Ancestors_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id); + var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id); + + var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id); + var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id); + var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id); + + var publishedSubChild1 = GetNode(mSubChild1.Id); + Assert.IsTrue(publishedSubChild1.Ancestors().Select(x => x.Id).ContainsAll(new[] {mChild1.Id, mRoot.Id})); + } + + [Test] + public void AncestorsOrSelf_Without_Examine() + { + var user = new User(0); + var mType = global::umbraco.cms.businesslogic.media.MediaType.MakeNew(user, "TestMediaType"); + var mRoot = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot", mType, user, -1); + + var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot.Id); + var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot.Id); + var mChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child3", mType, user, mRoot.Id); + + var mSubChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild1", mType, user, mChild1.Id); + var mSubChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild2", mType, user, mChild1.Id); + var mSubChild3 = global::umbraco.cms.businesslogic.media.Media.MakeNew("SubChild3", mType, user, mChild1.Id); + + var publishedSubChild1 = GetNode(mSubChild1.Id); + Assert.IsTrue(publishedSubChild1.AncestorsOrSelf().Select(x => x.Id).ContainsAll( + new[] { mSubChild1.Id, mChild1.Id, mRoot.Id })); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index 562bfd91e5..b6675e01fa 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -85,17 +85,15 @@ namespace Umbraco.Web var media = global::umbraco.library.GetMedia(id, true); if (media != null && media.Current != null) { - //if (media.MoveNext()) + media.MoveNext(); + ////error check + //if (media.Current.MoveToFirstChild() && media.Current.Name.InvariantEquals("error")) //{ - var current = media.Current; - //error check - if (media.Current.MoveToFirstChild() && media.Current.Name.InvariantEquals("error")) - { - return null; - } + // return null; + //} + //var current = media.Current; - return ConvertFromXPathNavigator(current); - //} + return ConvertFromXPathNavigator(media.Current); } return null; @@ -195,7 +193,8 @@ namespace Umbraco.Web ? GetUmbracoMedia(d.ParentId) : null, //callback to return the children of the current node based on the xml structure already found - d => GetChildrenMedia(d.ParentId, xpath), + //d => GetChildrenMedia(d.ParentId, xpath), + d => GetChildrenMedia(d.Id, xpath), GetProperty); } @@ -285,32 +284,36 @@ namespace Umbraco.Web var media = library.GetMedia(parentId, true); if (media != null && media.Current != null) { - if (!media.MoveNext()) - return null; xpath = media.Current; } + else + { + return null; + } } - var children = xpath.SelectChildren(XPathNodeType.Element); - var mediaList = new List(); - while (children.Current != null) + //The xpath might be the whole xpath including the current ones ancestors so we need to select the current node + var item = xpath.Select("//*[@id='" + parentId + "']"); + if (item.Current == null) { - if (!children.MoveNext()) - { - break; - } + return null; + } + var children = item.Current.SelectChildren(XPathNodeType.Element); + var mediaList = new List(); + foreach(XPathNavigator x in children) + { //NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but // will leave it here as it must have done something! - if (children.Current.Name != "contents") + if (x.Name != "contents") { //make sure it's actually a node, not a property - if (!string.IsNullOrEmpty(children.Current.GetAttribute("path", "")) && - !string.IsNullOrEmpty(children.Current.GetAttribute("id", ""))) + if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) && + !string.IsNullOrEmpty(x.GetAttribute("id", ""))) { - mediaList.Add(ConvertFromXPathNavigator(children.Current)); + mediaList.Add(ConvertFromXPathNavigator(x)); } - } + } } return mediaList; } From 3dd411f159c8280c8383d181701d55cebebe3b1e Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Thu, 15 Nov 2012 21:46:54 +0500 Subject: [PATCH 14/21] Working on #U4-1174, lots of updates/fixes to media in MVC regarding loading data from the examine cache to return media + lots of unit tests. --- .../ContentStores/PublishMediaStoreTests.cs | 6 +- .../PublishedContent/PublishedMediaTests.cs | 107 ++++++ .../ExamineResources.Designer.cs | 99 +++++ .../ExamineHelpers/ExamineResources.resx | 127 ++++++ .../ExamineHelpers/IndexInitializer.cs | 360 ++++++++++++++++++ .../TestHelpers/ExamineHelpers/media.xml | 51 +++ src/Umbraco.Tests/TestHelpers/TestHelper.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 23 +- src/Umbraco.Web/DefaultPublishedMediaStore.cs | 121 ++++-- 9 files changed, 854 insertions(+), 42 deletions(-) create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs create mode 100644 src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml diff --git a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs index 0595923589..eaced2de55 100644 --- a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs +++ b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs @@ -247,10 +247,12 @@ namespace Umbraco.Tests.ContentStores a => null, //we're not going to test this so ignore a => new List(), - (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a))), + (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), + false), //callback to get the children d => children, - (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a))); + (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), + false); return dicDoc; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 973655b9c1..c37cd0cf18 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -1,11 +1,24 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis.Standard; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.ExamineHelpers; using Umbraco.Web; +using UmbracoExamine; +using UmbracoExamine.DataServices; using umbraco.BusinessLogic; using System.Linq; @@ -95,6 +108,98 @@ namespace Umbraco.Tests.PublishedContent return GetNode(id, GetUmbracoContext("/test", 1234)); } + [Test] + public void Children_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 1111); + var rootChildren = publishedMedia.Children(); + Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { 2222, 1113, 1114, 1115, 1116 })); + + var publishedChild1 = store.GetDocumentById(GetUmbracoContext("/test", 1234), 2222); + var subChildren = publishedChild1.Children(); + Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { 2112 })); + } + + [Test] + public void Descendants_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 1111); + var rootDescendants = publishedMedia.Descendants(); + Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1113, 1114, 1115, 1116 })); + + var publishedChild1 = store.GetDocumentById(GetUmbracoContext("/test", 1234), 2222); + var subDescendants = publishedChild1.Descendants(); + Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 3113 })); + } + + [Test] + public void DescendantsOrSelf_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 1111); + var rootDescendants = publishedMedia.DescendantsOrSelf(); + Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 1111, 2112, 2222, 1113, 1114, 1115, 1116 })); + + var publishedChild1 = store.GetDocumentById(GetUmbracoContext("/test", 1234), 2222); + var subDescendants = publishedChild1.DescendantsOrSelf(); + Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2222, 2112, 3113 })); + } + + [Test] + public void Ancestors_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 3113); + var ancestors = publishedMedia.Ancestors(); + Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1111 })); + } + + [Test] + public void AncestorsOrSelf_With_Examine() + { + var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\CWSIndexSetTest", Guid.NewGuid().ToString())); + var indexInit = new IndexInitializer(); + var indexer = indexInit.GetUmbracoIndexer(newIndexFolder); + indexer.RebuildIndex(); + + var store = new DefaultPublishedMediaStore(indexInit.GetUmbracoSearcher(newIndexFolder)); + + //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace + var publishedMedia = store.GetDocumentById(GetUmbracoContext("/test", 1234), 3113); + var ancestors = publishedMedia.AncestorsOrSelf(); + Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 3113, 2112, 2222, 1111 })); + } + [Test] public void Children_Without_Examine() { @@ -233,4 +338,6 @@ namespace Umbraco.Tests.PublishedContent new[] { mSubChild1.Id, mChild1.Id, mRoot.Id })); } } + + } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs new file mode 100644 index 0000000000..4d3c1d1ce6 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.Designer.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.17929 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Umbraco.Tests.TestHelpers.ExamineHelpers { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExamineResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExamineResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Tests.TestHelpers.ExamineHelpers.ExamineResources", typeof(ExamineResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<media> + /// <node id="1111" version="902e13f7-5793-482a-9e06-cd94eebd1de0" parentID="-1" level="1" writerID="0" nodeType="1031" template="0" sortOrder="2" createDate="2010-05-19T15:26:08" updateDate="2010-05-19T15:26:09" nodeName="Product Images" urlName="productimages" writerName="Administrator" nodeTypeAlias="Folder" path="-1,1111"> + /// <data alias="contents"></data> + /// <node id="2222" version="902e13f7-5793-482a-9e06-cd94eebd1de0" parentID="-1" level="1" writerID="0" [rest of string was truncated]";. + /// + internal static string media { + get { + return ResourceManager.GetString("media", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8"?> + ///<!DOCTYPE root[ + /// <!ELEMENT CWS_Contact ANY> + /// <!ATTLIST CWS_Contact id ID #REQUIRED> + /// <!ELEMENT CWS_EmailAFriend ANY> + /// <!ATTLIST CWS_EmailAFriend id ID #REQUIRED> + /// <!ELEMENT CWS_EventItem ANY> + /// <!ATTLIST CWS_EventItem id ID #REQUIRED> + /// <!ELEMENT CWS_Galleries ANY> + /// <!ATTLIST CWS_Galleries id ID #REQUIRED> + /// <!ELEMENT CWS_Gallery ANY> + /// <!ATTLIST CWS_Gallery id ID #REQUIRED> + /// <!ELEMENT CWS_Home ANY> + /// <!ATTLIST CWS_Home id ID #REQUIRED> + /// <!ELEMENT CWS_NewsEven [rest of string was truncated]";. + /// + internal static string umbraco { + get { + return ResourceManager.GetString("umbraco", resourceCulture); + } + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx new file mode 100644 index 0000000000..4d91faaa26 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/ExamineResources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + media.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + umbraco.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs new file mode 100644 index 0000000000..bf295d7211 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/IndexInitializer.cs @@ -0,0 +1,360 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using System.Xml.XPath; +using Examine; +using Examine.LuceneEngine; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis.Standard; +using UmbracoExamine; +using UmbracoExamine.DataServices; + +namespace Umbraco.Tests.TestHelpers.ExamineHelpers +{ + /// + /// Used internally by test classes to initialize a new index from the template + /// + internal class IndexInitializer + { + + public IndexInitializer() + { + //ensure the umbraco.config and media.xml files exist at the location where we need to load them + var appData = Path.Combine(TestHelper.CurrentAssemblyDirectory, "App_Data"); + Directory.CreateDirectory(appData); + var umbConfig = Path.Combine(appData, "umbraco.config"); + File.Delete(umbConfig); + using (var s = File.CreateText(umbConfig)) + { + s.Write(ExamineResources.umbraco); + } + + var umbMedia = Path.Combine(appData, "media.xml"); + File.Delete(umbMedia); + using (var s = File.CreateText(umbMedia)) + { + s.Write(ExamineResources.media); + } + } + + public UmbracoContentIndexer GetUmbracoIndexer(DirectoryInfo d) + { + var i = new UmbracoContentIndexer(new IndexCriteria( + new[] + { + new TestIndexField { Name = "id", EnableSorting = true, Type = "Number" }, + new TestIndexField { Name = "nodeName", EnableSorting = true }, + new TestIndexField { Name = "updateDate", EnableSorting = true, Type = "DateTime" }, + new TestIndexField { Name = "writerName" }, + new TestIndexField { Name = "path" }, + new TestIndexField { Name = "nodeTypeAlias" }, + new TestIndexField { Name = "parentID" } + }, + Enumerable.Empty(), + //new[] + // { + // new TestIndexField { Name = "headerText" }, + // new TestIndexField { Name = "bodyText" }, + // new TestIndexField { Name = "metaDescription" }, + // new TestIndexField { Name = "metaKeywords" }, + // new TestIndexField { Name = "bodyTextColOne" }, + // new TestIndexField { Name = "bodyTextColTwo" }, + // new TestIndexField { Name = "xmlStorageTest" } + // }, + Enumerable.Empty(), + //new[] + // { + // "CWS_Home", + // "CWS_Textpage", + // "CWS_TextpageTwoCol", + // "CWS_NewsEventsList", + // "CWS_NewsItem", + // "CWS_Gallery", + // "CWS_EventItem", + // "Image", + // }, + new string[] { }, + -1), + d, + new TestDataService(), + new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), + false); + + //i.IndexSecondsInterval = 1; + + i.IndexingError += IndexingError; + + return i; + } + public UmbracoExamineSearcher GetUmbracoSearcher(DirectoryInfo d) + { + return new UmbracoExamineSearcher(d, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); + } + //public SimpleDataIndexer GetSimpleIndexer(DirectoryInfo d) + //{ + // var i = new SimpleDataIndexer(new IndexCriteria( + // new IIndexField[] { }, + // new[] + // { + // new TestIndexField { Name = "Author" }, + // new TestIndexField { Name = "DateCreated", EnableSorting = true, Type = "DateTime" }, + // new TestIndexField { Name = "Title" }, + // new TestIndexField { Name = "Photographer" }, + // new TestIndexField { Name = "YearCreated", Type = "Date.Year" }, + // new TestIndexField { Name = "MonthCreated", Type = "Date.Month" }, + // new TestIndexField { Name = "DayCreated", Type = "Date.Day" }, + // new TestIndexField { Name = "HourCreated", Type = "Date.Hour" }, + // new TestIndexField { Name = "MinuteCreated", Type = "Date.Minute" }, + // new TestIndexField { Name = "SomeNumber", Type = "Number" }, + // new TestIndexField { Name = "SomeFloat", Type = "Float" }, + // new TestIndexField { Name = "SomeDouble", Type = "Double" }, + // new TestIndexField { Name = "SomeLong", Type = "Long" } + // }, + // new string[] { }, + // new string[] { }, + // -1), + // d, + // new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), + // new TestSimpleDataProvider(), + // new[] { "Documents", "Pictures" }, + // false); + // i.IndexingError += IndexingError; + + // return i; + //} + public LuceneSearcher GetLuceneSearcher(DirectoryInfo d) + { + return new LuceneSearcher(d, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); + } + //public PDFIndexer GetPdfIndexer(DirectoryInfo d) + //{ + // var i = new PDFIndexer(d, + // new TestDataService(), + // new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), + // false); + + // i.IndexingError += IndexingError; + + // return i; + //} + public MultiIndexSearcher GetMultiSearcher(DirectoryInfo pdfDir, DirectoryInfo simpleDir, DirectoryInfo conventionDir, DirectoryInfo cwsDir) + { + var i = new MultiIndexSearcher(new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29)); + return i; + } + + + internal void IndexingError(object sender, IndexingErrorEventArgs e) + { + throw new ApplicationException(e.Message, e.InnerException); + } + + internal class TestIndexField : IIndexField + { + public string Name { get; set; } + public bool EnableSorting { get; set; } + public string Type { get; set; } + } + + internal class TestDataService : IDataService + { + + public TestDataService() + { + ContentService = new TestContentService(); + LogService = new TestLogService(); + MediaService = new TestMediaService(); + } + + #region IDataService Members + + public IContentService ContentService { get; private set; } + + public ILogService LogService { get; private set; } + + public IMediaService MediaService { get; private set; } + + public string MapPath(string virtualPath) + { + return new DirectoryInfo(TestHelper.CurrentAssemblyDirectory) + "\\" + virtualPath.Replace("/", "\\"); + } + + #endregion + } + + /// + /// A mock data service used to return content from the XML data file created with CWS + /// + internal class TestContentService : IContentService + { + public const int ProtectedNode = 1142; + + public TestContentService() + { + var xmlFile = new DirectoryInfo(TestHelper.CurrentAssemblyDirectory).GetDirectories("App_Data") + .Single() + .GetFiles("umbraco.config") + .Single(); + + _xDoc = XDocument.Load(xmlFile.FullName); + } + + #region IContentService Members + + /// + /// Return the XDocument containing the xml from the umbraco.config xml file + /// + /// + /// + /// + /// This is no different in the test suite as published content + /// + public XDocument GetLatestContentByXPath(string xpath) + { + var xdoc = XDocument.Parse(""); + xdoc.Root.Add(_xDoc.XPathSelectElements(xpath)); + + return xdoc; + } + + /// + /// Return the XDocument containing the xml from the umbraco.config xml file + /// + /// + /// + public XDocument GetPublishedContentByXPath(string xpath) + { + var xdoc = XDocument.Parse(""); + xdoc.Root.Add(_xDoc.XPathSelectElements(xpath)); + + return xdoc; + } + + public string StripHtml(string value) + { + const string pattern = @"<(.|\n)*?>"; + return Regex.Replace(value, pattern, string.Empty); + } + + public bool IsProtected(int nodeId, string path) + { + // single node is marked as protected for test indexer + // hierarchy is not important for this test + return nodeId == ProtectedNode; + } + + public IEnumerable GetAllUserPropertyNames() + { + return GetPublishedContentByXPath("//*[count(@id)>0]") + .Root + .Elements() + .Select(x => x.Name.LocalName) + .ToList(); + } + + public IEnumerable GetAllSystemPropertyNames() + { + return new Dictionary() + { + {"id", FieldIndexTypes.NOT_ANALYZED}, + {"version", FieldIndexTypes.NOT_ANALYZED}, + {"parentID", FieldIndexTypes.NOT_ANALYZED}, + {"level", FieldIndexTypes.NOT_ANALYZED}, + {"writerID", FieldIndexTypes.NOT_ANALYZED}, + {"creatorID", FieldIndexTypes.NOT_ANALYZED}, + {"nodeType", FieldIndexTypes.NOT_ANALYZED}, + {"template", FieldIndexTypes.NOT_ANALYZED}, + {"sortOrder", FieldIndexTypes.NOT_ANALYZED}, + {"createDate", FieldIndexTypes.NOT_ANALYZED}, + {"updateDate", FieldIndexTypes.NOT_ANALYZED}, + {"nodeName", FieldIndexTypes.ANALYZED}, + {"urlName", FieldIndexTypes.NOT_ANALYZED}, + {"writerName", FieldIndexTypes.ANALYZED}, + {"creatorName", FieldIndexTypes.ANALYZED}, + {"nodeTypeAlias", FieldIndexTypes.ANALYZED}, + {"path", FieldIndexTypes.NOT_ANALYZED} + }.Select(x => x.Key); + } + + #endregion + + private readonly XDocument _xDoc; + + + + + + + + } + + internal class TestLogService : ILogService + { + #region ILogService Members + + public string ProviderName { get; set; } + + public void AddErrorLog(int nodeId, string msg) + { + Trace.WriteLine("ERROR: (" + nodeId.ToString() + ") " + msg); + } + + public void AddInfoLog(int nodeId, string msg) + { + Trace.WriteLine("INFO: (" + nodeId.ToString() + ") " + msg); + } + + public void AddVerboseLog(int nodeId, string msg) + { + if (LogLevel == LoggingLevel.Verbose) + Trace.WriteLine("VERBOSE: (" + nodeId.ToString() + ") " + msg); + } + + public LoggingLevel LogLevel + { + get + { + return LoggingLevel.Verbose; + } + set + { + //do nothing + } + } + + #endregion + } + + internal class TestMediaService : IMediaService + { + + public TestMediaService() + { + var xmlFile = new DirectoryInfo(TestHelper.CurrentAssemblyDirectory).GetDirectories("App_Data") + .Single() + .GetFiles("media.xml") + .Single(); + + m_Doc = XDocument.Load(xmlFile.FullName); + } + + #region IMediaService Members + + public System.Xml.Linq.XDocument GetLatestMediaByXpath(string xpath) + { + var xdoc = XDocument.Parse(""); + xdoc.Root.Add(m_Doc.XPathSelectElements(xpath)); + return xdoc; + } + + #endregion + + private XDocument m_Doc; + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml new file mode 100644 index 0000000000..dd645c6d71 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/ExamineHelpers/media.xml @@ -0,0 +1,51 @@ + + + + + + + + + 115 + 268 + 10726 + jpg + + + 115 + 268 + 10726 + jpg + + + + + + 115 + 268 + 10726 + jpg + + + + 115 + 268 + 10726 + jpg + + + + 115 + 268 + 10726 + jpg + + + + 115 + 268 + 10726 + jpg + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 204fe819af..49aa724605 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -51,7 +51,7 @@ namespace Umbraco.Tests.TestHelpers { get { - var codeBase = Assembly.GetCallingAssembly().CodeBase; + var codeBase = typeof(TestHelper).Assembly.CodeBase; var uri = new Uri(codeBase); var path = uri.LocalPath; return Path.GetDirectoryName(path); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 72528a7163..6b0607a1f0 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -35,6 +35,10 @@ ..\packages\log4net.2.0.0\lib\net40-full\log4net.dll + + False + ..\Umbraco.Web.UI\bin\Lucene.Net.dll + ..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll @@ -51,6 +55,10 @@ + + False + ..\Umbraco.Web.UI\bin\UmbracoExamine.dll + @@ -97,6 +105,12 @@ + + True + True + ExamineResources.resx + + @@ -128,6 +142,7 @@ + Always @@ -178,9 +193,15 @@ Umbraco.Web - + + + ResXFileCodeGenerator + ExamineResources.Designer.cs + + + diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index b6675e01fa..79eed8ef72 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -5,6 +5,8 @@ using System.IO; using System.Linq; using System.Xml.XPath; using Examine; +using Examine.Providers; +using Lucene.Net.Documents; using Umbraco.Core; using Umbraco.Core.Dynamics; using Umbraco.Core.Models; @@ -21,6 +23,22 @@ namespace Umbraco.Web /// internal class DefaultPublishedMediaStore : IPublishedMediaStore { + + public DefaultPublishedMediaStore() + { + } + + /// + /// Generally used for unit testing to use an explicit examine searcher + /// + /// + internal DefaultPublishedMediaStore(BaseSearchProvider searchProvider) + { + _searchProvider = searchProvider; + } + + private readonly BaseSearchProvider _searchProvider; + public virtual IPublishedContent GetDocumentById(UmbracoContext umbracoContext, int nodeId) { return GetUmbracoMedia(nodeId); @@ -53,21 +71,42 @@ namespace Umbraco.Web } } - private IPublishedContent GetUmbracoMedia(int id) + private BaseSearchProvider GetSearchProviderSafe() { + if (_searchProvider != null) + return _searchProvider; + var eMgr = GetExamineManagerSafe(); if (eMgr != null) + { + try + { + //by default use the InternalSearcher + return eMgr.SearchProviderCollection["InternalSearcher"]; + } + catch (FileNotFoundException) + { + //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco + //See this thread: http://examine.cdodeplex.com/discussions/264341 + //Catch the exception here for the time being, and just fallback to GetMedia + //TODO: Need to fix examine in LB scenarios! + } + } + return null; + } + + private IPublishedContent GetUmbracoMedia(int id) + { + var searchProvider = GetSearchProviderSafe(); + + if (searchProvider != null) { try { //first check in Examine as this is WAY faster - var criteria = eMgr - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); + var criteria = searchProvider.CreateSearchCriteria("media"); var filter = criteria.Id(id); - var results = eMgr - .SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); + var results = searchProvider.Search(filter.Compile()); if (results.Any()) { return ConvertFromSearchResult(results.First()); @@ -86,13 +125,6 @@ namespace Umbraco.Web if (media != null && media.Current != null) { media.MoveNext(); - ////error check - //if (media.Current.MoveToFirstChild() && media.Current.Name.InvariantEquals("error")) - //{ - // return null; - //} - //var current = media.Current; - return ConvertFromXPathNavigator(media.Current); } @@ -129,15 +161,13 @@ namespace Umbraco.Web return new DictionaryPublishedContent(values, - d => d.ParentId != -1 //parent should be null if -1 - ? GetUmbracoMedia(d.ParentId) - : null, - //callback to return the children of the current node - d => GetChildrenMedia(d.ParentId), - GetProperty) - { - LoadedFromExamine = true - }; + d => d.ParentId != -1 //parent should be null if -1 + ? GetUmbracoMedia(d.ParentId) + : null, + //callback to return the children of the current node + d => GetChildrenMedia(d.Id), + GetProperty, + true); } internal IPublishedContent ConvertFromXPathNavigator(XPathNavigator xpath) @@ -193,9 +223,9 @@ namespace Umbraco.Web ? GetUmbracoMedia(d.ParentId) : null, //callback to return the children of the current node based on the xml structure already found - //d => GetChildrenMedia(d.ParentId, xpath), d => GetChildrenMedia(d.Id, xpath), - GetProperty); + GetProperty, + false); } /// @@ -254,20 +284,16 @@ namespace Umbraco.Web //if there is no navigator, try examine first, then re-look it up if (xpath == null) { - var eMgr = GetExamineManagerSafe(); + var searchProvider = GetSearchProviderSafe(); - if (eMgr != null) + if (searchProvider != null) { try { //first check in Examine as this is WAY faster - var criteria = eMgr - .SearchProviderCollection["InternalSearcher"] - .CreateSearchCriteria("media"); + var criteria = searchProvider.CreateSearchCriteria("media"); var filter = criteria.ParentId(parentId); - var results = eMgr - .SearchProviderCollection["InternalSearcher"] - .Search(filter.Compile()); + var results = searchProvider.Search(filter.Compile()); if (results.Any()) { return results.Select(ConvertFromSearchResult); @@ -332,7 +358,8 @@ namespace Umbraco.Web IDictionary valueDictionary, Func getParent, Func> getChildren, - Func getProperty) + Func getProperty, + bool fromExamine) { if (valueDictionary == null) throw new ArgumentNullException("valueDictionary"); if (getParent == null) throw new ArgumentNullException("getParent"); @@ -342,7 +369,7 @@ namespace Umbraco.Web _getChildren = getChildren; _getProperty = getProperty; - LoadedFromExamine = false; //default to false + LoadedFromExamine = fromExamine; ValidateAndSetProperty(valueDictionary, val => Id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! ValidateAndSetProperty(valueDictionary, val => TemplateId = int.Parse(val), "template", "templateId"); @@ -356,8 +383,8 @@ namespace Umbraco.Web ValidateAndSetProperty(valueDictionary, val => WriterId = int.Parse(val), "writerID"); ValidateAndSetProperty(valueDictionary, val => CreatorId = int.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 ValidateAndSetProperty(valueDictionary, val => Path = val, "path", "__Path"); - ValidateAndSetProperty(valueDictionary, val => CreateDate = DateTime.Parse(val), "createDate"); - ValidateAndSetProperty(valueDictionary, val => UpdateDate = DateTime.Parse(val), "updateDate"); + ValidateAndSetProperty(valueDictionary, val => CreateDate = ParseDateTimeValue(val), "createDate"); + ValidateAndSetProperty(valueDictionary, val => UpdateDate = ParseDateTimeValue(val), "updateDate"); ValidateAndSetProperty(valueDictionary, val => Level = int.Parse(val), "level"); ValidateAndSetProperty(valueDictionary, val => { @@ -381,10 +408,28 @@ namespace Umbraco.Web } } + private DateTime ParseDateTimeValue(string val) + { + if (LoadedFromExamine) + { + try + { + //we might need to parse the date time using Lucene converters + return DateTools.StringToDate(val); + } + catch (FormatException) + { + //swallow exception, its not formatted correctly so revert to just trying to parse + } + } + + return DateTime.Parse(val); + } + /// /// Flag to get/set if this was laoded from examine cache /// - internal bool LoadedFromExamine { get; set; } + internal bool LoadedFromExamine { get; private set; } private readonly Func _getParent; private readonly Func> _getChildren; From 8be97295dc00982ef12d49b33ca6fa73c9443bf3 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Fri, 16 Nov 2012 00:38:03 +0500 Subject: [PATCH 15/21] Added new method to RenderMvcController for easier return of the current template when hijacking routes. Added better error checking for media handling in MVC. --- src/Umbraco.Web/DefaultPublishedMediaStore.cs | 14 ++++++++++++++ src/Umbraco.Web/Mvc/RenderMvcController.cs | 19 ++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs index 79eed8ef72..4b0909936f 100644 --- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs +++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs @@ -125,6 +125,20 @@ namespace Umbraco.Web if (media != null && media.Current != null) { media.MoveNext(); + var moved = media.Current.MoveToFirstChild(); + //first check if we have an error + if (moved) + { + if (media.Current.Name.InvariantEquals("error")) + { + return null; + } + } + if (moved) + { + //move back to the parent and return + media.Current.MoveToParent(); + } return ConvertFromXPathNavigator(media.Current); } diff --git a/src/Umbraco.Web/Mvc/RenderMvcController.cs b/src/Umbraco.Web/Mvc/RenderMvcController.cs index 08469ef9ff..9a7d0a4f64 100644 --- a/src/Umbraco.Web/Mvc/RenderMvcController.cs +++ b/src/Umbraco.Web/Mvc/RenderMvcController.cs @@ -61,20 +61,33 @@ namespace Umbraco.Web.Mvc } /// - /// The default action to render the front-end view + /// Returns an ActionResult based on the template name found in the route values and the given model. /// + /// /// /// - public virtual ActionResult Index(RenderModel model) + /// + /// If the template found in the route values doesn't physically exist, then an empty ContentResult will be returned. + /// + protected ActionResult CurrentTemplate(T model) { var template = ControllerContext.RouteData.Values["action"].ToString(); if (!EnsurePhsyicalViewExists(template)) { return Content(""); } - return View(template, model); } + /// + /// The default action to render the front-end view + /// + /// + /// + public virtual ActionResult Index(RenderModel model) + { + return CurrentTemplate(model); + } + } } \ No newline at end of file From 4cb03ab36ce3357683a4cb5ad45218c92ad0781a Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Fri, 16 Nov 2012 10:08:12 +0500 Subject: [PATCH 16/21] Fixes issue with ChildActions when inherting from UmbracoViewPage and using the @Umbraco helper. --- src/Umbraco.Web/Mvc/UmbracoViewPage.cs | 46 ++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPage.cs b/src/Umbraco.Web/Mvc/UmbracoViewPage.cs index ed3c18e181..ff926fd4b4 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPage.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPage.cs @@ -19,7 +19,28 @@ namespace Umbraco.Web.Mvc /// public UmbracoContext UmbracoContext { - get { return (UmbracoContext) ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); } + get + { + //we should always try to return the context from the data tokens just in case its a custom context and not + //using the UmbracoContext.Current. + //we will fallback to the singleton if necessary. + if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-context")) + { + return (UmbracoContext)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); + } + //next check if it is a child action and see if the parent has it set in data tokens + if (ViewContext.IsChildAction) + { + if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-context")) + { + return (UmbracoContext)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context"); + } + } + + //lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this + //class and are rendering it outside of the normal Umbraco routing process. Very unlikely. + return UmbracoContext.Current; + } } /// @@ -35,7 +56,28 @@ namespace Umbraco.Web.Mvc /// internal PublishedContentRequest PublishedContentRequest { - get { return (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); } + get + { + //we should always try to return the object from the data tokens just in case its a custom object and not + //using the UmbracoContext.Current. + //we will fallback to the singleton if necessary. + if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request")) + { + return (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); + } + //next check if it is a child action and see if the parent has it set in data tokens + if (ViewContext.IsChildAction) + { + if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request")) + { + return (PublishedContentRequest)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request"); + } + } + + //lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this + //class and are rendering it outside of the normal Umbraco routing process. Very unlikely. + return UmbracoContext.Current.PublishedContentRequest; + } } private UmbracoHelper _helper; From 3ba9e09164ee70b6044c408cfc4e13df5647e90c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 16 Nov 2012 14:41:30 -0100 Subject: [PATCH 17/21] Update obsolete messages to point to the correct classes (instead of being misleading) :-) --- .../ApplicationStartupHandler.cs | 2 +- src/umbraco.cms/businesslogic/cache/Cache.cs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/umbraco.businesslogic/ApplicationStartupHandler.cs b/src/umbraco.businesslogic/ApplicationStartupHandler.cs index 0e805a45fd..1a9858bec7 100644 --- a/src/umbraco.businesslogic/ApplicationStartupHandler.cs +++ b/src/umbraco.businesslogic/ApplicationStartupHandler.cs @@ -17,7 +17,7 @@ namespace umbraco.businesslogic /// Class inhiriting from ApplicationStartupHandler are automaticly registered and instantiated by umbraco on application start. /// To use, inhirite the ApplicationStartupHandler Class and add an empty constructor. /// - [Obsolete("This class is no longer used, implement IApplicationEvents instead")] + [Obsolete("This class is no longer used, implement IApplicationEventHandler instead")] public abstract class ApplicationStartupHandler : IApplicationStartupHandler { diff --git a/src/umbraco.cms/businesslogic/cache/Cache.cs b/src/umbraco.cms/businesslogic/cache/Cache.cs index 5cbbdf0db8..926a3d8b07 100644 --- a/src/umbraco.cms/businesslogic/cache/Cache.cs +++ b/src/umbraco.cms/businesslogic/cache/Cache.cs @@ -22,7 +22,7 @@ namespace umbraco.cms.businesslogic.cache /// umbraco content is removed, but also other cache items from pages running in /// the same application / website. Use with care :-) /// - [Obsolete("Use the ApplicationContext.Cache.ClearAllCache instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.ClearAllCache instead")] public static void ClearAllCache() { var helper = new CacheHelper(System.Web.HttpRuntime.Cache); @@ -33,7 +33,7 @@ namespace umbraco.cms.businesslogic.cache /// Clears the item in umbraco's runtime cache with the given key /// /// Key - [Obsolete("Use the ApplicationContext.Cache.ClearCacheItem instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.ClearCacheItem instead")] public static void ClearCacheItem(string key) { var helper = new CacheHelper(System.Web.HttpRuntime.Cache); @@ -46,7 +46,7 @@ namespace umbraco.cms.businesslogic.cache /// input parameter. (using [object].GetType()) /// /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" - [Obsolete("Use the ApplicationContext.Cache.ClearCacheObjectTypes instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.ClearCacheObjectTypes instead")] public static void ClearCacheObjectTypes(string TypeName) { var helper = new CacheHelper(System.Web.HttpRuntime.Cache); @@ -57,7 +57,7 @@ namespace umbraco.cms.businesslogic.cache /// Clears all cache items that starts with the key passed. /// /// The start of the key - [Obsolete("Use the ApplicationContext.Cache.ClearCacheByKeySearch instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch instead")] public static void ClearCacheByKeySearch(string KeyStartsWith) { var helper = new CacheHelper(System.Web.HttpRuntime.Cache); @@ -88,22 +88,22 @@ namespace umbraco.cms.businesslogic.cache public delegate TT GetCacheItemDelegate(); - [Obsolete("Use the ApplicationContext.Cache.GetCacheItem instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.GetCacheItem instead")] public static TT GetCacheItem(string cacheKey, object syncLock, TimeSpan timeout, GetCacheItemDelegate getCacheItem) { return GetCacheItem(cacheKey, syncLock, null, timeout, getCacheItem); } - [Obsolete("Use the ApplicationContext.Cache.GetCacheItem instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.GetCacheItem instead")] public static TT GetCacheItem(string cacheKey, object syncLock, CacheItemRemovedCallback refreshAction, TimeSpan timeout, GetCacheItemDelegate getCacheItem) { return GetCacheItem(cacheKey, syncLock, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); } - - [Obsolete("Use the ApplicationContext.Cache.GetCacheItem instead")] + + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.GetCacheItem instead")] public static TT GetCacheItem(string cacheKey, object syncLock, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout, GetCacheItemDelegate getCacheItem) @@ -111,7 +111,7 @@ namespace umbraco.cms.businesslogic.cache return GetCacheItem(cacheKey, syncLock, priority, refreshAction, null, timeout, getCacheItem); } - [Obsolete("Use the ApplicationContext.Cache.GetCacheItem instead")] + [Obsolete("Use the ApplicationContext.Current.ApplicationCache.GetCacheItem instead")] public static TT GetCacheItem(string cacheKey, object syncLock, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan timeout, GetCacheItemDelegate getCacheItem) From e3efc0d19ba0f075608cc822be7d1bfa4e106c2a Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 16 Nov 2012 15:52:14 -0100 Subject: [PATCH 18/21] U4-1194 - Mvc templates do not work when running in a virtual directory --- src/Umbraco.Web/UmbracoModule.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index cad2639599..bc021fc761 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -319,11 +319,8 @@ namespace Umbraco.Web switch (engine) { case RenderingEngine.Mvc: - //the Path is normally ~/umbraco but we need to remove the start ~/ of it and if someone modifies this - //then we should be rendering the MVC stuff in that location. - rewritePath = "~/" - + GlobalSettings.Path.TrimStart(new[] { '~', '/' }).TrimEnd(new[] { '/' }) - + "/RenderMvc"; + // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any) + rewritePath = GlobalSettings.Path.TrimEnd(new[] { '/' }) + "/RenderMvc"; // we rewrite the path to the path of the handler (i.e. default.aspx or /umbraco/RenderMvc ) context.RewritePath(rewritePath, "", currentQuery.TrimStart(new[] { '?' }), false); From 1b54ce1d48c04a5e06f5df92208250f15e85b977 Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 17 Nov 2012 12:29:05 -0100 Subject: [PATCH 19/21] U4-1210 - ysod on rollback --- src/umbraco.cms/businesslogic/web/Document.cs | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index b2d836c89e..438ddf95d7 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -402,12 +402,11 @@ namespace umbraco.cms.businesslogic.web tmp.CreateContent(dct); //now create the document data - SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, updateDate, Text) values (1, " + - tmp.Id + ", 0, " + - u.Id + ", @versionId, @updateDate, @text)", - SqlHelper.CreateParameter("@versionId", tmp.Version), - SqlHelper.CreateParameter("@updateDate", DateTime.Now), - SqlHelper.CreateParameter("@text", tmp.Text)); + SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, updateDate, Text) " + + "values (1, " + tmp.Id + ", 0, " + u.Id + ", @versionId, @updateDate, @text)", + SqlHelper.CreateParameter("@versionId", tmp.Version), + SqlHelper.CreateParameter("@updateDate", DateTime.Now), + SqlHelper.CreateParameter("@text", tmp.Text)); //read the whole object from the db Document d = new Document(newId); @@ -1005,13 +1004,15 @@ namespace umbraco.cms.businesslogic.web //PPH make sure that there is only 1 newest node, this is important in regard to schedueled publishing... SqlHelper.ExecuteNonQuery("update cmsDocument set newest = 0 where nodeId = " + Id); - SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, updateDate, Text, TemplateId) values (1,@id, 0, @userId, @versionId, @updateDate, @text, @template)", + SqlHelper.ExecuteNonQuery("insert into cmsDocument (newest, nodeId, published, documentUser, versionId, updateDate, Text, TemplateId) " + + "values (1, @id, 0, @userId, @versionId, @updateDate, @text, @template)", SqlHelper.CreateParameter("@id", Id), - SqlHelper.CreateParameter("@template", _template > 0 ? (object)_template : (object)DBNull.Value), //pass null in if the template doesn't have a valid id SqlHelper.CreateParameter("@userId", u.Id), SqlHelper.CreateParameter("@versionId", newVersion), SqlHelper.CreateParameter("@updateDate", versionDate), - SqlHelper.CreateParameter("@text", Text)); + SqlHelper.CreateParameter("@text", Text), + SqlHelper.CreateParameter("@template", _template > 0 ? (object)_template : (object)DBNull.Value) //pass null in if the template doesn't have a valid id + ); SqlHelper.ExecuteNonQuery("update cmsDocument set published = 0 where nodeId = " + Id); SqlHelper.ExecuteNonQuery("update cmsDocument set published = 1, newest = 0 where versionId = @versionId", @@ -1072,22 +1073,24 @@ namespace umbraco.cms.businesslogic.web if (_template != 0) { - SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text, TemplateId) values (" + - Id + - ", 0, " + u.Id + ", @versionId, @text, " + - _template + ")", - SqlHelper.CreateParameter("@versionId", newVersion), - SqlHelper.CreateParameter("@updateDate", versionDate), - SqlHelper.CreateParameter("@text", Text)); + SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text, TemplateId) " + + "values (@nodeId, 0, @userId, @versionId, @updateDate, @text, @templateId)", + SqlHelper.CreateParameter("@nodeId", Id), + SqlHelper.CreateParameter("@userId", u.Id), + SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), + SqlHelper.CreateParameter("@text", Text), + SqlHelper.CreateParameter("@templateId", _template)); } else { - SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text) values (" + - Id + - ", 0, " + u.Id + ", @versionId, @text )", - SqlHelper.CreateParameter("@versionId", newVersion), - SqlHelper.CreateParameter("@updateDate", versionDate), - SqlHelper.CreateParameter("@text", Text)); + SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text) " + + "values (@nodeId, 0, @userId, @versionId, @updateDate, @text)", + SqlHelper.CreateParameter("@nodeId", Id), + SqlHelper.CreateParameter("@userId", u.Id), + SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), + SqlHelper.CreateParameter("@text", Text)); } // Get new version @@ -1134,12 +1137,11 @@ namespace umbraco.cms.businesslogic.web DateTime versionDate = DateTime.Now; Guid newVersion = createNewVersion(versionDate); - SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text) values (" + - Id + ", 0, " + u.Id + - ", @versionId, @text)", - SqlHelper.CreateParameter("@versionId", newVersion), - SqlHelper.CreateParameter("@updateDate", versionDate), - SqlHelper.CreateParameter("@text", Text)); + SqlHelper.ExecuteNonQuery("insert into cmsDocument (nodeId, published, documentUser, versionId, updateDate, Text) " + + "values (" + Id + ", 0, " + u.Id + ", @versionId, @text)", + SqlHelper.CreateParameter("@versionId", newVersion), + SqlHelper.CreateParameter("@updateDate", versionDate), + SqlHelper.CreateParameter("@text", Text)); SqlHelper.ExecuteNonQuery("update cmsDocument set published = 0 where nodeId = " + Id); SqlHelper.ExecuteNonQuery("update cmsDocument set published = 1 where versionId = @versionId", SqlHelper.CreateParameter("@versionId", tempVersion)); From b9d814c3fb6993075afd47f24865a85071c9204f Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 17 Nov 2012 13:28:38 -0100 Subject: [PATCH 20/21] (michielvoo pull request) Consitent new lines in templates --- .../businesslogic/template/MasterpageHelper.cs | 10 +++++----- src/umbraco.cms/businesslogic/template/Template.cs | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/umbraco.cms/businesslogic/template/MasterpageHelper.cs b/src/umbraco.cms/businesslogic/template/MasterpageHelper.cs index 2c68335247..54d8153015 100644 --- a/src/umbraco.cms/businesslogic/template/MasterpageHelper.cs +++ b/src/umbraco.cms/businesslogic/template/MasterpageHelper.cs @@ -13,6 +13,7 @@ namespace umbraco.cms.businesslogic.template internal class MasterPageHelper { internal static readonly string DefaultMasterTemplate = SystemDirectories.Umbraco + "/masterpages/default.master"; + private static readonly char[] NewLineChars = Environment.NewLine.ToCharArray(); internal static bool MasterPageExists(Template t) { @@ -77,14 +78,13 @@ namespace umbraco.cms.businesslogic.template // Add header to master page if it doesn't exist if (!masterPageContent.TrimStart().StartsWith("<%@")) { - masterPageContent = GetMasterPageHeader(template) + "\n" + masterPageContent; + masterPageContent = GetMasterPageHeader(template) + Environment.NewLine + masterPageContent; } else { // verify that the masterpage attribute is the same as the masterpage string masterHeader = - masterPageContent.Substring(0, masterPageContent.IndexOf("%>") + 2).Trim( - Environment.NewLine.ToCharArray()); + masterPageContent.Substring(0, masterPageContent.IndexOf("%>") + 2).Trim(NewLineChars); // find the masterpagefile attribute MatchCollection m = Regex.Matches(masterHeader, "(?\\S*)=\"(?[^\"]*)\"", @@ -140,7 +140,7 @@ namespace umbraco.cms.businesslogic.template internal static string ConvertToMasterPageSyntax(Template template) { - string masterPageContent = GetMasterContentElement(template) + "\n"; + string masterPageContent = GetMasterContentElement(template) + Environment.NewLine; masterPageContent += template.Design; @@ -148,7 +148,7 @@ namespace umbraco.cms.businesslogic.template masterPageContent = EnsureMasterPageSyntax(template.Alias, masterPageContent); // append ending asp:content element - masterPageContent += "\n" + Environment.NewLine; + masterPageContent += Environment.NewLine + "" + Environment.NewLine; return masterPageContent; } diff --git a/src/umbraco.cms/businesslogic/template/Template.cs b/src/umbraco.cms/businesslogic/template/Template.cs index 32763615c9..9a5e8368bd 100644 --- a/src/umbraco.cms/businesslogic/template/Template.cs +++ b/src/umbraco.cms/businesslogic/template/Template.cs @@ -42,6 +42,7 @@ namespace umbraco.cms.businesslogic.template private static readonly string UmbracoTemplateCacheKey = "UmbracoTemplateCache"; private static object _templateLoaderLocker = new object(); private static Guid _objectType = new Guid("6fbde604-4178-42ce-a10b-8a2600a2f07d"); + private static readonly char[] NewLineChars = Environment.NewLine.ToCharArray(); #endregion @@ -262,7 +263,7 @@ namespace umbraco.cms.businesslogic.template { FlushCache(); - _design = value.Trim(Environment.NewLine.ToCharArray()); + _design = value.Trim(NewLineChars); //we only switch to MVC View editing if the template has a view file, and MVC editing is enabled if (Umbraco.Core.Configuration.UmbracoSettings.DefaultRenderingEngine == RenderingEngine.Mvc && !MasterPageHelper.IsMasterPageSyntax(_design)) From b02afb3af80afc46f08991f815e31bffbe6b333e Mon Sep 17 00:00:00 2001 From: Stephan Date: Sat, 17 Nov 2012 13:45:53 -0100 Subject: [PATCH 21/21] u4-1186 - Manage hostnames UI bug with validators --- .../umbraco/dialogs/AssignDomain.aspx | 9 ++++---- .../umbraco/dialogs/AssignDomain.aspx | 9 ++++---- .../dialogs/AssignDomain.aspx.designer.cs | 21 +++++++++---------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/AssignDomain.aspx b/src/Umbraco.Web.UI/umbraco/dialogs/AssignDomain.aspx index cdf12bfcdd..7154a241c1 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/AssignDomain.aspx +++ b/src/Umbraco.Web.UI/umbraco/dialogs/AssignDomain.aspx @@ -21,14 +21,13 @@ - -
<%= umbraco.ui.Text("assignDomain", "domainHelp") %> + +
<%= umbraco.ui.Text("assignDomain", "domainHelp") %>
- - + + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx index cdf12bfcdd..7154a241c1 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx @@ -21,14 +21,13 @@ - -
<%= umbraco.ui.Text("assignDomain", "domainHelp") %> + +
<%= umbraco.ui.Text("assignDomain", "domainHelp") %>
- - + + diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx.designer.cs index 656f9d04e6..6b4692dbdd 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx.designer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/AssignDomain.aspx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.4200 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -67,15 +66,6 @@ namespace umbraco.dialogs { /// protected global::umbraco.uicontrols.PropertyPanel prop_lang; - /// - /// LanguageValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator LanguageValidator; - /// /// Languages control. /// @@ -85,6 +75,15 @@ namespace umbraco.dialogs { /// protected global::System.Web.UI.WebControls.DropDownList Languages; + /// + /// LanguageValidator control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator LanguageValidator; + /// /// ok control. ///