25 Commits

Author SHA1 Message Date
unknown
6ab08dfbf7 Made syntax highlighting test more specific 2018-09-28 13:43:12 -06:00
unknown
035d825757 Updated unit test for error handling to be more specific, deleted unnecessary comments 2018-09-17 14:48:28 -06:00
unknown
147d4f4ac0 Copied syntax highlighting test to markdown from url unit test, cleaned up code 2018-09-13 12:02:27 -06:00
unknown
7ea26df373 Added unit test for syntax highlighting 2018-09-13 09:51:56 -06:00
unknown
9f7f1d63be Improved code efficiency, improved test for syntax highlighting 2018-09-05 10:54:59 -06:00
unknown
ffda180b16 Commented code for clarity 2018-09-03 12:18:55 -06:00
unknown
4d5b8a159e Added a unit test for error handling in MarkdownFromURLMacro 2018-09-03 10:58:35 -06:00
unknown
5090d681b3 Work towards a highlight.js unit test 2018-09-03 09:27:25 -06:00
unknown
ad3937e5db Added unit test for MarkdownFromURL 2018-09-03 08:40:46 -06:00
unknown
bb8417e57b Added a unit test for markdown rendering functionality 2018-08-22 14:15:25 -06:00
unknown
27dcf6d8bf Added error handling for a variety of errors. 2018-08-10 16:27:26 -06:00
unknown
804c460981 Deleted code meant for testing. 2018-08-08 18:20:10 -05:00
unknown
14e81297dd Seperated Markdown into two macros: Markdown and Markdown From URL 2018-08-08 18:14:00 -05:00
unknown
8a4b73b7bd Removed URL parameter and updated MarkdownMacro.java to get URL from bodyContent. 2018-08-08 16:06:31 -05:00
unknown
e3fa72d307 Added import markdown from URL funtionality 2018-08-06 19:25:49 -05:00
Boris Berenberg
fae6196f14 added Highlight.js and updated flexmark, release 1.3.2 2018-05-15 00:09:53 -04:00
Boris Berenberg [Atlas Authority]
1edf603b3e Updated readme to clarify the library in use 2018-05-10 18:22:04 +00:00
Boris Berenberg
8bc3987cf7 updated flexmark version and enabled a few more extensions and raised version to 1.3.2 2018-05-09 14:03:35 -04:00
Boris Berenberg
e91e0128fd 1.3.1 release 2017-07-31 13:26:43 -04:00
bberenberg
ed36d3ed45 [maven-release-plugin] prepare for next development iteration 2015-01-13 10:40:14 -08:00
bberenberg
d6a812be55 [maven-release-plugin] prepare release confluence-markdown-macro-1.1 2015-01-13 10:40:11 -08:00
bberenberg
7e10af38a8 datacenter support 2015-01-13 10:37:19 -08:00
bberenberg
9bc4b9dad6 Testing SSP Testing 2014-11-05 13:05:18 +01:00
bberenberg
086ca73151 SSP 2014-11-05 13:02:11 +01:00
bberenberg
bed352856f [maven-release-plugin] prepare for next development iteration 2013-06-20 19:19:02 -07:00
23 changed files with 705 additions and 60 deletions

View File

@@ -1,7 +1,7 @@
Confluence Markdown Macro
========================
This macro uses the PegDown library to convert from Markdown to HTML within Confluence.
This macro uses the Flexmark library to convert from Markdown to HTML within Confluence.
It can be accessed via:

69
pom.xml
View File

@@ -9,13 +9,13 @@
</parent>
<groupId>com.atlassian.plugins.confluence.markdown</groupId>
<artifactId>confluence-markdown-macro</artifactId>
<version>1.0</version>
<version>1.3.2</version>
<organization>
<name>Atlassian</name>
<url>http://www.atlassian.com/</url>
</organization>
<name>confluence-markdown-macro</name>
<description>This plugin provides a Markdown render macro for Confluence.</description>
<description>This plugin provides macros to render markdown for Confluence.</description>
<packaging>atlassian-plugin</packaging>
<dependencies>
<dependency>
@@ -49,10 +49,54 @@
<version>2.2.2-atlassian-1</version>
</dependency>
<dependency>
<groupId>org.pegdown</groupId>
<artifactId>pegdown</artifactId>
<version>1.2.1</version>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-all</artifactId>
<version>0.32.24</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-ext-anchorlink</artifactId>
<version>0.32.24</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-ext-autolink</artifactId>
<version>0.32.24</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-ext-superscript</artifactId>
<version>0.32.24</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-ext-youtube-embedded</artifactId>
<version>0.32.24</version>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.32</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
@@ -64,6 +108,8 @@
<configuration>
<productVersion>${confluence.version}</productVersion>
<productDataVersion>${confluence.data.version}</productDataVersion>
<enableQuickReload>true</enableQuickReload>
<enableFastdev>false</enableFastdev>
</configuration>
</plugin>
<plugin>
@@ -76,14 +122,15 @@
</plugins>
</build>
<properties>
<confluence.version>4.3.7</confluence.version>
<confluence.data.version>4.3.7</confluence.data.version>
<amps.version>4.2.2</amps.version>
<confluence.version>6.9.0</confluence.version>
<confluence.data.version>6.9.0</confluence.data.version>
<amps.version>6.3.0</amps.version>
<plugin.testrunner.version>1.1</plugin.testrunner.version>
<atlassian.spring.scanner.version>2.1.7</atlassian.spring.scanner.version>
</properties>
<scm>
<connection>scm:git:git@bitbucket.org:atlassianlabs/confluence-markdown-macro.git</connection>
<developerConnection>scm:git:git@bitbucket.org:atlassianlabs/confluence-markdown-macro.git</developerConnection>
<url>https://bitbucket.org/atlassianlabs/confluence-markdown-macro</url>
<connection>scm:git:git@bitbucket.org:atlasauthority/markdown-macro-for-confluence.git</connection>
<developerConnection>scm:git:git@bitbucket.org:atlasauthority/markdown-macro-for-confluence.git</developerConnection>
<url>https://bitbucket.org/atlasauthority/markdown-macro-for-confluence</url>
</scm>
</project>

View File

@@ -1,11 +0,0 @@
#release configuration
#Thu Jun 20 18:34:51 PDT 2013
scm.tagNameFormat=@{project.artifactId}-@{project.version}
pushChanges=true
scm.url=scm\:git\:git@bitbucket.org\:atlassianlabs/confluence-markdown-macro.git
preparationGoals=clean verify
remoteTagging=true
scm.commentPrefix=[maven-release-plugin]
exec.additionalArguments=-P defaultProfile,defaultProfile
exec.snapshotReleasePluginAllowed=false
completedPhase=check-poms

View File

@@ -0,0 +1,199 @@
package com.atlassian.plugins.confluence.markdown;
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext;
import com.atlassian.confluence.content.render.xhtml.XhtmlException;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.confluence.xhtml.api.MacroDefinition;
import com.atlassian.confluence.xhtml.api.MacroDefinitionHandler;
import com.atlassian.confluence.xhtml.api.XhtmlContent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;
//import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.webresource.api.assembler.PageBuilderService;
import org.springframework.beans.factory.annotation.Autowired;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughSubscriptExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.ext.ins.InsExtension;
import com.vladsch.flexmark.ext.definition.DefinitionExtension;
import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension;
import com.vladsch.flexmark.ext.footnotes.FootnoteExtension;
import com.vladsch.flexmark.ext.wikilink.WikiLinkExtension;
import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension;
import com.vladsch.flexmark.superscript.SuperscriptExtension;
import com.vladsch.flexmark.ext.youtube.embedded.YouTubeLinkExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.options.MutableDataSet;
import java.net.*;
import java.io.*;
//@Scanned
public class MarkdownFromURLMacro extends BaseMacro implements Macro
{
private final XhtmlContent xhtmlUtils;
private PageBuilderService pageBuilderService;
@Autowired
public MarkdownFromURLMacro(@ComponentImport PageBuilderService pageBuilderService, XhtmlContent xhtmlUtils) {
this.pageBuilderService = pageBuilderService;
this.xhtmlUtils = xhtmlUtils;
}
// public MarkdownFromURLMacro(XhtmlContent xhtmlUtils)
// {
// this.xhtmlUtils = xhtmlUtils;
// }
@Override
public BodyType getBodyType()
{
return BodyType.PLAIN_TEXT;
}
@Override
public OutputType getOutputType()
{
return OutputType.BLOCK;
}
@Override
public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
{
//If the macro was not left blank, run the macro. Otherwise, return an empty string.
if (bodyContent != null) {
//Include highlight.js in the webpage.
//Adds something like <script type="text/javascript" src="path-to-javascript-file.js"></script> to the <head> of the page
pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
//Set options for flexmark. See flexmark documentation for more info.
MutableDataSet options = new MutableDataSet();
options.set(Parser.EXTENSIONS, Arrays.asList(
TablesExtension.create(),
StrikethroughSubscriptExtension.create(),
InsExtension.create(),
TaskListExtension.create(),
FootnoteExtension.create(),
WikiLinkExtension.create(),
DefinitionExtension.create(),
AnchorLinkExtension.create(),
AutolinkExtension.create(),
SuperscriptExtension.create(),
YouTubeLinkExtension.create()
));
//JavaScript for syntax highlighting. See highlight.js documentation for more info.
String highlightjs = "<script>\n" +
"AJS.$('[data-macro-name=\"markdown\"] code').each(function(i, block) {\n" +
" hljs.highlightBlock(block);\n" +
" });\n" +
"</script>";
//Define a custom exception for trying to import from a private Bitbucket repository
class privateRepositoryException extends Exception {
public privateRepositoryException(String message) {
super(message);
}
}
//Set up parser and renderer for flexmark.
Parser parser = Parser.builder(options).build();
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
String exceptionsToReturn = "";
String html = "";
String toParse = "";
//Import markdown from a URL and render it to html.
//Enclosed in a try/catch block in order to catch exceptions and return error messages to the user
try {
//Create a URL object from the URL typed in by the user,
// then fetch the URL content line by line and store each line temporarily in inputLine.
//Concatenate all the lines into toParse and trim leading and trailing whitespace.
URL importFrom = new URL(bodyContent);
BufferedReader in = new BufferedReader(
new InputStreamReader(importFrom.openStream())
);
String inputLine;
while ((inputLine = in.readLine()) != null) {
toParse = toParse + "\n" + inputLine;
}
in.close();
toParse = toParse.trim();
//If the content of the URL is a file in a private Bitbucket repository, then throw an exception.
//Otherwise, parse and render the markdown.
if (toParse.startsWith("<html>\n<head>\n <title>OpenID transaction in progress</title>")) {
throw new privateRepositoryException("Cannot import from private repository.");
}else {
Node document = parser.parse(toParse);
html = renderer.render(document) + highlightjs;
}
}
catch (MalformedURLException u) {
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Invalid URL.</strong><br>Please enter a valid URL. If you are not trying to import markdown from a URL, use the Markdown macro instead of the Markdown from URL macro.<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
}
catch (privateRepositoryException p) {
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Importing from private Bitbucket repositories is not supported.</strong><br>Please make your repository public before importing. Alternatively, you can copy and paste your markdown into the Markdown macro.<br>If you are allowed access, you can find the markdown file <a href='" + bodyContent + "'>here</a>.<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
}
catch (FileNotFoundException f) {
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: URL does not exist.</strong><br>" + bodyContent + "<br>Please double check your URL. Perhaps you made a typo or perhaps the page has been moved.<br>This can also be caused by changing the Github repository containing the file from public to private. If this is the case go back to the raw file and re-copy the link.<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
}
catch (IOException e) {
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Unexpected error.</strong><br>" + e.toString() + "<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
}
finally {
//If there were exceptions, set the html to return to an error message (or multiple)
if (exceptionsToReturn != "") {
html = "<p style='background: #ffe0e0; border-radius: 5px; padding: 10px;'>" + exceptionsToReturn + "</p>";
}
//Return the output, which is either rendered markdown or an error message.
return html;
}
}else {
return "";
}
}
@Override
public boolean hasBody() {
return true; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public RenderMode getBodyRenderMode() {
return RenderMode.NO_RENDER; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public String execute(Map map, String s, RenderContext renderContext) throws MacroException {
try {
return execute(map, s, new DefaultConversionContext(renderContext));
} catch (MacroExecutionException e) {
throw new MacroException(e.getMessage(),e);
}
}
}

View File

@@ -10,6 +10,7 @@ import com.atlassian.confluence.xhtml.api.MacroDefinitionHandler;
import com.atlassian.confluence.xhtml.api.XhtmlContent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -17,18 +18,42 @@ import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;
import org.pegdown.Parser;
import org.pegdown.PegDownProcessor;
//import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.webresource.api.assembler.PageBuilderService;
import org.springframework.beans.factory.annotation.Autowired;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughSubscriptExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.ext.ins.InsExtension;
import com.vladsch.flexmark.ext.definition.DefinitionExtension;
import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension;
import com.vladsch.flexmark.ext.footnotes.FootnoteExtension;
import com.vladsch.flexmark.ext.wikilink.WikiLinkExtension;
import com.vladsch.flexmark.ext.autolink.AutolinkExtension;
import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension;
import com.vladsch.flexmark.superscript.SuperscriptExtension;
import com.vladsch.flexmark.ext.youtube.embedded.YouTubeLinkExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.options.MutableDataSet;
//@Scanned
public class MarkdownMacro extends BaseMacro implements Macro
{
private final XhtmlContent xhtmlUtils;
public MarkdownMacro(XhtmlContent xhtmlUtils)
{
private final XhtmlContent xhtmlUtils;
private PageBuilderService pageBuilderService;
@Autowired
public MarkdownMacro(@ComponentImport PageBuilderService pageBuilderService, XhtmlContent xhtmlUtils) {
this.pageBuilderService = pageBuilderService;
this.xhtmlUtils = xhtmlUtils;
}
@Override
public BodyType getBodyType()
{
@@ -44,9 +69,46 @@ public class MarkdownMacro extends BaseMacro implements Macro
@Override
public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
{
PegDownProcessor translator = new PegDownProcessor(Parser.ALL);
String output = translator.markdownToHtml(bodyContent);
return output;
//Include highlight.js in the webpage.
//Adds something like <script type="text/javascript" src="path-to-javascript-file.js"></script> to the <head> of the page
pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
//Set options for flexmark. See flexmark documentation for more info.
MutableDataSet options = new MutableDataSet();
options.set(Parser.EXTENSIONS, Arrays.asList(
TablesExtension.create(),
StrikethroughSubscriptExtension.create(),
InsExtension.create(),
TaskListExtension.create(),
FootnoteExtension.create(),
WikiLinkExtension.create(),
DefinitionExtension.create(),
AnchorLinkExtension.create(),
AutolinkExtension.create(),
SuperscriptExtension.create(),
YouTubeLinkExtension.create()
));
//JavaScript for syntax highlighting. See highlight.js documentation for more info.
String highlightjs = "<script>\n" +
"AJS.$('[data-macro-name=\"markdown\"] code').each(function(i, block) {\n" +
" hljs.highlightBlock(block);\n" +
" });\n" +
"</script>";
//Set up parser and renderer for flexmark.
Parser parser = Parser.builder(options).build();
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
// Parse and render the body content from markdown to HTML,
// then return the output along with the JavaScript for syntax highlighting
Node document = parser.parse(bodyContent);
String html = renderer.render(document) + highlightjs;
return html;
}
@Override

View File

@@ -1,6 +0,0 @@
package com.atlassian.plugins.confluence.markdown;
public interface MyPluginComponent
{
String getName();
}

Binary file not shown.

View File

@@ -5,30 +5,48 @@
<vendor name="${project.organization.name}" url="${project.organization.url}" />
<param name="plugin-icon">images/pluginIcon.png</param>
<param name="plugin-logo">images/pluginLogo.png</param>
<param name="atlassian-data-center-compatible">true</param>
</plugin-info>
<xhtml-macro name="markdown"
<xhtml-macro name="markdown"
class="com.atlassian.plugins.confluence.markdown.MarkdownMacro"
key="markdown"
icon="/download/resources/com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/images/pluginIcon.png"
documentation-url="http://daringfireball.net/projects/markdown/">
documentation-url="https://spec.commonmark.org/0.28/">
<category name="formatting"/>
<parameters>
<parameter name="extended" type="boolean" default ="true">
</parameter>
</parameters>
<parameters />
</xhtml-macro>
<macro name="markdown"
class="com.atlassian.plugins.confluence.markdown.MarkdownMacro"
key="markdown.wiki"
icon="/download/resources/com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/images/pluginIcon.png"
documentation-url="http://daringfireball.net/projects/markdown/">
documentation-url="https://spec.commonmark.org/0.28/">
<category name="formatting"/>
<parameters>
<parameter name="extended" type="boolean" default ="true">
</parameter>
</parameters>
<parameters />
</macro>
<xhtml-macro name="markdown-from-url"
class="com.atlassian.plugins.confluence.markdown.MarkdownFromURLMacro"
key="markdown-from-url"
icon="/download/resources/com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/images/pluginIcon.png"
documentation-url="https://spec.commonmark.org/0.28/">
<category name="formatting"/>
<parameters />
</xhtml-macro>
<macro name="markdown-from-url"
class="com.atlassian.plugins.confluence.markdown.MarkdownFromURLMacro"
key="markdown-from-url.wiki"
icon="/download/resources/com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/images/pluginIcon.png"
documentation-url="https://spec.commonmark.org/0.28/">
<category name="formatting"/>
<parameters />
</macro>
<resource type="i18n" name="markdown" location="markdownproperties/markdown"/>
<resource type="i18n" name="markdown-from-url" location="markdown-from-url-properties/markdown-from-url"/>
<resource type="download" name="images/" key="images" location="images/"/>
</atlassian-plugin>
<web-resource key="highlightjs" name="Highlight.js" >
<resource type="download" name="highlight.min.js" location="js/highlight.min.js" />
<resource type="download" name="highlight.min.css" location="css/highlight.min.css" />
</web-resource>
</atlassian-plugin>

View File

@@ -0,0 +1 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown-from-url.label=Markdown From URL
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown-from-url.desc=This macro dynamically imports Markdown from a URL and renders it into HTML.

View File

@@ -1,3 +1 @@
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=This macro renders text in Markdown syntax into HTML.
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.param.extended.label=Enable extended Markdown properties:
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.param.extended.desc=Find info about extended Markdown at: <a href="https://github.com/sirthias/pegdown#readme">https://github.com/sirthias/pegdown#readme</a>
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=This macro renders Markdown into HTML.

View File

@@ -1,3 +1 @@
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=Dieses Makro wandelt Markdown in HTML um.
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.param.extended.label=Aktivieren sich weiter Eigenschaften Markdown:
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.param.extended.desc=Befindet sich information \u00FCber Extended Markdown: <a href="https://github.com/sirthias/pegdown#readme">https://github.com/sirthias/pegdown#readme</a>
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=Dieses Makro wandelt Markdown in HTML um.

View File

@@ -1,3 +1 @@
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=Ce macro converti le text format\u00E9 selon Markdown vers HTML
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.param.extended.label=Activez les propri\u00E9t\u00E9s avanc\u00E9es de Markdown:Pour plus d'information sur
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.param.extended.desc=Pour plus d'information sur les propri\u00E9t\u00E9s avanc\u00E9es de Markdown: <a href="https://github.com/sirthias/pegdown#readme">https://github.com/sirthias/pegdown#readme</a>
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=Ce macro converti Markdown vers HTML

View File

@@ -0,0 +1 @@
com.atlassian.plugins.confluence.markdown.confluence-markdown-macro.markdown.desc=Ce macro converti le text format\u00E9 selon Markdown vers HTML

View File

@@ -0,0 +1,172 @@
package ut.com.atlassian.plugins.confluence;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.HashMap;
import java.util.List;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.*;
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.webresource.api.assembler.PageBuilderService;
import com.atlassian.webresource.api.assembler.RequiredResources;
import com.atlassian.webresource.api.assembler.WebResourceAssembler;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.atlassian.plugins.confluence.markdown.MarkdownFromURLMacro;
@RunWith (MockitoJUnitRunner.class)
public class MarkdownFromURLUnitTest {
@Mock
ConversionContext conversionContext;
@Mock
PageBuilderService pageBuilderService;
@Mock
WebResourceAssembler webResourceAssembler;
@Mock
RequiredResources requiredResources;
@InjectMocks
MarkdownFromURLMacro markdownMacro;
@Test
public void testMarkdownRendering() throws MacroExecutionException, MalformedURLException {
/*Test that markdown is correctly retrieved from a URL and rendered into HTML*/
String file = new File("src/test/resources/testMarkdown.md").toURI().toURL().toString();
@SuppressWarnings({ "rawtypes", "unchecked" })
String output = markdownMacro.execute(new HashMap(), file, conversionContext);
assertTrue(output.contains("<em>Italic</em>"));
}
@Test
public void testErrorHandling() throws MacroExecutionException, MalformedURLException {
/*Test error handling of nonexistent URLs*/
String input1 = new File("src/test/resources/nonexistentfile.md").toURI().toURL().toString();
@SuppressWarnings({ "rawtypes", "unchecked" })
String output1 = markdownMacro.execute(new HashMap(), input1, conversionContext);
assertTrue(output1.contains("URL does not exist"));
assertTrue(output1.contains(input1));
/*Test error handling of invalid URLs*/
String input2 = "not_a_URL";
@SuppressWarnings({ "rawtypes", "unchecked" })
String output2 = markdownMacro.execute(new HashMap(), input2, conversionContext);
assertTrue(output2.contains("Invalid URL"));
/*Test error handling of importing from a private Bitbucket repository*/
String input3 = new File("src/test/resources/testPrivateBitbucket.html").toURI().toURL().toString();
@SuppressWarnings({ "rawtypes", "unchecked" })
String output3 = markdownMacro.execute(new HashMap(), input3, conversionContext);
assertTrue(output3.contains("Importing from private Bitbucket repositories is not supported"));
assertTrue(output3.contains(input3));
}
@Test
public void testSyntaxHighlighting() throws MacroExecutionException, FailingHttpStatusCodeException, MalformedURLException, IOException {
/*Test that the javascript for syntax highlighting works*/
// Run the macro with an input of a line of code
// Create a temporary HTML file containing the output
// Parse the HTML file with htmlunit
// Assert that the page contains three spans with the correct classes
// Note: Does not test if highlight.js and highlight.css are correctly included in the page
try (final WebClient webClient = new WebClient()) {
String file = new File("src/test/resources/testSyntaxHighlighting.md").toURI().toURL().toString();
@SuppressWarnings({ "rawtypes", "unchecked" })
String output = markdownMacro.execute(new HashMap(), file, conversionContext);
String toWrite = "<!DOCTYPE html>\r\n" +
"<html>\r\n" +
"<head>\r\n" +
" <title>Syntax Highlighting Page</title>\r\n" +
" <script src=\"./jquery-3.3.1.min.js\"></script>\r\n" +
" <script src=\"../../main/resources/js/highlight.min.js\"></script>\r\n" +
" <link href=\"../../main/resources/css/highlight.min.css\" rel=\"stylesheet\" />\r\n" +
"</head>\r\n" +
"<body>\r\n" +
" <script>\r\n" +
" var AJS = {\r\n" +
" $: $\r\n" +
" };\r\n" +
" </script>\r\n" +
"<div data-macro-name='markdown'>" +
output +
"</div>" +
"</body>\r\n" +
"</html>";
File tmpHTMLFile = File.createTempFile("syntax-highlighting-", ".html", new File("src/test/resources"));
FileWriter writer = new FileWriter(tmpHTMLFile);
writer.write(toWrite);
writer.close();
final HtmlPage page = webClient.getPage(tmpHTMLFile.toURI().toURL().toString());
HtmlElement document = page.getDocumentElement();
List<HtmlElement> spans = document.getElementsByTagName("span");
tmpHTMLFile.delete();
HtmlElement hljsClassSpan = null;
int numberOfHljsClassSpans = 0;
HtmlElement hljsKeywordSpan = null;
int numberOfHljsKeywordSpans = 0;
HtmlElement hljsTitleSpan = null;
int numberOfHljsTitleSpans = 0;
//Check the class of every span element and increment the counters accordingly
//Also define the three span objects as the first span of the correct class
for (HtmlElement span : spans) {
if (span.getAttribute("class").contains("hljs-class")) {
numberOfHljsClassSpans++;
if (numberOfHljsClassSpans == 1) {
hljsClassSpan = span;
}
}
if (span.getAttribute("class").contains("hljs-keyword")) {
numberOfHljsKeywordSpans++;
if (numberOfHljsKeywordSpans == 1) {
hljsKeywordSpan = span;
}
}
if (span.getAttribute("class").contains("hljs-title")) {
numberOfHljsTitleSpans++;
if (numberOfHljsTitleSpans == 1) {
hljsTitleSpan = span;
}
}
}
// Test that there is exactly one span with a css class of hljs-class
// and that it's parent is a code element with a css class of hljs
assertThat(numberOfHljsClassSpans, is(1));
assertTrue(hljsClassSpan.getParentNode().getNodeName().equals("code"));
assertTrue(hljsClassSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs"));
// Test that there is exactly one span with a css class of hljs-keyword
// and that it's parent is a span element with a css class of hljs-class
assertThat(numberOfHljsKeywordSpans, is(1));
assertTrue(hljsKeywordSpan.getParentNode().getNodeName().equals("span"));
assertTrue(hljsKeywordSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
// Test that there is exactly one span with a css class of hljs-title
// and that it's parent is a span element with a css class of hljs-class
assertThat(numberOfHljsTitleSpans, is(1));
assertTrue(hljsTitleSpan.getParentNode().getNodeName().equals("span"));
assertTrue(hljsTitleSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
}
}
@Before
public void setup() {
//Mock methods for pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
Mockito.when(pageBuilderService.assembler()).thenReturn(webResourceAssembler);
Mockito.when(webResourceAssembler.resources()).thenReturn(requiredResources);
Mockito.when(requiredResources.requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs")).thenReturn(requiredResources);
}
}

View File

@@ -0,0 +1,144 @@
package ut.com.atlassian.plugins.confluence;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.*;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.List;
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.webresource.api.assembler.PageBuilderService;
import com.atlassian.webresource.api.assembler.RequiredResources;
import com.atlassian.webresource.api.assembler.WebResourceAssembler;
import com.atlassian.plugins.confluence.markdown.MarkdownMacro;
@RunWith (MockitoJUnitRunner.class)
public class MarkdownUnitTest {
@Mock
ConversionContext conversionContext;
@Mock
PageBuilderService pageBuilderService;
@Mock
WebResourceAssembler webResourceAssembler;
@Mock
RequiredResources requiredResources;
@InjectMocks
MarkdownMacro markdownMacro;
@Test
public void testMarkdownRendering() throws MacroExecutionException {
/*Test that markdown is correctly rendered into HTML*/
@SuppressWarnings({ "rawtypes", "unchecked" })
String output = markdownMacro.execute(new HashMap(), "*Italic*", conversionContext);
assertTrue(output.contains("<em>Italic</em>"));
}
@Test
public void testSyntaxHighlighting() throws MacroExecutionException, IOException {
/*Test that the javascript for syntax highlighting works*/
// Run the macro with an input of a line of code
// Create a temporary HTML file containing the output
// Parse the HTML file with htmlunit
// Assert that the page contains three spans with the correct classes
// Note: Does not test if highlight.js and highlight.css are correctly included in the page
try (final WebClient webClient = new WebClient()) {
@SuppressWarnings({ "rawtypes", "unchecked" })
String output = markdownMacro.execute(new HashMap(), "`class className() {}`", conversionContext);
String toWrite = "<!DOCTYPE html>\r\n" +
"<html>\r\n" +
"<head>\r\n" +
" <title>Syntax Highlighting Page</title>\r\n" +
" <script src=\"./jquery-3.3.1.min.js\"></script>\r\n" +
" <script src=\"../../main/resources/js/highlight.min.js\"></script>\r\n" +
" <link href=\"../../main/resources/css/highlight.min.css\" rel=\"stylesheet\" />\r\n" +
"</head>\r\n" +
"<body>\r\n" +
" <script>\r\n" +
" var AJS = {\r\n" +
" $: $\r\n" +
" };\r\n" +
" </script>\r\n" +
"<div data-macro-name='markdown'>" +
output +
"</div>" +
"</body>\r\n" +
"</html>";
File tmpHTMLFile = File.createTempFile("syntax-highlighting-", ".html", new File("src/test/resources"));
FileWriter writer = new FileWriter(tmpHTMLFile);
writer.write(toWrite);
writer.close();
final HtmlPage page = webClient.getPage(tmpHTMLFile.toURI().toURL().toString());
HtmlElement document = page.getDocumentElement();
List<HtmlElement> spans = document.getElementsByTagName("span");
tmpHTMLFile.delete();
HtmlElement hljsClassSpan = null;
int numberOfHljsClassSpans = 0;
HtmlElement hljsKeywordSpan = null;
int numberOfHljsKeywordSpans = 0;
HtmlElement hljsTitleSpan = null;
int numberOfHljsTitleSpans = 0;
//Check the class of every span element and increment the counters accordingly
//Also define the three span objects as the first span of the correct class
for (HtmlElement span : spans) {
if (span.getAttribute("class").contains("hljs-class")) {
numberOfHljsClassSpans++;
if (numberOfHljsClassSpans == 1) {
hljsClassSpan = span;
}
}
if (span.getAttribute("class").contains("hljs-keyword")) {
numberOfHljsKeywordSpans++;
if (numberOfHljsKeywordSpans == 1) {
hljsKeywordSpan = span;
}
}
if (span.getAttribute("class").contains("hljs-title")) {
numberOfHljsTitleSpans++;
if (numberOfHljsTitleSpans == 1) {
hljsTitleSpan = span;
}
}
}
// Test that there is exactly one span with a css class of hljs-class
// and that it's parent is a code element with a css class of hljs
assertThat(numberOfHljsClassSpans, is(1));
assertTrue(hljsClassSpan.getParentNode().getNodeName().equals("code"));
assertTrue(hljsClassSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs"));
// Test that there is exactly one span with a css class of hljs-keyword
// and that it's parent is a span element with a css class of hljs-class
assertThat(numberOfHljsKeywordSpans, is(1));
assertTrue(hljsKeywordSpan.getParentNode().getNodeName().equals("span"));
assertTrue(hljsKeywordSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
// Test that there is exactly one span with a css class of hljs-title
// and that it's parent is a span element with a css class of hljs-class
assertThat(numberOfHljsTitleSpans, is(1));
assertTrue(hljsTitleSpan.getParentNode().getNodeName().equals("span"));
assertTrue(hljsTitleSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
}
}
@Before
public void setup() {
//Mock methods for pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
Mockito.when(pageBuilderService.assembler()).thenReturn(webResourceAssembler);
Mockito.when(webResourceAssembler.resources()).thenReturn(requiredResources);
Mockito.when(requiredResources.requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs")).thenReturn(requiredResources);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
*Italic*

View File

@@ -0,0 +1,15 @@
<html>
<head>
<title>OpenID transaction in progress</title>
</head>
<body onload="document.forms[0].submit();">
<form id="openid_message" action="https://id.atlassian.com/openid/v2/op" method="post" accept-charset="UTF-8" enctype="application/x-www-form-urlencoded"><input name="openid.ns.crowdid" type="hidden" value="https://developer.atlassian.com/display/CROWDDEV/CrowdID+OpenID+extensions#CrowdIDOpenIDextensions-login-page-parameters"/><input name="openid.return_to" type="hidden" value="https://bitbucket.org/socialauth/complete/atlassianid/?janrain_nonce=2018-09-03T16%3A44%3A09ZAMqmtD"/><input name="openid.realm" type="hidden" value="https://bitbucket.org"/><input name="openid.ns" type="hidden" value="http://specs.openid.net/auth/2.0"/><input name="openid.sreg.optional" type="hidden" value="fullname,nickname,email"/><input name="openid.claimed_id" type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select"/><input name="openid.ns.sreg" type="hidden" value="http://openid.net/extensions/sreg/1.1"/><input name="openid.crowdid.application" type="hidden" value="bitbucket"/><input name="openid.assoc_handle" type="hidden" value="8063999"/><input name="openid.mode" type="hidden" value="checkid_setup"/><input name="openid.identity" type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select"/><input type="submit" value="Continue"/></form>
<script>
var elements = document.forms[0].elements;
for (var i = 0; i < elements.length; i++) {
elements[i].style.display = "none";
}
</script>
</body>
</html>

View File

@@ -0,0 +1 @@
`class className() {}`