diff --git a/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs b/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs index 7bde2ffddd..035a76d052 100644 --- a/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs +++ b/src/Umbraco.Tests/FrontEnd/UmbracoHelperTests.cs @@ -11,6 +11,7 @@ namespace Umbraco.Tests.FrontEnd [TestFixture] public class UmbracoHelperTests { + [Test] public void Truncate_Simple() { diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index e7fdfea54d..dcb590889b 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -3,10 +3,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; +using System.Web.Security; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; @@ -15,6 +18,8 @@ using Umbraco.Core.Services; using Moq; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; namespace Umbraco.Tests { @@ -76,16 +81,75 @@ namespace Umbraco.Tests public void Can_Get_Umbraco_Context() { var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - ApplicationContext.EnsureContext(appCtx, true); var umbCtx = UmbracoContext.EnsureContext( - new Mock().Object, + Mock.Of(), appCtx, + new Mock(null, null).Object, Mock.Of(), + Enumerable.Empty(), true); Assert.AreEqual(umbCtx, UmbracoContext.Current); } + [Test] + public void Can_Mock_Umbraco_Helper() + { + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); + + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + + var helper = new UmbracoHelper(umbCtx, + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + new UrlProvider(umbCtx, new[] {Mock.Of()}, UrlProviderMode.Auto), Mock.Of(), + Mock.Of(), + new MembershipHelper(umbCtx, Mock.Of(), Mock.Of())); + + Assert.Pass(); + } + + [Test] + public void Can_Mock_Umbraco_Helper_Get_Url() + { + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); + + var umbCtx = UmbracoContext.EnsureContext( + Mock.Of(), + appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), + true); + + var urlHelper = new Mock(); + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns("/hello/world/1234"); + + var helper = new UmbracoHelper(umbCtx, + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + new UrlProvider(umbCtx, new[] + { + urlHelper.Object + }, UrlProviderMode.Auto), Mock.Of(), + Mock.Of(), + new MembershipHelper(umbCtx, Mock.Of(), Mock.Of())); + + Assert.AreEqual("/hello/world/1234", helper.Url(1234)); + } } } diff --git a/src/Umbraco.Tests/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Mvc/SurfaceControllerTests.cs index b4fd390ac4..317ad418f1 100644 --- a/src/Umbraco.Tests/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Mvc/SurfaceControllerTests.cs @@ -1,16 +1,26 @@ using System.CodeDom; +using System.Linq; using System.Web; using System.Web.Mvc; +using System.Web.Security; using Moq; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Dictionary; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.ObjectResolution; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Profiling; +using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; namespace Umbraco.Tests.Mvc { @@ -21,11 +31,13 @@ namespace Umbraco.Tests.Mvc public void Can_Construct_And_Get_Result() { var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - ApplicationContext.EnsureContext(appCtx, true); var umbCtx = UmbracoContext.EnsureContext( new Mock().Object, appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), true); var ctrl = new TestSurfaceController(umbCtx); @@ -44,6 +56,9 @@ namespace Umbraco.Tests.Mvc var umbCtx = UmbracoContext.EnsureContext( new Mock().Object, appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), true); var ctrl = new TestSurfaceController(umbCtx); @@ -54,12 +69,18 @@ namespace Umbraco.Tests.Mvc [Test] public void Umbraco_Helper_Not_Null() { - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - ApplicationContext.EnsureContext(appCtx, true); - + var appCtx = new ApplicationContext( + new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), + MockHelper.GetMockedServiceContext(), + CacheHelper.CreateDisabledCacheHelper(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + var umbCtx = UmbracoContext.EnsureContext( new Mock().Object, appCtx, + new Mock(null, null).Object, + Mock.Of(), + Enumerable.Empty(), true); var ctrl = new TestSurfaceController(umbCtx); @@ -70,58 +91,60 @@ namespace Umbraco.Tests.Mvc [Test] public void Can_Lookup_Content() { - //init app context - var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); - //TODO: Need to either make this public or make all methods on the UmbracoHelper or - // in v7 the PublishedContentQuery object virtual so we can just mock the methods - - var contentCaches = new Mock(); - - //init content resolver - //TODO: This is not public so people cannot actually do this! - - PublishedCachesResolver.Current = new PublishedCachesResolver(contentCaches.Object); - - //init umb context - var umbCtx = UmbracoContext.EnsureContext( new Mock().Object, appCtx, + new Mock(null, null).Object, + Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), + Enumerable.Empty(), true); - //setup the mock - - contentCaches.Setup(caches => caches.CreateContextualContentCache(It.IsAny())) - .Returns(new ContextualPublishedContentCache( - Mock.Of(cache => - cache.GetById(It.IsAny(), false, It.IsAny()) == - //return mock of IPublishedContent for any call to GetById - Mock.Of(content => content.Id == 2)), - umbCtx)); - + var helper = new UmbracoHelper( + umbCtx, + Mock.Of(), + Mock.Of(query => query.TypedContent(It.IsAny()) == + //return mock of IPublishedContent for any call to GetById + Mock.Of(content => content.Id == 2)), + Mock.Of(), + Mock.Of(), + Mock.Of(), + new UrlProvider(umbCtx, Enumerable.Empty()), + Mock.Of(), + Mock.Of(), + new MembershipHelper(umbCtx, Mock.Of(), Mock.Of())); + var ctrl = new TestSurfaceController(umbCtx, helper); + var result = ctrl.GetContent(2) as PublishedContentResult; - - - using (var uTest = new DisposableUmbracoTest(appCtx)) - { - var ctrl = new TestSurfaceController(uTest.UmbracoContext); - var result = ctrl.GetContent(2) as PublishedContentResult; - - Assert.IsNotNull(result); - Assert.AreEqual(2, result.Content.Id); - } + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Content.Id); } public class TestSurfaceController : SurfaceController { + private readonly UmbracoHelper _umbracoHelper; + public TestSurfaceController(UmbracoContext umbracoContext) : base(umbracoContext) { } + public TestSurfaceController(UmbracoContext umbracoContext, UmbracoHelper umbracoHelper) + : base(umbracoContext) + { + _umbracoHelper = umbracoHelper; + } + + /// + /// Returns an UmbracoHelper object + /// + public override UmbracoHelper Umbraco + { + get { return _umbracoHelper ?? base.Umbraco; } + } + public ActionResult Index() { return View(); diff --git a/src/Umbraco.Tests/TestHelpers/DisposableUmbracoTest.cs b/src/Umbraco.Tests/TestHelpers/DisposableUmbracoTest.cs deleted file mode 100644 index d261487449..0000000000 --- a/src/Umbraco.Tests/TestHelpers/DisposableUmbracoTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Web; -using Moq; -using Umbraco.Core; -using Umbraco.Core.ObjectResolution; -using Umbraco.Web; - -namespace Umbraco.Tests.TestHelpers -{ - //NOTE: This is just a POC! Looking at the simplest way to expose some code so people can very easily test - // their Umbraco controllers, etc.... - public class DisposableUmbracoTest : DisposableObject - { - public ApplicationContext ApplicationContext { get; set; } - public UmbracoContext UmbracoContext { get; set; } - - public DisposableUmbracoTest(ApplicationContext applicationContext) - { - //init umb context - var umbctx = UmbracoContext.EnsureContext( - new Mock().Object, - applicationContext, - true); - - Init(applicationContext, umbctx); - } - - public DisposableUmbracoTest(ApplicationContext applicationContext, UmbracoContext umbracoContext) - { - Init(applicationContext, umbracoContext); - } - - private void Init(ApplicationContext applicationContext, UmbracoContext umbracoContext) - { - ApplicationContext = applicationContext; - UmbracoContext = umbracoContext; - - ApplicationContext.Current = applicationContext; - UmbracoContext.Current = umbracoContext; - - Resolution.Freeze(); - } - - protected override void DisposeResources() - { - ApplicationContext.Current = null; - UmbracoContext.Current = null; - Resolution.Reset(); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3ec6e46ab5..af7a5061b2 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -512,7 +512,6 @@ - diff --git a/src/Umbraco.Web/CanvasDesignerUtility.cs b/src/Umbraco.Web/CanvasDesignerUtility.cs index 722b7247c9..d4ad46a4aa 100644 --- a/src/Umbraco.Web/CanvasDesignerUtility.cs +++ b/src/Umbraco.Web/CanvasDesignerUtility.cs @@ -20,6 +20,11 @@ using System.Text.RegularExpressions; namespace Umbraco.Web { + //TODO: This class needs to be overhauled: + // No statics that have dependencies! + // Make into a real class that accept the dependencies required. + // Remove all usages of singletons: HttpContext.Current, ApplicationContext.Current, UmbracoContext.Current, etc... + internal static class CanvasDesignerUtility { diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index 7b989bf674..11fef4a25d 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -86,11 +86,13 @@ namespace Umbraco.Web.Editors UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements; UmbracoContext.HttpContext.Items[global::Umbraco.Core.Constants.Conventions.Url.AltTemplate] = null; + var renderer = new UmbracoComponentRenderer(UmbracoContext); + var result = Request.CreateResponse(); //need to create a specific content result formatted as html since this controller has been configured //with only json formatters. result.Content = new StringContent( - Umbraco.RenderMacro(macro, macroParams, legacyPage).ToString(), + renderer.RenderMacro(macro, macroParams, legacyPage).ToString(), Encoding.UTF8, "text/html"); return result; diff --git a/src/Umbraco.Web/HtmlStringUtilities.cs b/src/Umbraco.Web/HtmlStringUtilities.cs new file mode 100644 index 0000000000..5ba1d17f4e --- /dev/null +++ b/src/Umbraco.Web/HtmlStringUtilities.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Web; +using HtmlAgilityPack; + +namespace Umbraco.Web +{ + /// + /// Utility class for working with strings and HTML in views + /// + /// + /// The UmbracoHelper uses this class for it's string methods + /// + public sealed class HtmlStringUtilities + { + /// + /// Replaces text line breaks with html line breaks + /// + /// The text. + /// The text with text line breaks replaced with html linebreaks (
)
+ public string ReplaceLineBreaksForHtml(string text) + { + return text.Replace("\n", "
\n"); + } + + public HtmlString StripHtmlTags(string html, params string[] tags) + { + var doc = new HtmlDocument(); + doc.LoadHtml("

" + html + "

"); + var targets = new List(); + + var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*"); + if (nodes != null) + { + foreach (var node in nodes) + { + //is element + if (node.NodeType != HtmlNodeType.Element) continue; + var filterAllTags = (tags == null || !tags.Any()); + if (filterAllTags || tags.Any(tag => string.Equals(tag, node.Name, StringComparison.CurrentCultureIgnoreCase))) + { + targets.Add(node); + } + } + foreach (var target in targets) + { + HtmlNode content = doc.CreateTextNode(target.InnerText); + target.ParentNode.ReplaceChild(content, target); + } + } + else + { + return new HtmlString(html); + } + return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml); + } + + internal string Join(string seperator, params object[] args) + { + var results = args.Where(arg => arg != null && arg.GetType() != typeof(TIgnore)).Select(arg => string.Format("{0}", arg)).Where(sArg => !string.IsNullOrWhiteSpace(sArg)).ToList(); + return string.Join(seperator, results); + } + + internal string Concatenate(params object[] args) + { + var result = new StringBuilder(); + foreach (var sArg in args.Where(arg => arg != null && arg.GetType() != typeof(TIgnore)).Select(arg => string.Format("{0}", arg)).Where(sArg => !string.IsNullOrWhiteSpace(sArg))) + { + result.Append(sArg); + } + return result.ToString(); + } + + internal string Coalesce(params object[] args) + { + foreach (var sArg in args.Where(arg => arg != null && arg.GetType() != typeof(TIgnore)).Select(arg => string.Format("{0}", arg)).Where(sArg => !string.IsNullOrWhiteSpace(sArg))) + { + return sArg; + } + return string.Empty; + } + + public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) + { + using (var outputms = new MemoryStream()) + { + using (var outputtw = new StreamWriter(outputms)) + { + using (var ms = new MemoryStream()) + { + using (var tw = new StreamWriter(ms)) + { + tw.Write(html); + tw.Flush(); + ms.Position = 0; + var tagStack = new Stack(); + + using (TextReader tr = new StreamReader(ms)) + { + bool isInsideElement = false, + lengthReached = false, + insideTagSpaceEncountered = false, + isTagClose = false; + + int ic = 0, + currentLength = 0, + currentTextLength = 0; + + string currentTag = string.Empty, + tagContents = string.Empty; + + while ((ic = tr.Read()) != -1) + { + bool write = true; + + switch ((char)ic) + { + case '<': + if (!lengthReached) + { + isInsideElement = true; + } + + insideTagSpaceEncountered = false; + currentTag = string.Empty; + tagContents = string.Empty; + isTagClose = false; + if (tr.Peek() == (int)'/') + { + isTagClose = true; + } + break; + + case '>': + isInsideElement = false; + + if (isTagClose && tagStack.Count > 0) + { + string thisTag = tagStack.Pop(); + outputtw.Write(""); + } + if (!isTagClose && currentTag.Length > 0) + { + if (!lengthReached) + { + tagStack.Push(currentTag); + outputtw.Write("<" + currentTag); + if (!string.IsNullOrEmpty(tagContents)) + { + if (tagContents.EndsWith("/")) + { + // No end tag e.g.
. + tagStack.Pop(); + } + + outputtw.Write(tagContents); + write = true; + insideTagSpaceEncountered = false; + } + outputtw.Write(">"); + } + } + // Continue to next iteration of the text reader. + continue; + + default: + if (isInsideElement) + { + if (ic == (int)' ') + { + if (!insideTagSpaceEncountered) + { + insideTagSpaceEncountered = true; + } + } + + if (!insideTagSpaceEncountered) + { + currentTag += (char)ic; + } + } + break; + } + + if (isInsideElement || insideTagSpaceEncountered) + { + write = false; + if (insideTagSpaceEncountered) + { + tagContents += (char)ic; + } + } + + if (!isInsideElement || treatTagsAsContent) + { + currentTextLength++; + } + + if (currentTextLength <= length || (lengthReached && isInsideElement)) + { + if (write) + { + var charToWrite = (char)ic; + outputtw.Write(charToWrite); + currentLength++; + } + } + + if (!lengthReached && currentTextLength >= length) + { + // Reached truncate limit. + if (addElipsis) + { + outputtw.Write("…"); + } + lengthReached = true; + } + + } + + } + } + } + outputtw.Flush(); + outputms.Position = 0; + using (TextReader outputtr = new StreamReader(outputms)) + { + return new HtmlString(outputtr.ReadToEnd().Replace(" ", " ").Trim()); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/IDynamicPublishedContentQuery.cs b/src/Umbraco.Web/IDynamicPublishedContentQuery.cs new file mode 100644 index 0000000000..051ba07ff6 --- /dev/null +++ b/src/Umbraco.Web/IDynamicPublishedContentQuery.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Xml.XPath; +using Umbraco.Core.Xml; + +namespace Umbraco.Web +{ + /// + /// Query methods used for accessing content dynamically in templates + /// + public interface IDynamicPublishedContentQuery + { + dynamic Content(int id); + dynamic ContentSingleAtXPath(string xpath, params XPathVariable[] vars); + dynamic ContentSingleAtXPath(XPathExpression xpath, params XPathVariable[] vars); + dynamic Content(IEnumerable ids); + dynamic ContentAtXPath(string xpath, params XPathVariable[] vars); + dynamic ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars); + dynamic ContentAtRoot(); + + dynamic Media(int id); + dynamic Media(IEnumerable ids); + dynamic MediaAtRoot(); + + /// + /// Searches content + /// + /// + /// + /// + /// + dynamic Search(string term, bool useWildCards = true, string searchProvider = null); + + /// + /// Searhes content + /// + /// + /// + /// + dynamic Search(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/ITagQuery.cs b/src/Umbraco.Web/ITagQuery.cs new file mode 100644 index 0000000000..48098e1904 --- /dev/null +++ b/src/Umbraco.Web/ITagQuery.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Web.Models; + +namespace Umbraco.Web +{ + public interface ITagQuery + { + /// + /// Returns all content that is tagged with the specified tag value and optional tag group + /// + /// + /// + /// + IEnumerable GetContentByTag(string tag, string tagGroup = null); + + /// + /// Returns all content that has been tagged with any tag in the specified group + /// + /// + /// + IEnumerable GetContentByTagGroup(string tagGroup); + + /// + /// Returns all Media that is tagged with the specified tag value and optional tag group + /// + /// + /// + /// + IEnumerable GetMediaByTag(string tag, string tagGroup = null); + + /// + /// Returns all Media that has been tagged with any tag in the specified group + /// + /// + /// + IEnumerable GetMediaByTagGroup(string tagGroup); + + /// + /// Get every tag stored in the database (with optional group) + /// + IEnumerable GetAllTags(string group = null); + + /// + /// Get all tags for content items (with optional group) + /// + /// + /// + IEnumerable GetAllContentTags(string group = null); + + /// + /// Get all tags for media items (with optional group) + /// + /// + /// + IEnumerable GetAllMediaTags(string group = null); + + /// + /// Get all tags for member items (with optional group) + /// + /// + /// + IEnumerable GetAllMemberTags(string group = null); + + /// + /// Returns all tags attached to a property by entity id + /// + /// + /// + /// + /// + IEnumerable GetTagsForProperty(int contentId, string propertyTypeAlias, string tagGroup = null); + + /// + /// Returns all tags attached to an entity (content, media or member) by entity id + /// + /// + /// + /// + IEnumerable GetTagsForEntity(int contentId, string tagGroup = null); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/ITypedPublishedContentQuery.cs b/src/Umbraco.Web/ITypedPublishedContentQuery.cs new file mode 100644 index 0000000000..aefe243a0b --- /dev/null +++ b/src/Umbraco.Web/ITypedPublishedContentQuery.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Xml.XPath; +using Umbraco.Core.Models; +using Umbraco.Core.Xml; + +namespace Umbraco.Web +{ + /// + /// Query methods used for accessing strongly typed content in templates + /// + public interface ITypedPublishedContentQuery + { + IPublishedContent TypedContent(int id); + IPublishedContent TypedContentSingleAtXPath(string xpath, params XPathVariable[] vars); + IEnumerable TypedContent(IEnumerable ids); + IEnumerable TypedContentAtXPath(string xpath, params XPathVariable[] vars); + IEnumerable TypedContentAtXPath(XPathExpression xpath, params XPathVariable[] vars); + IEnumerable TypedContentAtRoot(); + + IPublishedContent TypedMedia(int id); + IEnumerable TypedMedia(IEnumerable ids); + IEnumerable TypedMediaAtRoot(); + + /// + /// Searches content + /// + /// + /// + /// + /// + IEnumerable TypedSearch(string term, bool useWildCards = true, string searchProvider = null); + + /// + /// Searhes content + /// + /// + /// + /// + IEnumerable TypedSearch(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/IUmbracoComponentRenderer.cs b/src/Umbraco.Web/IUmbracoComponentRenderer.cs new file mode 100644 index 0000000000..aaff577491 --- /dev/null +++ b/src/Umbraco.Web/IUmbracoComponentRenderer.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Web; +using Umbraco.Core.Models; + +namespace Umbraco.Web +{ + /// + /// Methods used to render umbraco components as HTML in templates + /// + public interface IUmbracoComponentRenderer + { + /// + /// Renders the template for the specified pageId and an optional altTemplateId + /// + /// + /// If not specified, will use the template assigned to the node + /// + IHtmlString RenderTemplate(int pageId, int? altTemplateId = null); + + /// + /// Renders the macro with the specified alias. + /// + /// The alias. + /// + IHtmlString RenderMacro(string alias); + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + IHtmlString RenderMacro(string alias, object parameters); + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + IHtmlString RenderMacro(string alias, IDictionary parameters); + + /// + /// Renders an field to the template + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + //// + /// + IHtmlString Field(IPublishedContent currentPage, string fieldAlias, + string altFieldAlias = "", string altText = "", string insertBefore = "", string insertAfter = "", + bool recursive = false, bool convertLineBreaks = false, bool removeParagraphTags = false, + RenderFieldCaseType casing = RenderFieldCaseType.Unchanged, + RenderFieldEncodingType encoding = RenderFieldEncodingType.Unchanged, + bool formatAsDate = false, + bool formatAsDateWithTime = false, + string formatAsDateWithTimeSeparator = ""); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs index 66111875f5..b39403c8bb 100644 --- a/src/Umbraco.Web/Mvc/PluginController.cs +++ b/src/Umbraco.Web/Mvc/PluginController.cs @@ -18,6 +18,8 @@ namespace Umbraco.Web.Mvc /// private static readonly ConcurrentDictionary MetadataStorage = new ConcurrentDictionary(); + private UmbracoHelper _umbracoHelper; + /// /// Default constructor /// @@ -27,7 +29,6 @@ namespace Umbraco.Web.Mvc if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); UmbracoContext = umbracoContext; InstanceId = Guid.NewGuid(); - Umbraco = new UmbracoHelper(umbracoContext); } /// @@ -38,7 +39,10 @@ namespace Umbraco.Web.Mvc /// /// Returns an UmbracoHelper object /// - public UmbracoHelper Umbraco { get; private set; } + public virtual UmbracoHelper Umbraco + { + get { return _umbracoHelper ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext)); } + } /// /// Returns an ILogger @@ -51,7 +55,7 @@ namespace Umbraco.Web.Mvc /// /// Returns a ProfilingLogger /// - public ProfilingLogger ProfilingLogger + public virtual ProfilingLogger ProfilingLogger { get { return UmbracoContext.Application.ProfilingLogger; } } @@ -59,12 +63,12 @@ namespace Umbraco.Web.Mvc /// /// Returns the current UmbracoContext /// - public UmbracoContext UmbracoContext { get; private set; } + public virtual UmbracoContext UmbracoContext { get; private set; } /// /// Returns the current ApplicationContext /// - public ApplicationContext ApplicationContext + public virtual ApplicationContext ApplicationContext { get { return UmbracoContext.Application; } } diff --git a/src/Umbraco.Web/Mvc/RenderMvcController.cs b/src/Umbraco.Web/Mvc/RenderMvcController.cs index da3a5d107f..aeab8448da 100644 --- a/src/Umbraco.Web/Mvc/RenderMvcController.cs +++ b/src/Umbraco.Web/Mvc/RenderMvcController.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Mvc /// /// Returns the current UmbracoContext /// - protected new UmbracoContext UmbracoContext + protected virtual new UmbracoContext UmbracoContext { get { return PublishedContentRequest.RoutingContext.UmbracoContext; } } @@ -49,11 +49,10 @@ namespace Umbraco.Web.Mvc get { return PublishedContentRequest.PublishedContent; } } - //TODO: make this protected once we make PublishedContentRequest not internal after we figure out what it should actually contain /// /// Returns the current PublishedContentRequest /// - internal PublishedContentRequest PublishedContentRequest + protected internal virtual PublishedContentRequest PublishedContentRequest { get { diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs index 5304821131..1f2bca4144 100644 --- a/src/Umbraco.Web/Mvc/SurfaceController.cs +++ b/src/Umbraco.Web/Mvc/SurfaceController.cs @@ -24,7 +24,6 @@ namespace Umbraco.Web.Mvc protected SurfaceController(UmbracoContext umbracoContext) : base(umbracoContext) { - _membershipHelper = new MembershipHelper(umbracoContext); } /// @@ -33,17 +32,16 @@ namespace Umbraco.Web.Mvc protected SurfaceController() : base(UmbracoContext.Current) { - _membershipHelper = new MembershipHelper(UmbracoContext.Current); } - private readonly MembershipHelper _membershipHelper; + private MembershipHelper _membershipHelper; /// /// Returns the MemberHelper instance /// - public MembershipHelper Members + public virtual MembershipHelper Members { - get { return _membershipHelper; } + get { return _membershipHelper ?? (_membershipHelper = new MembershipHelper(UmbracoContext)); } } /// @@ -101,7 +99,7 @@ namespace Umbraco.Web.Mvc /// /// Gets the current page. /// - protected IPublishedContent CurrentPage + protected virtual IPublishedContent CurrentPage { get { diff --git a/src/Umbraco.Web/Mvc/UmbracoController.cs b/src/Umbraco.Web/Mvc/UmbracoController.cs index 413680522d..1ab13cedac 100644 --- a/src/Umbraco.Web/Mvc/UmbracoController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoController.cs @@ -29,7 +29,7 @@ namespace Umbraco.Web.Mvc /// /// Returns an UmbracoHelper object /// - public UmbracoHelper Umbraco + public virtual UmbracoHelper Umbraco { get { return _umbraco ?? (_umbraco = new UmbracoHelper(UmbracoContext)); } } @@ -45,7 +45,7 @@ namespace Umbraco.Web.Mvc /// /// Returns a ProfilingLogger /// - public ProfilingLogger ProfilingLogger + public virtual ProfilingLogger ProfilingLogger { get { return UmbracoContext.Application.ProfilingLogger; } } @@ -53,12 +53,12 @@ namespace Umbraco.Web.Mvc /// /// Returns the current UmbracoContext /// - public UmbracoContext UmbracoContext { get; private set; } + public virtual UmbracoContext UmbracoContext { get; private set; } /// /// Returns the current ApplicationContext /// - public ApplicationContext ApplicationContext + public virtual ApplicationContext ApplicationContext { get { return UmbracoContext.Application; } } @@ -82,7 +82,7 @@ namespace Umbraco.Web.Mvc /// /// Returns the WebSecurity instance /// - public WebSecurity Security + public virtual WebSecurity Security { get { return UmbracoContext.Security; } } diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 33469c0c4b..afeddd1bf1 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Xml.XPath; using Umbraco.Core; @@ -13,82 +14,130 @@ namespace Umbraco.Web /// /// A class used to query for published content, media items /// - public class PublishedContentQuery + public class PublishedContentQuery : ITypedPublishedContentQuery, IDynamicPublishedContentQuery { + private readonly ITypedPublishedContentQuery _typedContentQuery; + private readonly IDynamicPublishedContentQuery _dynamicContentQuery; private readonly ContextualPublishedContentCache _contentCache; private readonly ContextualPublishedMediaCache _mediaCache; + /// + /// Constructor used to return results from the caches + /// + /// + /// public PublishedContentQuery(ContextualPublishedContentCache contentCache, ContextualPublishedMediaCache mediaCache) { + if (contentCache == null) throw new ArgumentNullException("contentCache"); + if (mediaCache == null) throw new ArgumentNullException("mediaCache"); _contentCache = contentCache; _mediaCache = mediaCache; } + /// + /// Constructor used to wrap the ITypedPublishedContentQuery and IDynamicPublishedContentQuery objects passed in + /// + /// + /// + public PublishedContentQuery(ITypedPublishedContentQuery typedContentQuery, IDynamicPublishedContentQuery dynamicContentQuery) + { + if (typedContentQuery == null) throw new ArgumentNullException("typedContentQuery"); + if (dynamicContentQuery == null) throw new ArgumentNullException("dynamicContentQuery"); + _typedContentQuery = typedContentQuery; + _dynamicContentQuery = dynamicContentQuery; + } + #region Content public IPublishedContent TypedContent(int id) { - return TypedDocumentById(id, _contentCache); + return _typedContentQuery == null + ? TypedDocumentById(id, _contentCache) + : _typedContentQuery.TypedContent(id); } public IPublishedContent TypedContentSingleAtXPath(string xpath, params XPathVariable[] vars) { - return TypedDocumentByXPath(xpath, vars, _contentCache); + return _typedContentQuery == null + ? TypedDocumentByXPath(xpath, vars, _contentCache) + : _typedContentQuery.TypedContentSingleAtXPath(xpath, vars); } public IEnumerable TypedContent(IEnumerable ids) { - return TypedDocumentsByIds(_contentCache, ids); + return _typedContentQuery == null + ? TypedDocumentsByIds(_contentCache, ids) + : _typedContentQuery.TypedContent(ids); } public IEnumerable TypedContentAtXPath(string xpath, params XPathVariable[] vars) { - return TypedDocumentsByXPath(xpath, vars, _contentCache); + return _typedContentQuery == null + ? TypedDocumentsByXPath(xpath, vars, _contentCache) + : _typedContentQuery.TypedContentAtXPath(xpath, vars); } public IEnumerable TypedContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) { - return TypedDocumentsByXPath(xpath, vars, _contentCache); + return _typedContentQuery == null + ? TypedDocumentsByXPath(xpath, vars, _contentCache) + : _typedContentQuery.TypedContentAtXPath(xpath, vars); } public IEnumerable TypedContentAtRoot() { - return TypedDocumentsAtRoot(_contentCache); + return _typedContentQuery == null + ? TypedDocumentsAtRoot(_contentCache) + : _typedContentQuery.TypedContentAtRoot(); } public dynamic Content(int id) { - return DocumentById(id, _contentCache, DynamicNull.Null); + return _dynamicContentQuery == null + ? DocumentById(id, _contentCache, DynamicNull.Null) + : _dynamicContentQuery.Content(id); } public dynamic ContentSingleAtXPath(string xpath, params XPathVariable[] vars) { - return DocumentByXPath(xpath, vars, _contentCache, DynamicNull.Null); + return _dynamicContentQuery == null + ? DocumentByXPath(xpath, vars, _contentCache, DynamicNull.Null) + : _dynamicContentQuery.ContentSingleAtXPath(xpath, vars); } public dynamic ContentSingleAtXPath(XPathExpression xpath, params XPathVariable[] vars) { - return DocumentByXPath(xpath, vars, _contentCache, DynamicNull.Null); + return _dynamicContentQuery == null + ? DocumentByXPath(xpath, vars, _contentCache, DynamicNull.Null) + : _dynamicContentQuery.ContentSingleAtXPath(xpath, vars); } public dynamic Content(IEnumerable ids) { - return DocumentByIds(_contentCache, ids.ToArray()); + return _dynamicContentQuery == null + ? DocumentByIds(_contentCache, ids.ToArray()) + : _dynamicContentQuery.Content(ids); } public dynamic ContentAtXPath(string xpath, params XPathVariable[] vars) { - return DocumentsByXPath(xpath, vars, _contentCache); + return _dynamicContentQuery == null + ? DocumentsByXPath(xpath, vars, _contentCache) + : _dynamicContentQuery.ContentAtXPath(xpath, vars); } public dynamic ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) { - return DocumentsByXPath(xpath, vars, _contentCache); + return _dynamicContentQuery == null + ? DocumentsByXPath(xpath, vars, _contentCache) + : _dynamicContentQuery.ContentAtXPath(xpath, vars); } public dynamic ContentAtRoot() { - return DocumentsAtRoot(_contentCache); + return _dynamicContentQuery == null + ? DocumentsAtRoot(_contentCache) + : _dynamicContentQuery.ContentAtRoot(); } #endregion @@ -97,32 +146,44 @@ namespace Umbraco.Web public IPublishedContent TypedMedia(int id) { - return TypedDocumentById(id, _mediaCache); + return _typedContentQuery == null + ? TypedDocumentById(id, _mediaCache) + : _typedContentQuery.TypedMedia(id); } public IEnumerable TypedMedia(IEnumerable ids) { - return TypedDocumentsByIds(_mediaCache, ids); + return _typedContentQuery == null + ? TypedDocumentsByIds(_mediaCache, ids) + : _typedContentQuery.TypedMedia(ids); } public IEnumerable TypedMediaAtRoot() { - return TypedDocumentsAtRoot(_mediaCache); + return _typedContentQuery == null + ? TypedDocumentsAtRoot(_mediaCache) + : _typedContentQuery.TypedMediaAtRoot(); } public dynamic Media(int id) { - return DocumentById(id, _mediaCache, DynamicNull.Null); + return _dynamicContentQuery == null + ? DocumentById(id, _mediaCache, DynamicNull.Null) + : _dynamicContentQuery.Media(id); } public dynamic Media(IEnumerable ids) { - return DocumentByIds(_mediaCache, ids); + return _dynamicContentQuery == null + ? DocumentByIds(_mediaCache, ids) + : _dynamicContentQuery.Media(ids); } public dynamic MediaAtRoot() { - return DocumentsAtRoot(_mediaCache); + return _dynamicContentQuery == null + ? DocumentsAtRoot(_mediaCache) + : _dynamicContentQuery.MediaAtRoot(); } #endregion @@ -240,8 +301,10 @@ namespace Umbraco.Web /// public dynamic Search(string term, bool useWildCards = true, string searchProvider = null) { - return new DynamicPublishedContentList( - TypedSearch(term, useWildCards, searchProvider)); + return _dynamicContentQuery == null + ? new DynamicPublishedContentList( + TypedSearch(term, useWildCards, searchProvider)) + : _dynamicContentQuery.Search(term, useWildCards, searchProvider); } /// @@ -252,8 +315,10 @@ namespace Umbraco.Web /// public dynamic Search(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { - return new DynamicPublishedContentList( - TypedSearch(criteria, searchProvider)); + return _dynamicContentQuery == null + ? new DynamicPublishedContentList( + TypedSearch(criteria, searchProvider)) + : _dynamicContentQuery.Search(criteria, searchProvider); } /// @@ -265,6 +330,8 @@ namespace Umbraco.Web /// public IEnumerable TypedSearch(string term, bool useWildCards = true, string searchProvider = null) { + if (_typedContentQuery != null) return _typedContentQuery.TypedSearch(term, useWildCards, searchProvider); + var searcher = Examine.ExamineManager.Instance.DefaultSearchProvider; if (string.IsNullOrEmpty(searchProvider) == false) searcher = Examine.ExamineManager.Instance.SearchProviderCollection[searchProvider]; @@ -281,6 +348,8 @@ namespace Umbraco.Web /// public IEnumerable TypedSearch(Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { + if (_typedContentQuery != null) return _typedContentQuery.TypedSearch(criteria, searchProvider); + var s = Examine.ExamineManager.Instance.DefaultSearchProvider; if (searchProvider != null) s = searchProvider; diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index f4b9181bef..714ba64e64 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -21,8 +21,10 @@ namespace Umbraco.Web.Routing /// The Umbraco context. /// /// The list of url providers. - internal UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders) + public UrlProvider(UmbracoContext umbracoContext, IWebRoutingSection routingSettings, IEnumerable urlProviders) { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + _umbracoContext = umbracoContext; _urlProviders = urlProviders; @@ -32,7 +34,23 @@ namespace Umbraco.Web.Routing if (Enum.TryParse(routingSettings.UrlProviderMode, out provider)) { Mode = provider; - } + } + } + + /// + /// Initializes a new instance of the class with an Umbraco context and a list of url providers. + /// + /// The Umbraco context. + /// The list of url providers. + /// + public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlProviders, UrlProviderMode provider = UrlProviderMode.Auto) + { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + + _umbracoContext = umbracoContext; + _urlProviders = urlProviders; + + Mode = provider; } private readonly UmbracoContext _umbracoContext; diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index a5f54908d6..3284674338 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -23,23 +23,43 @@ namespace Umbraco.Web.Security /// public class MembershipHelper { + private readonly MembershipProvider _membershipProvider; + private readonly RoleProvider _roleProvider; private readonly ApplicationContext _applicationContext; private readonly HttpContextBase _httpContext; #region Constructors public MembershipHelper(ApplicationContext applicationContext, HttpContextBase httpContext) + : this(applicationContext, httpContext, MPE.GetMembersMembershipProvider(), Roles.Provider) + { + } + + public MembershipHelper(ApplicationContext applicationContext, HttpContextBase httpContext, MembershipProvider membershipProvider, RoleProvider roleProvider) { if (applicationContext == null) throw new ArgumentNullException("applicationContext"); if (httpContext == null) throw new ArgumentNullException("httpContext"); + if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); + if (roleProvider == null) throw new ArgumentNullException("roleProvider"); _applicationContext = applicationContext; _httpContext = httpContext; + _membershipProvider = membershipProvider; + _roleProvider = roleProvider; } public MembershipHelper(UmbracoContext umbracoContext) + : this(umbracoContext, MPE.GetMembersMembershipProvider(), Roles.Provider) + { + } + + public MembershipHelper(UmbracoContext umbracoContext, MembershipProvider membershipProvider, RoleProvider roleProvider) { if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + if (membershipProvider == null) throw new ArgumentNullException("membershipProvider"); + if (roleProvider == null) throw new ArgumentNullException("roleProvider"); _httpContext = umbracoContext.HttpContext; _applicationContext = umbracoContext.Application; + _membershipProvider = membershipProvider; + _roleProvider = roleProvider; } #endregion @@ -49,7 +69,7 @@ namespace Umbraco.Web.Security /// public bool IsUmbracoMembershipProviderActive() { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; return provider.IsUmbracoMembershipProvider(); } @@ -60,7 +80,7 @@ namespace Umbraco.Web.Security /// /// The updated MembershipUser object /// - public Attempt UpdateMemberProfile(ProfileModel model) + public virtual Attempt UpdateMemberProfile(ProfileModel model) { if (IsLoggedIn() == false) { @@ -68,7 +88,7 @@ namespace Umbraco.Web.Security } //get the current membership user - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; var membershipUser = provider.GetCurrentUser(); //NOTE: This should never happen since they are logged in if (membershipUser == null) throw new InvalidOperationException("Could not find member with username " + _httpContext.User.Identity.Name); @@ -130,12 +150,12 @@ namespace Umbraco.Web.Security /// true to log the member in upon successful registration /// /// - public MembershipUser RegisterMember(RegisterModel model, out MembershipCreateStatus status, bool logMemberIn = true) + public virtual MembershipUser RegisterMember(RegisterModel model, out MembershipCreateStatus status, bool logMemberIn = true) { model.Username = (model.UsernameIsEmail || model.Username == null) ? model.Email : model.Username; MembershipUser membershipUser; - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; //update their real name if (provider.IsUmbracoMembershipProvider()) { @@ -190,9 +210,9 @@ namespace Umbraco.Web.Security /// /// /// - public bool Login(string username, string password) + public virtual bool Login(string username, string password) { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; //Validate credentials if (provider.ValidateUser(username, password) == false) { @@ -214,19 +234,19 @@ namespace Umbraco.Web.Security /// /// Logs out the current member /// - public void Logout() + public virtual void Logout() { FormsAuthentication.SignOut(); } #region Querying for front-end - public IPublishedContent GetByProviderKey(object key) + public virtual IPublishedContent GetByProviderKey(object key) { return _applicationContext.ApplicationCache.RequestCache.GetCacheItem( GetCacheKey("GetByProviderKey", key), () => { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider() == false) { throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); @@ -237,12 +257,12 @@ namespace Umbraco.Web.Security }); } - public IPublishedContent GetById(int memberId) + public virtual IPublishedContent GetById(int memberId) { return _applicationContext.ApplicationCache.RequestCache.GetCacheItem( GetCacheKey("GetById", memberId), () => { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider() == false) { throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); @@ -253,12 +273,12 @@ namespace Umbraco.Web.Security }); } - public IPublishedContent GetByUsername(string username) + public virtual IPublishedContent GetByUsername(string username) { return _applicationContext.ApplicationCache.RequestCache.GetCacheItem( GetCacheKey("GetByUsername", username), () => { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider() == false) { throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); @@ -269,12 +289,12 @@ namespace Umbraco.Web.Security }); } - public IPublishedContent GetByEmail(string email) + public virtual IPublishedContent GetByEmail(string email) { return _applicationContext.ApplicationCache.RequestCache.GetCacheItem( GetCacheKey("GetByEmail", email), () => { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider() == false) { throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active"); @@ -289,7 +309,7 @@ namespace Umbraco.Web.Security /// Returns the currently logged in member as IPublishedContent /// /// - public IPublishedContent GetCurrentMember() + public virtual IPublishedContent GetCurrentMember() { if (IsLoggedIn() == false) { @@ -321,14 +341,14 @@ namespace Umbraco.Web.Security /// profile properties /// /// - public ProfileModel GetCurrentMemberProfileModel() + public virtual ProfileModel GetCurrentMemberProfileModel() { if (IsLoggedIn() == false) { return null; } - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider()) { @@ -380,9 +400,9 @@ namespace Umbraco.Web.Security /// /// /// - public RegisterModel CreateRegistrationModel(string memberTypeAlias = null) + public virtual RegisterModel CreateRegistrationModel(string memberTypeAlias = null) { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider()) { memberTypeAlias = memberTypeAlias ?? Constants.Conventions.MemberTypes.DefaultAlias; @@ -466,7 +486,7 @@ namespace Umbraco.Web.Security /// Returns the login status model of the currently logged in member, if no member is logged in it returns null; /// /// - public LoginStatusModel GetCurrentLoginStatus() + public virtual LoginStatusModel GetCurrentLoginStatus() { var model = LoginStatusModel.CreateModel(); @@ -476,7 +496,7 @@ namespace Umbraco.Web.Security return model; } - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider()) { @@ -540,7 +560,7 @@ namespace Umbraco.Web.Security /// /// /// - public bool IsMemberAuthorized( + public virtual bool IsMemberAuthorized( bool allowAll = false, IEnumerable allowTypes = null, IEnumerable allowGroups = null, @@ -566,7 +586,7 @@ namespace Umbraco.Web.Security } else { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; string username; if (provider.IsUmbracoMembershipProvider()) @@ -599,7 +619,7 @@ namespace Umbraco.Web.Security if (allowAction && allowGroupsList.Any(allowGroup => allowGroup != string.Empty)) { // Allow only if member is assigned to a group in the list - var groups = Roles.GetRolesForUser(username); + var groups = _roleProvider.GetRolesForUser(username); allowAction = allowGroupsList.Select(s => s.ToLowerInvariant()).Intersect(groups.Select(myGroup => myGroup.ToLowerInvariant())).Any(); } @@ -616,7 +636,7 @@ namespace Umbraco.Web.Security /// /// /// - public Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, string membershipProviderName) + public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, string membershipProviderName) { var provider = Membership.Providers[membershipProviderName]; if (provider == null) @@ -633,7 +653,7 @@ namespace Umbraco.Web.Security /// /// /// - public Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) + public virtual Attempt ChangePassword(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider) { // YES! It is completely insane how many options you have to take into account based on the membership provider. yikes! @@ -826,7 +846,7 @@ namespace Umbraco.Web.Security return _applicationContext.ApplicationCache.RequestCache.GetCacheItem( GetCacheKey("GetCurrentPersistedMember"), () => { - var provider = MPE.GetMembersMembershipProvider(); + var provider = _membershipProvider; if (provider.IsUmbracoMembershipProvider() == false) { diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 5239207316..d16d22b946 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -1,24 +1,15 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web; using System.Web.Security; -using Newtonsoft.Json.Linq; using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; -using Umbraco.Web.Models; -using Umbraco.Web.Models.ContentEditing; using umbraco; -using umbraco.DataLayer; using umbraco.businesslogic.Exceptions; -using umbraco.providers; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; -using Member = umbraco.cms.businesslogic.member.Member; using User = umbraco.BusinessLogic.User; namespace Umbraco.Web.Security @@ -35,10 +26,6 @@ namespace Umbraco.Web.Security { _httpContext = httpContext; _applicationContext = applicationContext; - //This ensures the dispose method is called when the request terminates, though - // we also ensure this happens in the Umbraco module because the UmbracoContext is added to the - // http context items. - _httpContext.DisposeOnPipelineCompleted(this); } /// @@ -70,7 +57,7 @@ namespace Umbraco.Web.Security /// Gets the current user. /// /// The current user. - public IUser CurrentUser + public virtual IUser CurrentUser { get { @@ -94,7 +81,7 @@ namespace Umbraco.Web.Security /// /// The user Id /// returns the number of seconds until their session times out - public double PerformLogin(int userId) + public virtual double PerformLogin(int userId) { var user = _applicationContext.Services.UserService.GetUserById(userId); return PerformLogin(user).GetRemainingAuthSeconds(); @@ -105,7 +92,7 @@ namespace Umbraco.Web.Security /// /// /// returns the number of seconds until their session times out - public FormsAuthenticationTicket PerformLogin(IUser user) + public virtual FormsAuthenticationTicket PerformLogin(IUser user) { var ticket = _httpContext.CreateUmbracoAuthTicket(new UserData(Guid.NewGuid().ToString("N")) { @@ -128,7 +115,7 @@ namespace Umbraco.Web.Security /// /// Clears the current login for the currently logged in user /// - public void ClearCurrentLogin() + public virtual void ClearCurrentLogin() { _httpContext.UmbracoLogout(); } @@ -136,7 +123,7 @@ namespace Umbraco.Web.Security /// /// Renews the user's login ticket /// - public void RenewLoginTimeout() + public virtual void RenewLoginTimeout() { _httpContext.RenewUmbracoAuthTicket(); } @@ -147,7 +134,7 @@ namespace Umbraco.Web.Security /// /// /// - public bool ValidateBackOfficeCredentials(string username, string password) + public virtual bool ValidateBackOfficeCredentials(string username, string password) { var membershipProvider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); return membershipProvider != null && membershipProvider.ValidateUser(username, password); @@ -159,7 +146,7 @@ namespace Umbraco.Web.Security /// /// /// - public MembershipUser GetBackOfficeMembershipUser(string username, bool setOnline) + public virtual MembershipUser GetBackOfficeMembershipUser(string username, bool setOnline) { var membershipProvider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); return membershipProvider != null ? membershipProvider.GetUser(username, setOnline) : null; @@ -284,7 +271,7 @@ namespace Umbraco.Web.Security /// Gets the currnet user's id. /// /// - public int GetUserId() + public virtual int GetUserId() { var identity = _httpContext.GetCurrentIdentity(false); if (identity == null) @@ -296,7 +283,7 @@ namespace Umbraco.Web.Security /// Returns the current user's unique session id - used to mitigate csrf attacks or any other reason to validate a request /// /// - public string GetSessionId() + public virtual string GetSessionId() { var identity = _httpContext.GetCurrentIdentity(false); if (identity == null) @@ -319,7 +306,7 @@ namespace Umbraco.Web.Security /// Validates the currently logged in user and ensures they are not timed out /// /// - public bool ValidateCurrentUser() + public virtual bool ValidateCurrentUser() { var result = ValidateCurrentUser(false); return result == ValidateRequestAttempt.Success; diff --git a/src/Umbraco.Web/TagQuery.cs b/src/Umbraco.Web/TagQuery.cs index 58dcdc3947..ded4dea66e 100644 --- a/src/Umbraco.Web/TagQuery.cs +++ b/src/Umbraco.Web/TagQuery.cs @@ -11,10 +11,10 @@ namespace Umbraco.Web /// /// A class that exposes methods used to query tag data in views /// - public class TagQuery + public class TagQuery : ITagQuery { private readonly ITagService _tagService; - private readonly PublishedContentQuery _contentQuery; + private readonly ITypedPublishedContentQuery _typedContentQuery; [Obsolete("Use the alternate constructor specifying the contentQuery instead")] public TagQuery(ITagService tagService) @@ -22,12 +22,26 @@ namespace Umbraco.Web { } + [Obsolete("Use the alternate constructor specifying the ITypedPublishedContentQuery instead")] public TagQuery(ITagService tagService, PublishedContentQuery contentQuery) { if (tagService == null) throw new ArgumentNullException("tagService"); if (contentQuery == null) throw new ArgumentNullException("contentQuery"); _tagService = tagService; - _contentQuery = contentQuery; + _typedContentQuery = contentQuery; + } + + /// + /// Constructor + /// + /// + /// + public TagQuery(ITagService tagService, ITypedPublishedContentQuery typedContentQuery) + { + if (tagService == null) throw new ArgumentNullException("tagService"); + if (typedContentQuery == null) throw new ArgumentNullException("typedContentQuery"); + _tagService = tagService; + _typedContentQuery = typedContentQuery; } /// @@ -40,7 +54,7 @@ namespace Umbraco.Web { var ids = _tagService.GetTaggedContentByTag(tag, tagGroup) .Select(x => x.EntityId); - return _contentQuery.TypedContent(ids) + return _typedContentQuery.TypedContent(ids) .Where(x => x != null); } @@ -53,7 +67,7 @@ namespace Umbraco.Web { var ids = _tagService.GetTaggedContentByTagGroup(tagGroup) .Select(x => x.EntityId); - return _contentQuery.TypedContent(ids) + return _typedContentQuery.TypedContent(ids) .Where(x => x != null); } @@ -67,7 +81,7 @@ namespace Umbraco.Web { var ids = _tagService.GetTaggedMediaByTag(tag, tagGroup) .Select(x => x.EntityId); - return _contentQuery.TypedMedia(ids) + return _typedContentQuery.TypedMedia(ids) .Where(x => x != null); } @@ -80,7 +94,7 @@ namespace Umbraco.Web { var ids = _tagService.GetTaggedMediaByTagGroup(tagGroup) .Select(x => x.EntityId); - return _contentQuery.TypedMedia(ids) + return _typedContentQuery.TypedMedia(ids) .Where(x => x != null); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 92eb13bd86..cdac9d13ca 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -275,7 +275,12 @@ + + + + + @@ -562,6 +567,7 @@ + True True diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs new file mode 100644 index 0000000000..9e789ac0c5 --- /dev/null +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Xml.Linq; +using System.Xml.XPath; +using HtmlAgilityPack; +using Umbraco.Core; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Dynamics; +using Umbraco.Core.Models; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Core.Xml; +using Umbraco.Web.Routing; +using Umbraco.Web.Security; +using Umbraco.Web.Templates; +using umbraco; +using System.Collections.Generic; +using umbraco.cms.businesslogic.web; +using umbraco.presentation.templateControls; +using Umbraco.Core.Cache; + +namespace Umbraco.Web +{ + + /// + /// Methods used to render umbraco components as HTML in templates + /// + /// + /// Used by UmbracoHelper + /// + internal class UmbracoComponentRenderer : IUmbracoComponentRenderer + { + private readonly UmbracoContext _umbracoContext; + + public UmbracoComponentRenderer(UmbracoContext umbracoContext) + { + _umbracoContext = umbracoContext; + } + + /// + /// 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()); + } + } + + /// + /// Renders the macro with the specified alias. + /// + /// The alias. + /// + public IHtmlString RenderMacro(string alias) + { + return RenderMacro(alias, new { }); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + public IHtmlString RenderMacro(string alias, object parameters) + { + return RenderMacro(alias, parameters.ToDictionary()); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// + public IHtmlString RenderMacro(string alias, IDictionary parameters) + { + + if (_umbracoContext.PublishedContentRequest == null) + { + throw new InvalidOperationException("Cannot render a macro when there is no current PublishedContentRequest."); + } + + return RenderMacro(alias, parameters, _umbracoContext.PublishedContentRequest.UmbracoPage); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The alias. + /// The parameters. + /// The legacy umbraco page object that is required for some macros + /// + internal IHtmlString RenderMacro(string alias, IDictionary parameters, page umbracoPage) + { + if (alias == null) throw new ArgumentNullException("alias"); + if (umbracoPage == null) throw new ArgumentNullException("umbracoPage"); + + var m = macro.GetMacro(alias); + if (m == null) + { + throw new KeyNotFoundException("Could not find macro with alias " + alias); + } + + return RenderMacro(m, parameters, umbracoPage); + } + + /// + /// Renders the macro with the specified alias, passing in the specified parameters. + /// + /// The macro. + /// The parameters. + /// The legacy umbraco page object that is required for some macros + /// + internal IHtmlString RenderMacro(macro m, IDictionary parameters, page umbracoPage) + { + if (umbracoPage == null) throw new ArgumentNullException("umbracoPage"); + if (m == null) throw new ArgumentNullException("m"); + + if (_umbracoContext.PageId == null) + { + throw new InvalidOperationException("Cannot render a macro when UmbracoContext.PageId is null."); + } + + var macroProps = new Hashtable(); + foreach (var i in parameters) + { + //TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method of macro.cs + // looks for a lower case match. WTF. the whole macro concept needs to be rewritten. + + + //NOTE: the value could have html encoded values, so we need to deal with that + macroProps.Add(i.Key.ToLowerInvariant(), (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); + } + var macroControl = m.renderMacro(macroProps, + umbracoPage.Elements, + _umbracoContext.PageId.Value); + + 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). + // + // I'm sure there's more things that will get lost with this context changing but I guess we'll figure + // those out as we go along. One thing we lose is the content type response output. + // http://issues.umbraco.org/issue/U4-1599 if it is setup during the macro execution. So + // here we'll save the content type response and reset it after execute is called. + + var contentType = _umbracoContext.HttpContext.Response.ContentType; + var traceIsEnabled = containerPage.Trace.IsEnabled; + containerPage.Trace.IsEnabled = false; + _umbracoContext.HttpContext.Server.Execute(containerPage, output, true); + containerPage.Trace.IsEnabled = traceIsEnabled; + //reset the content type + _umbracoContext.HttpContext.Response.ContentType = contentType; + + //Now, we need to ensure that local links are parsed + html = TemplateUtilities.ParseInternalLinks(output.ToString()); + } + } + + return new HtmlString(html); + } + + /// + /// Renders an field to the template + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + //// + /// + public IHtmlString Field(IPublishedContent currentPage, string fieldAlias, + string altFieldAlias = "", string altText = "", string insertBefore = "", string insertAfter = "", + bool recursive = false, bool convertLineBreaks = false, bool removeParagraphTags = false, + RenderFieldCaseType casing = RenderFieldCaseType.Unchanged, + RenderFieldEncodingType encoding = RenderFieldEncodingType.Unchanged, + bool formatAsDate = false, + bool formatAsDateWithTime = false, + string formatAsDateWithTimeSeparator = "") + + //TODO: commented out until as it is not implemented by umbraco:item yet + //,string formatString = "") + { + Mandate.ParameterNotNull(currentPage, "currentPage"); + Mandate.ParameterNotNullOrEmpty(fieldAlias, "fieldAlias"); + + //TODO: This is real nasty and we should re-write the 'item' and 'ItemRenderer' class but si fine for now + + var attributes = new Dictionary + { + {"field", fieldAlias}, + {"recursive", recursive.ToString().ToLowerInvariant()}, + {"useifempty", altFieldAlias}, + {"textifempty", altText}, + {"stripparagraph", removeParagraphTags.ToString().ToLowerInvariant()}, + { + "case", casing == RenderFieldCaseType.Lower ? "lower" + : casing == RenderFieldCaseType.Upper ? "upper" + : casing == RenderFieldCaseType.Title ? "title" + : string.Empty + }, + {"inserttextbefore", insertBefore}, + {"inserttextafter", insertAfter}, + {"convertlinebreaks", convertLineBreaks.ToString().ToLowerInvariant()}, + {"formatasdate", formatAsDate.ToString().ToLowerInvariant()}, + {"formatasdatewithtime", formatAsDateWithTime.ToString().ToLowerInvariant()}, + {"formatasdatewithtimeseparator", formatAsDateWithTimeSeparator} + }; + switch (encoding) + { + case RenderFieldEncodingType.Url: + attributes.Add("urlencode", "true"); + break; + case RenderFieldEncodingType.Html: + attributes.Add("htmlencode", "true"); + break; + case RenderFieldEncodingType.Unchanged: + default: + break; + } + + //need to convert our dictionary over to this weird dictionary type + var attributesForItem = new AttributeCollectionAdapter( + new AttributeCollection( + new StateBag())); + foreach (var i in attributes) + { + attributesForItem.Add(i.Key, i.Value); + } + + + + var item = new Item(currentPage) + { + Field = fieldAlias, + TextIfEmpty = altText, + LegacyAttributes = attributesForItem + }; + + //here we are going to check if we are in the context of an Umbraco routed page, if we are we + //will leave the NodeId empty since the underlying ItemRenderer will work ever so slightly faster + //since it already knows about the current page. Otherwise, we'll assign the id based on our + //currently assigned node. The PublishedContentRequest will be null if: + // * we are rendering a partial view or child action + // * we are rendering a view from a custom route + if ((_umbracoContext.PublishedContentRequest == null + || _umbracoContext.PublishedContentRequest.PublishedContent.Id != currentPage.Id) + && currentPage.Id > 0) // in case we're rendering a detached content (id == 0) + { + item.NodeId = currentPage.Id.ToString(CultureInfo.InvariantCulture); + } + + + var containerPage = new FormlessPage(); + containerPage.Controls.Add(item); + + using (var output = new StringWriter()) + using (var htmlWriter = new HtmlTextWriter(output)) + { + ItemRenderer.Instance.Init(item); + ItemRenderer.Instance.Load(item); + ItemRenderer.Instance.Render(item, htmlWriter); + + //because we are rendering the output through the legacy Item (webforms) stuff, the {localLinks} will already be replaced. + return new HtmlString(output.ToString()); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs index efbe290f15..e3f86b8eb0 100644 --- a/src/Umbraco.Web/UmbracoContext.cs +++ b/src/Umbraco.Web/UmbracoContext.cs @@ -84,139 +84,18 @@ namespace Umbraco.Web bool replaceContext, bool? preview) { - return EnsureContext(httpContext, applicationContext, webSecurity, UmbracoConfig.For.UmbracoSettings(), replaceContext, preview); + return EnsureContext(httpContext, applicationContext, webSecurity, UmbracoConfig.For.UmbracoSettings(), UrlProviderResolver.Current.Providers, replaceContext, preview); } #endregion /// - /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing - /// context is created and assigned. - /// - /// - /// - /// - /// - /// - /// The Singleton context object - /// - /// - /// This is created in order to standardize the creation of the singleton. Normally it is created during a request - /// in the UmbracoModule, however this module does not execute during application startup so we need to ensure it - /// during the startup process as well. - /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 - /// - public static UmbracoContext EnsureContext( - HttpContextBase httpContext, - ApplicationContext applicationContext, - WebSecurity webSecurity, - IUmbracoSettingsSection umbracoSettings) - { - return EnsureContext(httpContext, applicationContext, webSecurity, umbracoSettings, false); - } - - - - /// - /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing - /// context is created and assigned. - /// - /// - /// - /// - /// - /// The Singleton context object - /// - /// - /// This is created in order to standardize the creation of the singleton. Normally it is created during a request - /// in the UmbracoModule, however this module does not execute during application startup so we need to ensure it - /// during the startup process as well. - /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 - /// - public static UmbracoContext EnsureContext( - HttpContextBase httpContext, - ApplicationContext applicationContext, - IUmbracoSettingsSection umbracoSettings) - { - return EnsureContext(httpContext, applicationContext, new WebSecurity(httpContext, applicationContext), umbracoSettings, false); - } - - - - /// - /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing - /// context is created and assigned. - /// - /// - /// - /// - /// - /// if set to true will replace the current singleton with a new one, this is generally only ever used because - /// during application startup the base url domain will not be available so after app startup we'll replace the current - /// context with a new one in which we can access the httpcontext.Request object. - /// - /// - /// The Singleton context object - /// - /// - /// This is created in order to standardize the creation of the singleton. Normally it is created during a request - /// in the UmbracoModule, however this module does not execute during application startup so we need to ensure it - /// during the startup process as well. - /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 - /// - public static UmbracoContext EnsureContext( - HttpContextBase httpContext, - ApplicationContext applicationContext, - IUmbracoSettingsSection umbracoSettings, - bool replaceContext) - { - return EnsureContext(httpContext, applicationContext, new WebSecurity(httpContext, applicationContext), umbracoSettings, replaceContext); - } - - - - - /// - /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing - /// context is created and assigned. - /// - /// - /// - /// - /// - /// - /// if set to true will replace the current singleton with a new one, this is generally only ever used because - /// during application startup the base url domain will not be available so after app startup we'll replace the current - /// context with a new one in which we can access the httpcontext.Request object. - /// - /// - /// The Singleton context object - /// - /// - /// This is created in order to standardize the creation of the singleton. Normally it is created during a request - /// in the UmbracoModule, however this module does not execute during application startup so we need to ensure it - /// during the startup process as well. - /// See: http://issues.umbraco.org/issue/U4-1890, http://issues.umbraco.org/issue/U4-1717 - /// - public static UmbracoContext EnsureContext( - HttpContextBase httpContext, - ApplicationContext applicationContext, - WebSecurity webSecurity, - IUmbracoSettingsSection umbracoSettings, - bool replaceContext) - { - return EnsureContext(httpContext, applicationContext, new WebSecurity(httpContext, applicationContext), umbracoSettings, replaceContext, null); - } - - - - /// - /// This is a helper method which is called to ensure that the singleton context is created and the nice url and routing - /// context is created and assigned. + /// This is a helper method which is called to ensure that the singleton context is created /// /// /// /// /// + /// /// /// if set to true will replace the current singleton with a new one, this is generally only ever used because /// during application startup the base url domain will not be available so after app startup we'll replace the current @@ -237,9 +116,16 @@ namespace Umbraco.Web ApplicationContext applicationContext, WebSecurity webSecurity, IUmbracoSettingsSection umbracoSettings, + IEnumerable urlProviders, bool replaceContext, - bool? preview) + bool? preview = null) { + if (httpContext == null) throw new ArgumentNullException("httpContext"); + if (applicationContext == null) throw new ArgumentNullException("applicationContext"); + if (webSecurity == null) throw new ArgumentNullException("webSecurity"); + if (umbracoSettings == null) throw new ArgumentNullException("umbracoSettings"); + if (urlProviders == null) throw new ArgumentNullException("urlProviders"); + if (UmbracoContext.Current != null) { if (replaceContext == false) @@ -257,15 +143,18 @@ namespace Umbraco.Web // create the RoutingContext, and assign var routingContext = new RoutingContext( umbracoContext, + + //TODO: Until the new cache is done we can't really expose these to override/mock new Lazy>(() => ContentFinderResolver.Current.Finders), new Lazy(() => ContentLastChanceFinderResolver.Current.Finder), + // create the nice urls provider // there's one per request because there are some behavior parameters that can be changed new Lazy( () => new UrlProvider( umbracoContext, umbracoSettings.WebRouting, - UrlProviderResolver.Current.Providers), + urlProviders), false)); //assign the routing context back diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 073afcb0b9..0429cf83ae 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -1,15 +1,9 @@ using System; -using System.Collections; using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Text; using System.Web; using System.Web.Security; -using System.Web.UI; using System.Xml.Linq; using System.Xml.XPath; -using HtmlAgilityPack; using Umbraco.Core; using Umbraco.Core.Dictionary; using Umbraco.Core.Dynamics; @@ -19,48 +13,59 @@ using Umbraco.Core.Services; using Umbraco.Core.Xml; using Umbraco.Web.Routing; using Umbraco.Web.Security; -using Umbraco.Web.Templates; -using umbraco; using System.Collections.Generic; -using umbraco.cms.businesslogic.web; -using umbraco.presentation.templateControls; using Umbraco.Core.Cache; -using AttributeCollection = System.Web.UI.AttributeCollection; namespace Umbraco.Web { - /// /// A helper class that provides many useful methods and functionality for using Umbraco in templates /// - public class UmbracoHelper - { + public class UmbracoHelper : IUmbracoComponentRenderer + { private readonly UmbracoContext _umbracoContext; private readonly IPublishedContent _currentPage; - private PublishedContentQuery _query; - private readonly MembershipHelper _membershipHelper; - private TagQuery _tag; + private readonly ITypedPublishedContentQuery _typedQuery; + private readonly IDynamicPublishedContentQuery _dynamicQuery; + + private readonly HtmlStringUtilities _stringUtilities = new HtmlStringUtilities(); + + private IUmbracoComponentRenderer _componentRenderer; + private PublishedContentQuery _query; + private MembershipHelper _membershipHelper; + private ITagQuery _tag; + private IDataTypeService _dataTypeService; + private UrlProvider _urlProvider; + private ICultureDictionary _cultureDictionary; /// /// Lazy instantiates the tag context /// - public TagQuery TagQuery + public ITagQuery TagQuery { - get { return _tag ?? (_tag = new TagQuery(UmbracoContext.Application.Services.TagService, ContentQuery)); } + get { return _tag ?? (_tag = new TagQuery(UmbracoContext.Application.Services.TagService, _typedQuery)); } } /// - /// Lazy instantiates the query context + /// Lazy instantiates the query context if not specified in the constructor /// - public PublishedContentQuery ContentQuery - { - get { return _query ?? (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache)); } - } + public PublishedContentQuery ContentQuery + { + get + { + //If the content query doesn't exist it will either be created with the ITypedPublishedContentQuery, IDynamicPublishedContentQuery + // used to construct this instance or with the content caches of the UmbracoContext + return _query ?? + (_query = _typedQuery != null + ? new PublishedContentQuery(_typedQuery, _dynamicQuery) + : new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache)); + } + } /// /// Helper method to ensure an umbraco context is set when it is needed /// - private UmbracoContext UmbracoContext + public UmbracoContext UmbracoContext { get { @@ -73,58 +78,142 @@ namespace Umbraco.Web } /// - /// Empty constructor to create an umbraco helper for access to methods that don't have dependencies or used for testing + /// Lazy instantiates the membership helper if not specified in the constructor /// - public UmbracoHelper() - { + public MembershipHelper MembershipHelper + { + get { return _membershipHelper ?? (_membershipHelper = new MembershipHelper(UmbracoContext)); } } + /// + /// Lazy instantiates the UrlProvider if not specified in the constructor + /// + public UrlProvider UrlProvider + { + get { return _urlProvider ?? (_urlProvider = UmbracoContext.UrlProvider); } + } + + /// + /// Lazy instantiates the IDataTypeService if not specified in the constructor + /// + public IDataTypeService DataTypeService + { + get { return _dataTypeService ?? (_dataTypeService = UmbracoContext.Application.Services.DataTypeService); } + } + + /// + /// Lazy instantiates the IUmbracoComponentRenderer if not specified in the constructor + /// + public IUmbracoComponentRenderer UmbracoComponentRenderer + { + get { return _componentRenderer ?? (_componentRenderer = new UmbracoComponentRenderer(UmbracoContext)); } + } + + #region Constructors + /// + /// Empty constructor to create an umbraco helper for access to methods that don't have dependencies + /// + public UmbracoHelper() + { + } + + /// + /// Constructor accepting all dependencies + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// This constructor can be used to create a testable UmbracoHelper + /// + public UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content, + ITypedPublishedContentQuery typedQuery, + IDynamicPublishedContentQuery dynamicQuery, + ITagQuery tagQuery, + IDataTypeService dataTypeService, + UrlProvider urlProvider, + ICultureDictionary cultureDictionary, + IUmbracoComponentRenderer componentRenderer, + MembershipHelper membershipHelper) + { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + if (content == null) throw new ArgumentNullException("content"); + if (typedQuery == null) throw new ArgumentNullException("typedQuery"); + if (dynamicQuery == null) throw new ArgumentNullException("dynamicQuery"); + if (tagQuery == null) throw new ArgumentNullException("tagQuery"); + if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); + if (urlProvider == null) throw new ArgumentNullException("urlProvider"); + if (cultureDictionary == null) throw new ArgumentNullException("cultureDictionary"); + if (componentRenderer == null) throw new ArgumentNullException("componentRenderer"); + if (membershipHelper == null) throw new ArgumentNullException("membershipHelper"); + + _umbracoContext = umbracoContext; + _tag = tagQuery; + _dataTypeService = dataTypeService; + _urlProvider = urlProvider; + _cultureDictionary = cultureDictionary; + _componentRenderer = componentRenderer; + _membershipHelper = membershipHelper; + _currentPage = content; + _typedQuery = typedQuery; + _dynamicQuery = dynamicQuery; + } + + [Obsolete("Use the constructor specifying all dependencies")] + [EditorBrowsable(EditorBrowsableState.Never)] public UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content, PublishedContentQuery query) : this(umbracoContext) { if (content == null) throw new ArgumentNullException("content"); if (query == null) throw new ArgumentNullException("query"); - _membershipHelper = new MembershipHelper(_umbracoContext); _currentPage = content; _query = query; } - - /// - /// Custom constructor setting the current page to the parameter passed in - /// - /// - /// - public UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content) - : this(umbracoContext) - { - if (content == null) throw new ArgumentNullException("content"); - _currentPage = content; - _membershipHelper = new MembershipHelper(_umbracoContext); - } - /// - /// Standard constructor setting the current page to the page that has been routed to - /// - /// - public UmbracoHelper(UmbracoContext umbracoContext) - { - if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); - if (umbracoContext.RoutingContext == null) throw new NullReferenceException("The RoutingContext on the UmbracoContext cannot be null"); - _umbracoContext = umbracoContext; - _membershipHelper = new MembershipHelper(_umbracoContext); - if (_umbracoContext.IsFrontEndUmbracoRequest) - { - _currentPage = _umbracoContext.PublishedContentRequest.PublishedContent; - } - } + /// + /// Custom constructor setting the current page to the parameter passed in + /// + /// + /// + public UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content) + : this(umbracoContext) + { + if (content == null) throw new ArgumentNullException("content"); + _currentPage = content; + } + /// + /// Standard constructor setting the current page to the page that has been routed to + /// + /// + public UmbracoHelper(UmbracoContext umbracoContext) + { + if (umbracoContext == null) throw new ArgumentNullException("umbracoContext"); + if (umbracoContext.RoutingContext == null) throw new NullReferenceException("The RoutingContext on the UmbracoContext cannot be null"); + + _umbracoContext = umbracoContext; + if (_umbracoContext.IsFrontEndUmbracoRequest) + { + _currentPage = _umbracoContext.PublishedContentRequest.PublishedContent; + } + } + + [Obsolete("Use the constructor specifying all dependencies")] + [EditorBrowsable(EditorBrowsableState.Never)] public UmbracoHelper(UmbracoContext umbracoContext, PublishedContentQuery query) : this(umbracoContext) { if (query == null) throw new ArgumentNullException("query"); _query = query; - _membershipHelper = new MembershipHelper(_umbracoContext); - } + } + #endregion /// /// Returns the current IPublishedContent item assigned to the UmbracoHelper @@ -155,21 +244,9 @@ namespace Umbraco.Web /// 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()); - } - } + { + return _componentRenderer.RenderTemplate(pageId, altTemplateId); + } #region RenderMacro @@ -180,7 +257,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias) { - return RenderMacro(alias, new { }); + return _componentRenderer.RenderMacro(alias, new { }); } /// @@ -191,7 +268,7 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, object parameters) { - return RenderMacro(alias, parameters.ToDictionary()); + return _componentRenderer.RenderMacro(alias, parameters.ToDictionary()); } /// @@ -202,111 +279,9 @@ namespace Umbraco.Web /// public IHtmlString RenderMacro(string alias, IDictionary parameters) { - - if (UmbracoContext.PublishedContentRequest == null) - { - throw new InvalidOperationException("Cannot render a macro when there is no current PublishedContentRequest."); - } - - return RenderMacro(alias, parameters, UmbracoContext.PublishedContentRequest.UmbracoPage); + return _componentRenderer.RenderMacro(alias, parameters); } - /// - /// Renders the macro with the specified alias, passing in the specified parameters. - /// - /// The alias. - /// The parameters. - /// The legacy umbraco page object that is required for some macros - /// - internal IHtmlString RenderMacro(string alias, IDictionary parameters, page umbracoPage) - { - if (alias == null) throw new ArgumentNullException("alias"); - if (umbracoPage == null) throw new ArgumentNullException("umbracoPage"); - - var m = macro.GetMacro(alias); - if (m == null) - { - throw new KeyNotFoundException("Could not find macro with alias " + alias); - } - - return RenderMacro(m, parameters, umbracoPage); - } - - /// - /// Renders the macro with the specified alias, passing in the specified parameters. - /// - /// The macro. - /// The parameters. - /// The legacy umbraco page object that is required for some macros - /// - internal IHtmlString RenderMacro(macro m, IDictionary parameters, page umbracoPage) - { - if (umbracoPage == null) throw new ArgumentNullException("umbracoPage"); - if (m == null) throw new ArgumentNullException("m"); - - if (UmbracoContext.PageId == null) - { - throw new InvalidOperationException("Cannot render a macro when UmbracoContext.PageId is null."); - } - - var macroProps = new Hashtable(); - foreach (var i in parameters) - { - //TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method of macro.cs - // looks for a lower case match. WTF. the whole macro concept needs to be rewritten. - - - //NOTE: the value could have html encoded values, so we need to deal with that - macroProps.Add(i.Key.ToLowerInvariant(), (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); - } - var macroControl = m.renderMacro(macroProps, - umbracoPage.Elements, - UmbracoContext.PageId.Value); - - 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). - // - // I'm sure there's more things that will get lost with this context changing but I guess we'll figure - // those out as we go along. One thing we lose is the content type response output. - // http://issues.umbraco.org/issue/U4-1599 if it is setup during the macro execution. So - // here we'll save the content type response and reset it after execute is called. - - var contentType = UmbracoContext.HttpContext.Response.ContentType; - var traceIsEnabled = containerPage.Trace.IsEnabled; - containerPage.Trace.IsEnabled = false; - UmbracoContext.HttpContext.Server.Execute(containerPage, output, true); - containerPage.Trace.IsEnabled = traceIsEnabled; - //reset the content type - UmbracoContext.HttpContext.Response.ContentType = contentType; - - //Now, we need to ensure that local links are parsed - html = TemplateUtilities.ParseInternalLinks(output.ToString()); - } - } - - return new HtmlString(html); - } - #endregion #region Field @@ -337,13 +312,10 @@ namespace Umbraco.Web bool formatAsDate = false, bool formatAsDateWithTime = false, string formatAsDateWithTimeSeparator = "") - - //TODO: commented out until as it is not implemented by umbraco:item yet - //,string formatString = "") { - return Field(AssignedContentItem, fieldAlias, altFieldAlias, + return _componentRenderer.Field(AssignedContentItem, fieldAlias, altFieldAlias, altText, insertBefore, insertAfter, recursive, convertLineBreaks, removeParagraphTags, - casing, encoding, formatAsDate, formatAsDateWithTime, formatAsDateWithTimeSeparator); // formatString); + casing, encoding, formatAsDate, formatAsDateWithTime, formatAsDateWithTimeSeparator); } /// @@ -373,101 +345,16 @@ namespace Umbraco.Web bool formatAsDate = false, bool formatAsDateWithTime = false, string formatAsDateWithTimeSeparator = "") - - //TODO: commented out until as it is not implemented by umbraco:item yet - //,string formatString = "") { - Mandate.ParameterNotNull(currentPage, "currentPage"); - Mandate.ParameterNotNullOrEmpty(fieldAlias, "fieldAlias"); - - //TODO: This is real nasty and we should re-write the 'item' and 'ItemRenderer' class but si fine for now - - var attributes = new Dictionary - { - {"field", fieldAlias}, - {"recursive", recursive.ToString().ToLowerInvariant()}, - {"useifempty", altFieldAlias}, - {"textifempty", altText}, - {"stripparagraph", removeParagraphTags.ToString().ToLowerInvariant()}, - { - "case", casing == RenderFieldCaseType.Lower ? "lower" - : casing == RenderFieldCaseType.Upper ? "upper" - : casing == RenderFieldCaseType.Title ? "title" - : string.Empty - }, - {"inserttextbefore", insertBefore}, - {"inserttextafter", insertAfter}, - {"convertlinebreaks", convertLineBreaks.ToString().ToLowerInvariant()}, - {"formatasdate", formatAsDate.ToString().ToLowerInvariant()}, - {"formatasdatewithtime", formatAsDateWithTime.ToString().ToLowerInvariant()}, - {"formatasdatewithtimeseparator", formatAsDateWithTimeSeparator} - }; - switch (encoding) - { - case RenderFieldEncodingType.Url: - attributes.Add("urlencode", "true"); - break; - case RenderFieldEncodingType.Html: - attributes.Add("htmlencode", "true"); - break; - case RenderFieldEncodingType.Unchanged: - default: - break; - } - - //need to convert our dictionary over to this weird dictionary type - var attributesForItem = new AttributeCollectionAdapter( - new AttributeCollection( - new StateBag())); - foreach (var i in attributes) - { - attributesForItem.Add(i.Key, i.Value); - } - - - - var item = new Item(currentPage) - { - Field = fieldAlias, - TextIfEmpty = altText, - LegacyAttributes = attributesForItem - }; - - //here we are going to check if we are in the context of an Umbraco routed page, if we are we - //will leave the NodeId empty since the underlying ItemRenderer will work ever so slightly faster - //since it already knows about the current page. Otherwise, we'll assign the id based on our - //currently assigned node. The PublishedContentRequest will be null if: - // * we are rendering a partial view or child action - // * we are rendering a view from a custom route - if ((UmbracoContext.PublishedContentRequest == null - || UmbracoContext.PublishedContentRequest.PublishedContent.Id != currentPage.Id) - && currentPage.Id > 0) // in case we're rendering a detached content (id == 0) - { - item.NodeId = currentPage.Id.ToString(); - } - - - var containerPage = new FormlessPage(); - containerPage.Controls.Add(item); - - using (var output = new StringWriter()) - using (var htmlWriter = new HtmlTextWriter(output)) - { - ItemRenderer.Instance.Init(item); - ItemRenderer.Instance.Load(item); - ItemRenderer.Instance.Render(item, htmlWriter); - - //because we are rendering the output through the legacy Item (webforms) stuff, the {localLinks} will already be replaced. - return new HtmlString(output.ToString()); - } + return _componentRenderer.Field(currentPage, fieldAlias, altFieldAlias, + altText, insertBefore, insertAfter, recursive, convertLineBreaks, removeParagraphTags, + casing, encoding, formatAsDate, formatAsDateWithTime, formatAsDateWithTimeSeparator); } #endregion #region Dictionary - private ICultureDictionary _cultureDictionary; - /// /// Returns the dictionary value for the key specified /// @@ -520,7 +407,7 @@ namespace Umbraco.Web { if (IsProtected(path)) { - return _membershipHelper.IsLoggedIn() + return MembershipHelper.IsLoggedIn() && UmbracoContext.Application.Services.PublicAccessService.HasAccess(path, GetCurrentMember(), Roles.Provider); } return true; @@ -545,7 +432,7 @@ namespace Umbraco.Web /// True is the current user is logged in public bool MemberIsLoggedOn() { - return _membershipHelper.IsLoggedIn(); + return MembershipHelper.IsLoggedIn(); } #endregion @@ -571,7 +458,7 @@ namespace Umbraco.Web /// The url for the content. public string Url(int contentId) { - return UmbracoContext.Current.UrlProvider.GetUrl(contentId); + return UrlProvider.GetUrl(contentId); } /// @@ -582,7 +469,7 @@ namespace Umbraco.Web /// The url for the content. public string Url(int contentId, UrlProviderMode mode) { - return UmbracoContext.Current.UrlProvider.GetUrl(contentId, mode); + return UrlProvider.GetUrl(contentId, mode); } /// @@ -602,7 +489,7 @@ namespace Umbraco.Web /// The absolute url for the content. public string UrlAbsolute(int contentId) { - return UmbracoContext.Current.UrlProvider.GetUrl(contentId, true); + return UrlProvider.GetUrl(contentId, true); } #endregion @@ -612,39 +499,39 @@ namespace Umbraco.Web public IPublishedContent TypedMember(object id) { var asInt = id.TryConvertTo(); - return asInt ? _membershipHelper.GetById(asInt.Result) : _membershipHelper.GetByProviderKey(id); + return asInt ? MembershipHelper.GetById(asInt.Result) : MembershipHelper.GetByProviderKey(id); } public IPublishedContent TypedMember(int id) { - return _membershipHelper.GetById(id); + return MembershipHelper.GetById(id); } public IPublishedContent TypedMember(string id) { var asInt = id.TryConvertTo(); - return asInt ? _membershipHelper.GetById(asInt.Result) : _membershipHelper.GetByProviderKey(id); + return asInt ? MembershipHelper.GetById(asInt.Result) : MembershipHelper.GetByProviderKey(id); } public dynamic Member(object id) { var asInt = id.TryConvertTo(); return asInt - ? _membershipHelper.GetById(asInt.Result).AsDynamic() - : _membershipHelper.GetByProviderKey(id).AsDynamic(); + ? MembershipHelper.GetById(asInt.Result).AsDynamic() + : MembershipHelper.GetByProviderKey(id).AsDynamic(); } public dynamic Member(int id) { - return _membershipHelper.GetById(id).AsDynamic(); + return MembershipHelper.GetById(id).AsDynamic(); } public dynamic Member(string id) { var asInt = id.TryConvertTo(); return asInt - ? _membershipHelper.GetById(asInt.Result).AsDynamic() - : _membershipHelper.GetByProviderKey(id).AsDynamic(); + ? MembershipHelper.GetById(asInt.Result).AsDynamic() + : MembershipHelper.GetByProviderKey(id).AsDynamic(); } #endregion @@ -1021,10 +908,7 @@ namespace Umbraco.Web /// The text with text line breaks replaced with html linebreaks (
)
public string ReplaceLineBreaksForHtml(string text) { - if (bool.Parse(Umbraco.Core.Configuration.GlobalSettings.EditXhtmlMode)) - return text.Replace("\n", "
\n"); - else - return text.Replace("\n", "
\n"); + return _stringUtilities.ReplaceLineBreaksForHtml(text); } /// @@ -1047,79 +931,22 @@ namespace Umbraco.Web } public HtmlString StripHtml(string html, params string[] tags) { - return StripHtmlTags(html, tags); + return _stringUtilities.StripHtmlTags(html, tags); } - - private HtmlString StripHtmlTags(string html, params string[] tags) - { - var doc = new HtmlDocument(); - doc.LoadHtml("

" + html + "

"); - var targets = new List(); - - var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*"); - if (nodes != null) - { - foreach (var node in nodes) - { - //is element - if (node.NodeType != HtmlNodeType.Element) continue; - var filterAllTags = (tags == null || !tags.Any()); - if (filterAllTags || tags.Any(tag => string.Equals(tag, node.Name, StringComparison.CurrentCultureIgnoreCase))) - { - targets.Add(node); - } - } - foreach (var target in targets) - { - HtmlNode content = doc.CreateTextNode(target.InnerText); - target.ParentNode.ReplaceChild(content, target); - } - } - else - { - return new HtmlString(html); - } - return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml); - } - + public string Coalesce(params object[] args) { - return Coalesce(args); - } - - internal string Coalesce(params object[] args) - { - foreach (var sArg in args.Where(arg => arg != null && arg.GetType() != typeof(TIgnore)).Select(arg => string.Format("{0}", arg)).Where(sArg => !string.IsNullOrWhiteSpace(sArg))) - { - return sArg; - } - return string.Empty; + return _stringUtilities.Coalesce(args); } public string Concatenate(params object[] args) { - return Concatenate(args); - } - - internal string Concatenate(params object[] args) - { - var result = new StringBuilder(); - foreach (var sArg in args.Where(arg => arg != null && arg.GetType() != typeof(TIgnore)).Select(arg => string.Format("{0}", arg)).Where(sArg => !string.IsNullOrWhiteSpace(sArg))) - { - result.Append(sArg); - } - return result.ToString(); + return _stringUtilities.Concatenate(args); } public string Join(string seperator, params object[] args) { - return Join(seperator, args); - } - - internal string Join(string seperator, params object[] args) - { - var results = args.Where(arg => arg != null && arg.GetType() != typeof(TIgnore)).Select(arg => string.Format("{0}", arg)).Where(sArg => !string.IsNullOrWhiteSpace(sArg)).ToList(); - return string.Join(seperator, results); + return _stringUtilities.Join(seperator, args); } public IHtmlString Truncate(IHtmlString html, int length) @@ -1156,153 +983,7 @@ namespace Umbraco.Web } public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) { - using (var outputms = new MemoryStream()) - { - using (var outputtw = new StreamWriter(outputms)) - { - using (var ms = new MemoryStream()) - { - using (var tw = new StreamWriter(ms)) - { - tw.Write(html); - tw.Flush(); - ms.Position = 0; - var tagStack = new Stack(); - - using (TextReader tr = new StreamReader(ms)) - { - bool isInsideElement = false, - lengthReached = false, - insideTagSpaceEncountered = false, - isTagClose = false; - - int ic = 0, - currentLength = 0, - currentTextLength = 0; - - string currentTag = string.Empty, - tagContents = string.Empty; - - while ((ic = tr.Read()) != -1) - { - bool write = true; - - switch ((char)ic) - { - case '<': - if (!lengthReached) - { - isInsideElement = true; - } - - insideTagSpaceEncountered = false; - currentTag = string.Empty; - tagContents = string.Empty; - isTagClose = false; - if (tr.Peek() == (int)'/') - { - isTagClose = true; - } - break; - - case '>': - isInsideElement = false; - - if (isTagClose && tagStack.Count > 0) - { - string thisTag = tagStack.Pop(); - outputtw.Write(""); - } - if (!isTagClose && currentTag.Length > 0) - { - if (!lengthReached) - { - tagStack.Push(currentTag); - outputtw.Write("<" + currentTag); - if (!string.IsNullOrEmpty(tagContents)) - { - if (tagContents.EndsWith("/")) - { - // No end tag e.g.
. - tagStack.Pop(); - } - - outputtw.Write(tagContents); - write = true; - insideTagSpaceEncountered = false; - } - outputtw.Write(">"); - } - } - // Continue to next iteration of the text reader. - continue; - - default: - if (isInsideElement) - { - if (ic == (int)' ') - { - if (!insideTagSpaceEncountered) - { - insideTagSpaceEncountered = true; - } - } - - if (!insideTagSpaceEncountered) - { - currentTag += (char)ic; - } - } - break; - } - - if (isInsideElement || insideTagSpaceEncountered) - { - write = false; - if (insideTagSpaceEncountered) - { - tagContents += (char)ic; - } - } - - if (!isInsideElement || treatTagsAsContent) - { - currentTextLength++; - } - - if (currentTextLength <= length || (lengthReached && isInsideElement)) - { - if (write) - { - var charToWrite = (char)ic; - outputtw.Write(charToWrite); - currentLength++; - } - } - - if (!lengthReached && currentTextLength >= length) - { - // Reached truncate limit. - if (addElipsis) - { - outputtw.Write("…"); - } - lengthReached = true; - } - - } - - } - } - } - outputtw.Flush(); - outputms.Position = 0; - using (TextReader outputtr = new StreamReader(outputms)) - { - return new HtmlString(outputtr.ReadToEnd().Replace(" ", " ").Trim()); - } - } - } + return _stringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent); } @@ -1325,8 +1006,7 @@ namespace Umbraco.Web public string GetPreValueAsString(int id) { - var ds = _umbracoContext.Application.Services.DataTypeService; - return ds.GetPreValueAsString(id); + return DataTypeService.GetPreValueAsString(id); } #endregion diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs index d9d93a458e..9ef42a798c 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/RazorLibraryCore.cs @@ -13,6 +13,7 @@ namespace umbraco.MacroEngines.Library { private readonly INode _node; private readonly UmbracoHelper _umbracoHelper; + private readonly HtmlStringUtilities _stringUtilities = new HtmlStringUtilities(); /// /// An empty HtmlHelper with a blank ViewContext, used only to access some htmlHelper extension methods @@ -193,16 +194,16 @@ namespace umbraco.MacroEngines.Library public string Coalesce(params object[] args) { - return _umbracoHelper.Coalesce(args); + return _stringUtilities.Coalesce(args); } public string Concatenate(params object[] args) { - return _umbracoHelper.Concatenate(args); + return _stringUtilities.Concatenate(args); } public string Join(string seperator, params object[] args) { - return _umbracoHelper.Join(seperator, args); + return _stringUtilities.Join(seperator, args); } public HtmlString If(bool test, string valueIfTrue, string valueIfFalse)