public ArticleDto ParseArticleRawText(string sourceText, string relativePath) { ArticleDto article = new ArticleDto(@"Articles\MyArticle.md", this); article.Content = @"#My Article Some important text"; article.Attributes = new Dictionary<string, IList<string>>(); article.Attributes.Add("Title", new List<string>()); article.Attributes["Title"].Add("My Great Article"); return article; }
public void LoadArticleBadDate() { string input = @"--- Title:MyArticle Date:XXXXXX --- ##The internals Some article content... "; MarkdownArticleProcessor ar = new MarkdownArticleProcessor(getAttributeSet()); ArticleDto output = ar.ParseArticleRawText(input, "TestArticle"); Assert.AreEqual(1, output.Attributes.Count); Assert.AreEqual("MyArticle", output.Attributes["Title"][0]); }
public void LoadArticlePositive() { string input = @"--- Title:MyArticle Date:2015-01-02 --- ##The internals Some article content... "; MarkdownArticleProcessor ar = new MarkdownArticleProcessor(getAttributeSet()); ArticleDto output = ar.ParseArticleRawText(input, "TestArticle"); Assert.AreEqual(2, output.Attributes.Count); Assert.AreEqual("MyArticle", output.Attributes["Title"][0]); Assert.AreEqual("2015-01-02", output.Attributes["Date"][0]); Assert.AreEqual("##The internals\r\nSome article content...\r\n\r\n", output.Content); }
/// <summary> /// Determines the correct output path for an article. /// </summary> /// <param name="article">The article whose path is to be calculated. This object is not updated.</param> /// <returns>The new article output path.</returns> public static string CalculateArticlePath(ArticleDto article) { if(article.Attributes.Any(x => x.Key == "Path" && x.Value.Count > 0)) { string path = article.Attributes.First(x => x.Key == "Path").Value[0]; if(path.IndexOfAny(Path.GetInvalidPathChars()) > -1) { Logger.LogWarning("Article " + article.SourceFileRelativePath + " specified invalid path " + path + " which was ignored."); } else { return path.Replace('\\', '/'); } } string noExtPath = Path.GetDirectoryName(article.SourceFileRelativePath) + "/" + Path.GetFileNameWithoutExtension(article.SourceFileRelativePath); noExtPath = noExtPath.Replace('\\', '/'); noExtPath += ".html"; return noExtPath; }
/// <summary> /// Determines the correct output path for an article. /// </summary> /// <param name="article">The article whose path is to be calculated. This object is not updated.</param> /// <returns>The new article output path.</returns> public static string CalculateArticlePath(ArticleDto article) { if (article.Attributes.Any(x => x.Key == "Path" && x.Value.Count > 0)) { string path = article.Attributes.First(x => x.Key == "Path").Value[0]; if (path.IndexOfAny(Path.GetInvalidPathChars()) > -1) { Logger.LogWarning("Article " + article.SourceFileRelativePath + " specified invalid path " + path + " which was ignored."); } else { return(path.Replace('\\', '/')); } } string noExtPath = Path.GetDirectoryName(article.SourceFileRelativePath) + "/" + Path.GetFileNameWithoutExtension(article.SourceFileRelativePath); noExtPath = noExtPath.Replace('\\', '/'); noExtPath += ".html"; return(noExtPath); }
public void ExpandArticleMacrosTest() { MarkdownArticleProcessor ar = new MarkdownArticleProcessor(getAttributeSet()); ArticleDto a = ar.ParseArticleRawText( @"--- Title:Xbox Controller Repair Date:2014-08-02 Category:Electronics Tags:Gaming,Electronics --- ##The internals ![:youtube 800 600](https://www.youtube.com/embed/Di5AT4MI6BY) ![:youtube 512 400](http://www.youtube.com/embed/Di5AT4MI6BY)", "Some\\File.md"); MacroInvocation mi = ar.LocateMacrosInContent(a)[0]; MarkdownMacro mdm = new MarkdownMacro("youtube", "<iframe width=\"%p1%\" height=\"%p2%\" src=\"%v1%\" frameborder=\"0\"></iframe>"); ArticleDto[] allArticles = new ArticleDto[1]; allArticles[0] = a; string finalText = mdm.Expand(mi.Parameters, mi.Values, a, allArticles); Assert.AreEqual( "<iframe width=\"800\" height=\"600\" src=\"https://www.youtube.com/embed/Di5AT4MI6BY\" frameborder=\"0\"></iframe>", finalText.Trim()); }
public void ExpandMacroTest() { MarkdownArticleProcessor ar = new MarkdownArticleProcessor(getAttributeSet()); ArticleDto a = ar.ParseArticleRawText( @"--- Title:Xbox Controller Repair Date:2014-08-02 Category:Electronics Tags:Gaming,Electronics --- ![:youtube 800 600](https://www.youtube.com/embed/Di5AT4MI6BY)", "Some\\File.md"); MacroInvocation mi = ar.LocateMacrosInContent(a)[0]; MarkdownMacro mdm = new MarkdownMacro("youtube", "<iframe width=\"%p1%\" height=\"%p2%\" src=\"%v1%\" frameborder=\"0\"></iframe>"); ArticleDto[] allArticles = new ArticleDto[1]; allArticles[0] = a; string finalText = mdm.Expand(mi.Parameters, mi.Values, a, allArticles); Assert.AreEqual( "<iframe width=\"800\" height=\"600\" src=\"https://www.youtube.com/embed/Di5AT4MI6BY\" frameborder=\"0\"></iframe>", finalText.Trim()); }
public void LocateMacro() { MarkdownArticleProcessor ar = new MarkdownArticleProcessor(getAttributeSet()); ArticleDto a = ar.ParseArticleRawText( @"--- Title:Xbox Controller Repair Date:2014-08-02 Category:Electronics Tags:Gaming,Electronics --- ##The internals ![:youtube 800 600](https://www.youtube.com/embed/Di5AT4MI6BY) ![:youtube 512 400](http://www.youtube.com/embed/Di5AT4MI6BY) ![My image caption](imgs/someImageNotAMacro.jpg", "Some\\File.md"); MacroInvocation mi = ar.LocateMacrosInContent(a)[0]; MarkdownMacro mdm = new MarkdownMacro("youtube", "<iframe width=\"%p1%\" height=\"%p2%\" src=\"%v1%\" frameborder=\"0\"></iframe>"); ArticleDto[] allArticles = new ArticleDto[1]; IList<MacroInvocation> mis = ar.LocateMacrosInContent(a); Assert.AreEqual(2, mis.Count); Assert.AreEqual(17, mis[0].StartingCharIndex); Assert.AreEqual(79, mis[0].EndingCharIndex); }
public void EscapedArticleMacrosTest() { MarkdownArticleProcessor ar = new MarkdownArticleProcessor(getAttributeSet()); ArticleDto a = ar.ParseArticleRawText( @"--- Title:Xbox Controller Repair Date:2014-08-02 Category:Electronics Tags:Gaming,Electronics --- ##The internals ![:youtube 800 600](https://www.youtube.com/embed/Di5AT4MI6BY) \![:noMatchMacro parm1]", "Some\\File.md"); MacroInvocation mi = ar.LocateMacrosInContent(a)[0]; MarkdownMacro mdm = new MarkdownMacro("youtube", "<iframe width=\"%p1%\" height=\"%p2%\" src=\"%v1%\" frameborder=\"0\"></iframe>"); ArticleDto[] allArticles = new ArticleDto[1]; allArticles[0] = a; IList<MacroInvocation> miList = MarkdownUtil.LocateMarkdownMacros(a.Content); Assert.AreEqual(1, miList.Count); Assert.AreEqual("youtube", miList[0].MacroName); }
public IList<MacroInvocation> LocateMacrosInContent(ArticleDto article) { return new List<MacroInvocation>(); }
public void FinaliseArticleHtml(ArticleDto article) { article.Content = @"<html><heade><title>My Great Aticle</title></head><body><h1>My Article</h1> Some important text</body></html>"; }
/// <summary> /// Top level function of the site compilation/conversion process /// </summary> public bool ConvertArticles() { List <ArticleDto> articles = new List <ArticleDto>(); IList <ITemplateProcessor> templateProcessors; List <TemplateDto> templates = new List <TemplateDto>(); List <IVirtualArticleGeneratorLoader> virtualArticleGenLoaders = new List <IVirtualArticleGeneratorLoader>(); List <IVirtualArticleGenerator> articleGenerators = new List <IVirtualArticleGenerator>(); virtualArticleGenLoaders.Add(new XslMarkdownArticleGeneratorLoader(validAttributes)); virtualArticleGenLoaders.Add(new XslArticleGeneratorLoader(validAttributes)); string[] articlePaths; templateProcessors = new List <ITemplateProcessor>(); templateProcessors.Add(new MarkdownTemplateProcessor(validAttributes)); if (validAttributes == null) { return(false); } //Load articles //At this point in time, we only have one type of article reader... IArticleProcessor articleProcessor = new MarkdownArticleProcessor(validAttributes); articlePaths = Directory.GetFiles(paths.ArticlesRootDir, "*" + articleProcessor.PrimaryFileExtension); foreach (string path in articlePaths) { string relativePath = path.Substring(paths.ArticlesRootDir.Length); ArticleDto art = articleProcessor.ParseArticleRawText(File.ReadAllText(path), relativePath); if (art != null) { articles.Add(art); } } //Load templates foreach (ITemplateProcessor tp in templateProcessors) { string[] templatePaths = Directory.GetFiles(paths.TemplatesRootDir, "*" + tp.PrimaryFileExtension); foreach (string path in templatePaths) { string relativePath = path.Substring(paths.ArticlesRootDir.Length); TemplateDto template = tp.ParseTemplateRawText(File.ReadAllText(path), relativePath, Path.GetFileNameWithoutExtension(relativePath)); if (template != null) { template.TemplateProcessor = tp; templates.Add(template); } } } //Load virtual article generators if (!string.IsNullOrEmpty(paths.VirtualArticlesRootDir)) { foreach (IVirtualArticleGeneratorLoader loader in virtualArticleGenLoaders) { string[] generatorPaths = Directory.GetFiles(paths.VirtualArticlesRootDir, "*" + loader.PrimaryFileExtension); foreach (string path in generatorPaths) { if (path.ToLower().EndsWith(loader.PrimaryFileExtension.ToLower())) { IVirtualArticleGenerator gen = loader.ParseGeneratorFromFile(path); if (gen != null) { articleGenerators.Add(gen); } } } } } applyOutputPaths(articles); createVirtualArticles(articles, articleGenerators); applyArticleTemplates(articles, templates); applyOutputPaths(articles); applyMacros(articles); transformArticles(articles); outputArticles(articles); return(true); }
/// <summary> /// Expands all macros found in an article's content. Will also expand macros added by macros. /// </summary> /// <param name="a">The article in question. Content will be updated.</param> /// <param name="articles">List of all known articles.</param> public void ProcessArticleMacros(ArticleDto a, IList <ArticleDto> articles) { IList <MacroInvocation> invokes = null; //This loop is just to allow an escape from an infinite macro expansion cycle for (int cycle = 0; cycle < 20; cycle++) { invokes = a.ArticleProcessorUsed.LocateMacrosInContent(a); invokes = new List <MacroInvocation>(invokes.OrderByDescending(x => x.StartingCharIndex)); foreach (MacroInvocation mi in invokes) { IMacro mac = null; foreach (var provider in macroProviders) { mac = provider.TryLoadMacro(mi.MacroName); if (mac != null) { break; } } if (mac == null) { Logger.LogWarning("Macro " + mi.MacroName + " not found."); continue; } //TODO: This is not the most efficient way to handle progressive replace. Should use stringbuilders. string pre = a.Content.Substring(0, mi.StartingCharIndex); string post = a.Content.Substring(mi.EndingCharIndex); a.Content = pre + mac.Expand(mi.Parameters, mi.Values, a, articles) + post; } if (invokes.Count == 0) { break; } } if (invokes.Count > 0) { Logger.LogWarning("Article " + a.SourceFileRelativePath + " still had macros to expand after many passes. Check for macros recurively including each other."); } if (a.Template != null) { for (int cycle = 0; cycle < 20; cycle++) { invokes = a.Template.TemplateProcessor.LocateMacrosInArticleTemplateContent(a); invokes = new List <MacroInvocation> (invokes.OrderByDescending(x => x.StartingCharIndex)); foreach (MacroInvocation mi in invokes) { IMacro mac = null; foreach (var provider in macroProviders) { mac = provider.TryLoadMacro(mi.MacroName); if (mac != null) { break; } } if (mac == null) { Logger.LogWarning("Macro " + mi.MacroName + " not found."); continue; } //TODO: This is not the most efficient way to handle progressive replace. Should use stringbuilders. string pre = a.TemplateContent.Substring(0, mi.StartingCharIndex); string post = a.TemplateContent.Substring(mi.EndingCharIndex); a.TemplateContent = pre + mac.Expand(mi.Parameters, mi.Values, a, articles) + post; } } if (invokes.Count > 0) { Logger.LogWarning("Article template for " + a.SourceFileRelativePath + " still had macros to expand after many passes. Check for macros recurively including each other."); } } }
/// <summary> /// Expands all macros found in an article's content. Will also expand macros added by macros. /// </summary> /// <param name="a">The article in question. Content will be updated.</param> /// <param name="articles">List of all known articles.</param> public void ProcessArticleMacros(ArticleDto a, IList<ArticleDto> articles) { IList<MacroInvocation> invokes = null; //This loop is just to allow an escape from an infinite macro expansion cycle for(int cycle = 0; cycle < 20; cycle++) { invokes = a.ArticleProcessorUsed.LocateMacrosInContent(a); invokes = new List<MacroInvocation>(invokes.OrderByDescending(x => x.StartingCharIndex)); foreach(MacroInvocation mi in invokes) { IMacro mac = null; foreach(var provider in macroProviders) { mac = provider.TryLoadMacro(mi.MacroName); if (mac != null) break; } if(mac == null) { Logger.LogWarning("Macro " + mi.MacroName + " not found."); continue; } //TODO: This is not the most efficient way to handle progressive replace. Should use stringbuilders. string pre = a.Content.Substring(0, mi.StartingCharIndex); string post = a.Content.Substring(mi.EndingCharIndex); a.Content = pre + mac.Expand(mi.Parameters, mi.Values, a, articles) + post; } if (invokes.Count == 0) break; } if (invokes.Count > 0) Logger.LogWarning("Article " + a.SourceFileRelativePath + " still had macros to expand after many passes. Check for macros recurively including each other."); if(a.Template != null) { for(int cycle = 0; cycle < 20; cycle++) { invokes = a.Template.TemplateProcessor.LocateMacrosInArticleTemplateContent(a); invokes = new List<MacroInvocation> (invokes.OrderByDescending(x => x.StartingCharIndex)); foreach (MacroInvocation mi in invokes) { IMacro mac = null; foreach (var provider in macroProviders) { mac = provider.TryLoadMacro(mi.MacroName); if (mac != null) break; } if (mac == null) { Logger.LogWarning("Macro " + mi.MacroName + " not found."); continue; } //TODO: This is not the most efficient way to handle progressive replace. Should use stringbuilders. string pre = a.TemplateContent.Substring(0, mi.StartingCharIndex); string post = a.TemplateContent.Substring(mi.EndingCharIndex); a.TemplateContent = pre + mac.Expand(mi.Parameters, mi.Values, a, articles) + post; } } if (invokes.Count > 0) Logger.LogWarning("Article template for " + a.SourceFileRelativePath + " still had macros to expand after many passes. Check for macros recurively including each other."); } }
public IList <MacroInvocation> LocateMacrosInContent(ArticleDto article) { return(new List <MacroInvocation>()); }
public void BasicPathCreation() { ArticleDto article = new ArticleDto(@"Articles\MyArticle.md", new MockArticleProcessor()); string calcPath = AttributeTranformations.CalculateArticlePath(article); Assert.AreEqual("Articles/MyArticle.html", calcPath); }