Display infobar for blocked relative image path (#1695)

* Updated Parsing extension to show infobar when relative URL isblocked and updated corresponding tests

* Updated Controller to display infobar when html img tag is embedded in markdown
This commit is contained in:
Divyansh Srivastava
2020-03-26 15:17:28 -07:00
committed by GitHub
parent 15cefc664a
commit c2e219b446
6 changed files with 114 additions and 132 deletions

View File

@@ -1,117 +1,103 @@
// Copyright (c) Microsoft Corporation // Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.IO; using System.IO;
using Markdig; using Markdig;
using Markdig.Extensions.Figures; using Markdig.Extensions.Figures;
using Markdig.Extensions.Tables; using Markdig.Extensions.Tables;
using Markdig.Renderers; using Markdig.Renderers;
using Markdig.Renderers.Html; using Markdig.Renderers.Html;
using Markdig.Syntax; using Markdig.Syntax;
using Markdig.Syntax.Inlines; using Markdig.Syntax.Inlines;
namespace MarkdownPreviewHandler namespace MarkdownPreviewHandler
{ {
/// <summary> /// <summary>
/// Callback if extension blocks external images. /// Callback if extension blocks external images.
/// </summary> /// </summary>
public delegate void ImagesBlockedCallBack(); public delegate void ImagesBlockedCallBack();
/// <summary> /// <summary>
/// Markdig Extension to process html nodes in markdown AST. /// Markdig Extension to process html nodes in markdown AST.
/// </summary> /// </summary>
public class HTMLParsingExtension : IMarkdownExtension public class HTMLParsingExtension : IMarkdownExtension
{ {
/// <summary> /// <summary>
/// Callback if extension blocks external images. /// Callback if extension blocks external images.
/// </summary> /// </summary>
private readonly ImagesBlockedCallBack imagesBlockedCallBack; private readonly ImagesBlockedCallBack imagesBlockedCallBack;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HTMLParsingExtension"/> class. /// Initializes a new instance of the <see cref="HTMLParsingExtension"/> class.
/// </summary> /// </summary>
/// <param name="imagesBlockedCallBack">Callback function if image is blocked by extension.</param> /// <param name="imagesBlockedCallBack">Callback function if image is blocked by extension.</param>
/// <param name="baseUrl">Absolute path of markdown file.</param> /// <param name="baseUrl">Absolute path of markdown file.</param>
public HTMLParsingExtension(ImagesBlockedCallBack imagesBlockedCallBack, string baseUrl = "") public HTMLParsingExtension(ImagesBlockedCallBack imagesBlockedCallBack, string baseUrl = "")
{ {
this.imagesBlockedCallBack = imagesBlockedCallBack; this.imagesBlockedCallBack = imagesBlockedCallBack;
this.BaseUrl = baseUrl; this.BaseUrl = baseUrl;
} }
/// <summary> /// <summary>
/// Gets or sets path to directory containing markdown file. /// Gets or sets path to directory containing markdown file.
/// </summary> /// </summary>
public string BaseUrl { get; set; } public string BaseUrl { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public void Setup(MarkdownPipelineBuilder pipeline) public void Setup(MarkdownPipelineBuilder pipeline)
{ {
// Make sure we don't have a delegate twice // Make sure we don't have a delegate twice
pipeline.DocumentProcessed -= this.PipelineOnDocumentProcessed; pipeline.DocumentProcessed -= this.PipelineOnDocumentProcessed;
pipeline.DocumentProcessed += this.PipelineOnDocumentProcessed; pipeline.DocumentProcessed += this.PipelineOnDocumentProcessed;
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{ {
} }
/// <summary> /// <summary>
/// Process nodes in markdown AST. /// Process nodes in markdown AST.
/// </summary> /// </summary>
/// <param name="document">Markdown Document.</param> /// <param name="document">Markdown Document.</param>
public void PipelineOnDocumentProcessed(MarkdownDocument document) public void PipelineOnDocumentProcessed(MarkdownDocument document)
{ {
foreach (var node in document.Descendants()) foreach (var node in document.Descendants())
{ {
if (node is Block) if (node is Block)
{ {
if (node is Table) if (node is Table)
{ {
node.GetAttributes().AddClass("table table-striped table-bordered"); node.GetAttributes().AddClass("table table-striped table-bordered");
} }
else if (node is QuoteBlock) else if (node is QuoteBlock)
{ {
node.GetAttributes().AddClass("blockquote"); node.GetAttributes().AddClass("blockquote");
} }
else if (node is Figure) else if (node is Figure)
{ {
node.GetAttributes().AddClass("figure"); node.GetAttributes().AddClass("figure");
} }
else if (node is FigureCaption) else if (node is FigureCaption)
{ {
node.GetAttributes().AddClass("figure-caption"); node.GetAttributes().AddClass("figure-caption");
} }
} }
else if (node is Inline) else if (node is Inline)
{ {
if (node is LinkInline link) if (node is LinkInline link)
{ {
if (link.IsImage) if (link.IsImage)
{ {
link.Url = "#";
link.GetAttributes().AddClass("img-fluid"); link.GetAttributes().AddClass("img-fluid");
} this.imagesBlockedCallBack();
}
if (!Uri.TryCreate(link.Url, UriKind.Absolute, out _)) }
{ }
link.Url = link.Url.TrimStart('/', '\\'); }
this.BaseUrl = this.BaseUrl.TrimEnd('/', '\\'); }
Uri uriLink = new Uri(Path.Combine(this.BaseUrl, link.Url)); }
link.Url = uriLink.ToString(); }
}
else
{
if (link.IsImage)
{
link.Url = "#";
this.imagesBlockedCallBack();
}
}
}
}
}
}
}
}

View File

@@ -8,6 +8,7 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using Common; using Common;
using Markdig; using Markdig;
@@ -83,6 +84,12 @@ namespace MarkdownPreviewHandler
string fileText = File.ReadAllText(filePath); string fileText = File.ReadAllText(filePath);
this.extension.BaseUrl = Path.GetDirectoryName(filePath); this.extension.BaseUrl = Path.GetDirectoryName(filePath);
Regex rgx = new Regex(@"<[ ]*img.*>");
if (rgx.IsMatch(fileText))
{
this.infoBarDisplayed = true;
}
MarkdownPipeline pipeline = this.pipelineBuilder.Build(); MarkdownPipeline pipeline = this.pipelineBuilder.Build();
string parsedMarkdown = Markdown.ToHtml(fileText, pipeline); string parsedMarkdown = Markdown.ToHtml(fileText, pipeline);
sb.AppendFormat("{0}{1}{2}", this.htmlHeader, parsedMarkdown, this.htmlFooter); sb.AppendFormat("{0}{1}{2}", this.htmlHeader, parsedMarkdown, this.htmlFooter);

View File

@@ -47,7 +47,7 @@ namespace PreviewPaneUnitTests
} }
[TestMethod] [TestMethod]
public void Extension_UpdatesFigureClassAndRelativeUrltoAbsolute_WhenUsed() public void Extension_UpdatesFigureClassAndBlocksRelativeUrl_WhenUsed()
{ {
// arrange // arrange
String mdString = "![text](a.jpg \"Figure\")"; String mdString = "![text](a.jpg \"Figure\")";
@@ -58,37 +58,7 @@ namespace PreviewPaneUnitTests
String html = Markdown.ToHtml(mdString, markdownPipeline); String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert // Assert
Assert.AreEqual(html, "<p><img src=\"file:///C:/Users/a.jpg\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n"); Assert.AreEqual(html, "<p><img src=\"#\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
}
[TestMethod]
public void Extension_CreatesCorrectAbsoluteLinkByTrimmingForwardSlash_WhenUsed()
{
// arrange
String mdString = "![text](\\document\\a.jpg \"Figure\")";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:\\Users\\");
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<p><img src=\"file:///C:/Users/document/a.jpg\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
}
[TestMethod]
public void Extension_CreatesCorrectAbsoluteLinkByTrimmingBackwardSlash_WhenUsed()
{
// arrange
String mdString = "![text](/document/a.jpg \"Figure\")";
HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(() => { }, "C:/Users/");
MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension);
// Act
String html = Markdown.ToHtml(mdString, markdownPipeline);
// Assert
Assert.AreEqual(html, "<p><img src=\"file:///C:/Users/document/a.jpg\" class=\"img-fluid\" alt=\"text\" title=\"Figure\" /></p>\n");
} }
[TestMethod] [TestMethod]

View File

@@ -0,0 +1,2 @@
## Something
<img src="./a.jpg" \>

View File

@@ -42,6 +42,20 @@ namespace PreviewPaneUnitTests
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox)); Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
} }
[TestMethod]
public void MarkdownPreviewHandlerControl__AddsInfoBarToFormIfHTMLImageTagIsPresent_WhenDoPreviewIsCalled()
{
// Arrange
MarkdownPreviewHandlerControl markdownPreviewHandlerControl = new MarkdownPreviewHandlerControl();
// Act
markdownPreviewHandlerControl.DoPreview<string>("HelperFiles/MarkdownWithHTMLImageTag.txt");
// Assert
Assert.AreEqual(markdownPreviewHandlerControl.Controls.Count, 2);
Assert.IsInstanceOfType(markdownPreviewHandlerControl.Controls[1], typeof(RichTextBox));
}
[TestMethod] [TestMethod]
public void MarkdownPreviewHandlerControl__DoesNotAddInfoBarToFormIfExternalImageLinkNotPresent_WhenDoPreviewIsCalled() public void MarkdownPreviewHandlerControl__DoesNotAddInfoBarToFormIfExternalImageLinkNotPresent_WhenDoPreviewIsCalled()
{ {

View File

@@ -116,6 +116,9 @@
<Content Include="HelperFiles\MarkdownWithExternalImage.txt"> <Content Include="HelperFiles\MarkdownWithExternalImage.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="HelperFiles\MarkdownWithHTMLImageTag.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="HelperFiles\MarkdownWithscript.txt"> <Content Include="HelperFiles\MarkdownWithscript.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>