//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 } }