diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs index c24810c57c..e3c15b1a10 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs @@ -1215,6 +1215,40 @@ namespace Umbraco.Core.Configuration } } + private static MacroErrorBehaviour? _macroErrorBehaviour; + + /// + /// 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) + /// + /// MacroErrorBehaviour enum defining how to handle macro errors. + public static MacroErrorBehaviour MacroErrorBehaviour + { + get + { + if (_macroErrorBehaviour == null) + { + try + { + var behaviour = MacroErrorBehaviour.Inline; + var value = GetKey("/settings/content/MacroErrors"); + if (value != null) + { + Enum.TryParse(value, true, out behaviour); + } + _macroErrorBehaviour = behaviour; + } + catch (Exception ex) + { + LogHelper.Error("Could not load /settings/content/MacroErrors from umbracosettings.config", ex); + _macroErrorBehaviour = MacroErrorBehaviour.Inline; + } + } + return _macroErrorBehaviour.Value; + } + } /// /// Configuration regarding webservices diff --git a/src/Umbraco.Core/MacroErrorBehaviour.cs b/src/Umbraco.Core/MacroErrorBehaviour.cs new file mode 100644 index 0000000000..50d9faa19c --- /dev/null +++ b/src/Umbraco.Core/MacroErrorBehaviour.cs @@ -0,0 +1,23 @@ +namespace Umbraco.Core +{ + public enum MacroErrorBehaviour + { + /// + /// Default umbraco behavior - show an inline error within the + /// macro but allow the page to continue rendering. + /// + Inline, + + /// + /// Silently eat the error and do not display the offending macro. + /// + Silent, + + /// + /// 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. + /// + Throw + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index f20e9e573a..ab878ff025 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -71,6 +71,7 @@ + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 5144e8bfac..669f213dc7 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -86,6 +86,14 @@ false + + + inline diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 1fafe2f61b..62a6e424a6 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -81,6 +81,13 @@ 1800 + + inline diff --git a/src/Umbraco.Web/umbraco.presentation/macro.cs b/src/Umbraco.Web/umbraco.presentation/macro.cs index 771e1350ca..5c24da5362 100644 --- a/src/Umbraco.Web/umbraco.presentation/macro.cs +++ b/src/Umbraco.Web/umbraco.presentation/macro.cs @@ -18,17 +18,15 @@ using System.Xml.Xsl; using Umbraco.Core; 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; @@ -429,8 +427,25 @@ namespace umbraco { renderFailed = true; Exceptions.Add(e); - LogHelper.WarnWithException("Error loading userControl (" + Model.TypeName + ")", true, e); - macroControl = new LiteralControl("Error loading userControl '" + Model.TypeName + "'"); + LogHelper.WarnWithException("Error loading userControl (" + Model.TypeName + ")", true, e); + + // Invoke any error handlers for this macro + var macroErrorEventArgs = new MacroErrorEventArgs {Name = Model.Name, Alias = Model.Alias, File = Model.TypeName, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour}; + OnError(macroErrorEventArgs); + + // Check how to handle the error for this macro. + // (note the error event above may have changed the default behaviour as defined in settings) + switch (macroErrorEventArgs.Behaviour) + { + case MacroErrorBehaviour.Inline: + macroControl = new LiteralControl("Error loading userControl '" + Model.TypeName + "'"); + break; + case MacroErrorBehaviour.Silent: + macroControl = new LiteralControl(""); + break; + case MacroErrorBehaviour.Throw: + throw; + } break; } case (int)MacroTypes.CustomControl: @@ -451,11 +466,24 @@ namespace umbraco LogHelper.WarnWithException("Error loading customControl (Assembly: " + Model.TypeAssembly + ", Type: '" + Model.TypeName + "'", true, e); - - macroControl = - new LiteralControl("Error loading customControl (Assembly: " + Model.TypeAssembly + - ", Type: '" + - Model.TypeName + "'"); + + // Invoke any error handlers for this macro + var macroErrorEventArgs = new MacroErrorEventArgs {Name = Model.Name, Alias = Model.Alias, File = Model.TypeAssembly, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour}; + OnError(macroErrorEventArgs); + + // Check how to handle the error for this macro. + // (note the error event above may have changed the default behaviour as defined in settings) + switch (macroErrorEventArgs.Behaviour) + { + case MacroErrorBehaviour.Inline: + macroControl = new LiteralControl("Error loading customControl (Assembly: " + Model.TypeAssembly + ", Type: '" + Model.TypeName + "'"); + break; + case MacroErrorBehaviour.Silent: + macroControl = new LiteralControl(""); + break; + case MacroErrorBehaviour.Throw: + throw; + } break; } case (int)MacroTypes.XSLT: @@ -487,24 +515,23 @@ namespace umbraco true, e); - var result = - new LiteralControl("Error loading MacroEngine script (file: " + ScriptFile + ")"); - - /* - string args = "
    "; - foreach(object key in attributes.Keys) - args += "
  • " + key.ToString() + ": " + attributes[key] + "
  • "; - - foreach (object key in pageElements.Keys) - args += "
  • " + key.ToString() + ": " + pageElements[key] + "
  • "; - - args += "
"; - - result.Text += args; - */ - - macroControl = result; + // Invoke any error handlers for this macro + var macroErrorEventArgs = new MacroErrorEventArgs {Name = Model.Name, Alias = Model.Alias, File = ScriptFile, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour}; + OnError(macroErrorEventArgs); + // Check how to handle the error for this macro. + // (note the error event above may have changed the default behaviour as defined in settings) + switch (macroErrorEventArgs.Behaviour) + { + case MacroErrorBehaviour.Inline: + macroControl = new LiteralControl("Error loading MacroEngine script (file: " + ScriptFile + ")"); + break; + case MacroErrorBehaviour.Silent: + macroControl = new LiteralControl(""); + break; + case MacroErrorBehaviour.Throw: + throw; + } break; } default: @@ -801,7 +828,26 @@ namespace umbraco { Exceptions.Add(e); LogHelper.WarnWithException("Error loading XSLT " + Model.Xslt, true, e); - return new LiteralControl("Error reading XSLT file: \\xslt\\" + XsltFile); + Control macroControl = null; + + // Invoke any error handlers for this macro + var macroErrorEventArgs = new MacroErrorEventArgs {Name = Model.Name, Alias = Model.Alias, File = Model.Xslt, Exception = e, Behaviour = UmbracoSettings.MacroErrorBehaviour}; + OnError(macroErrorEventArgs); + + // Check how to handle the error for this macro. + // (note the error event above may have changed the default behaviour as defined in settings) + switch (macroErrorEventArgs.Behaviour) + { + case MacroErrorBehaviour.Inline: + macroControl = new LiteralControl("Error reading XSLT file: \\xslt\\" + XsltFile); + break; + case MacroErrorBehaviour.Silent: + macroControl = new LiteralControl(""); + break; + case MacroErrorBehaviour.Throw: + throw; + } + return macroControl; } } } @@ -1505,7 +1551,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 +1577,7 @@ namespace umbraco catch (Exception e) { LogHelper.WarnWithException(string.Format("Error creating usercontrol ({0})", fileName), true, e); - - return new LiteralControl( - string.Format( - "
Error creating control ({0}).
Maybe file doesn't exists or the usercontrol has a cache directive, which is not allowed! See the tracestack for more information!
", - fileName)); + throw; } } @@ -1802,5 +1844,31 @@ namespace umbraco value = false; return false; } + + #region Events + + /// + /// The macro error event handler. + /// + public delegate void ErrorEventHandler(object sender, MacroErrorEventArgs e); + + /// + /// Occurs when a macro error is raised. + /// + public static event ErrorEventHandler Error; + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnError(MacroErrorEventArgs e) + { + if (Error != null) + { + Error(this, e); + } + } + + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs index dc94b66cd8..db27764b80 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs @@ -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; } } } diff --git a/src/umbraco.MacroEngines/RazorCore/RazorMacroEngine.cs b/src/umbraco.MacroEngines/RazorCore/RazorMacroEngine.cs index e30d8c5dca..83304f7ab2 100644 --- a/src/umbraco.MacroEngines/RazorCore/RazorMacroEngine.cs +++ b/src/umbraco.MacroEngines/RazorCore/RazorMacroEngine.cs @@ -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("
Error loading Razor Script {0}
", macro.ScriptName); - if (GlobalSettings.DebugMode) - loading = loading + exception.Message; - loading = loading + "
"; - return loading; + throw; } } diff --git a/src/umbraco.businesslogic/UmbracoSettings.cs b/src/umbraco.businesslogic/UmbracoSettings.cs index 22133c3945..c7db9fdeb1 100644 --- a/src/umbraco.businesslogic/UmbracoSettings.cs +++ b/src/umbraco.businesslogic/UmbracoSettings.cs @@ -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; } } + /// + /// 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) + /// + /// MacroErrorBehaviour enum defining how to handle macro errors. + public static MacroErrorBehaviour MacroErrorBehaviour + { + get { return Umbraco.Core.Configuration.UmbracoSettings.MacroErrorBehaviour; } + } + /// /// Configuration regarding webservices /// diff --git a/src/umbraco.cms/businesslogic/events/EventArgs.cs b/src/umbraco.cms/businesslogic/events/EventArgs.cs index 3514065cee..87942060f6 100644 --- a/src/umbraco.cms/businesslogic/events/EventArgs.cs +++ b/src/umbraco.cms/businesslogic/events/EventArgs.cs @@ -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,36 @@ namespace umbraco.cms.businesslogic { { public bool CancelChildren { get; set; } } - + + // Provides information on the macro that caused an error + public class MacroErrorEventArgs : System.EventArgs + { + /// + /// Name of the faulting macro. + /// + public string Name { get; set; } + + /// + /// Alias of the faulting macro. + /// + public string Alias { get; set; } + + /// + /// Filename of the faulting macro. + /// + public string File { get; set; } + + /// + /// Exception raised. + /// + public Exception Exception { get; set; } + + /// + /// Gets or sets the desired behaviour when a matching macro causes an error. See + /// for definitions. By setting this in your event + /// you can override the default behaviour defined in UmbracoSettings.config. + /// + /// Macro error behaviour enum. + public MacroErrorBehaviour Behaviour { get; set; } + } }