Merge
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// A case-insensitive configuration converter for enumerations.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the enumeration.</typeparam>
|
||||
internal class CaseInsensitiveEnumConfigConverter<T> : ConfigurationConverterBase
|
||||
where T : struct
|
||||
{
|
||||
public override object ConvertFrom(ITypeDescriptorContext ctx, CultureInfo ci, object data)
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException("data");
|
||||
|
||||
//return Enum.Parse(typeof(T), (string)data, true);
|
||||
|
||||
T value;
|
||||
if (Enum.TryParse((string)data, true, out value))
|
||||
return value;
|
||||
|
||||
throw new Exception(string.Format("\"{0}\" is not valid {1} value. Valid values are: {2}.",
|
||||
data, typeof(T).Name,
|
||||
string.Join(", ", Enum.GetValues(typeof(T)).Cast<T>())));
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/Umbraco.Core/Configuration/ConfigurationKeyAttribute.cs
Normal file
44
src/Umbraco.Core/Configuration/ConfigurationKeyAttribute.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the configuration key for a section or a group.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class ConfigurationKeyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationKeyAttribute"/> class with a configuration key.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The configurationkey.</param>
|
||||
/// <remarks>The default configuration key type is <c>Umbraco</c>.</remarks>
|
||||
public ConfigurationKeyAttribute(string configurationKey)
|
||||
: this(configurationKey, ConfigurationKeyType.Umbraco)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigurationKeyAttribute"/> class with a configuration key and a key type.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The configurationkey.</param>
|
||||
/// <param name="keyType">The key type.</param>
|
||||
public ConfigurationKeyAttribute(string configurationKey, ConfigurationKeyType keyType)
|
||||
{
|
||||
ConfigurationKey = configurationKey;
|
||||
KeyType = keyType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration key.
|
||||
/// </summary>
|
||||
public string ConfigurationKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the configuration key type.
|
||||
/// </summary>
|
||||
public ConfigurationKeyType KeyType { get; private set; }
|
||||
}
|
||||
}
|
||||
28
src/Umbraco.Core/Configuration/ConfigurationKeyType.cs
Normal file
28
src/Umbraco.Core/Configuration/ConfigurationKeyType.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the type of configuration section keys.
|
||||
/// </summary>
|
||||
internal enum ConfigurationKeyType
|
||||
{
|
||||
/// <summary>
|
||||
/// An Umbraco section ie with path "/umbraco/sectionKey".
|
||||
/// </summary>
|
||||
Umbraco,
|
||||
|
||||
/// <summary>
|
||||
/// An Umbraco plugins section ie with path "/umbraco.plugins/sectionKey".
|
||||
/// </summary>
|
||||
Plugins,
|
||||
|
||||
/// <summary>
|
||||
/// A raw section ie with path "/sectionKey".
|
||||
/// </summary>
|
||||
Raw
|
||||
}
|
||||
}
|
||||
@@ -42,15 +42,24 @@ namespace Umbraco.Core.Configuration
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// used for unit tests
|
||||
/// Used in unit testing to reset all config items that were set with property setters (i.e. did not come from config)
|
||||
/// </summary>
|
||||
internal static void ResetCache()
|
||||
private static void ResetInternal()
|
||||
{
|
||||
_reservedUrlsCache = null;
|
||||
_reservedPaths = null;
|
||||
_reservedUrls = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets settings that were set programmatically, to their initial values.
|
||||
/// </summary>
|
||||
/// <remarks>To be used in unit tests.</remarks>
|
||||
internal static void Reset()
|
||||
{
|
||||
ResetInternal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reserved urls from web.config.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
{
|
||||
// note - still must work on how to support read-only and ResetSection for collections
|
||||
// note - still must work on how to spread config over files (aka DeepConfig in v5)
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Umbraco section within the configuration file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The requirement for these sections is to be read-only.</para>
|
||||
/// <para>However for unit tests purposes it is internally possible to override some values, and
|
||||
/// then calling <c>>ResetSection</c> should cancel these changes and bring the section back to
|
||||
/// what it was originally.</para>
|
||||
/// <para>The <c>UmbracoSettings.For{T}</c> method will return a section, either one that
|
||||
/// is in the configuration file, or a section that was created with default values.</para>
|
||||
/// </remarks>
|
||||
internal abstract class UmbracoConfigurationSection : ConfigurationSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the section actually is in the configuration file.
|
||||
/// </summary>
|
||||
protected bool IsPresent { get { return ElementInformation.IsPresent; } }
|
||||
|
||||
/// <summary>
|
||||
/// Resets settings that were set programmatically, to their initial values.
|
||||
/// </summary>
|
||||
/// <remarks>>To be used in unit tests.</remarks>
|
||||
internal protected virtual void ResetSection()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@ using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using System.Xml;
|
||||
using System.Configuration;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
|
||||
namespace Umbraco.Core.Configuration
|
||||
@@ -52,15 +54,14 @@ namespace Umbraco.Core.Configuration
|
||||
/// <summary>
|
||||
/// Used in unit testing to reset all config items that were set with property setters (i.e. did not come from config)
|
||||
/// </summary>
|
||||
internal static void ResetSetters()
|
||||
private static void ResetInternal()
|
||||
{
|
||||
_addTrailingSlash = null;
|
||||
_forceSafeAliases = null;
|
||||
_useLegacySchema = null;
|
||||
_useDomainPrefixes = null;
|
||||
_umbracoLibraryCacheDuration = null;
|
||||
_trySkipIisCustomErrors = null;
|
||||
SettingsFilePath = null;
|
||||
SettingsFilePath = null;
|
||||
}
|
||||
|
||||
internal const string TempFriendlyXmlChildContainerNodename = ""; // "children";
|
||||
@@ -503,7 +504,7 @@ namespace Umbraco.Core.Configuration
|
||||
private static IEnumerable<RazorDataTypeModelStaticMappingItem> _razorDataTypeModelStaticMapping;
|
||||
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
|
||||
|
||||
public static IEnumerable<RazorDataTypeModelStaticMappingItem> RazorDataTypeModelStaticMapping
|
||||
internal static IEnumerable<RazorDataTypeModelStaticMappingItem> RazorDataTypeModelStaticMapping
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -652,23 +653,22 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
private static bool? _trySkipIisCustomErrors;
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether to try to skip IIS custom errors.
|
||||
/// </summary>
|
||||
[UmbracoWillObsolete("Use UmbracoSettings.For<WebRouting>.TrySkipIisCustomErrors instead.")]
|
||||
internal static bool TrySkipIisCustomErrors
|
||||
{
|
||||
get { return GetKeyValue("/settings/web.routing/@trySkipIisCustomErrors", false); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating where to try to skip IIS custom errors.
|
||||
/// Gets a value indicating whether internal redirect preserves the template.
|
||||
/// </summary>
|
||||
public static bool TrySkipIisCustomErrors
|
||||
{
|
||||
get
|
||||
{
|
||||
// default: false
|
||||
return _trySkipIisCustomErrors ?? GetKeyValue("/settings/web.routing/@trySkipIisCustomErrors", false);
|
||||
}
|
||||
internal set
|
||||
{
|
||||
// used for unit testing
|
||||
_trySkipIisCustomErrors = value;
|
||||
}
|
||||
[UmbracoWillObsolete("Use UmbracoSettings.For<WebRouting>.InternalRedirectPerservesTemplate instead.")]
|
||||
internal static bool InternalRedirectPreservesTemplate
|
||||
{
|
||||
get { return GetKeyValue("/settings/web.routing/@internalRedirectPreservesTemplate", false); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1440,6 +1440,95 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Extensible settings
|
||||
|
||||
/// <summary>
|
||||
/// Resets settings that were set programmatically, to their initial values.
|
||||
/// </summary>
|
||||
/// <remarks>To be used in unit tests.</remarks>
|
||||
internal static void Reset()
|
||||
{
|
||||
ResetInternal();
|
||||
|
||||
using (new WriteLock(SectionsLock))
|
||||
{
|
||||
foreach (var section in Sections.Values)
|
||||
section.ResetSection();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ReaderWriterLockSlim SectionsLock = new ReaderWriterLockSlim();
|
||||
private static readonly Dictionary<Type, UmbracoConfigurationSection> Sections = new Dictionary<Type, UmbracoConfigurationSection>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified UmbracoConfigurationSection.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the UmbracoConfigurationSectiont.</typeparam>
|
||||
/// <returns>The UmbracoConfigurationSection of the specified type.</returns>
|
||||
internal static T For<T>()
|
||||
where T : UmbracoConfigurationSection, new()
|
||||
{
|
||||
var sectionType = typeof (T);
|
||||
using (new WriteLock(SectionsLock))
|
||||
{
|
||||
if (Sections.ContainsKey(sectionType)) return Sections[sectionType] as T;
|
||||
|
||||
var attr = sectionType.GetCustomAttribute<ConfigurationKeyAttribute>(false);
|
||||
if (attr == null)
|
||||
throw new InvalidOperationException(string.Format("Type \"{0}\" is missing attribute ConfigurationKeyAttribute.", sectionType.FullName));
|
||||
|
||||
var sectionKey = attr.ConfigurationKey;
|
||||
if (string.IsNullOrWhiteSpace(sectionKey))
|
||||
throw new InvalidOperationException(string.Format("Type \"{0}\" ConfigurationKeyAttribute value is null or empty.", sectionType.FullName));
|
||||
|
||||
var keyType = attr.KeyType;
|
||||
var section = GetSection(sectionType, sectionKey, keyType);
|
||||
|
||||
Sections[sectionType] = section;
|
||||
return section as T;
|
||||
}
|
||||
}
|
||||
|
||||
private static UmbracoConfigurationSection GetSection(Type sectionType, string key, ConfigurationKeyType keyType)
|
||||
{
|
||||
if (!sectionType.Inherits<UmbracoConfigurationSection>())
|
||||
throw new ArgumentException(string.Format(
|
||||
"Type \"{0}\" does not inherit from UmbracoConfigurationSection.", sectionType.FullName), "sectionType");
|
||||
|
||||
switch (keyType)
|
||||
{
|
||||
case ConfigurationKeyType.Umbraco:
|
||||
key = "umbraco/" + key;
|
||||
break;
|
||||
case ConfigurationKeyType.Plugins:
|
||||
key = "umbraco.plugins/" + key;
|
||||
break;
|
||||
case ConfigurationKeyType.Raw:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("keyType", keyType, "Invalid ConfigurationKeyType value.");
|
||||
}
|
||||
|
||||
var section = ConfigurationManager.GetSection(key);
|
||||
|
||||
if (section != null && section.GetType() != sectionType)
|
||||
throw new InvalidCastException(string.Format("Section at key \"{0}\" is of type \"{1}\" and not \"{2}\".",
|
||||
key, section.GetType().FullName, sectionType.FullName));
|
||||
|
||||
if (section != null) return section as UmbracoConfigurationSection;
|
||||
|
||||
section = Activator.CreateInstance(sectionType) as UmbracoConfigurationSection;
|
||||
|
||||
if (section == null)
|
||||
throw new NullReferenceException(string.Format(
|
||||
"Activator failed to create an instance of type \"{0}\" for key\"{1}\" and returned null.",
|
||||
sectionType.FullName, key));
|
||||
|
||||
return section as UmbracoConfigurationSection;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -114,10 +114,14 @@
|
||||
<Compile Include="CodeAnnotations\UmbracoWillObsoleteAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
|
||||
<Compile Include="Configuration\CaseInsensitiveEnumConfigConverter.cs" />
|
||||
<Compile Include="Configuration\ClientDependencyConfiguration.cs" />
|
||||
<Compile Include="Configuration\ConfigurationKeyAttribute.cs" />
|
||||
<Compile Include="Configuration\ConfigurationKeyType.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElement.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
|
||||
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
|
||||
<Compile Include="Configuration\UmbracoConfigurationSection.cs" />
|
||||
<Compile Include="Configuration\UmbracoVersion.cs" />
|
||||
<Compile Include="CoreBootManager.cs" />
|
||||
<Compile Include="DatabaseContext.cs" />
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Umbraco.Tests.ContentStores
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
UmbracoSettings.ResetSetters();
|
||||
UmbracoSettings.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace Umbraco.Tests.Routing
|
||||
Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177));
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
routingContext.UrlProvider.EnforceAbsoluteUrls = true;
|
||||
routingContext.UrlProvider.Mode = UrlProviderMode.Absolute;
|
||||
Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177));
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Umbraco.Tests.Routing
|
||||
SettingsForTests.UseDomainPrefixes = true;
|
||||
Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999));
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
routingContext.UrlProvider.EnforceAbsoluteUrls = true;
|
||||
routingContext.UrlProvider.Mode = UrlProviderMode.Absolute;
|
||||
Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace Umbraco.Tests.Routing
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311));
|
||||
|
||||
SettingsForTests.UseDomainPrefixes = false;
|
||||
routingContext.UrlProvider.EnforceAbsoluteUrls = true;
|
||||
routingContext.UrlProvider.Mode = UrlProviderMode.Absolute;
|
||||
Assert.AreEqual("http://domain1.com/en/1001-1-1/", routingContext.UrlProvider.GetUrl(100111));
|
||||
Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311));
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
//reset settings
|
||||
SettingsForTests.Reset();
|
||||
UmbracoSettings.ResetSetters();
|
||||
|
||||
TestHelper.CleanContentDirectories();
|
||||
//reset the app context, this should reset most things that require resetting like ALL resolvers
|
||||
ApplicationContext.Current.DisposeIfDisposable();
|
||||
|
||||
@@ -99,8 +99,9 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
UmbracoSettings.ResetSetters();
|
||||
GlobalSettings.ResetCache();
|
||||
UmbracoSettings.Reset();
|
||||
GlobalSettings.Reset();
|
||||
|
||||
foreach (var kvp in SavedAppSettings)
|
||||
ConfigurationManager.AppSettings.Set(kvp.Key, kvp.Value);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Tests
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
UmbracoSettings.ResetSetters();
|
||||
UmbracoSettings.Reset();
|
||||
}
|
||||
|
||||
// test normal urls
|
||||
|
||||
@@ -257,9 +257,15 @@
|
||||
over and render its build-in error page. See MS doc for HttpResponseBase.TrySkipIisCustomErrors.
|
||||
The default value is false, for backward compatibility reasons, which means that IIS _will_ take
|
||||
over, and _prevent_ Umbraco 404 pages to show.
|
||||
@internalRedirectPreservesTemplate
|
||||
By default as soon as we're not displaying the initial document, we reset the template set by the
|
||||
finder or by the alt. template. Set this option to true to preserve the template set by the finder
|
||||
or by the alt. template, in case of an internal redirect.
|
||||
(false by default, and in fact should remain false unless you know what you're doing)
|
||||
-->
|
||||
<web.routing
|
||||
trySkipIisCustomErrors="false">
|
||||
trySkipIisCustomErrors="false"
|
||||
internalRedirectPreservesTemplate="false">
|
||||
</web.routing>
|
||||
|
||||
</settings>
|
||||
|
||||
@@ -209,9 +209,15 @@
|
||||
over and render its build-in error page. See MS doc for HttpResponseBase.TrySkipIisCustomErrors.
|
||||
The default value is false, for backward compatibility reasons, which means that IIS _will_ take
|
||||
over, and _prevent_ Umbraco 404 pages to show.
|
||||
@internalRedirectPreservesTemplate
|
||||
By default as soon as we're not displaying the initial document, we reset the template set by the
|
||||
finder or by the alt. template. Set this option to true to preserve the template set by the finder
|
||||
or by the alt. template, in case of an internal redirect.
|
||||
(false by default, and in fact should remain false unless you know what you're doing)
|
||||
-->
|
||||
<web.routing
|
||||
trySkipIisCustomErrors="false">
|
||||
trySkipIisCustomErrors="false"
|
||||
internalRedirectPreservesTemplate="false">
|
||||
</web.routing>
|
||||
|
||||
</settings>
|
||||
@@ -1,22 +1,19 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Web.SessionState;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Web.BaseRest
|
||||
{
|
||||
internal class BaseRestHandler : IHttpHandler, IRequiresSessionState
|
||||
{
|
||||
static string _baseUrl;
|
||||
static readonly string BaseUrl;
|
||||
|
||||
static BaseRestHandler()
|
||||
{
|
||||
_baseUrl = UriUtility.ToAbsolute(Umbraco.Core.IO.SystemDirectories.Base).ToLower();
|
||||
if (!_baseUrl.EndsWith("/"))
|
||||
_baseUrl += "/";
|
||||
BaseUrl = UriUtility.ToAbsolute(Core.IO.SystemDirectories.Base).ToLower();
|
||||
if (!BaseUrl.EndsWith("/"))
|
||||
BaseUrl += "/";
|
||||
}
|
||||
|
||||
public bool IsReusable
|
||||
@@ -31,8 +28,8 @@ namespace Umbraco.Web.BaseRest
|
||||
/// <returns>A value indicating whether the specified Uri should be routed to the BaseRestHandler.</returns>
|
||||
public static bool IsBaseRestRequest(Uri uri)
|
||||
{
|
||||
return Umbraco.Core.Configuration.UmbracoSettings.EnableBaseRestHandler
|
||||
&& uri.AbsolutePath.ToLowerInvariant().StartsWith(_baseUrl);
|
||||
return Core.Configuration.UmbracoSettings.For<Configuration.BaseRestSection>().Enabled
|
||||
&& uri.AbsolutePath.ToLowerInvariant().StartsWith(BaseUrl);
|
||||
}
|
||||
|
||||
public void ProcessRequest(HttpContext context)
|
||||
@@ -40,17 +37,17 @@ namespace Umbraco.Web.BaseRest
|
||||
string url = context.Request.RawUrl;
|
||||
|
||||
// sanitize and split the url
|
||||
url = url.Substring(_baseUrl.Length);
|
||||
url = url.Substring(BaseUrl.Length);
|
||||
if (url.ToLower().Contains(".aspx"))
|
||||
url = url.Substring(0, url.IndexOf(".aspx"));
|
||||
url = url.Substring(0, url.IndexOf(".aspx", StringComparison.OrdinalIgnoreCase));
|
||||
if (url.ToLower().Contains("?"))
|
||||
url = url.Substring(0, url.IndexOf("?"));
|
||||
var urlParts = url.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
url = url.Substring(0, url.IndexOf("?", StringComparison.OrdinalIgnoreCase));
|
||||
var urlParts = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// by default, return xml content
|
||||
context.Response.ContentType = "text/xml";
|
||||
|
||||
// ensure that we have a valid request ie /base/library/method/[parameter].aspx
|
||||
// ensure that we have a valid request ie /base/library/method/[parameters].aspx
|
||||
if (urlParts.Length < 2)
|
||||
{
|
||||
context.Response.Write("<error>Invalid request, missing parts.</error>");
|
||||
@@ -60,10 +57,11 @@ namespace Umbraco.Web.BaseRest
|
||||
return;
|
||||
}
|
||||
|
||||
string extensionAlias = urlParts[0];
|
||||
string methodName = urlParts[1];
|
||||
var extensionAlias = urlParts[0];
|
||||
var methodName = urlParts[1];
|
||||
var paramsCount = urlParts.Length - 2;
|
||||
|
||||
var method = RestExtensionMethodInfo.GetMethod(extensionAlias, methodName);
|
||||
var method = RestExtensionMethodInfo.GetMethod(extensionAlias, methodName, paramsCount);
|
||||
|
||||
if (!method.Exists)
|
||||
{
|
||||
@@ -84,7 +82,7 @@ namespace Umbraco.Web.BaseRest
|
||||
|
||||
TrySetCulture();
|
||||
|
||||
string result = method.Invoke(urlParts.Skip(2).ToArray());
|
||||
var result = method.Invoke(urlParts.Skip(2).ToArray());
|
||||
if (result.Length >= 7 && result.Substring(0, 7) == "<error>")
|
||||
{
|
||||
context.Response.StatusCode = 500;
|
||||
@@ -98,15 +96,15 @@ namespace Umbraco.Web.BaseRest
|
||||
|
||||
#region from baseHttpModule.cs
|
||||
|
||||
// fixme - is this ok?
|
||||
// note - is this ok?
|
||||
|
||||
private static void TrySetCulture()
|
||||
{
|
||||
string domain = HttpContext.Current.Request.Url.Host; // host only
|
||||
var domain = HttpContext.Current.Request.Url.Host; // host only
|
||||
if (TrySetCulture(domain)) return;
|
||||
|
||||
domain = HttpContext.Current.Request.Url.Authority; // host with port
|
||||
if (TrySetCulture(domain)) return;
|
||||
TrySetCulture(domain);
|
||||
}
|
||||
|
||||
private static bool TrySetCulture(string domain)
|
||||
|
||||
@@ -1,17 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Configuration;
|
||||
using System.Configuration;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.BaseRest.Configuration
|
||||
{
|
||||
public class BaseRestSection : ConfigurationSection
|
||||
{
|
||||
[ConfigurationProperty("", IsKey = false, IsRequired = false, IsDefaultCollection = true)]
|
||||
// note: the name should be "BaseRest" but we keep it "BaseRestSection" for compat. reasons.
|
||||
|
||||
[ConfigurationKey("BaseRestExtensions", ConfigurationKeyType.Raw)]
|
||||
internal class BaseRestSection : UmbracoConfigurationSection
|
||||
{
|
||||
private const string KeyEnabled = "enabled";
|
||||
|
||||
private bool? _enabled;
|
||||
|
||||
internal protected override void ResetSection()
|
||||
{
|
||||
base.ResetSection();
|
||||
|
||||
_enabled = null;
|
||||
}
|
||||
|
||||
[ConfigurationProperty("", IsKey = false, IsRequired = false, IsDefaultCollection = true)]
|
||||
public ExtensionElementCollection Items
|
||||
{
|
||||
get { return (ExtensionElementCollection)base[""]; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether base rest extensions are enabled.
|
||||
/// </summary>
|
||||
[ConfigurationProperty(KeyEnabled, DefaultValue = true, IsRequired = false)]
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _enabled ?? (IsPresent
|
||||
? (bool)this[KeyEnabled]
|
||||
: true);
|
||||
}
|
||||
internal set { _enabled = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,20 @@ namespace Umbraco.Web.BaseRest.Configuration
|
||||
[ConfigurationCollection(typeof(ExtensionElement), CollectionType = ConfigurationElementCollectionType.BasicMapAlternate)]
|
||||
public class ExtensionElement : ConfigurationElementCollection
|
||||
{
|
||||
const string Key_Alias = "alias";
|
||||
const string Key_Type = "type";
|
||||
const string Key_Method = "method";
|
||||
const string KeyAlias = "alias";
|
||||
const string KeyType = "type";
|
||||
const string KeyMethod = "method";
|
||||
|
||||
[ConfigurationProperty(Key_Alias, IsKey = true, IsRequired = true)]
|
||||
[ConfigurationProperty(KeyAlias, IsKey = true, IsRequired = true)]
|
||||
public string Alias
|
||||
{
|
||||
get { return (string)base[Key_Alias]; }
|
||||
get { return (string)base[KeyAlias]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(Key_Type, IsKey = false, IsRequired = true)]
|
||||
[ConfigurationProperty(KeyType, IsKey = false, IsRequired = true)]
|
||||
public string Type
|
||||
{
|
||||
get { return (string)base[Key_Type]; }
|
||||
get { return (string)base[KeyType]; }
|
||||
}
|
||||
|
||||
public override ConfigurationElementCollectionType CollectionType
|
||||
@@ -32,12 +32,12 @@ namespace Umbraco.Web.BaseRest.Configuration
|
||||
|
||||
protected override string ElementName
|
||||
{
|
||||
get { return Key_Method; }
|
||||
get { return KeyMethod; }
|
||||
}
|
||||
|
||||
protected override bool IsElementName(string elementName)
|
||||
{
|
||||
return elementName.Equals(Key_Method, StringComparison.InvariantCultureIgnoreCase);
|
||||
return elementName.Equals(KeyMethod, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Web.BaseRest.Configuration
|
||||
[ConfigurationCollection(typeof(ExtensionElement), CollectionType = ConfigurationElementCollectionType.BasicMapAlternate)]
|
||||
public class ExtensionElementCollection : ConfigurationElementCollection
|
||||
{
|
||||
const string Key_Extension = "extension";
|
||||
const string KeyExtension = "extension";
|
||||
|
||||
public override ConfigurationElementCollectionType CollectionType
|
||||
{
|
||||
@@ -18,12 +18,12 @@ namespace Umbraco.Web.BaseRest.Configuration
|
||||
|
||||
protected override string ElementName
|
||||
{
|
||||
get { return Key_Extension; }
|
||||
get { return KeyExtension; }
|
||||
}
|
||||
|
||||
protected override bool IsElementName(string elementName)
|
||||
{
|
||||
return elementName.Equals(Key_Extension, StringComparison.InvariantCultureIgnoreCase);
|
||||
return elementName.Equals(KeyExtension, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
|
||||
@@ -1,54 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Configuration;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Web.BaseRest.Configuration
|
||||
{
|
||||
public class MethodElement : ConfigurationElement
|
||||
{
|
||||
const string Key_Name = "name";
|
||||
const string Key_AllowAll = "allowAll";
|
||||
const string Key_AllowGroup = "allowGroup";
|
||||
const string Key_AllowType = "allowType";
|
||||
const string Key_AllowMember = "allowMember";
|
||||
const string Key_ReturnXml = "returnXml";
|
||||
const string KeyName = "name";
|
||||
const string KeyAllowAll = "allowAll";
|
||||
const string KeyAllowGroup = "allowGroup";
|
||||
const string KeyAllowType = "allowType";
|
||||
const string KeyAllowMember = "allowMember";
|
||||
const string KeyReturnXml = "returnXml";
|
||||
|
||||
[ConfigurationProperty(Key_Name, IsKey = true, IsRequired = true)]
|
||||
[ConfigurationProperty(KeyName, IsKey = true, IsRequired = true)]
|
||||
public string Name
|
||||
{
|
||||
get { return (string)base[Key_Name]; }
|
||||
get { return (string)base[KeyName]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(Key_AllowAll, IsKey = false, IsRequired = false, DefaultValue = false)]
|
||||
[ConfigurationProperty(KeyAllowAll, IsKey = false, IsRequired = false, DefaultValue = false)]
|
||||
public bool AllowAll
|
||||
{
|
||||
get { return (bool)base[Key_AllowAll]; }
|
||||
get { return (bool)base[KeyAllowAll]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(Key_AllowGroup, IsKey = false, IsRequired = false, DefaultValue = null)]
|
||||
[ConfigurationProperty(KeyAllowGroup, IsKey = false, IsRequired = false, DefaultValue = null)]
|
||||
public string AllowGroup
|
||||
{
|
||||
get { return (string)base[Key_AllowGroup]; }
|
||||
get { return (string)base[KeyAllowGroup]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(Key_AllowType, IsKey = false, IsRequired = false, DefaultValue = null)]
|
||||
[ConfigurationProperty(KeyAllowType, IsKey = false, IsRequired = false, DefaultValue = null)]
|
||||
public string AllowType
|
||||
{
|
||||
get { return (string)base[Key_AllowType]; }
|
||||
get { return (string)base[KeyAllowType]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(Key_AllowMember, IsKey = false, IsRequired = false, DefaultValue = null)]
|
||||
[ConfigurationProperty(KeyAllowMember, IsKey = false, IsRequired = false, DefaultValue = null)]
|
||||
public string AllowMember
|
||||
{
|
||||
get { return (string)base[Key_AllowMember]; }
|
||||
get { return (string)base[KeyAllowMember]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty(Key_ReturnXml, IsKey = false, IsRequired = false, DefaultValue = true)]
|
||||
[ConfigurationProperty(KeyReturnXml, IsKey = false, IsRequired = false, DefaultValue = true)]
|
||||
public bool ReturnXml
|
||||
{
|
||||
get { return (bool)base[Key_ReturnXml]; }
|
||||
get { return (bool)base[KeyReturnXml]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
@@ -16,19 +16,23 @@ namespace Umbraco.Web.BaseRest
|
||||
{
|
||||
#region Utilities
|
||||
|
||||
static char[] Split = new char[] { ',' };
|
||||
static readonly char[] Split = new[] { ',' };
|
||||
|
||||
static string[] SplitString(string s)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
return new string[] { };
|
||||
else
|
||||
return s.ToLower().Split(Split, StringSplitOptions.RemoveEmptyEntries);
|
||||
return string.IsNullOrWhiteSpace(s)
|
||||
? new string[] { }
|
||||
: s.ToLower().Split(Split, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
static string GetAttribute(XmlNode node, string name)
|
||||
static string GetAttribute(XmlNode node, string name)
|
||||
{
|
||||
var attribute = node.Attributes[name];
|
||||
if (node == null)
|
||||
throw new ArgumentNullException("node");
|
||||
var attributes = node.Attributes;
|
||||
if (attributes == null)
|
||||
throw new ArgumentException(@"Node has no Attributes collection.", "node");
|
||||
var attribute = attributes[name];
|
||||
return attribute == null ? null : attribute.Value;
|
||||
}
|
||||
|
||||
@@ -36,28 +40,28 @@ namespace Umbraco.Web.BaseRest
|
||||
|
||||
private RestExtensionMethodInfo()
|
||||
{
|
||||
this.Exists = false;
|
||||
Exists = false;
|
||||
}
|
||||
|
||||
private RestExtensionMethodInfo(bool allowAll, string allowGroup, string allowType, string allowMember, bool returnXml, MethodInfo method)
|
||||
{
|
||||
this.Exists = true;
|
||||
Exists = true;
|
||||
_allowAll = allowAll;
|
||||
_allowGroups = SplitString(allowGroup);
|
||||
_allowTypes = SplitString(allowType);
|
||||
_allowMembers = SplitString(allowMember);
|
||||
this.ReturnXml = returnXml;
|
||||
ReturnXml = returnXml;
|
||||
_method = method;
|
||||
}
|
||||
|
||||
static RestExtensionMethodInfo MissingMethod = new RestExtensionMethodInfo();
|
||||
static Dictionary<string, RestExtensionMethodInfo> _cache = new Dictionary<string, RestExtensionMethodInfo>();
|
||||
static readonly RestExtensionMethodInfo MissingMethod = new RestExtensionMethodInfo();
|
||||
static readonly Dictionary<string, RestExtensionMethodInfo> Cache = new Dictionary<string, RestExtensionMethodInfo>();
|
||||
|
||||
bool _allowAll;
|
||||
string[] _allowGroups;
|
||||
string[] _allowTypes;
|
||||
string[] _allowMembers;
|
||||
MethodInfo _method;
|
||||
readonly bool _allowAll;
|
||||
readonly string[] _allowGroups;
|
||||
readonly string[] _allowTypes;
|
||||
readonly string[] _allowMembers;
|
||||
readonly MethodInfo _method;
|
||||
|
||||
public bool Exists { get; private set; }
|
||||
public bool ReturnXml { get; private set; }
|
||||
@@ -68,10 +72,12 @@ namespace Umbraco.Web.BaseRest
|
||||
// by looking everywhere (configuration, attributes, legacy attributes)
|
||||
// returns MissingMethod (ie .Exists == false) if not found
|
||||
//
|
||||
public static RestExtensionMethodInfo GetMethod(string extensionAlias, string methodName)
|
||||
public static RestExtensionMethodInfo GetMethod(string extensionAlias, string methodName, int paramsCount)
|
||||
{
|
||||
return GetFromConfiguration(extensionAlias, methodName)
|
||||
?? GetFromAttribute(extensionAlias, methodName)
|
||||
// note - legacy does not support paramsCount
|
||||
|
||||
return GetFromConfiguration(extensionAlias, methodName, paramsCount)
|
||||
?? GetFromAttribute(extensionAlias, methodName, paramsCount)
|
||||
?? GetFromLegacyConfiguration(extensionAlias, methodName) // that one should be obsoleted at some point
|
||||
?? GetFromLegacyAttribute(extensionAlias, methodName) // that one should be obsoleted at some point
|
||||
?? MissingMethod;
|
||||
@@ -83,36 +89,40 @@ namespace Umbraco.Web.BaseRest
|
||||
//
|
||||
static RestExtensionMethodInfo GetFromLegacyConfiguration(string extensionAlias, string methodName)
|
||||
{
|
||||
const string ExtensionXPath = "/RestExtensions/ext [@alias='{0}']";
|
||||
const string MethodXPath = "./permission [@method='{0}']";
|
||||
const string extensionXPath = "/RestExtensions/ext [@alias='{0}']";
|
||||
const string methodXPath = "./permission [@method='{0}']";
|
||||
|
||||
var config = (Configuration.BaseRestSection)System.Configuration.ConfigurationManager.GetSection("BaseRestExtensions");
|
||||
|
||||
if (config == null)
|
||||
return null; // does not exist
|
||||
|
||||
// fixme - at the moment we reload the config file each time
|
||||
// note - at the moment we reload the config file each time
|
||||
// we have to support live edits of the config file for backward compatibility reason
|
||||
// so if we want to cache, we'd also need to implement a watcher on the config file...
|
||||
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(IOHelper.MapPath(SystemFiles.RestextensionsConfig));
|
||||
|
||||
var eNode = doc.SelectSingleNode(string.Format(ExtensionXPath, extensionAlias));
|
||||
var eNode = doc.SelectSingleNode(string.Format(extensionXPath, extensionAlias));
|
||||
|
||||
if (eNode == null)
|
||||
return null; // does not exist
|
||||
|
||||
var mNode = eNode.SelectSingleNode(string.Format(MethodXPath, methodName));
|
||||
var mNode = eNode.SelectSingleNode(string.Format(methodXPath, methodName));
|
||||
|
||||
if (mNode == null)
|
||||
return null; // does not exist
|
||||
|
||||
string assemblyName = eNode.Attributes["assembly"].Value;
|
||||
var attributes = eNode.Attributes;
|
||||
if (attributes == null)
|
||||
return null; // has no attributes
|
||||
|
||||
var assemblyName = attributes["assembly"].Value;
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
|
||||
string typeName = eNode.Attributes["type"].Value;
|
||||
Type type = assembly.GetType(typeName);
|
||||
var typeName = attributes["type"].Value;
|
||||
var type = assembly.GetType(typeName);
|
||||
|
||||
if (type == null)
|
||||
return null; // does not exist
|
||||
@@ -137,31 +147,45 @@ namespace Umbraco.Web.BaseRest
|
||||
// by looking at the configuration file
|
||||
// returns null if not found
|
||||
//
|
||||
static RestExtensionMethodInfo GetFromConfiguration(string extensionAlias, string methodName)
|
||||
static RestExtensionMethodInfo GetFromConfiguration(string extensionAlias, string methodName, int paramsCount)
|
||||
{
|
||||
var config = (Configuration.BaseRestSection)System.Configuration.ConfigurationManager.GetSection("BaseRestExtensions");
|
||||
var config = Core.Configuration.UmbracoSettings.For<Configuration.BaseRestSection>();
|
||||
|
||||
if (config == null)
|
||||
return null; // does not exist
|
||||
|
||||
Configuration.ExtensionElement configExtension = config.Items[extensionAlias];
|
||||
var configExtension = config.Items[extensionAlias];
|
||||
if (configExtension == null)
|
||||
return null; // does not exist
|
||||
|
||||
Configuration.MethodElement configMethod = configExtension[methodName];
|
||||
var configMethod = configExtension[methodName];
|
||||
if (configMethod == null)
|
||||
return null; // does not exist
|
||||
|
||||
MethodInfo method;
|
||||
MethodInfo method = null;
|
||||
try
|
||||
{
|
||||
var parts = configExtension.Type.Split(',');
|
||||
if (parts.Length > 2)
|
||||
throw new Exception(string.Format("Failed to load extension '{0}', invalid type."));
|
||||
throw new Exception(string.Format("Failed to load extension '{0}', invalid type.", configExtension.Type));
|
||||
|
||||
var assembly = parts.Length == 1 ? Assembly.GetExecutingAssembly() : Assembly.Load(parts[1]);
|
||||
var type = assembly.GetType(parts[0]);
|
||||
method = type.GetMethod(methodName);
|
||||
|
||||
if (type == null)
|
||||
throw new Exception(string.Format("Could not get type \"{0}\".", parts[0]));
|
||||
|
||||
var methods = type.GetMethods()
|
||||
.Where(m => m.Name == methodName)
|
||||
.Where(m => m.GetParameters().Count() == paramsCount)
|
||||
.ToArray();
|
||||
|
||||
if (methods.Length > 1)
|
||||
throw new Exception(string.Format("Method \"{0}\" has many overloads with same number of parameters.", methodName));
|
||||
|
||||
if (methods.Length > 0)
|
||||
{
|
||||
method = methods[0];
|
||||
if (!method.IsPublic || !method.IsStatic)
|
||||
throw new Exception(string.Format("Method \"{0}\" has to be public and static.", methodName));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -187,20 +211,24 @@ namespace Umbraco.Web.BaseRest
|
||||
{
|
||||
// here we can cache because any change would trigger an app restart anyway
|
||||
|
||||
string cacheKey = extensionAlias + "." + methodName;
|
||||
lock (_cache)
|
||||
var cacheKey = extensionAlias + "." + methodName;
|
||||
lock (Cache)
|
||||
{
|
||||
// if it's in the cache, return
|
||||
if (_cache.ContainsKey(cacheKey))
|
||||
return _cache[cacheKey];
|
||||
if (Cache.ContainsKey(cacheKey))
|
||||
return Cache[cacheKey];
|
||||
}
|
||||
|
||||
// find an extension with that alias, then find a method with that name,
|
||||
// which has been properly marked with the attribute, and use the attribute
|
||||
// properties to setup a RestExtensionMethodInfo
|
||||
|
||||
// note: add #pragma - yes it's obsolete but we still want to support it for the time being
|
||||
|
||||
var extensions = PluginManager.Current.ResolveLegacyRestExtensions()
|
||||
#pragma warning disable 612,618
|
||||
.Where(type => type.GetCustomAttribute<global::umbraco.presentation.umbracobase.RestExtension>(false).GetAlias() == extensionAlias);
|
||||
#pragma warning restore 612,618
|
||||
|
||||
RestExtensionMethodInfo info = null;
|
||||
|
||||
@@ -209,7 +237,9 @@ namespace Umbraco.Web.BaseRest
|
||||
var method = extension.GetMethod(methodName);
|
||||
if (method == null) continue; // not implementing the method = ignore
|
||||
|
||||
#pragma warning disable 612,618
|
||||
var attribute = method.GetCustomAttributes(typeof(global::umbraco.presentation.umbracobase.RestExtensionMethod), false).Cast<global::umbraco.presentation.umbracobase.RestExtensionMethod>().SingleOrDefault();
|
||||
#pragma warning restore 612,618
|
||||
if (attribute == null) continue; // method has not attribute = ignore
|
||||
|
||||
// got it!
|
||||
@@ -219,9 +249,9 @@ namespace Umbraco.Web.BaseRest
|
||||
method);
|
||||
|
||||
// cache
|
||||
lock (_cache)
|
||||
lock (Cache)
|
||||
{
|
||||
_cache[cacheKey] = info;
|
||||
Cache[cacheKey] = info;
|
||||
}
|
||||
|
||||
// got it, no need to look any further
|
||||
@@ -235,16 +265,16 @@ namespace Umbraco.Web.BaseRest
|
||||
// by looking for the attributes
|
||||
// returns null if not found
|
||||
//
|
||||
static RestExtensionMethodInfo GetFromAttribute(string extensionAlias, string methodName)
|
||||
static RestExtensionMethodInfo GetFromAttribute(string extensionAlias, string methodName, int paramsCount)
|
||||
{
|
||||
// here we can cache because any change would trigger an app restart
|
||||
|
||||
string cacheKey = extensionAlias + "." + methodName;
|
||||
lock (_cache)
|
||||
var cacheKey = string.Format("{0}.{1}[{2}]", extensionAlias, methodName, paramsCount);
|
||||
lock (Cache)
|
||||
{
|
||||
// if it's in the cache, return
|
||||
if (_cache.ContainsKey(cacheKey))
|
||||
return _cache[cacheKey];
|
||||
if (Cache.ContainsKey(cacheKey))
|
||||
return Cache[cacheKey];
|
||||
}
|
||||
|
||||
// find an extension with that alias, then find a method with that name,
|
||||
@@ -260,8 +290,19 @@ namespace Umbraco.Web.BaseRest
|
||||
|
||||
foreach (var extension in extensions) // foreach classes with extension alias
|
||||
{
|
||||
var method = extension.GetMethod(methodName);
|
||||
if (method == null) continue; // not implementing the method = ignore
|
||||
var methods = extension.GetMethods()
|
||||
.Where(m => m.Name == methodName)
|
||||
.Where(m => m.GetParameters().Count() == paramsCount)
|
||||
.ToArray();
|
||||
|
||||
if (methods.Length == 0) continue; // not implementing the method = ignore
|
||||
|
||||
if (methods.Length > 1)
|
||||
throw new Exception(string.Format("Method \"{0}\" has many overloads with same number of parameters.", methodName));
|
||||
|
||||
var method = methods[0];
|
||||
if (!method.IsPublic || !method.IsStatic)
|
||||
throw new Exception(string.Format("Method \"{0}\" has to be public and static.", methodName));
|
||||
|
||||
var attribute = method.GetCustomAttributes(typeof(RestExtensionMethodAttribute), false).Cast<RestExtensionMethodAttribute>().SingleOrDefault();
|
||||
if (attribute == null) continue; // method has not attribute = ignore
|
||||
@@ -273,9 +314,9 @@ namespace Umbraco.Web.BaseRest
|
||||
method);
|
||||
|
||||
// cache
|
||||
lock (_cache)
|
||||
lock (Cache)
|
||||
{
|
||||
_cache[cacheKey] = info;
|
||||
Cache[cacheKey] = info;
|
||||
}
|
||||
|
||||
// got it, no need to look any further
|
||||
@@ -301,11 +342,11 @@ namespace Umbraco.Web.BaseRest
|
||||
if (member == null)
|
||||
return false;
|
||||
|
||||
bool allowed = false;
|
||||
var allowed = false;
|
||||
|
||||
if (_allowGroups.Length > 0)
|
||||
{
|
||||
// fixme - are these equivalent?
|
||||
// note - assuming these are equivalent
|
||||
//var groups = member.Groups.Values.Cast<MemberGroup>().Select(group => group.Text);
|
||||
var groups = System.Web.Security.Roles.GetRolesForUser(member.LoginName);
|
||||
allowed = groups.Select(s => s.ToLower()).Intersect(_allowGroups).Any();
|
||||
@@ -318,7 +359,7 @@ namespace Umbraco.Web.BaseRest
|
||||
|
||||
if (!allowed && _allowMembers.Length > 0)
|
||||
{
|
||||
allowed = _allowMembers.Contains(member.Id.ToString());
|
||||
allowed = _allowMembers.Contains(member.Id.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
return allowed;
|
||||
@@ -351,13 +392,14 @@ namespace Umbraco.Web.BaseRest
|
||||
}
|
||||
else
|
||||
{
|
||||
object[] methodParams = new object[parameters.Length];
|
||||
var methodParams = new object[parameters.Length];
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
|
||||
foreach (ParameterInfo pInfo in _method.GetParameters())
|
||||
foreach (var pInfo in _method.GetParameters())
|
||||
{
|
||||
Type myType = Type.GetType(pInfo.ParameterType.ToString());
|
||||
var myType = Type.GetType(pInfo.ParameterType.ToString());
|
||||
if (myType == null) throw new Exception("Failed to get type.");
|
||||
methodParams[(i)] = Convert.ChangeType(parameters[i], myType);
|
||||
i++;
|
||||
}
|
||||
@@ -375,34 +417,27 @@ namespace Umbraco.Web.BaseRest
|
||||
case "System.Xml.Linq.XDocument":
|
||||
return response.ToString();
|
||||
case "System.Xml.XmlDocument":
|
||||
XmlDocument xmlDoc = (XmlDocument)response;
|
||||
StringWriter sw = new StringWriter();
|
||||
XmlTextWriter xw = new XmlTextWriter(sw);
|
||||
var xmlDoc = (XmlDocument)response;
|
||||
var sw = new StringWriter();
|
||||
var xw = new XmlTextWriter(sw);
|
||||
xmlDoc.WriteTo(xw);
|
||||
return sw.ToString();
|
||||
default:
|
||||
string strResponse = (string)response.ToString();
|
||||
var strResponse = response.ToString();
|
||||
|
||||
if (this.ReturnXml)
|
||||
if (ReturnXml)
|
||||
{
|
||||
// do a quick "is this html?" check... if it is add CDATA...
|
||||
if (strResponse.Contains("<") || strResponse.Contains(">"))
|
||||
strResponse = "<![CDATA[" + strResponse + "]]>";
|
||||
return "<value>" + strResponse + "</value>";
|
||||
}
|
||||
else
|
||||
{
|
||||
return strResponse;
|
||||
}
|
||||
|
||||
return strResponse;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.ReturnXml)
|
||||
return "<error>Null value returned</error>";
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return ReturnXml ? "<error>Null value returned</error>" : string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
76
src/Umbraco.Web/Configuration/WebRouting.cs
Normal file
76
src/Umbraco.Web/Configuration/WebRouting.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// The Web.Routing settings section.
|
||||
/// </summary>
|
||||
[ConfigurationKey("web.routing", ConfigurationKeyType.Umbraco)]
|
||||
internal class WebRouting : UmbracoConfigurationSection
|
||||
{
|
||||
private const string KeyTrySkipIisCustomErrors = "trySkipIisCustomErrors";
|
||||
private const string KeyUrlProviderMode = "urlProviderMode";
|
||||
private const string KeyInternalRedirectPreservesTemplate = "internalRedirectPreservesTemplate";
|
||||
|
||||
private bool? _trySkipIisCustomErrors;
|
||||
private Routing.UrlProviderMode? _urlProviderMode;
|
||||
private bool? _internalRedirectPreservesTemplate;
|
||||
|
||||
internal protected override void ResetSection()
|
||||
{
|
||||
base.ResetSection();
|
||||
|
||||
_trySkipIisCustomErrors = null;
|
||||
_urlProviderMode = null;
|
||||
_internalRedirectPreservesTemplate = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to try to skip IIS custom errors.
|
||||
/// </summary>
|
||||
[ConfigurationProperty(KeyTrySkipIisCustomErrors, DefaultValue = false, IsRequired = false)]
|
||||
public bool TrySkipIisCustomErrors
|
||||
{
|
||||
get
|
||||
{
|
||||
return _trySkipIisCustomErrors ?? (IsPresent
|
||||
? (bool)this[KeyTrySkipIisCustomErrors]
|
||||
: UmbracoSettings.TrySkipIisCustomErrors);
|
||||
}
|
||||
internal set { _trySkipIisCustomErrors = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the url provider mode.
|
||||
/// </summary>
|
||||
[ConfigurationProperty(KeyUrlProviderMode, DefaultValue = Routing.UrlProviderMode.AutoLegacy, IsRequired = false)]
|
||||
[TypeConverter(typeof(CaseInsensitiveEnumConfigConverter<Routing.UrlProviderMode>))]
|
||||
public Routing.UrlProviderMode UrlProviderMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _urlProviderMode ?? (IsPresent
|
||||
? (Routing.UrlProviderMode)this[KeyUrlProviderMode]
|
||||
: Routing.UrlProviderMode.Auto);
|
||||
}
|
||||
internal set { _urlProviderMode = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether internal redirect preserves the template.
|
||||
/// </summary>
|
||||
[ConfigurationProperty(KeyInternalRedirectPreservesTemplate, DefaultValue = false, IsRequired = false)]
|
||||
public bool InternalRedirectPreservesTemplate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _internalRedirectPreservesTemplate ?? (IsPresent
|
||||
? (bool)this[KeyInternalRedirectPreservesTemplate]
|
||||
: UmbracoSettings.InternalRedirectPreservesTemplate);
|
||||
}
|
||||
internal set { _internalRedirectPreservesTemplate = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,14 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <param name="mode">The url mode.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute)
|
||||
public string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, UrlProviderMode mode)
|
||||
{
|
||||
return null; // we have nothing to say
|
||||
}
|
||||
|
||||
@@ -23,14 +23,13 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <param name="mode">The url mode.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>The url is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute)
|
||||
public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, UrlProviderMode mode)
|
||||
{
|
||||
DomainAndUri domainUri;
|
||||
string path;
|
||||
@@ -49,7 +48,7 @@ namespace Umbraco.Web.Routing
|
||||
{
|
||||
// there was a route in the cache - extract domainUri and path
|
||||
// route is /<path> or <domainRootId>/<path>
|
||||
int pos = route.IndexOf('/');
|
||||
var pos = route.IndexOf('/');
|
||||
path = pos == 0 ? route : route.Substring(pos);
|
||||
domainUri = pos == 0 ? null : DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current);
|
||||
}
|
||||
@@ -97,7 +96,7 @@ namespace Umbraco.Web.Routing
|
||||
}
|
||||
|
||||
// assemble the url from domainUri (maybe null) and path
|
||||
return AssembleUrl(domainUri, path, current, absolute).ToString();
|
||||
return AssembleUrl(domainUri, path, current, mode).ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -185,27 +184,65 @@ namespace Umbraco.Web.Routing
|
||||
|
||||
#region Utilities
|
||||
|
||||
Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, bool absolute)
|
||||
Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode)
|
||||
{
|
||||
Uri uri;
|
||||
|
||||
if (domainUri == null)
|
||||
// ignore vdir at that point, UriFromUmbraco will do it
|
||||
|
||||
if (mode == UrlProviderMode.AutoLegacy)
|
||||
{
|
||||
// no domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (!absolute || current == null)
|
||||
uri = new Uri(path, UriKind.Relative);
|
||||
else
|
||||
uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path);
|
||||
mode = Core.Configuration.UmbracoSettings.UseDomainPrefixes
|
||||
? UrlProviderMode.Absolute
|
||||
: UrlProviderMode.Auto;
|
||||
}
|
||||
else
|
||||
|
||||
if (mode == UrlProviderMode.AutoLegacy)
|
||||
{
|
||||
// a domain was found : return an absolute or relative url
|
||||
// ignore vdir at that point
|
||||
if (!absolute && current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority))
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative); // relative
|
||||
else
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); // absolute
|
||||
mode = Core.Configuration.UmbracoSettings.UseDomainPrefixes
|
||||
? UrlProviderMode.Absolute
|
||||
: UrlProviderMode.Auto;
|
||||
}
|
||||
|
||||
if (domainUri == null) // no domain was found
|
||||
{
|
||||
if (current == null)
|
||||
mode = UrlProviderMode.Relative; // best we can do
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case UrlProviderMode.Absolute:
|
||||
uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path);
|
||||
break;
|
||||
case UrlProviderMode.Relative:
|
||||
case UrlProviderMode.Auto:
|
||||
uri = new Uri(path, UriKind.Relative);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("mode");
|
||||
}
|
||||
}
|
||||
else // a domain was found
|
||||
{
|
||||
if (mode == UrlProviderMode.Auto)
|
||||
{
|
||||
if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority))
|
||||
mode = UrlProviderMode.Relative;
|
||||
else
|
||||
mode = UrlProviderMode.Absolute;
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case UrlProviderMode.Absolute:
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
|
||||
break;
|
||||
case UrlProviderMode.Relative:
|
||||
uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("mode");
|
||||
}
|
||||
}
|
||||
|
||||
// UriFromUmbraco will handle vdir
|
||||
|
||||
@@ -15,14 +15,13 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <param name="mode">The url mode.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>The url is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para>
|
||||
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
|
||||
/// </remarks>
|
||||
string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute);
|
||||
string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, UrlProviderMode mode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the other urls of a published content.
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Globalization;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Web.Configuration;
|
||||
using umbraco;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
@@ -104,11 +106,30 @@ namespace Umbraco.Web.Routing
|
||||
set
|
||||
{
|
||||
_publishedContent = value;
|
||||
IsInternalRedirectPublishedContent = false;
|
||||
TemplateModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the requested content, following an internal redirect.
|
||||
/// </summary>
|
||||
/// <param name="content">The requested content.</param>
|
||||
/// <remarks>Depending on <c>UmbracoSettings.InternalRedirectPreservesTemplate</c>, will
|
||||
/// preserve or reset the template, if any.</remarks>
|
||||
public void SetInternalRedirectPublishedContent(IPublishedContent content)
|
||||
{
|
||||
// unless a template has been set already by the finder,
|
||||
// template should be null at that point.
|
||||
var initial = IsInitialPublishedContent;
|
||||
var template = _template;
|
||||
PublishedContent = content;
|
||||
IsInternalRedirectPublishedContent = (initial && !IsInitialPublishedContent);
|
||||
if (IsInternalRedirectPublishedContent && UmbracoSettings.For<WebRouting>().InternalRedirectPreservesTemplate)
|
||||
_template = template;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the initial requested content.
|
||||
/// </summary>
|
||||
/// <remarks>The initial requested content is the content that was found by the finders,
|
||||
@@ -133,8 +154,15 @@ namespace Umbraco.Web.Routing
|
||||
{
|
||||
// note: it can very well be null if the initial content was not found
|
||||
_initialPublishedContent = _publishedContent;
|
||||
IsInternalRedirectPublishedContent = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the current published has been obtained from the
|
||||
/// initial published content following internal redirections exclusively.
|
||||
/// </summary>
|
||||
public bool IsInternalRedirectPublishedContent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content request has a content.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.IO;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Web.Configuration;
|
||||
|
||||
using umbraco;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
@@ -444,7 +446,7 @@ namespace Umbraco.Web.Routing
|
||||
// redirect to another page
|
||||
var node = _routingContext.PublishedContentStore.GetDocumentById(_routingContext.UmbracoContext, internalRedirectId);
|
||||
|
||||
_pcr.PublishedContent = node;
|
||||
_pcr.SetInternalRedirectPublishedContent(node); // don't use .PublishedContent here
|
||||
if (node != null)
|
||||
{
|
||||
redirect = true;
|
||||
@@ -532,7 +534,10 @@ namespace Umbraco.Web.Routing
|
||||
// read the alternate template alias, from querystring, form, cookie or server vars,
|
||||
// only if the published content is the initial once, else the alternate template
|
||||
// does not apply
|
||||
string altTemplate = _pcr.IsInitialPublishedContent
|
||||
// + optionnally, apply the alternate template on internal redirects
|
||||
var useAltTemplate = _pcr.IsInitialPublishedContent
|
||||
|| (UmbracoSettings.For<WebRouting>().InternalRedirectPreservesTemplate && _pcr.IsInternalRedirectPublishedContent);
|
||||
string altTemplate = useAltTemplate
|
||||
? _routingContext.UmbracoContext.HttpContext.Request["altTemplate"]
|
||||
: null;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing
|
||||
_umbracoContext = umbracoContext;
|
||||
_contentCache = contentCache;
|
||||
_urlProviders = urlProviders;
|
||||
EnforceAbsoluteUrls = false;
|
||||
Mode = UmbracoSettings.For<Configuration.WebRouting>().UrlProviderMode;
|
||||
}
|
||||
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
@@ -32,9 +32,9 @@ namespace Umbraco.Web.Routing
|
||||
private readonly IEnumerable<IUrlProvider> _urlProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the provider should enforce absolute urls.
|
||||
/// Gets or sets the provider url mode.
|
||||
/// </summary>
|
||||
public bool EnforceAbsoluteUrls { get; set; }
|
||||
public UrlProviderMode Mode { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -46,13 +46,12 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="id">The published content identifier.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on the current url, settings, and options.</para>
|
||||
/// <para>The url is absolute or relative depending on <c>Mode</c> and on the current url.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id)
|
||||
{
|
||||
var absolute = UmbracoSettings.UseDomainPrefixes | EnforceAbsoluteUrls;
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, absolute);
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, Mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,14 +61,14 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on the current url and settings, unless <c>absolute</c> is true,
|
||||
/// in which case the url is always absolute.</para>
|
||||
/// <para>The url is absolute or relative depending on <c>Mode</c> and on <c>current</c>, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id, bool absolute)
|
||||
{
|
||||
absolute = absolute | EnforceAbsoluteUrls;
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, absolute);
|
||||
var mode = absolute ? UrlProviderMode.Absolute : Mode;
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,14 +79,46 @@ namespace Umbraco.Web.Routing
|
||||
/// <param name="absolute">A value indicating whether the url should be absolute in any case.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on url indicated by <c>current</c> and settings, unless
|
||||
/// <para>The url is absolute or relative depending on <c>Mode</c> and on <c>current</c>, unless
|
||||
/// <c>absolute</c> is true, in which case the url is always absolute.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id, Uri current, bool absolute)
|
||||
{
|
||||
absolute = absolute | EnforceAbsoluteUrls;
|
||||
var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, _contentCache, id, current, absolute)).FirstOrDefault(u => u != null);
|
||||
var mode = absolute ? UrlProviderMode.Absolute : Mode;
|
||||
return GetUrl(id, current, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content identifier.</param>
|
||||
/// <param name="mode">The url mode.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on <c>mode</c> and on the current url.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id, UrlProviderMode mode)
|
||||
{
|
||||
return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nice url of a published content.
|
||||
/// </summary>
|
||||
/// <param name="id">The published content id.</param>
|
||||
/// <param name="current">The current absolute url.</param>
|
||||
/// <param name="mode">The url mode.</param>
|
||||
/// <returns>The url for the published content.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The url is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para>
|
||||
/// <para>If the provider is unable to provide a url, it returns "#".</para>
|
||||
/// </remarks>
|
||||
public string GetUrl(int id, Uri current, UrlProviderMode mode)
|
||||
{
|
||||
var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, _contentCache, id, current, mode))
|
||||
.FirstOrDefault(u => u != null);
|
||||
return url ?? "#"; // legacy wants this
|
||||
}
|
||||
|
||||
|
||||
36
src/Umbraco.Web/Routing/UrlProviderMode.cs
Normal file
36
src/Umbraco.Web/Routing/UrlProviderMode.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace Umbraco.Web.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the type of urls that the url provider should produce.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The <c>AutoLegacy</c> option is equivalent to <c>Auto</c> but it also respects the legacy <c>useDomainPrefixes</c> setting.
|
||||
/// When that setting is true, then all urls are absolute. Otherwise, urls will be relative or absolute, depending on hostnames.</para>
|
||||
/// <para>The <c>Relative</c> option can lead to invalid results when combined with hostnames, but it is the only way to reproduce
|
||||
/// the true, pre-4.10, always-relative behavior of Umbraco.</para>
|
||||
/// <para>For the time being, the default option is <c>AutoLegacy</c> although in the future it will be <c>Auto</c>.</para>
|
||||
/// </remarks>
|
||||
internal enum UrlProviderMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the url provider should determine automatically whether to return relative or absolute urls,
|
||||
/// and also respect the legacy <c>useDomainPrefixes</c> setting.
|
||||
/// </summary>
|
||||
AutoLegacy,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the url provider should produce relative urls exclusively.
|
||||
/// </summary>
|
||||
Relative,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the url provider should produce absolute urls exclusively.
|
||||
/// </summary>
|
||||
Absolute,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the url provider should determine automatically whether to return relative or absolute urls.
|
||||
/// </summary>
|
||||
Auto
|
||||
}
|
||||
}
|
||||
@@ -272,6 +272,7 @@
|
||||
<Compile Include="Cache\PageCacheRefresher.cs" />
|
||||
<Compile Include="Cache\TemplateCacheRefresher.cs" />
|
||||
<Compile Include="Cache\UserCacheRefresher.cs" />
|
||||
<Compile Include="Configuration\WebRouting.cs" />
|
||||
<Compile Include="Dynamics\DynamicExpression.cs" />
|
||||
<Compile Include="Dynamics\DynamicGrouping.cs" />
|
||||
<Compile Include="Dynamics\DynamicPublishedContentIdEqualityComparer.cs" />
|
||||
@@ -324,6 +325,7 @@
|
||||
<Compile Include="Mvc\MemberAuthorizeAttribute.cs" />
|
||||
<Compile Include="Mvc\ControllerFactoryExtensions.cs" />
|
||||
<Compile Include="Mvc\SurfaceRouteHandler.cs" />
|
||||
<Compile Include="Routing\UrlProviderMode.cs" />
|
||||
<Compile Include="Search\ExamineIndexerModel.cs" />
|
||||
<Compile Include="Search\LuceneIndexerExtensions.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\AssignDomain2.aspx.cs">
|
||||
|
||||
@@ -10,6 +10,7 @@ using umbraco;
|
||||
using umbraco.IO;
|
||||
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
|
||||
using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Web.Configuration;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
@@ -130,7 +131,7 @@ namespace Umbraco.Web
|
||||
else if (pcr.Is404)
|
||||
{
|
||||
response.StatusCode = 404;
|
||||
response.TrySkipIisCustomErrors = UmbracoSettings.TrySkipIisCustomErrors;
|
||||
response.TrySkipIisCustomErrors = UmbracoSettings.For<WebRouting>().TrySkipIisCustomErrors;
|
||||
}
|
||||
|
||||
if (pcr.ResponseStatusCode > 0)
|
||||
|
||||
Reference in New Issue
Block a user