diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 59c7a51710..14797ef36a 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -274,8 +274,6 @@
-
-
diff --git a/src/Umbraco.Tests/ContentVariations/ContentVariationProviderTests.cs b/src/Umbraco.Tests/ContentVariations/ContentVariationProviderTests.cs
deleted file mode 100644
index 8f17befb57..0000000000
--- a/src/Umbraco.Tests/ContentVariations/ContentVariationProviderTests.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Web;
-using NUnit.Framework;
-using Umbraco.Core.ContentVariations;
-
-namespace Umbraco.Tests.ContentVariations
-{
- [TestFixture]
- public class ContentVariationProviderTests
- {
-
- [Test]
- public void Get_Advertised_Segments()
- {
- var provider = new MyTestProvider();
-
- Assert.AreEqual(3, provider.SegmentsAdvertised.Count());
- }
-
- [ContentVariation("Test1")]
- [ContentVariation("Test2")]
- [ContentVariation("Test3")]
- [ContentVariation("Test2")]
- private class MyTestProvider : ContentVariationProvider
- {
- public override IEnumerable GetSegmentsForRequest(Uri originalRequestUrl, Uri cleanedRequestUrl, HttpRequestBase httpRequest)
- {
- return Enumerable.Empty();
- }
- }
-
- }
-}
diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs
index c9ed53ef2a..db1ce0ad3e 100644
--- a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs
+++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs
@@ -7,6 +7,7 @@ using System.Web.Mvc;
using System.Web.Routing;
using System.Xml;
using NUnit.Framework;
+using Umbraco.Web.Routing.Segments;
using Umbraco.Web.Security;
using umbraco.BusinessLogic;
using Umbraco.Core;
diff --git a/src/Umbraco.Tests/Routing/ContentSegmentProviderTests.cs b/src/Umbraco.Tests/Routing/ContentSegmentProviderTests.cs
new file mode 100644
index 0000000000..4675fd1a8e
--- /dev/null
+++ b/src/Umbraco.Tests/Routing/ContentSegmentProviderTests.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using NUnit.Framework;
+using Umbraco.Web.Routing.Segments;
+
+namespace Umbraco.Tests.Routing
+{
+ [TestFixture]
+ public class ContentSegmentProviderTests
+ {
+
+ [Test]
+ public void Get_Advertised_Segments()
+ {
+ var provider = new MyTestProvider();
+
+ Assert.AreEqual(3, provider.SegmentsAdvertised.Count());
+ }
+
+ [ContentSegment("Test1")]
+ [ContentSegment("Test2")]
+ [ContentSegment("Test3")]
+ [ContentSegment("Test2")]
+ private class MyTestProvider : ContentSegmentProvider
+ {
+ public override IDictionary GetSegmentsForRequest(Uri originalRequestUrl, Uri cleanedRequestUrl, HttpRequestBase httpRequest)
+ {
+ return new Dictionary();
+ }
+ }
+
+ }
+}
diff --git a/src/Umbraco.Tests/Routing/ContentSegmentsTests.cs b/src/Umbraco.Tests/Routing/ContentSegmentsTests.cs
new file mode 100644
index 0000000000..fa4b5a8d6f
--- /dev/null
+++ b/src/Umbraco.Tests/Routing/ContentSegmentsTests.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Web.Routing;
+using Umbraco.Web.Routing.Segments;
+
+namespace Umbraco.Tests.Routing
+{
+ [TestFixture]
+ public class ContentSegmentsTests
+ {
+
+ [Test]
+ public void Get_All_Segments_For_Request()
+ {
+ var result = RequestSegments.GetAllSegmentsForRequest(new ContentSegmentProvider[]
+ {
+ new MyTestProvider1(), new MyTestProvider2()
+ }, new Uri("http://localhost/test?blah=1"), new Uri("http://localhost/test/"), Mock.Of());
+
+ Assert.AreEqual(4, result.Count());
+ Assert.AreEqual(true, result["key1"]);
+ Assert.AreEqual("blah", result["key2"]);
+ Assert.AreEqual(9876, result["key3"]);
+ Assert.AreEqual(123, result["key4"]);
+ }
+
+ private class MyTestProvider1 : ContentSegmentProvider
+ {
+ public override IDictionary GetSegmentsForRequest(Uri originalRequestUrl, Uri cleanedRequestUrl, HttpRequestBase httpRequest)
+ {
+ return new Dictionary { { "key1", false }, { "key2", "blahdd" }, { "key3", 9876 } };
+ }
+ }
+
+ private class MyTestProvider2 : ContentSegmentProvider
+ {
+ public override IDictionary GetSegmentsForRequest(Uri originalRequestUrl, Uri cleanedRequestUrl, HttpRequestBase httpRequest)
+ {
+ return new Dictionary {{"key1", true}, {"key2", "blah"}, {"key4", 123}};
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs
index e387642ef2..4657ea6b78 100644
--- a/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs
+++ b/src/Umbraco.Tests/Routing/uQueryGetNodeIdByUrlTests.cs
@@ -1,4 +1,5 @@
-using Moq;
+using System.Linq;
+using Moq;
using NUnit.Framework;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Tests.TestHelpers;
@@ -6,6 +7,7 @@ using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Web.Routing;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.template;
+using Umbraco.Web.Routing.Segments;
namespace Umbraco.Tests.Routing
{
diff --git a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs
index d55a437976..673e00c204 100644
--- a/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs
+++ b/src/Umbraco.Tests/TestHelpers/BaseRoutingTest.cs
@@ -8,6 +8,7 @@ using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Web;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
using Umbraco.Web.Routing;
+using Umbraco.Web.Routing.Segments;
namespace Umbraco.Tests.TestHelpers
{
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index ad44294f60..bfe782f095 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -169,7 +169,7 @@
-
+
@@ -317,6 +317,7 @@
+
diff --git a/src/Umbraco.Web/Routing/RequestSegments.cs b/src/Umbraco.Web/Routing/RequestSegments.cs
new file mode 100644
index 0000000000..4f83bbfef2
--- /dev/null
+++ b/src/Umbraco.Web/Routing/RequestSegments.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using Umbraco.Web.Routing.Segments;
+
+namespace Umbraco.Web.Routing
+{
+ public class RequestSegments
+ {
+ private readonly IEnumerable _segmentProviders;
+ private readonly Lazy> _assignedSegments;
+
+ public RequestSegments(IEnumerable segmentProviders,
+ Uri originalRequestUrl,
+ Uri cleanedRequestUrl,
+ HttpRequestBase httpRequest)
+ {
+ _segmentProviders = segmentProviders;
+
+ _assignedSegments = new Lazy>(() =>
+ GetAllSegmentsForRequest(
+ _segmentProviders,
+ originalRequestUrl,
+ cleanedRequestUrl,
+ httpRequest));
+ }
+
+ ///
+ /// Returns the assigned segments for the current request
+ ///
+ public IDictionary AssignedSegments
+ {
+ get { return _assignedSegments.Value; }
+ }
+
+ ///
+ /// Returns true if any assigned segment value that is a boolean is set to true that matches the specified key
+ ///
+ ///
+ ///
+ ///
+ /// Example: RequestIs("Mobile") if a segment key is "Mobile" and it's value is a boolean true.
+ ///
+ public bool RequestIs(string segmentKey)
+ {
+ return AssignedSegments.Any(x => x.Key == segmentKey && x.Value is bool && (bool)x.Value);
+ }
+
+ ///
+ /// Returns true if any assigned segment has a value equal to the one specified
+ ///
+ ///
+ ///
+ public bool RequestContains(string segmentVal)
+ {
+ return AssignedSegments.Any(x => x.Value.ToString() == segmentVal);
+ }
+
+ ///
+ /// Returns true if any assigned segment key + value matches the specified parameters
+ ///
+ ///
+ ///
+ ///
+ public bool RequestEquals(string key, object val)
+ {
+ return AssignedSegments.Any(x => x.Key == key && x.Value == val);
+ }
+
+ ///
+ /// Internal so it can be tested
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static IDictionary GetAllSegmentsForRequest(
+ IEnumerable segmentProviders,
+ Uri originalRequestUrl,
+ Uri cleanedRequestUrl,
+ HttpRequestBase httpRequest)
+ {
+ //get all key/vals, there might be duplicates so we will simply take the last one in
+ var allKeyVals = segmentProviders.SelectMany(x =>
+ x.GetSegmentsForRequest(
+ originalRequestUrl,
+ cleanedRequestUrl,
+ httpRequest));
+
+ var d = new Dictionary();
+ foreach (var keyVal in allKeyVals)
+ {
+ d[keyVal.Key] = keyVal.Value;
+ }
+ return d;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs
index b999e2241a..d496ad4955 100644
--- a/src/Umbraco.Web/Routing/RoutingContext.cs
+++ b/src/Umbraco.Web/Routing/RoutingContext.cs
@@ -1,77 +1,100 @@
using System;
using System.Collections.Generic;
+using System.ServiceModel;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Web.Routing
{
-
- ///
- /// Provides context for the routing of a request.
- ///
+ ///
+ /// Provides context for the routing of a request.
+ ///
public class RoutingContext
{
- private readonly Lazy _urlProvider;
- private readonly Lazy> _publishedContentFinders;
- private readonly Lazy _publishedContentLastChanceFinder;
+
+ private readonly Lazy _urlProvider;
+ private readonly Lazy> _publishedContentFinders;
+ private readonly Lazy _publishedContentLastChanceFinder;
+
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The document lookups resolver.
- ///
- /// The nice urls provider.
- internal RoutingContext(
- UmbracoContext umbracoContext,
- IEnumerable contentFinders,
- IContentFinder contentLastChanceFinder,
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The document lookups resolver.
+ ///
+ /// The nice urls provider.
+ internal RoutingContext(
+ UmbracoContext umbracoContext,
+ IEnumerable contentFinders,
+ IContentFinder contentLastChanceFinder,
UrlProvider urlProvider)
+ : this(umbracoContext)
{
- UmbracoContext = umbracoContext;
_publishedContentFinders = new Lazy>(() => contentFinders, false);
_publishedContentLastChanceFinder = new Lazy(() => contentLastChanceFinder, false);
- _urlProvider = new Lazy(() => urlProvider, false);
+ _urlProvider = new Lazy(() => urlProvider, false);
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
internal RoutingContext(
UmbracoContext umbracoContext,
Lazy> contentFinders,
Lazy contentLastChanceFinder,
Lazy urlProvider)
+ : this(umbracoContext)
{
- UmbracoContext = umbracoContext;
+
_publishedContentFinders = contentFinders;
_publishedContentLastChanceFinder = contentLastChanceFinder;
_urlProvider = urlProvider;
}
- ///
- /// Gets the Umbraco context.
- ///
- public UmbracoContext UmbracoContext { get; private set; }
-
- ///
- /// Gets the published content finders.
- ///
- internal IEnumerable PublishedContentFinders
- {
- get { return _publishedContentFinders.Value; }
+ ///
+ /// Creates the lazy assigned segments logic
+ ///
+ ///
+ private RoutingContext(UmbracoContext umbracoContext)
+ {
+ UmbracoContext = umbracoContext;
}
- ///
- /// Gets the published content last chance finder.
- ///
- internal IContentFinder PublishedContentLastChanceFinder
- {
+
+
+ ///
+ /// Gets the Umbraco context.
+ ///
+ public UmbracoContext UmbracoContext { get; private set; }
+
+ ///
+ /// Gets the published content finders.
+ ///
+ internal IEnumerable PublishedContentFinders
+ {
+ get { return _publishedContentFinders.Value; }
+ }
+
+ ///
+ /// Gets the published content last chance finder.
+ ///
+ internal IContentFinder PublishedContentLastChanceFinder
+ {
get { return _publishedContentLastChanceFinder.Value; }
- }
+ }
- ///
- /// Gets the urls provider.
- ///
- public UrlProvider UrlProvider
- {
- get { return _urlProvider.Value; }
- }
+ ///
+ /// Gets the urls provider.
+ ///
+ public UrlProvider UrlProvider
+ {
+ get { return _urlProvider.Value; }
+ }
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Routing/Segments/BrowserCapabilitiesProvider.cs b/src/Umbraco.Web/Routing/Segments/BrowserCapabilitiesProvider.cs
new file mode 100644
index 0000000000..c87bda1ee7
--- /dev/null
+++ b/src/Umbraco.Web/Routing/Segments/BrowserCapabilitiesProvider.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Web;
+
+namespace Umbraco.Web.Routing.Segments
+{
+ ///
+ /// Assigns segments based on MS's HttpBrowserCapabilities object
+ ///
+ internal class BrowserCapabilitiesProvider : ContentSegmentProvider
+ {
+ public BrowserCapabilitiesProvider()
+ {
+ _browserCapabilityProps = typeof(HttpBrowserCapabilities).GetProperties()
+ .Where(x => PropNames.Contains(x.Name))
+ .ToArray();
+ }
+
+ private readonly PropertyInfo[] _browserCapabilityProps;
+
+ private static readonly string[] PropNames =
+ {
+ "IsMobileDevice",
+ "JavaApplets",
+ "MajorVersion",
+ "MinorVersion",
+ "MobileDeviceModel",
+ "MobileDeviceManufacturer",
+ "Platform"
+ };
+
+ public override IDictionary GetSegmentsForRequest(Uri originalRequestUrl, Uri cleanedRequestUrl, HttpRequestBase httpRequest)
+ {
+ return _browserCapabilityProps
+ .Select(x => new {key = x.Name, val = x.GetValue(httpRequest.Browser, null)})
+ .ToDictionary(key => key.key, val => val.val);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ContentVariations/ContentVariationAttribute.cs b/src/Umbraco.Web/Routing/Segments/ContentSegmentAttribute.cs
similarity index 56%
rename from src/Umbraco.Core/ContentVariations/ContentVariationAttribute.cs
rename to src/Umbraco.Web/Routing/Segments/ContentSegmentAttribute.cs
index a26e98ed3b..4f85a90524 100644
--- a/src/Umbraco.Core/ContentVariations/ContentVariationAttribute.cs
+++ b/src/Umbraco.Web/Routing/Segments/ContentSegmentAttribute.cs
@@ -1,13 +1,13 @@
using System;
-namespace Umbraco.Core.ContentVariations
+namespace Umbraco.Web.Routing.Segments
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public sealed class ContentVariationAttribute : Attribute
+ public sealed class ContentSegmentAttribute : Attribute
{
public string SegmentName { get; private set; }
- public ContentVariationAttribute(string segmentName)
+ public ContentSegmentAttribute(string segmentName)
{
SegmentName = segmentName;
}
diff --git a/src/Umbraco.Core/ContentVariations/ContentVariationProvider.cs b/src/Umbraco.Web/Routing/Segments/ContentSegmentProvider.cs
similarity index 82%
rename from src/Umbraco.Core/ContentVariations/ContentVariationProvider.cs
rename to src/Umbraco.Web/Routing/Segments/ContentSegmentProvider.cs
index c9a5dfea9a..558aa03140 100644
--- a/src/Umbraco.Core/ContentVariations/ContentVariationProvider.cs
+++ b/src/Umbraco.Web/Routing/Segments/ContentSegmentProvider.cs
@@ -1,23 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading;
-using System.Threading.Tasks;
using System.Web;
+using Umbraco.Core;
-namespace Umbraco.Core.ContentVariations
+namespace Umbraco.Web.Routing.Segments
{
-
-
///
/// Returns the segment names to assign to the current request
///
///
/// The provider also exposes via attributes which static segments can be applied to content variations
///
- public abstract class ContentVariationProvider
+ public abstract class ContentSegmentProvider
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private IEnumerable _advertised;
@@ -34,7 +31,7 @@ namespace Umbraco.Core.ContentVariations
if (_advertised == null)
{
lck.UpgradeToWriteLock();
- _advertised = this.GetType().GetCustomAttributes(false)
+ _advertised = this.GetType().GetCustomAttributes(false)
.Select(x => x.SegmentName)
.Distinct()
.ToArray();
@@ -45,13 +42,13 @@ namespace Umbraco.Core.ContentVariations
}
///
- /// Returns the segment names to assign to the current request
+ /// Returns the segment names and values to assign to the current request
///
///
///
///
///
- public abstract IEnumerable GetSegmentsForRequest(
+ public abstract IDictionary GetSegmentsForRequest(
Uri originalRequestUrl,
Uri cleanedRequestUrl,
HttpRequestBase httpRequest);
diff --git a/src/Umbraco.Web/Routing/Segments/ContentSegmentProviderResolver.cs b/src/Umbraco.Web/Routing/Segments/ContentSegmentProviderResolver.cs
new file mode 100644
index 0000000000..8ccde5ebf5
--- /dev/null
+++ b/src/Umbraco.Web/Routing/Segments/ContentSegmentProviderResolver.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.ObjectResolution;
+
+namespace Umbraco.Web.Routing.Segments
+{
+ ///
+ /// Resolver for content variation providers
+ ///
+ public sealed class ContentSegmentProviderResolver : ManyObjectsResolverBase
+ {
+ ///
+ /// Initializes a new instance of the class with
+ /// an initial list of provider types.
+ ///
+ /// The list of provider types
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal ContentSegmentProviderResolver(IEnumerable converters)
+ : base(converters)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// an initial list of provider types.
+ ///
+ /// The list of provider types
+ /// The resolver is created by the WebBootManager and thus the constructor remains internal.
+ internal ContentSegmentProviderResolver(params Type[] converters)
+ : base(converters)
+ { }
+
+ ///
+ /// Gets the providers.
+ ///
+ public IEnumerable Providers
+ {
+ get { return Values; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 03f5c397c9..e09ce61caf 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -463,6 +463,11 @@
+
+
+
+
+
diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs
index f8a29d2a06..4602ff0a22 100644
--- a/src/Umbraco.Web/UmbracoContext.cs
+++ b/src/Umbraco.Web/UmbracoContext.cs
@@ -4,6 +4,7 @@ using System.Web;
using Umbraco.Core;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
+using Umbraco.Web.Routing.Segments;
using Umbraco.Web.Security;
using umbraco;
using umbraco.BusinessLogic;
@@ -315,6 +316,21 @@ namespace Umbraco.Web
}
}
+ private RequestSegments _requestSegments;
+
+ ///
+ /// Return the current request segments
+ ///
+ public RequestSegments RequestSegments
+ {
+ get
+ {
+ return _requestSegments ?? (_requestSegments = new RequestSegments(
+ ContentSegmentProviderResolver.Current.Providers,
+ OriginalRequestUrl, CleanedUmbracoUrl, HttpContext.Request));
+ }
+ }
+
///
/// This is used internally for performance calculations, the ObjectCreated DateTime is set as soon as this
/// object is instantiated which in the web site is created during the BeginRequest phase.
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index 65112efce7..d9892ed1b5 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.cs
@@ -31,6 +31,7 @@ using Umbraco.Web.PropertyEditors;
using Umbraco.Web.PropertyEditors.ValueConverters;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
+using Umbraco.Web.Routing.Segments;
using Umbraco.Web.Security;
using Umbraco.Web.UI.JavaScript;
using Umbraco.Web.WebApi;
@@ -292,6 +293,9 @@ namespace Umbraco.Web
{
base.InitializeResolvers();
+ ContentSegmentProviderResolver.Current = new ContentSegmentProviderResolver(
+ typeof(BrowserCapabilitiesProvider));
+
XsltExtensionsResolver.Current = new XsltExtensionsResolver(() => PluginManager.Current.ResolveXsltExtensions());
//set the default RenderMvcController