public MacroContent Execute(MacroModel model) { var filename = model.MacroSource; // ensure the file exists var path = IOHelper.FindFile(filename); if (File.Exists(IOHelper.MapPath(path)) == false) { throw new Exception($"Failed to load control, file {filename} does not exist."); } // load the control var control = (UserControl) new UserControl().LoadControl(path); control.ID = string.IsNullOrEmpty(model.MacroControlIdentifier) ? GetControlUniqueId(filename) : model.MacroControlIdentifier; // initialize the control // note: we are not setting the 'CurrentNode' property on the control anymore, // as that was an INode which is gone in v8. Use UmbracoContext to access the // current content. Current.Logger.Info <UserControlMacroEngine>("Loaded control '{UserControlFile}' with ID '{UserControlId}'", filename, control.ID); UpdateControlProperties(control, model); return(new MacroContent { Control = control }); }
/// <summary> /// Executes a macro of a given type. /// </summary> private Attempt <MacroContent> ExecuteMacroWithErrorWrapper(MacroModel macro, string msgIn, string msgOut, Func <MacroContent> getMacroContent, Func <string> msgErr) { using (_plogger.DebugDuration <MacroRenderer>(msgIn, msgOut)) { return(ExecuteProfileMacroWithErrorWrapper(macro, msgIn, getMacroContent, msgErr)); } }
/// <summary> /// Renders a PartialView Macro. /// </summary> /// <returns>The text output of the macro execution.</returns> private static MacroContent ExecutePartialView(MacroModel macro) { var engine = new PartialViewMacroEngine(); var content = UmbracoContext.Current.PublishedRequest.PublishedContent; return(engine.Execute(macro, content)); }
// gets this macro content cache identifier private string GetContentCacheIdentifier(MacroModel model, int pageId) { var id = new StringBuilder(); var alias = model.Alias; id.AppendFormat("{0}-", alias); if (model.CacheByPage) { id.AppendFormat("{0}-", pageId); } if (model.CacheByMember) { object key = 0; if (_umbracoContextAccessor.UmbracoContext.HttpContext?.User?.Identity?.IsAuthenticated ?? false) { //ugh, membershipproviders :( var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); var member = Core.Security.MembershipProviderExtensions.GetCurrentUser(provider); key = member?.ProviderUserKey ?? 0; } id.AppendFormat("m{0}-", key); } foreach (var value in model.Properties.Select(x => x.Value)) { id.AppendFormat("{0}-", value.Length <= 255 ? value : value.Substring(0, 255)); } return(id.ToString()); }
// generates the model properties according to the attributes public static void GenerateMacroModelPropertiesFromAttributes(MacroModel model, Hashtable attributes) { foreach (string key in attributes.Keys) { model.Properties.Add(new MacroPropertyModel(key, attributes[key].ToString())); } }
// set the control properties according to the model properties ie parameters internal static void UpdateControlProperties(Control control, MacroModel model) { var type = control.GetType(); foreach (var modelProperty in model.Properties) { var controlProperty = type.GetProperty(modelProperty.Key); if (controlProperty == null) { Current.Logger.Warn <UserControlMacroEngine>("Control property '{UserControlProperty}' doesn't exist or isn't accessible, skip.", modelProperty.Key); continue; } var tryConvert = modelProperty.Value.TryConvertTo(controlProperty.PropertyType); if (tryConvert.Success) { try { controlProperty.SetValue(control, tryConvert.Result, null); Current.Logger.Debug <UserControlMacroEngine>("Set property '{UserControlProperty}' value '{UserControlPropertyValue}'", modelProperty.Key, modelProperty.Value); } catch (Exception e) { Current.Logger.Warn <UserControlMacroEngine>(e, "Failed to set property '{UserControlProperty}' value '{UserControlPropertyValue}'", modelProperty.Key, modelProperty.Value); } } else { Current.Logger.Warn <UserControlMacroEngine>("Failed to set property '{UserControlProperty}' value '{UserControlPropertyValue}'", modelProperty.Key, modelProperty.Value); } } }
public string Execute(MacroModel macro, IPublishedContent content) { if (macro == null) { throw new ArgumentNullException("macro"); } if (content == null) { throw new ArgumentNullException("content"); } if (macro.ScriptName.IsNullOrWhiteSpace()) { throw new ArgumentException("The ScriptName property of the macro object cannot be null or empty"); } 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(Umbraco.Web.Mvc.Constants.DataTokenCurrentViewContext)) { viewContext = (ViewContext)mvcHandler.RequestContext.RouteData.DataTokens[Umbraco.Web.Mvc.Constants.DataTokenCurrentViewContext]; } routeVals.DataTokens.Add("ParentActionViewContext", viewContext); } var request = new RequestContext(http, routeVals); string output; using (var controller = new PartialViewMacroController(macro, content)) { //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); } return(output); }
// updates the model properties values according to the attributes private static void UpdateMacroModelProperties(MacroModel model, IDictionary <string, object> macroParams) { foreach (var prop in model.Properties) { var key = prop.Key.ToLowerInvariant(); prop.Value = macroParams.ContainsKey(key) ? macroParams[key]?.ToString() ?? string.Empty : string.Empty; } }
// updates the model properties values according to the attributes private static void UpdateMacroModelProperties(MacroModel model, Hashtable attributes) { foreach (var prop in model.Properties) { var key = prop.Key.ToLowerInvariant(); prop.Value = attributes.ContainsKey(key) ? attributes[key]?.ToString() ?? string.Empty : string.Empty; } }
private MacroContent Render(MacroModel macro, IPublishedContent content, IDictionary pageElements) { if (content == null) { throw new ArgumentNullException(nameof(content)); } var macroInfo = $"Render Macro: {macro.Name}, type: {macro.MacroType}, cache: {macro.CacheDuration}"; using (_plogger.DebugDuration <MacroRenderer>(macroInfo, "Rendered Macro.")) { // parse macro parameters ie replace the special [#key], [$key], etc. syntaxes foreach (var prop in macro.Properties) { prop.Value = ParseAttribute(pageElements, prop.Value); } var cultureName = System.Threading.Thread.CurrentThread.CurrentUICulture.Name; macro.CacheIdentifier = GetContentCacheIdentifier(macro, content.Id, cultureName); // get the macro from cache if it is there var macroContent = GetMacroContentFromCache(macro); // macroContent.IsEmpty may be true, meaning the macro produces no output, // but still can be cached because its execution did not trigger any error. // so we need to actually render, only if macroContent is null if (macroContent != null) { return(macroContent); } // this will take care of errors // it may throw, if we actually want to throw, so better not // catch anything here and let the exception be thrown var attempt = ExecuteMacroOfType(macro, content); // by convention ExecuteMacroByType must either throw or return a result // just check to avoid internal errors macroContent = attempt.Result; if (macroContent == null) { throw new Exception("Internal error, ExecuteMacroOfType returned no content."); } // add to cache if render is successful // content may be empty but that's not an issue if (attempt.Success) { // write to cache (if appropriate) AddMacroContentToCache(macro, macroContent); } return(macroContent); } }
public MacroContent Execute(MacroModel macro, IPublishedContent content) { if (macro == null) { throw new ArgumentNullException(nameof(macro)); } if (content == null) { throw new ArgumentNullException(nameof(content)); } if (macro.MacroSource.IsNullOrWhiteSpace()) { throw new ArgumentException("The MacroSource property of the macro object cannot be null or empty"); } var http = _getHttpContext(); var umbCtx = _getUmbracoContext(); var routeVals = new RouteData(); routeVals.Values.Add("controller", "PartialViewMacro"); routeVals.Values.Add("action", "Index"); routeVals.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); //required for UmbracoViewPage //lets render this controller as a child action var viewContext = new ViewContext { ViewData = new ViewDataDictionary() }; //try and extract the current view context from the route values, this would be set in the UmbracoViewPage or in // the UmbracoPageResult if POSTing to an MVC controller but rendering in Webforms if (http.Request.RequestContext.RouteData.DataTokens.ContainsKey(Mvc.Constants.DataTokenCurrentViewContext)) { viewContext = (ViewContext)http.Request.RequestContext.RouteData.DataTokens[Mvc.Constants.DataTokenCurrentViewContext]; } routeVals.DataTokens.Add("ParentActionViewContext", viewContext); var request = new RequestContext(http, routeVals); string output; using (var controller = new PartialViewMacroController(macro, content)) { controller.ViewData = viewContext.ViewData; controller.ControllerContext = new ControllerContext(request, controller); //call the action to render var result = controller.Index(); output = controller.RenderViewResultAsString(result); } return(new MacroContent { Text = output }); }
public static MacroContent ExecuteUserControl(MacroModel macro) { // add tilde for v4 defined macros if (string.IsNullOrEmpty(macro.MacroSource) == false && macro.MacroSource.StartsWith("~") == false) { macro.MacroSource = "~/" + macro.MacroSource; } var engine = new UserControlMacroEngine(); return(engine.Execute(macro)); }
/// <summary> /// Executes a macro of a given type. /// </summary> private Attempt <MacroContent> ExecuteProfileMacroWithErrorWrapper(MacroModel macro, string msgIn, Func <MacroContent> getMacroContent, Func <string> msgErr) { try { return(Attempt.Succeed(getMacroContent())); } catch (Exception e) { Exceptions.Add(e); _plogger.Warn <MacroRenderer>(e, "Failed {MsgIn}", msgIn); var macroErrorEventArgs = new MacroErrorEventArgs { Name = macro.Name, Alias = macro.Alias, MacroSource = macro.MacroSource, Exception = e, Behaviour = Current.Configs.Settings().Content.MacroErrorBehaviour }; OnError(macroErrorEventArgs); switch (macroErrorEventArgs.Behaviour) { case MacroErrorBehaviour.Inline: // do not throw, eat the exception, display the trace error message return(Attempt.Fail(new MacroContent { Text = msgErr() }, e)); case MacroErrorBehaviour.Silent: // do not throw, eat the exception, do not display anything return(Attempt.Fail(new MacroContent { Text = string.Empty }, e)); case MacroErrorBehaviour.Content: // do not throw, eat the exception, display the custom content return(Attempt.Fail(new MacroContent { Text = macroErrorEventArgs.Html ?? string.Empty }, e)); //case MacroErrorBehaviour.Throw: default: // see http://issues.umbraco.org/issue/U4-497 at the end // throw the original exception throw; } } }
public MacroContent Render(string macroAlias, IPublishedContent content, IDictionary <string, object> macroParams) { var m = _macroService.GetByAlias(macroAlias); if (m == null) { throw new InvalidOperationException("No macro found by alias " + macroAlias); } var page = new PublishedContentHashtableConverter(content); var macro = new MacroModel(m); UpdateMacroModelProperties(macro, macroParams); return(Render(macro, content, page.Elements)); }
public string Execute(MacroModel macro, INode node) { if (node == null) { return(string.Empty); } var umbCtx = _getUmbracoContext(); //NOTE: This is a bit of a nasty hack to check if the INode is actually already based on an IPublishedContent // (will be the case when using LegacyConvertedNode ) return(Execute(macro, (node is IPublishedContent) ? (IPublishedContent)node : umbCtx.ContentCache.GetById(node.Id))); }
// stores macro content into the cache private void AddMacroContentToCache(MacroModel model, MacroContent macroContent) { // only if cache is enabled if (_umbracoContextAccessor.UmbracoContext.InPreviewMode || model.CacheDuration <= 0) { return; } // just make sure... if (macroContent == null) { return; } // do not cache if it should cache by member and there's not member if (model.CacheByMember) { var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); var member = Core.Security.MembershipProviderExtensions.GetCurrentUser(provider); var key = member?.ProviderUserKey; if (key == null) { return; } } // this is legacy and I'm not sure what exactly it is supposed to do if (macroContent.Control != null) { macroContent.ControlId = macroContent.Control.ID; } // remember when we cache the content macroContent.Date = DateTime.Now; var cache = _appCaches.RuntimeCache; cache.Insert( CacheKeys.MacroContentCacheKey + model.CacheIdentifier, () => macroContent, new TimeSpan(0, 0, model.CacheDuration), priority: CacheItemPriority.NotRemovable ); _plogger.Debug <MacroRenderer>("Macro content saved to cache '{MacroCacheId}'", model.CacheIdentifier); }
// gets this macro content from the cache // ensuring that it is appropriate to use the cache private MacroContent GetMacroContentFromCache(MacroModel model) { // only if cache is enabled if (_umbracoContextAccessor.UmbracoContext.InPreviewMode || model.CacheDuration <= 0) { return(null); } var cache = _appCaches.RuntimeCache; var macroContent = cache.GetCacheItem <MacroContent>(CacheKeys.MacroContentCacheKey + model.CacheIdentifier); if (macroContent == null) { return(null); } _plogger.Debug <MacroRenderer>("Macro content loaded from cache '{MacroCacheId}'", model.CacheIdentifier); // ensure that the source has not changed // note: does not handle dependencies, and never has var macroSource = GetMacroFile(model); // null if macro is not file-based if (macroSource != null) { if (macroSource.Exists == false) { _plogger.Debug <MacroRenderer>("Macro source does not exist anymore, ignore cache."); return(null); } if (macroContent.Date < macroSource.LastWriteTime) { _plogger.Debug <MacroRenderer>("Macro source has changed, ignore cache."); return(null); } } // this is legacy and I'm not sure what exactly it is supposed to do if (macroContent.Control != null) { macroContent.Control.ID = macroContent.ControlId; } return(macroContent); }
// gets the macro source file name // null if the macro is not file-based internal static string GetMacroFileName(MacroModel model) { string filename; switch (model.MacroType) { case MacroTypes.PartialView: filename = model.MacroSource; // partial views are saved with their full virtual path break; default: // not file-based, or not supported filename = null; break; } return(filename); }
public static string GetRenderedMacro(int macroId, Hashtable elements, Hashtable attributes, int pageId, IMacroService macroService, IProfilingLogger plogger) { var m = macroService.GetById(macroId); if (m == null) { return(string.Empty); } var model = new MacroModel(m); // get as text, will render the control if any var renderer = new MacroRenderer(plogger); var macroContent = renderer.Render(model, elements, pageId); var text = macroContent.GetAsText(); // remove hrefs text = HrefRegex.Replace(text, match => "href=\"javascript:void(0)\""); return(text); }
// gets the macro source file // null if macro is not file-based private static FileInfo GetMacroFile(MacroModel model) { var filename = GetMacroFileName(model); if (filename == null) { return(null); } var mapped = IOHelper.MapPath(filename); if (mapped == null) { return(null); } var file = new FileInfo(mapped); return(file.Exists ? file : null); }
// gets this macro content cache identifier private string GetContentCacheIdentifier(MacroModel model, int pageId, string cultureName) { var id = new StringBuilder(); var alias = model.Alias; id.AppendFormat("{0}-", alias); //always add current culture to the key to allow variants to have different cache results if (!string.IsNullOrEmpty(cultureName)) { // are there any unusual culture formats we'd need to handle? id.AppendFormat("{0}-", cultureName); } if (model.CacheByPage) { id.AppendFormat("{0}-", pageId); } if (model.CacheByMember) { object key = 0; if (_umbracoContextAccessor.UmbracoContext.HttpContext?.User?.Identity?.IsAuthenticated ?? false) { //ugh, membershipproviders :( var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); var member = Core.Security.MembershipProviderExtensions.GetCurrentUser(provider); key = member?.ProviderUserKey ?? 0; } id.AppendFormat("m{0}-", key); } foreach (var value in model.Properties.Select(x => x.Value)) { id.AppendFormat("{0}-", value.Length <= 255 ? value : value.Substring(0, 255)); } return(id.ToString()); }
/// <summary> /// Executes a macro. /// </summary> /// <remarks>Returns an attempt that is successful if the macro ran successfully. If the macro failed /// to run properly, the attempt fails, though it may contain a content. But for instance that content /// should not be cached. In that case the attempt may also contain an exception.</remarks> private Attempt <MacroContent> ExecuteMacroOfType(MacroModel model) { // ensure that we are running against a published node (ie available in XML) // that may not be the case if the macro is embedded in a RTE of an unpublished document if (UmbracoContext.Current.PublishedRequest == null || UmbracoContext.Current.PublishedRequest.HasPublishedContent == false) { return(Attempt.Fail(new MacroContent { Text = "[macro]" })); } var textService = Current.Services.TextService; switch (model.MacroType) { case MacroTypes.PartialView: return(ExecuteMacroWithErrorWrapper(model, $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", "Executed PartialView.", () => ExecutePartialView(model), () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource }))); case MacroTypes.UserControl: return(ExecuteMacroWithErrorWrapper(model, $"Loading UserControl: MacroSource=\"{model.MacroSource}\".", "Loaded UserControl.", () => ExecuteUserControl(model), () => textService.Localize("errors/macroErrorLoadingUsercontrol", new[] { model.MacroSource }))); //case MacroTypes.Script: default: return(ExecuteMacroWithErrorWrapper(model, $"Execute macro with unsupported type \"{model.MacroType}\".", "Executed.", () => { throw new Exception("Unsupported macro type."); }, () => textService.Localize("errors/macroErrorUnsupportedType"))); } }
/// <summary> /// Executes a macro. /// </summary> /// <remarks>Returns an attempt that is successful if the macro ran successfully. If the macro failed /// to run properly, the attempt fails, though it may contain a content. But for instance that content /// should not be cached. In that case the attempt may also contain an exception.</remarks> private Attempt <MacroContent> ExecuteMacroOfType(MacroModel model, IPublishedContent content) { if (model == null) { throw new ArgumentNullException(nameof(model)); } // ensure that we are running against a published node (ie available in XML) // that may not be the case if the macro is embedded in a RTE of an unpublished document if (content == null) { return(Attempt.Fail(new MacroContent { Text = "[macro failed (no content)]" })); } var textService = _textService; switch (model.MacroType) { case MacroTypes.PartialView: return(ExecuteMacroWithErrorWrapper(model, $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", "Executed PartialView.", () => ExecutePartialView(model, content), () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource }))); default: return(ExecuteMacroWithErrorWrapper(model, $"Execute macro with unsupported type \"{model.MacroType}\".", "Executed.", () => { throw new Exception("Unsupported macro type."); }, () => textService.Localize("errors/macroErrorUnsupportedType"))); } }
public PartialViewMacroController(MacroModel macro, IPublishedContent content) { _macro = macro; _content = content; }
// still, this is ugly. The macro should have a Content property // referring to IPublishedContent we're rendering the macro against, // this is all soooo convoluted ;-( public MacroContent Render(MacroModel macro, Hashtable pageElements, int pageId, Hashtable attributes) { UpdateMacroModelProperties(macro, attributes); return(Render(macro, pageElements, pageId)); }
/// <summary> /// Renders a PartialView Macro. /// </summary> /// <returns>The text output of the macro execution.</returns> private MacroContent ExecutePartialView(MacroModel macro, IPublishedContent content) { var engine = new PartialViewMacroEngine(); return(engine.Execute(macro, content)); }
public static string MacroContentByHttp(int pageId, Guid pageVersion, Hashtable attributes, IMacroService macroService) { // though... we only support FullTrust now? if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted) { return("<span style='color: red'>Cannot render macro content in the rich text editor when the application is running in a Partial Trust environment</span>"); } var tempAlias = attributes["macroalias"]?.ToString() ?? attributes["macroAlias"].ToString(); var m = macroService.GetByAlias(tempAlias); if (m == null) { return(string.Empty); } var macro = new MacroModel(m); if (macro.RenderInEditor == false) { return(ShowNoMacroContent(macro)); } var querystring = $"umbPageId={pageId}&umbVersionId={pageVersion}"; var ide = attributes.GetEnumerator(); while (ide.MoveNext()) { querystring += $"&umb_{ide.Key}={HttpContext.Current.Server.UrlEncode((ide.Value ?? String.Empty).ToString())}"; } // create a new 'HttpWebRequest' object to the mentioned URL. var useSsl = Current.Configs.Global().UseHttps; var protocol = useSsl ? "https" : "http"; var currentRequest = HttpContext.Current.Request; var serverVars = currentRequest.ServerVariables; var umbracoDir = IOHelper.ResolveUrl(SystemDirectories.Umbraco); var url = $"{protocol}://{serverVars["SERVER_NAME"]}:{serverVars["SERVER_PORT"]}{umbracoDir}/macroResultWrapper.aspx?{querystring}"; var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url); // allows for validation of SSL conversations (to bypass SSL errors in debug mode!) ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate; // propagate the user's context // TODO: this is the worst thing ever. // also will not work if people decide to put their own custom auth system in place. var inCookie = currentRequest.Cookies[Current.Configs.Settings().Security.AuthCookieName]; if (inCookie == null) { throw new NullReferenceException("No auth cookie found"); } var cookie = new Cookie(inCookie.Name, inCookie.Value, inCookie.Path, serverVars["SERVER_NAME"]); myHttpWebRequest.CookieContainer = new CookieContainer(); myHttpWebRequest.CookieContainer.Add(cookie); // assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable. HttpWebResponse myHttpWebResponse = null; var text = string.Empty; try { myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse(); if (myHttpWebResponse.StatusCode == HttpStatusCode.OK) { var streamResponse = myHttpWebResponse.GetResponseStream(); if (streamResponse == null) { throw new Exception("Internal error, no response stream."); } var streamRead = new StreamReader(streamResponse); var readBuff = new char[256]; var count = streamRead.Read(readBuff, 0, 256); while (count > 0) { var outputData = new string(readBuff, 0, count); text += outputData; count = streamRead.Read(readBuff, 0, 256); } streamResponse.Close(); streamRead.Close(); // find the content of a form const string grabStart = "<!-- grab start -->"; const string grabEnd = "<!-- grab end -->"; var grabStartPos = text.InvariantIndexOf(grabStart) + grabStart.Length; var grabEndPos = text.InvariantIndexOf(grabEnd) - grabStartPos; text = text.Substring(grabStartPos, grabEndPos); } else { text = ShowNoMacroContent(macro); } } catch (Exception) { text = ShowNoMacroContent(macro); } finally { // release the HttpWebResponse Resource. myHttpWebResponse?.Close(); } return(text.Replace("\n", string.Empty).Replace("\r", string.Empty)); }
private static string ShowNoMacroContent(MacroModel model) { var name = HttpUtility.HtmlEncode(model.Name); // safe return($"<span style=\"color: green\"><strong>{name}</strong><br />No macro content available for WYSIWYG editing</span>"); }
public PartialViewMacroController(UmbracoContext umbracoContext, MacroModel macro, INode currentPage) { _umbracoContext = umbracoContext; _macro = macro; _currentPage = currentPage; }
public string Execute(MacroModel macro, INode node) { var umbCtx = _getUmbracoContext(); return(Execute(macro, umbCtx.ContentCache.GetById(node.Id))); }