Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bc33e0115 | |||
| 8576d9c67e | |||
| 9452d5fd09 | |||
| 1f40d726de | |||
| 2699611ba3 | |||
| b1fa06bfe8 | |||
| 4dc22b6ce4 | |||
| 883277125a | |||
| ff961abaab | |||
| b2414c1ab8 | |||
| 0e392b398c | |||
| 5914690ad8 | |||
| dbbdd16bb9 | |||
| 762cca145f | |||
| 5cd2d568e3 | |||
| 259fbcb8f0 | |||
| db3f21429e | |||
| 04b9607741 | |||
| 5bd2571395 | |||
| c0cd157e60 | |||
| ac2f9b172c | |||
| b8e4a73c3b | |||
| 68c4255a51 | |||
| d07212c89b | |||
| 28c55320a1 | |||
| fd58017e97 | |||
| 09408b898e | |||
| d1b05332c5 | |||
| caad0afed8 | |||
| 62d365573f | |||
| f3b7a8a581 | |||
| a765f36d24 | |||
| 046da1a8ce | |||
| ca7abd6c78 | |||
| 7cc0f2a514 |
@@ -37,6 +37,7 @@ src\Umbraco.Web.UI\macroScripts\*
|
||||
src\Umbraco.Web.UI\xslt\*
|
||||
src\Umbraco.Web.UI\images\*
|
||||
src\Umbraco.Web.UI\scripts\*
|
||||
src\Umbraco.Web.UI\Web.*.config.transformed
|
||||
|
||||
umbraco\presentation\umbraco\plugins\uComponents\uComponentsInstaller.ascx
|
||||
umbraco\presentation\packages\uComponents\MultiNodePicker\CustomTreeService.asmx
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
@ECHO OFF
|
||||
set version=4.11.5
|
||||
set version=4.11.6
|
||||
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%version%
|
||||
|
||||
echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Code\dummy.txt
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Configuration
|
||||
#region Private static fields
|
||||
|
||||
// CURRENT UMBRACO VERSION ID
|
||||
private const string CurrentUmbracoVersion = "4.11.5";
|
||||
private const string CurrentUmbracoVersion = "4.11.6";
|
||||
|
||||
private static readonly object Locker = new object();
|
||||
//make this volatile so that we can ensure thread safety with a double check lock
|
||||
@@ -156,6 +156,9 @@ namespace Umbraco.Core.Configuration
|
||||
/// This will return the MVC area that we will route all custom routes through like surface controllers, etc...
|
||||
/// We will use the 'Path' (default ~/umbraco) to create it but since it cannot contain '/' and people may specify a path of ~/asdf/asdf/admin
|
||||
/// we will convert the '/' to '-' and use that as the path. its a bit lame but will work.
|
||||
///
|
||||
/// We also make sure that the virtual directory (SystemDirectories.Root) is stripped off first, otherwise we'd end up with something
|
||||
/// like "MyVirtualDirectory-Umbraco" instead of just "Umbraco".
|
||||
/// </remarks>
|
||||
internal static string UmbracoMvcArea
|
||||
{
|
||||
@@ -165,7 +168,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified");
|
||||
}
|
||||
return Path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim();
|
||||
return Path.TrimStart(SystemDirectories.Root).TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1215,6 +1215,40 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
private static MacroErrorBehaviour? _macroErrorBehaviour;
|
||||
|
||||
/// <summary>
|
||||
/// This configuration setting defines how to handle macro errors:
|
||||
/// - Inline - Show error within macro as text (default and current Umbraco 'normal' behavior)
|
||||
/// - Silent - Suppress error and hide macro
|
||||
/// - Throw - Throw an exception and invoke the global error handler (if one is defined, if not you'll get a YSOD)
|
||||
/// </summary>
|
||||
/// <value>MacroErrorBehaviour enum defining how to handle macro errors.</value>
|
||||
public static MacroErrorBehaviour MacroErrorBehaviour
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_macroErrorBehaviour == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var behaviour = MacroErrorBehaviour.Inline;
|
||||
var value = GetKey("/settings/content/MacroErrors");
|
||||
if (value != null)
|
||||
{
|
||||
Enum<MacroErrorBehaviour>.TryParse(value, true, out behaviour);
|
||||
}
|
||||
_macroErrorBehaviour = behaviour;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<UmbracoSettings>("Could not load /settings/content/MacroErrors from umbracosettings.config", ex);
|
||||
_macroErrorBehaviour = MacroErrorBehaviour.Inline;
|
||||
}
|
||||
}
|
||||
return _macroErrorBehaviour.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration regarding webservices
|
||||
|
||||
@@ -32,8 +32,7 @@ namespace Umbraco.Core
|
||||
LogHelper.Info<CoreBootManager>("Umbraco application starting");
|
||||
_timer = DisposableTimer.Start(x => LogHelper.Info<CoreBootManager>("Umbraco application startup complete" + " (took " + x + "ms)"));
|
||||
|
||||
//create the ApplicationContext
|
||||
ApplicationContext = ApplicationContext.Current = new ApplicationContext();
|
||||
CreateApplicationContext();
|
||||
|
||||
InitializeResolvers();
|
||||
|
||||
@@ -42,6 +41,15 @@ namespace Umbraco.Core
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and assigns the application context singleton
|
||||
/// </summary>
|
||||
protected virtual void CreateApplicationContext()
|
||||
{
|
||||
//create the ApplicationContext
|
||||
ApplicationContext = ApplicationContext.Current = new ApplicationContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires after initialization and calls the callback to allow for customizations to occur
|
||||
/// </summary>
|
||||
@@ -72,8 +80,7 @@ namespace Umbraco.Core
|
||||
if (_isComplete)
|
||||
throw new InvalidOperationException("The boot manager has already been completed");
|
||||
|
||||
//freeze resolution to not allow Resolvers to be modified
|
||||
Resolution.Freeze();
|
||||
FreezeResolution();
|
||||
|
||||
//stop the timer and log the output
|
||||
_timer.Dispose();
|
||||
@@ -88,6 +95,14 @@ namespace Umbraco.Core
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freeze resolution to not allow Resolvers to be modified
|
||||
/// </summary>
|
||||
protected virtual void FreezeResolution()
|
||||
{
|
||||
Resolution.Freeze();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the resolvers
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
// Provides information on the macro that caused an error
|
||||
public class MacroErrorEventArgs : System.EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the faulting macro.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Alias of the faulting macro.
|
||||
/// </summary>
|
||||
public string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filename, file path, fully qualified class name, or other key used by the macro engine to do it's processing of the faulting macro.
|
||||
/// </summary>
|
||||
public string ItemKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Exception raised.
|
||||
/// </summary>
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the desired behaviour when a matching macro causes an error. See
|
||||
/// <see cref="MacroErrorBehaviour"/> for definitions. By setting this in your event
|
||||
/// you can override the default behaviour defined in UmbracoSettings.config.
|
||||
/// </summary>
|
||||
/// <value>Macro error behaviour enum.</value>
|
||||
public MacroErrorBehaviour Behaviour { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using System.IO;
|
||||
using System.Configuration;
|
||||
using System.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -228,6 +229,20 @@ namespace Umbraco.Core.IO
|
||||
filePath = String.Empty;
|
||||
}
|
||||
|
||||
//Break up the file in name and extension before applying the UrlReplaceCharacters
|
||||
var fileNamePart = filePath.Substring(0, filePath.LastIndexOf('.'));
|
||||
var ext = filePath.Substring(filePath.LastIndexOf('.'));
|
||||
|
||||
//Because the file usually is downloadable as well we check characters against 'UmbracoSettings.UrlReplaceCharacters'
|
||||
XmlNode replaceChars = UmbracoSettings.UrlReplaceCharacters;
|
||||
foreach (XmlNode n in replaceChars.SelectNodes("char"))
|
||||
{
|
||||
if (n.Attributes.GetNamedItem("org") != null && n.Attributes.GetNamedItem("org").Value != "")
|
||||
fileNamePart = fileNamePart.Replace(n.Attributes.GetNamedItem("org").Value, XmlHelper.GetNodeValue(n));
|
||||
}
|
||||
|
||||
filePath = string.Concat(fileNamePart, ext);
|
||||
|
||||
// Adapted from: http://stackoverflow.com/a/4827510/5018
|
||||
// Combined both Reserved Characters and Character Data
|
||||
// from http://en.wikipedia.org/wiki/Percent-encoding
|
||||
|
||||
@@ -183,16 +183,26 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
private static string _root;
|
||||
/// <summary>
|
||||
/// Gets the root path of the application
|
||||
/// </summary>
|
||||
public static string Root
|
||||
{
|
||||
get
|
||||
{
|
||||
string appPath = HttpRuntime.AppDomainAppVirtualPath ?? string.Empty;
|
||||
if (appPath == "/")
|
||||
appPath = string.Empty;
|
||||
if (_root == null)
|
||||
{
|
||||
string appPath = HttpRuntime.AppDomainAppVirtualPath ?? string.Empty;
|
||||
if (appPath == "/")
|
||||
appPath = string.Empty;
|
||||
|
||||
return appPath;
|
||||
_root = appPath;
|
||||
}
|
||||
return _root;
|
||||
}
|
||||
//Only required for unit tests
|
||||
internal set { _root = value; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,23 +64,28 @@ namespace Umbraco.Core.Logging
|
||||
{
|
||||
_forceStop = true;
|
||||
var windowsIdentity = WindowsIdentity.GetCurrent();
|
||||
base.Append(new LoggingEvent(new LoggingEventData
|
||||
{
|
||||
Level = Level.Error,
|
||||
Message =
|
||||
"Unable to clear out the AsynchronousRollingFileAppender buffer in the allotted time, forcing a shutdown",
|
||||
TimeStamp = DateTime.UtcNow,
|
||||
Identity = "",
|
||||
ExceptionString = "",
|
||||
UserName = windowsIdentity != null ? windowsIdentity.Name : "",
|
||||
Domain = AppDomain.CurrentDomain.FriendlyName,
|
||||
ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
|
||||
LocationInfo =
|
||||
new LocationInfo(this.GetType().Name, "OnClose", "AsynchronousRollingFileAppender.cs", "59"),
|
||||
LoggerName = this.GetType().FullName,
|
||||
Properties = new PropertiesDictionary(),
|
||||
})
|
||||
);
|
||||
|
||||
var logEvent = new LoggingEvent(new LoggingEventData
|
||||
{
|
||||
Level = global::log4net.Core.Level.Error,
|
||||
Message =
|
||||
"Unable to clear out the AsynchronousRollingFileAppender buffer in the allotted time, forcing a shutdown",
|
||||
TimeStamp = DateTime.UtcNow,
|
||||
Identity = "",
|
||||
ExceptionString = "",
|
||||
UserName = windowsIdentity != null ? windowsIdentity.Name : "",
|
||||
Domain = AppDomain.CurrentDomain.FriendlyName,
|
||||
ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
|
||||
LocationInfo =
|
||||
new LocationInfo(this.GetType().Name, "OnClose", "AsynchronousRollingFileAppender.cs", "59"),
|
||||
LoggerName = this.GetType().FullName,
|
||||
Properties = new PropertiesDictionary(),
|
||||
});
|
||||
|
||||
if (this.DateTimeStrategy != null)
|
||||
{
|
||||
base.Append(logEvent);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnClose();
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public enum MacroErrorBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Default umbraco behavior - show an inline error within the
|
||||
/// macro but allow the page to continue rendering.
|
||||
/// </summary>
|
||||
Inline,
|
||||
|
||||
/// <summary>
|
||||
/// Silently eat the error and do not display the offending macro.
|
||||
/// </summary>
|
||||
Silent,
|
||||
|
||||
/// <summary>
|
||||
/// Throw an exception which can be caught by the global error handler
|
||||
/// defined in Application_OnError. If no such error handler is defined
|
||||
/// then you'll see the Yellow Screen Of Death (YSOD) error page.
|
||||
/// </summary>
|
||||
Throw
|
||||
}
|
||||
}
|
||||
@@ -179,6 +179,8 @@ namespace Umbraco.Core
|
||||
public static string TrimEnd(this string value, string forRemoving)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return value;
|
||||
if (string.IsNullOrEmpty(forRemoving)) return value;
|
||||
|
||||
while (value.EndsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
value = value.Remove(value.LastIndexOf(forRemoving, StringComparison.InvariantCultureIgnoreCase));
|
||||
@@ -189,6 +191,8 @@ namespace Umbraco.Core
|
||||
public static string TrimStart(this string value, string forRemoving)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return value;
|
||||
if (string.IsNullOrEmpty(forRemoving)) return value;
|
||||
|
||||
while (value.StartsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
value = value.Substring(forRemoving.Length);
|
||||
|
||||
@@ -69,8 +69,10 @@
|
||||
<Compile Include="Dictionary\ICultureDictionaryFactory.cs" />
|
||||
<Compile Include="Dynamics\DynamicInstanceHelper.cs" />
|
||||
<Compile Include="Enum.cs" />
|
||||
<Compile Include="Events\MacroErrorEventArgs.cs" />
|
||||
<Compile Include="HashCodeCombiner.cs" />
|
||||
<Compile Include="IO\FileSystemWrapper.cs" />
|
||||
<Compile Include="MacroErrorBehaviour.cs" />
|
||||
<Compile Include="Media\IImageUrlProvider.cs" />
|
||||
<Compile Include="ObjectResolution\LazyManyObjectsResolverbase.cs" />
|
||||
<Compile Include="PublishedContentExtensions.cs" />
|
||||
|
||||
@@ -10,6 +10,77 @@ namespace Umbraco.Tests
|
||||
[TestFixture]
|
||||
public class EnumerableExtensionsTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Flatten_List()
|
||||
{
|
||||
var hierarchy = new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem(),
|
||||
new TestItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
}
|
||||
}
|
||||
},
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
{
|
||||
new TestItem()
|
||||
{
|
||||
Children = new List<TestItem>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var flattened = hierarchy.Children.FlattenList(x => x.Children);
|
||||
|
||||
Assert.AreEqual(10, flattened.Count());
|
||||
}
|
||||
|
||||
private class TestItem
|
||||
{
|
||||
public TestItem()
|
||||
{
|
||||
Children = Enumerable.Empty<TestItem>();
|
||||
}
|
||||
public IEnumerable<TestItem> Children { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InGroupsOf_ReturnsAllElements()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Configuration;
|
||||
using System.Web.Routing;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using System.Web.Mvc;
|
||||
|
||||
@@ -22,12 +23,27 @@ namespace Umbraco.Tests
|
||||
|
||||
public override void TearDown()
|
||||
{
|
||||
//ensure this is reset
|
||||
SystemDirectories.Root = null;
|
||||
SettingsForTests.UmbracoPath = "~/umbraco";
|
||||
//reset the app config
|
||||
base.TearDown();
|
||||
|
||||
}
|
||||
|
||||
[TestCase("/umbraco/umbraco.aspx")]
|
||||
[TestCase("~/umbraco", "/", "umbraco")]
|
||||
[TestCase("~/umbraco", "/MyVirtualDir", "umbraco")]
|
||||
[TestCase("~/customPath", "/MyVirtualDir/", "custompath")]
|
||||
[TestCase("~/some-wacky/nestedPath", "/MyVirtualDir", "some-wacky-nestedpath")]
|
||||
[TestCase("~/some-wacky/nestedPath", "/MyVirtualDir/NestedVDir/", "some-wacky-nestedpath")]
|
||||
public void Umbraco_Mvc_Area(string path, string rootPath, string outcome)
|
||||
{
|
||||
SettingsForTests.UmbracoPath = path;
|
||||
SystemDirectories.Root = rootPath;
|
||||
Assert.AreEqual(outcome, Umbraco.Core.Configuration.GlobalSettings.UmbracoMvcArea);
|
||||
}
|
||||
|
||||
[TestCase("/umbraco/umbraco.aspx")]
|
||||
[TestCase("/umbraco/editContent.aspx")]
|
||||
[TestCase("/install/default.aspx")]
|
||||
[TestCase("/install/")]
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
<content><![CDATA[]]></content>
|
||||
<umbracoUrlAlias><![CDATA[this/is/my/alias, anotheralias]]></umbracoUrlAlias>
|
||||
<umbracoNaviHide>1</umbracoNaviHide>
|
||||
<siteTitle><![CDATA[This is my site]]></siteTitle>
|
||||
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
|
||||
<content><![CDATA[<div>This is some content</div>]]></content>
|
||||
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
|
||||
@@ -78,6 +79,38 @@ namespace Umbraco.Tests.PublishedContent
|
||||
/// <returns></returns>
|
||||
protected abstract dynamic GetDynamicNode(int id);
|
||||
|
||||
[Test]
|
||||
public void Recursive_Property()
|
||||
{
|
||||
var doc = GetDynamicNode(1174);
|
||||
var prop = doc.GetProperty("siteTitle", true);
|
||||
Assert.IsNotNull(prop);
|
||||
Assert.AreEqual("This is my site", prop.Value);
|
||||
prop = doc.GetProperty("_siteTitle"); //test with underscore prefix
|
||||
Assert.IsNotNull(prop);
|
||||
Assert.AreEqual("This is my site", prop.Value);
|
||||
Assert.AreEqual("This is my site", doc._siteTitle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the internal instance level caching of returning properties
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://issues.umbraco.org/issue/U4-1824
|
||||
/// http://issues.umbraco.org/issue/U4-1825
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Can_Return_Property_And_Value()
|
||||
{
|
||||
var doc = GetDynamicNode(1173);
|
||||
|
||||
Assert.IsTrue(doc.HasProperty("umbracoUrlAlias"));
|
||||
var prop = doc.GetProperty("umbracoUrlAlias");
|
||||
Assert.IsNotNull(prop);
|
||||
Assert.AreEqual("page2/alias, 2ndpagealias", prop.Value);
|
||||
Assert.AreEqual("page2/alias, 2ndpagealias", doc.umbracoUrlAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the IsLast method with the result set from a Where statement
|
||||
/// </summary>
|
||||
|
||||
@@ -2391,7 +2391,7 @@ xcopy "$(ProjectDir)"..\..\lib\SQLCE4\x86\*.* "$(TargetDir)x86\" /Y /F /E /D</Po
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>61637</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:61638/</IISUrl>
|
||||
<IISUrl>http://localhost:61639/VirtualDir</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -21,7 +21,7 @@ More information and documentation can be found on CodePlex: http://umbracoexami
|
||||
interval="10"
|
||||
analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"/>
|
||||
|
||||
<!-- default external indexer, which excludes protected and published pages-->
|
||||
<!-- default external indexer, which excludes protected and unpublished pages-->
|
||||
<add name="ExternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
|
||||
supportUnpublished="false"
|
||||
supportProtected="false"
|
||||
|
||||
@@ -86,6 +86,14 @@
|
||||
<!-- Setting this to true can increase render time for pages with a large number of links -->
|
||||
<!-- If running Umbraco in virtual directory this *must* be set to true! -->
|
||||
<ResolveUrlsFromTextString>false</ResolveUrlsFromTextString>
|
||||
|
||||
<!-- How Umbraco should handle errors during macro execution. Can be one of the following values:
|
||||
- inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour.
|
||||
- silent - Silently suppress the error and do not render the offending macro.
|
||||
- throw - Throw an exception which can be caught by the global error handler defined in Application_OnError. If no such
|
||||
error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page.
|
||||
Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. -->
|
||||
<MacroErrors>inline</MacroErrors>
|
||||
</content>
|
||||
|
||||
<security>
|
||||
|
||||
@@ -81,6 +81,13 @@
|
||||
<!-- In seconds. 0 will disable cache -->
|
||||
<UmbracoLibraryCacheDuration>1800</UmbracoLibraryCacheDuration>
|
||||
|
||||
<!-- How Umbraco should handle errors during macro execution. Can be one of the following values:
|
||||
- inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour.
|
||||
- silent - Silently suppress the error and do not render the offending macro.
|
||||
- throw - Throw an exception which can be caught by the global error handler defined in Application_OnError. If no such
|
||||
error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page.
|
||||
Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. -->
|
||||
<MacroErrors>inline</MacroErrors>
|
||||
</content>
|
||||
|
||||
<security>
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Web.Mvc.Html;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Mvc;
|
||||
using umbraco;
|
||||
using umbraco.cms.businesslogic.member;
|
||||
@@ -20,6 +21,30 @@ namespace Umbraco.Web
|
||||
/// </summary>
|
||||
public static class HtmlHelperRenderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Will render the preview badge when in preview mode which is not required ever unless the MVC page you are
|
||||
/// using does not inherit from UmbracoTemplatePage
|
||||
/// </summary>
|
||||
/// <param name="helper"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// See: http://issues.umbraco.org/issue/U4-1614
|
||||
/// </remarks>
|
||||
public static MvcHtmlString PreviewBadge(this HtmlHelper helper)
|
||||
{
|
||||
if (UmbracoContext.Current.InPreviewMode)
|
||||
{
|
||||
var htmlBadge =
|
||||
String.Format(UmbracoSettings.PreviewBadge,
|
||||
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
||||
IOHelper.ResolveUrl(SystemDirectories.UmbracoClient),
|
||||
UmbracoContext.Current.HttpContext.Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path));
|
||||
return new MvcHtmlString(htmlBadge);
|
||||
}
|
||||
return new MvcHtmlString("");
|
||||
|
||||
}
|
||||
|
||||
public static IHtmlString CachedPartial(
|
||||
this HtmlHelper htmlHelper,
|
||||
string partialViewName,
|
||||
|
||||
@@ -7,33 +7,37 @@ using System.Linq;
|
||||
|
||||
namespace Umbraco.Web.Macros
|
||||
{
|
||||
/// <summary>
|
||||
/// Controller to render macro content for Parital View Macros
|
||||
/// </summary>
|
||||
internal class PartialViewMacroController : Controller
|
||||
{
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
private readonly MacroModel _macro;
|
||||
private readonly INode _currentPage;
|
||||
|
||||
public PartialViewMacroController(UmbracoContext umbracoContext, MacroModel macro, INode currentPage)
|
||||
{
|
||||
_umbracoContext = umbracoContext;
|
||||
_macro = macro;
|
||||
_currentPage = currentPage;
|
||||
}
|
||||
/// <summary>
|
||||
/// Controller to render macro content for Parital View Macros
|
||||
/// </summary>
|
||||
internal class PartialViewMacroController : Controller
|
||||
{
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
private readonly MacroModel _macro;
|
||||
private readonly INode _currentPage;
|
||||
|
||||
/// <summary>
|
||||
/// Child action to render a macro
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[ChildActionOnly]
|
||||
public PartialViewResult Index()
|
||||
{
|
||||
var model = new PartialViewMacroModel(_currentPage.ConvertFromNode(),
|
||||
_macro.Properties.ToDictionary(x => x.Key, x => (object)x.Value));
|
||||
return PartialView(_macro.ScriptName, model);
|
||||
}
|
||||
public PartialViewMacroController(UmbracoContext umbracoContext, MacroModel macro, INode currentPage)
|
||||
{
|
||||
_umbracoContext = umbracoContext;
|
||||
_macro = macro;
|
||||
_currentPage = currentPage;
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Child action to render a macro
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[ChildActionOnly]
|
||||
public PartialViewResult Index()
|
||||
{
|
||||
var model = new PartialViewMacroModel(
|
||||
_currentPage.ConvertFromNode(),
|
||||
_macro.Id,
|
||||
_macro.Alias,
|
||||
_macro.Name,
|
||||
_macro.Properties.ToDictionary(x => x.Key, x => (object)x.Value));
|
||||
return PartialView(_macro.ScriptName, model);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,51 +12,53 @@ using Umbraco.Core.IO;
|
||||
using umbraco.cms.businesslogic.macro;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using System.Web.Mvc.Html;
|
||||
|
||||
namespace Umbraco.Web.Macros
|
||||
{
|
||||
/// <summary>
|
||||
/// A macro engine using MVC Partial Views to execute
|
||||
/// </summary>
|
||||
public class PartialViewMacroEngine : IMacroEngine
|
||||
{
|
||||
private readonly Func<HttpContextBase> _getHttpContext;
|
||||
private readonly Func<UmbracoContext> _getUmbracoContext;
|
||||
/// <summary>
|
||||
/// A macro engine using MVC Partial Views to execute
|
||||
/// </summary>
|
||||
public class PartialViewMacroEngine : IMacroEngine
|
||||
{
|
||||
private readonly Func<HttpContextBase> _getHttpContext;
|
||||
private readonly Func<UmbracoContext> _getUmbracoContext;
|
||||
|
||||
public const string EngineName = "Partial View Macro Engine";
|
||||
public const string EngineName = "Partial View Macro Engine";
|
||||
|
||||
public PartialViewMacroEngine()
|
||||
{
|
||||
_getHttpContext = () =>
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new InvalidOperationException("The " + this.GetType() + " cannot execute with a null HttpContext.Current reference");
|
||||
return new HttpContextWrapper(HttpContext.Current);
|
||||
};
|
||||
public PartialViewMacroEngine()
|
||||
{
|
||||
_getHttpContext = () =>
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new InvalidOperationException("The " + this.GetType() + " cannot execute with a null HttpContext.Current reference");
|
||||
return new HttpContextWrapper(HttpContext.Current);
|
||||
};
|
||||
|
||||
_getUmbracoContext = () =>
|
||||
{
|
||||
if (UmbracoContext.Current == null)
|
||||
throw new InvalidOperationException("The " + this.GetType() + " cannot execute with a null UmbracoContext.Current reference");
|
||||
return UmbracoContext.Current;
|
||||
};
|
||||
}
|
||||
_getUmbracoContext = () =>
|
||||
{
|
||||
if (UmbracoContext.Current == null)
|
||||
throw new InvalidOperationException("The " + this.GetType() + " cannot execute with a null UmbracoContext.Current reference");
|
||||
return UmbracoContext.Current;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor generally used for unit testing
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="umbracoContext"> </param>
|
||||
internal PartialViewMacroEngine(HttpContextBase httpContext, UmbracoContext umbracoContext)
|
||||
{
|
||||
_getHttpContext = () => httpContext;
|
||||
_getUmbracoContext = () => umbracoContext;
|
||||
}
|
||||
/// <summary>
|
||||
/// Constructor generally used for unit testing
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="umbracoContext"> </param>
|
||||
internal PartialViewMacroEngine(HttpContextBase httpContext, UmbracoContext umbracoContext)
|
||||
{
|
||||
_getHttpContext = () => httpContext;
|
||||
_getUmbracoContext = () => umbracoContext;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return EngineName; }
|
||||
}
|
||||
public string Name
|
||||
{
|
||||
get { return EngineName; }
|
||||
}
|
||||
|
||||
//NOTE: We do not return any supported extensions because we don't want the MacroEngineFactory to return this
|
||||
// macro engine when searching for engines via extension. Those types of engines are reserved for files that are
|
||||
@@ -65,7 +67,6 @@ namespace Umbraco.Web.Macros
|
||||
public IEnumerable<string> SupportedExtensions
|
||||
{
|
||||
get { return Enumerable.Empty<string>(); }
|
||||
//get { return new[] {"cshtml", "vbhtml"}; }
|
||||
}
|
||||
|
||||
//NOTE: We do not return any supported extensions because we don't want the MacroEngineFactory to return this
|
||||
@@ -75,92 +76,99 @@ namespace Umbraco.Web.Macros
|
||||
public IEnumerable<string> SupportedUIExtensions
|
||||
{
|
||||
get { return Enumerable.Empty<string>(); }
|
||||
//get { return new[] { "cshtml", "vbhtml" }; }
|
||||
}
|
||||
public Dictionary<string, IMacroGuiRendering> SupportedProperties
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public Dictionary<string, IMacroGuiRendering> SupportedProperties
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
public bool Validate(string code, string tempFileName, INode currentPage, out string errorMessage)
|
||||
{
|
||||
var temp = GetVirtualPathFromPhysicalPath(tempFileName);
|
||||
try
|
||||
{
|
||||
CompileAndInstantiate(temp);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
errorMessage = exception.Message;
|
||||
return false;
|
||||
}
|
||||
errorMessage = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Validate(string code, string tempFileName, INode currentPage, out string errorMessage)
|
||||
{
|
||||
var temp = GetVirtualPathFromPhysicalPath(tempFileName);
|
||||
try
|
||||
{
|
||||
CompileAndInstantiate(temp);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
errorMessage = exception.Message;
|
||||
return false;
|
||||
}
|
||||
errorMessage = string.Empty;
|
||||
return true;
|
||||
}
|
||||
public string Execute(MacroModel macro, INode currentPage)
|
||||
{
|
||||
if (macro == null) throw new ArgumentNullException("macro");
|
||||
if (currentPage == null) throw new ArgumentNullException("currentPage");
|
||||
if (macro.ScriptName.IsNullOrWhiteSpace()) throw new ArgumentException("The ScriptName property of the macro object cannot be null or empty");
|
||||
|
||||
public string Execute(MacroModel macro, INode currentPage)
|
||||
{
|
||||
if (macro == null) throw new ArgumentNullException("macro");
|
||||
if (currentPage == null) throw new ArgumentNullException("currentPage");
|
||||
if (!macro.ScriptName.StartsWith(SystemDirectories.MvcViews + "/MacroPartials/")
|
||||
&& (!Regex.IsMatch(macro.ScriptName, "~/App_Plugins/.+?/Views/MacroPartials", RegexOptions.Compiled)))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot render the Partial View Macro with file: " + macro.ScriptName + ". All Partial View Macros must exist in the " + SystemDirectories.MvcViews + "/MacroPartials/ folder");
|
||||
}
|
||||
|
||||
if (!macro.ScriptName.StartsWith(SystemDirectories.MvcViews + "/MacroPartials/")
|
||||
&& (!Regex.IsMatch(macro.ScriptName, "~/App_Plugins/.+?/Views/MacroPartials", RegexOptions.Compiled)))
|
||||
{
|
||||
throw new InvalidOperationException("Cannot render the Partial View Macro with file: " + macro.ScriptName + ". All Partial View Macros must exist in the " + SystemDirectories.MvcViews + "/MacroPartials/ folder");
|
||||
}
|
||||
var http = _getHttpContext();
|
||||
var umbCtx = _getUmbracoContext();
|
||||
var routeVals = new RouteData();
|
||||
routeVals.Values.Add("controller", "PartialViewMacro");
|
||||
routeVals.Values.Add("action", "Index");
|
||||
routeVals.DataTokens.Add("umbraco-context", umbCtx); //required for UmbracoViewPage
|
||||
|
||||
var http = _getHttpContext();
|
||||
var umbCtx = _getUmbracoContext();
|
||||
var routeVals = new RouteData();
|
||||
routeVals.Values.Add("controller", "PartialViewMacro");
|
||||
routeVals.Values.Add("action", "Index");
|
||||
routeVals.DataTokens.Add("umbraco-context", umbCtx); //required for UmbracoViewPage
|
||||
//lets render this controller as a child action if we are currently executing using MVC
|
||||
//(otherwise don't do this since we're using webforms)
|
||||
var mvcHandler = http.CurrentHandler as MvcHandler;
|
||||
var viewContext = new ViewContext { ViewData = new ViewDataDictionary() }; ;
|
||||
if (mvcHandler != null)
|
||||
{
|
||||
//try and extract the current view context from the route values, this would be set in the UmbracoViewPage.
|
||||
if (mvcHandler.RequestContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext))
|
||||
{
|
||||
viewContext = (ViewContext)mvcHandler.RequestContext.RouteData.DataTokens[Constants.DataTokenCurrentViewContext];
|
||||
}
|
||||
routeVals.DataTokens.Add("ParentActionViewContext", viewContext);
|
||||
}
|
||||
|
||||
////lets render this controller as a child action if we are currently executing using MVC
|
||||
////(otherwise don't do this since we're using webforms)
|
||||
//var mvcHandler = http.CurrentHandler as MvcHandler;
|
||||
//if (mvcHandler != null)
|
||||
//{
|
||||
// routeVals.DataTokens.Add("ParentActionViewContext",
|
||||
// //If we could get access to the currently executing controller we could do this but this is nearly
|
||||
// //impossible. The only way to do that would be to store the controller instance in the route values
|
||||
// //in the base class of the UmbracoController.... but not sure the reprocussions of that, i think it could
|
||||
// //work but is a bit nasty.
|
||||
// new ViewContext());
|
||||
//}
|
||||
var request = new RequestContext(http, routeVals);
|
||||
string output;
|
||||
using (var controller = new PartialViewMacroController(umbCtx, macro, currentPage))
|
||||
{
|
||||
//bubble up the model state from the main view context to our custom controller.
|
||||
//when merging we'll create a new dictionary, otherwise you might run into an enumeration error
|
||||
// caused from ModelStateDictionary
|
||||
controller.ModelState.Merge(new ModelStateDictionary(viewContext.ViewData.ModelState));
|
||||
controller.ControllerContext = new ControllerContext(request, controller);
|
||||
//call the action to render
|
||||
var result = controller.Index();
|
||||
output = controller.RenderViewResultAsString(result);
|
||||
}
|
||||
|
||||
var request = new RequestContext(http, routeVals);
|
||||
string output;
|
||||
using (var controller = new PartialViewMacroController(umbCtx, macro, currentPage))
|
||||
{
|
||||
controller.ControllerContext = new ControllerContext(request, controller);
|
||||
var result = controller.Index();
|
||||
output = controller.RenderViewResultAsString(result);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
private string GetVirtualPathFromPhysicalPath(string physicalPath)
|
||||
{
|
||||
string rootpath = _getHttpContext().Server.MapPath("~/");
|
||||
physicalPath = physicalPath.Replace(rootpath, "");
|
||||
physicalPath = physicalPath.Replace("\\", "/");
|
||||
return "~/" + physicalPath;
|
||||
}
|
||||
|
||||
private string GetVirtualPathFromPhysicalPath(string physicalPath)
|
||||
{
|
||||
string rootpath = _getHttpContext().Server.MapPath("~/");
|
||||
physicalPath = physicalPath.Replace(rootpath, "");
|
||||
physicalPath = physicalPath.Replace("\\", "/");
|
||||
return "~/" + physicalPath;
|
||||
}
|
||||
private static PartialViewMacroPage CompileAndInstantiate(string virtualPath)
|
||||
{
|
||||
//Compile Razor - We Will Leave This To ASP.NET Compilation Engine & ASP.NET WebPages
|
||||
//Security in medium trust is strict around here, so we can only pass a virtual file path
|
||||
//ASP.NET Compilation Engine caches returned types
|
||||
//Changed From BuildManager As Other Properties Are Attached Like Context Path/
|
||||
var webPageBase = WebPageBase.CreateInstanceFromVirtualPath(virtualPath);
|
||||
var webPage = webPageBase as PartialViewMacroPage;
|
||||
if (webPage == null)
|
||||
throw new InvalidCastException("All Partial View Macro views must inherit from " + typeof(PartialViewMacroPage).FullName);
|
||||
return webPage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static PartialViewMacroPage CompileAndInstantiate(string virtualPath)
|
||||
{
|
||||
//Compile Razor - We Will Leave This To ASP.NET Compilation Engine & ASP.NET WebPages
|
||||
//Security in medium trust is strict around here, so we can only pass a virtual file path
|
||||
//ASP.NET Compilation Engine caches returned types
|
||||
//Changed From BuildManager As Other Properties Are Attached Like Context Path/
|
||||
var webPageBase = WebPageBase.CreateInstanceFromVirtualPath(virtualPath);
|
||||
var webPage = webPageBase as PartialViewMacroPage;
|
||||
if (webPage == null)
|
||||
throw new InvalidCastException("All Partial View Macro views must inherit from " + typeof(PartialViewMacroPage).FullName);
|
||||
return webPage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Macros
|
||||
{
|
||||
/// <summary>
|
||||
/// The base view class that PartialViewMacro views need to inherit from
|
||||
/// </summary>
|
||||
public abstract class PartialViewMacroPage : UmbracoViewPage<PartialViewMacroModel>
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// The base view class that PartialViewMacro views need to inherit from
|
||||
/// </summary>
|
||||
public abstract class PartialViewMacroPage : UmbracoViewPage<PartialViewMacroModel>
|
||||
{
|
||||
protected override void InitializePage()
|
||||
{
|
||||
base.InitializePage();
|
||||
//set the model to the current node if it is not set, this is generally not the case
|
||||
if (Model != null)
|
||||
{
|
||||
CurrentPage = Model.Content.AsDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the a DynamicPublishedContent object
|
||||
/// </summary>
|
||||
public dynamic CurrentPage { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -637,7 +637,14 @@ namespace Umbraco.Web.Models
|
||||
|
||||
public IPublishedContentProperty GetProperty(string alias)
|
||||
{
|
||||
return GetProperty(alias, false);
|
||||
var prop = GetProperty(alias, false);
|
||||
if (prop == null && alias.StartsWith("_"))
|
||||
{
|
||||
//if it is prefixed and the first result failed, try to get it by recursive
|
||||
var recursiveAlias = alias.Substring(1, alias.Length - 1);
|
||||
return GetProperty(recursiveAlias, true);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
public IPublishedContentProperty GetProperty(string alias, bool recursive)
|
||||
{
|
||||
|
||||
@@ -1,23 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The model used when rendering Partial View Macros
|
||||
/// </summary>
|
||||
public class PartialViewMacroModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The model used when rendering Partial View Macros
|
||||
/// </summary>
|
||||
public class PartialViewMacroModel
|
||||
{
|
||||
|
||||
public PartialViewMacroModel(IPublishedContent page, IDictionary<string, object> macroParams)
|
||||
{
|
||||
CurrentPage = page;
|
||||
MacroParameters = macroParams;
|
||||
}
|
||||
public PartialViewMacroModel(IPublishedContent page,
|
||||
int macroId,
|
||||
string macroAlias,
|
||||
string macroName,
|
||||
IDictionary<string, object> macroParams)
|
||||
{
|
||||
Content = page;
|
||||
MacroParameters = macroParams;
|
||||
MacroName = macroName;
|
||||
MacroAlias = macroAlias;
|
||||
MacroId = macroId;
|
||||
}
|
||||
|
||||
public IPublishedContent CurrentPage { get; private set; }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use the constructor accepting the macro id instead")]
|
||||
public PartialViewMacroModel(IPublishedContent page, IDictionary<string, object> macroParams)
|
||||
{
|
||||
Content = page;
|
||||
MacroParameters = macroParams;
|
||||
}
|
||||
|
||||
public IDictionary<string, object> MacroParameters { get; private set; }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use the Content property instead")]
|
||||
public IPublishedContent CurrentPage
|
||||
{
|
||||
get { return Content; }
|
||||
}
|
||||
|
||||
}
|
||||
public IPublishedContent Content { get; private set; }
|
||||
public string MacroName { get; private set; }
|
||||
public string MacroAlias { get; private set; }
|
||||
public int MacroId { get; private set; }
|
||||
public IDictionary<string, object> MacroParameters { get; private set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
public const string ViewLocation = "~/Views";
|
||||
}
|
||||
/// <summary>
|
||||
/// constants
|
||||
/// </summary>
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string ViewLocation = "~/Views";
|
||||
|
||||
internal const string DataTokenCurrentViewContext = "umbraco-current-view-context";
|
||||
}
|
||||
}
|
||||
@@ -97,56 +97,58 @@ namespace Umbraco.Web.Mvc
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normally in MVC the way that the View object gets assigned to the result is to Execute the ViewResult, this however
|
||||
/// will write to the Response output stream which isn't what we want. Instead, this method will use the same logic inside
|
||||
/// of MVC to assign the View object to the result but without executing it. This also ensures that the ViewData and the TempData
|
||||
/// is assigned from the controller.
|
||||
/// This is only relavent for view results of PartialViewResult or ViewResult.
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <param name="controller"></param>
|
||||
internal static void EnsureViewObjectDataOnResult(this ControllerBase controller, ViewResultBase result)
|
||||
{
|
||||
result.ViewData.ModelState.Merge(controller.ViewData.ModelState);
|
||||
/// <summary>
|
||||
/// Normally in MVC the way that the View object gets assigned to the result is to Execute the ViewResult, this however
|
||||
/// will write to the Response output stream which isn't what we want. Instead, this method will use the same logic inside
|
||||
/// of MVC to assign the View object to the result but without executing it. This also ensures that the ViewData and the TempData
|
||||
/// is assigned from the controller.
|
||||
/// This is only relavent for view results of PartialViewResult or ViewResult.
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <param name="controller"></param>
|
||||
internal static void EnsureViewObjectDataOnResult(this ControllerBase controller, ViewResultBase result)
|
||||
{
|
||||
//when merging we'll create a new dictionary, otherwise you might run into an enumeration error
|
||||
// caused from ModelStateDictionary
|
||||
result.ViewData.ModelState.Merge(new ModelStateDictionary(controller.ViewData.ModelState));
|
||||
|
||||
// Temporarily copy the dictionary to avoid enumerator-modification errors
|
||||
var newViewDataDict = new ViewDataDictionary(controller.ViewData);
|
||||
foreach (var d in newViewDataDict)
|
||||
result.ViewData[d.Key] = d.Value;
|
||||
// Temporarily copy the dictionary to avoid enumerator-modification errors
|
||||
var newViewDataDict = new ViewDataDictionary(controller.ViewData);
|
||||
foreach (var d in newViewDataDict)
|
||||
result.ViewData[d.Key] = d.Value;
|
||||
|
||||
result.TempData = controller.TempData;
|
||||
result.TempData = controller.TempData;
|
||||
|
||||
if (result.View != null) return;
|
||||
if (result.View != null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(result.ViewName))
|
||||
result.ViewName = controller.ControllerContext.RouteData.GetRequiredString("action");
|
||||
if (string.IsNullOrEmpty(result.ViewName))
|
||||
result.ViewName = controller.ControllerContext.RouteData.GetRequiredString("action");
|
||||
|
||||
if (result.View != null) return;
|
||||
if (result.View != null) return;
|
||||
|
||||
if (result is PartialViewResult)
|
||||
{
|
||||
var viewEngineResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
|
||||
if (result is PartialViewResult)
|
||||
{
|
||||
var viewEngineResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
|
||||
|
||||
if (viewEngineResult.View == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the view " + result.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
|
||||
}
|
||||
if (viewEngineResult.View == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the view " + result.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
|
||||
}
|
||||
|
||||
result.View = viewEngineResult.View;
|
||||
}
|
||||
else if (result is ViewResult)
|
||||
{
|
||||
var vr = (ViewResult)result;
|
||||
var viewEngineResult = ViewEngines.Engines.FindView(controller.ControllerContext, vr.ViewName, vr.MasterName);
|
||||
result.View = viewEngineResult.View;
|
||||
}
|
||||
else if (result is ViewResult)
|
||||
{
|
||||
var vr = (ViewResult)result;
|
||||
var viewEngineResult = ViewEngines.Engines.FindView(controller.ControllerContext, vr.ViewName, vr.MasterName);
|
||||
|
||||
if (viewEngineResult.View == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the view " + vr.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
|
||||
}
|
||||
if (viewEngineResult.View == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the view " + vr.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
|
||||
}
|
||||
|
||||
result.View = viewEngineResult.View;
|
||||
}
|
||||
}
|
||||
result.View = viewEngineResult.View;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to ensure that actions with duplicate names that are not child actions don't get executed when
|
||||
/// we are Posting and not redirecting.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See issue: http://issues.umbraco.org/issue/U4-1819
|
||||
/// </remarks>
|
||||
public class NotChildAction : ActionMethodSelectorAttribute
|
||||
{
|
||||
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
|
||||
{
|
||||
var isChildAction = controllerContext.IsChildAction;
|
||||
return !isChildAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,89 +8,119 @@ using Umbraco.Core;
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The base controller that all Presentation Add-in controllers should inherit from
|
||||
/// </summary>
|
||||
[MergeModelStateToChildAction]
|
||||
public abstract class SurfaceController : PluginController
|
||||
{
|
||||
/// <summary>
|
||||
/// The base controller that all Presentation Add-in controllers should inherit from
|
||||
/// </summary>
|
||||
[MergeModelStateToChildAction]
|
||||
public abstract class SurfaceController : PluginController
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
protected SurfaceController(UmbracoContext umbracoContext)
|
||||
: base(umbracoContext)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
protected SurfaceController(UmbracoContext umbracoContext)
|
||||
: base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty constructor, uses Singleton to resolve the UmbracoContext
|
||||
/// </summary>
|
||||
protected SurfaceController()
|
||||
: base(UmbracoContext.Current)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Empty constructor, uses Singleton to resolve the UmbracoContext
|
||||
/// </summary>
|
||||
protected SurfaceController()
|
||||
: base(UmbracoContext.Current)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(pageId, UmbracoContext);
|
||||
}
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(pageId, UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(publishedContent, UmbracoContext);
|
||||
}
|
||||
/// <summary>
|
||||
/// Redirects to the Umbraco page with the given id
|
||||
/// </summary>
|
||||
/// <param name="publishedContent"></param>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent)
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(publishedContent, UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirects to the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage()
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(CurrentPage, UmbracoContext);
|
||||
}
|
||||
/// <summary>
|
||||
/// Redirects to the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage()
|
||||
{
|
||||
return new RedirectToUmbracoPageResult(CurrentPage, UmbracoContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected UmbracoPageResult CurrentUmbracoPage()
|
||||
{
|
||||
return new UmbracoPageResult();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected UmbracoPageResult CurrentUmbracoPage()
|
||||
{
|
||||
return new UmbracoPageResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current page.
|
||||
/// </summary>
|
||||
protected IPublishedContent CurrentPage
|
||||
{
|
||||
get
|
||||
{
|
||||
var routeData = ControllerContext.IsChildAction
|
||||
? ControllerContext.ParentActionViewContext.RouteData
|
||||
: ControllerContext.RouteData;
|
||||
|
||||
if (!routeData.DataTokens.ContainsKey("umbraco-route-def"))
|
||||
/// <summary>
|
||||
/// Gets the current page.
|
||||
/// </summary>
|
||||
protected IPublishedContent CurrentPage
|
||||
{
|
||||
get
|
||||
{
|
||||
var routeDefAttempt = TryGetRouteDefinitionFromAncestorViewContexts();
|
||||
if (!routeDefAttempt.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request");
|
||||
throw routeDefAttempt.Error;
|
||||
}
|
||||
|
||||
var routeDef = (RouteDefinition)routeData.DataTokens["umbraco-route-def"];
|
||||
return routeDef.PublishedContentRequest.PublishedContent;
|
||||
}
|
||||
}
|
||||
var routeDef = routeDefAttempt.Result;
|
||||
return routeDef.PublishedContentRequest.PublishedContent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// we need to recursively find the route definition based on the parent view context
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We may have Child Actions within Child actions so we need to recursively look this up.
|
||||
/// see: http://issues.umbraco.org/issue/U4-1844
|
||||
/// </remarks>
|
||||
private Attempt<RouteDefinition> TryGetRouteDefinitionFromAncestorViewContexts()
|
||||
{
|
||||
ControllerContext currentContext = ControllerContext;
|
||||
while (currentContext != null)
|
||||
{
|
||||
var currentRouteData = currentContext.RouteData;
|
||||
if (currentRouteData.DataTokens.ContainsKey("umbraco-route-def"))
|
||||
{
|
||||
return new Attempt<RouteDefinition>(true, (RouteDefinition)currentRouteData.DataTokens["umbraco-route-def"]);
|
||||
}
|
||||
if (currentContext.IsChildAction)
|
||||
{
|
||||
//assign current context to parent
|
||||
currentContext = currentContext.ParentActionViewContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
//exit the loop
|
||||
currentRouteData = null;
|
||||
}
|
||||
}
|
||||
return new Attempt<RouteDefinition>(
|
||||
new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
using System;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.Dynamics;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models;
|
||||
|
||||
@@ -60,5 +65,33 @@ namespace Umbraco.Web.Mvc
|
||||
get { return _helper ?? (_helper = new UmbracoHelper(UmbracoContext, Model.Content)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will detect the end /body tag and insert the preview badge if in preview mode
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteLiteral(object value)
|
||||
{
|
||||
// filter / add preview banner
|
||||
if (Response.ContentType.InvariantEquals("text/html") && UmbracoContext.Current.InPreviewMode) // ASP.NET default value
|
||||
{
|
||||
var text = value.ToString().ToLowerInvariant();
|
||||
int pos = text.IndexOf("</body>");
|
||||
if (pos > -1)
|
||||
{
|
||||
var htmlBadge =
|
||||
String.Format(UmbracoSettings.PreviewBadge,
|
||||
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
||||
IOHelper.ResolveUrl(SystemDirectories.UmbracoClient),
|
||||
Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path));
|
||||
|
||||
text = text.Substring(0, pos) + htmlBadge + text.Substring(pos, text.Length - pos);
|
||||
base.WriteLiteral(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.WriteLiteral(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,94 +4,113 @@ using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// The View that umbraco front-end views inherit from
|
||||
/// </summary>
|
||||
public abstract class UmbracoViewPage<T> : WebViewPage<T>
|
||||
{
|
||||
protected UmbracoViewPage()
|
||||
{
|
||||
/// <summary>
|
||||
/// The View that umbraco front-end views inherit from
|
||||
/// </summary>
|
||||
public abstract class UmbracoViewPage<T> : WebViewPage<T>
|
||||
{
|
||||
protected UmbracoViewPage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current UmbracoContext
|
||||
/// </summary>
|
||||
public UmbracoContext UmbracoContext
|
||||
{
|
||||
get
|
||||
{
|
||||
//we should always try to return the context from the data tokens just in case its a custom context and not
|
||||
//using the UmbracoContext.Current.
|
||||
//we will fallback to the singleton if necessary.
|
||||
if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-context"))
|
||||
{
|
||||
return (UmbracoContext)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context");
|
||||
}
|
||||
//next check if it is a child action and see if the parent has it set in data tokens
|
||||
if (ViewContext.IsChildAction)
|
||||
{
|
||||
if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-context"))
|
||||
{
|
||||
return (UmbracoContext)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context");
|
||||
}
|
||||
}
|
||||
|
||||
//lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this
|
||||
//class and are rendering it outside of the normal Umbraco routing process. Very unlikely.
|
||||
return UmbracoContext.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current ApplicationContext
|
||||
/// </summary>
|
||||
public ApplicationContext ApplicationContext
|
||||
{
|
||||
get { return UmbracoContext.Application; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the current UmbracoContext
|
||||
/// </summary>
|
||||
public UmbracoContext UmbracoContext
|
||||
{
|
||||
get
|
||||
{
|
||||
//we should always try to return the context from the data tokens just in case its a custom context and not
|
||||
//using the UmbracoContext.Current.
|
||||
//we will fallback to the singleton if necessary.
|
||||
if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-context"))
|
||||
{
|
||||
return (UmbracoContext)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context");
|
||||
}
|
||||
//next check if it is a child action and see if the parent has it set in data tokens
|
||||
if (ViewContext.IsChildAction)
|
||||
{
|
||||
if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-context"))
|
||||
{
|
||||
return (UmbracoContext)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-context");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current PublishedContentRequest
|
||||
/// </summary>
|
||||
internal PublishedContentRequest PublishedContentRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
//we should always try to return the object from the data tokens just in case its a custom object and not
|
||||
//using the UmbracoContext.Current.
|
||||
//we will fallback to the singleton if necessary.
|
||||
if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request"))
|
||||
{
|
||||
return (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request");
|
||||
}
|
||||
//next check if it is a child action and see if the parent has it set in data tokens
|
||||
if (ViewContext.IsChildAction)
|
||||
{
|
||||
if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request"))
|
||||
{
|
||||
return (PublishedContentRequest)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request");
|
||||
}
|
||||
}
|
||||
//lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this
|
||||
//class and are rendering it outside of the normal Umbraco routing process. Very unlikely.
|
||||
return UmbracoContext.Current;
|
||||
}
|
||||
}
|
||||
|
||||
//lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this
|
||||
//class and are rendering it outside of the normal Umbraco routing process. Very unlikely.
|
||||
return UmbracoContext.Current.PublishedContentRequest;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the current ApplicationContext
|
||||
/// </summary>
|
||||
public ApplicationContext ApplicationContext
|
||||
{
|
||||
get { return UmbracoContext.Application; }
|
||||
}
|
||||
|
||||
private UmbracoHelper _helper;
|
||||
/// <summary>
|
||||
/// Returns the current PublishedContentRequest
|
||||
/// </summary>
|
||||
internal PublishedContentRequest PublishedContentRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
//we should always try to return the object from the data tokens just in case its a custom object and not
|
||||
//using the UmbracoContext.Current.
|
||||
//we will fallback to the singleton if necessary.
|
||||
if (ViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request"))
|
||||
{
|
||||
return (PublishedContentRequest)ViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request");
|
||||
}
|
||||
//next check if it is a child action and see if the parent has it set in data tokens
|
||||
if (ViewContext.IsChildAction)
|
||||
{
|
||||
if (ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey("umbraco-doc-request"))
|
||||
{
|
||||
return (PublishedContentRequest)ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject("umbraco-doc-request");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an UmbracoHelper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This constructs the UmbracoHelper with the content model of the page routed to
|
||||
/// </remarks>
|
||||
public virtual UmbracoHelper Umbraco
|
||||
{
|
||||
get { return _helper ?? (_helper = new UmbracoHelper(UmbracoContext)); }
|
||||
}
|
||||
//lastly, we will use the singleton, the only reason this should ever happen is is someone is rendering a page that inherits from this
|
||||
//class and are rendering it outside of the normal Umbraco routing process. Very unlikely.
|
||||
return UmbracoContext.Current.PublishedContentRequest;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private UmbracoHelper _helper;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an UmbracoHelper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This constructs the UmbracoHelper with the content model of the page routed to
|
||||
/// </remarks>
|
||||
public virtual UmbracoHelper Umbraco
|
||||
{
|
||||
get { return _helper ?? (_helper = new UmbracoHelper(UmbracoContext)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the current view context is added to the route data tokens so we can extract it if we like
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently this is required by mvc macro engines
|
||||
/// </remarks>
|
||||
protected override void InitializePage()
|
||||
{
|
||||
base.InitializePage();
|
||||
if (!ViewContext.IsChildAction)
|
||||
{
|
||||
if (!ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext))
|
||||
{
|
||||
ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, this.ViewContext);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -274,6 +274,7 @@
|
||||
<Compile Include="DefaultPublishedMediaStore.cs" />
|
||||
<Compile Include="Dictionary\UmbracoCultureDictionary.cs" />
|
||||
<Compile Include="Dictionary\UmbracoCultureDictionaryFactory.cs" />
|
||||
<Compile Include="Mvc\NotChildAction.cs" />
|
||||
<Compile Include="Mvc\UmbracoViewPage.cs" />
|
||||
<Compile Include="PublishedContentExtensions.cs" />
|
||||
<Compile Include="ExamineExtensions.cs" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
using Umbraco.Web.Routing;
|
||||
using umbraco;
|
||||
using umbraco.IO;
|
||||
@@ -33,7 +34,74 @@ namespace Umbraco.Web
|
||||
/// </summary>
|
||||
private static UmbracoContext _umbracoContext;
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="applicationContext"></param>
|
||||
/// <returns>
|
||||
/// The Singleton context object
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
[UmbracoProposedPublic("http://issues.umbraco.org/issue/U4-1717")]
|
||||
internal static UmbracoContext EnsureContext(HttpContextBase httpContext, ApplicationContext applicationContext)
|
||||
{
|
||||
return EnsureContext(httpContext, applicationContext, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="applicationContext"></param>
|
||||
/// <param name="replaceContext">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The Singleton context object
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
internal static UmbracoContext EnsureContext(HttpContextBase httpContext, ApplicationContext applicationContext, bool replaceContext)
|
||||
{
|
||||
if (UmbracoContext.Current != null && !replaceContext)
|
||||
return UmbracoContext.Current;
|
||||
|
||||
var umbracoContext = new UmbracoContext(httpContext, applicationContext, RoutesCacheResolver.Current.RoutesCache);
|
||||
|
||||
// create the nice urls provider
|
||||
var niceUrls = new NiceUrlProvider(PublishedContentStoreResolver.Current.PublishedContentStore, umbracoContext);
|
||||
|
||||
// create the RoutingContext, and assign
|
||||
var routingContext = new RoutingContext(
|
||||
umbracoContext,
|
||||
DocumentLookupsResolver.Current.DocumentLookups,
|
||||
LastChanceLookupResolver.Current.LastChanceLookup,
|
||||
PublishedContentStoreResolver.Current.PublishedContentStore,
|
||||
niceUrls);
|
||||
|
||||
//assign the routing context back
|
||||
umbracoContext.RoutingContext = routingContext;
|
||||
|
||||
//assign the singleton
|
||||
UmbracoContext.Current = umbracoContext;
|
||||
return UmbracoContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Umbraco context.
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
@@ -55,7 +123,19 @@ namespace Umbraco.Web
|
||||
|
||||
// set the urls...
|
||||
//original request url
|
||||
this.OriginalRequestUrl = httpContext.Request.Url;
|
||||
//NOTE: The request will not be available during app startup so we can only set this to an absolute URL of localhost, this
|
||||
// is a work around to being able to access the UmbracoContext during application startup and this will also ensure that people
|
||||
// 'could' still generate URLs during startup BUT any domain driven URL generation will not work because it is NOT possible to get
|
||||
// the current domain during application startup.
|
||||
// see: http://issues.umbraco.org/issue/U4-1890
|
||||
|
||||
var requestUrl = new Uri("http://localhost");
|
||||
var request = GetRequestFromContext();
|
||||
if (request != null)
|
||||
{
|
||||
requestUrl = request.Url;
|
||||
}
|
||||
this.OriginalRequestUrl = requestUrl;
|
||||
//cleaned request url
|
||||
this.CleanedUmbracoUrl = UriUtility.UriToUmbraco(this.OriginalRequestUrl);
|
||||
|
||||
@@ -220,7 +300,10 @@ namespace Umbraco.Web
|
||||
{
|
||||
get
|
||||
{
|
||||
return GlobalSettings.DebugMode && (!string.IsNullOrEmpty(HttpContext.Request["umbdebugshowtrace"]) || !string.IsNullOrEmpty(HttpContext.Request["umbdebug"]));
|
||||
var request = GetRequestFromContext();
|
||||
//NOTE: the request can be null during app startup!
|
||||
return GlobalSettings.DebugMode && request != null
|
||||
&& (!string.IsNullOrEmpty(request["umbdebugshowtrace"]) || !string.IsNullOrEmpty(request["umbdebug"]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,10 +346,11 @@ namespace Umbraco.Web
|
||||
{
|
||||
get
|
||||
{
|
||||
if (HttpContext.Request == null || HttpContext.Request.Url == null)
|
||||
var request = GetRequestFromContext();
|
||||
if (request == null || request.Url == null)
|
||||
return false;
|
||||
|
||||
var currentUrl = HttpContext.Request.Url.AbsolutePath;
|
||||
var currentUrl = request.Url.AbsolutePath;
|
||||
// zb-00004 #29956 : refactor cookies names & handling
|
||||
return
|
||||
StateHelper.Cookies.Preview.HasValue // has preview cookie
|
||||
@@ -275,6 +359,18 @@ namespace Umbraco.Web
|
||||
}
|
||||
}
|
||||
|
||||
private HttpRequestBase GetRequestFromContext()
|
||||
{
|
||||
try
|
||||
{
|
||||
return HttpContext.Request;
|
||||
}
|
||||
catch (System.Web.HttpException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -50,23 +50,8 @@ namespace Umbraco.Web
|
||||
legacyRequestInitializer.InitializeRequest();
|
||||
|
||||
// create the UmbracoContext singleton, one per request, and assign
|
||||
var umbracoContext = new UmbracoContext(
|
||||
httpContext,
|
||||
ApplicationContext.Current,
|
||||
RoutesCacheResolver.Current.RoutesCache);
|
||||
UmbracoContext.Current = umbracoContext;
|
||||
|
||||
// create the nice urls provider
|
||||
var niceUrls = new NiceUrlProvider(PublishedContentStoreResolver.Current.PublishedContentStore, umbracoContext);
|
||||
|
||||
// create the RoutingContext, and assign
|
||||
var routingContext = new RoutingContext(
|
||||
umbracoContext,
|
||||
DocumentLookupsResolver.Current.DocumentLookups,
|
||||
LastChanceLookupResolver.Current.LastChanceLookup,
|
||||
PublishedContentStoreResolver.Current.PublishedContentStore,
|
||||
niceUrls);
|
||||
umbracoContext.RoutingContext = routingContext;
|
||||
// NOTE: we assign 'true' to ensure the context is replaced if it is already set (i.e. during app startup)
|
||||
UmbracoContext.EnsureContext(httpContext, ApplicationContext.Current, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Web
|
||||
// see also VirtualPathUtility.ToAppRelative
|
||||
public static string ToAppRelative(string virtualPath)
|
||||
{
|
||||
if (virtualPath.StartsWith(_appPathPrefix))
|
||||
if (virtualPath.InvariantStartsWith(_appPathPrefix))
|
||||
virtualPath = virtualPath.Substring(_appPathPrefix.Length);
|
||||
if (virtualPath.Length == 0)
|
||||
virtualPath = "/";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core;
|
||||
@@ -57,7 +58,7 @@ namespace Umbraco.Web
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override IBootManager Initialize()
|
||||
{
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK]
|
||||
@@ -98,6 +99,19 @@ namespace Umbraco.Web
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method in order to ensure that the UmbracoContext is also created, this can only be
|
||||
/// created after resolution is frozen!
|
||||
/// </summary>
|
||||
protected override void FreezeResolution()
|
||||
{
|
||||
base.FreezeResolution();
|
||||
|
||||
//before we do anything, we'll ensure the umbraco context
|
||||
//see: http://issues.umbraco.org/issue/U4-1717
|
||||
UmbracoContext.EnsureContext(new HttpContextWrapper(_umbracoApplication.Context), ApplicationContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that the OnApplicationStarting methods of the IApplicationEvents are called
|
||||
/// </summary>
|
||||
|
||||
@@ -1091,8 +1091,7 @@ namespace umbraco
|
||||
string sql =
|
||||
@"select umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, cmsContentXml.xml from umbracoNode
|
||||
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
|
||||
inner join cmsDocument on cmsDocument.nodeId = umbracoNode.id
|
||||
where cmsDocument.published = 1
|
||||
where umbracoNode.id in (select cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1)
|
||||
order by umbracoNode.level, umbracoNode.sortOrder";
|
||||
|
||||
lock (DbReadSyncLock)
|
||||
|
||||
@@ -16,19 +16,19 @@ using System.Web.UI.WebControls;
|
||||
using System.Xml;
|
||||
using System.Xml.Xsl;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Macros;
|
||||
using Umbraco.Web.Templates;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.BusinessLogic.Utils;
|
||||
using umbraco.cms.businesslogic;
|
||||
using umbraco.cms.businesslogic.macro;
|
||||
using umbraco.cms.businesslogic.member;
|
||||
using umbraco.DataLayer;
|
||||
using umbraco.NodeFactory;
|
||||
using umbraco.presentation.templateControls;
|
||||
using umbraco.presentation.xslt.Exslt;
|
||||
using Content = umbraco.cms.businesslogic.Content;
|
||||
using Macro = umbraco.cms.businesslogic.macro.Macro;
|
||||
|
||||
@@ -373,6 +373,16 @@ namespace umbraco
|
||||
switch (macroType)
|
||||
{
|
||||
case (int)MacroTypes.PartialView:
|
||||
|
||||
//error handler for partial views, is an action because we need to re-use it twice below
|
||||
Func<Exception, Control> handleError = e =>
|
||||
{
|
||||
LogHelper.WarnWithException<macro>("Error loading Partial View (file: " + ScriptFile + ")", true, e);
|
||||
// Invoke any error handlers for this macro
|
||||
var macroErrorEventArgs = new MacroErrorEventArgs { Name = Model.Name, Alias = Model.Alias, ItemKey = Model.ScriptName, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour };
|
||||
return GetControlForErrorBehavior("Error loading Partial View script (file: " + ScriptFile + ")", macroErrorEventArgs);
|
||||
};
|
||||
|
||||
TraceInfo("umbracoMacro", "Partial View added (" + Model.TypeName + ")");
|
||||
try
|
||||
{
|
||||
@@ -380,41 +390,27 @@ namespace umbraco
|
||||
macroControl = new LiteralControl(result.Result);
|
||||
if (result.ResultException != null)
|
||||
{
|
||||
// we'll throw the error if we run in release mode, show details if we're in release mode!
|
||||
renderFailed = true;
|
||||
if (HttpContext.Current != null && !HttpContext.Current.IsDebuggingEnabled)
|
||||
throw result.ResultException;
|
||||
Exceptions.Add(result.ResultException);
|
||||
macroControl = handleError(result.ResultException);
|
||||
//if it is null, then we are supposed to throw the exception
|
||||
if (macroControl == null)
|
||||
{
|
||||
throw result.ResultException;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
renderFailed = true;
|
||||
Exceptions.Add(e);
|
||||
|
||||
LogHelper.WarnWithException<macro>("Error loading MacroEngine script (file: " + ScriptFile + ", Type: '" + Model.TypeName + "'",
|
||||
true,
|
||||
e);
|
||||
|
||||
var result =
|
||||
new LiteralControl("Error loading MacroEngine script (file: " + ScriptFile + ")");
|
||||
|
||||
/*
|
||||
string args = "<ul>";
|
||||
foreach(object key in attributes.Keys)
|
||||
args += "<li><strong>" + key.ToString() + ": </strong> " + attributes[key] + "</li>";
|
||||
|
||||
foreach (object key in pageElements.Keys)
|
||||
args += "<li><strong>" + key.ToString() + ": </strong> " + pageElements[key] + "</li>";
|
||||
|
||||
args += "</ul>";
|
||||
|
||||
result.Text += args;
|
||||
*/
|
||||
|
||||
macroControl = result;
|
||||
|
||||
break;
|
||||
Exceptions.Add(e);
|
||||
macroControl = handleError(e);
|
||||
//if it is null, then we are supposed to throw the (original) exception
|
||||
// see: http://issues.umbraco.org/issue/U4-497 at the end
|
||||
if (macroControl == null)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -429,17 +425,26 @@ namespace umbraco
|
||||
{
|
||||
renderFailed = true;
|
||||
Exceptions.Add(e);
|
||||
LogHelper.WarnWithException<macro>("Error loading userControl (" + Model.TypeName + ")", true, e);
|
||||
macroControl = new LiteralControl("Error loading userControl '" + Model.TypeName + "'");
|
||||
LogHelper.WarnWithException<macro>("Error loading userControl (" + Model.TypeName + ")", true, e);
|
||||
|
||||
// Invoke any error handlers for this macro
|
||||
var macroErrorEventArgs = new MacroErrorEventArgs {Name = Model.Name, Alias = Model.Alias, ItemKey = Model.TypeName, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour};
|
||||
|
||||
macroControl = GetControlForErrorBehavior("Error loading userControl '" + Model.TypeName + "'", macroErrorEventArgs);
|
||||
//if it is null, then we are supposed to throw the (original) exception
|
||||
// see: http://issues.umbraco.org/issue/U4-497 at the end
|
||||
if (macroControl == null)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case (int)MacroTypes.CustomControl:
|
||||
try
|
||||
{
|
||||
TraceInfo("umbracoMacro",
|
||||
"Custom control added (" + Model.TypeName + ")");
|
||||
TraceInfo("umbracoMacro",
|
||||
"ScriptAssembly (" + Model.TypeAssembly + ")");
|
||||
TraceInfo("umbracoMacro", "Custom control added (" + Model.TypeName + ")");
|
||||
TraceInfo("umbracoMacro", "ScriptAssembly (" + Model.TypeAssembly + ")");
|
||||
macroControl = loadControl(Model.TypeAssembly, ScriptType, Model, pageElements);
|
||||
break;
|
||||
}
|
||||
@@ -448,32 +453,52 @@ namespace umbraco
|
||||
renderFailed = true;
|
||||
Exceptions.Add(e);
|
||||
|
||||
LogHelper.WarnWithException<macro>("Error loading customControl (Assembly: " +
|
||||
Model.TypeAssembly +
|
||||
", Type: '" + Model.TypeName + "'", true, e);
|
||||
|
||||
macroControl =
|
||||
new LiteralControl("Error loading customControl (Assembly: " + Model.TypeAssembly +
|
||||
", Type: '" +
|
||||
Model.TypeName + "'");
|
||||
LogHelper.WarnWithException<macro>("Error loading customControl (Assembly: " + Model.TypeAssembly + ", Type: '" + Model.TypeName + "'", true, e);
|
||||
|
||||
// Invoke any error handlers for this macro
|
||||
var macroErrorEventArgs = new MacroErrorEventArgs {Name = Model.Name, Alias = Model.Alias, ItemKey = Model.TypeAssembly, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour};
|
||||
|
||||
macroControl = GetControlForErrorBehavior("Error loading customControl (Assembly: " + Model.TypeAssembly + ", Type: '" + Model.TypeName + "'", macroErrorEventArgs);
|
||||
//if it is null, then we are supposed to throw the (original) exception
|
||||
// see: http://issues.umbraco.org/issue/U4-497 at the end
|
||||
if (macroControl == null)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case (int)MacroTypes.XSLT:
|
||||
macroControl = loadMacroXSLT(this, Model, pageElements);
|
||||
macroControl = LoadMacroXslt(this, Model, pageElements, true);
|
||||
break;
|
||||
case (int)MacroTypes.Script:
|
||||
|
||||
//error handler for partial views, is an action because we need to re-use it twice below
|
||||
Func<Exception, Control> handleMacroScriptError = e =>
|
||||
{
|
||||
LogHelper.WarnWithException<macro>("Error loading MacroEngine script (file: " + ScriptFile + ", Type: '" + Model.TypeName + "'", true, e);
|
||||
|
||||
// Invoke any error handlers for this macro
|
||||
var macroErrorEventArgs = new MacroErrorEventArgs { Name = Model.Name, Alias = Model.Alias, ItemKey = ScriptFile, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour };
|
||||
|
||||
return GetControlForErrorBehavior("Error loading MacroEngine script (file: " + ScriptFile + ")", macroErrorEventArgs);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
TraceInfo("umbracoMacro",
|
||||
"MacroEngine script added (" + ScriptFile + ")");
|
||||
TraceInfo("umbracoMacro", "MacroEngine script added (" + ScriptFile + ")");
|
||||
ScriptingMacroResult result = loadMacroScript(Model);
|
||||
macroControl = new LiteralControl(result.Result);
|
||||
if (result.ResultException != null)
|
||||
{
|
||||
// we'll throw the error if we run in release mode, show details if we're in release mode!
|
||||
renderFailed = true;
|
||||
if (HttpContext.Current != null && !HttpContext.Current.IsDebuggingEnabled)
|
||||
Exceptions.Add(result.ResultException);
|
||||
macroControl = handleMacroScriptError(result.ResultException);
|
||||
//if it is null, then we are supposed to throw the exception
|
||||
if (macroControl == null)
|
||||
{
|
||||
throw result.ResultException;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -482,36 +507,19 @@ namespace umbraco
|
||||
renderFailed = true;
|
||||
Exceptions.Add(e);
|
||||
|
||||
LogHelper.WarnWithException<macro>(
|
||||
"Error loading MacroEngine script (file: " + ScriptFile + ", Type: '" + Model.TypeName + "'",
|
||||
true,
|
||||
e);
|
||||
|
||||
var result =
|
||||
new LiteralControl("Error loading MacroEngine script (file: " + ScriptFile + ")");
|
||||
|
||||
/*
|
||||
string args = "<ul>";
|
||||
foreach(object key in attributes.Keys)
|
||||
args += "<li><strong>" + key.ToString() + ": </strong> " + attributes[key] + "</li>";
|
||||
|
||||
foreach (object key in pageElements.Keys)
|
||||
args += "<li><strong>" + key.ToString() + ": </strong> " + pageElements[key] + "</li>";
|
||||
|
||||
args += "</ul>";
|
||||
|
||||
result.Text += args;
|
||||
*/
|
||||
|
||||
macroControl = result;
|
||||
macroControl = handleMacroScriptError(e);
|
||||
//if it is null, then we are supposed to throw the (original) exception
|
||||
// see: http://issues.umbraco.org/issue/U4-497 at the end
|
||||
if (macroControl == null)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (GlobalSettings.DebugMode)
|
||||
macroControl =
|
||||
new LiteralControl("<Macro: " + Name + " (" + ScriptAssembly + "," + ScriptType +
|
||||
")>");
|
||||
macroControl = new LiteralControl("<Macro: " + Name + " (" + ScriptAssembly + "," + ScriptType + ")>");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -601,6 +609,28 @@ namespace umbraco
|
||||
return macroControl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the error event and based on the error behavior either return a control to display or throw the exception
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
private Control GetControlForErrorBehavior(string msg, MacroErrorEventArgs args)
|
||||
{
|
||||
OnError(args);
|
||||
|
||||
switch (args.Behaviour)
|
||||
{
|
||||
case MacroErrorBehaviour.Inline:
|
||||
return new LiteralControl(msg);
|
||||
case MacroErrorBehaviour.Silent:
|
||||
return new LiteralControl("");
|
||||
case MacroErrorBehaviour.Throw:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check that the file has not recently changed
|
||||
/// </summary>
|
||||
@@ -735,30 +765,20 @@ namespace umbraco
|
||||
HttpRuntime.Cache.Remove("macroXslt_" + XsltFile);
|
||||
}
|
||||
|
||||
private Hashtable keysToLowerCase(Hashtable input)
|
||||
{
|
||||
var retval = new Hashtable();
|
||||
foreach (object key in input.Keys)
|
||||
retval.Add(key.ToString().ToLower(), input[key]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Control loadMacroXSLT(macro macro, MacroModel model, Hashtable pageElements)
|
||||
internal Control LoadMacroXslt(macro macro, MacroModel model, Hashtable pageElements, bool throwError)
|
||||
{
|
||||
if (XsltFile.Trim() != string.Empty)
|
||||
{
|
||||
// Get main XML
|
||||
XmlDocument umbracoXML = content.Instance.XmlContent;
|
||||
var umbracoXml = content.Instance.XmlContent;
|
||||
|
||||
// Create XML document for Macro
|
||||
var macroXML = new XmlDocument();
|
||||
macroXML.LoadXml("<macro/>");
|
||||
var macroXml = new XmlDocument();
|
||||
macroXml.LoadXml("<macro/>");
|
||||
|
||||
foreach (MacroPropertyModel prop in macro.Model.Properties)
|
||||
foreach (var prop in macro.Model.Properties)
|
||||
{
|
||||
addMacroXmlNode(umbracoXML, macroXML, prop.Key, prop.Type,
|
||||
prop.Value);
|
||||
addMacroXmlNode(umbracoXml, macroXml, prop.Key, prop.Type, prop.Value);
|
||||
}
|
||||
|
||||
if (HttpContext.Current.Request.QueryString["umbDebug"] != null && GlobalSettings.DebugMode)
|
||||
@@ -766,50 +786,70 @@ namespace umbraco
|
||||
return
|
||||
new LiteralControl("<div style=\"border: 2px solid green; padding: 5px;\"><b>Debug from " +
|
||||
macro.Name +
|
||||
"</b><br/><p>" + HttpContext.Current.Server.HtmlEncode(macroXML.OuterXml) +
|
||||
"</b><br/><p>" + HttpContext.Current.Server.HtmlEncode(macroXml.OuterXml) +
|
||||
"</p></div>");
|
||||
}
|
||||
else
|
||||
|
||||
try
|
||||
{
|
||||
var xsltFile = getXslt(XsltFile);
|
||||
|
||||
try
|
||||
{
|
||||
XslCompiledTransform xsltFile = getXslt(XsltFile);
|
||||
var result = CreateControlsFromText(GetXsltTransformResult(macroXml, xsltFile));
|
||||
|
||||
try
|
||||
{
|
||||
Control result = CreateControlsFromText(GetXsltTransformResult(macroXML, xsltFile));
|
||||
TraceInfo("umbracoMacro", "After performing transformation");
|
||||
|
||||
TraceInfo("umbracoMacro", "After performing transformation");
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Exceptions.Add(e);
|
||||
LogHelper.WarnWithException<macro>("Error parsing XSLT file", e);
|
||||
// inner exception code by Daniel Lindstr?m from SBBS.se
|
||||
Exception ie = e;
|
||||
while (ie != null)
|
||||
{
|
||||
TraceWarn("umbracoMacro InnerException", ie.Message, ie);
|
||||
ie = ie.InnerException;
|
||||
}
|
||||
return new LiteralControl("Error parsing XSLT file: \\xslt\\" + XsltFile);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Exceptions.Add(e);
|
||||
LogHelper.WarnWithException<macro>("Error loading XSLT " + Model.Xslt, true, e);
|
||||
return new LiteralControl("Error reading XSLT file: \\xslt\\" + XsltFile);
|
||||
LogHelper.WarnWithException<macro>("Error parsing XSLT file", e);
|
||||
// inner exception code by Daniel Lindstr?m from SBBS.se
|
||||
Exception ie = e;
|
||||
while (ie != null)
|
||||
{
|
||||
TraceWarn("umbracoMacro InnerException", ie.Message, ie);
|
||||
ie = ie.InnerException;
|
||||
}
|
||||
|
||||
var macroErrorEventArgs = new MacroErrorEventArgs { Name = Model.Name, Alias = Model.Alias, ItemKey = Model.Xslt, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour };
|
||||
var macroControl = GetControlForErrorBehavior("Error parsing XSLT file: \\xslt\\" + XsltFile, macroErrorEventArgs);
|
||||
//if it is null, then we are supposed to throw the (original) exception
|
||||
// see: http://issues.umbraco.org/issue/U4-497 at the end
|
||||
if (macroControl == null && throwError)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
return macroControl;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Exceptions.Add(e);
|
||||
LogHelper.WarnWithException<macro>("Error loading XSLT " + Model.Xslt, true, e);
|
||||
|
||||
// Invoke any error handlers for this macro
|
||||
var macroErrorEventArgs = new MacroErrorEventArgs { Name = Model.Name, Alias = Model.Alias, ItemKey = Model.Xslt, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour };
|
||||
var macroControl = GetControlForErrorBehavior("Error reading XSLT file: \\xslt\\" + XsltFile, macroErrorEventArgs);
|
||||
//if it is null, then we are supposed to throw the (original) exception
|
||||
// see: http://issues.umbraco.org/issue/U4-497 at the end
|
||||
if (macroControl == null && throwError)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
return macroControl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceWarn("macro", "Xslt is empty");
|
||||
return new LiteralControl(string.Empty);
|
||||
}
|
||||
|
||||
TraceWarn("macro", "Xslt is empty");
|
||||
return new LiteralControl(string.Empty);
|
||||
}
|
||||
|
||||
public Control loadMacroXSLT(macro macro, MacroModel model, Hashtable pageElements)
|
||||
{
|
||||
return LoadMacroXslt(macro, model, pageElements, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1505,7 +1545,7 @@ namespace umbraco
|
||||
}
|
||||
|
||||
if (!File.Exists(IOHelper.MapPath(userControlPath)))
|
||||
return new LiteralControl(string.Format("UserControl {0} does not exist.", fileName));
|
||||
throw new UmbracoException(string.Format("UserControl {0} does not exist.", fileName));
|
||||
|
||||
var oControl = (UserControl)new UserControl().LoadControl(userControlPath);
|
||||
|
||||
@@ -1531,11 +1571,7 @@ namespace umbraco
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.WarnWithException<macro>(string.Format("Error creating usercontrol ({0})", fileName), true, e);
|
||||
|
||||
return new LiteralControl(
|
||||
string.Format(
|
||||
"<div style=\"color: black; padding: 3px; border: 2px solid red\"><b style=\"color:red\">Error creating control ({0}).</b><br/> Maybe file doesn't exists or the usercontrol has a cache directive, which is not allowed! See the tracestack for more information!</div>",
|
||||
fileName));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1802,5 +1838,26 @@ namespace umbraco
|
||||
value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a macro error is raised.
|
||||
/// </summary>
|
||||
public static event EventHandler<MacroErrorEventArgs> Error;
|
||||
|
||||
/// <summary>
|
||||
/// Raises the <see cref="MacroErrorEventArgs"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="MacroErrorEventArgs"/> instance containing the event data.</param>
|
||||
protected void OnError(MacroErrorEventArgs e)
|
||||
{
|
||||
if (Error != null)
|
||||
{
|
||||
Error(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ namespace umbraco.controls
|
||||
// Iterate through the property types and add them to the tab
|
||||
// zb-00036 #29889 : fix property types getter to get the right set of properties
|
||||
// ge : had a bit of a corrupt db and got weird NRE errors so rewrote this to catch the error and rethrow with detail
|
||||
foreach (PropertyType propertyType in tab.GetPropertyTypes(_content.ContentType.Id))
|
||||
foreach (PropertyType propertyType in tab.GetPropertyTypes(_content.ContentType.Id).OrderBy(x=>x.SortOrder))
|
||||
{
|
||||
// table.Rows.Add(addControl(_content.getProperty(editPropertyType.Alias), tp));
|
||||
var property = _content.getProperty(propertyType);
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.IO;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.cms.businesslogic;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace umbraco.presentation.preview
|
||||
{
|
||||
@@ -38,12 +39,12 @@ namespace umbraco.presentation.preview
|
||||
|
||||
public PreviewContent(Guid previewSet)
|
||||
{
|
||||
ValidPreviewSet = updatePreviewPaths(previewSet, true);
|
||||
ValidPreviewSet = UpdatePreviewPaths(previewSet, true);
|
||||
}
|
||||
public PreviewContent(User user, Guid previewSet, bool validate)
|
||||
{
|
||||
_userId = user.Id;
|
||||
ValidPreviewSet = updatePreviewPaths(previewSet, validate);
|
||||
ValidPreviewSet = UpdatePreviewPaths(previewSet, validate);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +86,7 @@ namespace umbraco.presentation.preview
|
||||
|
||||
}
|
||||
|
||||
private bool updatePreviewPaths(Guid previewSet, bool validate)
|
||||
private bool UpdatePreviewPaths(Guid previewSet, bool validate)
|
||||
{
|
||||
if (_userId == -1)
|
||||
{
|
||||
@@ -112,8 +113,8 @@ namespace umbraco.presentation.preview
|
||||
|
||||
private static string GetPreviewsetPath(int userId, Guid previewSet)
|
||||
{
|
||||
return IO.IOHelper.MapPath(
|
||||
Path.Combine(IO.SystemDirectories.Preview, userId + "_" + previewSet + ".config"));
|
||||
return IOHelper.MapPath(
|
||||
Path.Combine(SystemDirectories.Preview, userId + "_" + previewSet + ".config"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,7 +140,7 @@ namespace umbraco.presentation.preview
|
||||
public void SavePreviewSet()
|
||||
{
|
||||
//make sure the preview folder exists first
|
||||
var dir = new DirectoryInfo(IO.IOHelper.MapPath(IO.SystemDirectories.Preview));
|
||||
var dir = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Preview));
|
||||
if (!dir.Exists)
|
||||
{
|
||||
dir.Create();
|
||||
@@ -155,17 +156,17 @@ namespace umbraco.presentation.preview
|
||||
{
|
||||
foreach (FileInfo file in dir.GetFiles(userId + "_*.config"))
|
||||
{
|
||||
deletePreviewFile(userId, file);
|
||||
DeletePreviewFile(userId, file);
|
||||
}
|
||||
// also delete any files accessed more than one hour ago
|
||||
foreach (FileInfo file in dir.GetFiles("*.config"))
|
||||
{
|
||||
if ((DateTime.Now - file.LastAccessTime).TotalMinutes > 1)
|
||||
deletePreviewFile(userId, file);
|
||||
DeletePreviewFile(userId, file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deletePreviewFile(int userId, FileInfo file)
|
||||
private static void DeletePreviewFile(int userId, FileInfo file)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -191,7 +192,7 @@ namespace umbraco.presentation.preview
|
||||
if (StateHelper.Cookies.Preview.HasValue)
|
||||
{
|
||||
|
||||
deletePreviewFile(
|
||||
DeletePreviewFile(
|
||||
UmbracoContext.Current.UmbracoUser.Id,
|
||||
new FileInfo(GetPreviewsetPath(
|
||||
UmbracoContext.Current.UmbracoUser.Id,
|
||||
|
||||
@@ -88,8 +88,12 @@ namespace umbraco.settings
|
||||
|
||||
private void save_click(object sender, System.Web.UI.ImageClickEventArgs e) {
|
||||
|
||||
foreach (TextBox t in languageFields) {
|
||||
if (t.Text != "") {
|
||||
foreach (TextBox t in languageFields)
|
||||
{
|
||||
//check for null but allow empty string!
|
||||
// http://issues.umbraco.org/issue/U4-1931
|
||||
if (t.Text != null)
|
||||
{
|
||||
currentItem.setValue(int.Parse(t.ID),t.Text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,6 +183,7 @@ namespace umbraco.presentation.templateControls
|
||||
System.Web.HttpContext.Current.Trace.Warn("Template", "Result of macro " + tempMacro.Name + " is null");
|
||||
} catch (Exception ee) {
|
||||
System.Web.HttpContext.Current.Trace.Warn("Template", "Error adding macro " + tempMacro.Name, ee);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using umbraco.interfaces;
|
||||
using umbraco.NodeFactory;
|
||||
|
||||
namespace umbraco
|
||||
@@ -20,6 +21,17 @@ namespace umbraco
|
||||
/// <param name="level">The level.</param>
|
||||
/// <returns>Returns an ancestor node by path level.</returns>
|
||||
public static Node GetAncestorByPathLevel(this Node node, int level)
|
||||
{
|
||||
return (Node) GetAncestorByPathLevel((INode) node, level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ancestor by path level.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
/// <param name="level">The level.</param>
|
||||
/// <returns>Returns an ancestor node by path level.</returns>
|
||||
public static INode GetAncestorByPathLevel(this INode node, int level)
|
||||
{
|
||||
var nodeId = uQuery.GetNodeIdByPathLevel(node.Path, level);
|
||||
return uQuery.GetNode(nodeId);
|
||||
@@ -32,12 +44,23 @@ namespace umbraco
|
||||
/// <param name="node">an umbraco.presentation.nodeFactory.Node object</param>
|
||||
/// <returns>Node as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetAncestorNodes(this Node node)
|
||||
{
|
||||
return GetAncestorNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'ancestor'
|
||||
/// Get the Ancestor Nodes from current to root (useful for breadcrumbs)
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetAncestorNodes(this INode node)
|
||||
{
|
||||
var ancestor = node.Parent;
|
||||
|
||||
while (ancestor != null)
|
||||
{
|
||||
yield return ancestor as Node;
|
||||
yield return ancestor as INode;
|
||||
|
||||
ancestor = ancestor.Parent;
|
||||
}
|
||||
@@ -49,6 +72,16 @@ namespace umbraco
|
||||
/// <param name="node">an umbraco.presentation.nodeFactory.Node object</param>
|
||||
/// <returns>Node as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetAncestorOrSelfNodes(this Node node)
|
||||
{
|
||||
return GetAncestorOrSelfNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'ancestor-or-self'
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetAncestorOrSelfNodes(this INode node)
|
||||
{
|
||||
yield return node;
|
||||
|
||||
@@ -64,10 +97,20 @@ namespace umbraco
|
||||
/// <param name="node">an umbraco.presentation.nodeFactory.Node object</param>
|
||||
/// <returns>Node as IEumerable</returns>
|
||||
public static IEnumerable<Node> GetPrecedingSiblingNodes(this Node node)
|
||||
{
|
||||
return GetPrecedingSiblingNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'preceding-sibling'
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEumerable</returns>
|
||||
public static IEnumerable<INode> GetPrecedingSiblingNodes(this INode node)
|
||||
{
|
||||
if (node.Parent != null)
|
||||
{
|
||||
var parentNode = node.Parent as Node;
|
||||
var parentNode = node.Parent as INode;
|
||||
foreach (var precedingSiblingNode in parentNode.GetChildNodes().Where(childNode => childNode.SortOrder < node.SortOrder))
|
||||
{
|
||||
yield return precedingSiblingNode;
|
||||
@@ -81,10 +124,20 @@ namespace umbraco
|
||||
/// <param name="node">an umbraco.presentation.nodeFactory.Node object</param>
|
||||
/// <returns>Node as IEumerable</returns>
|
||||
public static IEnumerable<Node> GetFollowingSiblingNodes(this Node node)
|
||||
{
|
||||
return GetFollowingSiblingNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'following-sibling'
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEumerable</returns>
|
||||
public static IEnumerable<INode> GetFollowingSiblingNodes(this INode node)
|
||||
{
|
||||
if (node.Parent != null)
|
||||
{
|
||||
var parentNode = node.Parent as Node;
|
||||
var parentNode = node.Parent as INode;
|
||||
foreach (var followingSiblingNode in parentNode.GetChildNodes().Where(childNode => childNode.SortOrder > node.SortOrder))
|
||||
{
|
||||
yield return followingSiblingNode;
|
||||
@@ -98,10 +151,20 @@ namespace umbraco
|
||||
/// <param name="node">an umbraco.presentation.nodeFactory.Node object</param>
|
||||
/// <returns>Node as IEumerable</returns>
|
||||
public static IEnumerable<Node> GetSiblingNodes(this Node node)
|
||||
{
|
||||
return GetSiblingNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all sibling Nodes
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEumerable</returns>
|
||||
public static IEnumerable<INode> GetSiblingNodes(this INode node)
|
||||
{
|
||||
if (node.Parent != null)
|
||||
{
|
||||
var parentNode = node.Parent as Node;
|
||||
var parentNode = node.Parent as INode;
|
||||
foreach (var siblingNode in parentNode.GetChildNodes().Where(childNode => childNode.Id != node.Id))
|
||||
{
|
||||
yield return siblingNode;
|
||||
@@ -115,6 +178,16 @@ namespace umbraco
|
||||
/// <param name="node">an umbraco.presentation.nodeFactory.Node object</param>
|
||||
/// <returns>Node as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetDescendantOrSelfNodes(this Node node)
|
||||
{
|
||||
return GetDescendantOrSelfNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'descendant-or-self'
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetDescendantOrSelfNodes(this INode node)
|
||||
{
|
||||
yield return node;
|
||||
|
||||
@@ -133,7 +206,19 @@ namespace umbraco
|
||||
/// <returns>Node as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetDescendantNodes(this Node node)
|
||||
{
|
||||
foreach (Node child in node.Children)
|
||||
return GetDescendantNodes((INode) node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'descendant'
|
||||
/// Make the All Descendants LINQ queryable
|
||||
/// taken from: http://our.umbraco.org/wiki/how-tos/useful-helper-extension-methods-%28linq-null-safe-access%29
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetDescendantNodes(this INode node)
|
||||
{
|
||||
foreach (INode child in node.ChildrenAsList)
|
||||
{
|
||||
yield return child;
|
||||
|
||||
@@ -153,7 +238,19 @@ namespace umbraco
|
||||
/// <returns>Nodes as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetDescendantNodes(this Node node, Func<Node, bool> func)
|
||||
{
|
||||
foreach (Node child in node.Children)
|
||||
return GetDescendantNodes((INode)node, (Func<INode, bool>) func).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drills down into the descendant nodes returning those where Func is true, when Func is false further descendants are not checked
|
||||
/// taken from: http://ucomponents.codeplex.com/discussions/246406
|
||||
/// </summary>
|
||||
/// <param name="node">The <c>umbraco.interfaces.INode</c>.</param>
|
||||
/// <param name="func">The func</param>
|
||||
/// <returns>INode as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetDescendantNodes(this INode node, Func<INode, bool> func)
|
||||
{
|
||||
foreach (INode child in node.ChildrenAsList)
|
||||
{
|
||||
if (func(child))
|
||||
{
|
||||
@@ -175,6 +272,18 @@ namespace umbraco
|
||||
/// <param name="documentTypeAlias">The document type alias.</param>
|
||||
/// <returns>Nodes as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetDescendantNodesByType(this Node node, string documentTypeAlias)
|
||||
{
|
||||
return GetDescendantNodesByType((INode)node, documentTypeAlias).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the descendant nodes by document-type.
|
||||
/// Get all descendants, and then return only those that match the requested typeAlias
|
||||
/// </summary>
|
||||
/// <param name="node">The <c>umbraco.interfaces.INode</c>.</param>
|
||||
/// <param name="documentTypeAlias">The document type alias.</param>
|
||||
/// <returns>INodes as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetDescendantNodesByType(this INode node, string documentTypeAlias)
|
||||
{
|
||||
return node.GetDescendantNodes().Where(x => x.NodeTypeAlias == documentTypeAlias);
|
||||
}
|
||||
@@ -189,10 +298,20 @@ namespace umbraco
|
||||
/// <returns>Node as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetChildNodes(this Node node)
|
||||
{
|
||||
foreach (Node child in node.Children)
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
return GetChildNodes((INode)node).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Functionally similar to the XPath axis 'child'
|
||||
/// Make the imediate Children LINQ queryable
|
||||
/// Performance optimised for just imediate children.
|
||||
/// taken from: http://our.umbraco.org/wiki/how-tos/useful-helper-extension-methods-%28linq-null-safe-access%29
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <returns>INode as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetChildNodes(this INode node)
|
||||
{
|
||||
return node.ChildrenAsList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -203,7 +322,18 @@ namespace umbraco
|
||||
/// <returns>Nodes as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetChildNodes(this Node node, Func<Node, bool> func)
|
||||
{
|
||||
foreach (Node child in node.Children)
|
||||
return GetChildNodes((INode)node, (Func<INode, bool>) func).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child nodes.
|
||||
/// </summary>
|
||||
/// <param name="node">The <c>umbraco.interfaces.INode</c>.</param>
|
||||
/// <param name="func">The func.</param>
|
||||
/// <returns>INodes as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetChildNodes(this INode node, Func<INode, bool> func)
|
||||
{
|
||||
foreach (INode child in node.ChildrenAsList)
|
||||
{
|
||||
if (func(child))
|
||||
{
|
||||
@@ -219,6 +349,17 @@ namespace umbraco
|
||||
/// <param name="documentTypeAlias">The document type alias.</param>
|
||||
/// <returns>Nodes as IEnumerable</returns>
|
||||
public static IEnumerable<Node> GetChildNodesByType(this Node node, string documentTypeAlias)
|
||||
{
|
||||
return GetChildNodesByType((INode)node, documentTypeAlias).Cast<Node>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the child nodes by document-type.
|
||||
/// </summary>
|
||||
/// <param name="node">The <c>umbraco.interfaces.INode</c>.</param>
|
||||
/// <param name="documentTypeAlias">The document type alias.</param>
|
||||
/// <returns>INodes as IEnumerable</returns>
|
||||
public static IEnumerable<INode> GetChildNodesByType(this INode node, string documentTypeAlias)
|
||||
{
|
||||
return node.GetChildNodes(n => n.NodeTypeAlias == documentTypeAlias);
|
||||
}
|
||||
@@ -231,20 +372,18 @@ namespace umbraco
|
||||
/// <returns>null or Node</returns>
|
||||
public static Node GetChildNodeByName(this Node parentNode, string nodeName)
|
||||
{
|
||||
Node node = null;
|
||||
return (Node) GetChildNodeByName((INode)parentNode, nodeName);
|
||||
}
|
||||
|
||||
foreach (Node child in parentNode.Children)
|
||||
{
|
||||
if (child.Name == nodeName)
|
||||
{
|
||||
node = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
//// return node.GetChildNodes(n => n.Name == nodeName);
|
||||
/// <summary>
|
||||
/// Extension method on Node to retun a matching child node by name
|
||||
/// </summary>
|
||||
/// <param name="parentNode">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="nodeName">name of node to search for</param>
|
||||
/// <returns>null or INode</returns>
|
||||
public static INode GetChildNodeByName(this INode parentNode, string nodeName)
|
||||
{
|
||||
return parentNode.ChildrenAsList.FirstOrDefault(child => child.Name == nodeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -256,6 +395,19 @@ namespace umbraco
|
||||
/// <c>true</c> if the specified node has property; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool HasProperty(this Node node, string propertyAlias)
|
||||
{
|
||||
return HasProperty((INode)node, propertyAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified node has property.
|
||||
/// </summary>
|
||||
/// <param name="node">The INode.</param>
|
||||
/// <param name="propertyAlias">The property alias.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified node has property; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool HasProperty(this INode node, string propertyAlias)
|
||||
{
|
||||
var property = node.GetProperty(propertyAlias);
|
||||
return (property != null);
|
||||
@@ -269,6 +421,18 @@ namespace umbraco
|
||||
/// <param name="propertyAlias">alias of property to get</param>
|
||||
/// <returns>default(T) or property cast to (T)</returns>
|
||||
public static T GetProperty<T>(this Node node, string propertyAlias)
|
||||
{
|
||||
return GetProperty<T>((INode)node, propertyAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a value of type T from a property
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type T to cast to</typeparam>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="propertyAlias">alias of property to get</param>
|
||||
/// <returns>default(T) or property cast to (T)</returns>
|
||||
public static T GetProperty<T>(this INode node, string propertyAlias)
|
||||
{
|
||||
// check to see if return object handles it's own object hydration
|
||||
if (typeof(uQuery.IGetProperty).IsAssignableFrom(typeof(T)))
|
||||
@@ -282,6 +446,8 @@ namespace umbraco
|
||||
return (T)t;
|
||||
}
|
||||
|
||||
//TODO: This should be converted to our extension method TryConvertTo<T> ....
|
||||
|
||||
var typeConverter = TypeDescriptor.GetConverter(typeof(T));
|
||||
if (typeConverter != null)
|
||||
{
|
||||
@@ -343,6 +509,17 @@ namespace umbraco
|
||||
/// <param name="propertyAlias">alias of propety to get</param>
|
||||
/// <returns>empty string, or property value as string</returns>
|
||||
private static string GetPropertyAsString(this Node node, string propertyAlias)
|
||||
{
|
||||
return GetPropertyAsString((INode)node, propertyAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string value for the supplied property alias
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="propertyAlias">alias of propety to get</param>
|
||||
/// <returns>empty string, or property value as string</returns>
|
||||
private static string GetPropertyAsString(this INode node, string propertyAlias)
|
||||
{
|
||||
var propertyValue = string.Empty;
|
||||
var property = node.GetProperty(propertyAlias);
|
||||
@@ -362,6 +539,17 @@ namespace umbraco
|
||||
/// <param name="propertyAlias">alias of propety to get</param>
|
||||
/// <returns>true if can cast value, else false for all other circumstances</returns>
|
||||
private static bool GetPropertyAsBoolean(this Node node, string propertyAlias)
|
||||
{
|
||||
return GetPropertyAsBoolean((INode)node, propertyAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a boolean value for the supplied property alias (works with built in Yes/No dataype)
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="propertyAlias">alias of propety to get</param>
|
||||
/// <returns>true if can cast value, else false for all other circumstances</returns>
|
||||
private static bool GetPropertyAsBoolean(this INode node, string propertyAlias)
|
||||
{
|
||||
var propertyValue = false;
|
||||
var property = node.GetProperty(propertyAlias);
|
||||
@@ -390,6 +578,18 @@ namespace umbraco
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
public static int Level(this Node node)
|
||||
{
|
||||
return Level((INode)node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell me the level of this node (0 = root)
|
||||
/// updated from Depth and changed to start at 0
|
||||
/// to align with other 'Level' methods (eg xslt)
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
public static int Level(this INode node)
|
||||
{
|
||||
return node.Path.Split(',').Length - 1;
|
||||
}
|
||||
@@ -400,6 +600,16 @@ namespace umbraco
|
||||
/// <param name="node">The node.</param>
|
||||
/// <returns>Returns an <c>XmlNode</c> for the selected Node</returns>
|
||||
public static XmlNode ToXml(this Node node)
|
||||
{
|
||||
return ToXml((INode) node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XML for the Node.
|
||||
/// </summary>
|
||||
/// <param name="node">The INode.</param>
|
||||
/// <returns>Returns an <c>XmlNode</c> for the selected Node</returns>
|
||||
public static XmlNode ToXml(this INode node)
|
||||
{
|
||||
return ((IHasXmlNode)umbraco.library.GetXmlNodeById(node.Id.ToString()).Current).GetNode();
|
||||
}
|
||||
@@ -412,6 +622,18 @@ namespace umbraco
|
||||
/// <param name="cropName">name of crop to get url for</param>
|
||||
/// <returns>emtpy string or url</returns>
|
||||
public static string GetImageCropperUrl(this Node node, string propertyAlias, string cropName)
|
||||
{
|
||||
return GetImageCropperUrl((INode)node, propertyAlias, cropName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the url for a given crop name using the built in Image Cropper datatype
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="propertyAlias">property alias</param>
|
||||
/// <param name="cropName">name of crop to get url for</param>
|
||||
/// <returns>emtpy string or url</returns>
|
||||
public static string GetImageCropperUrl(this INode node, string propertyAlias, string cropName)
|
||||
{
|
||||
string cropUrl = string.Empty;
|
||||
|
||||
@@ -453,6 +675,18 @@ namespace umbraco
|
||||
/// <param name="value">value to set</param>
|
||||
/// <returns>the same node object on which this is an extension method</returns>
|
||||
public static Node SetProperty(this Node node, string propertyAlias, object value)
|
||||
{
|
||||
return (Node) SetProperty((INode)node, propertyAlias, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a property value on this node
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="propertyAlias">alias of property to set</param>
|
||||
/// <param name="value">value to set</param>
|
||||
/// <returns>the same node object on which this is an extension method</returns>
|
||||
public static INode SetProperty(this INode node, string propertyAlias, object value)
|
||||
{
|
||||
var document = new Document(node.Id);
|
||||
|
||||
@@ -468,6 +702,17 @@ namespace umbraco
|
||||
/// <param name="useAdminUser">if true then publishes under the context of User(0), if false uses current user</param>
|
||||
/// <returns>the same node object on which this is an extension method</returns>
|
||||
public static Node Publish(this Node node, bool useAdminUser)
|
||||
{
|
||||
return (Node) Publish((INode)node, useAdminUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Republishes this node
|
||||
/// </summary>
|
||||
/// <param name="node">an umbraco.interfaces.INode object</param>
|
||||
/// <param name="useAdminUser">if true then publishes under the context of User(0), if false uses current user</param>
|
||||
/// <returns>the same node object on which this is an extension method</returns>
|
||||
public static INode Publish(this INode node, bool useAdminUser)
|
||||
{
|
||||
var document = new Document(node.Id);
|
||||
|
||||
@@ -484,6 +729,18 @@ namespace umbraco
|
||||
/// Returns a random node from a collection of nodes.
|
||||
/// </returns>
|
||||
public static Node GetRandom(this IList<Node> nodes)
|
||||
{
|
||||
return (Node)GetRandom(nodes.Cast<INode>().ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random node.
|
||||
/// </summary>
|
||||
/// <param name="nodes">The nodes.</param>
|
||||
/// <returns>
|
||||
/// Returns a random INode from a collection of INodes.
|
||||
/// </returns>
|
||||
public static INode GetRandom(this IList<INode> nodes)
|
||||
{
|
||||
var random = umbraco.library.GetRandom();
|
||||
return nodes[random.Next(0, nodes.Count)];
|
||||
@@ -499,7 +756,21 @@ namespace umbraco
|
||||
/// </returns>
|
||||
public static IEnumerable<Node> GetRandom(this IList<Node> nodes, int numberOfItems)
|
||||
{
|
||||
var output = new List<Node>(numberOfItems);
|
||||
var randomNodes = GetRandom(nodes.Cast<INode>().ToList(), numberOfItems);
|
||||
return randomNodes.Cast<Node>().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of random nodes.
|
||||
/// </summary>
|
||||
/// <param name="nodes">The nodes.</param>
|
||||
/// <param name="numberOfItems">The number of items.</param>
|
||||
/// <returns>
|
||||
/// Returns the specified number of random INodes from a collection of INodes.
|
||||
/// </returns>
|
||||
public static IEnumerable<INode> GetRandom(this IList<INode> nodes, int numberOfItems)
|
||||
{
|
||||
var output = new List<INode>(numberOfItems);
|
||||
|
||||
while (output.Count < numberOfItems)
|
||||
{
|
||||
@@ -519,6 +790,16 @@ namespace umbraco
|
||||
/// <param name="node">The node.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this Node node)
|
||||
{
|
||||
return GetFullNiceUrl((INode)node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full nice URL.
|
||||
/// </summary>
|
||||
/// <param name="node">The node as INode.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this INode node)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(node.NiceUrl) && node.NiceUrl[0] == '/')
|
||||
{
|
||||
@@ -535,6 +816,17 @@ namespace umbraco
|
||||
/// <param name="language">The language.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this Node node, string language)
|
||||
{
|
||||
return GetFullNiceUrl((INode)node, language);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full nice URL.
|
||||
/// </summary>
|
||||
/// <param name="node">The node as INode.</param>
|
||||
/// <param name="language">The language.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this INode node, string language)
|
||||
{
|
||||
return node.GetFullNiceUrl(language, false);
|
||||
}
|
||||
@@ -547,6 +839,18 @@ namespace umbraco
|
||||
/// <param name="ssl">if set to <c>true</c> [SSL].</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this Node node, string language, bool ssl)
|
||||
{
|
||||
return GetFullNiceUrl((INode)node, language, ssl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full nice URL.
|
||||
/// </summary>
|
||||
/// <param name="node">The node as INode.</param>
|
||||
/// <param name="language">The language.</param>
|
||||
/// <param name="ssl">if set to <c>true</c> [SSL].</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this INode node, string language, bool ssl)
|
||||
{
|
||||
foreach (var domain in library.GetCurrentDomains(node.Id))
|
||||
{
|
||||
@@ -566,6 +870,17 @@ namespace umbraco
|
||||
/// <param name="domain">The domain.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this Node node, Domain domain)
|
||||
{
|
||||
return GetFullNiceUrl((INode)node, domain);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full nice URL.
|
||||
/// </summary>
|
||||
/// <param name="node">The node as INode.</param>
|
||||
/// <param name="domain">The domain.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this INode node, Domain domain)
|
||||
{
|
||||
return node.GetFullNiceUrl(domain, false);
|
||||
}
|
||||
@@ -578,6 +893,18 @@ namespace umbraco
|
||||
/// <param name="ssl">if set to <c>true</c> [SSL].</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this Node node, Domain domain, bool ssl)
|
||||
{
|
||||
return GetFullNiceUrl((INode)node, domain, ssl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full nice URL.
|
||||
/// </summary>
|
||||
/// <param name="node">The node as INode.</param>
|
||||
/// <param name="domain">The domain.</param>
|
||||
/// <param name="ssl">if set to <c>true</c> [SSL].</param>
|
||||
/// <returns></returns>
|
||||
public static string GetFullNiceUrl(this INode node, Domain domain, bool ssl)
|
||||
{
|
||||
// TODO: [OA] Document on Codeplex
|
||||
if (!string.IsNullOrEmpty(node.NiceUrl) && node.NiceUrl[0] == '/')
|
||||
@@ -590,6 +917,7 @@ namespace umbraco
|
||||
|
||||
|
||||
#pragma warning disable 0618
|
||||
|
||||
/// <summary>
|
||||
/// Converts legacy nodeFactory collection to INode collection
|
||||
/// </summary>
|
||||
|
||||
@@ -171,11 +171,7 @@ namespace umbraco.MacroEngines
|
||||
Success = false;
|
||||
ResultException = exception;
|
||||
HttpContext.Current.Trace.Warn("umbracoMacro", string.Format("Error Loading Razor Script (file: {0}) {1} {2}", macro.Name, exception.Message, exception.StackTrace));
|
||||
var loading = string.Format("<div style=\"border: 1px solid #990000\">Error loading Razor Script {0}</br/>", macro.ScriptName);
|
||||
if (GlobalSettings.DebugMode)
|
||||
loading = loading + exception.Message;
|
||||
loading = loading + "</div>";
|
||||
return loading;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace umbraco.MacroEngines
|
||||
|
||||
private DynamicNodeList _cachedChildren;
|
||||
private readonly ConcurrentDictionary<string, object> _cachedMemberOutput = new ConcurrentDictionary<string, object>();
|
||||
private readonly ConcurrentDictionary<string, PropertyResult> _cachedProperties = new ConcurrentDictionary<string, PropertyResult>();
|
||||
|
||||
internal readonly DynamicBackingItem n;
|
||||
|
||||
@@ -461,12 +462,82 @@ namespace umbraco.MacroEngines
|
||||
return GetDataTypeCallback(docTypeAlias, propertyAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value from the property result and ensure it is filtered through the razor data type converters
|
||||
/// </summary>
|
||||
/// <param name="propResult"></param>
|
||||
/// <param name="result">The value result for the property</param>
|
||||
/// <returns>true if getting the property data was successful</returns>
|
||||
private bool TryGetPropertyData(PropertyResult propResult, out object result)
|
||||
{
|
||||
if (propResult == null) throw new ArgumentNullException("propResult");
|
||||
//special casing for true/false properties
|
||||
//int/decimal are handled by ConvertPropertyValueByDataType
|
||||
//fallback is stringT
|
||||
if (n.NodeTypeAlias == null && propResult.Alias == null)
|
||||
{
|
||||
throw new ArgumentNullException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch.");
|
||||
}
|
||||
|
||||
//contextAlias is the node which the property data was returned from
|
||||
//Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias);
|
||||
var dataType = GetDataType(propResult.ContextAlias, propResult.Alias);
|
||||
|
||||
var staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping
|
||||
.FirstOrDefault(mapping => mapping.Applies(dataType, propResult.ContextAlias, propResult.Alias));
|
||||
|
||||
if (staticMapping != null)
|
||||
{
|
||||
var dataTypeType = Type.GetType(staticMapping.TypeName);
|
||||
if (dataTypeType != null)
|
||||
{
|
||||
object valueOutput = null;
|
||||
if (TryCreateInstanceRazorDataTypeModel(dataType, dataTypeType, propResult.Value, out valueOutput))
|
||||
{
|
||||
result = valueOutput;
|
||||
return true;
|
||||
}
|
||||
LogHelper.Warn<DynamicNode>(string.Format("Failed to create the instance of the model binder"));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<DynamicNode>(string.Format("staticMapping type name {0} came back as null from Type.GetType; check the casing, assembly presence, assembly framework version, namespace", staticMapping.TypeName));
|
||||
}
|
||||
}
|
||||
|
||||
if (RazorDataTypeModelTypes != null && RazorDataTypeModelTypes.Any(model => model.Key.Item1 == dataType) && dataType != Guid.Empty)
|
||||
{
|
||||
var razorDataTypeModelDefinition = RazorDataTypeModelTypes.Where(model => model.Key.Item1 == dataType).OrderByDescending(model => model.Key.Item2).FirstOrDefault();
|
||||
if (!(razorDataTypeModelDefinition.Equals(default(KeyValuePair<System.Tuple<Guid, int>, Type>))))
|
||||
{
|
||||
Type dataTypeType = razorDataTypeModelDefinition.Value;
|
||||
object valueResult = null;
|
||||
if (TryCreateInstanceRazorDataTypeModel(dataType, dataTypeType, propResult.Value, out valueResult))
|
||||
{
|
||||
result = valueResult;
|
||||
return true;
|
||||
}
|
||||
LogHelper.Warn<DynamicNode>(string.Format("Failed to create the instance of the model binder"));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<DynamicNode>(string.Format("Could not get the dataTypeType for the RazorDataTypeModel"));
|
||||
}
|
||||
}
|
||||
|
||||
result = propResult.Value;
|
||||
//convert the string value to a known type
|
||||
var returnVal = ConvertPropertyValueByDataType(ref result, propResult.Alias, dataType);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
var name = binder.Name;
|
||||
|
||||
//check the cache first!
|
||||
if (_cachedMemberOutput.TryGetValue(name, out result))
|
||||
if (_cachedMemberOutput.TryGetValue(binder.Name, out result))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -477,7 +548,7 @@ namespace umbraco.MacroEngines
|
||||
{
|
||||
result = GetChildrenAsList;
|
||||
//cache the result so we don't have to re-process the whole thing
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
_cachedMemberOutput.TryAdd(binder.Name, result);
|
||||
return true;
|
||||
}
|
||||
if (binder.Name.InvariantEquals("parentId"))
|
||||
@@ -488,7 +559,7 @@ namespace umbraco.MacroEngines
|
||||
throw new InvalidOperationException(string.Format("The node {0} does not have a parent", Id));
|
||||
}
|
||||
result = parent.Id;
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
_cachedMemberOutput.TryAdd(binder.Name, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -501,87 +572,26 @@ namespace umbraco.MacroEngines
|
||||
name = name.Substring(1, name.Length - 1);
|
||||
recursive = true;
|
||||
}
|
||||
var data = n.GetProperty(name, recursive, out propertyExists);
|
||||
// check for nicer support of Pascal Casing EVEN if alias is camelCasing:
|
||||
if (data == null && name.Substring(0, 1).ToUpper() == name.Substring(0, 1) && !propertyExists)
|
||||
|
||||
PropertyResult prop;
|
||||
if (!_cachedProperties.TryGetValue(binder.Name, out prop))
|
||||
{
|
||||
data = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1)), recursive, out propertyExists);
|
||||
prop = n.GetProperty(name, recursive, out propertyExists);
|
||||
// check for nicer support of Pascal Casing EVEN if alias is camelCasing:
|
||||
if (prop == null && name.Substring(0, 1).ToUpper() == name.Substring(0, 1) && !propertyExists)
|
||||
{
|
||||
prop = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1)), recursive, out propertyExists);
|
||||
}
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
if (prop != null)
|
||||
{
|
||||
result = data.Value;
|
||||
//special casing for true/false properties
|
||||
//int/decimal are handled by ConvertPropertyValueByDataType
|
||||
//fallback is stringT
|
||||
if (n.NodeTypeAlias == null && data.Alias == null)
|
||||
if (TryGetPropertyData(prop, out result))
|
||||
{
|
||||
throw new ArgumentNullException("No node alias or property alias available. Unable to look up the datatype of the property you are trying to fetch.");
|
||||
//cache the result so we don't have to re-process the whole thing
|
||||
_cachedMemberOutput.TryAdd(binder.Name, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
//contextAlias is the node which the property data was returned from
|
||||
//Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias);
|
||||
var dataType = GetDataType(data.ContextAlias, data.Alias);
|
||||
|
||||
var staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping.FirstOrDefault(mapping =>
|
||||
{
|
||||
return mapping.Applies(dataType, data.ContextAlias, data.Alias);
|
||||
});
|
||||
if (staticMapping != null)
|
||||
{
|
||||
|
||||
Type dataTypeType = Type.GetType(staticMapping.TypeName);
|
||||
if (dataTypeType != null)
|
||||
{
|
||||
object instance = null;
|
||||
if (TryCreateInstanceRazorDataTypeModel(dataType, dataTypeType, data.Value, out instance))
|
||||
{
|
||||
result = instance;
|
||||
//cache the result so we don't have to re-process the whole thing
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<DynamicNode>(string.Format("Failed to create the instance of the model binder"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<DynamicNode>(string.Format("staticMapping type name {0} came back as null from Type.GetType; check the casing, assembly presence, assembly framework version, namespace", staticMapping.TypeName));
|
||||
}
|
||||
}
|
||||
|
||||
if (RazorDataTypeModelTypes != null && RazorDataTypeModelTypes.Any(model => model.Key.Item1 == dataType) && dataType != Guid.Empty)
|
||||
{
|
||||
var razorDataTypeModelDefinition = RazorDataTypeModelTypes.Where(model => model.Key.Item1 == dataType).OrderByDescending(model => model.Key.Item2).FirstOrDefault();
|
||||
if (!(razorDataTypeModelDefinition.Equals(default(KeyValuePair<System.Tuple<Guid, int>, Type>))))
|
||||
{
|
||||
Type dataTypeType = razorDataTypeModelDefinition.Value;
|
||||
object instance = null;
|
||||
if (TryCreateInstanceRazorDataTypeModel(dataType, dataTypeType, data.Value, out instance))
|
||||
{
|
||||
result = instance;
|
||||
//cache the result so we don't have to re-process the whole thing
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<DynamicNode>(string.Format("Failed to create the instance of the model binder"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.Warn<DynamicNode>(string.Format("Could not get the dataTypeType for the RazorDataTypeModel"));
|
||||
}
|
||||
}
|
||||
|
||||
//convert the string value to a known type
|
||||
var returnVal = ConvertPropertyValueByDataType(ref result, name, dataType);
|
||||
//cache the result so we don't have to re-process the whole thing
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
//check if the alias is that of a child type
|
||||
@@ -597,7 +607,7 @@ namespace umbraco.MacroEngines
|
||||
{
|
||||
result = new DynamicNodeList(filteredTypeChildren);
|
||||
//cache the result so we don't have to re-process the whole thing
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
_cachedMemberOutput.TryAdd(binder.Name, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -609,7 +619,7 @@ namespace umbraco.MacroEngines
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
_cachedMemberOutput.TryAdd(name, result);
|
||||
_cachedMemberOutput.TryAdd(binder.Name, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1332,14 +1342,12 @@ namespace umbraco.MacroEngines
|
||||
{
|
||||
if (n == null) return null;
|
||||
|
||||
object result;
|
||||
IProperty prop;
|
||||
PropertyResult prop;
|
||||
|
||||
//check the cache first!
|
||||
if (_cachedMemberOutput.TryGetValue(alias, out result))
|
||||
{
|
||||
prop = result as IProperty;
|
||||
if (prop != null)
|
||||
return prop;
|
||||
if (_cachedProperties.TryGetValue(alias, out prop))
|
||||
{
|
||||
return prop;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -1355,6 +1363,12 @@ namespace umbraco.MacroEngines
|
||||
prop = n.GetProperty(alias);
|
||||
}
|
||||
}
|
||||
if (prop == null && alias.StartsWith("_"))
|
||||
{
|
||||
//if the prop is still null but it starts with an _ then we'll check recursively
|
||||
var recursiveAlias = alias.Substring(1, alias.Length - 1);
|
||||
prop = n.GetProperty(recursiveAlias, true);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -1365,7 +1379,7 @@ namespace umbraco.MacroEngines
|
||||
return null;
|
||||
|
||||
//cache it!
|
||||
_cachedMemberOutput.TryAdd(alias, prop);
|
||||
_cachedProperties.TryAdd(alias, prop);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using System.Xml;
|
||||
using umbraco.BusinessLogic;
|
||||
using Umbraco.Core;
|
||||
using System.Collections.Generic;
|
||||
using umbraco.MacroEngines;
|
||||
|
||||
@@ -554,6 +550,18 @@ namespace umbraco
|
||||
get { return Umbraco.Core.Configuration.UmbracoSettings.ResolveUrlsFromTextString; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This configuration setting defines how to handle macro errors:
|
||||
/// - Inline - Show error within macro as text (default and current Umbraco 'normal' behavior)
|
||||
/// - Silent - Suppress error and hide macro
|
||||
/// - Throw - Throw an exception and invoke the global error handler (if one is defined, if not you'll get a YSOD)
|
||||
/// </summary>
|
||||
/// <value>MacroErrorBehaviour enum defining how to handle macro errors.</value>
|
||||
public static MacroErrorBehaviour MacroErrorBehaviour
|
||||
{
|
||||
get { return Umbraco.Core.Configuration.UmbracoSettings.MacroErrorBehaviour; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration regarding webservices
|
||||
/// </summary>
|
||||
|
||||
@@ -144,7 +144,8 @@ namespace umbraco.cms.businesslogic
|
||||
}
|
||||
foreach (Tuple<string, string> key in toDelete)
|
||||
{
|
||||
_propertyTypeCache.Remove(key);
|
||||
if(_propertyTypeCache != null)
|
||||
_propertyTypeCache.Remove(key);
|
||||
}
|
||||
}
|
||||
//don't put lock around this as it is ConcurrentDictionary.
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace umbraco.cms.businesslogic
|
||||
/// </summary>
|
||||
public class Dictionary
|
||||
{
|
||||
private static volatile bool cacheIsEnsured = false;
|
||||
private static readonly object m_Locker = new object();
|
||||
private static Hashtable DictionaryItems = Hashtable.Synchronized(new Hashtable());
|
||||
private static Guid topLevelParent = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde");
|
||||
private static volatile bool _cacheIsEnsured = false;
|
||||
private static readonly object Locker = new object();
|
||||
private static readonly Hashtable DictionaryItems = Hashtable.Synchronized(new Hashtable());
|
||||
private static readonly Guid TopLevelParent = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde");
|
||||
|
||||
protected static ISqlHelper SqlHelper
|
||||
{
|
||||
@@ -30,13 +30,13 @@ namespace umbraco.cms.businesslogic
|
||||
/// <summary>
|
||||
/// Reads all items from the database and stores in local cache
|
||||
/// </summary>
|
||||
private static void ensureCache()
|
||||
private static void EnsureCache()
|
||||
{
|
||||
if (!cacheIsEnsured)
|
||||
if (!_cacheIsEnsured)
|
||||
{
|
||||
lock (m_Locker)
|
||||
lock (Locker)
|
||||
{
|
||||
if (!cacheIsEnsured)
|
||||
if (!_cacheIsEnsured)
|
||||
{
|
||||
using (IRecordsReader dr = SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary"))
|
||||
{
|
||||
@@ -55,7 +55,7 @@ namespace umbraco.cms.businesslogic
|
||||
}
|
||||
}
|
||||
|
||||
cacheIsEnsured = true;
|
||||
_cacheIsEnsured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,10 +68,10 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
get
|
||||
{
|
||||
ensureCache();
|
||||
EnsureCache();
|
||||
|
||||
return DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
.Where(x => x.ParentId == topLevelParent).OrderBy(item => item.key)
|
||||
.Where(x => x.ParentId == TopLevelParent).OrderBy(item => item.key)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -105,11 +105,9 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public DictionaryItem(string key)
|
||||
{
|
||||
ensureCache();
|
||||
EnsureCache();
|
||||
|
||||
var item = DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
.Where(x => x.key == key)
|
||||
.SingleOrDefault();
|
||||
var item = DictionaryItems.Values.Cast<DictionaryItem>().SingleOrDefault(x => x.key == key);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
@@ -124,11 +122,9 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public DictionaryItem(Guid id)
|
||||
{
|
||||
ensureCache();
|
||||
EnsureCache();
|
||||
|
||||
var item = DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
.Where(x => x.UniqueId == id)
|
||||
.SingleOrDefault();
|
||||
var item = DictionaryItems.Values.Cast<DictionaryItem>().SingleOrDefault(x => x.UniqueId == id);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
@@ -143,11 +139,9 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public DictionaryItem(int id)
|
||||
{
|
||||
ensureCache();
|
||||
EnsureCache();
|
||||
|
||||
var item = DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
.Where(x => x.id == id)
|
||||
.SingleOrDefault();
|
||||
var item = DictionaryItems.Values.Cast<DictionaryItem>().SingleOrDefault(x => x.id == id);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
@@ -170,7 +164,7 @@ namespace umbraco.cms.businesslogic
|
||||
return DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
.Where(x => x.id == id)
|
||||
.Select(x => x.ParentId)
|
||||
.SingleOrDefault() == topLevelParent;
|
||||
.SingleOrDefault() == TopLevelParent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,9 +176,7 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
if (_parent == null)
|
||||
{
|
||||
var p = DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
.Where(x => x.UniqueId == this.ParentId)
|
||||
.SingleOrDefault();
|
||||
var p = DictionaryItems.Values.Cast<DictionaryItem>().SingleOrDefault(x => x.UniqueId == this.ParentId);
|
||||
|
||||
if (p == null)
|
||||
{
|
||||
@@ -221,7 +213,7 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public static bool hasKey(string key)
|
||||
{
|
||||
ensureCache();
|
||||
EnsureCache();
|
||||
return DictionaryItems.ContainsKey(key);
|
||||
}
|
||||
|
||||
@@ -243,7 +235,7 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
if (!hasKey(value))
|
||||
{
|
||||
lock (m_Locker)
|
||||
lock (Locker)
|
||||
{
|
||||
SqlHelper.ExecuteNonQuery("Update cmsDictionary set [key] = @key WHERE pk = @Id", SqlHelper.CreateParameter("@key", value),
|
||||
SqlHelper.CreateParameter("@Id", id));
|
||||
@@ -329,7 +321,7 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public static int addKey(string key, string defaultValue, string parentKey)
|
||||
{
|
||||
ensureCache();
|
||||
EnsureCache();
|
||||
|
||||
if (hasKey(parentKey))
|
||||
{
|
||||
@@ -342,8 +334,8 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public static int addKey(string key, string defaultValue)
|
||||
{
|
||||
ensureCache();
|
||||
int retval = createKey(key, topLevelParent, defaultValue);
|
||||
EnsureCache();
|
||||
int retval = createKey(key, TopLevelParent, defaultValue);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections;
|
||||
using System.Linq;
|
||||
using umbraco.DataLayer;
|
||||
using System.Xml;
|
||||
using umbraco.cms.businesslogic.media;
|
||||
using umbraco.interfaces;
|
||||
using umbraco.cms.businesslogic.propertytype;
|
||||
|
||||
@@ -80,21 +81,28 @@ namespace umbraco.cms.businesslogic.datatype
|
||||
#region Public methods
|
||||
public override void delete()
|
||||
{
|
||||
//first clear the prevalues
|
||||
PreValues.DeleteByDataTypeDefinition(this.Id);
|
||||
|
||||
//next clear out the property types
|
||||
var propTypes = PropertyType.GetByDataTypeDefinition(this.Id);
|
||||
foreach (var p in propTypes)
|
||||
DeleteEventArgs e = new DeleteEventArgs();
|
||||
FireBeforeDelete(e);
|
||||
if (!e.Cancel)
|
||||
{
|
||||
p.delete();
|
||||
//first clear the prevalues
|
||||
PreValues.DeleteByDataTypeDefinition(this.Id);
|
||||
|
||||
//next clear out the property types
|
||||
var propTypes = PropertyType.GetByDataTypeDefinition(this.Id);
|
||||
foreach (var p in propTypes)
|
||||
{
|
||||
p.delete();
|
||||
}
|
||||
|
||||
//delete the cmsDataType role, then the umbracoNode
|
||||
SqlHelper.ExecuteNonQuery("delete from cmsDataType where nodeId=@nodeId",
|
||||
SqlHelper.CreateParameter("@nodeId", this.Id));
|
||||
base.delete();
|
||||
|
||||
cache.Cache.ClearCacheItem(string.Format("UmbracoDataTypeDefinition{0}", Id));
|
||||
FireAfterDelete(e);
|
||||
}
|
||||
|
||||
//delete the cmsDataType role, then the umbracoNode
|
||||
SqlHelper.ExecuteNonQuery("delete from cmsDataType where nodeId=@nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
|
||||
base.delete();
|
||||
|
||||
cache.Cache.ClearCacheItem(string.Format("UmbracoDataTypeDefinition{0}", Id));
|
||||
}
|
||||
|
||||
[Obsolete("Use the standard delete() method instead")]
|
||||
@@ -363,12 +371,42 @@ namespace umbraco.cms.businesslogic.datatype
|
||||
New(this, e);
|
||||
}
|
||||
|
||||
[Obsolete("This event is not used! Use the BeforeDelete or AfterDelete events")]
|
||||
public static event DeleteEventHandler Deleting;
|
||||
protected virtual void OnDeleting(EventArgs e)
|
||||
{
|
||||
if (Deleting != null)
|
||||
Deleting(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [before delete].
|
||||
/// </summary>
|
||||
public new static event DeleteEventHandler BeforeDelete;
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:BeforeDelete"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
protected new virtual void FireBeforeDelete(DeleteEventArgs e)
|
||||
{
|
||||
if (BeforeDelete != null)
|
||||
BeforeDelete(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [after delete].
|
||||
/// </summary>
|
||||
public new static event DeleteEventHandler AfterDelete;
|
||||
/// <summary>
|
||||
/// Raises the <see cref="E:AfterDelete"/> event.
|
||||
/// </summary>
|
||||
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
||||
protected new virtual void FireAfterDelete(DeleteEventArgs e)
|
||||
{
|
||||
if (AfterDelete != null)
|
||||
AfterDelete(this, e);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Umbraco.Core;
|
||||
using umbraco.cms.businesslogic.member;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
|
||||
@@ -61,5 +62,6 @@ namespace umbraco.cms.businesslogic {
|
||||
{
|
||||
public bool CancelChildren { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace umbraco.editorControls
|
||||
// Don't query if there's nothing to query for..
|
||||
if (string.IsNullOrWhiteSpace(Value.ToString()) == false)
|
||||
{
|
||||
IRecordsReader dr = SqlHelper.ExecuteReader("Select [value] from cmsDataTypeprevalues where id in (@id)", SqlHelper.CreateParameter("id", Value.ToString()));
|
||||
IRecordsReader dr = SqlHelper.ExecuteReader("Select [value] from cmsDataTypeprevalues where id in (" + SqlHelper.EscapeString(Value.ToString()) + ")");
|
||||
|
||||
while (dr.Read())
|
||||
{
|
||||
|
||||
@@ -75,8 +75,8 @@ namespace umbraco.editorControls
|
||||
set
|
||||
{
|
||||
int integer;
|
||||
|
||||
if (int.TryParse(value, NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out integer))
|
||||
|
||||
if (int.TryParse(value, NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out integer))
|
||||
{
|
||||
base.Text = integer.ToString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user