//Constructor public Helper(string mailerDefPath, HttpContext ctx) { _ctx = ctx; //Current request context (needed because of the async nature of the processing) string props = IOHelper.ReadTextFromFile(mailerDefPath); //Get the Front Matter of the form action definition file _mailerProps = new SimpleYAMLParser(props); //Clone current request Form data CloneRequestData(); //Add extra info to the managed data AddExtraInfoToRequestData(); }
//Extracts Front-Matter from current file private void ProcessFrontMatter() { if (_FrontMatter != null) { return; } string strFM = string.Empty; bool cacheEnabled = Common.GetFieldValue("UseMDCaching", null, "1") == "1"; if (cacheEnabled) //If the file cache is enabled { strFM = HttpRuntime.Cache[this.FilePath + "_FM"] as string; //Try to read Front-Matter from cache if (!string.IsNullOrEmpty(strFM)) //If it in the cache, use it { _FrontMatter = new SimpleYAMLParser(strFM); return; } else { strFM = "---\r\n---"; //Re-set to an empty FrontMatter (if I use an empty string it would be reading contents from this for all the files without Front Matter } } //If cache is not enabled or the FM is not currently cached, read from contents //Default value (empty), prevents Content property from processing Front-Matter twice if it's still not read _FrontMatter = new SimpleYAMLParser(strFM); //Extract and remove YAML Front Matter EnsureContent(); Match fm = FRONT_MATTER_RE.Match(this._content); if (fm.Length > 0) //If there's front matter available { strFM = fm.Groups[0].Value; //Save front matter text _FrontMatter = new SimpleYAMLParser(strFM); } //Cache FM contents if caching is enabled if (cacheEnabled) { HttpRuntime.Cache.Insert(this.FilePath + "_FM", strFM, new CacheDependency(this.FilePath)); //Add FM to cache with dependency on the current MD/MDH file } }
//Extracts Front-Matter from current file private void ProcessFrontMatter() { if (_FrontMatter != null) { return; } //Non empty value, for caching string strFM = "---\r\n---"; strFM = GetFMFromCache(); if (!string.IsNullOrEmpty(strFM)) //If it in the cache, just use it { _FrontMatter = new SimpleYAMLParser(strFM); return; //Ready! } else { //If cache is not enabled or the FM is not currently cached, read it from the file content //Default value (empty FM, but no empty string), prevents the Content property from processing Front-Matter twice if it's not read yet _FrontMatter = new SimpleYAMLParser(strFM); } //Extract and remove YAML Front Matter EnsureContent(); //If it's a .yml file, wrap the full content as Front-Matter (.yml files don't neeed to have the FM delimiters, but I wan't to support them) if (this.FileExt == ".yml" && !_rawContent.StartsWith("---\r\n")) { _rawContent = "---\r\n" + _rawContent + "\r\n---"; } strFM = SimpleYAMLParser.GetFrontMatterFromContent(_rawContent); _FrontMatter = new SimpleYAMLParser(strFM); //Cache the final FM content (if caching is enabled) AddFMToCache(strFM); }
//Removes the front matter, if any, from the actual content of the file //and removes the extra empty lines at the beginning and the end private void RemoveFrontMatter() { _rawContent = SimpleYAMLParser.RemoveFrontMatterFromContent(_rawContent); }
/// <summary> /// Reads a template from cache if available. If not, reads it from disk. /// Substitutes the template fields such as {basefolder}, before caching the result /// </summary> /// <param name="filePath">Path to the template</param> /// <param name="ctx">The current request context (needed in in order to transform virtual paths)</param> /// <param name="isInclude">true to indicate that the current template is a fragment of other template, so that is excludes content and other fragments from processing</param> /// <returns>The text contents of the template</returns> /// <exception cref="System.Security.SecurityException">Thrown if the app has no read access to the requested file</exception> /// <exception cref="System.IO.FileNotFoundException">Thrown when the requested file does not exist</exception> private static string ReadTemplate(string templateVirtualPath, HttpContext ctx, List <string> cacheDependencies, List <string> graph = null, bool isInclude = false) { string templatePath = ctx.Server.MapPath(templateVirtualPath); string cachedContent = HttpRuntime.Cache[templatePath] as string; //Templates are always cached for performance reasons (no switch/parameter to disable it) if (string.IsNullOrEmpty(cachedContent)) { //Check for circular references if (graph != null) { //Check if current file is already on the graph //if it is, then we have a circular reference if (graph.Contains(templatePath)) { throw new CircularReferenceException(String.Format("Template not valid!\nThe file '{0}' is a circular reference: {1}", templateVirtualPath, String.Join(" >\n ", graph.ToArray()))); } graph.Add(templatePath); //Add current include to the graph to track circular references } var templateContents = IOHelper.ReadTextFromFile(templatePath); //Read template contents from disk //Add current file as cache dependency (the read process will add the fragments if needed) if (!cacheDependencies.Contains(templatePath)) { cacheDependencies.Add(templatePath); } string phValue; //The value to substitute the placeholder ///////////////////////////////////////////// // Process template inheritance (if any) ///////////////////////////////////////////// //Check if there's Front-Matter or not string strFM = SimpleYAMLParser.GetFrontMatterFromContent(templateContents); //If FM is present if (strFM != SimpleYAMLParser.EMPTY_FRONT_MATTER) { //Remove the FM templateContents = SimpleYAMLParser.RemoveFrontMatterFromContent(templateContents); //get the Layout field from the current template's FM SimpleYAMLParser yaml = new SimpleYAMLParser(strFM); string layout = yaml["layout"]; //If there's a layout specified, use it as the base layout for the current template if (!string.IsNullOrEmpty(layout)) { //Process the layout string parentTemplateVPath = VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(templateVirtualPath)) + "/" + layout;; string parentTemplateContent = ReadTemplate(parentTemplateVPath, ctx, cacheDependencies, graph); //Substitute the content field in the parent layout, with the current template's content templateContents = TemplatingHelper.ReplacePlaceHolder(parentTemplateContent, "content", templateContents); } } //////////////////////////////////////////// //Search for includes in the current file and substitute them, before substituting any other placeholder //////////////////////////////////////////// string[] includes = TemplatingHelper.GetAllPlaceHolderNames(templateContents, "", FILE_INCLUDES_PREFIX); //Substitute includes with their contents foreach (string include in includes) { string includeFileName = VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(templateVirtualPath)) + "/" + include.Substring(FILE_INCLUDES_PREFIX.Length); //The current template file folder + the include filename try { //Initialize graph to detect circular references List <string> newGraph; if (graph == null) { newGraph = new List <string>() { templatePath }; } else { newGraph = graph; } phValue = ReadTemplate(includeFileName, ctx, cacheDependencies, newGraph, true); //Insert the raw contents of the include (no processing!) } catch (CircularReferenceException crex) { throw crex; } catch //If it fails, simply do nothing and show the error { phValue = String.Format("<!-- Include file '{0}' not found -->", includeFileName); } templateContents = TemplatingHelper.ReplacePlaceHolder(templateContents, include, phValue); } if (!isInclude) { //After inserting all the "includes", check if there's a content placeholder present (mandatory) if (!TemplatingHelper.IsPlaceHolderPresent(templateContents, "content")) { throw new Exception("Invalid template '" + templateVirtualPath + "': The " + TemplatingHelper.GetPlaceholderName("content") + " placeholder must be present!"); } ////////////////////////////// //Replace template-specific fields ////////////////////////////// //Legacy "basefolder" placeholder (now "~/" it's recommended) templateContents = TemplatingHelper.ReplacePlaceHolder(templateContents, "basefolder", "~/"); //Template base folder templateContents = TemplatingHelper.ReplacePlaceHolder(templateContents, "templatebasefolder", VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(VirtualPathUtility.ToAbsolute(templateVirtualPath)))); //Transform virtual paths into absolute to the root paths (This is done only once per file if cached) templateContents = WebHelper.TransformVirtualPaths(templateContents); //If it's the main file, add result to cache with dependency on the file(s) //Templates are cached ALWAYS, and this is not dependent on the caching parameter (that one is only for MarkDown or MDH files) HttpRuntime.Cache.Insert(templatePath, templateContents, new CacheDependency(cacheDependencies.ToArray())); //Keep a list of template's dependencies to reuse when not reading from cache ctx.Application[templatePath] = cacheDependencies; } return(templateContents); //Return content } else { //Get dependencies for this template cacheDependencies.AddRange(ctx.Application[templatePath] as List <string>); return(cachedContent); //Return directly from cache } }