Compare commits

...

35 Commits

Author SHA1 Message Date
Sebastiaan Janssen 2bc33e0115 Merge 2013-03-27 08:58:34 -01:00
Sebastiaan Janssen 8576d9c67e Bump version to 4.11.6 2013-03-27 08:56:30 -01:00
Shannon Deminick 9452d5fd09 adds patch for #U4-1754 and updates 2013-03-27 03:08:37 +06:00
Shannon Deminick 1f40d726de adds patch for #U4-1754 2013-03-27 02:59:42 +06:00
Shannon Deminick 2699611ba3 adds patch for #U4-1754 2013-03-27 02:58:59 +06:00
Shannon Deminick b1fa06bfe8 Removes unused code. 2013-03-26 22:02:49 +06:00
Shannon Deminick 4dc22b6ce4 Applies patch for #U4-497 for getting exceptions for xslt 2013-03-26 22:02:08 +06:00
Sebastiaan Janssen 883277125a Can't use parameters here as the query gets butchered. 2013-03-26 13:27:46 -01:00
Sebastiaan Janssen ff961abaab Actual fix for U4-1987 Error when attempting to save document type 2013-03-26 09:50:48 -01:00
Sebastiaan Janssen b2414c1ab8 Fixes U4-1987 Error when attempting to save document type 2013-03-26 09:33:08 -01:00
Stephen Roberts 0e392b398c Fixed Property order for document types with Master document types. 2013-03-14 11:08:02 +00:00
Sebastiaan Janssen 5914690ad8 Allow leading sign in number field so that negative numbers are supported 2013-03-25 10:09:02 -01:00
Shannon Deminick dbbdd16bb9 Fixes: #U4-497 2013-03-23 02:34:34 +06:00
Shannon Deminick 762cca145f Fixes: #U4-1931 - allow empty string in dictionary item. 2013-03-22 21:59:21 +06:00
Morten Christensen 5cd2d568e3 Fixes U4-1938 by actually calling and checking the Before/AfterDelete events for DataTypeDefinitions.
Backported from 32fb51f603a3
2013-03-22 11:12:32 -01:00
Sebastiaan Janssen 259fbcb8f0 Fixed U4-1934 Error in comments of config files 2013-03-18 08:50:24 -01:00
Shannon Deminick db3f21429e Fixes infinite loop bug with string trimming. 2013-03-15 11:31:11 +06:00
Shannon Deminick 04b9607741 Fixes: #U4-1916 and another issue for vdirs, added unit tests, changed UI web project to always
launch in a VDIR.
2013-03-15 08:03:19 +04:00
Shannon Deminick 5bd2571395 Merge 2013-03-12 00:01:14 +04:00
Shannon Deminick c0cd157e60 applies patch for #U4-1890 2013-03-12 00:00:55 +04:00
Sebastiaan Janssen ac2f9b172c .transformed files are auto-generated and shouldn't be under source control 2013-03-11 17:15:30 -01:00
Sebastiaan Janssen b8e4a73c3b Revert Casey's mistakenly added files 2013-03-11 15:55:35 -01:00
Shannon Deminick 68c4255a51 updates proper error handling for razor script macros 2013-03-11 21:49:04 +06:00
Shannon Deminick d07212c89b Fixed changes with new MacroErrors config and how macros are rendered so it supports all macro types and also fixes up a bit of code
in the umbraco.macro class.
2013-03-11 21:06:02 +06:00
Shannon Deminick 28c55320a1 Updates the pull request for handling macro errors.
Backports fixes for partial view macros from 6.x - U4-1488, U4-1844, U4-1730, U4-1843
2013-03-11 20:11:11 +06:00
Shannon Deminick fd58017e97 imported patches for pull request spudstuff/u4497 2013-03-11 18:49:45 +06:00
Casey Neehouse 09408b898e Merge 2013-03-08 19:38:33 +05:00
Morten Christensen d1b05332c5 Manually adds fix for U4-1869 from 6.0.3 branch 2013-03-07 20:28:13 -01:00
Casey Neehouse caad0afed8 Corrects u4-1870 - Refactor Load Content SQL to check if ID is in published list
versus using a join that can cause duplicate entries.
2013-03-07 21:00:31 +05:00
Shannon Deminick 62d365573f Back ports fixes for: #U4-1614, #U4-793 - preview working properly in mvc 2013-03-06 03:17:18 +06:00
Shannon Deminick f3b7a8a581 Fixes: #U4-1471 - issue with having NewRelic installed and some sort of conflict with shutting down
our logger.
2013-03-05 23:49:45 +06:00
Shannon Deminick a765f36d24 Adds unit tests for DynamicNode and DynamicPublishedContent, fixes both when querying for recursive properties.
Fixes: #U4-1839
2013-03-05 21:00:16 +06:00
Shannon Deminick 046da1a8ce Fixes: #U4-1824 - issue with razor dynamic node property access performance enhancements. 2013-03-05 07:06:41 +06:00
Shannon Deminick ca7abd6c78 Merge 2013-03-05 06:08:59 +06:00
Shannon Deminick 7cc0f2a514 Fixes: #U4-1819 2013-03-05 05:56:53 +06:00
52 changed files with 1750 additions and 734 deletions
+1
View File
@@ -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
View File
@@ -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
+19 -4
View File
@@ -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; }
}
}
+15
View File
@@ -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
+14 -4
View File
@@ -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();
+23
View File
@@ -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
}
}
+4
View File
@@ -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);
+2
View File
@@ -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()
{
+17 -1
View File
@@ -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>
+1 -1
View File
@@ -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);
}
}
}
+123 -115
View File
@@ -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;
}
}
}
+21 -7
View File
@@ -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)
{
+39 -13
View File
@@ -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; }
}
}
+9 -4
View File
@@ -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";
}
}
+43 -41
View File
@@ -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;
}
}
}
}
+20
View File
@@ -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;
}
}
}
+104 -74
View File
@@ -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);
}
}
}
+102 -83
View File
@@ -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);
}
}
}
}
}
+1
View File
@@ -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" />
+101 -5
View File
@@ -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;
}
}
}
}
+2 -17
View File
@@ -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>
+1 -1
View File
@@ -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 = "/";
+15 -1
View File
@@ -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)
+184 -127
View File
@@ -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("&lt;Macro: " + Name + " (" + ScriptAssembly + "," + ScriptType +
")&gt;");
macroControl = new LiteralControl("&lt;Macro: " + Name + " (" + ScriptAssembly + "," + ScriptType + ")&gt;");
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;
}
+13 -5
View File
@@ -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>
+2 -1
View File
@@ -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.
+24 -32
View File
@@ -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();
}