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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 132 deletions

View File

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

View File

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

View File

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

View File

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