Skip to content

Commit

Permalink
Enable markdown descriptions.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkalus committed Jul 28, 2021
1 parent 3fd58fe commit 5862380
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 2 deletions.
164 changes: 164 additions & 0 deletions src/main/java/org/segrada/rendering/markdown/MarkdownRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package org.segrada.rendering.markdown;

import com.google.inject.Injector;
import com.vladsch.flexmark.ext.emoji.EmojiExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.ext.typographic.TypographicExtension;
import com.vladsch.flexmark.ext.wikilink.WikiLink;
import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkRefProcessor;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.html.HtmlRenderer.Builder;
import com.vladsch.flexmark.html.HtmlWriter;
import com.vladsch.flexmark.html.renderer.*;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.ast.TextCollectingVisitor;
import com.vladsch.flexmark.util.data.DataHolder;
import com.vladsch.flexmark.util.data.MutableDataHolder;
import com.vladsch.flexmark.util.data.MutableDataSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.segrada.model.prototype.ISource;
import org.segrada.service.SourceService;
import org.unbescape.html.HtmlEscape;

import java.util.*;

public class MarkdownRenderer {
/**
* reference to injector
*/
public static Injector injector;

/**
* cache for source reference links
* TODO: use different cache, because otherwise this might fill up memory eventually, rather unlikely, but there is no deletion of old keys here
*/
private static final Map<String, String> sourceReferenceCache = new HashMap<>();

public static void setInjector(Injector injector) {
MarkdownRenderer.injector = injector;
}

/**
* get current source service instance from injector
* @return SourceService instance
*/
public static SourceService getSourceService() {
return injector.getInstance(SourceService.class);
}

// markdown data holder with custom extension + more extensions defined below
final private static DataHolder OPTIONS = new MutableDataSet()
.set(TablesExtension.CLASS_NAME, "table table-condensed")
.set(Parser.EXTENSIONS, Arrays.asList(
CustomExtension.create(), // will use wiki link renderer to work links
EmojiExtension.create(),
TablesExtension.create(),
TypographicExtension.create()
)).toImmutable();

// markdown parser and renderer
static final Parser PARSER = Parser.builder(OPTIONS).build();
static final HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build();

/**
* Render or remove links
*/
public static class BibliographicEntryLinkNodeRenderer implements NodeRenderer {
@Override
public @Nullable Set<NodeRenderingHandler<?>> getNodeRenderingHandlers() {
HashSet<NodeRenderingHandler<?>> set = new HashSet();
set.add(new NodeRenderingHandler(WikiLink.class, this::render));
return set;
}

private void render(Node node, @NotNull NodeRendererContext context, @NotNull HtmlWriter html) {
if (node instanceof WikiLink) {
WikiLink link = (WikiLink)node;

String possibleKey = link.getLink().toString();

SourceService sourceService = getSourceService();

// fallback in case of errors
if (sourceService == null) {
html.text(node.getChars().unescape());
return;
}

// find corresponding source
ISource source = sourceService.findByRef(possibleKey);
if (source == null) {
// not found: just print text
html.text(node.getChars().unescape());
} else {
// otherwise: create nice link
String url = "source/show/" + source.getUid();

ResolvedLink resolvedLink = context.resolveLink(LinkType.LINK, url, (Boolean)null);
html.attr("href", url);
html.attr("class", "sg-data-add");
html.attr("title", source.getShortRef());
html.srcPos(node.getChars()).withAttr(resolvedLink).tag("a");
html.text(source.getShortTitle());
html.tag("/a");
}
}
}

public static class Factory implements NodeRendererFactory {
public Factory() {
}

@NotNull
public NodeRenderer apply(@NotNull DataHolder options) {
return new BibliographicEntryLinkNodeRenderer();
}
}
}

static class CustomExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
@Override
public void rendererOptions(@NotNull MutableDataHolder options) {

}

@Override
public void parserOptions(MutableDataHolder mutableDataHolder) {
}

@Override
public void extend(Parser.Builder parserBuilder) {
parserBuilder.linkRefProcessorFactory(new WikiLinkLinkRefProcessor.Factory()); // we will use wiki link extension to help us build links
}

@Override
public void extend(@NotNull Builder htmlRendererBuilder, @NotNull String rendererType) {
htmlRendererBuilder.nodeRendererFactory(new BibliographicEntryLinkNodeRenderer.Factory());
}

static CustomExtension create() {
return new CustomExtension();
}
}

/**
* renders markdown to HTML
* @param markdown source
* @return html
*/
public static String render(String markdown) {
return RENDERER.render(PARSER.parse(markdown));
}

/**
* converts markdown to plain text
* @param markdown source
* @return text
*/
public static String toPlainText(String markdown) {
TextCollectingVisitor textCollectingVisitor = new TextCollectingVisitor();
return textCollectingVisitor.collectAndGetText(PARSER.parse(markdown));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class DefaultMarkupFilter extends MarkupFilter {
"-&gt;", "&rarr;",
};

private static final Pattern bibRefPattern = Pattern.compile("\\[\\[([^:\\[\\]]+:[^\\[\\]]+)\\]\\]");
private static final Pattern bibRefPattern = Pattern.compile("\\[\\[([^]]+)\\]\\]");

/**
* @return pattern to find bibliographic references
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.segrada.rendering.markup;

import org.jsoup.Jsoup;
import org.jsoup.parser.Parser;
import org.jsoup.safety.Whitelist;
import org.segrada.rendering.markdown.MarkdownRenderer;

/**
* Copyright 2015-2021 Maximilian Kalus [segrada@auxnet.de]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Markdown markup filter
*/
public class MarkdownMarkupFilter extends MarkupFilter {
@Override
public String toHTML(String markupText) {
return MarkdownRenderer.render(markupText);
}

@Override
public String toPlain(String markupText) {
// sane default
if (markupText == null || markupText.equals("")) return "";

// remove bibliographic entries
String plainText = markupText.replaceAll("\\[\\[[\\w:]+\\]\\]", "").replaceAll("\\[[0-9f]+:\\]", "");

// clean
return MarkdownRenderer.toPlainText(plainText);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import org.segrada.config.ServiceModule;
import org.segrada.config.TemplateModule;
import org.segrada.controller.*;
import org.segrada.rendering.markdown.MarkdownRenderer;
import org.segrada.rendering.markup.DefaultMarkupFilter;
import org.segrada.rendering.markup.MarkdownMarkupFilter;

import javax.servlet.annotation.WebListener;
import java.util.Map;
Expand Down Expand Up @@ -94,6 +96,7 @@ protected void configureServlets() {
OrientDBFilter.setInjector(injector);
SegradaSimplePageCachingFilter.setInjector(injector);
DefaultMarkupFilter.setInjector(injector);
MarkdownRenderer.setInjector(injector);
CheckAuthentication.setInjector(injector);

return injector;
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/less/single/segrada.less
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ td .sg-data-taglist span {

.sg-description {
margin-top: 20px;

hr {
border-color: #000;
}
}

.sg-map {
Expand Down
6 changes: 6 additions & 0 deletions src/main/webapp/WEB-INF/templates/partials/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ <h2>Text area with markup</h2>
HTML
</label>
</div>
<div class="radio-inline">
<label>
<input class="sg-plain-editor" th:attr="data-editor=${'#' + name + '-' + id}" type="radio" th:id="${markupName + '-' + id + '_markdown'}" th:name="${markupName}" value="markdown" th:checked="${#strings.equals('markdown', markupField)}">
Markdown
</label>
</div>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/main/webapp/css/segrada.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5862380

Please sign in to comment.