/// <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; } }
// gets the control for the macro, using GetXsltTransform methods for execution // will pick XmlDocument or Navigator mode depending on the capabilities of the published caches internal Control LoadMacroXslt(macro macro, MacroModel model, Hashtable pageElements, bool throwError) { if (XsltFile.Trim() == string.Empty) { TraceWarn("macro", "Xslt is empty"); return new LiteralControl(string.Empty); } using (DisposableTimer.DebugDuration<macro>("Executing XSLT: " + XsltFile)) { XmlDocument macroXml = null; // get master xml document var cache = UmbracoContext.Current.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); XmlDocument umbracoXml = cache.GetXml(UmbracoContext.Current, UmbracoContext.Current.InPreviewMode); macroXml = new XmlDocument(); macroXml.LoadXml("<macro/>"); foreach (var prop in macro.Model.Properties) { AddMacroXmlNode(umbracoXml, macroXml, prop.Key, prop.Type, prop.Value); } if (HttpContext.Current.Request.QueryString["umbDebug"] != null && GlobalSettings.DebugMode) { var outerXml = macroXml.OuterXml; return new LiteralControl("<div style=\"border: 2px solid green; padding: 5px;\"><b>Debug from " + macro.Name + "</b><br/><p>" + HttpContext.Current.Server.HtmlEncode(outerXml) + "</p></div>"); } try { var xsltFile = getXslt(XsltFile); using (DisposableTimer.DebugDuration<macro>("Performing transformation")) { try { var transformed = GetXsltTransformResult(macroXml, xsltFile); var result = CreateControlsFromText(transformed); return result; } catch (Exception e) { Exceptions.Add(e); LogHelper.WarnWithException<macro>("Error parsing XSLT file", e); 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; } } }
public Control renderMacro(Hashtable pageElements, int pageId) { var macroInfo = (Model.MacroType == MacroTypes.Script && Model.Name.IsNullOrWhiteSpace()) ? string.Format("Render Inline Macro, Cache: {0})", Model.CacheDuration) : string.Format("Render Macro: {0}, type: {1}, cache: {2})", Name, Model.MacroType, Model.CacheDuration); using (DisposableTimer.DebugDuration<macro>(macroInfo)) { TraceInfo("renderMacro", macroInfo, excludeProfiling:true); StateHelper.SetContextValue(MacrosAddedKey, StateHelper.GetContextValue<int>(MacrosAddedKey) + 1); // zb-00037 #29875 : parse attributes here (and before anything else) foreach (MacroPropertyModel prop in Model.Properties) prop.Value = helper.parseAttribute(pageElements, prop.Value); Model.CacheIdentifier = GetCacheIdentifier(Model, pageElements, pageId); string macroHtml; Control macroControl; //get the macro from cache if it is there GetMacroFromCache(out macroHtml, out macroControl); // FlorisRobbemont: Empty macroHtml (not null, but "") doesn't mean a re-render is necessary if (macroHtml == null && macroControl == null) { var renderFailed = false; var macroType = Model.MacroType != MacroTypes.Unknown ? (int) Model.MacroType : MacroType; 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); }; using (DisposableTimer.DebugDuration<macro>("Executing Partial View: " + Model.TypeName)) { TraceInfo("umbracoMacro", "Partial View added (" + Model.TypeName + ")", excludeProfiling:true); try { var result = LoadPartialViewMacro(Model); macroControl = new LiteralControl(result.Result); if (result.ResultException != null) { renderFailed = true; 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; } } } catch (Exception e) { renderFailed = true; 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; } case (int) MacroTypes.UserControl: using (DisposableTimer.DebugDuration<macro>("Executing UserControl: " + Model.TypeName)) { try { TraceInfo("umbracoMacro", "Usercontrol added (" + Model.TypeName + ")", excludeProfiling:true); // Add tilde for v4 defined macros if (string.IsNullOrEmpty(Model.TypeName) == false && Model.TypeName.StartsWith("~") == false) Model.TypeName = "~/" + Model.TypeName; macroControl = loadUserControl(ScriptType, Model, pageElements); break; } catch (Exception e) { renderFailed = true; Exceptions.Add(e); 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: using (DisposableTimer.DebugDuration<macro>("Executing CustomControl: " + Model.TypeName + "." + Model.TypeAssembly)) { try { TraceInfo("umbracoMacro", "Custom control added (" + Model.TypeName + "), ScriptAssembly: " + Model.TypeAssembly, excludeProfiling: true); macroControl = loadControl(Model.TypeAssembly, ScriptType, Model, pageElements); break; } catch (Exception e) { renderFailed = true; Exceptions.Add(e); 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, 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); }; using (DisposableTimer.DebugDuration<macro>("Executing MacroEngineScript: " + ScriptFile)) { try { TraceInfo("umbracoMacro", "MacroEngine script added (" + ScriptFile + ")", excludeProfiling: true); ScriptingMacroResult result = loadMacroScript(Model); macroControl = new LiteralControl(result.Result); if (result.ResultException != null) { renderFailed = true; 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; } catch (Exception e) { renderFailed = true; Exceptions.Add(e); 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; } } case (int) MacroTypes.Unknown: default: if (GlobalSettings.DebugMode) { macroControl = new LiteralControl("<Macro: " + Name + " (" + ScriptAssembly + "," + ScriptType + ")>"); } break; } //add to cache if render is successful if (renderFailed == false) { macroControl = AddMacroResultToCache(macroControl); } } else if (macroControl == null) { macroControl = new LiteralControl(macroHtml); } return macroControl; } }
/// <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); } }
void macro_Error(object sender, MacroErrorEventArgs e) { // From http://stackoverflow.com/a/5936867/1110395: ErrorSignal.FromCurrentContext().Raise(e.Exception); }
internal Control LoadMacroXslt(macro macro, MacroModel model, Hashtable pageElements, bool throwError) { if (XsltFile.Trim() != string.Empty) { // Get main XML var umbracoXml = content.Instance.XmlContent; // Create XML document for Macro var macroXml = new XmlDocument(); macroXml.LoadXml("<macro/>"); foreach (var prop in macro.Model.Properties) { addMacroXmlNode(umbracoXml, macroXml, prop.Key, prop.Type, prop.Value); } if (HttpContext.Current.Request.QueryString["umbDebug"] != null && GlobalSettings.DebugMode) { 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) + "</p></div>"); } try { var xsltFile = getXslt(XsltFile); try { var result = CreateControlsFromText(GetXsltTransformResult(macroXml, xsltFile)); 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; } 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; } } TraceWarn("macro", "Xslt is empty"); return new LiteralControl(string.Empty); }
public Control renderMacro(Hashtable pageElements, int pageId) { TraceInfo("renderMacro", string.Format("Rendering started (macro: {0}, type: {1}, cacheRate: {2})", Name, MacroType, Model.CacheDuration)); StateHelper.SetContextValue(macrosAddedKey, StateHelper.GetContextValue<int>(macrosAddedKey) + 1); String macroHtml = null; Control macroControl = null; // zb-00037 #29875 : parse attributes here (and before anything else) foreach (MacroPropertyModel prop in Model.Properties) prop.Value = helper.parseAttribute(pageElements, prop.Value); Model.CacheIdentifier = GetCacheIdentifier(Model, pageElements, pageId); if (!UmbracoContext.Current.InPreviewMode && Model.CacheDuration > 0) { if (cacheMacroAsString(Model)) { macroHtml = macroCache["macroHtml_" + Model.CacheIdentifier] as String; // FlorisRobbemont: // An empty string means: macroHtml has been cached before, but didn't had any output (Macro doesn't need to be rendered again) // An empty reference (null) means: macroHtml has NOT been cached before if (macroHtml != null) { if (MacroNeedsToBeClearedFromCache(Model, "macroHtml_DateAdded_" + Model.CacheIdentifier)) { macroHtml = null; TraceInfo("renderMacro", string.Format("Macro removed from cache due to file change '{0}'.", Model.CacheIdentifier)); } else { TraceInfo("renderMacro", string.Format("Macro Content loaded from cache '{0}'.", Model.CacheIdentifier)); } } } else { var cacheContent = macroCache["macroControl_" + Model.CacheIdentifier] as MacroCacheContent; if (cacheContent != null) { macroControl = cacheContent.Content; macroControl.ID = cacheContent.ID; if (MacroNeedsToBeClearedFromCache(Model, "macroControl_DateAdded_" + Model.CacheIdentifier)) { TraceInfo("renderMacro", string.Format("Macro removed from cache due to file change '{0}'.", Model.CacheIdentifier)); macroControl = null; } else { TraceInfo("renderMacro", string.Format("Macro Control loaded from cache '{0}'.", Model.CacheIdentifier)); } } } } // FlorisRobbemont: Empty macroHtml (not null, but "") doesn't mean a re-render is necessary if (macroHtml == null && macroControl == null) { bool renderFailed = false; int macroType = Model.MacroType != MacroTypes.Unknown ? (int)Model.MacroType : MacroType; 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 { var result = LoadPartialViewMacro(Model); macroControl = new LiteralControl(result.Result); if (result.ResultException != null) { renderFailed = true; 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; } } } catch (Exception e) { renderFailed = true; 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; case (int)MacroTypes.UserControl: try { TraceInfo("umbracoMacro","Usercontrol added (" + Model.TypeName + ")"); // Add tilde for v4 defined macros if (string.IsNullOrEmpty(Model.TypeName) == false && Model.TypeName.StartsWith("~") == false) Model.TypeName = "~/" + Model.TypeName; macroControl = loadUserControl(ScriptType, Model, pageElements); break; } catch (Exception e) { renderFailed = true; Exceptions.Add(e); 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 + ")"); macroControl = loadControl(Model.TypeAssembly, ScriptType, Model, pageElements); break; } catch (Exception e) { renderFailed = true; Exceptions.Add(e); 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, 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 + ")"); ScriptingMacroResult result = loadMacroScript(Model); macroControl = new LiteralControl(result.Result); if (result.ResultException != null) { renderFailed = true; 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; } catch (Exception e) { renderFailed = true; Exceptions.Add(e); macroControl = handleMacroScriptError(e); //if it is null, then we are supposed to throw the (original) exception // see: http://issues.umbraco.org/issue/U4-497 at the end if (macroControl == null) { throw; } break; } default: if (GlobalSettings.DebugMode) macroControl = new LiteralControl("<Macro: " + Name + " (" + ScriptAssembly + "," + ScriptType + ")>"); break; } // Add result to cache if successful if (!renderFailed && Model.CacheDuration > 0) { // do not add to cache if there's no member and it should cache by personalization if (!Model.CacheByMember || (Model.CacheByMember && Member.GetCurrentMember() != null)) { if (macroControl != null) { // NH: Scripts and XSLT can be generated as strings, but not controls as page events wouldn't be hit (such as Page_Load, etc) if (cacheMacroAsString(Model)) { string outputCacheString = ""; using (var sw = new StringWriter()) { var hw = new HtmlTextWriter(sw); macroControl.RenderControl(hw); outputCacheString = sw.ToString(); } macroCache.Insert("macroHtml_" + Model.CacheIdentifier, outputCacheString, null, DateTime.Now.AddSeconds(Model.CacheDuration), TimeSpan.Zero, CacheItemPriority.NotRemovable, //FlorisRobbemont: issue #27610 -> Macro output cache should not be removable null); macroCache.Insert("macroHtml_DateAdded_" + Model.CacheIdentifier, DateTime.Now, null, DateTime.Now.AddSeconds(Model.CacheDuration), TimeSpan.Zero, CacheItemPriority.NotRemovable, //FlorisRobbemont: issue #27610 -> Macro output cache should not be removable null); // zb-00003 #29470 : replace by text if not already text // otherwise it is rendered twice if (!(macroControl is LiteralControl)) macroControl = new LiteralControl(outputCacheString); TraceInfo("renderMacro", string.Format("Macro Content saved to cache '{0}'.", Model.CacheIdentifier)); } else { macroCache.Insert("macroControl_" + Model.CacheIdentifier, new MacroCacheContent(macroControl, macroControl.ID), null, DateTime.Now.AddSeconds(Model.CacheDuration), TimeSpan.Zero, CacheItemPriority.NotRemovable, //FlorisRobbemont: issue #27610 -> Macro output cache should not be removable null); macroCache.Insert("macroControl_DateAdded_" + Model.CacheIdentifier, DateTime.Now, null, DateTime.Now.AddSeconds(Model.CacheDuration), TimeSpan.Zero, CacheItemPriority.NotRemovable, //FlorisRobbemont: issue #27610 -> Macro output cache should not be removable null); TraceInfo("renderMacro", string.Format("Macro Control saved to cache '{0}'.", Model.CacheIdentifier)); } } } } } else if (macroControl == null) { // FlorisRobbemont: Extra check to see if the macroHtml is not null macroControl = new LiteralControl(macroHtml == null ? "" : macroHtml); } return macroControl; }